xref: /expo/docs/next.config.js (revision 1984dd5a)
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 semver from 'semver';
7import { fileURLToPath } from 'url';
8
9import * as navigation from './constants/navigation.cjs';
10import { VERSIONS } from './constants/versions.cjs';
11import remarkExportHeadings from './mdx-plugins/remark-export-headings.cjs';
12import remarkExportYaml from './mdx-plugins/remark-export-yaml.cjs';
13import remarkLinkRewrite from './mdx-plugins/remark-link-rewrite.cjs';
14import createSitemap from './scripts/create-sitemap.cjs';
15
16const { copySync, removeSync, readJsonSync } = fsExtra;
17
18// note(simek): We cannot use direct JSON import because ESLint do not support `assert { type: 'json' }` syntax yet:
19// * https://github.com/eslint/eslint/discussions/15305
20const { version, betaVersion } = readJsonSync('./package.json');
21const dirname = fileURLToPath(new URL('.', import.meta.url));
22
23// Prepare the latest version by copying the actual exact latest version
24const vLatest = join('pages', 'versions', `v${version}/`);
25const latest = join('pages', 'versions', 'latest/');
26removeSync(latest);
27copySync(vLatest, latest);
28logInfo(`Copied latest Expo SDK version from v${version}`);
29
30/** @type {import('next').NextConfig}  */
31export default {
32  trailingSlash: true,
33  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
34  compiler: { emotion: true },
35  swcMinify: true,
36  webpack: (config, options) => {
37    // Add preval support for `constants/*` only and move it to the `.next/preval` cache.
38    // It's to prevent over-usage and separate the cache to allow manually invalidation.
39    // See: https://github.com/kentcdodds/babel-plugin-preval/issues/19
40    config.module.rules.push({
41      test: /.js$/,
42      include: [join(dirname, 'constants')],
43      use: {
44        loader: 'babel-loader',
45        options: {
46          // Keep this path in sync with package.json and other scripts that clear the cache
47          cacheDirectory: '.next/preval',
48          plugins: ['preval'],
49          presets: ['next/babel'],
50        },
51      },
52    });
53
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          options: {
62            remarkPlugins: [
63              [remarkFrontmatter, ['yaml']],
64              remarkExportYaml,
65              remarkExportHeadings,
66              remarkLinkRewrite,
67            ],
68            rehypePlugins: [rehypeSlug],
69          },
70        },
71      ],
72    });
73
74    // Fix inline or browser MDX usage
75    config.resolve.fallback = { fs: false, path: 'path-browserify' };
76
77    return config;
78  },
79
80  // Create a map of all pages to export
81  async exportPathMap(defaultPathMap, { dev, outDir }) {
82    if (dev) {
83      return defaultPathMap;
84    }
85    const pathMap = Object.assign(
86      ...Object.entries(defaultPathMap).map(([pathname, page]) => {
87        if (pathname.match(/unversioned/)) {
88          // Remove unversioned pages from the exported site
89          return {};
90        } else {
91          // Remove newer unreleased versions from the exported side
92          const versionMatch = pathname.match(/\/v(\d\d\.\d\.\d)\//);
93          if (versionMatch?.[1] && semver.gt(versionMatch[1], betaVersion || version)) {
94            return {};
95          }
96        }
97
98        return { [pathname]: page };
99      })
100    );
101
102    const sitemapEntries = createSitemap({
103      pathMap,
104      domain: `https://docs.expo.dev`,
105      output: join(outDir, `sitemap.xml`),
106      // Some of the search engines only track the first N items from the sitemap,
107      // this makes sure our starting and general guides are first, and API index last (in order from new to old)
108      pathsPriority: [
109        ...navigation.generalDirectories,
110        ...navigation.easDirectories,
111        ...VERSIONS.map(version => `versions/${version}`),
112      ],
113      // Some of our pages are "hidden" and should not be added to the sitemap
114      pathsHidden: navigation.previewDirectories,
115    });
116    logInfo(`�� Generated sitemap with ${sitemapEntries.length} entries`);
117
118    return pathMap;
119  },
120  async headers() {
121    const cacheHeaders = [{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' }];
122    return [{ source: '/_next/static/:static*', headers: cacheHeaders }];
123  },
124};
125