1/* eslint-disable import/order */ 2const { copySync, removeSync } = require('fs-extra'); 3const merge = require('lodash/merge'); 4const { join } = require('path'); 5const semver = require('semver'); 6const { ESBuildMinifyPlugin } = require('esbuild-loader'); 7 8const navigation = require('./constants/navigation-data'); 9const versions = require('./constants/versions'); 10const { version, betaVersion } = require('./package.json'); 11 12// To generate a sitemap, we need context about the supported versions and navigational data 13const createSitemap = require('./scripts/create-sitemap'); 14 15// copy versions/v(latest version) to versions/latest 16// (Next.js only half-handles symlinks) 17const vLatest = join('pages', 'versions', `v${version}/`); 18const latest = join('pages', 'versions', 'latest/'); 19removeSync(latest); 20copySync(vLatest, latest); 21 22// Determine if we are using esbuild for MDX transpiling 23const enableEsbuild = !!process.env.USE_ESBUILD; 24 25console.log(enableEsbuild ? 'Using esbuild for MDX files' : 'Using babel for MDX files'); 26 27module.exports = { 28 // future: { 29 // webpack5: true, 30 // }, 31 trailingSlash: true, 32 // Rather than use `@zeit/next-mdx`, we replicate it 33 pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'], 34 webpack: (config, options) => { 35 // Add preval support for `constants/*` only and move it to the `.next/preval` cache. 36 // It's to prevent over-usage and separate the cache to allow manually invalidation. 37 // See: https://github.com/kentcdodds/babel-plugin-preval/issues/19 38 config.module.rules.push({ 39 test: /.jsx?$/, 40 include: [join(__dirname, 'constants')], 41 use: merge({}, options.defaultLoaders.babel, { 42 options: { 43 // Keep this path in sync with package.json and other scripts that clear the cache 44 cacheDirectory: '.next/preval', 45 plugins: ['preval'], 46 }, 47 }), 48 }); 49 50 // Add support for MDX with our custom loader and esbuild 51 config.module.rules.push({ 52 test: /.mdx?$/, // load both .md and .mdx files 53 use: [ 54 !enableEsbuild 55 ? options.defaultLoaders.babel 56 : { 57 loader: 'esbuild-loader', 58 options: { 59 loader: 'tsx', 60 target: 'es2017', 61 }, 62 }, 63 { 64 loader: '@mdx-js/loader', 65 options: { 66 remarkPlugins: [ 67 [require('remark-frontmatter'), ['yaml']], 68 require('./mdx-plugins/remark-export-yaml'), 69 require('./mdx-plugins/remark-export-headings'), 70 require('./mdx-plugins/remark-link-rewrite'), 71 ], 72 }, 73 }, 74 ], 75 }); 76 77 // Fix inline or browser MDX usage: https://mdxjs.com/getting-started/webpack#running-mdx-in-the-browser 78 // Webpack 4 79 config.node = { fs: 'empty' }; 80 // Webpack 5 81 // config.resolve.fallback = { fs: false, path: require.resolve('path-browserify') }; 82 83 // Add the esbuild plugin only when using esbuild 84 if (enableEsbuild) { 85 config.optimization.minimizer = [ 86 new ESBuildMinifyPlugin({ 87 target: 'es2017', 88 }), 89 ]; 90 } 91 92 return config; 93 }, 94 // Create a map of all pages to export 95 async exportPathMap(defaultPathMap, { dev, outDir }) { 96 if (dev) { 97 return defaultPathMap; 98 } 99 const pathMap = Object.assign( 100 ...Object.entries(defaultPathMap).map(([pathname, page]) => { 101 if (pathname.match(/\/v[1-9][^/]*$/)) { 102 // ends in "/v<version>" 103 pathname += '/index.html'; // TODO: find out why we need to do this 104 } 105 if (pathname.match(/unversioned/)) { 106 return {}; 107 } else { 108 // hide versions greater than the package.json version number 109 const versionMatch = pathname.match(/\/v(\d\d\.\d\.\d)\//); 110 if ( 111 versionMatch && 112 versionMatch[1] && 113 semver.gt(versionMatch[1], betaVersion || version) 114 ) { 115 return {}; 116 } 117 return { [pathname]: page }; 118 } 119 }) 120 ); 121 122 createSitemap({ 123 pathMap, 124 domain: `https://docs.expo.dev`, 125 output: join(outDir, `sitemap.xml`), 126 // Some of the search engines only track the first N items from the sitemap, 127 // this makes sure our starting and general guides are first, and API index last (in order from new to old) 128 pathsPriority: [ 129 ...navigation.startingDirectories, 130 ...navigation.generalDirectories, 131 ...navigation.easDirectories, 132 ...versions.VERSIONS.map(version => `versions/${version}`), 133 ], 134 // Some of our pages are "hidden" and should not be added to the sitemap 135 pathsHidden: navigation.previewDirectories, 136 }); 137 138 return pathMap; 139 }, 140 async headers() { 141 const cacheHeaders = [{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' }]; 142 return [{ source: '/_next/static/:static*', headers: cacheHeaders }]; 143 }, 144}; 145