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 +191 -5
- package/dist/esm-parser.d.ts.map +1 -1
- package/dist/esm-parser.js +2 -0
- package/dist/esm-parser.js.map +1 -1
- package/dist/esm-parser.test.js +179 -1
- package/dist/esm-parser.test.js.map +1 -1
- package/dist/parse.d.ts +44 -0
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +127 -0
- package/dist/parse.js.map +1 -1
- package/dist/safe-mdx.d.ts +29 -1
- package/dist/safe-mdx.d.ts.map +1 -1
- package/dist/safe-mdx.js +81 -10
- package/dist/safe-mdx.js.map +1 -1
- package/dist/safe-mdx.test.js +179 -0
- package/dist/safe-mdx.test.js.map +1 -1
- package/package.json +1 -1
- package/src/esm-parser.test.ts +192 -1
- package/src/esm-parser.ts +2 -0
- package/src/parse.ts +165 -0
- package/src/safe-mdx.test.tsx +190 -1
- package/src/safe-mdx.tsx +104 -8
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`
|
|
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
|
-
|
|
295
|
-
|
|
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 `
|
|
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.
|
package/dist/esm-parser.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/esm-parser.js
CHANGED
|
@@ -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
|
});
|
package/dist/esm-parser.js.map
CHANGED
|
@@ -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"}
|
package/dist/esm-parser.test.js
CHANGED
|
@@ -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;
|
|
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
|
package/dist/parse.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|