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