1const path = require('path');
2const visit = require('unist-util-visit');
3const { URL } = require('url');
4
5const DEFAULT_OPTIONS = {
6  extension: 'md',
7  pagesDir: 'pages',
8  trailingSlash: true,
9};
10
11// This is a fallback domain, used to parse URLs. If origin matches this, its an internal link
12const FAKE_DOMAIN = 'https://fake.domain';
13
14/**
15 * This rewrites internal MDX links to absolute URLs from the root domain.
16 * It's the similar behavior of GitHub MDX linking, but for Next.
17 *
18 * @param {object} options
19 * @param {string} [options.extension="md"]
20 * @param {string} [options.pagesDir="pages"]
21 * @param {boolean} [options.trailingSlash=true]
22 * @returns {function} remark plugin
23 */
24module.exports = function remarkLinkRewrite(options) {
25  const settings = { ...DEFAULT_OPTIONS, ...options };
26
27  return (tree, file) => {
28    // we can't rewrite files without knowing where the file exists
29    if (!file.cwd || !file.history || !file.history.length) {
30      return;
31    }
32    // index references should be ignored, it's handled by Next
33    const ignoredIndex = 'index' + (settings.trailingSlash ? '/' : '');
34
35    visit(tree, 'link', node => {
36      // parse the url with a fallback fake domain, used to determine if it's internal or not
37      const ref = new URL(node.url, FAKE_DOMAIN);
38      // only rewrite internal non-url nodes
39      if (ref.origin === FAKE_DOMAIN) {
40        // if only a hash is provided, we need to calculate from the same file
41        const oldUrl =
42          ref.hash && ref.pathname === '/'
43            ? `${path.basename(file.history[0])}${ref.hash}`
44            : node.url;
45
46        // resolve the absolute path to the linked md file (supports hashes)
47        const absolute = path.resolve(path.dirname(file.history[0]), oldUrl);
48        // resolve the relative path between the linked file and our pages dir (root of the website)
49        const relative = path.relative(path.join(file.cwd, settings.pagesDir), absolute);
50        // rewrite the URL without the `.md` extension, using trailing slash or nothing
51        let newUrl = relative.replace(`.${settings.extension}`, settings.trailingSlash ? '/' : '');
52
53        // if the url is referencing the ignored index, remove it
54        if (newUrl.includes(ignoredIndex)) {
55          newUrl = newUrl.replace(ignoredIndex, '');
56        }
57
58        node.url = `/${newUrl}`;
59      }
60    });
61  };
62};
63