safe-mdx 1.0.4 → 1.2.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 +112 -4
- package/dist/dynamic-esm-component.d.ts +6 -0
- package/dist/dynamic-esm-component.d.ts.map +1 -0
- package/dist/dynamic-esm-component.js +58 -0
- package/dist/dynamic-esm-component.js.map +1 -0
- package/dist/esm-parser.d.ts +19 -0
- package/dist/esm-parser.d.ts.map +1 -0
- package/dist/esm-parser.js +69 -0
- package/dist/esm-parser.js.map +1 -0
- package/dist/esm-parser.test.d.ts +2 -0
- package/dist/esm-parser.test.d.ts.map +1 -0
- package/dist/esm-parser.test.js +124 -0
- package/dist/esm-parser.test.js.map +1 -0
- package/dist/safe-mdx.bench.d.ts +2 -0
- package/dist/safe-mdx.bench.d.ts.map +1 -0
- package/dist/safe-mdx.bench.js +41 -0
- package/dist/safe-mdx.bench.js.map +1 -0
- package/dist/safe-mdx.d.ts +24 -7
- package/dist/safe-mdx.d.ts.map +1 -1
- package/dist/safe-mdx.js +351 -101
- package/dist/safe-mdx.js.map +1 -1
- package/dist/safe-mdx.test.js +770 -9
- package/dist/safe-mdx.test.js.map +1 -1
- package/package.json +6 -2
- package/src/dynamic-esm-component.tsx +85 -0
- package/src/esm-parser.test.ts +141 -0
- package/src/esm-parser.ts +89 -0
- package/src/safe-mdx.bench.tsx +52 -0
- package/src/safe-mdx.test.tsx +843 -10
- package/src/safe-mdx.tsx +490 -193
package/README.md
CHANGED
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
- Render MDX without `eval` on the server, so you can render MDX in Cloudflare Workers and Vercel Edge
|
|
14
14
|
- Works with React Server Components
|
|
15
15
|
- Supports custom MDX components
|
|
16
|
-
-
|
|
16
|
+
- custom `createElement`. Pass a no-op function to use safe-mdx as a validation step.
|
|
17
|
+
- use `componentPropsSchema` to validate component props against a schema (works with Zod, Valibot, etc).
|
|
18
|
+
- ESM `https://` imports support with `allowClientEsmImports` option (disabled by default for security)
|
|
19
|
+
- fast. 3ms to render the [full mdx document for Zod v3](https://github.com/colinhacks/zod/blob/0a49fa39348b7c72b19ddedc3b0f879bd395304b/packages/docs/content/packages/v3.mdx) (2500 lines)
|
|
17
20
|
|
|
18
21
|
## Why
|
|
19
22
|
|
|
@@ -76,6 +79,104 @@ export function Page() {
|
|
|
76
79
|
}
|
|
77
80
|
```
|
|
78
81
|
|
|
82
|
+
## JSX Components in Attributes
|
|
83
|
+
|
|
84
|
+
safe-mdx supports using JSX components inside component attributes, providing a secure alternative to JavaScript evaluation.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { SafeMdxRenderer } from 'safe-mdx'
|
|
88
|
+
import { mdxParse } from 'safe-mdx/parse'
|
|
89
|
+
|
|
90
|
+
const code = `
|
|
91
|
+
# Components in Attributes
|
|
92
|
+
|
|
93
|
+
<Card
|
|
94
|
+
icon={<Icon name="star" />}
|
|
95
|
+
actions={<Button variant="primary">Click me</Button>}
|
|
96
|
+
>
|
|
97
|
+
Card content with JSX components in attributes
|
|
98
|
+
</Card>
|
|
99
|
+
`
|
|
100
|
+
|
|
101
|
+
export function Page() {
|
|
102
|
+
const ast = mdxParse(code)
|
|
103
|
+
return (
|
|
104
|
+
<SafeMdxRenderer
|
|
105
|
+
markdown={code}
|
|
106
|
+
mdast={ast}
|
|
107
|
+
components={{
|
|
108
|
+
Card({ icon, actions, children }) {
|
|
109
|
+
return (
|
|
110
|
+
<div className="card">
|
|
111
|
+
<div className="header">
|
|
112
|
+
{icon}
|
|
113
|
+
<div className="actions">{actions}</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="content">{children}</div>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
Icon({ name }) {
|
|
120
|
+
return <span>⭐</span> // Your icon component
|
|
121
|
+
},
|
|
122
|
+
Button({ variant, children }) {
|
|
123
|
+
return <button className={variant}>{children}</button>
|
|
124
|
+
},
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### ESM Imports in Attributes
|
|
132
|
+
|
|
133
|
+
To use externally imported components in attributes, enable the `allowClientEsmImports` option:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { SafeMdxRenderer } from 'safe-mdx'
|
|
137
|
+
import { mdxParse } from 'safe-mdx/parse'
|
|
138
|
+
|
|
139
|
+
const code = `
|
|
140
|
+
import { Icon } from 'https://esm.sh/lucide-react'
|
|
141
|
+
import Button from 'https://esm.sh/my-ui-library'
|
|
142
|
+
|
|
143
|
+
# External Components in Attributes
|
|
144
|
+
|
|
145
|
+
<Card
|
|
146
|
+
icon={<Icon name="star" />}
|
|
147
|
+
action={<Button>External Button</Button>}
|
|
148
|
+
>
|
|
149
|
+
Using externally imported components
|
|
150
|
+
</Card>
|
|
151
|
+
`
|
|
152
|
+
|
|
153
|
+
export function Page() {
|
|
154
|
+
const ast = mdxParse(code)
|
|
155
|
+
return (
|
|
156
|
+
<SafeMdxRenderer
|
|
157
|
+
markdown={code}
|
|
158
|
+
mdast={ast}
|
|
159
|
+
allowClientEsmImports={true} // Required for ESM imports
|
|
160
|
+
components={{
|
|
161
|
+
Card({ icon, action, children }) {
|
|
162
|
+
return (
|
|
163
|
+
<div className="card">
|
|
164
|
+
<div className="header">
|
|
165
|
+
{icon}
|
|
166
|
+
{action}
|
|
167
|
+
</div>
|
|
168
|
+
<div className="content">{children}</div>
|
|
169
|
+
</div>
|
|
170
|
+
)
|
|
171
|
+
},
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Security Note**: ESM imports are disabled by default. Only enable `allowClientEsmImports` when you trust the MDX source, as it allows loading external code.
|
|
179
|
+
|
|
79
180
|
## Change default MDX parser
|
|
80
181
|
|
|
81
182
|
If you want to use custom MDX plugins, you can pass your own MDX processed ast.
|
|
@@ -206,7 +307,14 @@ This is ok if you render your MDX in isolation from each tenant, for example on
|
|
|
206
307
|
|
|
207
308
|
These features are not supported yet:
|
|
208
309
|
|
|
209
|
-
- expressions or
|
|
210
|
-
- importing components or data from other files
|
|
310
|
+
- 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.
|
|
311
|
+
- importing components or data from other files (unless `allowClientEsmImports` is enabled for https:// imports).
|
|
312
|
+
- Exporting irresolvable or declaring components inline in the MDX
|
|
313
|
+
|
|
314
|
+
**Note**: JSX components in attributes are now supported! You can use React components inside attributes like `<Card icon={<Icon />}>` without relying on JavaScript evaluation.
|
|
315
|
+
|
|
316
|
+
To overcome the remaining limitations you can define custom logic in your components and pass them to `SafeMdxRenderer` `components` prop. This will also make your MDX files cleaner and easier to read.
|
|
317
|
+
|
|
318
|
+
## Future Roadmap
|
|
211
319
|
|
|
212
|
-
|
|
320
|
+
- add support for scope parameter to allow referencing variables in expressions and code
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic-esm-component.d.ts","sourceRoot":"","sources":["../src/dynamic-esm-component.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,mBAAmB,CAAC,EAChC,SAAS,EACT,aAAa,EACb,GAAG,KAAK,EACX,EAAE;IACC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACrB,2CAgCA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Component, lazy, useState, useSyncExternalStore, Suspense } from 'react';
|
|
4
|
+
// Hook to detect hydration state
|
|
5
|
+
const noop = (callback) => {
|
|
6
|
+
return () => { };
|
|
7
|
+
};
|
|
8
|
+
function useHydrated() {
|
|
9
|
+
return useSyncExternalStore(noop, () => true, // client snapshot
|
|
10
|
+
() => false);
|
|
11
|
+
}
|
|
12
|
+
// Error boundary for ESM components
|
|
13
|
+
class EsmErrorBoundary extends Component {
|
|
14
|
+
constructor(props) {
|
|
15
|
+
super(props);
|
|
16
|
+
this.state = { hasError: false };
|
|
17
|
+
}
|
|
18
|
+
static getDerivedStateFromError() {
|
|
19
|
+
return { hasError: true };
|
|
20
|
+
}
|
|
21
|
+
componentDidCatch(error, errorInfo) {
|
|
22
|
+
console.error(`Error loading ESM component ${this.props.componentName}:`, error, errorInfo);
|
|
23
|
+
}
|
|
24
|
+
render() {
|
|
25
|
+
if (this.state.hasError) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return this.props.children;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Dynamic ESM component loader
|
|
32
|
+
export function DynamicEsmComponent({ importUrl, componentName, ...props }) {
|
|
33
|
+
const isHydrated = useHydrated();
|
|
34
|
+
const [LazyComponent] = useState(() => {
|
|
35
|
+
if (typeof window === 'undefined') {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return lazy(async () => {
|
|
39
|
+
try {
|
|
40
|
+
const module = await import(/* @vite-ignore */ importUrl);
|
|
41
|
+
const Component = module[componentName] || module.default;
|
|
42
|
+
if (!Component) {
|
|
43
|
+
throw new Error(`Component "${componentName}" not found in module ${importUrl}`);
|
|
44
|
+
}
|
|
45
|
+
return { default: Component };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(`Failed to load ESM component from ${importUrl}:`, error);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
if (!isHydrated || !LazyComponent) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return (_jsx(EsmErrorBoundary, { componentName: componentName, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyComponent, { ...props }) }) }));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=dynamic-esm-component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic-esm-component.js","sourceRoot":"","sources":["../src/dynamic-esm-component.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAc,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAExF,iCAAiC;AACjC,MAAM,IAAI,GAAG,CAAC,QAAoB,EAAE,EAAE;IAClC,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;AACnB,CAAC,CAAA;AAED,SAAS,WAAW;IAChB,OAAO,oBAAoB,CACvB,IAAI,EACJ,GAAG,EAAE,CAAC,IAAI,EAAE,kBAAkB;IAC9B,GAAG,EAAE,CAAC,KAAK,CACd,CAAA;AACL,CAAC;AAED,oCAAoC;AACpC,MAAM,gBAAiB,SAAQ,SAG9B;IACG,YAAY,KAA2D;QACnE,KAAK,CAAC,KAAK,CAAC,CAAA;QACZ,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACpC,CAAC;IAED,MAAM,CAAC,wBAAwB;QAC3B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC7B,CAAC;IAED,iBAAiB,CAAC,KAAY,EAAE,SAA0B;QACtD,OAAO,CAAC,KAAK,CAAC,+BAA+B,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;IAC/F,CAAC;IAED,MAAM;QACF,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;YACrB,OAAO,IAAI,CAAA;SACd;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IAC9B,CAAC;CACJ;AAED,+BAA+B;AAC/B,MAAM,UAAU,mBAAmB,CAAC,EAChC,SAAS,EACT,aAAa,EACb,GAAG,KAAK,EAKX;IACG,MAAM,UAAU,GAAG,WAAW,EAAE,CAAA;IAChC,MAAM,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE;QAClC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YAC/B,OAAO,IAAI,CAAA;SACd;QACD,OAAO,IAAI,CAAC,KAAK,IAAI,EAAE;YACnB,IAAI;gBACA,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAA;gBACzD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAA;gBACzD,IAAI,CAAC,SAAS,EAAE;oBACZ,MAAM,IAAI,KAAK,CAAC,cAAc,aAAa,yBAAyB,SAAS,EAAE,CAAC,CAAA;iBACnF;gBACD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;aAChC;YAAC,OAAO,KAAK,EAAE;gBACZ,OAAO,CAAC,KAAK,CAAC,qCAAqC,SAAS,GAAG,EAAE,KAAK,CAAC,CAAA;gBACvE,MAAM,KAAK,CAAA;aACd;QACL,CAAC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,UAAU,IAAI,CAAC,aAAa,EAAE;QAC/B,OAAO,IAAI,CAAA;KACd;IAED,OAAO,CACH,KAAC,gBAAgB,IAAC,aAAa,EAAE,aAAa,YAC1C,KAAC,QAAQ,IAAC,QAAQ,EAAE,IAAI,YACpB,KAAC,aAAa,OAAK,KAAK,GAAI,GACrB,GACI,CACtB,CAAA;AACL,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SafeMdxError } from './safe-mdx.js';
|
|
2
|
+
export interface ParsedImport {
|
|
3
|
+
componentName: string;
|
|
4
|
+
importUrl: string;
|
|
5
|
+
isDefault: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parses ESM import statements from mdxjsEsm nodes
|
|
9
|
+
* Only processes HTTPS imports for security
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseEsmImports(node: any, onError: (error: SafeMdxError) => void): Map<string, string>;
|
|
12
|
+
/**
|
|
13
|
+
* Extracts component info from an import map entry
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractComponentInfo(importInfo: string): {
|
|
16
|
+
importUrl: string;
|
|
17
|
+
componentName: string;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=esm-parser.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates if a string is a valid HTTPS URL
|
|
3
|
+
*/
|
|
4
|
+
function isValidHttpsUrl(url) {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = new URL(url);
|
|
7
|
+
return parsed.protocol === 'https:';
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parses ESM import statements from mdxjsEsm nodes
|
|
15
|
+
* Only processes HTTPS imports for security
|
|
16
|
+
*/
|
|
17
|
+
export function parseEsmImports(node, onError) {
|
|
18
|
+
const imports = new Map();
|
|
19
|
+
if (!node.data?.estree) {
|
|
20
|
+
return imports;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const program = node.data.estree;
|
|
24
|
+
for (const statement of program.body) {
|
|
25
|
+
if (statement.type === 'ImportDeclaration' &&
|
|
26
|
+
statement.source.value &&
|
|
27
|
+
typeof statement.source.value === 'string') {
|
|
28
|
+
const importUrl = statement.source.value;
|
|
29
|
+
// Validate URL
|
|
30
|
+
if (!isValidHttpsUrl(importUrl)) {
|
|
31
|
+
onError({
|
|
32
|
+
message: `Invalid import URL: "${importUrl}". Only HTTPS URLs are allowed for security reasons.`,
|
|
33
|
+
line: node.position?.start?.line,
|
|
34
|
+
});
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
// Process import specifiers
|
|
38
|
+
for (const specifier of statement.specifiers) {
|
|
39
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
40
|
+
imports.set(specifier.local.name, importUrl);
|
|
41
|
+
}
|
|
42
|
+
else if (specifier.type === 'ImportSpecifier') {
|
|
43
|
+
const importedName = specifier.imported.type === 'Identifier'
|
|
44
|
+
? specifier.imported.name
|
|
45
|
+
: String(specifier.imported.value);
|
|
46
|
+
imports.set(specifier.local.name, `${importUrl}#${importedName}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
onError({
|
|
54
|
+
message: `Failed to parse ESM import: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
55
|
+
line: node.position?.start?.line,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return imports;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extracts component info from an import map entry
|
|
62
|
+
*/
|
|
63
|
+
export function extractComponentInfo(importInfo) {
|
|
64
|
+
const [importUrl, componentName] = importInfo.includes('#')
|
|
65
|
+
? importInfo.split('#')
|
|
66
|
+
: [importInfo, 'default'];
|
|
67
|
+
return { importUrl, componentName };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=esm-parser.js.map
|
|
@@ -0,0 +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;QACA,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAA;KACtC;IAAC,MAAM;QACJ,OAAO,KAAK,CAAA;KACf;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;QACpB,OAAO,OAAO,CAAA;KACjB;IAED,IAAI;QACA,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;QAEhC,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE;YAClC,IAAI,SAAS,CAAC,IAAI,KAAK,mBAAmB;gBACtC,SAAS,CAAC,MAAM,CAAC,KAAK;gBACtB,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE;gBAE5C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAA;gBAExC,eAAe;gBACf,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE;oBAC7B,OAAO,CAAC;wBACJ,OAAO,EAAE,wBAAwB,SAAS,sDAAsD;wBAChG,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI;qBACnC,CAAC,CAAA;oBACF,SAAQ;iBACX;gBAED,4BAA4B;gBAC5B,KAAK,MAAM,SAAS,IAAI,SAAS,CAAC,UAAU,EAAE;oBAC1C,IAAI,SAAS,CAAC,IAAI,KAAK,wBAAwB,EAAE;wBAC7C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;qBAC/C;yBAAM,IAAI,SAAS,CAAC,IAAI,KAAK,iBAAiB,EAAE;wBAC7C,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;qBACJ;iBACJ;aACJ;SACJ;KACJ;IAAC,OAAO,KAAK,EAAE;QACZ,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;KACL;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esm-parser.test.d.ts","sourceRoot":"","sources":["../src/esm-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { expect, test, describe } from 'vitest';
|
|
2
|
+
import { parseEsmImports, extractComponentInfo } from './esm-parser.js';
|
|
3
|
+
import { mdxParse } from './parse.js';
|
|
4
|
+
describe('parseEsmImports', () => {
|
|
5
|
+
test('parses default imports from HTTPS URLs', () => {
|
|
6
|
+
const code = `import MyComponent from 'https://esm.sh/some-component'`;
|
|
7
|
+
const mdast = mdxParse(code);
|
|
8
|
+
const errors = [];
|
|
9
|
+
// Find the mdxjsEsm node
|
|
10
|
+
const esmNode = mdast.children.find((node) => node.type === 'mdxjsEsm');
|
|
11
|
+
const imports = parseEsmImports(esmNode, (err) => errors.push(err));
|
|
12
|
+
expect(Array.from(imports.entries())).toMatchInlineSnapshot(`
|
|
13
|
+
[
|
|
14
|
+
[
|
|
15
|
+
"MyComponent",
|
|
16
|
+
"https://esm.sh/some-component",
|
|
17
|
+
],
|
|
18
|
+
]
|
|
19
|
+
`);
|
|
20
|
+
expect(errors).toMatchInlineSnapshot(`[]`);
|
|
21
|
+
});
|
|
22
|
+
test('parses named imports from HTTPS URLs', () => {
|
|
23
|
+
const code = `import { Button, Card as MyCard } from 'https://esm.sh/ui-library'`;
|
|
24
|
+
const mdast = mdxParse(code);
|
|
25
|
+
const errors = [];
|
|
26
|
+
const esmNode = mdast.children.find((node) => node.type === 'mdxjsEsm');
|
|
27
|
+
const imports = parseEsmImports(esmNode, (err) => errors.push(err));
|
|
28
|
+
expect(Array.from(imports.entries())).toMatchInlineSnapshot(`
|
|
29
|
+
[
|
|
30
|
+
[
|
|
31
|
+
"Button",
|
|
32
|
+
"https://esm.sh/ui-library#Button",
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
"MyCard",
|
|
36
|
+
"https://esm.sh/ui-library#Card",
|
|
37
|
+
],
|
|
38
|
+
]
|
|
39
|
+
`);
|
|
40
|
+
expect(errors).toMatchInlineSnapshot(`[]`);
|
|
41
|
+
});
|
|
42
|
+
test('rejects non-HTTPS URLs', () => {
|
|
43
|
+
const code = `
|
|
44
|
+
import Component1 from 'http://insecure.com/component'
|
|
45
|
+
import Component2 from 'file:///local/path'
|
|
46
|
+
import Component3 from './relative/path'
|
|
47
|
+
`;
|
|
48
|
+
const mdast = mdxParse(code);
|
|
49
|
+
const errors = [];
|
|
50
|
+
mdast.children.forEach((node) => {
|
|
51
|
+
if (node.type === 'mdxjsEsm') {
|
|
52
|
+
parseEsmImports(node, (err) => errors.push(err));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
expect(errors).toMatchInlineSnapshot(`
|
|
56
|
+
[
|
|
57
|
+
{
|
|
58
|
+
"line": 2,
|
|
59
|
+
"message": "Invalid import URL: "http://insecure.com/component". Only HTTPS URLs are allowed for security reasons.",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"line": 2,
|
|
63
|
+
"message": "Invalid import URL: "file:///local/path". Only HTTPS URLs are allowed for security reasons.",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"line": 2,
|
|
67
|
+
"message": "Invalid import URL: "./relative/path". Only HTTPS URLs are allowed for security reasons.",
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
`);
|
|
71
|
+
});
|
|
72
|
+
test('handles multiple import types in one statement', () => {
|
|
73
|
+
const code = `import Default, { Named1, Named2 as Alias } from 'https://esm.sh/mixed-exports'`;
|
|
74
|
+
const mdast = mdxParse(code);
|
|
75
|
+
const errors = [];
|
|
76
|
+
const esmNode = mdast.children.find((node) => node.type === 'mdxjsEsm');
|
|
77
|
+
const imports = parseEsmImports(esmNode, (err) => errors.push(err));
|
|
78
|
+
expect(Array.from(imports.entries())).toMatchInlineSnapshot(`
|
|
79
|
+
[
|
|
80
|
+
[
|
|
81
|
+
"Default",
|
|
82
|
+
"https://esm.sh/mixed-exports",
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
"Named1",
|
|
86
|
+
"https://esm.sh/mixed-exports#Named1",
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"Alias",
|
|
90
|
+
"https://esm.sh/mixed-exports#Named2",
|
|
91
|
+
],
|
|
92
|
+
]
|
|
93
|
+
`);
|
|
94
|
+
expect(errors).toMatchInlineSnapshot(`[]`);
|
|
95
|
+
});
|
|
96
|
+
test('returns empty map when no estree data', () => {
|
|
97
|
+
const errors = [];
|
|
98
|
+
const node = { type: 'mdxjsEsm', position: { start: { line: 1 } } };
|
|
99
|
+
const imports = parseEsmImports(node, (err) => errors.push(err));
|
|
100
|
+
expect(imports.size).toBe(0);
|
|
101
|
+
expect(errors).toMatchInlineSnapshot(`[]`);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('extractComponentInfo', () => {
|
|
105
|
+
test('extracts default import info', () => {
|
|
106
|
+
const result = extractComponentInfo('https://esm.sh/component');
|
|
107
|
+
expect(result).toMatchInlineSnapshot(`
|
|
108
|
+
{
|
|
109
|
+
"componentName": "default",
|
|
110
|
+
"importUrl": "https://esm.sh/component",
|
|
111
|
+
}
|
|
112
|
+
`);
|
|
113
|
+
});
|
|
114
|
+
test('extracts named import info', () => {
|
|
115
|
+
const result = extractComponentInfo('https://esm.sh/ui-library#Button');
|
|
116
|
+
expect(result).toMatchInlineSnapshot(`
|
|
117
|
+
{
|
|
118
|
+
"componentName": "Button",
|
|
119
|
+
"importUrl": "https://esm.sh/ui-library",
|
|
120
|
+
}
|
|
121
|
+
`);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=esm-parser.test.js.map
|
|
@@ -0,0 +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;gBAC1B,eAAe,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;aACnD;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-mdx.bench.d.ts","sourceRoot":"","sources":["../src/safe-mdx.bench.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { bench, describe } from 'vitest';
|
|
3
|
+
import { mdxParse } from './parse.js';
|
|
4
|
+
import { MdastToJsx } from './safe-mdx.js';
|
|
5
|
+
let longMdxContent = await fetch('https://raw.githubusercontent.com/colinhacks/zod/0a49fa39348b7c72b19ddedc3b0f879bd395304b/packages/docs/content/packages/v3.mdx').then((x) => x.text());
|
|
6
|
+
function Callout({ children }) {
|
|
7
|
+
return (_jsx("div", { style: {
|
|
8
|
+
borderLeft: '4px solid #0070f3',
|
|
9
|
+
background: '#f0f8ff',
|
|
10
|
+
padding: '8px 16px',
|
|
11
|
+
margin: '16px 0',
|
|
12
|
+
}, children: children }));
|
|
13
|
+
}
|
|
14
|
+
const mdast = mdxParse(longMdxContent);
|
|
15
|
+
describe('safe-mdx performance benchmarks', () => {
|
|
16
|
+
bench('MdastToJsx class processing (long MDX)', () => {
|
|
17
|
+
const visitor = new MdastToJsx({
|
|
18
|
+
markdown: longMdxContent,
|
|
19
|
+
mdast,
|
|
20
|
+
components: { Callout },
|
|
21
|
+
});
|
|
22
|
+
visitor.run();
|
|
23
|
+
const errors = visitor.errors;
|
|
24
|
+
// export interface SafeMdxError {
|
|
25
|
+
// message: string
|
|
26
|
+
// line?: number
|
|
27
|
+
// schemaPath?: string
|
|
28
|
+
// }
|
|
29
|
+
});
|
|
30
|
+
bench('MdastToJsx with noop createElement (long MDX)', () => {
|
|
31
|
+
const noopCreateElement = () => null;
|
|
32
|
+
const visitor = new MdastToJsx({
|
|
33
|
+
markdown: longMdxContent,
|
|
34
|
+
mdast,
|
|
35
|
+
components: { Callout },
|
|
36
|
+
createElement: noopCreateElement,
|
|
37
|
+
});
|
|
38
|
+
visitor.run();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=safe-mdx.bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-mdx.bench.js","sourceRoot":"","sources":["../src/safe-mdx.bench.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,IAAI,cAAc,GAAG,MAAM,KAAK,CAC5B,iIAAiI,CACpI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;AAEvB,SAAS,OAAO,CAAC,EAAE,QAAQ,EAAqB;IAC5C,OAAO,CACH,cACI,KAAK,EAAE;YACH,UAAU,EAAE,mBAAmB;YAC/B,UAAU,EAAE,SAAS;YACrB,OAAO,EAAE,UAAU;YACnB,MAAM,EAAE,QAAQ;SACnB,YAEA,QAAQ,GACP,CACT,CAAA;AACL,CAAC;AAED,MAAM,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAA;AAEtC,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC7C,KAAK,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACjD,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC;YAC3B,QAAQ,EAAE,cAAc;YACxB,KAAK;YACL,UAAU,EAAE,EAAE,OAAO,EAAE;SAC1B,CAAC,CAAA;QACF,OAAO,CAAC,GAAG,EAAE,CAAA;QACb,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;QAC7B,kCAAkC;QAClC,sBAAsB;QACtB,oBAAoB;QACpB,0BAA0B;QAC1B,IAAI;IACR,CAAC,CAAC,CAAA;IAEF,KAAK,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACxD,MAAM,iBAAiB,GAAG,GAAG,EAAE,CAAC,IAAI,CAAA;QACpC,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC;YAC3B,QAAQ,EAAE,cAAc;YACxB,KAAK;YACL,UAAU,EAAE,EAAE,OAAO,EAAE;YACvB,aAAa,EAAE,iBAAiB;SACnC,CAAC,CAAA;QACF,OAAO,CAAC,GAAG,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA"}
|
package/dist/safe-mdx.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
3
|
import type { Node, Parent, Root, RootContent } from 'mdast';
|
|
3
4
|
import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';
|
|
5
|
+
import type { JSXElement } from 'estree-jsx';
|
|
4
6
|
import { ReactNode } from 'react';
|
|
5
7
|
export type MyRootContent = RootContent | Root;
|
|
6
8
|
declare module 'mdast' {
|
|
@@ -12,36 +14,51 @@ declare module 'mdast' {
|
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
export type RenderNode = (node: MyRootContent, transform: (node: MyRootContent) => ReactNode) => ReactNode | undefined;
|
|
17
|
+
export interface SafeMdxError {
|
|
18
|
+
message: string;
|
|
19
|
+
line?: number;
|
|
20
|
+
schemaPath?: string;
|
|
21
|
+
}
|
|
22
|
+
export type ComponentPropsSchema = Record<string, StandardSchemaV1>;
|
|
23
|
+
export type CreateElementFunction = (type: any, props?: any, ...children: ReactNode[]) => ReactNode;
|
|
15
24
|
export declare const SafeMdxRenderer: React.NamedExoticComponent<{
|
|
16
25
|
components?: ComponentsMap;
|
|
17
26
|
markdown?: string;
|
|
18
27
|
mdast: MyRootContent;
|
|
19
28
|
renderNode?: RenderNode;
|
|
29
|
+
componentPropsSchema?: ComponentPropsSchema;
|
|
30
|
+
createElement?: CreateElementFunction;
|
|
31
|
+
allowClientEsmImports?: boolean;
|
|
20
32
|
}>;
|
|
21
33
|
export declare class MdastToJsx {
|
|
22
34
|
mdast: MyRootContent;
|
|
23
35
|
str: string;
|
|
24
36
|
jsxStr: string;
|
|
25
37
|
c: ComponentsMap;
|
|
26
|
-
errors:
|
|
27
|
-
message: string;
|
|
28
|
-
}[];
|
|
38
|
+
errors: SafeMdxError[];
|
|
29
39
|
renderNode?: RenderNode;
|
|
30
|
-
|
|
40
|
+
componentPropsSchema?: ComponentPropsSchema;
|
|
41
|
+
createElement: CreateElementFunction;
|
|
42
|
+
esmImports: Map<string, string>;
|
|
43
|
+
allowClientEsmImports: boolean;
|
|
44
|
+
constructor({ markdown: code, mdast, components, renderNode, componentPropsSchema, createElement, allowClientEsmImports, }: {
|
|
31
45
|
markdown?: string;
|
|
32
46
|
mdast: MyRootContent;
|
|
33
47
|
components?: ComponentsMap;
|
|
34
48
|
renderNode?: (node: MyRootContent, transform: (node: MyRootContent) => ReactNode) => ReactNode | undefined;
|
|
49
|
+
componentPropsSchema?: ComponentPropsSchema;
|
|
50
|
+
createElement?: CreateElementFunction;
|
|
51
|
+
allowClientEsmImports?: boolean;
|
|
35
52
|
});
|
|
53
|
+
validateComponentProps(componentName: string, props: Record<string, any>, line?: number): void;
|
|
36
54
|
mapMdastChildren(node: any): any;
|
|
37
55
|
mapJsxChildren(node: any): any;
|
|
38
56
|
jsxTransformer(node: MyRootContent): ReactNode;
|
|
57
|
+
transformJsxElement(jsxElement: JSXElement, onError?: (err: SafeMdxError) => void, line?: number): ReactNode;
|
|
58
|
+
getJsxAttrs(node: MdxJsxFlowElement | MdxJsxTextElement, onError?: (err: SafeMdxError) => void): [string, any][];
|
|
39
59
|
run(): any;
|
|
40
60
|
mdastTransformer(node: MyRootContent): ReactNode;
|
|
41
61
|
}
|
|
42
|
-
export declare function getJsxAttrs(node: MdxJsxFlowElement | MdxJsxTextElement, onError?: (err: {
|
|
43
|
-
message: string;
|
|
44
|
-
}) => void): [string, any][];
|
|
45
62
|
export declare function mdastBfs(node: Parent | Node, cb?: (node: Node | Parent) => any): any[];
|
|
46
63
|
declare const nativeTags: readonly ["blockquote", "strong", "em", "del", "hr", "a", "b", "br", "button", "div", "form", "h1", "h2", "h3", "h4", "head", "iframe", "img", "input", "label", "li", "link", "ol", "p", "path", "picture", "script", "section", "source", "span", "sub", "sup", "svg", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "ul", "video", "code", "pre", "figure", "canvas", "details", "dl", "dt", "dd", "fieldset", "footer", "header", "legend", "main", "mark", "nav", "progress", "summary", "time"];
|
|
47
64
|
type ComponentsMap = {
|
package/dist/safe-mdx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-mdx.d.ts","sourceRoot":"","sources":["../src/safe-mdx.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"safe-mdx.d.ts","sourceRoot":"","sources":["../src/safe-mdx.tsx"],"names":[],"mappings":"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"}
|