Skip to content

Commit

Permalink
fix Module not found: Can't resolve 'nextra-theme-docs/style.css' f…
Browse files Browse the repository at this point in the history
…or imported markdown files that located outside of CWD (#1332)

* fix

* update snapshots

* Update packages/nextra/src/compile.ts

Co-authored-by: Shu Ding <g@shud.in>

Co-authored-by: Shu Ding <g@shud.in>
  • Loading branch information
dimaMachina and shuding committed Jan 21, 2023
1 parent 624d6b4 commit 4b2052f
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 65 deletions.
6 changes: 6 additions & 0 deletions .changeset/modern-rings-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'nextra': patch
'nextra-theme-docs': patch
---

fix `Module not found: Can't resolve 'nextra-theme-docs/style.css'` for imported markdown files that located outside of CWD
10 changes: 5 additions & 5 deletions packages/nextra/__test__/__snapshots__/compile.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function MDXContent(props = {}) {
const {wrapper: MDXLayout} = Object.assign({}, _provideComponents(), props.components);
return MDXLayout ? <MDXLayout {...props}><_createMdxContent {...props} /></MDXLayout> : _createMdxContent(props);
}
export default MDXContent;
",
"searchIndexKey": null,
"structurizedData": {},
Expand Down Expand Up @@ -54,7 +54,7 @@ function MDXContent(props = {}) {
const {wrapper: MDXLayout} = Object.assign({}, _provideComponents(), props.components);
return MDXLayout ? <MDXLayout {...props}><_createMdxContent {...props} /></MDXLayout> : _createMdxContent(props);
}
export default MDXContent;
",
"searchIndexKey": null,
"structurizedData": {},
Expand Down Expand Up @@ -90,7 +90,7 @@ function MDXContent(props = {}) {
const {wrapper: MDXLayout} = Object.assign({}, _provideComponents(), props.components);
return MDXLayout ? <MDXLayout {...props}><_createMdxContent {...props} /></MDXLayout> : _createMdxContent(props);
}
export default MDXContent;
",
"searchIndexKey": null,
"structurizedData": {},
Expand Down Expand Up @@ -120,7 +120,7 @@ function MDXContent(props = {}) {
const {wrapper: MDXLayout} = Object.assign({}, _provideComponents(), props.components);
return MDXLayout ? <MDXLayout {...props}><_createMdxContent {...props} /></MDXLayout> : _createMdxContent(props);
}
export default MDXContent;
",
"searchIndexKey": null,
"structurizedData": {},
Expand Down Expand Up @@ -149,7 +149,7 @@ function MDXContent(props = {}) {
const {wrapper: MDXLayout} = Object.assign({}, _provideComponents(), props.components);
return MDXLayout ? <MDXLayout {...props}><_createMdxContent {...props} /></MDXLayout> : _createMdxContent(props);
}
export default MDXContent;
",
"searchIndexKey": null,
"structurizedData": {},
Expand Down
23 changes: 17 additions & 6 deletions packages/nextra/src/compile.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from 'node:path'
import { createRequire } from 'node:module'
import { createProcessor, ProcessorOptions } from '@mdx-js/mdx'
import { Processor } from '@mdx-js/mdx/lib/core'
import remarkGfm from 'remark-gfm'
Expand All @@ -8,6 +10,7 @@ import grayMatter from 'gray-matter'
import {
remarkStaticImage,
remarkHeadings,
remarkReplaceImports,
structurize,
parseMeta,
attachMeta
Expand All @@ -17,12 +20,14 @@ import theme from './theme.json'
import { truthy } from './utils'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import { CODE_BLOCK_FILENAME_REGEX, DEFAULT_LOCALE } from './constants'
import { CODE_BLOCK_FILENAME_REGEX, CWD, DEFAULT_LOCALE } from './constants'

globalThis.__nextra_temp_do_not_use = () => {
import('./__temp__')
}

const require = createRequire(import.meta.url)

const DEFAULT_REHYPE_PRETTY_CODE_OPTIONS = {
theme,
onVisitLine(node: any) {
Expand Down Expand Up @@ -61,8 +66,7 @@ export async function compileMdx(
| 'latex'
| 'codeHighlight'
> & { mdxOptions?: MdxOptions; route?: string; locale?: string } = {},
filePath = '',
useCachedCompiler = false
{ filePath = '', useCachedCompiler = false, isPageImport = true } = {}
) {
// Extract frontMatter information if it exists
const { data: frontMatter, content } = grayMatter(source)
Expand Down Expand Up @@ -103,13 +107,19 @@ export async function compileMdx(
const format =
_format === 'detect' ? (filePath.endsWith('.mdx') ? 'mdx' : 'md') : _format

// https://github.com/shuding/nextra/issues/1303
const isFileOutsideCWD =
!isPageImport && path.relative(CWD, filePath).startsWith('..')

const compiler =
(useCachedCompiler && cachedCompilerForFormat[format]) ||
(cachedCompilerForFormat[format] = createProcessor({
jsx,
format,
outputFormat,
providerImportSource: 'nextra/mdx',
providerImportSource: isFileOutsideCWD
? require.resolve('nextra').replace(/index\.js$/, 'mdx.mjs') // fixes Package subpath './mdx' is not defined by "exports"
: 'nextra/mdx',
remarkPlugins: [
...remarkPlugins,
remarkGfm,
Expand All @@ -118,7 +128,8 @@ export async function compileMdx(
searchIndexKey !== null &&
structurize(structurizedData, loaderOptions.flexsearch),
loaderOptions.readingTime && readingTime,
loaderOptions.latex && remarkMath
loaderOptions.latex && remarkMath,
isFileOutsideCWD && remarkReplaceImports
].filter(truthy),
rehypePlugins: [
...rehypePlugins,
Expand Down Expand Up @@ -149,7 +160,7 @@ export async function compileMdx(
const readingTime = vFile.data.readingTime as ReadingTime | undefined

return {
result: String(vFile).replace('export default MDXContent;', ''),
result: String(vFile),
...headingMeta,
...(readingTime && { readingTime }),
structurizedData,
Expand Down
4 changes: 2 additions & 2 deletions packages/nextra/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
loader: 'nextra/loader',
options: {
...nextraLoaderOptions,
pageImport: true
isPageImport: true
}
}
]
Expand All @@ -123,7 +123,7 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
{
loader: 'nextra/loader',
options: {
metaImport: true
isMetaImport: true
}
}
]
Expand Down
28 changes: 15 additions & 13 deletions packages/nextra/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ async function loader(
source: string
): Promise<string> {
const {
metaImport,
pageImport,
isMetaImport = false,
isPageImport = false,
theme,
themeConfig,
locales,
Expand All @@ -77,7 +77,7 @@ async function loader(
context.cacheable(true)

// _meta.js used as a page.
if (metaImport) {
if (isMetaImport) {
return 'export default () => null'
}

Expand Down Expand Up @@ -160,8 +160,11 @@ async function loader(
route: pageNextRoute,
locale
},
mdxPath,
false // TODO: produce hydration errors or error - Create a new processor first, by calling it: use `processor()` instead of `processor`.
{
filePath: mdxPath,
useCachedCompiler: false, // TODO: produce hydration errors or error - Create a new processor first, by calling it: use `processor()` instead of `processor`.
isPageImport
}
)

const katexCssImport = latex ? "import 'katex/dist/katex.min.css'" : ''
Expand All @@ -170,10 +173,8 @@ async function loader(
: ''

// Imported as a normal component, no need to add the layout.
if (!pageImport) {
return `${cssImport}
${result}
export default MDXContent`
if (!isPageImport) {
return result
}

const { route, pageMap, dynamicMetaItems } = resolvePageMap({
Expand Down Expand Up @@ -255,7 +256,10 @@ ${themeConfigImport}
${katexCssImport}
${cssImport}
${finalResult}
${finalResult.replace(
'export default MDXContent;',
"export { default } from 'nextra/layout'"
)}
setupNextraPage({
pageNextRoute: ${JSON.stringify(pageNextRoute)},
Expand All @@ -281,9 +285,7 @@ setupNextraPage({
)
.join(',')
}]
})
export { default } from 'nextra/layout'`
})`
}

export default function syncLoader(
Expand Down
1 change: 1 addition & 0 deletions packages/nextra/src/mdx-plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { parseMeta, attachMeta } from './rehype'
export { remarkHeadings } from './remark-headings'
export { remarkReplaceImports } from './remark-replace-imports'
export { remarkStaticImage } from './static-image'
export { structurize } from './structurize'
24 changes: 24 additions & 0 deletions packages/nextra/src/mdx-plugins/remark-replace-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createRequire } from 'node:module'
import { visit } from 'unist-util-visit'
import { Plugin } from 'unified'
import { Root } from 'mdast'

const require = createRequire(import.meta.url)

/**
* Markdown files that imported outside CWD could not find `nextra/mdx` or `next/image`
* to fix it we need to replace import path with absolute for them
* https://github.com/shuding/nextra/issues/1303
*/
export const remarkReplaceImports: Plugin<[], Root> = () => {
return (tree, _file, done) => {
visit(tree, 'mdxjsEsm', (node: any) => {
const { source } = node.data.estree.body[0]
const absolutePath = require.resolve(source.value)

source.value = absolutePath
source.raw = `"${absolutePath}"`
})
done()
}
}
4 changes: 2 additions & 2 deletions packages/nextra/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ type MetaFilename = typeof META_FILENAME
type MarkdownExtension = (typeof MARKDOWN_EXTENSIONS)[number]

export interface LoaderOptions extends NextraConfig {
metaImport?: boolean
pageImport?: boolean
isMetaImport?: boolean
isPageImport?: boolean
locales: string[]
defaultLocale: string
pageMapCache: PageMapCache
Expand Down
90 changes: 53 additions & 37 deletions packages/nextra/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,45 @@ import tsconfig from './tsconfig.json'

const { target } = tsconfig.compilerOptions

const esmOptions = defineConfig({
format: 'esm',
dts: true,
target,
bundle: true,
splitting: false,
external: ['shiki', './__temp__', 'webpack'],
esbuildPlugins: [
// https://github.com/evanw/esbuild/issues/622#issuecomment-769462611
{
name: 'add-mjs',
setup(build) {
build.onResolve({ filter: /.*/ }, async args => {
if (
args.importer &&
args.path.startsWith('.') &&
!args.path.endsWith('.json')
) {
let isDir: boolean
try {
isDir = (
await fs.stat(path.join(args.resolveDir, args.path))
).isDirectory()
} catch {
isDir = false
}

if (isDir) {
// it's a directory
return { path: args.path + '/index.mjs', external: true }
}
return { path: args.path + '.mjs', external: true }
}
})
}
}
]
})

export default defineConfig([
{
name: 'nextra',
Expand All @@ -16,43 +55,20 @@ export default defineConfig([
},
{
name: 'nextra-esm',
entry: ['src/**/*.ts', 'src/**/*.tsx'],
format: 'esm',
dts: true,
target,
bundle: true,
splitting: false,
external: ['shiki', './__temp__', 'webpack'],
esbuildPlugins: [
// https://github.com/evanw/esbuild/issues/622#issuecomment-769462611
{
name: 'add-mjs',
setup(build) {
build.onResolve({ filter: /.*/ }, async args => {
if (
args.importer &&
args.path.startsWith('.') &&
!args.path.endsWith('.json')
) {
let isDir: boolean
try {
isDir = (
await fs.stat(path.join(args.resolveDir, args.path))
).isDirectory()
} catch {
isDir = false
}

if (isDir) {
// it's a directory
return { path: args.path + '/index.mjs', external: true }
}
return { path: args.path + '.mjs', external: true }
}
})
}
}
]
entry: [
'src/**/*.ts',
'src/**/*.tsx',
'!src/compile.ts',
'!src/mdx-plugins/remark-replace-imports.ts'
],
...esmOptions
},
{
name: 'nextra-esm-import.meta',
entry: ['src/compile.ts', 'src/mdx-plugins/remark-replace-imports.ts'],
...esmOptions,
// import.meta is available only from es2020
target: 'es2020'
},
{
entry: ['src/types.ts'],
Expand Down

1 comment on commit 4b2052f

@vercel
Copy link

@vercel vercel bot commented on 4b2052f Jan 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.