diff --git a/plugins/vite/markdownImport.test.ts b/plugins/vite/markdownImport.test.ts new file mode 100644 index 0000000000..04252645b5 --- /dev/null +++ b/plugins/vite/markdownImport.test.ts @@ -0,0 +1,51 @@ +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import { describe, expect, it, vi } from 'vitest'; + +import { viteMarkdownImport } from './markdownImport'; + +describe('viteMarkdownImport', () => { + it('rewrites bare markdown imports into raw string modules', async () => { + const tempDir = await mkdtemp(join(tmpdir(), 'vite-markdown-import-')); + const markdownPath = join(tempDir, 'sample.md'); + const markdownContent = '# hello\n\nThis is markdown.\n'; + + await writeFile(markdownPath, markdownContent); + + const plugin = viteMarkdownImport(); + const resolve = vi.fn().mockResolvedValue({ id: markdownPath }); + + const resolved = await plugin.resolveId?.call( + { resolve } as never, + './sample.md', + join(tempDir, 'entry.ts'), + {}, + ); + + expect(resolved).toEqual({ + id: `${markdownPath}?lobe-md-import`, + moduleSideEffects: false, + }); + + const loaded = await plugin.load?.call({} as never, `${markdownPath}?lobe-md-import`); + + expect(loaded).toBe(`export default ${JSON.stringify(markdownContent)};`); + + await rm(tempDir, { force: true, recursive: true }); + }); + + it('leaves explicit markdown queries untouched', async () => { + const plugin = viteMarkdownImport(); + + const resolved = await plugin.resolveId?.call( + { resolve: vi.fn() } as never, + './sample.md?raw', + '/tmp/entry.ts', + {}, + ); + + expect(resolved).toBeNull(); + }); +}); diff --git a/plugins/vite/markdownImport.ts b/plugins/vite/markdownImport.ts new file mode 100644 index 0000000000..3dbe2888c2 --- /dev/null +++ b/plugins/vite/markdownImport.ts @@ -0,0 +1,52 @@ +import { readFile } from 'node:fs/promises'; + +import type { Plugin } from 'vite'; + +const MARKDOWN_IMPORT_QUERY = 'lobe-md-import'; + +function hasQuery(id: string) { + return id.includes('?'); +} + +function isMarkdownFile(id: string) { + return id.replace(/[?#].*$/, '').endsWith('.md'); +} + +function matchesMarkdownImportQuery(id: string) { + const query = id.split('?')[1]; + if (!query) return false; + + const params = new URLSearchParams(query); + + return params.has(MARKDOWN_IMPORT_QUERY); +} + +function withMarkdownImportQuery(id: string) { + return `${id}${hasQuery(id) ? '&' : '?'}${MARKDOWN_IMPORT_QUERY}`; +} + +export function viteMarkdownImport(): Plugin { + return { + enforce: 'pre', + async load(id) { + if (!matchesMarkdownImportQuery(id)) return null; + + const filePath = id.replace(/[?#].*$/, ''); + const content = await readFile(filePath, 'utf8'); + + return `export default ${JSON.stringify(content)};`; + }, + name: 'vite-markdown-import', + async resolveId(source, importer, options) { + if (!importer || hasQuery(source) || !isMarkdownFile(source)) return null; + + const resolved = await this.resolve(source, importer, { ...options, skipSelf: true }); + if (!resolved) return null; + + return { + id: withMarkdownImportQuery(resolved.id), + moduleSideEffects: false, + }; + }, + }; +} diff --git a/plugins/vite/sharedRendererConfig.ts b/plugins/vite/sharedRendererConfig.ts index c774c584e0..31fa999c14 100644 --- a/plugins/vite/sharedRendererConfig.ts +++ b/plugins/vite/sharedRendererConfig.ts @@ -4,6 +4,7 @@ import { nodePolyfills } from 'vite-plugin-node-polyfills'; import tsconfigPaths from 'vite-tsconfig-paths'; import { viteEmotionSpeedy } from './emotionSpeedy'; +import { viteMarkdownImport } from './markdownImport'; import { viteNodeModuleStub } from './nodeModuleStub'; import { vitePlatformResolve } from './platformResolve'; @@ -123,6 +124,7 @@ export function sharedRendererPlugins(options: SharedRendererOptions) { const defaultTsconfigPaths = options.tsconfigPaths ?? true; return [ viteEmotionSpeedy(), + viteMarkdownImport(), nodePolyfills({ include: ['buffer'] }), viteNodeModuleStub(), vitePlatformResolve(options.platform), diff --git a/src/vite.d.ts b/src/vite.d.ts index 821c6319fe..459c6add83 100644 --- a/src/vite.d.ts +++ b/src/vite.d.ts @@ -1,5 +1,10 @@ import 'vite/client'; +declare module '*.md' { + const content: string; + export default content; +} + declare module 'pdfjs-dist/build/pdf.worker.min.mjs?url' { const url: string; export default url;