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