safe-mdx 1.3.10 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -181,6 +181,80 @@ subpath, so enabling `allowClientEsmImports` does not need any extra prop.
181
181
 
182
182
  **Security Note**: ESM imports are disabled by default. Only enable `allowClientEsmImports` when you trust the MDX source, as it allows loading external code.
183
183
 
184
+ ## Server-side Module Resolution
185
+
186
+ Resolve MDX `import` statements against pre-loaded modules on the server — no client-side `eval` or ESM fetching needed. This is the recommended approach when your MDX files import local components.
187
+
188
+ **Simple case** — use Vite's `import.meta.glob` with `{ eager: true }` to load all modules upfront. The result is already the shape `modules` expects:
189
+
190
+ ```tsx
191
+ import { SafeMdxRenderer } from 'safe-mdx'
192
+ import { mdxParse } from 'safe-mdx/parse'
193
+
194
+ // { eager: true } returns the modules directly instead of lazy loaders:
195
+ // { './snippets/card.tsx': { Card, default: ... }, './snippets/badge.tsx': { Badge, ... } }
196
+ const modules = import.meta.glob('./snippets/**/*.tsx', { eager: true })
197
+
198
+ const code = `
199
+ import { Card } from '/snippets/card'
200
+ import { Badge } from '/snippets/badge'
201
+
202
+ # Hello
203
+
204
+ <Card title="Welcome">
205
+ Status: <Badge label="new" />
206
+ </Card>
207
+ `
208
+
209
+ export function Page() {
210
+ const mdast = mdxParse(code)
211
+ return (
212
+ <SafeMdxRenderer
213
+ markdown={code}
214
+ mdast={mdast}
215
+ modules={modules}
216
+ baseUrl="./pages/"
217
+ />
218
+ )
219
+ }
220
+ ```
221
+
222
+ **With Vite `import.meta.glob`** — use `resolveModules` to lazily load only the modules the MDX actually imports:
223
+
224
+ ```tsx
225
+ import { SafeMdxRenderer } from 'safe-mdx'
226
+ import { mdxParse, resolveModules } from 'safe-mdx/parse'
227
+
228
+ const code = `
229
+ import { Card } from '/snippets/card'
230
+
231
+ # Hello
232
+
233
+ <Card title="Welcome">content</Card>
234
+ `
235
+
236
+ export async function Page() {
237
+ const lazyGlob = import.meta.glob('./snippets/**/*.tsx')
238
+ const mdast = mdxParse(code)
239
+ const modules = await resolveModules({
240
+ glob: lazyGlob,
241
+ mdast,
242
+ baseUrl: './pages/',
243
+ })
244
+
245
+ return (
246
+ <SafeMdxRenderer
247
+ markdown={code}
248
+ mdast={mdast}
249
+ modules={modules}
250
+ baseUrl="./pages/"
251
+ />
252
+ )
253
+ }
254
+ ```
255
+
256
+ `baseUrl` is the directory of the MDX file being rendered — it's used to resolve relative imports like `./card` to the correct module key. If omitted it defaults to `'./'`.
257
+
184
258
  ## Change default MDX parser
185
259
 
186
260
  If you want to use custom MDX plugins, you can pass your own MDX processed ast.
@@ -280,20 +354,132 @@ Instead you can use `renderNode` to return some JSX for a specific mdast node:
280
354
  />
