xref: /expo/docs/next.config.js (revision 665c2bac)
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