@blocklet/pages-kit-block-studio 0.6.0 โ 0.6.2
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/lib/cjs/middlewares/init-resource-router.js +35 -0
- package/lib/cjs/middlewares/init-uploader-router.js +0 -3
- package/lib/cjs/plugins/_theme.js +3 -2
- package/lib/cjs/plugins/vite-plugin-block-studio.js +5 -125
- package/lib/cjs/plugins/vite-plugin-code-splitter.js +594 -246
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/cjs/utils/helper.js +17 -3
- package/lib/esm/middlewares/init-resource-router.js +36 -1
- package/lib/esm/middlewares/init-uploader-router.js +0 -3
- package/lib/esm/plugins/_theme.js +5 -4
- package/lib/esm/plugins/vite-plugin-block-studio.js +6 -126
- package/lib/esm/plugins/vite-plugin-code-splitter.js +562 -247
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/utils/helper.js +13 -2
- package/lib/types/plugins/vite-plugin-block-studio.d.ts +0 -1
- package/lib/types/plugins/vite-plugin-code-splitter.d.ts +8 -4
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/lib/types/utils/helper.d.ts +6 -1
- package/package.json +5 -5
|
@@ -1,188 +1,235 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Vite Plugin: Component Code Splitter
|
|
3
|
+
*
|
|
4
|
+
* ๐ WORKFLOW OVERVIEW:
|
|
5
|
+
*
|
|
6
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
7
|
+
* โ PLUGIN 1: CODE SPLITTER โ
|
|
8
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
9
|
+
*
|
|
10
|
+
* ๐ Phase 1: Config Stage
|
|
11
|
+
* โโโ Analyze entry files to find all exports
|
|
12
|
+
* โโโ Detect: default, EditComponent, PropertiesSchema, GetServerSideProps
|
|
13
|
+
* โโโ Create separate entry points for each export:
|
|
14
|
+
* โข Component โ Component?exportName=default&target=browser
|
|
15
|
+
* โข EditComponent โ Component_EditComponent?exportName=EditComponent&target=browser
|
|
16
|
+
* โข GetServerSideProps โ Component_GetServerSideProps?exportName=GetServerSideProps&target=node
|
|
17
|
+
*
|
|
18
|
+
* ๐ Phase 2: Resolve & Load Stage
|
|
19
|
+
* โโโ resolveId: Identify virtual modules with ?exportName= params
|
|
20
|
+
* โโโ load: Transform code and split exports
|
|
21
|
+
* โโโ Use esbuild for fast transformation
|
|
22
|
+
* โโโ Apply tree-shaking to remove unused code
|
|
23
|
+
* โโโ Filter to keep only specified exports
|
|
24
|
+
*
|
|
25
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
26
|
+
* โ PLUGIN 2: POST-BUILD PROCESSING โ
|
|
27
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
28
|
+
*
|
|
29
|
+
* ๐ง Phase 3: Post-Build Optimization
|
|
30
|
+
* โโโ Scan generated files and classify by target:
|
|
31
|
+
* โ โโโ Node.js Target: GetServerSideProps functions
|
|
32
|
+
* โ โโโ Browser Target: Components, EditComponents, Schemas
|
|
33
|
+
* โโโ Node.js files: Re-bundle with esbuild (platform: node, format: cjs)
|
|
34
|
+
* โโโ Browser files: Transpile with TypeScript + builtin module transformer
|
|
35
|
+
* โโโ Generate dependency mapping (chunks-map.json)
|
|
36
|
+
*
|
|
37
|
+
* ๐ก WHY THIS DESIGN:
|
|
38
|
+
* โข Reduces bundle size by splitting unused exports
|
|
39
|
+
* โข Optimizes for different runtime environments (browser vs node)
|
|
40
|
+
* โข Enables selective loading in Component Studio
|
|
41
|
+
* โข Maintains clean separation between edit-time and runtime code
|
|
42
|
+
*/
|
|
43
|
+
import { BuiltinModules } from '@blocklet/pages-kit/utils/builtin';
|
|
44
|
+
import { createBuiltinModuleTransformer } from '@blocklet/pages-kit/utils/typescript/builtin-module-transformer';
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
import { analyzeFileChunks } from '@blocklet/pages-kit/utils/typescript/chunks-analyzer-transformer';
|
|
47
|
+
import * as esbuild from 'esbuild';
|
|
48
|
+
import fs, { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
49
|
+
import pLimit from 'p-limit';
|
|
50
|
+
import path, { basename } from 'path';
|
|
3
51
|
import ts from 'typescript';
|
|
4
52
|
import { EDIT_COMPONENT_FILE_NAME_REGEX } from '../constants';
|
|
5
|
-
import { EDIT_COMPONENT_NAME, getEditComponentBlockName, logger } from '../utils/helper';
|
|
53
|
+
import { EDIT_COMPONENT_NAME, getEditComponentBlockName, logger, PROPERTIES_SCHEMA_NAME, AIGNE_OUTPUT_VALUE_SCHEMA_NAME, GET_SERVER_SIDE_PROPS_NAME, getPropertiesSchemaBlockName, getAigneOutputValueSchemaBlockName, getGetServerSidePropsBlockName, } from '../utils/helper';
|
|
54
|
+
const minify = true;
|
|
55
|
+
/**
|
|
56
|
+
* ๅฏผๅบ้
็ฝฎ
|
|
57
|
+
*/
|
|
58
|
+
const EXPORT_CONFIGS = [
|
|
59
|
+
{
|
|
60
|
+
exportName: 'default',
|
|
61
|
+
getBlockName: (key) => `${key}`,
|
|
62
|
+
description: 'Default',
|
|
63
|
+
supportSeparateFile: false,
|
|
64
|
+
target: 'browser',
|
|
65
|
+
external: [],
|
|
66
|
+
},
|
|
67
|
+
// {
|
|
68
|
+
// exportName: 'default',
|
|
69
|
+
// getBlockName: (key) => `${key}(default)`,
|
|
70
|
+
// description: 'Default CJS',
|
|
71
|
+
// supportSeparateFile: false,
|
|
72
|
+
// target: 'node',
|
|
73
|
+
// external: [],
|
|
74
|
+
// },
|
|
75
|
+
{
|
|
76
|
+
exportName: EDIT_COMPONENT_NAME,
|
|
77
|
+
getBlockName: getEditComponentBlockName,
|
|
78
|
+
description: 'EditComponent',
|
|
79
|
+
supportSeparateFile: true, // ๆฏๆ็ฌ็ซ็ @edit-component ๆไปถ
|
|
80
|
+
target: 'browser', // ๅ็ซฏ็ปไปถ
|
|
81
|
+
external: [],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
exportName: PROPERTIES_SCHEMA_NAME,
|
|
85
|
+
getBlockName: getPropertiesSchemaBlockName,
|
|
86
|
+
description: 'PropertiesSchema',
|
|
87
|
+
supportSeparateFile: false,
|
|
88
|
+
target: 'browser', // ๅ็ซฏ schema
|
|
89
|
+
external: [],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
exportName: AIGNE_OUTPUT_VALUE_SCHEMA_NAME,
|
|
93
|
+
getBlockName: getAigneOutputValueSchemaBlockName,
|
|
94
|
+
description: 'AigneOutputValueSchema',
|
|
95
|
+
supportSeparateFile: false,
|
|
96
|
+
target: 'browser', // ๅ็ซฏ schema
|
|
97
|
+
external: [],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
exportName: GET_SERVER_SIDE_PROPS_NAME,
|
|
101
|
+
getBlockName: getGetServerSidePropsBlockName,
|
|
102
|
+
description: 'GetServerSideProps',
|
|
103
|
+
supportSeparateFile: false,
|
|
104
|
+
target: 'node', // ๆๅก็ซฏๅฝๆฐ
|
|
105
|
+
format: 'esm', // esm ๆ ผๅผ
|
|
106
|
+
// ๆ้ค็ไพ่ต, ้่ฆ่ทๅ็ซฏไฟๆไธ่ด
|
|
107
|
+
external: [...Object.keys(BuiltinModules)],
|
|
108
|
+
},
|
|
109
|
+
];
|
|
6
110
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param sourceFile TypeScriptๆบๆไปถ
|
|
111
|
+
* ๅๆ็ปไปถๆไปถไธญ็ๆๆๅฏผๅบ
|
|
9
112
|
*/
|
|
10
|
-
function
|
|
11
|
-
const result = {
|
|
12
|
-
|
|
113
|
+
function analyzeAllExports(sourceFile) {
|
|
114
|
+
const result = {
|
|
115
|
+
namedExports: new Map(),
|
|
116
|
+
exportDeclarations: [],
|
|
117
|
+
};
|
|
13
118
|
function visit(node) {
|
|
14
|
-
//
|
|
15
|
-
if (ts.isExportAssignment(node)) {
|
|
16
|
-
result.defaultExport = {
|
|
17
|
-
pos: node.pos,
|
|
18
|
-
end: node.end,
|
|
19
|
-
};
|
|
119
|
+
// ้ป่ฎคๅฏผๅบ - export default xxx
|
|
120
|
+
if (ts.isExportAssignment(node) && !node.isExportEquals) {
|
|
121
|
+
result.defaultExport = { pos: node.pos, end: node.end };
|
|
20
122
|
}
|
|
21
|
-
//
|
|
123
|
+
// ๅฝๆฐๅฃฐๆ็้ป่ฎคๅฏผๅบ - export default function
|
|
22
124
|
if (ts.isFunctionDeclaration(node) && node.modifiers) {
|
|
23
125
|
const isExport = node.modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
24
126
|
const isDefault = node.modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
|
|
25
127
|
if (isExport && isDefault) {
|
|
26
|
-
result.defaultExport = {
|
|
27
|
-
pos: node.pos,
|
|
28
|
-
end: node.end,
|
|
29
|
-
};
|
|
128
|
+
result.defaultExport = { pos: node.pos, end: node.end };
|
|
30
129
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
result.
|
|
34
|
-
pos: node.pos,
|
|
35
|
-
end: node.end,
|
|
36
|
-
};
|
|
130
|
+
else if (isExport && !isDefault && node.name) {
|
|
131
|
+
// ๅฝๅๅฏผๅบ็ๅฝๆฐ - export function xxx
|
|
132
|
+
result.namedExports.set(node.name.text, { pos: node.pos, end: node.end });
|
|
37
133
|
}
|
|
38
134
|
}
|
|
39
|
-
//
|
|
135
|
+
// ็ฑปๅฃฐๆ็ๅฏผๅบ
|
|
40
136
|
if (ts.isClassDeclaration(node) && node.modifiers) {
|
|
41
137
|
const isExport = node.modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
result.editComponent = {
|
|
45
|
-
pos: node.pos,
|
|
46
|
-
end: node.end,
|
|
47
|
-
};
|
|
138
|
+
if (isExport && node.name) {
|
|
139
|
+
result.namedExports.set(node.name.text, { pos: node.pos, end: node.end });
|
|
48
140
|
}
|
|
49
141
|
}
|
|
50
|
-
//
|
|
142
|
+
// ๅ้ๅฃฐๆ็ๅฏผๅบ - export const xxx
|
|
51
143
|
if (ts.isVariableStatement(node) && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
52
144
|
const { declarations } = node.declarationList;
|
|
53
145
|
for (const decl of declarations) {
|
|
54
|
-
if (ts.isIdentifier(decl.name)
|
|
55
|
-
result.
|
|
56
|
-
pos: node.pos,
|
|
57
|
-
end: node.end,
|
|
58
|
-
};
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// ๆฃๆฅๅฏผๅบๅฃฐๆ
|
|
64
|
-
if (ts.isExportDeclaration(node) && node.exportClause) {
|
|
65
|
-
if (ts.isNamedExports(node.exportClause)) {
|
|
66
|
-
// ๆฃๆฅๆฎ้ๅฏผๅบ export { EditComponent }
|
|
67
|
-
const hasEditComponent = node.exportClause.elements.some((element) => ts.isExportSpecifier(element) && element.name.text === EDIT_COMPONENT_NAME);
|
|
68
|
-
// ๆฃๆฅ้ๅฝๅๅฏผๅบ export { SomeComponent as EditComponent }
|
|
69
|
-
const hasRenamedEditComponent = node.exportClause.elements.some((element) => ts.isExportSpecifier(element) && element.propertyName && element.name.text === EDIT_COMPONENT_NAME);
|
|
70
|
-
if (hasEditComponent || hasRenamedEditComponent) {
|
|
71
|
-
result.editComponent = {
|
|
72
|
-
pos: node.pos,
|
|
73
|
-
end: node.end,
|
|
74
|
-
};
|
|
146
|
+
if (ts.isIdentifier(decl.name)) {
|
|
147
|
+
result.namedExports.set(decl.name.text, { pos: node.pos, end: node.end });
|
|
75
148
|
}
|
|
76
149
|
}
|
|
77
150
|
}
|
|
78
|
-
//
|
|
79
|
-
if (ts.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
88
|
-
break;
|
|
151
|
+
// ๅฏผๅบๅฃฐๆ - export { xxx, yyy }
|
|
152
|
+
if (ts.isExportDeclaration(node)) {
|
|
153
|
+
result.exportDeclarations.push({ pos: node.pos, end: node.end });
|
|
154
|
+
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
155
|
+
// ่ฎฐๅฝ่ฟไธชๅฏผๅบๅฃฐๆๅ
ๅซ็ๆๆๅฏผๅบๅ
|
|
156
|
+
node.exportClause.elements.forEach((element) => {
|
|
157
|
+
if (ts.isExportSpecifier(element)) {
|
|
158
|
+
const exportName = element.propertyName ? element.name.text : element.name.text;
|
|
159
|
+
result.namedExports.set(exportName, { pos: node.pos, end: node.end });
|
|
89
160
|
}
|
|
90
|
-
}
|
|
161
|
+
});
|
|
91
162
|
}
|
|
92
163
|
}
|
|
93
|
-
// ้ๅฝๅค็ๆๆๅญ่็น
|
|
94
164
|
ts.forEachChild(node, visit);
|
|
95
165
|
}
|
|
96
|
-
// ๅผๅง้ๅ
|
|
97
166
|
visit(sourceFile);
|
|
98
167
|
return result;
|
|
99
168
|
}
|
|
100
169
|
/**
|
|
101
|
-
*
|
|
170
|
+
* ่งฃๆ exportName ๅๆฐ
|
|
102
171
|
*/
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (renderType === 'view') {
|
|
109
|
-
// ็งป้ค EditComponent
|
|
110
|
-
if (analysis.editComponent) {
|
|
111
|
-
// ๆ นๆฎไฝ็ฝฎๆฟๆขไธบ็ฉบๅ
ๅฎน
|
|
112
|
-
const beforeEdit = code.substring(0, analysis.editComponent.pos);
|
|
113
|
-
const afterEdit = code.substring(analysis.editComponent.end);
|
|
114
|
-
return beforeEdit + afterEdit;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else if (renderType === 'setting') {
|
|
118
|
-
// ็งป้คๆๆฟๆข้ป่ฎคๅฏผๅบ๏ผ็ดๆฅๅฏผๅบEditComponent
|
|
119
|
-
if (analysis.defaultExport && analysis.editComponent) {
|
|
120
|
-
// ็ผ่พๆจกๅผ๏ผไฟๆๅฝๅๅฏผๅบ๏ผไฝ่ฎฉEditComponentๅๆถไนไฝไธบ้ป่ฎคๅฏผๅบ
|
|
121
|
-
const defaultExport = `
|
|
122
|
-
// Export EditComponent as both named export and default
|
|
123
|
-
export { ${EDIT_COMPONENT_NAME} as default };
|
|
124
|
-
`;
|
|
125
|
-
const beforeDefault = code.substring(0, analysis.defaultExport.pos);
|
|
126
|
-
const afterDefault = code.substring(analysis.defaultExport.end);
|
|
127
|
-
// ๆฟๆข้ป่ฎคๅฏผๅบไธบEditComponent
|
|
128
|
-
return beforeDefault + defaultExport + afterDefault;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
// ๅฆๆๆฒกๆ่ฟ่กไฟฎๆน๏ผ่ฟๅๅๅงไปฃ็
|
|
132
|
-
return code;
|
|
172
|
+
function parseExportNames(exportNameParam) {
|
|
173
|
+
return exportNameParam
|
|
174
|
+
.split(',')
|
|
175
|
+
.map((name) => name.trim())
|
|
176
|
+
.filter(Boolean);
|
|
133
177
|
}
|
|
134
178
|
/**
|
|
135
|
-
*
|
|
179
|
+
* ่ฝฌๆขไปฃ็ ๏ผๅชไฟ็ๆๅฎ็ๅฏผๅบ
|
|
136
180
|
*/
|
|
137
|
-
function
|
|
181
|
+
function transformCodeWithExports(code, exportNames) {
|
|
138
182
|
try {
|
|
139
|
-
// ๅๅปบๆบๆไปถ
|
|
140
183
|
const sourceFile = ts.createSourceFile('temp.tsx', code, ts.ScriptTarget.Latest, true);
|
|
141
|
-
//
|
|
184
|
+
// ๆ ๅๅๅฏผๅบๅ็งฐ๏ผdefault ๅค็๏ผ
|
|
185
|
+
const normalizedExportNames = new Set(exportNames.map((name) => (name === 'default' ? 'default' : name)));
|
|
142
186
|
const transformerFactory = (context) => {
|
|
143
187
|
return (sourceFile) => {
|
|
144
|
-
// ่ฎฟ้ฎๅนถ่ฝฌๆข่็น
|
|
145
188
|
const visitor = (node) => {
|
|
146
|
-
//
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
189
|
+
// ๅค็้ป่ฎคๅฏผๅบ
|
|
190
|
+
if (ts.isExportAssignment(node) && !node.isExportEquals) {
|
|
191
|
+
return normalizedExportNames.has('default') ? node : ts.factory.createEmptyStatement();
|
|
192
|
+
}
|
|
193
|
+
// ๅค็ๅฝๆฐ/็ฑป็้ป่ฎคๅฏผๅบ
|
|
194
|
+
if (ts.isFunctionDeclaration(node) && node.modifiers) {
|
|
195
|
+
const isExport = node.modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
196
|
+
const isDefault = node.modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
|
|
197
|
+
if (isExport && isDefault) {
|
|
198
|
+
return normalizedExportNames.has('default') ? node : ts.factory.createEmptyStatement();
|
|
154
199
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const { elements } = node.exportClause;
|
|
158
|
-
if (elements.some((e) => ts.isExportSpecifier(e) && e.name.text === EDIT_COMPONENT_NAME)) {
|
|
159
|
-
// ๅฆๆๅชๆEditComponentไธไธชๅฏผๅบ๏ผๅฐฑๅฎๅ
จ็งป้ค
|
|
160
|
-
if (elements.length === 1) {
|
|
161
|
-
return ts.factory.createEmptyStatement();
|
|
162
|
-
}
|
|
163
|
-
// ๅฆๅๅๅปบๆฐ็ๅฏผๅบๅฃฐๆ๏ผไฝไธๅ
ๅซEditComponent
|
|
164
|
-
const newElements = elements.filter((e) => !(ts.isExportSpecifier(e) && e.name.text === EDIT_COMPONENT_NAME));
|
|
165
|
-
return ts.factory.createExportDeclaration(node.modifiers, node.isTypeOnly, ts.factory.createNamedExports(newElements), node.moduleSpecifier);
|
|
166
|
-
}
|
|
200
|
+
if (isExport && !isDefault && node.name) {
|
|
201
|
+
return normalizedExportNames.has(node.name.text) ? node : ts.factory.createEmptyStatement();
|
|
167
202
|
}
|
|
168
203
|
}
|
|
169
|
-
//
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
return ts.factory.createExportDeclaration(undefined, false, ts.factory.createNamedExports([
|
|
175
|
-
ts.factory.createExportSpecifier(false, ts.factory.createIdentifier(EDIT_COMPONENT_NAME), ts.factory.createIdentifier('default')),
|
|
176
|
-
]));
|
|
204
|
+
// ๅค็็ฑปๅฃฐๆ็ๅฏผๅบ
|
|
205
|
+
if (ts.isClassDeclaration(node) && node.modifiers) {
|
|
206
|
+
const isExport = node.modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
207
|
+
if (isExport && node.name) {
|
|
208
|
+
return normalizedExportNames.has(node.name.text) ? node : ts.factory.createEmptyStatement();
|
|
177
209
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
210
|
+
}
|
|
211
|
+
// ๅค็ๅ้ๅฃฐๆ็ๅฏผๅบ
|
|
212
|
+
if (ts.isVariableStatement(node) && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
213
|
+
const { declarations } = node.declarationList;
|
|
214
|
+
const shouldKeep = declarations.some((decl) => ts.isIdentifier(decl.name) && normalizedExportNames.has(decl.name.text));
|
|
215
|
+
return shouldKeep ? node : ts.factory.createEmptyStatement();
|
|
216
|
+
}
|
|
217
|
+
// ๅค็ๅฏผๅบๅฃฐๆ - export { xxx, yyy }
|
|
218
|
+
if (ts.isExportDeclaration(node) && node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
219
|
+
const { elements } = node.exportClause;
|
|
220
|
+
const keptElements = elements.filter((element) => {
|
|
221
|
+
if (ts.isExportSpecifier(element)) {
|
|
222
|
+
const exportName = element.propertyName ? element.name.text : element.name.text;
|
|
223
|
+
return normalizedExportNames.has(exportName);
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
});
|
|
227
|
+
if (keptElements.length === 0) {
|
|
228
|
+
return ts.factory.createEmptyStatement();
|
|
229
|
+
}
|
|
230
|
+
if (keptElements.length < elements.length) {
|
|
231
|
+
// ๅๅปบๆฐ็ๅฏผๅบๅฃฐๆ๏ผๅชๅ
ๅซ้่ฆไฟ็็ๅฏผๅบ
|
|
232
|
+
return ts.factory.createExportDeclaration(node.modifiers, node.isTypeOnly, ts.factory.createNamedExports(keptElements), node.moduleSpecifier);
|
|
186
233
|
}
|
|
187
234
|
}
|
|
188
235
|
return ts.visitEachChild(node, visitor, context);
|
|
@@ -195,10 +242,9 @@ function transformCodeWithPrinter(code, renderType) {
|
|
|
195
242
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
196
243
|
if (result.transformed.length > 0) {
|
|
197
244
|
const transformedSourceFile = result.transformed[0];
|
|
198
|
-
// TypeScript ็ฑปๅไฟ่ฏ
|
|
199
245
|
if (transformedSourceFile) {
|
|
200
246
|
const transformedCode = printer.printFile(transformedSourceFile);
|
|
201
|
-
result.dispose();
|
|
247
|
+
result.dispose();
|
|
202
248
|
return transformedCode;
|
|
203
249
|
}
|
|
204
250
|
}
|
|
@@ -206,40 +252,68 @@ function transformCodeWithPrinter(code, renderType) {
|
|
|
206
252
|
return code;
|
|
207
253
|
}
|
|
208
254
|
catch (error) {
|
|
209
|
-
logger.error('Error transforming with
|
|
210
|
-
|
|
211
|
-
return transformCode(code, renderType);
|
|
255
|
+
logger.error('Error transforming with exports:', error);
|
|
256
|
+
return code;
|
|
212
257
|
}
|
|
213
258
|
}
|
|
214
259
|
/**
|
|
215
|
-
*
|
|
260
|
+
* ็ฎๅ็ไปฃ็ ่ฝฌๆข๏ผ้่ฟๅญ็ฌฆไธฒๆฟๆข๏ผfallback๏ผ
|
|
216
261
|
*/
|
|
217
|
-
function
|
|
262
|
+
function transformCodeSimple(code, exportNames) {
|
|
263
|
+
const sourceFile = ts.createSourceFile('temp.tsx', code, ts.ScriptTarget.Latest, true);
|
|
264
|
+
const analysis = analyzeAllExports(sourceFile);
|
|
265
|
+
const normalizedExportNames = new Set(exportNames.map((name) => (name === 'default' ? 'default' : name)));
|
|
266
|
+
let result = code;
|
|
267
|
+
const toRemove = [];
|
|
268
|
+
// ๆถ้้่ฆ็งป้ค็่็น
|
|
269
|
+
if (analysis.defaultExport && !normalizedExportNames.has('default')) {
|
|
270
|
+
toRemove.push(analysis.defaultExport);
|
|
271
|
+
}
|
|
272
|
+
analysis.namedExports.forEach((range, name) => {
|
|
273
|
+
if (!normalizedExportNames.has(name)) {
|
|
274
|
+
toRemove.push(range);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
// ๆไฝ็ฝฎๅๅบๆๅ๏ผ้ฟๅ
ไฝ็ฝฎๅ็งป
|
|
278
|
+
toRemove.sort((a, b) => b.pos - a.pos);
|
|
279
|
+
// ็งป้คไธ้่ฆ็ๅฏผๅบ
|
|
280
|
+
for (const range of toRemove) {
|
|
281
|
+
const before = result.substring(0, range.pos);
|
|
282
|
+
const after = result.substring(range.end);
|
|
283
|
+
result = before + after;
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* ่ทๅๆไปถไธญ็ๆๆๅฏผๅบๅ็งฐ
|
|
289
|
+
*/
|
|
290
|
+
function getFileExports(filePath) {
|
|
218
291
|
try {
|
|
219
292
|
if (!fs.existsSync(filePath)) {
|
|
220
|
-
return
|
|
293
|
+
return [];
|
|
221
294
|
}
|
|
222
|
-
// ่ฏปๅๆไปถๅ
ๅฎน
|
|
223
295
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
224
|
-
// ๅๅปบๆบๆไปถ
|
|
225
296
|
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
297
|
+
const analysis = analyzeAllExports(sourceFile);
|
|
298
|
+
const exports = [];
|
|
299
|
+
if (analysis.defaultExport) {
|
|
300
|
+
exports.push('default');
|
|
301
|
+
}
|
|
302
|
+
exports.push(...Array.from(analysis.namedExports.keys()));
|
|
303
|
+
return exports;
|
|
230
304
|
}
|
|
231
305
|
catch (error) {
|
|
232
|
-
logger.warn(`Error
|
|
233
|
-
return
|
|
306
|
+
logger.warn(`Error analyzing exports in ${filePath}:`, error);
|
|
307
|
+
return [];
|
|
234
308
|
}
|
|
235
309
|
}
|
|
236
|
-
|
|
310
|
+
/**
|
|
311
|
+
* ๆฃๆฅ็ฎๅฝไธญๆฏๅฆๅญๅจ@edit-componentๆไปถ
|
|
312
|
+
*/
|
|
237
313
|
function findEditComponentFileInDir(filePath) {
|
|
238
314
|
const dirPath = path.dirname(filePath);
|
|
239
|
-
// ่ทๅ็ฎๅฝไธญ็ๆๆๆไปถ
|
|
240
315
|
try {
|
|
241
316
|
const files = fs.readdirSync(dirPath);
|
|
242
|
-
// ๆฃๆฅๆฏๅฆๆๅน้
็ๆไปถ
|
|
243
317
|
const file = files.find((file) => EDIT_COMPONENT_FILE_NAME_REGEX.test(file));
|
|
244
318
|
if (file) {
|
|
245
319
|
return path.join(dirPath, file);
|
|
@@ -252,105 +326,346 @@ function findEditComponentFileInDir(filePath) {
|
|
|
252
326
|
}
|
|
253
327
|
}
|
|
254
328
|
/**
|
|
255
|
-
* Vite
|
|
256
|
-
*
|
|
329
|
+
* ๐ฏ MAIN EXPORT: Vite Plugin Factory
|
|
330
|
+
* Creates two plugins that work together to split and optimize component code
|
|
257
331
|
*/
|
|
258
|
-
export function vitePluginCodeSplitter() {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
332
|
+
export function vitePluginCodeSplitter(options) {
|
|
333
|
+
const formats = options?.formats || ['es', 'cjs'];
|
|
334
|
+
const transpileBuiltinModule = options?.transpileBuiltinModule ?? true;
|
|
335
|
+
const skipBundleNodeTarget = options?.skipBundleNodeTarget ?? false;
|
|
336
|
+
return [
|
|
337
|
+
// ๐ง PLUGIN 1: CODE SPLITTER & TRANSFORMER
|
|
338
|
+
{
|
|
339
|
+
name: 'vite-plugin-code-splitter',
|
|
340
|
+
/**
|
|
341
|
+
* ๐ PHASE 1: CONFIG STAGE
|
|
342
|
+
* Analyzes entry files and creates separate entry points for each export type
|
|
343
|
+
*/
|
|
344
|
+
config(config) {
|
|
345
|
+
if (config.build && config.build.lib && typeof config.build.lib === 'object') {
|
|
346
|
+
const libConfig = config.build.lib;
|
|
347
|
+
if (libConfig.entry && typeof libConfig.entry === 'object' && !Array.isArray(libConfig.entry)) {
|
|
348
|
+
const newEntry = {};
|
|
349
|
+
Object.entries(libConfig.entry).forEach(([key, filePath]) => {
|
|
350
|
+
if (typeof filePath === 'string') {
|
|
351
|
+
try {
|
|
352
|
+
// ๅๆๆไปถไธญ็ๆๆๅฏผๅบ
|
|
353
|
+
const exports = getFileExports(filePath);
|
|
354
|
+
const fileName = path.basename(filePath);
|
|
355
|
+
logger.info(`Found exports in ${fileName}: ${exports.join(', ')}`);
|
|
356
|
+
// ๅค็ๆๆ้
็ฝฎ็ๅฏผๅบ็ฑปๅ
|
|
357
|
+
EXPORT_CONFIGS.forEach(({ exportName, getBlockName, description, supportSeparateFile, target }) => {
|
|
358
|
+
if (exports.includes(exportName)) {
|
|
359
|
+
newEntry[getBlockName(key)] = `${filePath}?exportName=${exportName}&target=${target}`;
|
|
360
|
+
logger.debug(`Added ${description} entry for ${fileName} (target: ${target})`);
|
|
361
|
+
}
|
|
362
|
+
else if (supportSeparateFile && exportName === EDIT_COMPONENT_NAME) {
|
|
363
|
+
// ๆฃๆฅๆฏๅฆๆ็ฌ็ซ็ @edit-component ๆไปถ
|
|
364
|
+
const editComponentFile = findEditComponentFileInDir(filePath);
|
|
365
|
+
if (editComponentFile) {
|
|
366
|
+
const editExports = getFileExports(editComponentFile);
|
|
367
|
+
if (editExports.includes(EDIT_COMPONENT_NAME)) {
|
|
368
|
+
newEntry[getEditComponentBlockName(key)] =
|
|
369
|
+
`${editComponentFile}?exportName=${EDIT_COMPONENT_NAME}&target=${target}`;
|
|
370
|
+
logger.info(`Found separate @edit-component file for ${path.basename(editComponentFile)} (target: ${target})`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
});
|
|
282
375
|
}
|
|
283
|
-
|
|
284
|
-
logger.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (editComponentFilePath) {
|
|
288
|
-
hasEditComponent = true;
|
|
289
|
-
logger.info(`Found separate @edit-component file for ${editComponentFilePath}`);
|
|
290
|
-
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
logger.warn(`Failed to analyze exports in ${filePath}: ${err}`);
|
|
378
|
+
// fallback ๅฐๅๅงๆไปถ
|
|
379
|
+
newEntry[key] = filePath;
|
|
291
380
|
}
|
|
292
381
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
newEntry[key] = `${filePath}?renderType=view`;
|
|
298
|
-
// ๅชไธบๅ
ๅซEditComponent็ๆไปถๅๅปบ็ผ่พๅ
ฅๅฃ
|
|
299
|
-
if (hasEditComponent) {
|
|
300
|
-
newEntry[getEditComponentBlockName(key)] = `${editComponentFilePath}?renderType=setting`;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
// ๆดๆฐ้
็ฝฎ
|
|
305
|
-
libConfig.entry = newEntry;
|
|
306
|
-
logger.info('Auto-split components enabled, entries updated');
|
|
382
|
+
});
|
|
383
|
+
libConfig.entry = newEntry;
|
|
384
|
+
logger.info('Auto-split components enabled with export-based splitting');
|
|
385
|
+
}
|
|
307
386
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
},
|
|
318
|
-
load(id) {
|
|
319
|
-
if (!id.includes('?renderType=')) {
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
try {
|
|
323
|
-
// ่งฃๆ id๏ผๆๅๆไปถ่ทฏๅพๅๆฅ่ฏขๅๆฐ
|
|
324
|
-
const [filePath, query] = id.split('?');
|
|
325
|
-
const params = new URLSearchParams(query);
|
|
326
|
-
const renderType = params.get('renderType');
|
|
327
|
-
if (!filePath || !renderType || (renderType !== 'view' && renderType !== 'setting')) {
|
|
328
|
-
return null;
|
|
387
|
+
return config;
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* ๐ PHASE 2A: RESOLVE STAGE
|
|
391
|
+
* Identifies virtual modules with exportName parameters
|
|
392
|
+
*/
|
|
393
|
+
resolveId(id) {
|
|
394
|
+
if (id.includes('?exportName=')) {
|
|
395
|
+
return id; // Mark as resolved to trigger load() for this virtual module
|
|
329
396
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
397
|
+
return null;
|
|
398
|
+
},
|
|
399
|
+
/**
|
|
400
|
+
* ๐ PHASE 2B: LOAD & TRANSFORM STAGE
|
|
401
|
+
* Loads and transforms code, keeping only specified exports
|
|
402
|
+
*/
|
|
403
|
+
async load(id) {
|
|
404
|
+
if (!id.includes('?exportName=')) {
|
|
333
405
|
return null;
|
|
334
406
|
}
|
|
335
|
-
// ่ฏปๅๆไปถๅ
ๅฎน
|
|
336
|
-
const code = fs.readFileSync(filePath, 'utf-8');
|
|
337
|
-
// ๆ นๆฎๆธฒๆ็ฑปๅ่ฟ่กไปฃ็ ่ฝฌๆข
|
|
338
|
-
// logger.info(`Splitting code for ${renderType}: ${filePath}`);
|
|
339
407
|
try {
|
|
340
|
-
|
|
341
|
-
|
|
408
|
+
const [filePath, query] = id.split('?');
|
|
409
|
+
const params = new URLSearchParams(query);
|
|
410
|
+
const exportNameParam = params.get('exportName');
|
|
411
|
+
const target = params.get('target') || 'browser';
|
|
412
|
+
if (!filePath || !exportNameParam) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
if (!fs.existsSync(filePath)) {
|
|
416
|
+
logger.error(`File not found: ${filePath}`);
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const exportNames = parseExportNames(exportNameParam);
|
|
420
|
+
logger.debug(`Processing exports [${exportNames.join(', ')}] with esbuild for: ${path.basename(filePath)} (target: ${target})`);
|
|
421
|
+
try {
|
|
422
|
+
// Use esbuild for transform + tree-shaking in one step
|
|
423
|
+
const result = await esbuild.transform(transformCodeWithExports(fs.readFileSync(filePath, 'utf-8'), exportNames), {
|
|
424
|
+
loader: path.extname(filePath).slice(1), // .tsx -> tsx
|
|
425
|
+
target: target === 'node' ? 'node18' : 'es2020',
|
|
426
|
+
platform: target === 'node' ? 'node' : 'browser',
|
|
427
|
+
format: 'esm',
|
|
428
|
+
jsx: 'automatic',
|
|
429
|
+
treeShaking: true,
|
|
430
|
+
minify,
|
|
431
|
+
sourcemap: false,
|
|
432
|
+
});
|
|
433
|
+
const transformedCode = result.code;
|
|
434
|
+
return transformedCode;
|
|
435
|
+
}
|
|
436
|
+
catch (esbuildError) {
|
|
437
|
+
logger.warn(`esbuild transform failed, falling back to TypeScript transform: ${esbuildError}`);
|
|
438
|
+
// Fallback to original method
|
|
439
|
+
const code = fs.readFileSync(filePath, 'utf-8');
|
|
440
|
+
try {
|
|
441
|
+
return transformCodeWithExports(code, exportNames);
|
|
442
|
+
}
|
|
443
|
+
catch (transformError) {
|
|
444
|
+
logger.warn(`TypeScript transform also failed, using simple transform: ${transformError}`);
|
|
445
|
+
return transformCodeSimple(code, exportNames);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
342
448
|
}
|
|
343
|
-
catch (
|
|
344
|
-
logger.
|
|
345
|
-
// ๅฆๆๅคฑ่ดฅ๏ผๅ้ๅฐๅบๆฌ่ฝฌๆข
|
|
346
|
-
return transformCode(code, renderType);
|
|
449
|
+
catch (error) {
|
|
450
|
+
logger.error(`Error processing module ${id}:`, error);
|
|
347
451
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
logger.error(`Error processing module ${id}:`, error);
|
|
351
|
-
}
|
|
352
|
-
return null;
|
|
452
|
+
return null;
|
|
453
|
+
},
|
|
353
454
|
},
|
|
354
|
-
|
|
455
|
+
// ๐ ๏ธ PLUGIN 2: POST-BUILD PROCESSOR
|
|
456
|
+
{
|
|
457
|
+
name: 'vite-plugin-code-splitter-post-build',
|
|
458
|
+
apply: 'build',
|
|
459
|
+
enforce: 'post',
|
|
460
|
+
/**
|
|
461
|
+
* ๐ง PHASE 3: POST-BUILD OPTIMIZATION
|
|
462
|
+
* Processes generated files based on their target environment
|
|
463
|
+
*/
|
|
464
|
+
async writeBundle(options) {
|
|
465
|
+
if (!transpileBuiltinModule) {
|
|
466
|
+
logger.info('Transpile builtin module is disabled, skipping post-build processing');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// ็จไบๅญๅจๆฏไธชๆ ผๅผ็ chunks ๆ ๅฐ
|
|
470
|
+
const allChunksMap = {};
|
|
471
|
+
const getExportConfig = (fileName) => {
|
|
472
|
+
// Extract export type from filename pattern: Component(ExportType).js
|
|
473
|
+
const match = fileName.match(/\(([^)]+)\)\.js$/);
|
|
474
|
+
if (!match || !match[1]) {
|
|
475
|
+
return null; // Regular component file, defaults to browser
|
|
476
|
+
}
|
|
477
|
+
const exportName = match[1];
|
|
478
|
+
return EXPORT_CONFIGS.find((config) => config.exportName === exportName || config.exportName.toLowerCase().includes(exportName.toLowerCase()));
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* ๐ฏ FILE TYPE CLASSIFIER
|
|
482
|
+
* Determines if a generated file should be treated as Node.js target
|
|
483
|
+
* Examples: Timeline(getServerSideProps).js โ Node.js target
|
|
484
|
+
* Timeline.js โ Browser target
|
|
485
|
+
*/
|
|
486
|
+
const isNodeTargetFile = (fileName) => {
|
|
487
|
+
// Look up target environment in EXPORT_CONFIGS
|
|
488
|
+
const config = getExportConfig(fileName);
|
|
489
|
+
return config?.target === 'node';
|
|
490
|
+
};
|
|
491
|
+
// ๆถ้ๆๆ Node.js ็ฎๆ ๆไปถ๏ผๅๅค้ๆฐๆๅปบ
|
|
492
|
+
const nodeTargetFiles = [];
|
|
493
|
+
// ๆซๆๆๅปบ่พๅบ๏ผๆพๅฐ Node.js ็ฎๆ ๆไปถ
|
|
494
|
+
const scanForNodeFiles = (dir) => {
|
|
495
|
+
const files = readdirSync(dir);
|
|
496
|
+
files.forEach((file) => {
|
|
497
|
+
const filePath = path.join(dir, file);
|
|
498
|
+
const stats = statSync(filePath);
|
|
499
|
+
if (stats.isDirectory()) {
|
|
500
|
+
scanForNodeFiles(filePath); // ้ๅฝๅค็ๅญ็ฎๅฝ
|
|
501
|
+
}
|
|
502
|
+
else if (file.endsWith('.js') && isNodeTargetFile(file)) {
|
|
503
|
+
logger.debug(`Found Node.js target file: ${file}, external: ${getExportConfig(file)?.external?.length ?? 0}`);
|
|
504
|
+
nodeTargetFiles.push({
|
|
505
|
+
filePath,
|
|
506
|
+
external: getExportConfig(file)?.external || [],
|
|
507
|
+
format: getExportConfig(file)?.format,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
};
|
|
512
|
+
formats.forEach((format) => {
|
|
513
|
+
const formatDir = path.resolve(options.dir || 'dist', `${format}`);
|
|
514
|
+
if (existsSync(formatDir)) {
|
|
515
|
+
scanForNodeFiles(formatDir);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
// ๅฆๆๆ Node.js ็ฎๆ ๆไปถ๏ผไธบๅฎไปฌ้ขๅค่งฆๅ Vite build
|
|
519
|
+
if (nodeTargetFiles.length > 0) {
|
|
520
|
+
logger.info(`Found ${nodeTargetFiles.length} Node.js target files, triggering clean Vite build...`);
|
|
521
|
+
await Promise.all(nodeTargetFiles.map(async ({ filePath, external, format }) => {
|
|
522
|
+
try {
|
|
523
|
+
const fileName = path.basename(filePath, '.js');
|
|
524
|
+
logger.debug(`Rebuilding Node.js target file: ${fileName}`);
|
|
525
|
+
// ไฝฟ็จ esbuild ่ฟ่กๅๆไปถๆๅ
|
|
526
|
+
const result = await esbuild.build({
|
|
527
|
+
entryPoints: [filePath],
|
|
528
|
+
bundle: !skipBundleNodeTarget, // ๆฏๅฆๆๅ
ๆๆไพ่ต
|
|
529
|
+
platform: 'node', // Node.js ๅนณๅฐ
|
|
530
|
+
format: format ?? 'cjs', // CommonJS ๆ ผๅผ
|
|
531
|
+
target: 'node18', // Node.js ็ฎๆ ็ๆฌ
|
|
532
|
+
outfile: filePath, // ็ดๆฅ่ฆ็ๅๆไปถ
|
|
533
|
+
allowOverwrite: true, // ๅ
่ฎธ่ฆ็่พๅ
ฅๆไปถ
|
|
534
|
+
external: external ?? [], // ๆ้ค็ไพ่ต
|
|
535
|
+
minify, // ๅฏๅๅฐ 50-70% ๅคงๅฐ
|
|
536
|
+
treeShaking: true, // ๅป้คๆชไฝฟ็จไปฃ็
|
|
537
|
+
sourcemap: false, // ไธ็ๆ sourcemap
|
|
538
|
+
write: true, // ็ดๆฅๅๅ
ฅๆไปถ
|
|
539
|
+
logLevel: 'error', // ๅชๆพ็คบ้่ฏฏ
|
|
540
|
+
// ่งฃๆ JSX ๅ TypeScript
|
|
541
|
+
loader: {
|
|
542
|
+
'.tsx': 'tsx',
|
|
543
|
+
'.ts': 'ts',
|
|
544
|
+
'.jsx': 'jsx',
|
|
545
|
+
'.js': 'js',
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
if (result.errors.length === 0) {
|
|
549
|
+
logger.info(`Successfully rebuilt ${fileName} as single CJS file using esbuild`);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
logger.error(`esbuild errors for ${fileName}:`, result.errors);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
logger.error(`Failed to rebuild Node.js target file ${filePath}:`, error);
|
|
557
|
+
}
|
|
558
|
+
}));
|
|
559
|
+
}
|
|
560
|
+
// ๅฏน็ๆ็ JavaScript ๆไปถ่ฟ่ก transpileModule ๅค็
|
|
561
|
+
const transpileBuiltinModuleFn = (dir, format) => {
|
|
562
|
+
const files = readdirSync(dir);
|
|
563
|
+
files.forEach((file) => {
|
|
564
|
+
const filePath = path.join(dir, file);
|
|
565
|
+
const stats = statSync(filePath);
|
|
566
|
+
if (stats.isDirectory()) {
|
|
567
|
+
transpileBuiltinModuleFn(filePath, format); // ้ๅฝๅค็ๅญ็ฎๅฝ
|
|
568
|
+
}
|
|
569
|
+
else if (file.endsWith('.js')) {
|
|
570
|
+
try {
|
|
571
|
+
// ่ทณ่ฟ node target ๆไปถ็ transpile
|
|
572
|
+
if (isNodeTargetFile(file)) {
|
|
573
|
+
logger.info(`Skipping transpile for Node.js target file: ${file}`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const script = readFileSync(filePath, 'utf8');
|
|
577
|
+
// @ts-ignore
|
|
578
|
+
const chunks = analyzeFileChunks(ts, script);
|
|
579
|
+
// ensure chunksMap is with all chunks
|
|
580
|
+
allChunksMap[basename(filePath)] = new Set(chunks);
|
|
581
|
+
const moduleMap = {
|
|
582
|
+
es: ts.ModuleKind.ESNext,
|
|
583
|
+
cjs: ts.ModuleKind.CommonJS,
|
|
584
|
+
umd: ts.ModuleKind.ESNext,
|
|
585
|
+
};
|
|
586
|
+
const code = ts.transpileModule(script, {
|
|
587
|
+
compilerOptions: {
|
|
588
|
+
jsx: ts.JsxEmit.React,
|
|
589
|
+
target: ts.ScriptTarget.ES2016,
|
|
590
|
+
module: moduleMap[format],
|
|
591
|
+
},
|
|
592
|
+
transformers: {
|
|
593
|
+
// @ts-ignore
|
|
594
|
+
before: [createBuiltinModuleTransformer(ts)],
|
|
595
|
+
},
|
|
596
|
+
}).outputText;
|
|
597
|
+
writeFileSync(filePath, code);
|
|
598
|
+
logger.info(`Transpiled browser target file: ${filePath}`);
|
|
599
|
+
}
|
|
600
|
+
catch (error) {
|
|
601
|
+
logger.error(`Failed to transpile ${filePath}:`, error);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
// ่ฟๅไธไธช Promise ไปฅไพฟ await
|
|
606
|
+
return new Promise((resolve, reject) => {
|
|
607
|
+
try {
|
|
608
|
+
resolve();
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
reject(error);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
};
|
|
615
|
+
// ่ทๅ่พๅบ็ฎๅฝ
|
|
616
|
+
const outDir = options.dir || 'dist';
|
|
617
|
+
const limit = pLimit(20);
|
|
618
|
+
// ไฝฟ็จ Promise.all ็ญๅพ
ๆๆๆ ผๅผๅค็ๅฎๆ
|
|
619
|
+
await Promise.all(formats.map(async (format) => {
|
|
620
|
+
const formatDir = path.resolve(outDir, `${format}`);
|
|
621
|
+
const chunkDir = path.resolve(formatDir, 'chunks');
|
|
622
|
+
// if not exists, create it
|
|
623
|
+
if (!existsSync(chunkDir)) {
|
|
624
|
+
mkdirSync(chunkDir, { recursive: true });
|
|
625
|
+
}
|
|
626
|
+
await limit(() => transpileBuiltinModuleFn(formatDir, format));
|
|
627
|
+
}));
|
|
628
|
+
// ็ฐๅจๅฏไปฅๅค็ๅฎๆดไพ่ต้พ
|
|
629
|
+
logger.info('All transpile tasks done, start to handle dependency chain...');
|
|
630
|
+
// ๅจ่ฟ้ๅค็ allChunksMap ็ๅฎๆดไพ่ต้พ
|
|
631
|
+
Object.entries(allChunksMap).forEach(([entryFile, directDeps]) => {
|
|
632
|
+
// ๆถ้ๆๆ้ดๆฅไพ่ต
|
|
633
|
+
const allDeps = [...directDeps];
|
|
634
|
+
// ้ๅฝๅฏปๆพ้ดๆฅไพ่ต
|
|
635
|
+
function findTransitiveDeps(chunks) {
|
|
636
|
+
chunks.forEach((chunk) => {
|
|
637
|
+
// ๅฆๆ่ฏฅchunkๆฌ่บซไนๆฏไธชๅ
ฅๅฃ(ๆไพ่ตๅ่กจ)
|
|
638
|
+
if (chunk in allChunksMap) {
|
|
639
|
+
const subDeps = allChunksMap[chunk];
|
|
640
|
+
// ๆทปๅ ๆชๅ
ๅซ็ไพ่ต
|
|
641
|
+
if (subDeps) {
|
|
642
|
+
subDeps.forEach((dep) => {
|
|
643
|
+
if (!allDeps.includes(dep)) {
|
|
644
|
+
allDeps.push(dep);
|
|
645
|
+
// ้ๅฝๅฏปๆพ่ฟไธชไพ่ต็ไพ่ต
|
|
646
|
+
findTransitiveDeps([dep]);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
// ๅผๅง้ๅฝๆฅๆพ
|
|
654
|
+
findTransitiveDeps([...directDeps]);
|
|
655
|
+
// ๆดๆฐไธบๅฎๆดไพ่ตๅ่กจ
|
|
656
|
+
allChunksMap[entryFile] = new Set(allDeps);
|
|
657
|
+
});
|
|
658
|
+
// ่ฝฌๆข Set ไธบๆฐ็ปไปฅไพฟๆญฃ็กฎๅบๅๅ
|
|
659
|
+
const serializedMap = Object.entries(allChunksMap).reduce((result, [key, deps]) => {
|
|
660
|
+
result[key] = Array.from(deps);
|
|
661
|
+
return result;
|
|
662
|
+
}, {});
|
|
663
|
+
formats.forEach((format) => {
|
|
664
|
+
// ๅญไธๆฅ
|
|
665
|
+
writeFileSync(path.join(outDir, `${format}/chunks-map.json`), JSON.stringify(serializedMap, null, 2));
|
|
666
|
+
});
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
];
|
|
355
670
|
}
|
|
356
671
|
export default vitePluginCodeSplitter;
|