281
355
  ```
282
356
 
357
+ ## Validating component props
358
+
359
+ Use `componentPropsSchema` to validate component props against a schema. Works with any library that implements [Standard Schema](https://standardschema.dev) (Zod, Valibot, ArkType, etc).
360
+
361
+ Validation errors are collected in `visitor.errors` with line numbers and property paths. The component still renders with the invalid props, so you can show errors alongside the content.
362
+
363
+ ```tsx
364
+ import { MdastToJsx, type ComponentPropsSchema } from 'safe-mdx'
365
+ import { mdxParse } from 'safe-mdx/parse'
366
+ import { z } from 'zod'
367
+
368
+ const code = `
369
+ <Heading level={2} title="test">Valid heading</Heading>
370
+
371
+ <Heading level={10}>Invalid - level too high</Heading>
372
+
373
+ <Cards count={-1}>Invalid - negative count</Cards>
374
+ `
375
+
376
+ const componentPropsSchema: ComponentPropsSchema = {
377
+ Heading: z.object({
378
+ level: z.number().min(1).max(6),
379
+ title: z.string().optional(),
380
+ }),
381
+ Cards: z.object({
382
+ count: z.number().positive(),
383
+ variant: z.enum(['default', 'outline']).optional(),
384
+ }),
385
+ }
386
+
387
+ export function Page() {
388
+ const mdast = mdxParse(code)
389
+ const visitor = new MdastToJsx({
390
+ markdown: code,
391
+ mdast,
392
+ components: {
393
+ Heading: ({ children, ...props }) => <h1 {...props}>{children}</h1>,
394
+ Cards: ({ children, ...props }) => <div {...props}>{children}</div>,
395
+ },
396
+ componentPropsSchema,
397
+ })
398
+ const jsx = visitor.run()
399
+
400
+ if (visitor.errors.length) {
401
+ // errors include type, line number, component name, property path, and message
402
+ // [
403
+ // { type: 'validation', message: 'Invalid props for component "Heading" at "level": Too big...', line: 3, schemaPath: 'level' },
404
+ // { type: 'validation', message: 'Invalid props for component "Cards" at "count": Too small...', line: 5, schemaPath: 'count' },
405
+ // ]
406
+ }
407
+
408
+ return jsx
409
+ }
410
+ ```
411
+
283
412
  ## Handling errors
284
413
 
285
- `safe-mdx` ignores missing components or expressions, to show a message to the user in case of these errors you can use `MdastToJsx` directly
414
+ `safe-mdx` collects errors during rendering and exposes them via the `onError` callback or the `visitor.errors` array. Each error has a `type` field so you can filter by category.
415
+
416
+ ### Error types
417
+
418
+ Every error is a `SafeMdxError` object:
419
+
420
+ ```ts
421
+ interface SafeMdxError {
422
+ type: 'validation' | 'missing-component' | 'expression' | 'esm-import'
423
+ message: string
424
+ line?: number // source line in the MDX
425
+ schemaPath?: string // only for validation errors, e.g. "user.age"
426
+ }
427
+ ```
428
+
429
+ | Type | When it fires |
430
+ |---|---|
431
+ | `validation` | Component props fail schema validation (via `componentPropsSchema`) |
432
+ | `missing-component` | MDX uses a `<Component>` that wasn't passed in `components` or resolved from `modules` |
433
+ | `expression` | An MDX expression like `{1 + fn()}` or a JSX attribute expression fails to evaluate |
434
+ | `esm-import` | An ESM `import` has an invalid URL or fails to parse (only with `allowClientEsmImports`) |
435
+
436
+ ### Using `onError` callback
437
+
438
+ Works with both `SafeMdxRenderer` and `MdastToJsx`. Called for each error during rendering. Throw inside the callback to stop rendering on the first error.
439
+
440
+ ```tsx
441
+ import { SafeMdxRenderer } from 'safe-mdx'
442
+ import { mdxParse } from 'safe-mdx/parse'
443
+
444
+ export function Page() {
445
+ const mdast = mdxParse(code)
446
+ return (
447
+ <SafeMdxRenderer
448
+ markdown={code}
449
+ mdast={mdast}
450
+ components={components}
451
+ componentPropsSchema={componentPropsSchema}
452
+ onError={(error) => {
453
+ // only throw on schema validation errors
454
+ if (error.type === 'validation') {
455
+ throw new Error(
456
+ `Invalid props on line ${error.line}: ${error.message}`,
457
+ )
458
+ }
459
+ // log other errors without stopping rendering
460
+ console.warn(`[safe-mdx] ${error.type}: ${error.message}`)
461
+ }}
462
+ />
463
+ )
464
+ }
465
+ ```
466
+
467
+ ### Using `MdastToJsx` directly
468
+
469
+ Access the full errors array after rendering:
286
470
 
287
471
  ```tsx
