xref: /expo/docs/next.config.js (revision bb8f4f99)
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 { ESBuildPlugin } = 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  trailingSlash: true,
29  // Rather than use `@zeit/next-mdx`, we replicate it
30  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
31  webpack: (config, options) => {
32    // Add preval support for `constants/*` only and move it to the `.next/preval` cache.
33    // It's to prevent over-usage and separate the cache to allow manually invalidation.
34    // See: https://github.com/kentcdodds/babel-plugin-preval/issues/19
35    config.module.rules.push({
36      test: /.jsx?$/,
37      include: [join(__dirname, 'constants')],
38      use: merge({}, options.defaultLoaders.babel, {
39        options: {
40          // Keep this path in sync with package.json and other scripts that clear the cache
41          cacheDirectory: '.next/preval',
42          plugins: ['preval'],
43        },
44      }),
45    });
46
47    // Add support for MDX with our custom loader and esbuild
48    config.module.rules.push({
49      test: /.mdx?$/, // load both .md and .mdx files
50      use: [
51        !enableEsbuild
52          ? options.defaultLoaders.babel
53          : {
54              loader: 'esbuild-loader',
55              options: {
56                loader: 'tsx',
57                target: 'es2017',
58              },
59            },
60        {
61          loader: '@mdx-js/loader',
62          options: {
63            remarkPlugins: [
64              require('./mdx-plugins/remark-heading-meta'),
65              require('./mdx-plugins/remark-link-rewrite'),
66            ],
67          },
68        },
69        join(__dirname, './common/md-loader'),
70      ],
71    });
72    // Fix inline or browser MDX usage: https://mdxjs.com/getting-started/webpack#running-mdx-in-the-browser
73    config.node = { fs: 'empty' };
74    // Add the esbuild plugin only when using esbuild
75    if (enableEsbuild) {
76      config.plugins.push(new ESBuildPlugin());
77    }
78
79    return config;
80  },
81  // Create a map of all pages to export
82  async exportPathMap(defaultPathMap, { dev, outDir }) {
83    if (dev) {
84      return defaultPathMap;
85    }
86    const pathMap = Object.assign(
87      ...Object.entries(defaultPathMap).map(([pathname, page]) => {
88        if (pathname.match(/\/v[1-9][^/]*$/)) {
89          // ends in "/v<version>"
90          pathname += '/index.html'; // TODO: find out why we need to do this
91        }
92        if (pathname.match(/unversioned/)) {
93          return {};
94        } else {
95          // hide versions greater than the package.json version number
96          const versionMatch = pathname.match(/\/v(\d\d\.\d\.\d)\//);
97          if (
98            versionMatch &&
99            versionMatch[1] &&
100            semver.gt(versionMatch[1], betaVersion || version)
101          ) {
102            return {};
103          }
104          return { [pathname]: page };
105        }
106      })
107    );
108    // Create a sitemap for crawlers like Google and Algolia
109    createSitemap({
110      pathMap,
111      domain: 'https://docs.expo.io',
112      output: join(outDir, 'sitemap.xml'),
113      // Some of the search engines only track the first N items from the sitemap,
114      // this makes sure our starting and general guides are first, and API index last (in order from new to old)
115      pathsPriority: [
116        ...navigation.startingDirectories,
117        ...navigation.generalDirectories,
118        ...versions.VERSIONS.map(version => `versions/${version}`),
119      ],
120      // Some of our pages are "hidden" and should not be added to the sitemap
121      pathsHidden: navigation.previewDirectories,
122    });
123    return pathMap;
124  },
125};
126