1import fsExtra from 'fs-extra'; 2import { info as logInfo } from 'next/dist/build/output/log.js'; 3import { join } from 'path'; 4import rehypeSlug from 'rehype-slug'; 5import remarkFrontmatter from 'remark-frontmatter'; 6import remarkGFM from 'remark-gfm'; 7import remarkMDX from 'remark-mdx'; 8import remarkMdxDisableExplicitJsx from 'remark-mdx-disable-explicit-jsx'; 9import remarkMDXFrontmatter from 'remark-mdx-frontmatter'; 10import semver from 'semver'; 11 12import remarkCodeTitle from './mdx-plugins/remark-code-title.js'; 13import remarkCreateStaticProps from './mdx-plugins/remark-create-static-props.js'; 14import remarkExportHeadings from './mdx-plugins/remark-export-headings.js'; 15import remarkLinkRewrite from './mdx-plugins/remark-link-rewrite.js'; 16import { copyAsLatest } from './scripts/copy-latest.js'; 17import createSitemap from './scripts/create-sitemap.js'; 18 19const { readJsonSync } = fsExtra; 20 21// note(simek): We cannot use direct JSON import because ESLint do not support `assert { type: 'json' }` syntax yet: 22// * https://github.com/eslint/eslint/discussions/15305 23const { version, betaVersion } = readJsonSync('./package.json'); 24const { VERSIONS } = readJsonSync('./public/static/constants/versions.json'); 25const navigation = readJsonSync('./public/static/constants/navigation.json'); 26 27copyAsLatest(version); 28logInfo(`Copied latest Expo SDK version from v${version}`); 29 30const removeConsole = 31 process.env.NODE_ENV !== 'development' 32 ? { 33 exclude: ['error'], 34 } 35 : false; 36 37/** @type {import('next').NextConfig} */ 38export default { 39 trailingSlash: true, 40 experimental: { 41 esmExternals: true, 42 // note(simek): would be nice enhancement, but it breaks the `@next/font` styles currently, 43 // and results in font face swap on every page reload 44 optimizeCss: false, 45 fontLoaders: [{ loader: '@next/font/google', options: { subsets: ['latin'] } }], 46 }, 47 pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'], 48 compiler: { 49 emotion: true, 50 reactRemoveProperties: true, 51 removeConsole, 52 }, 53 poweredByHeader: false, 54 webpack: (config, options) => { 55 // Add support for MDX with our custom loader 56 config.module.rules.push({ 57 test: /.mdx?$/, 58 use: [ 59 options.defaultLoaders.babel, 60 { 61 loader: '@mdx-js/loader', 62 /** @type {import('@mdx-js/loader').Options} */ 63 options: { 64 providerImportSource: '@mdx-js/react', 65 remarkPlugins: [ 66 remarkMDX, 67 remarkGFM, 68 [remarkMdxDisableExplicitJsx, { whiteList: ['kbd'] }], 69 remarkFrontmatter, 70 [remarkMDXFrontmatter, { name: 'meta' }], 71 remarkCodeTitle, 72 remarkExportHeadings, 73 remarkLinkRewrite, 74 [remarkCreateStaticProps, `{ meta: meta || {}, headings: headings || [] }`], 75 ], 76 rehypePlugins: [rehypeSlug], 77 }, 78 }, 79 ], 80 }); 81 82 // Fix inline or browser MDX usage 83 config.resolve.fallback = { fs: false, path: 'path-browserify' }; 84 85 return config; 86 }, 87 88 // Create a map of all pages to export 89 // https://nextjs.org/docs/api-reference/next.config.js/exportPathMap 90 async exportPathMap(defaultPathMap, { dev, outDir }) { 91 if (dev) { 92 return defaultPathMap; 93 } 94 const pathMap = Object.assign( 95 ...Object.entries(defaultPathMap).map(([pathname, page]) => { 96 if (pathname.match(/unversioned/)) { 97 // Remove unversioned pages from the exported site 98 return {}; 99 } else { 100 // Remove newer unreleased versions from the exported side 101 const versionMatch = pathname.match(/\/v(\d\d\.\d\.\d)\//); 102 if (versionMatch?.[1] && semver.gt(versionMatch[1], betaVersion || version, false)) { 103 return {}; 104 } 105 } 106 107 return { [pathname]: page }; 108 }) 109 ); 110 111 const sitemapEntries = createSitemap({ 112 pathMap, 113 domain: `https://docs.expo.dev`, 114 output: join(outDir, `sitemap.xml`), 115 // Some of the search engines only track the first N items from the sitemap, 116 // this makes sure our starting and general guides are first, and API index last (in order from new to old) 117 pathsPriority: [ 118 ...navigation.homeDirectories, 119 ...navigation.learnDirectories, 120 ...navigation.generalDirectories, 121 ...navigation.referenceDirectories.filter(dir => dir === 'versions'), 122 ...VERSIONS.map(version => `versions/${version}`), 123 ], 124 // Some of our pages are "hidden" and should not be added to the sitemap 125 pathsHidden: [...navigation.previewDirectories, ...navigation.archiveDirectories], 126 }); 127 logInfo(` Generated sitemap with ${sitemapEntries.length} entries`); 128 129 return pathMap; 130 }, 131 async headers() { 132 const cacheHeaders = [{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' }]; 133 return [{ source: '/_next/static/:static*', headers: cacheHeaders }]; 134 }, 135}; 136