mdx-bundler
Version:
Compile and bundle your MDX files and their dependencies. FAST.
234 lines (190 loc) • 7.8 kB
JavaScript
;
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");
}