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 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
- - Support for rendering streaming MDX, by using `completeJsxTags`
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 values defined with `export`
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
- To overcome these 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.
320
+ - add support for scope parameter to allow referencing variables in expressions and code
@@ -0,0 +1,6 @@
1
+ export declare function DynamicEsmComponent({ importUrl, componentName, ...props }: {
2
+ importUrl: string;
3
+ componentName: string;
4
+ [key: string]: any;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=dynamic-esm-component.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=esm-parser.test.d.ts.map
@@ -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":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=safe-mdx.bench.d.ts.map
@@ -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"}
@@ -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
- constructor({ markdown: code, mdast, components, renderNode, }: {
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 = {
@@ -1 +1 @@
1
- {"version":3,"file":"safe-mdx.d.ts","sourceRoot":"","sources":["../src/safe-mdx.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAA;AAE1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAE9E,OAAO,EAAY,SAAS,EAAE,MAAM,OAAO,CAAA;AAS3C,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,IAAI,CAAA;AAE9C,OAAO,QAAQ,OAAO,CAAC;IACnB,UAAiB,WAAW;QACxB,EAAE,CAAC,EAAE,MAAM,CAAA;KACd;IACD,UAAiB,IAAI;QACjB,WAAW,CAAC,EAAE,WAAW,CAAA;KAC5B;CACJ;AAED,MAAM,MAAM,UAAU,GAAG,CACrB,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,SAAS,KAC5C,SAAS,GAAG,SAAS,CAAA;AAE1B,eAAO,MAAM,eAAe;iBAMX,aAAa;eACf,MAAM;WACV,aAAa;iBACP,UAAU;EAUzB,CAAA;AAEF,qBAAa,UAAU;IACnB,KAAK,EAAE,aAAa,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAK;IACnB,CAAC,EAAE,aAAa,CAAA;IAChB,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAK;IAClC,UAAU,CAAC,EAAE,UAAU,CAAA;gBAEX,EACR,QAAQ,EAAE,IAAS,EACnB,KAAK,EACL,UAAgC,EAChC,UAAU,GACb,EAAE;QACC,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,KAAK,EAAE,aAAa,CAAA;QACpB,UAAU,CAAC,EAAE,aAAa,CAAA;QAC1B,UAAU,CAAC,EAAE,CACT,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,SAAS,KAC5C,SAAS,GAAG,SAAS,CAAA;KAC7B;IAgBD,gBAAgB,CAAC,IAAI,EAAE,GAAG;IAiB1B,cAAc,CAAC,IAAI,EAAE,GAAG;IAiBxB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS;IAsC9C,GAAG;IAQH,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS;CA+SnD;AAED,wBAAgB,WAAW,CACvB,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,EAC3C,OAAO,GAAE,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,KAAK,IAAoB,mBAmE9D;AAcD,wBAAgB,QAAQ,CACpB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,KAAK,GAAG,SAiBpC;AAUD,QAAA,MAAM,UAAU,+eA6DN,CAAA;AAEV,KAAK,aAAa,GAAG;KAAG,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG;CAAE,GAAG;IAChE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACrB,CAAA"}
1
+ {"version":3,"file":"safe-mdx.d.ts","sourceRoot":"","sources":["../src/safe-mdx.tsx"],"names":[],"mappings":"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"}