diff --git a/src/index.ts b/src/index.ts index 84f92c5..700d739 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ +import { requireMdx } from './resolveImport' import { remarkMdxImport } from './remarkMdxImport' import { stopService, transform } from './transform' import { MdxOptions, MdxPlugin, RemarkPlugin } from './types' -import mdx from '@mdx-js/mdx' import fs from 'fs' export { MdxOptions, MdxPlugin } @@ -55,7 +55,7 @@ function createPlugin( getMdxOptions?.(filePath).remarkPlugins ) remarkPlugins.push(mdxImportPlugin) - return mdx.createMdxAstCompiler({ + return requireMdx(root).createMdxAstCompiler({ remarkPlugins: remarkPlugins as any[] }) } diff --git a/src/resolveImport.ts b/src/resolveImport.ts new file mode 100644 index 0000000..56fbb57 --- /dev/null +++ b/src/resolveImport.ts @@ -0,0 +1,40 @@ +import findDependency from 'find-dependency' + +const importCache: { + [cacheKey: string]: string | undefined +} = {} + +type MdxModule = typeof import('@mdx-js/mdx') + +export function requireMdx(cwd: string): MdxModule { + return require(resolveImport('@mdx-js/mdx', cwd) || '@mdx-js/mdx') +} + +/** + * Search the node_modules of `cwd` and its ancestors until a package is found. + * Skip global `node_modules` and `vite/node_modules` (since `vite` might be + * a local clone). + */ +export function resolveImport( + name: string, + cwd: string, + throwOnMissing?: boolean +) { + const cacheKey = cwd + '\0' + name + if (!importCache[cacheKey]) { + const resolved = findDependency(name, { cwd, skipGlobal: true }) + if (throwOnMissing && !resolved) { + throw new Error(`[vite-plugin-mdx] "{name}" must be installed`) + } + importCache[cacheKey] = resolved + } + return importCache[cacheKey] +} + +/** + * Throw an error if the given `name` cannot be found from `cwd`. + * Otherwise, return the `name`. + */ +export function assertImportExists(name: string, cwd: string) { + return resolveImport(name, cwd, true) && name +} diff --git a/src/transform.ts b/src/transform.ts index 42cc551..ed7d936 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -1,18 +1,16 @@ import { startService, Service } from 'esbuild' -import mdx from '@mdx-js/mdx' -import findDependency from 'find-dependency' import { MdxOptions } from './types' +import { assertImportExists, requireMdx, resolveImport } from './resolveImport' export { transform } export { stopService } -const pluginName = 'vite-plugin-mdx' - async function transform( code_mdx: string, mdxOptions?: MdxOptions, root = __dirname ) { + const mdx = requireMdx(root) const code_jsx = await mdx(code_mdx, mdxOptions as any) const code_es2019 = await jsxToES2019(code_jsx) const code_final = injectImports(code_es2019, root) @@ -49,45 +47,25 @@ async function jsxToES2019(code_jsx: string) { } function injectImports(code_es2019: string, root: string) { - if (findPackage('preact', root)) { + if (resolveImport('preact', root)) { return [ `import { h } from 'preact'`, - `import { mdx } from '${getMdxImportPath('@mdx-js/preact', root)}'`, + `import { mdx } from '${assertImportExists('@mdx-js/preact', root)}'`, '', code_es2019 ].join('\n') } - if (findPackage('react', root)) { + if (resolveImport('react', root)) { return [ `import React from 'react'`, - `import { mdx } from '${getMdxImportPath('@mdx-js/react', root)}'`, + `import { mdx } from '${assertImportExists('@mdx-js/react', root)}'`, '', code_es2019 ].join('\n') } - throw new Error( - `[Wrong Usage][${pluginName}] You need to \`npm install react\` or \`npm install preact\`.` - ) -} - -function getMdxImportPath(mdxPackageName: string, root: string): string { - const mdxPackageRoot = findPackage(mdxPackageName, root) - if (mdxPackageRoot) { - return mdxPackageName - } - throw new Error( - `[Wrong Usage][${pluginName}] You need to \`npm install ${mdxPackageName}\`.` - ) -} - -/** - * Search the node_modules of `cwd` and its ancestors until a package is found. - * Skip global node_modules and vite/node_modules (local clone might be used). - */ -function findPackage(name: string, cwd: string) { - return findDependency(name, { cwd, skipGlobal: true }) + throw new Error(`[vite-plugin-mdx] "react" or "preact" must be installed`) } let _service: Promise | undefined