@modern-js/main-doc 3.0.0-alpha.0 → 3.0.0-alpha.1
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/docs/en/apis/app/commands.mdx +6 -30
- package/docs/en/components/bff-upload.mdx +3 -5
- package/docs/en/components/bundler.mdx +1 -1
- package/docs/en/components/enable-bff.mdx +6 -2
- package/docs/en/components/enable-ssg.mdx +1 -0
- package/docs/en/components/esbuild.mdx +2 -2
- package/docs/en/components/extend-bff-function.mdx +2 -4
- package/docs/en/components/hono.mdx +119 -0
- package/docs/en/components/international/custom-instance-code.mdx +16 -0
- package/docs/en/components/international/init-options-desc.mdx +1 -0
- package/docs/en/components/international/install-command.mdx +15 -1
- package/docs/en/components/international/instance-code.mdx +26 -0
- package/docs/en/configure/app/builder-plugins.mdx +1 -2
- package/docs/en/configure/app/dev/server.mdx +108 -0
- package/docs/en/configure/app/experiments/source-build.mdx +0 -1
- package/docs/en/configure/app/output/assets-retry.mdx +1 -1
- package/docs/en/configure/app/output/disable-inline-runtime-chunk.mdx +2 -2
- package/docs/en/configure/app/output/filename.mdx +2 -4
- package/docs/en/configure/app/output/temp-dir.mdx +3 -3
- package/docs/en/configure/app/performance/build-cache.mdx +1 -1
- package/docs/en/configure/app/performance/profile.mdx +1 -1
- package/docs/en/configure/app/plugins.mdx +1 -3
- package/docs/en/configure/app/runtime/router.mdx +0 -4
- package/docs/en/configure/app/security/sri.mdx +0 -1
- package/docs/en/configure/app/source/alias.mdx +1 -1
- package/docs/en/configure/app/source/enable-async-entry.mdx +1 -1
- package/docs/en/configure/app/source/include.mdx +2 -14
- package/docs/en/configure/app/tools/dev-server.mdx +8 -8
- package/docs/en/configure/app/usage.mdx +0 -12
- package/docs/en/guides/_meta.json +5 -0
- package/docs/en/guides/advanced-features/bff/_meta.json +9 -1
- package/docs/en/guides/advanced-features/bff/cross-project.mdx +1 -1
- package/docs/en/guides/advanced-features/bff/frameworks.mdx +2 -15
- package/docs/en/guides/advanced-features/bff/function.mdx +4 -4
- package/docs/en/guides/advanced-features/bff/operators.mdx +628 -0
- package/docs/en/guides/advanced-features/bff/sdk.mdx +17 -9
- package/docs/en/guides/advanced-features/bff/upload.mdx +3 -1
- package/docs/en/guides/advanced-features/international/configuration.mdx +7 -16
- package/docs/en/guides/advanced-features/international/quick-start.mdx +4 -32
- package/docs/en/guides/advanced-features/page-performance/optimize-bundle.mdx +1 -1
- package/docs/en/guides/advanced-features/page-performance/react-compiler.mdx +18 -4
- package/docs/en/guides/advanced-features/rspack-start.mdx +1 -1
- package/docs/en/guides/advanced-features/server-monitor/monitors.mdx +62 -5
- package/docs/en/guides/basic-features/data/data-cache.mdx +60 -76
- package/docs/en/guides/basic-features/data/data-fetch.mdx +15 -14
- package/docs/en/guides/basic-features/debug/proxy.mdx +6 -9
- package/docs/en/guides/basic-features/render/rsc.mdx +24 -19
- package/docs/en/guides/basic-features/render/ssg.mdx +4 -9
- package/docs/en/guides/basic-features/render/ssr-cache.mdx +0 -4
- package/docs/en/guides/basic-features/static-assets/svg-assets.mdx +0 -4
- package/docs/en/guides/get-started/tech-stack.mdx +1 -1
- package/docs/en/guides/upgrade/_meta.json +1 -0
- package/docs/en/guides/upgrade/config.mdx +936 -0
- package/docs/en/guides/upgrade/entry.mdx +463 -0
- package/docs/en/guides/upgrade/other.mdx +83 -0
- package/docs/en/guides/upgrade/overview.mdx +33 -0
- package/docs/en/guides/upgrade/tailwindcss.mdx +130 -0
- package/docs/en/guides/upgrade/web-server.mdx +91 -0
- package/docs/en/plugin/_meta.json +5 -0
- package/docs/en/plugin/cli-plugins/_meta.json +1 -1
- package/docs/en/plugin/cli-plugins/api.mdx +13 -63
- package/docs/en/plugin/cli-plugins/life-cycle.mdx +0 -4
- package/docs/en/plugin/introduction.mdx +8 -20
- package/docs/en/plugin/plugin-system.mdx +3 -3
- package/docs/en/plugin/runtime-plugins/_meta.json +1 -1
- package/docs/en/plugin/runtime-plugins/api.mdx +1 -1
- package/docs/en/plugin/server-plugins/_meta.json +1 -0
- package/docs/en/plugin/server-plugins/api.mdx +210 -1
- package/docs/en/plugin/server-plugins/life-cycle.mdx +41 -1
- package/docs/zh/apis/app/commands.mdx +6 -30
- package/docs/zh/components/bff-operator-code.mdx +5 -0
- package/docs/zh/components/bff-upload.mdx +0 -2
- package/docs/zh/components/bundler.mdx +1 -1
- package/docs/zh/components/enable-bff.mdx +6 -2
- package/docs/zh/components/enable-ssg.mdx +3 -1
- package/docs/zh/components/esbuild.mdx +2 -2
- package/docs/zh/components/extend-bff-function.mdx +2 -4
- package/docs/zh/components/hono.mdx +119 -0
- package/docs/zh/components/international/custom-instance-code.mdx +16 -0
- package/docs/zh/components/international/init-options-desc.mdx +1 -0
- package/docs/zh/components/international/install-command.mdx +15 -0
- package/docs/zh/components/international/instance-code.mdx +26 -0
- package/docs/zh/configure/app/builder-plugins.mdx +1 -2
- package/docs/zh/configure/app/dev/server.mdx +109 -2
- package/docs/zh/configure/app/experiments/source-build.mdx +0 -1
- package/docs/zh/configure/app/output/assets-retry.mdx +1 -1
- package/docs/zh/configure/app/output/disable-inline-runtime-chunk.mdx +2 -2
- package/docs/zh/configure/app/output/filename.mdx +2 -4
- package/docs/zh/configure/app/output/temp-dir.mdx +3 -3
- package/docs/zh/configure/app/performance/build-cache.mdx +1 -1
- package/docs/zh/configure/app/performance/profile.mdx +1 -1
- package/docs/zh/configure/app/plugins.mdx +1 -2
- package/docs/zh/configure/app/runtime/router.mdx +0 -4
- package/docs/zh/configure/app/security/sri.mdx +0 -1
- package/docs/zh/configure/app/source/alias.mdx +1 -1
- package/docs/zh/configure/app/source/enable-async-entry.mdx +1 -1
- package/docs/zh/configure/app/source/include.mdx +2 -16
- package/docs/zh/configure/app/tools/dev-server.mdx +5 -5
- package/docs/zh/configure/app/usage.mdx +0 -12
- package/docs/zh/guides/advanced-features/bff/_meta.json +9 -1
- package/docs/zh/guides/advanced-features/bff/frameworks.mdx +2 -16
- package/docs/zh/guides/advanced-features/bff/operators.mdx +628 -0
- package/docs/zh/guides/advanced-features/bff/sdk.mdx +19 -12
- package/docs/zh/guides/advanced-features/bff/upload.mdx +3 -1
- package/docs/zh/guides/advanced-features/international/configuration.mdx +7 -16
- package/docs/zh/guides/advanced-features/international/quick-start.mdx +2 -25
- package/docs/zh/guides/advanced-features/page-performance/optimize-bundle.mdx +1 -1
- package/docs/zh/guides/advanced-features/page-performance/react-compiler.mdx +18 -4
- package/docs/zh/guides/advanced-features/server-monitor/monitors.mdx +60 -5
- package/docs/zh/guides/basic-features/data/data-cache.mdx +47 -54
- package/docs/zh/guides/basic-features/data/data-fetch.mdx +9 -12
- package/docs/zh/guides/basic-features/debug/proxy.mdx +4 -7
- package/docs/zh/guides/basic-features/render/rsc.mdx +23 -37
- package/docs/zh/guides/basic-features/render/ssr-cache.mdx +0 -4
- package/docs/zh/guides/basic-features/static-assets/svg-assets.mdx +0 -4
- package/docs/zh/guides/get-started/tech-stack.mdx +1 -1
- package/docs/zh/guides/troubleshooting/builder.mdx +1 -1
- package/docs/zh/guides/upgrade/config.mdx +132 -1
- package/docs/zh/plugin/_meta.json +5 -0
- package/docs/zh/plugin/cli-plugins/_meta.json +1 -1
- package/docs/zh/plugin/cli-plugins/api.mdx +15 -65
- package/docs/zh/plugin/cli-plugins/life-cycle.mdx +0 -4
- package/docs/zh/plugin/introduction.mdx +4 -16
- package/docs/zh/plugin/plugin-system.mdx +3 -14
- package/docs/zh/plugin/runtime-plugins/_meta.json +1 -1
- package/docs/zh/plugin/runtime-plugins/api.mdx +1 -1
- package/docs/zh/plugin/server-plugins/_meta.json +1 -0
- package/docs/zh/plugin/server-plugins/api.mdx +210 -1
- package/docs/zh/plugin/server-plugins/life-cycle.mdx +41 -1
- package/package.json +2 -2
- package/src/components/FrameworkCode/index.tsx +605 -0
- package/docs/en/configure/app/performance/bundle-analyze.mdx +0 -24
- package/docs/en/configure/app/tools/babel.mdx +0 -225
- package/docs/en/plugin/cli-plugins/migration.mdx +0 -83
- package/docs/en/plugin/runtime-plugins/migration.mdx +0 -110
- package/docs/zh/components/default-mwa-generate.mdx +0 -4
- package/docs/zh/configure/app/performance/bundle-analyze.mdx +0 -24
- package/docs/zh/configure/app/tools/babel.mdx +0 -224
- package/docs/zh/plugin/cli-plugins/migration.mdx +0 -83
- package/docs/zh/plugin/runtime-plugins/migration.mdx +0 -110
- /package/docs/en/components/{router-legacy-tip.mdx → upgrade-config-deploy.mdx} +0 -0
- /package/docs/zh/components/{router-legacy-tip.mdx → upgrade-config-deploy.mdx} +0 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import { Tab, Tabs } from '@rspress/core/theme';
|
|
2
|
+
import React, {
|
|
3
|
+
type PropsWithChildren,
|
|
4
|
+
useMemo,
|
|
5
|
+
cloneElement,
|
|
6
|
+
useRef,
|
|
7
|
+
useEffect,
|
|
8
|
+
} from 'react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Framework transformation configuration
|
|
12
|
+
*/
|
|
13
|
+
export interface FrameworkTransform {
|
|
14
|
+
/** Framework identifier (used as Tab value) */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Framework display name (used as Tab label) */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Target import path */
|
|
19
|
+
importPath: string;
|
|
20
|
+
/** Code transformation function that receives original code and returns transformed code */
|
|
21
|
+
transform?: (code: string) => string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* FrameworkCode component for displaying code examples that support multiple runtime frameworks
|
|
26
|
+
*
|
|
27
|
+
* - Single framework: Renders transformed code directly without Tabs
|
|
28
|
+
* - Multiple frameworks: Displays multiple framework versions using Tabs
|
|
29
|
+
*
|
|
30
|
+
* The component automatically replaces the source import path with each framework's target import path.
|
|
31
|
+
* The code in the document should use the sourcePath, which will be replaced with each framework's importPath.
|
|
32
|
+
*/
|
|
33
|
+
interface FrameworkCodeProps {
|
|
34
|
+
/** Framework configuration list (optional, if empty and defaultImportPath is provided, will use it for replacement) */
|
|
35
|
+
frameworks?: FrameworkTransform[];
|
|
36
|
+
/** Default selected framework ID (only effective when multiple frameworks are provided) */
|
|
37
|
+
defaultTab?: string;
|
|
38
|
+
/** Source import path used in the document code (will be replaced with each framework's importPath) */
|
|
39
|
+
sourcePath: string;
|
|
40
|
+
/** Default import path to use when frameworks array is empty (optional) */
|
|
41
|
+
defaultImportPath?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const FrameworkCode: React.FC<PropsWithChildren<FrameworkCodeProps>> = ({
|
|
45
|
+
children,
|
|
46
|
+
frameworks,
|
|
47
|
+
defaultTab,
|
|
48
|
+
sourcePath,
|
|
49
|
+
defaultImportPath,
|
|
50
|
+
}) => {
|
|
51
|
+
/**
|
|
52
|
+
* Replace source import path with target framework import path
|
|
53
|
+
* Only replaces import/export statements, avoiding comments and string literals
|
|
54
|
+
*/
|
|
55
|
+
const replaceImportPath = (
|
|
56
|
+
code: string,
|
|
57
|
+
source: string,
|
|
58
|
+
target: string,
|
|
59
|
+
): string => {
|
|
60
|
+
// Escape special regex characters in source path
|
|
61
|
+
const escapedSource = source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
62
|
+
|
|
63
|
+
// Match import/export statements with the source path
|
|
64
|
+
// Supports:
|
|
65
|
+
// - import ... from 'source'
|
|
66
|
+
// - import type ... from 'source'
|
|
67
|
+
// - import ... from "source"
|
|
68
|
+
// - export ... from 'source'
|
|
69
|
+
// - export type ... from 'source'
|
|
70
|
+
// Uses a more precise regex that matches the full import/export statement
|
|
71
|
+
const importExportRegex = new RegExp(
|
|
72
|
+
`(import\\s+(?:type\\s+)?[^'"]*from\\s+|export\\s+(?:type\\s+)?[^'"]*from\\s+)(['"\`])${escapedSource}(['"\`])`,
|
|
73
|
+
'g',
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return code.replace(importExportRegex, `$1$2${target}$3`);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Component wrapper that clones ShikiPre and replaces import paths in the code
|
|
81
|
+
* This preserves shiki's syntax highlighting structure by only replacing the import path string
|
|
82
|
+
* in text nodes, without modifying the HTML structure
|
|
83
|
+
*/
|
|
84
|
+
const ShikiPreWithCode: React.FC<{
|
|
85
|
+
originalElement: React.ReactElement;
|
|
86
|
+
newCode: string;
|
|
87
|
+
originalCode: string;
|
|
88
|
+
sourcePath: string;
|
|
89
|
+
targetPath: string;
|
|
90
|
+
}> = ({ originalElement, newCode, originalCode, sourcePath, targetPath }) => {
|
|
91
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (containerRef.current) {
|
|
95
|
+
// Find the pre element inside
|
|
96
|
+
const preElement = containerRef.current.querySelector('pre');
|
|
97
|
+
if (preElement) {
|
|
98
|
+
// Get the code element (usually inside pre)
|
|
99
|
+
const codeElement = preElement.querySelector('code') || preElement;
|
|
100
|
+
|
|
101
|
+
// Escape special regex characters in source path
|
|
102
|
+
const escapedSource = sourcePath.replace(
|
|
103
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
104
|
+
'\\$&',
|
|
105
|
+
);
|
|
106
|
+
const regex = new RegExp(escapedSource, 'g');
|
|
107
|
+
|
|
108
|
+
// Replace import paths in all text nodes, preserving the HTML structure
|
|
109
|
+
const walker = document.createTreeWalker(
|
|
110
|
+
codeElement,
|
|
111
|
+
NodeFilter.SHOW_TEXT,
|
|
112
|
+
null,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
let node;
|
|
116
|
+
while ((node = walker.nextNode())) {
|
|
117
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent) {
|
|
118
|
+
const originalText = node.textContent;
|
|
119
|
+
// Only replace if the text contains the source path
|
|
120
|
+
if (originalText.includes(sourcePath)) {
|
|
121
|
+
node.textContent = originalText.replace(regex, targetPath);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}, [sourcePath, targetPath]);
|
|
128
|
+
|
|
129
|
+
return <div ref={containerRef}>{cloneElement(originalElement)}</div>;
|
|
130
|
+
};
|
|
131
|
+
const parseCodeBlocks = (
|
|
132
|
+
children: React.ReactNode,
|
|
133
|
+
): Array<{
|
|
134
|
+
meta: string;
|
|
135
|
+
code: string;
|
|
136
|
+
language: string;
|
|
137
|
+
title?: string;
|
|
138
|
+
originalElement?: React.ReactElement; // Save original ShikiPre element
|
|
139
|
+
}> => {
|
|
140
|
+
const blocks: Array<{
|
|
141
|
+
meta: string;
|
|
142
|
+
code: string;
|
|
143
|
+
language: string;
|
|
144
|
+
title?: string;
|
|
145
|
+
originalElement?: React.ReactElement;
|
|
146
|
+
}> = [];
|
|
147
|
+
|
|
148
|
+
const traverse = (node: any, depth = 0): void => {
|
|
149
|
+
if (!node) return;
|
|
150
|
+
|
|
151
|
+
// Handle Fragment (React.Fragment or <>...</>)
|
|
152
|
+
if (
|
|
153
|
+
node.type === React.Fragment ||
|
|
154
|
+
(typeof node.type === 'symbol' &&
|
|
155
|
+
node.type.toString().includes('Fragment'))
|
|
156
|
+
) {
|
|
157
|
+
if (node.props?.children) {
|
|
158
|
+
React.Children.forEach(node.props.children, child =>
|
|
159
|
+
traverse(child, depth + 1),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Handle pre > code structure (rspress parsed code blocks)
|
|
166
|
+
if (node.props) {
|
|
167
|
+
// Check if this is ShikiPre component (rspress code block component)
|
|
168
|
+
const nodeType = node.type;
|
|
169
|
+
const isShikiPre =
|
|
170
|
+
(typeof nodeType === 'function' && nodeType.name === 'ShikiPre') ||
|
|
171
|
+
(node.props?.className === 'shiki css-variables' && node.props?.lang);
|
|
172
|
+
|
|
173
|
+
// Check if this is a <pre> element containing <code>
|
|
174
|
+
const isPre =
|
|
175
|
+
nodeType === 'pre' ||
|
|
176
|
+
(typeof nodeType === 'string' && nodeType === 'pre') ||
|
|
177
|
+
nodeType?.displayName === 'pre' ||
|
|
178
|
+
(typeof nodeType === 'function' && nodeType.name === 'pre');
|
|
179
|
+
|
|
180
|
+
// Handle ShikiPre component (rspress code blocks)
|
|
181
|
+
if (isShikiPre) {
|
|
182
|
+
const lang = node.props.lang || 'ts';
|
|
183
|
+
const title = node.props.title;
|
|
184
|
+
|
|
185
|
+
// Extract code content from children
|
|
186
|
+
let codeContent = '';
|
|
187
|
+
const children = node.props.children;
|
|
188
|
+
|
|
189
|
+
if (children) {
|
|
190
|
+
// ShikiPre's children is usually a <pre> element
|
|
191
|
+
if (typeof children === 'object' && children.props) {
|
|
192
|
+
// Try to get code from pre element's children
|
|
193
|
+
const preChildren = children.props.children;
|
|
194
|
+
|
|
195
|
+
// Recursive function to extract text from React elements
|
|
196
|
+
const extractText = (node: any): string => {
|
|
197
|
+
if (typeof node === 'string') {
|
|
198
|
+
return node;
|
|
199
|
+
}
|
|
200
|
+
if (Array.isArray(node)) {
|
|
201
|
+
return node.map(extractText).join('');
|
|
202
|
+
}
|
|
203
|
+
if (node?.props?.children) {
|
|
204
|
+
return extractText(node.props.children);
|
|
205
|
+
}
|
|
206
|
+
return '';
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
codeContent = extractText(preChildren);
|
|
210
|
+
} else if (typeof children === 'string') {
|
|
211
|
+
codeContent = children;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (codeContent.trim()) {
|
|
216
|
+
blocks.push({
|
|
217
|
+
meta: title ? `title="${title}"` : '',
|
|
218
|
+
code: codeContent,
|
|
219
|
+
language: lang,
|
|
220
|
+
title,
|
|
221
|
+
originalElement: node, // Save original ShikiPre element
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Also check for code elements directly (might be wrapped differently)
|
|
228
|
+
const isCode =
|
|
229
|
+
nodeType === 'code' ||
|
|
230
|
+
(typeof nodeType === 'string' && nodeType === 'code') ||
|
|
231
|
+
nodeType?.displayName === 'code' ||
|
|
232
|
+
(typeof nodeType === 'function' && nodeType.name === 'code') ||
|
|
233
|
+
node.props?.className?.includes('language-');
|
|
234
|
+
|
|
235
|
+
if (isPre && node.props.children) {
|
|
236
|
+
const codeElement = React.Children.toArray(node.props.children).find(
|
|
237
|
+
(child: any) => {
|
|
238
|
+
const childType = child?.type;
|
|
239
|
+
return (
|
|
240
|
+
childType === 'code' ||
|
|
241
|
+
(typeof childType === 'string' && childType === 'code') ||
|
|
242
|
+
child?.props?.className?.includes('language-')
|
|
243
|
+
);
|
|
244
|
+
},
|
|
245
|
+
) as any;
|
|
246
|
+
|
|
247
|
+
if (codeElement) {
|
|
248
|
+
let codeContent = '';
|
|
249
|
+
|
|
250
|
+
if (typeof codeElement === 'string') {
|
|
251
|
+
codeContent = codeElement;
|
|
252
|
+
} else {
|
|
253
|
+
const children =
|
|
254
|
+
codeElement.props?.children || codeElement.children;
|
|
255
|
+
if (typeof children === 'string') {
|
|
256
|
+
codeContent = children;
|
|
257
|
+
} else if (Array.isArray(children)) {
|
|
258
|
+
codeContent = children
|
|
259
|
+
.map((c: any) =>
|
|
260
|
+
typeof c === 'string' ? c : c?.props?.children || '',
|
|
261
|
+
)
|
|
262
|
+
.join('');
|
|
263
|
+
} else if (children != null) {
|
|
264
|
+
codeContent = String(children);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Extract language from className (e.g., "language-ts" -> "ts")
|
|
269
|
+
const className = codeElement.props?.className || '';
|
|
270
|
+
const languageMatch = className.match(/language-(\w+)/);
|
|
271
|
+
const language = languageMatch?.[1] || 'ts';
|
|
272
|
+
|
|
273
|
+
// Extract title from meta or data-title attribute
|
|
274
|
+
const meta =
|
|
275
|
+
codeElement.props?.meta || codeElement.props?.['data-meta'] || '';
|
|
276
|
+
const titleMatch = meta.match(/title="([^"]+)"/);
|
|
277
|
+
const title = titleMatch?.[1] || codeElement.props?.['data-title'];
|
|
278
|
+
|
|
279
|
+
if (codeContent) {
|
|
280
|
+
blocks.push({
|
|
281
|
+
meta,
|
|
282
|
+
code: codeContent,
|
|
283
|
+
language,
|
|
284
|
+
title,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Handle direct code element with className containing language- (rspress structure)
|
|
292
|
+
if (isCode && !isPre && node.props?.className?.includes('language-')) {
|
|
293
|
+
let codeContent = '';
|
|
294
|
+
const children = node.props?.children;
|
|
295
|
+
|
|
296
|
+
if (typeof children === 'string') {
|
|
297
|
+
codeContent = children;
|
|
298
|
+
} else if (Array.isArray(children)) {
|
|
299
|
+
codeContent = children
|
|
300
|
+
.map((c: any) =>
|
|
301
|
+
typeof c === 'string' ? c : c?.props?.children || '',
|
|
302
|
+
)
|
|
303
|
+
.join('');
|
|
304
|
+
} else if (children != null) {
|
|
305
|
+
codeContent = String(children);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (codeContent) {
|
|
309
|
+
const className = node.props.className || '';
|
|
310
|
+
const languageMatch = className.match(/language-(\w+)/);
|
|
311
|
+
const language = languageMatch?.[1] || 'ts';
|
|
312
|
+
const meta = node.props?.meta || '';
|
|
313
|
+
const titleMatch = meta.match(/title="([^"]+)"/);
|
|
314
|
+
const title = titleMatch?.[1] || node.props?.['data-title'];
|
|
315
|
+
|
|
316
|
+
blocks.push({
|
|
317
|
+
meta,
|
|
318
|
+
code: codeContent,
|
|
319
|
+
language,
|
|
320
|
+
title,
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Handle direct code element with meta prop (alternative MDX structure)
|
|
327
|
+
if (node.props.meta && node.props.children) {
|
|
328
|
+
const metaMatch = node.props.meta.match(
|
|
329
|
+
/(\w+)(?:\s+title="([^"]+)")?/,
|
|
330
|
+
);
|
|
331
|
+
const language = metaMatch?.[1] || 'ts';
|
|
332
|
+
const title = metaMatch?.[2];
|
|
333
|
+
const codeContent = node.props.children;
|
|
334
|
+
|
|
335
|
+
if (codeContent) {
|
|
336
|
+
blocks.push({
|
|
337
|
+
meta: node.props.meta,
|
|
338
|
+
code:
|
|
339
|
+
typeof codeContent === 'string'
|
|
340
|
+
? codeContent
|
|
341
|
+
: String(codeContent),
|
|
342
|
+
language,
|
|
343
|
+
title,
|
|
344
|
+
});
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Generic check: any element with language- className (rspress might use custom components)
|
|
350
|
+
if (
|
|
351
|
+
node.props?.className &&
|
|
352
|
+
typeof node.props.className === 'string' &&
|
|
353
|
+
node.props.className.includes('language-')
|
|
354
|
+
) {
|
|
355
|
+
let codeContent = '';
|
|
356
|
+
const children = node.props?.children;
|
|
357
|
+
|
|
358
|
+
if (typeof children === 'string') {
|
|
359
|
+
codeContent = children;
|
|
360
|
+
} else if (Array.isArray(children)) {
|
|
361
|
+
codeContent = children
|
|
362
|
+
.map((c: any) => {
|
|
363
|
+
if (typeof c === 'string') return c;
|
|
364
|
+
// Try to extract text from nested elements
|
|
365
|
+
if (c?.props?.children) {
|
|
366
|
+
return typeof c.props.children === 'string'
|
|
367
|
+
? c.props.children
|
|
368
|
+
: String(c.props.children);
|
|
369
|
+
}
|
|
370
|
+
return '';
|
|
371
|
+
})
|
|
372
|
+
.join('');
|
|
373
|
+
} else if (children != null) {
|
|
374
|
+
codeContent = String(children);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (codeContent.trim()) {
|
|
378
|
+
const className = node.props.className;
|
|
379
|
+
const languageMatch = className.match(/language-(\w+)/);
|
|
380
|
+
const language = languageMatch?.[1] || 'ts';
|
|
381
|
+
const meta = node.props?.meta || '';
|
|
382
|
+
const titleMatch = meta.match(/title="([^"]+)"/);
|
|
383
|
+
const title = titleMatch?.[1] || node.props?.['data-title'];
|
|
384
|
+
|
|
385
|
+
blocks.push({
|
|
386
|
+
meta,
|
|
387
|
+
code: codeContent,
|
|
388
|
+
language,
|
|
389
|
+
title,
|
|
390
|
+
});
|
|
391
|
+
return; // Don't traverse children if we found a code block
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Recursively traverse children
|
|
396
|
+
if (node.props.children) {
|
|
397
|
+
React.Children.forEach(node.props.children, child =>
|
|
398
|
+
traverse(child, depth + 1),
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// First, try to extract code blocks from children
|
|
405
|
+
React.Children.forEach(children, traverse);
|
|
406
|
+
|
|
407
|
+
return blocks;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const codeBlocks = useMemo(() => parseCodeBlocks(children), [children]);
|
|
411
|
+
|
|
412
|
+
// If no code blocks, return children as-is
|
|
413
|
+
if (codeBlocks.length === 0) {
|
|
414
|
+
return <>{children}</>;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// If frameworks is empty but defaultImportPath is provided, use it for replacement
|
|
418
|
+
if (!frameworks || frameworks.length === 0) {
|
|
419
|
+
if (defaultImportPath) {
|
|
420
|
+
return (
|
|
421
|
+
<>
|
|
422
|
+
{codeBlocks.map((block, index) => {
|
|
423
|
+
const transformedCode = replaceImportPath(
|
|
424
|
+
block.code,
|
|
425
|
+
sourcePath,
|
|
426
|
+
defaultImportPath,
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<pre key={block.title || block.meta || `block-${index}`}>
|
|
431
|
+
<code className={`language-${block.language}`}>
|
|
432
|
+
{transformedCode}
|
|
433
|
+
</code>
|
|
434
|
+
</pre>
|
|
435
|
+
);
|
|
436
|
+
})}
|
|
437
|
+
</>
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
return <>{children}</>;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Single framework: render transformed code directly without Tabs
|
|
444
|
+
if (frameworks.length === 1) {
|
|
445
|
+
const framework = frameworks[0];
|
|
446
|
+
return (
|
|
447
|
+
<>
|
|
448
|
+
{codeBlocks.map((block, index) => {
|
|
449
|
+
// First replace source import path with framework's import path, then apply custom transform
|
|
450
|
+
let transformedCode = replaceImportPath(
|
|
451
|
+
block.code,
|
|
452
|
+
sourcePath,
|
|
453
|
+
framework.importPath,
|
|
454
|
+
);
|
|
455
|
+
if (framework.transform) {
|
|
456
|
+
transformedCode = framework.transform(transformedCode);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// If we have the original ShikiPre element, clone it with new code
|
|
460
|
+
if (block.originalElement) {
|
|
461
|
+
return (
|
|
462
|
+
<ShikiPreWithCode
|
|
463
|
+
key={block.title || block.meta || `block-${index}`}
|
|
464
|
+
originalElement={block.originalElement}
|
|
465
|
+
newCode={transformedCode}
|
|
466
|
+
originalCode={block.code}
|
|
467
|
+
sourcePath={sourcePath}
|
|
468
|
+
targetPath={framework.importPath}
|
|
469
|
+
/>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Fallback to simple pre/code if no original element
|
|
474
|
+
return (
|
|
475
|
+
<pre key={block.title || block.meta || `block-${index}`}>
|
|
476
|
+
<code className={`language-${block.language}`}>
|
|
477
|
+
{transformedCode}
|
|
478
|
+
</code>
|
|
479
|
+
</pre>
|
|
480
|
+
);
|
|
481
|
+
})}
|
|
482
|
+
</>
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Multiple frameworks: display using Tabs
|
|
487
|
+
// Try both formats: string array (labels) and object array
|
|
488
|
+
const defaultFrameworkId = defaultTab || frameworks[0]?.id || '';
|
|
489
|
+
const defaultFrameworkLabel =
|
|
490
|
+
frameworks.find(f => f.id === defaultFrameworkId)?.label ||
|
|
491
|
+
frameworks[0]?.label ||
|
|
492
|
+
'';
|
|
493
|
+
const frameworkValues = frameworks.map(f => f.label);
|
|
494
|
+
|
|
495
|
+
if (codeBlocks.length === 1) {
|
|
496
|
+
const block = codeBlocks[0];
|
|
497
|
+
|
|
498
|
+
return (
|
|
499
|
+
<div>
|
|
500
|
+
<Tabs
|
|
501
|
+
defaultValue={defaultFrameworkLabel}
|
|
502
|
+
values={frameworkValues}
|
|
503
|
+
{...({} as any)}
|
|
504
|
+
>
|
|
505
|
+
{frameworks.map(framework => {
|
|
506
|
+
// First replace source import path with framework's import path, then apply custom transform
|
|
507
|
+
let transformedCode = replaceImportPath(
|
|
508
|
+
block.code,
|
|
509
|
+
sourcePath,
|
|
510
|
+
framework.importPath,
|
|
511
|
+
);
|
|
512
|
+
if (framework.transform) {
|
|
513
|
+
transformedCode = framework.transform(transformedCode);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// If we have the original ShikiPre element, clone it with new code
|
|
517
|
+
const codeElement = block.originalElement ? (
|
|
518
|
+
<ShikiPreWithCode
|
|
519
|
+
originalElement={block.originalElement}
|
|
520
|
+
newCode={transformedCode}
|
|
521
|
+
originalCode={block.code}
|
|
522
|
+
sourcePath={sourcePath}
|
|
523
|
+
targetPath={framework.importPath}
|
|
524
|
+
/>
|
|
525
|
+
) : (
|
|
526
|
+
<pre>
|
|
527
|
+
<code className={`language-${block.language}`}>
|
|
528
|
+
{transformedCode}
|
|
529
|
+
</code>
|
|
530
|
+
</pre>
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
return (
|
|
534
|
+
<Tab
|
|
535
|
+
key={framework.id}
|
|
536
|
+
value={framework.label}
|
|
537
|
+
label={framework.label}
|
|
538
|
+
{...({} as any)}
|
|
539
|
+
>
|
|
540
|
+
{codeElement}
|
|
541
|
+
</Tab>
|
|
542
|
+
);
|
|
543
|
+
})}
|
|
544
|
+
</Tabs>
|
|
545
|
+
</div>
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Multiple code blocks: generate multiple framework versions for each block
|
|
550
|
+
return (
|
|
551
|
+
<>
|
|
552
|
+
{codeBlocks.map((block, blockIndex) => (
|
|
553
|
+
<div key={block.title || block.meta || `tabs-${blockIndex}`}>
|
|
554
|
+
<Tabs
|
|
555
|
+
defaultValue={defaultFrameworkLabel}
|
|
556
|
+
values={frameworkValues}
|
|
557
|
+
{...({} as any)}
|
|
558
|
+
>
|
|
559
|
+
{frameworks.map(framework => {
|
|
560
|
+
// First replace source import path with framework's import path, then apply custom transform
|
|
561
|
+
let transformedCode = replaceImportPath(
|
|
562
|
+
block.code,
|
|
563
|
+
sourcePath,
|
|
564
|
+
framework.importPath,
|
|
565
|
+
);
|
|
566
|
+
if (framework.transform) {
|
|
567
|
+
transformedCode = framework.transform(transformedCode);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// If we have the original ShikiPre element, clone it with new code
|
|
571
|
+
const codeElement = block.originalElement ? (
|
|
572
|
+
<ShikiPreWithCode
|
|
573
|
+
originalElement={block.originalElement}
|
|
574
|
+
newCode={transformedCode}
|
|
575
|
+
originalCode={block.code}
|
|
576
|
+
sourcePath={sourcePath}
|
|
577
|
+
targetPath={framework.importPath}
|
|
578
|
+
/>
|
|
579
|
+
) : (
|
|
580
|
+
<pre>
|
|
581
|
+
<code className={`language-${block.language}`}>
|
|
582
|
+
{transformedCode}
|
|
583
|
+
</code>
|
|
584
|
+
</pre>
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
return (
|
|
588
|
+
<Tab
|
|
589
|
+
key={framework.id}
|
|
590
|
+
value={framework.label}
|
|
591
|
+
label={framework.label}
|
|
592
|
+
{...({} as any)}
|
|
593
|
+
>
|
|
594
|
+
{codeElement}
|
|
595
|
+
</Tab>
|
|
596
|
+
);
|
|
597
|
+
})}
|
|
598
|
+
</Tabs>
|
|
599
|
+
</div>
|
|
600
|
+
))}
|
|
601
|
+
</>
|
|
602
|
+
);
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
export default FrameworkCode;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: bundleAnalyze
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# performance.bundleAnalyze
|
|
6
|
-
|
|
7
|
-
- **Type:** `Object | undefined`
|
|
8
|
-
|
|
9
|
-
Used to enable the [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) plugin to analyze the size of the output.
|
|
10
|
-
|
|
11
|
-
By default, Modern.js does not enable `webpack-bundle-analyzer`. When this feature is enabled, the default configuration is as follows:
|
|
12
|
-
|
|
13
|
-
```js
|
|
14
|
-
const defaultConfig = {
|
|
15
|
-
analyzerMode: 'static',
|
|
16
|
-
openAnalyzer: false,
|
|
17
|
-
// Distinguish by target names, such as `web`, `node`, etc.
|
|
18
|
-
reportFilename: `report-${target}.html`,
|
|
19
|
-
};
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
import RsbuildConfig from '@site-docs-en/components/rsbuild-config-tooltip';
|
|
23
|
-
|
|
24
|
-
<RsbuildConfig configName="performance.bundleAnalyze" />
|