UNPKG

mdx-bundler

Version:

Compile and bundle your MDX files and their dependencies. FAST.

234 lines (190 loc) 7.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.bundleMDX = bundleMDX; var _fs = _interopRequireDefault(require("fs")); var _path = _interopRequireDefault(require("path")); var _string_decoder = require("string_decoder"); var _remarkFrontmatter = _interopRequireDefault(require("remark-frontmatter")); var _remarkMdxFrontmatter = require("remark-mdx-frontmatter"); var _grayMatter = _interopRequireDefault(require("gray-matter")); var esbuild = _interopRequireWildcard(require("esbuild")); var _nodeResolve = require("@esbuild-plugins/node-resolve"); var _esbuildPluginGlobalExternals = require("@fal-works/esbuild-plugin-global-externals"); var _uuid = require("uuid"); var _dirnameMessedUp = _interopRequireDefault(require("./dirname-messed-up.cjs")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const { readFile, unlink } = _fs.default.promises; /** * * @param {string} mdxSource - A string of mdx source code * @param {import('./types').BundleMDXOptions} options * @returns */ async function bundleMDX(mdxSource, { files = {}, xdmOptions = options => options, esbuildOptions = options => options, globals = {}, cwd = _path.default.join(process.cwd(), `__mdx_bundler_fake_dir__`), grayMatterOptions = options => options } = {}) { /* c8 ignore start */ if (_dirnameMessedUp.default && !process.env.ESBUILD_BINARY_PATH) { console.warn(`mdx-bundler warning: esbuild maybe unable to find its binary, if your build fails you'll need to set ESBUILD_BINARY_PATH. Learn more: https://github.com/kentcdodds/mdx-bundler/blob/main/README.md#nextjs-esbuild-enoent`); } /* c8 ignore stop */ // xdm is a native ESM, and we're running in a CJS context. This is the // only way to import ESM within CJS const [{ default: xdmESBuild }] = await Promise.all([await import('xdm/esbuild.js')]); // extract the frontmatter const matter = (0, _grayMatter.default)(mdxSource, grayMatterOptions({})); const entryPath = _path.default.join(cwd, `./_mdx_bundler_entry_point-${(0, _uuid.v4)()}.mdx`); /** @type Record<string, string> */ const absoluteFiles = { [entryPath]: mdxSource }; for (const [filepath, fileCode] of Object.entries(files)) { absoluteFiles[_path.default.join(cwd, filepath)] = fileCode; } /** @type import('esbuild').Plugin */ const inMemoryPlugin = { name: 'inMemory', setup(build) { build.onResolve({ filter: /.*/ }, ({ path: filePath, importer }) => { if (filePath === entryPath) { return { path: filePath, pluginData: { inMemory: true, contents: absoluteFiles[filePath] } }; } const modulePath = _path.default.resolve(_path.default.dirname(importer), filePath); if (modulePath in absoluteFiles) { return { path: modulePath, pluginData: { inMemory: true, contents: absoluteFiles[modulePath] } }; } for (const ext of ['.js', '.ts', '.jsx', '.tsx', '.json', '.mdx']) { const fullModulePath = `${modulePath}${ext}`; if (fullModulePath in absoluteFiles) { return { path: fullModulePath, pluginData: { inMemory: true, contents: absoluteFiles[fullModulePath] } }; } } // Return an empty object so that esbuild will handle resolving the file itself. return {}; }); build.onLoad({ filter: /.*/ }, async ({ path: filePath, pluginData }) => { if (pluginData === undefined || !pluginData.inMemory) { // Return an empty object so that esbuild will load & parse the file contents itself. return null; } // the || .js allows people to exclude a file extension const fileType = (_path.default.extname(filePath) || '.jsx').slice(1); const contents = absoluteFiles[filePath]; if (fileType === 'mdx') return null; /** @type import('esbuild').Loader */ let loader; if (build.initialOptions.loader && build.initialOptions.loader[`.${fileType}`]) { loader = build.initialOptions.loader[`.${fileType}`]; } else { loader = fileType; } return { contents, loader }; }); } }; const buildOptions = esbuildOptions({ entryPoints: [entryPath], write: false, absWorkingDir: cwd, define: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }, plugins: [(0, _esbuildPluginGlobalExternals.globalExternals)({ ...globals, react: { varName: 'React', type: 'cjs' }, 'react-dom': { varName: 'ReactDOM', type: 'cjs' }, 'react/jsx-runtime': { varName: '_jsx_runtime', type: 'cjs' } }), // eslint-disable-next-line @babel/new-cap (0, _nodeResolve.NodeResolvePlugin)({ extensions: ['.js', '.ts', '.jsx', '.tsx'], resolveOptions: { basedir: cwd } }), inMemoryPlugin, xdmESBuild(xdmOptions({ remarkPlugins: [_remarkFrontmatter.default, [_remarkMdxFrontmatter.remarkMdxFrontmatter, { name: 'frontmatter' }]] }))], bundle: true, format: 'iife', globalName: 'Component', minify: true }); const bundled = await esbuild.build(buildOptions); if (bundled.outputFiles) { const decoder = new _string_decoder.StringDecoder('utf8'); const code = decoder.write(Buffer.from(bundled.outputFiles[0].contents)); return { code: `${code};return Component.default;`, frontmatter: matter.data, errors: bundled.errors, matter }; } if (buildOptions.outdir && buildOptions.write) { // We know that this has to be an array of entry point strings, with a single entry const entryFile = /** @type {{entryPoints: string[]}} */ buildOptions.entryPoints[0]; const fileName = _path.default.basename(entryFile).replace(/\.[^/.]+$/, '.js'); const code = await readFile(_path.default.join(buildOptions.outdir, fileName)); await unlink(_path.default.join(buildOptions.outdir, fileName)); return { code: `${code};return Component.default;`, frontmatter: matter.data, errors: bundled.errors, matter }; } throw new Error("You must either specify `write: false` or `write: true` and `outdir: '/path'` in your esbuild options"); }