288
472
  import { MdastToJsx } from 'safe-mdx'
473
+ import { mdxParse } from 'safe-mdx/parse'
289
474
 
290
475
  export function Page() {
476
+ const mdast = mdxParse(code)
291
477
  const visitor = new MdastToJsx({ markdown: code, mdast, components })
292
478
  const jsx = visitor.run()
293
479
 
294
- if (visitor.errors.length) {
295
- // handle errors here, like showing a message to the user for missing components
296
- }
480
+ // filter by type
481
+ const validationErrors = visitor.errors.filter(e => e.type === 'validation')
482
+ const missingComponents = visitor.errors.filter(e => e.type === 'missing-component')
297
483
 
298
484
  return jsx
299
485
  }
@@ -312,7 +498,7 @@ This is okay if you render your MDX in isolation from each tenant - for example
312
498
  These features are not supported yet:
313
499
 
314
500
  - Expressions that use methods or functions, currently expressions are evaluated with [eval-estree-expression](https://github.com/jonschlinkert/eval-estree-expression) with the functions option disabled.
315
- - Importing components or data from other files (unless `allowClientEsmImports` is enabled for `https://` imports).
501
+ - Importing components or data from other files (unless using `modules` prop for local imports or `allowClientEsmImports` for `https://` imports).
316
502
  - Exporting unresolved components or declaring components inline in the MDX
317
503
 
318
504
  **Note**: JSX components in attributes are now supported! You can use React components inside attributes like `<Card icon={<Icon />}>` without relying on JavaScript evaluation.
@@ -1 +1 @@
1
- {"version":3,"file":"esm-parser.d.ts","sourceRoot":"","sources":["../src/esm-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,MAAM,WAAW,YAAY;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;CACrB;AAcD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,IAAI,EAAE,GAAG,EACT,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACvC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAkDrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAMrG"}
1
+ {"version":3,"file":"esm-parser.d.ts","sourceRoot":"","sources":["../src/esm-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,MAAM,WAAW,YAAY;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;CACrB;AAcD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,IAAI,EAAE,GAAG,EACT,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACvC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAoDrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAMrG"}
@@ -29,6 +29,7 @@ export function parseEsmImports(node, onError) {
29
29
  // Validate URL
30
30
  if (!isValidHttpsUrl(importUrl)) {
31
31
  onError({
32
+ type: 'esm-import',
32
33
  message: `Invalid import URL: "${importUrl}". Only HTTPS URLs are allowed for security reasons.`,
33
34
  line: node.position?.start?.line,
34
35
  });
@@ -51,6 +52,7 @@ export function parseEsmImports(node, onError) {
51
52
  }
52
53
  catch (error) {
53
54
  onError({
55
+ type: 'esm-import',
54
56
  message: `Failed to parse ESM import: ${error instanceof Error ? error.message : 'Unknown error'}`,
55
57
  line: node.position?.start?.line,
56
58
  });
@@ -1 +1 @@
1
- {"version":3,"file":"esm-parser.js","sourceRoot":"","sources":["../src/esm-parser.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,SAAS,eAAe,CAAC,GAAW;IAChC,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAA;IAChB,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC3B,IAAS,EACT,OAAsC;IAEtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEzC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,OAAO,CAAA;IAClB,CAAC;IAED,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;QAEhC,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,IAAI,KAAK,mBAAmB;gBACtC,SAAS,CAAC,MAAM,CAAC,KAAK;gBACtB,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAE7C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAA;gBAExC,eAAe;gBACf,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC9B,OAAO,CAAC;wBACJ,OAAO,EAAE,wBAAwB,SAAS,sDAAsD;wBAChG,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI;qBACnC,CAAC,CAAA;oBACF,SAAQ;gBACZ,CAAC;gBAED,4BAA4B;gBAC5B,KAAK,MAAM,SAAS,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;oBAC3C,IAAI,SAAS,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;wBAC9C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;oBAChD,CAAC;yBAAM,IAAI,SAAS,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;wBAC9C,MAAM,YAAY,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;4BACzD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;4BACzB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;wBACtC,OAAO,CAAC,GAAG,CACP,SAAS,CAAC,KAAK,CAAC,IAAI,EACpB,GAAG,SAAS,IAAI,YAAY,EAAE,CACjC,CAAA;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC;YACJ,OAAO,EAAE,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE;YAClG,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI;SACnC,CAAC,CAAA;IACN,CAAC;IAED,OAAO,OAAO,CAAA;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACnD,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACvD,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;QACvB,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAE7B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AACvC,CAAC"}
1
+ {"version":3,"file":"esm-parser.js","sourceRoot":"","sources":["../src/esm-parser.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,SAAS,eAAe,CAAC,GAAW;IAChC,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAA;IAChB,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC3B,IAAS,EACT,OAAsC;IAEtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEzC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,OAAO,CAAA;IAClB,CAAC;IAED,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;QAEhC,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,IAAI,KAAK,mBAAmB;gBACtC,SAAS,CAAC,MAAM,CAAC,KAAK;gBACtB,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAE7C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAA;gBAExC,eAAe;gBACf,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC9B,OAAO,CAAC;wBACJ,IAAI,EAAE,YAAY;wBAClB,OAAO,EAAE,wBAAwB,SAAS,sDAAsD;wBAChG,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI;qBACnC,CAAC,CAAA;oBACF,SAAQ;gBACZ,CAAC;gBAED,4BAA4B;gBAC5B,KAAK,MAAM,SAAS,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;oBAC3C,IAAI,SAAS,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;wBAC9C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;oBAChD,CAAC;yBAAM,IAAI,SAAS,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;wBAC9C,MAAM,YAAY,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;4BACzD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;4BACzB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;wBACtC,OAAO,CAAC,GAAG,CACP,SAAS,CAAC,KAAK,CAAC,IAAI,EACpB,GAAG,SAAS,IAAI,YAAY,EAAE,CACjC,CAAA;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC;YACJ,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE;YAClG,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI;SACnC,CAAC,CAAA;IACN,CAAC;IAED,OAAO,OAAO,CAAA;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACnD,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACvD,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;QACvB,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAE7B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AACvC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { expect, test, describe } from 'vitest';
2
2
  import { parseEsmImports, extractComponentInfo } from './esm-parser.js';
3
- import { mdxParse } from './parse.js';
3
+ import { mdxParse, extractImports, resolveModulePath } from './parse.js';
4
4
  describe('parseEsmImports', () => {
5
5
  test('parses default imports from HTTPS URLs', () => {
6
6
  const code = `import MyComponent from 'https://esm.sh/some-component'`;
@@ -57,14 +57,17 @@ import Component3 from './relative/path'
57
57
  {
58
58
  "line": 2,
59
59
  "message": "Invalid import URL: "http://insecure.com/component". Only HTTPS URLs are allowed for security reasons.",
60
+ "type": "esm-import",
60
61
  },
61
62
  {
62
63
  "line": 2,
63
64
  "message": "Invalid import URL: "file:///local/path". Only HTTPS URLs are allowed for security reasons.",
65
+ "type": "esm-import",
64
66
  },
65
67
  {
66
68
  "line": 2,
67
69
  "message": "Invalid import URL: "./relative/path". Only HTTPS URLs are allowed for security reasons.",
70
+ "type": "esm-import",
68
71
  },
69
72
  ]
70
73
  `);
@@ -101,6 +104,181 @@ import Component3 from './relative/path'
101
104
  expect(errors).toMatchInlineSnapshot(`[]`);
102
105
  });
103
106
  });
107
+ describe('extractImports', () => {
108
+ test('extracts named, default, and namespace imports', () => {
109
+ const code = `import { Card } from './components/card'
110
+ import MyButton from '../ui/button'
111
+ import * as Utils from './utils'
112
+
113
+ # Hello
114
+ `;
115
+ const ast = mdxParse(code);
116
+ const imports = extractImports(ast);
117
+ expect(imports).toMatchInlineSnapshot(`
118
+ [
119
+ {
120
+ "source": "./components/card",
121
+ "specifiers": [
122
+ {
123
+ "imported": "Card",
124
+ "local": "Card",
125
+ "type": "named",
126
+ },
127
+ ],
128
+ },
129
+ {
130
+ "source": "../ui/button",
131
+ "specifiers": [
132
+ {
133
+ "imported": "default",
134
+ "local": "MyButton",
135
+ "type": "default",
136
+ },
137
+ ],
138
+ },
139
+ {
140
+ "source": "./utils",
141
+ "specifiers": [
142
+ {
143
+ "imported": "*",
144
+ "local": "Utils",
145
+ "type": "namespace",
146
+ },
147
+ ],
148
+ },
149
+ ]
150
+ `);
151
+ });
152
+ test('extracts npm package imports', () => {
153
+ const code = `import { Button } from 'some-ui-lib'
154
+ import React from 'react'
155
+ `;
156
+ const ast = mdxParse(code);
157
+ const imports = extractImports(ast);
158
+ expect(imports).toMatchInlineSnapshot(`
159
+ [
160
+ {
161
+ "source": "some-ui-lib",
162
+ "specifiers": [
163
+ {
164
+ "imported": "Button",
165
+ "local": "Button",
166
+ "type": "named",
167
+ },
168
+ ],
169
+ },
170
+ {
171
+ "source": "react",
172
+ "specifiers": [
173
+ {
174
+ "imported": "default",
175
+ "local": "React",
176
+ "type": "default",
177
+ },
178
+ ],
179
+ },
180
+ ]
181
+ `);
182
+ });
183
+ test('extracts absolute path imports (Mintlify style)', () => {
184
+ const code = `import Greeting from "/snippets/greeting.mdx"
185
+ import { Badge } from "/components/badge"
186
+ `;
187
+ const ast = mdxParse(code);
188
+ const imports = extractImports(ast);
189
+ expect(imports).toMatchInlineSnapshot(`
190
+ [
191
+ {
192
+ "source": "/snippets/greeting.mdx",
193
+ "specifiers": [
194
+ {
195
+ "imported": "default",
196
+ "local": "Greeting",
197
+ "type": "default",
198
+ },
199
+ ],
200
+ },
201
+ {
202
+ "source": "/components/badge",
203
+ "specifiers": [
204
+ {
205
+ "imported": "Badge",
206
+ "local": "Badge",
207
+ "type": "named",
208
+ },
209
+ ],
210
+ },
211
+ ]
212
+ `);
213
+ });
214
+ test('returns empty array when no imports', () => {
215
+ const code = `# Just a heading\n\nSome text.`;
216
+ const ast = mdxParse(code);
217
+ expect(extractImports(ast)).toMatchInlineSnapshot(`[]`);
218
+ });
219
+ test('handles aliased imports', () => {
220
+ const code = `import { Card as MyCard, Badge as B } from './ui'`;
221
+ const ast = mdxParse(code);
222
+ const imports = extractImports(ast);
223
+ expect(imports).toMatchInlineSnapshot(`
224
+ [
225
+ {
226
+ "source": "./ui",
227
+ "specifiers": [
228
+ {
229
+ "imported": "Card",
230
+ "local": "MyCard",
231
+ "type": "named",
232
+ },
233
+ {
234
+ "imported": "Badge",
235
+ "local": "B",
236
+ "type": "named",
237
+ },
238
+ ],
239
+ },
240
+ ]
241
+ `);
242
+ });
243
+ });
244
+ describe('resolveModulePath', () => {
245
+ const keys = [
246
+ './snippets/card.tsx',
247
+ './snippets/badge.ts',
248
+ './components/ui/index.tsx',
249
+ './pages/api/helpers.ts',
250
+ './pages/card.tsx',
251
+ './snippets/greeting.mdx',
252
+ ];
253
+ test('resolves absolute import /snippets/card', () => {
254
+ expect(resolveModulePath('/snippets/card', './pages/', keys))
255
+ .toMatchInlineSnapshot(`"./snippets/card.tsx"`);
256
+ });
257
+ test('resolves relative import ./card from ./pages/', () => {
258
+ expect(resolveModulePath('./card', './pages/', keys))
259
+ .toMatchInlineSnapshot(`"./pages/card.tsx"`);
260
+ });
261
+ test('resolves index.tsx', () => {
262
+ expect(resolveModulePath('/components/ui', './pages/', keys))
263
+ .toMatchInlineSnapshot(`"./components/ui/index.tsx"`);
264
+ });
265
+ test('resolves relative ../ path', () => {
266
+ expect(resolveModulePath('../snippets/greeting', './pages/api/', keys))
267
+ .toMatchInlineSnapshot(`undefined`);
268
+ });
269
+ test('resolves ../../ to reach project root', () => {
270
+ expect(resolveModulePath('../../snippets/greeting', './pages/api/', keys))
271
+ .toMatchInlineSnapshot(`"./snippets/greeting.mdx"`);
272
+ });
273
+ test('returns undefined for bare specifiers', () => {
274
+ expect(resolveModulePath('react', './pages/', keys))
275
+ .toMatchInlineSnapshot(`undefined`);
276
+ });
277
+ test('returns undefined for missing files', () => {
278
+ expect(resolveModulePath('./nonexistent', './pages/', keys))
279
+ .toMatchInlineSnapshot(`undefined`);
280
+ });
281
+ });
104
282
  describe('extractComponentInfo', () => {
105
283
  test('extracts default import info', () => {
106
284
  const result = extractComponentInfo('https://esm.sh/component');
@@ -1 +1 @@
1
- {"version":3,"file":"esm-parser.test.js","sourceRoot":"","sources":["../src/esm-parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGrC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,yDAAyD,CAAA;QACtE,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,MAAM,GAAmB,EAAE,CAAA;QAEjC,yBAAyB;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;QAC5E,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAEnE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;SAO3D,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,IAAI,GAAG,oEAAoE,CAAA;QACjF,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,MAAM,GAAmB,EAAE,CAAA;QAEjC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;QAC5E,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAEnE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;SAW3D,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,IAAI,GAAG;;;;CAIpB,CAAA;QACO,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,MAAM,GAAmB,EAAE,CAAA;QAEjC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAS,EAAE,EAAE;YACjC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC3B,eAAe,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;YACpD,CAAC;QACL,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;SAepC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,IAAI,GAAG,iFAAiF,CAAA;QAC9F,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,MAAM,GAAmB,EAAE,CAAA;QAEjC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;QAC5E,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAEnE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;SAe3D,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAmB,EAAE,CAAA;QACjC,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAA;QAEnE,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAEhE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,oBAAoB,CAAC,0BAA0B,CAAC,CAAA;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC;;;;;SAKpC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,oBAAoB,CAAC,kCAAkC,CAAC,CAAA;QACvE,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC;;;;;SAKpC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"esm-parser.test.js","sourceRoot":"","sources":["../src/esm-parser.test.ts"],"names":[],"mappings":"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"}
package/dist/parse.d.ts CHANGED
@@ -1,6 +1,23 @@
1
1
  import { Root } from 'mdast';
2
2
  import { parseHtmlToMdxAst, remarkMdxJsxNormalize } from './html/html-to-mdx-ast.js';
3
3
  export { parseHtmlToMdxAst, remarkMdxJsxNormalize };
4
+ export type MdxImportSpecifier = {
5
+ /** Name used in MDX (e.g. `Card`, `MyButton`, `Utils`) */
6
+ local: string;
7
+ /** Original export name. `'default'` for default imports, local name for named, `'*'` for namespace */
8
+ imported: string;
9
+ type: 'named' | 'default' | 'namespace';
10
+ };
11
+ export type MdxImport = {
12
+ /** Raw source string as written: `'./card'`, `'/snippets/ui'`, `'some-pkg'` */
13
+ source: string;
14
+ specifiers: MdxImportSpecifier[];
15
+ };
16
+ /**
17
+ * Extract all import declarations from a parsed mdast tree.
18
+ * Unlike `parseEsmImports`, this accepts ANY source (not just HTTPS URLs).
19
+ */
20
+ export declare function extractImports(ast: Root): MdxImport[];
4
21
  export declare function mdxParse(code: string): Root;
5
22
  /**
6
23
  * https://github.com/mdx-js/mdx/blob/b3351fadcb6f78833a72757b7135dcfb8ab646fe/packages/mdx/lib/plugin/remark-mark-and-unravel.js
@@ -12,4 +29,31 @@ export declare function mdxParse(code: string): Root;
12
29
  *
13
30
  */
14
31
  export declare function remarkMarkAndUnravel(): (tree: Root) => void;
32
+ /**
33
+ * Given an import source and a baseUrl, resolve the source to a key
34
+ * that exists in `moduleKeys`. Handles:
35
+ * - Relative imports (`./card`) resolved from `baseUrl`
36
+ * - Absolute imports (`/snippets/card`) normalized to `./snippets/card`
37
+ * - Extension resolution (tries .tsx, .ts, .jsx, .js, .mdx, .md, /index.*)
38
+ */
39
+ export declare function resolveModulePath(source: string, baseUrl: string, moduleKeys: string[]): string | undefined;
40
+ export type LazyGlob = Record<string, () => Promise<Record<string, any>>>;
41
+ export type EagerModules = Record<string, Record<string, any>>;
42
+ /**
43
+ * Given a lazy Vite glob and a parsed mdast, resolve only the imported
44
+ * modules eagerly. Returns the exact shape `SafeMdxRenderer.modules` expects.
45
+ *
46
+ * Usage:
47
+ * ```ts
48
+ * const lazyGlob = import.meta.glob('./snippets/*.tsx')
49
+ * const mdast = mdxParse(mdxString)
50
+ * const modules = await resolveModules({ glob: lazyGlob, mdast, baseUrl: './pages/' })
51
+ * <SafeMdxRenderer modules={modules} baseUrl="./pages/" ... />
52
+ * ```
53
+ */
54
+ export declare function resolveModules({ glob, mdast, baseUrl, }: {
55
+ glob: LazyGlob;
56
+ mdast: Root;
57
+ baseUrl: string;
58
+ }): Promise<EagerModules>;
15
59
  //# sourceMappingURL=parse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAe,MAAM,OAAO,CAAA;AAIzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AAEpF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;AAEnD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAET,IAAI,CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,KACf,MAAM,IAAI,UAqE9B"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAe,MAAM,OAAO,CAAA;AAIzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AAEpF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;AAInD,MAAM,MAAM,kBAAkB,GAAG;IAC7B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACpB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,kBAAkB,EAAE,CAAA;CACnC,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,SAAS,EAAE,CAkCrD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAET,IAAI,CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,KACf,MAAM,IAAI,UAqE9B;AAUD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAAE,GACrB,MAAM,GAAG,SAAS,CAyBpB;AAsBD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;AACzE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;AAE9D;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,EACjC,IAAI,EACJ,KAAK,EACL,OAAO,GACV,EAAE;IACC,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,IAAI,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CAClB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkBxB"}