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