tywrap 0.5.1 → 0.6.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/dist/cli.js +8 -6
- package/dist/cli.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +236 -147
- package/dist/config/index.js.map +1 -1
- package/dist/core/annotation-parser.d.ts.map +1 -1
- package/dist/core/annotation-parser.js +93 -83
- package/dist/core/annotation-parser.js.map +1 -1
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +26 -0
- package/dist/core/discovery.js.map +1 -1
- package/dist/core/emit-call.d.ts +58 -0
- package/dist/core/emit-call.d.ts.map +1 -0
- package/dist/core/emit-call.js +103 -0
- package/dist/core/emit-call.js.map +1 -0
- package/dist/core/generator.d.ts +2 -0
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +54 -211
- package/dist/core/generator.js.map +1 -1
- package/dist/core/mapper.d.ts.map +1 -1
- package/dist/core/mapper.js +118 -104
- package/dist/core/mapper.js.map +1 -1
- package/dist/index.d.ts +6 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -31
- package/dist/index.js.map +1 -1
- package/dist/runtime/bounded-context.d.ts +2 -2
- package/dist/runtime/bounded-context.js +2 -2
- package/dist/runtime/{safe-codec.d.ts → bridge-codec.d.ts} +11 -11
- package/dist/runtime/bridge-codec.d.ts.map +1 -0
- package/dist/runtime/{safe-codec.js → bridge-codec.js} +26 -26
- package/dist/runtime/bridge-codec.js.map +1 -0
- package/dist/runtime/{http-io.d.ts → http-transport.d.ts} +6 -6
- package/dist/runtime/http-transport.d.ts.map +1 -0
- package/dist/runtime/{http-io.js → http-transport.js} +3 -3
- package/dist/runtime/http-transport.js.map +1 -0
- package/dist/runtime/http.d.ts +3 -3
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.js +4 -4
- package/dist/runtime/http.js.map +1 -1
- package/dist/runtime/index.d.ts +9 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +9 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/node.d.ts +6 -6
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +7 -7
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +11 -11
- package/dist/runtime/pooled-transport.d.ts.map +1 -1
- package/dist/runtime/pooled-transport.js +10 -10
- package/dist/runtime/pooled-transport.js.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
- package/dist/runtime/{pyodide-io.d.ts → pyodide-transport.d.ts} +8 -8
- package/dist/runtime/pyodide-transport.d.ts.map +1 -0
- package/dist/runtime/{pyodide-io.js → pyodide-transport.js} +5 -5
- package/dist/runtime/pyodide-transport.js.map +1 -0
- package/dist/runtime/pyodide.d.ts +3 -3
- package/dist/runtime/pyodide.d.ts.map +1 -1
- package/dist/runtime/pyodide.js +5 -5
- package/dist/runtime/pyodide.js.map +1 -1
- package/dist/runtime/rpc-client.d.ts +5 -5
- package/dist/runtime/rpc-client.d.ts.map +1 -1
- package/dist/runtime/rpc-client.js +6 -7
- package/dist/runtime/rpc-client.js.map +1 -1
- package/dist/runtime/{process-io.d.ts → subprocess-transport.d.ts} +9 -9
- package/dist/runtime/subprocess-transport.d.ts.map +1 -0
- package/dist/runtime/{process-io.js → subprocess-transport.js} +6 -6
- package/dist/runtime/subprocess-transport.js.map +1 -0
- package/dist/runtime/{worker-pool.d.ts → transport-pool.d.ts} +15 -15
- package/dist/runtime/transport-pool.d.ts.map +1 -0
- package/dist/runtime/{worker-pool.js → transport-pool.js} +6 -6
- package/dist/runtime/transport-pool.js.map +1 -0
- package/dist/runtime/transport.d.ts +14 -8
- package/dist/runtime/transport.d.ts.map +1 -1
- package/dist/runtime/transport.js +10 -4
- package/dist/runtime/transport.js.map +1 -1
- package/dist/types/index.d.ts +10 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/cache.d.ts +2 -2
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/cache.js +2 -2
- package/dist/utils/cache.js.map +1 -1
- package/dist/utils/codec.d.ts +1 -1
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +181 -182
- package/dist/utils/codec.js.map +1 -1
- package/dist/version.d.ts +12 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -2
- package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
- package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
- package/runtime/python_bridge.py +54 -6
- package/runtime/safe_codec.py +8 -8
- package/runtime/tywrap_bridge_core.py +159 -23
- package/src/cli.ts +8 -12
- package/src/config/index.ts +271 -165
- package/src/core/annotation-parser.ts +112 -105
- package/src/core/discovery.ts +32 -0
- package/src/core/emit-call.ts +171 -0
- package/src/core/generator.ts +67 -263
- package/src/core/mapper.ts +125 -110
- package/src/index.ts +11 -68
- package/src/runtime/bounded-context.ts +2 -2
- package/src/runtime/{safe-codec.ts → bridge-codec.ts} +27 -27
- package/src/runtime/{http-io.ts → http-transport.ts} +5 -5
- package/src/runtime/http.ts +5 -5
- package/src/runtime/index.ts +23 -1
- package/src/runtime/node.ts +15 -15
- package/src/runtime/pooled-transport.ts +15 -15
- package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
- package/src/runtime/{pyodide-io.ts → pyodide-transport.ts} +7 -7
- package/src/runtime/pyodide.ts +6 -6
- package/src/runtime/rpc-client.ts +12 -8
- package/src/runtime/{process-io.ts → subprocess-transport.ts} +8 -8
- package/src/runtime/{worker-pool.ts → transport-pool.ts} +26 -26
- package/src/runtime/transport.ts +15 -8
- package/src/types/index.ts +10 -1
- package/src/utils/cache.ts +2 -2
- package/src/utils/codec.ts +217 -196
- package/src/version.ts +12 -0
- package/dist/runtime/http-io.d.ts.map +0 -1
- package/dist/runtime/http-io.js.map +0 -1
- package/dist/runtime/optimized-node.d.ts +0 -23
- package/dist/runtime/optimized-node.d.ts.map +0 -1
- package/dist/runtime/optimized-node.js +0 -23
- package/dist/runtime/optimized-node.js.map +0 -1
- package/dist/runtime/process-io.d.ts.map +0 -1
- package/dist/runtime/process-io.js.map +0 -1
- package/dist/runtime/protocol.d.ts +0 -3
- package/dist/runtime/protocol.d.ts.map +0 -1
- package/dist/runtime/protocol.js +0 -3
- package/dist/runtime/protocol.js.map +0 -1
- package/dist/runtime/pyodide-io.d.ts.map +0 -1
- package/dist/runtime/pyodide-io.js.map +0 -1
- package/dist/runtime/safe-codec.d.ts.map +0 -1
- package/dist/runtime/safe-codec.js.map +0 -1
- package/dist/runtime/worker-pool.d.ts.map +0 -1
- package/dist/runtime/worker-pool.js.map +0 -1
- package/dist/utils/bundle-optimizer.d.ts +0 -182
- package/dist/utils/bundle-optimizer.d.ts.map +0 -1
- package/dist/utils/bundle-optimizer.js +0 -680
- package/dist/utils/bundle-optimizer.js.map +0 -1
- package/dist/utils/memory-profiler.d.ts +0 -123
- package/dist/utils/memory-profiler.d.ts.map +0 -1
- package/dist/utils/memory-profiler.js +0 -426
- package/dist/utils/memory-profiler.js.map +0 -1
- package/src/runtime/optimized-node.ts +0 -25
- package/src/runtime/protocol.ts +0 -2
- package/src/utils/bundle-optimizer.ts +0 -876
- package/src/utils/memory-profiler.ts +0 -582
|
@@ -1,876 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bundle Size Optimization for tywrap
|
|
3
|
-
* Implements tree-shaking, code splitting, and runtime optimization strategies
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { writeFile } from 'node:fs/promises';
|
|
7
|
-
import ts from 'typescript';
|
|
8
|
-
|
|
9
|
-
import type { GeneratedCode } from '../types/index.js';
|
|
10
|
-
|
|
11
|
-
import { globalCache } from './cache.js';
|
|
12
|
-
|
|
13
|
-
export interface BundleAnalysis {
|
|
14
|
-
totalSize: number;
|
|
15
|
-
compressedSize: number;
|
|
16
|
-
compressionRatio: number;
|
|
17
|
-
modules: ModuleAnalysis[];
|
|
18
|
-
runtime: RuntimeAnalysis;
|
|
19
|
-
suggestions: OptimizationSuggestion[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface BundleManifest {
|
|
23
|
-
version: string;
|
|
24
|
-
target: BundleOptions['target'];
|
|
25
|
-
modules: string[];
|
|
26
|
-
runtime: BundleOptions['runtimeMode'] | 'minimal';
|
|
27
|
-
optimizations: {
|
|
28
|
-
treeShaking: boolean;
|
|
29
|
-
minified: boolean;
|
|
30
|
-
compressed: boolean;
|
|
31
|
-
codeSplitting: boolean;
|
|
32
|
-
};
|
|
33
|
-
stats: BundleAnalysis;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface ModuleAnalysis {
|
|
37
|
-
name: string;
|
|
38
|
-
size: number;
|
|
39
|
-
exports: string[];
|
|
40
|
-
imports: string[];
|
|
41
|
-
unusedExports: string[];
|
|
42
|
-
dependencies: string[];
|
|
43
|
-
treeshakeable: boolean;
|
|
44
|
-
complexity: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface RuntimeAnalysis {
|
|
48
|
-
coreSize: number;
|
|
49
|
-
bridgeSize: number;
|
|
50
|
-
codecSize: number;
|
|
51
|
-
utilsSize: number;
|
|
52
|
-
totalSize: number;
|
|
53
|
-
minimizable: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface OptimizationSuggestion {
|
|
57
|
-
type: 'tree-shaking' | 'code-splitting' | 'runtime-minimal' | 'compression' | 'lazy-loading';
|
|
58
|
-
severity: 'low' | 'medium' | 'high';
|
|
59
|
-
description: string;
|
|
60
|
-
estimatedSaving: number; // bytes
|
|
61
|
-
implementation: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface BundleOptions {
|
|
65
|
-
target: 'node' | 'browser' | 'deno' | 'bun' | 'universal';
|
|
66
|
-
minify: boolean;
|
|
67
|
-
compress: boolean;
|
|
68
|
-
treeShaking: boolean;
|
|
69
|
-
codeSplitting: boolean;
|
|
70
|
-
runtimeMode: 'full' | 'minimal' | 'lazy';
|
|
71
|
-
outputFormat: 'esm' | 'cjs' | 'umd';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class BundleOptimizer {
|
|
75
|
-
private options: BundleOptions;
|
|
76
|
-
private generatedModules = new Map<string, GeneratedCode>();
|
|
77
|
-
private analysisCache = new Map<string, ModuleAnalysis>();
|
|
78
|
-
|
|
79
|
-
constructor(options: Partial<BundleOptions> = {}) {
|
|
80
|
-
this.options = {
|
|
81
|
-
target: 'universal',
|
|
82
|
-
minify: true,
|
|
83
|
-
compress: true,
|
|
84
|
-
treeShaking: true,
|
|
85
|
-
codeSplitting: false,
|
|
86
|
-
runtimeMode: 'minimal',
|
|
87
|
-
outputFormat: 'esm',
|
|
88
|
-
...options,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Add generated module for optimization
|
|
94
|
-
*/
|
|
95
|
-
addModule(name: string, code: GeneratedCode): void {
|
|
96
|
-
this.generatedModules.set(name, code);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Analyze bundle composition and identify optimization opportunities
|
|
101
|
-
*/
|
|
102
|
-
async analyzeBundles(): Promise<BundleAnalysis> {
|
|
103
|
-
const modules: ModuleAnalysis[] = [];
|
|
104
|
-
let totalSize = 0;
|
|
105
|
-
|
|
106
|
-
// Analyze each generated module
|
|
107
|
-
for (const [name, code] of this.generatedModules) {
|
|
108
|
-
const analysis = await this.analyzeModule(name, code);
|
|
109
|
-
modules.push(analysis);
|
|
110
|
-
totalSize += analysis.size;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Analyze runtime overhead
|
|
114
|
-
const runtime = this.analyzeRuntime();
|
|
115
|
-
totalSize += runtime.totalSize;
|
|
116
|
-
|
|
117
|
-
// Estimate compressed size
|
|
118
|
-
const compressedSize = this.estimateCompressedSize(totalSize);
|
|
119
|
-
const compressionRatio = totalSize > 0 ? compressedSize / totalSize : 0;
|
|
120
|
-
|
|
121
|
-
// Generate optimization suggestions
|
|
122
|
-
const suggestions = this.generateOptimizationSuggestions(modules, runtime);
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
totalSize,
|
|
126
|
-
compressedSize,
|
|
127
|
-
compressionRatio,
|
|
128
|
-
modules,
|
|
129
|
-
runtime,
|
|
130
|
-
suggestions,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Analyze individual module
|
|
136
|
-
*/
|
|
137
|
-
private async analyzeModule(name: string, code: GeneratedCode): Promise<ModuleAnalysis> {
|
|
138
|
-
const cacheKey = globalCache.generateKey('module_analysis', name, code.typescript);
|
|
139
|
-
const cached = await globalCache.get<ModuleAnalysis>(cacheKey);
|
|
140
|
-
if (cached) {
|
|
141
|
-
return cached;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const typescript = code.typescript;
|
|
145
|
-
const size = Buffer.byteLength(typescript, 'utf8');
|
|
146
|
-
|
|
147
|
-
// Extract exports
|
|
148
|
-
const exports = this.extractExports(typescript);
|
|
149
|
-
|
|
150
|
-
// Extract imports
|
|
151
|
-
const imports = this.extractImports(typescript);
|
|
152
|
-
|
|
153
|
-
// Find dependencies
|
|
154
|
-
const dependencies = this.extractDependencies(typescript);
|
|
155
|
-
|
|
156
|
-
// Calculate complexity
|
|
157
|
-
const complexity = this.calculateComplexity(typescript);
|
|
158
|
-
|
|
159
|
-
// Check if treeshakeable
|
|
160
|
-
const treeshakeable = this.isTreeshakeable(typescript);
|
|
161
|
-
|
|
162
|
-
const analysis: ModuleAnalysis = {
|
|
163
|
-
name,
|
|
164
|
-
size,
|
|
165
|
-
exports,
|
|
166
|
-
imports,
|
|
167
|
-
unusedExports: [], // Would need usage analysis
|
|
168
|
-
dependencies,
|
|
169
|
-
treeshakeable,
|
|
170
|
-
complexity,
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
// Cache the analysis
|
|
174
|
-
await globalCache.set(cacheKey, analysis, { computeTime: 0 });
|
|
175
|
-
this.analysisCache.set(name, analysis);
|
|
176
|
-
|
|
177
|
-
return analysis;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Extract export statements from TypeScript code
|
|
182
|
-
*/
|
|
183
|
-
private extractExports(code: string): string[] {
|
|
184
|
-
const source = this.parseSource(code);
|
|
185
|
-
const exports = new Set<string>();
|
|
186
|
-
|
|
187
|
-
const visit = (node: ts.Node): void => {
|
|
188
|
-
if (
|
|
189
|
-
ts.isFunctionDeclaration(node) ||
|
|
190
|
-
ts.isClassDeclaration(node) ||
|
|
191
|
-
ts.isInterfaceDeclaration(node) ||
|
|
192
|
-
ts.isTypeAliasDeclaration(node) ||
|
|
193
|
-
ts.isEnumDeclaration(node)
|
|
194
|
-
) {
|
|
195
|
-
if (node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
196
|
-
const name = (node as ts.NamedDeclaration).name?.getText();
|
|
197
|
-
if (name) {
|
|
198
|
-
exports.add(name);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
} else if (ts.isVariableStatement(node)) {
|
|
202
|
-
if (node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
203
|
-
node.declarationList.declarations.forEach(d => {
|
|
204
|
-
if (ts.isIdentifier(d.name)) {
|
|
205
|
-
exports.add(d.name.text);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
} else if (ts.isExportAssignment(node)) {
|
|
210
|
-
if (ts.isIdentifier(node.expression)) {
|
|
211
|
-
exports.add(node.expression.text);
|
|
212
|
-
} else {
|
|
213
|
-
exports.add('default');
|
|
214
|
-
}
|
|
215
|
-
} else if (ts.isExportDeclaration(node)) {
|
|
216
|
-
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
217
|
-
node.exportClause.elements.forEach(el => {
|
|
218
|
-
exports.add((el.propertyName ?? el.name).text);
|
|
219
|
-
});
|
|
220
|
-
} else {
|
|
221
|
-
exports.add('*');
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
ts.forEachChild(node, visit);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
visit(source);
|
|
228
|
-
return Array.from(exports);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Extract import statements from TypeScript code
|
|
233
|
-
*/
|
|
234
|
-
private extractImports(code: string): string[] {
|
|
235
|
-
const source = this.parseSource(code);
|
|
236
|
-
const imports = new Set<string>();
|
|
237
|
-
|
|
238
|
-
const visit = (node: ts.Node): void => {
|
|
239
|
-
if (ts.isImportDeclaration(node)) {
|
|
240
|
-
const moduleName = (node.moduleSpecifier as ts.StringLiteral).text;
|
|
241
|
-
imports.add(moduleName);
|
|
242
|
-
const clause = node.importClause;
|
|
243
|
-
if (clause) {
|
|
244
|
-
if (clause.name) {
|
|
245
|
-
imports.add(clause.name.text);
|
|
246
|
-
}
|
|
247
|
-
if (clause.namedBindings) {
|
|
248
|
-
if (ts.isNamedImports(clause.namedBindings)) {
|
|
249
|
-
clause.namedBindings.elements.forEach(el => {
|
|
250
|
-
imports.add((el.propertyName ?? el.name).text);
|
|
251
|
-
});
|
|
252
|
-
} else if (ts.isNamespaceImport(clause.namedBindings)) {
|
|
253
|
-
imports.add(clause.namedBindings.name.text);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
} else if (
|
|
258
|
-
ts.isCallExpression(node) &&
|
|
259
|
-
node.expression.kind === ts.SyntaxKind.ImportKeyword
|
|
260
|
-
) {
|
|
261
|
-
const arg = node.arguments[0];
|
|
262
|
-
if (arg && ts.isStringLiteral(arg)) {
|
|
263
|
-
imports.add(arg.text);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
ts.forEachChild(node, visit);
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
visit(source);
|
|
270
|
-
return Array.from(imports);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Extract module dependencies
|
|
275
|
-
*/
|
|
276
|
-
private extractDependencies(code: string): string[] {
|
|
277
|
-
const source = this.parseSource(code);
|
|
278
|
-
const dependencies = new Set<string>();
|
|
279
|
-
|
|
280
|
-
const visit = (node: ts.Node): void => {
|
|
281
|
-
if (ts.isPropertyAccessExpression(node)) {
|
|
282
|
-
const expr = node.expression;
|
|
283
|
-
if (
|
|
284
|
-
ts.isCallExpression(expr) &&
|
|
285
|
-
ts.isIdentifier(expr.expression) &&
|
|
286
|
-
expr.expression.text === 'getRuntimeBridge'
|
|
287
|
-
) {
|
|
288
|
-
dependencies.add('runtime-bridge');
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (ts.isIdentifier(node)) {
|
|
293
|
-
if (node.text === 'decodeValue' || node.text === 'encodeValue') {
|
|
294
|
-
dependencies.add('codec');
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (ts.isImportDeclaration(node)) {
|
|
299
|
-
const moduleName = (node.moduleSpecifier as ts.StringLiteral).text;
|
|
300
|
-
if (node.importClause?.isTypeOnly) {
|
|
301
|
-
dependencies.add(moduleName);
|
|
302
|
-
} else {
|
|
303
|
-
const comments = ts.getTrailingCommentRanges(code, node.end) ?? [];
|
|
304
|
-
for (const range of comments) {
|
|
305
|
-
const comment = code.slice(range.pos, range.end);
|
|
306
|
-
if (/types?/i.test(comment)) {
|
|
307
|
-
dependencies.add(moduleName);
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
ts.forEachChild(node, visit);
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
visit(source);
|
|
318
|
-
return Array.from(dependencies);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
private parseSource(code: string): ts.SourceFile {
|
|
322
|
-
return ts.createSourceFile('module.ts', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Calculate code complexity score
|
|
327
|
-
*/
|
|
328
|
-
private calculateComplexity(code: string): number {
|
|
329
|
-
let complexity = 0;
|
|
330
|
-
|
|
331
|
-
// Function complexity
|
|
332
|
-
const functions = (code.match(/\bfunction\b/g) ?? []).length;
|
|
333
|
-
const asyncFunctions = (code.match(/\basync\s+function\b/g) ?? []).length;
|
|
334
|
-
complexity += functions + asyncFunctions * 1.5;
|
|
335
|
-
|
|
336
|
-
// Class complexity
|
|
337
|
-
const classes = (code.match(/\bclass\b/g) ?? []).length;
|
|
338
|
-
complexity += classes * 2;
|
|
339
|
-
|
|
340
|
-
// Interface complexity
|
|
341
|
-
const interfaces = (code.match(/\binterface\b/g) ?? []).length;
|
|
342
|
-
complexity += interfaces * 0.5;
|
|
343
|
-
|
|
344
|
-
// Type alias complexity
|
|
345
|
-
const types = (code.match(/\btype\b/g) ?? []).length;
|
|
346
|
-
complexity += types * 0.3;
|
|
347
|
-
|
|
348
|
-
// Generic complexity
|
|
349
|
-
const generics = (code.match(/<[^>]*>/g) ?? []).length;
|
|
350
|
-
complexity += generics * 0.2;
|
|
351
|
-
|
|
352
|
-
return Math.round(complexity);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Check if code is tree-shakeable
|
|
357
|
-
*/
|
|
358
|
-
private isTreeshakeable(code: string): boolean {
|
|
359
|
-
// Code is tree-shakeable if it uses ES modules and has no side effects
|
|
360
|
-
const hasESModules = code.includes('export') && !code.includes('module.exports');
|
|
361
|
-
const hasSideEffects =
|
|
362
|
-
code.includes('console.') || code.includes('window.') || code.includes('global.');
|
|
363
|
-
|
|
364
|
-
return hasESModules && !hasSideEffects;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Analyze runtime overhead
|
|
369
|
-
*/
|
|
370
|
-
private analyzeRuntime(): RuntimeAnalysis {
|
|
371
|
-
const estimates = {
|
|
372
|
-
coreSize: 5 * 1024, // 5KB - Core runtime logic
|
|
373
|
-
bridgeSize: this.estimateBridgeSize(),
|
|
374
|
-
codecSize: 8 * 1024, // 8KB - Codec utilities
|
|
375
|
-
utilsSize: 3 * 1024, // 3KB - Utility functions
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const totalSize = Object.values(estimates).reduce((sum, size) => sum + size, 0);
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
...estimates,
|
|
382
|
-
totalSize,
|
|
383
|
-
minimizable: this.options.runtimeMode !== 'full',
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Estimate bridge size based on target
|
|
389
|
-
*/
|
|
390
|
-
private estimateBridgeSize(): number {
|
|
391
|
-
switch (this.options.target) {
|
|
392
|
-
case 'node':
|
|
393
|
-
return 12 * 1024; // 12KB - Node.js specific bridge
|
|
394
|
-
case 'browser':
|
|
395
|
-
return 15 * 1024; // 15KB - Browser/Pyodide bridge
|
|
396
|
-
case 'deno':
|
|
397
|
-
return 10 * 1024; // 10KB - Deno bridge
|
|
398
|
-
case 'bun':
|
|
399
|
-
return 8 * 1024; // 8KB - Bun bridge
|
|
400
|
-
case 'universal':
|
|
401
|
-
return 25 * 1024; // 25KB - Universal bridge with all runtimes
|
|
402
|
-
default:
|
|
403
|
-
return 15 * 1024;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Estimate compressed size using typical compression ratios
|
|
409
|
-
*/
|
|
410
|
-
private estimateCompressedSize(originalSize: number): number {
|
|
411
|
-
// Typical gzip compression ratios for JavaScript/TypeScript:
|
|
412
|
-
// - Minified: 3:1 to 4:1
|
|
413
|
-
// - Non-minified: 4:1 to 6:1
|
|
414
|
-
const compressionRatio = this.options.minify ? 3.5 : 5;
|
|
415
|
-
return Math.round(originalSize / compressionRatio);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Generate optimization suggestions
|
|
420
|
-
*/
|
|
421
|
-
private generateOptimizationSuggestions(
|
|
422
|
-
modules: ModuleAnalysis[],
|
|
423
|
-
runtime: RuntimeAnalysis
|
|
424
|
-
): OptimizationSuggestion[] {
|
|
425
|
-
const suggestions: OptimizationSuggestion[] = [];
|
|
426
|
-
|
|
427
|
-
// Tree-shaking suggestions
|
|
428
|
-
if (this.options.treeShaking) {
|
|
429
|
-
const treeshakeableModules = modules.filter(m => m.treeshakeable);
|
|
430
|
-
if (treeshakeableModules.length > 0) {
|
|
431
|
-
const estimatedSaving = treeshakeableModules.reduce((sum, m) => sum + m.size * 0.3, 0);
|
|
432
|
-
suggestions.push({
|
|
433
|
-
type: 'tree-shaking',
|
|
434
|
-
severity: estimatedSaving > 10 * 1024 ? 'high' : 'medium',
|
|
435
|
-
description: `Enable tree-shaking for ${treeshakeableModules.length} modules`,
|
|
436
|
-
estimatedSaving,
|
|
437
|
-
implementation: 'Configure bundler with ES modules and sideEffects: false',
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Code splitting suggestions
|
|
443
|
-
const largeModules = modules.filter(m => m.size > 50 * 1024);
|
|
444
|
-
if (largeModules.length > 1 && !this.options.codeSplitting) {
|
|
445
|
-
const estimatedSaving = largeModules.reduce((sum, m) => sum + m.size * 0.2, 0);
|
|
446
|
-
suggestions.push({
|
|
447
|
-
type: 'code-splitting',
|
|
448
|
-
severity: 'medium',
|
|
449
|
-
description: `Split ${largeModules.length} large modules for lazy loading`,
|
|
450
|
-
estimatedSaving,
|
|
451
|
-
implementation: 'Implement dynamic imports and route-based code splitting',
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Runtime optimization suggestions
|
|
456
|
-
if (runtime.minimizable && this.options.runtimeMode === 'full') {
|
|
457
|
-
const estimatedSaving = runtime.totalSize * 0.4;
|
|
458
|
-
suggestions.push({
|
|
459
|
-
type: 'runtime-minimal',
|
|
460
|
-
severity: 'medium',
|
|
461
|
-
description: 'Use minimal runtime for production builds',
|
|
462
|
-
estimatedSaving,
|
|
463
|
-
implementation: 'Set runtimeMode to "minimal" and include only used features',
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Compression suggestions
|
|
468
|
-
if (!this.options.compress) {
|
|
469
|
-
const totalSize = modules.reduce((sum, m) => sum + m.size, 0) + runtime.totalSize;
|
|
470
|
-
const estimatedSaving = totalSize - this.estimateCompressedSize(totalSize);
|
|
471
|
-
suggestions.push({
|
|
472
|
-
type: 'compression',
|
|
473
|
-
severity: estimatedSaving > 20 * 1024 ? 'high' : 'medium',
|
|
474
|
-
description: 'Enable gzip/brotli compression',
|
|
475
|
-
estimatedSaving,
|
|
476
|
-
implementation: 'Configure web server or CDN with compression enabled',
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Lazy loading suggestions
|
|
481
|
-
const complexModules = modules.filter(m => m.complexity > 10);
|
|
482
|
-
if (complexModules.length > 0) {
|
|
483
|
-
const estimatedSaving = complexModules.reduce((sum, m) => sum + m.size * 0.5, 0);
|
|
484
|
-
suggestions.push({
|
|
485
|
-
type: 'lazy-loading',
|
|
486
|
-
severity: 'low',
|
|
487
|
-
description: `Implement lazy loading for ${complexModules.length} complex modules`,
|
|
488
|
-
estimatedSaving,
|
|
489
|
-
implementation: 'Use dynamic imports and load modules on demand',
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Sort suggestions by estimated saving
|
|
494
|
-
return suggestions.sort((a, b) => b.estimatedSaving - a.estimatedSaving);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Generate optimized bundle with tree-shaking
|
|
499
|
-
*/
|
|
500
|
-
async generateOptimizedBundle(): Promise<{
|
|
501
|
-
modules: Map<string, string>;
|
|
502
|
-
runtime: string;
|
|
503
|
-
manifest: BundleManifest;
|
|
504
|
-
}> {
|
|
505
|
-
const optimizedModules = new Map<string, string>();
|
|
506
|
-
|
|
507
|
-
// Apply optimizations to each module
|
|
508
|
-
for (const [name, code] of this.generatedModules) {
|
|
509
|
-
let optimized = code.typescript;
|
|
510
|
-
|
|
511
|
-
if (this.options.treeShaking) {
|
|
512
|
-
optimized = this.applyTreeShaking(optimized);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (this.options.minify) {
|
|
516
|
-
optimized = this.applyMinification(optimized);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
optimizedModules.set(name, optimized);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Generate minimal runtime
|
|
523
|
-
const runtime = this.generateMinimalRuntime();
|
|
524
|
-
|
|
525
|
-
// Create bundle manifest
|
|
526
|
-
const manifest: BundleManifest = {
|
|
527
|
-
version: '1.0.0',
|
|
528
|
-
target: this.options.target,
|
|
529
|
-
modules: Array.from(optimizedModules.keys()),
|
|
530
|
-
runtime: 'minimal',
|
|
531
|
-
optimizations: {
|
|
532
|
-
treeShaking: this.options.treeShaking,
|
|
533
|
-
minified: this.options.minify,
|
|
534
|
-
compressed: this.options.compress,
|
|
535
|
-
codeSplitting: this.options.codeSplitting,
|
|
536
|
-
},
|
|
537
|
-
stats: await this.analyzeBundles(),
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
return { modules: optimizedModules, runtime, manifest };
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* Apply tree-shaking to remove unused exports
|
|
545
|
-
*/
|
|
546
|
-
private applyTreeShaking(code: string): string {
|
|
547
|
-
// Basic tree-shaking implementation
|
|
548
|
-
// In a real implementation, this would use AST analysis
|
|
549
|
-
|
|
550
|
-
// Remove unused function declarations
|
|
551
|
-
const usedFunctions = this.findUsedFunctions(code);
|
|
552
|
-
const lines = code.split('\n');
|
|
553
|
-
const filteredLines = lines.filter(line => {
|
|
554
|
-
const functionName = this.getExportedFunctionName(line);
|
|
555
|
-
if (functionName) {
|
|
556
|
-
return usedFunctions.has(functionName);
|
|
557
|
-
}
|
|
558
|
-
return true;
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
return filteredLines.join('\n');
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Find functions that are actually used
|
|
566
|
-
*/
|
|
567
|
-
private findUsedFunctions(code: string): Set<string> {
|
|
568
|
-
const used = new Set<string>();
|
|
569
|
-
|
|
570
|
-
// Find all function calls
|
|
571
|
-
const callRegex = /(\w+)\s*\(/g;
|
|
572
|
-
let match;
|
|
573
|
-
while ((match = callRegex.exec(code)) !== null) {
|
|
574
|
-
if (match[1]) {
|
|
575
|
-
used.add(match[1]);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Find all exported functions (assume all exports are used externally)
|
|
580
|
-
for (const line of code.split('\n')) {
|
|
581
|
-
const exportName = this.getExportedFunctionName(line);
|
|
582
|
-
if (exportName) {
|
|
583
|
-
used.add(exportName);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
return used;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
private getExportedFunctionName(line: string): string | null {
|
|
591
|
-
const trimmed = line.trimStart();
|
|
592
|
-
if (!trimmed.startsWith('export ')) {
|
|
593
|
-
return null;
|
|
594
|
-
}
|
|
595
|
-
const tokens = this.splitWhitespace(trimmed);
|
|
596
|
-
const functionIndex = tokens.indexOf('function');
|
|
597
|
-
if (functionIndex === -1) {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
const nameToken = tokens[functionIndex + 1];
|
|
601
|
-
if (!nameToken) {
|
|
602
|
-
return null;
|
|
603
|
-
}
|
|
604
|
-
const name = nameToken.split('(')[0];
|
|
605
|
-
if (!name) {
|
|
606
|
-
return null;
|
|
607
|
-
}
|
|
608
|
-
return name;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
private splitWhitespace(value: string): string[] {
|
|
612
|
-
const tokens: string[] = [];
|
|
613
|
-
let current = '';
|
|
614
|
-
for (const char of value) {
|
|
615
|
-
if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
|
|
616
|
-
if (current) {
|
|
617
|
-
tokens.push(current);
|
|
618
|
-
current = '';
|
|
619
|
-
}
|
|
620
|
-
} else {
|
|
621
|
-
current += char;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
if (current) {
|
|
625
|
-
tokens.push(current);
|
|
626
|
-
}
|
|
627
|
-
return tokens;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/**
|
|
631
|
-
* Apply basic minification
|
|
632
|
-
*/
|
|
633
|
-
private applyMinification(code: string): string {
|
|
634
|
-
let minified = code;
|
|
635
|
-
|
|
636
|
-
// Remove comments
|
|
637
|
-
minified = minified.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
638
|
-
minified = minified.replace(/\/\/.*$/gm, '');
|
|
639
|
-
|
|
640
|
-
// Remove extra whitespace
|
|
641
|
-
minified = minified.replace(/\s+/g, ' ');
|
|
642
|
-
minified = minified.replace(/\s*([{}();,:])\s*/g, '$1');
|
|
643
|
-
|
|
644
|
-
// Remove trailing semicolons before }
|
|
645
|
-
minified = minified.replace(/;\s*}/g, '}');
|
|
646
|
-
|
|
647
|
-
return minified.trim();
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Generate minimal runtime based on target
|
|
652
|
-
*/
|
|
653
|
-
private generateMinimalRuntime(): string {
|
|
654
|
-
const baseRuntime = `
|
|
655
|
-
// tywrap minimal runtime
|
|
656
|
-
let __runtimeBridge;
|
|
657
|
-
|
|
658
|
-
const getRuntimeBridge = () => {
|
|
659
|
-
if (!__runtimeBridge) {
|
|
660
|
-
throw new Error('No runtime bridge configured. Call setRuntimeBridge(...) before using generated modules.');
|
|
661
|
-
}
|
|
662
|
-
return __runtimeBridge;
|
|
663
|
-
};
|
|
664
|
-
|
|
665
|
-
const setRuntimeBridge = (bridge) => {
|
|
666
|
-
__runtimeBridge = bridge;
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
const runtimeBridge = {
|
|
670
|
-
async call(module, functionName, args, kwargs) {
|
|
671
|
-
// Target-specific bridge implementation
|
|
672
|
-
${this.generateBridgeImplementation('call')}
|
|
673
|
-
},
|
|
674
|
-
async instantiate(module, className, args, kwargs) {
|
|
675
|
-
// Target-specific bridge implementation
|
|
676
|
-
${this.generateBridgeImplementation('instantiate')}
|
|
677
|
-
},
|
|
678
|
-
async callMethod(handle, methodName, args, kwargs) {
|
|
679
|
-
// Target-specific bridge implementation
|
|
680
|
-
${this.generateBridgeImplementation('call_method')}
|
|
681
|
-
},
|
|
682
|
-
async disposeInstance(handle) {
|
|
683
|
-
// Target-specific bridge implementation
|
|
684
|
-
${this.generateBridgeImplementation('dispose_instance')}
|
|
685
|
-
}
|
|
686
|
-
};
|
|
687
|
-
|
|
688
|
-
setRuntimeBridge(runtimeBridge);
|
|
689
|
-
|
|
690
|
-
// Minimal codec
|
|
691
|
-
const decodeValue = (value) => {
|
|
692
|
-
if (value?.__tywrap__) {
|
|
693
|
-
// Handle special types
|
|
694
|
-
return value.data ?? value;
|
|
695
|
-
}
|
|
696
|
-
return value;
|
|
697
|
-
};
|
|
698
|
-
|
|
699
|
-
export { getRuntimeBridge, setRuntimeBridge, decodeValue };
|
|
700
|
-
`;
|
|
701
|
-
|
|
702
|
-
return this.options.minify ? this.applyMinification(baseRuntime) : baseRuntime;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Generate bridge implementation based on target
|
|
707
|
-
*/
|
|
708
|
-
private generateBridgeImplementation(
|
|
709
|
-
method: 'call' | 'instantiate' | 'call_method' | 'dispose_instance'
|
|
710
|
-
): string {
|
|
711
|
-
switch (this.options.target) {
|
|
712
|
-
case 'node':
|
|
713
|
-
return `
|
|
714
|
-
// Node.js subprocess bridge
|
|
715
|
-
if (!this._process) {
|
|
716
|
-
const { spawn } = require('child_process');
|
|
717
|
-
this._process = spawn('python3', ['python_bridge.py']);
|
|
718
|
-
}
|
|
719
|
-
// Implementation details for ${method}...
|
|
720
|
-
return result;
|
|
721
|
-
`;
|
|
722
|
-
|
|
723
|
-
case 'browser':
|
|
724
|
-
return `
|
|
725
|
-
// Pyodide browser bridge
|
|
726
|
-
if (!globalThis.pyodide) {
|
|
727
|
-
await import('pyodide');
|
|
728
|
-
}
|
|
729
|
-
// Implementation details for ${method}...
|
|
730
|
-
return result;
|
|
731
|
-
`;
|
|
732
|
-
|
|
733
|
-
default:
|
|
734
|
-
return `
|
|
735
|
-
// Universal bridge - detect environment
|
|
736
|
-
if (typeof process !== 'undefined') {
|
|
737
|
-
// Node.js environment
|
|
738
|
-
} else if (typeof window !== 'undefined') {
|
|
739
|
-
// Browser environment
|
|
740
|
-
}
|
|
741
|
-
// Implementation details for ${method}...
|
|
742
|
-
return result;
|
|
743
|
-
`;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* Save bundle analysis report
|
|
749
|
-
*/
|
|
750
|
-
async saveAnalysisReport(filePath: string): Promise<BundleAnalysis> {
|
|
751
|
-
const analysis = await this.analyzeBundles();
|
|
752
|
-
|
|
753
|
-
const report = {
|
|
754
|
-
timestamp: new Date().toISOString(),
|
|
755
|
-
options: this.options,
|
|
756
|
-
analysis,
|
|
757
|
-
modules: Object.fromEntries(this.generatedModules.entries()),
|
|
758
|
-
};
|
|
759
|
-
|
|
760
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- report path is caller-provided
|
|
761
|
-
await writeFile(filePath, JSON.stringify(report, null, 2));
|
|
762
|
-
process.stdout.write(`Bundle analysis report saved to ${filePath}\n`);
|
|
763
|
-
|
|
764
|
-
return analysis;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
/**
|
|
768
|
-
* Clear all modules and cache
|
|
769
|
-
*/
|
|
770
|
-
clear(): void {
|
|
771
|
-
this.generatedModules.clear();
|
|
772
|
-
this.analysisCache.clear();
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
interface RollupBundleChunk {
|
|
777
|
-
code?: string;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
type RollupBundle = Record<string, RollupBundleChunk>;
|
|
781
|
-
|
|
782
|
-
interface RollupPlugin {
|
|
783
|
-
name: string;
|
|
784
|
-
generateBundle: (options: unknown, bundle: RollupBundle) => void;
|
|
785
|
-
writeBundle: () => Promise<void>;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
interface WebpackAsset {
|
|
789
|
-
source: () => string | Buffer;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
interface WebpackCompilation {
|
|
793
|
-
assets: Record<string, WebpackAsset>;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
interface WebpackCompiler {
|
|
797
|
-
hooks: {
|
|
798
|
-
emit: {
|
|
799
|
-
tap: (name: string, handler: (compilation: WebpackCompilation) => void) => void;
|
|
800
|
-
};
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
interface WebpackPlugin {
|
|
805
|
-
apply: (compiler: WebpackCompiler) => void;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// Export utilities for rollup/webpack integration
|
|
809
|
-
export function createRollupPlugin(options: Partial<BundleOptions> = {}): RollupPlugin {
|
|
810
|
-
const optimizer = new BundleOptimizer(options);
|
|
811
|
-
|
|
812
|
-
return {
|
|
813
|
-
name: 'tywrap-optimizer',
|
|
814
|
-
generateBundle(_options: unknown, bundle: RollupBundle): void {
|
|
815
|
-
// Integrate with Rollup bundle generation
|
|
816
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
817
|
-
if (
|
|
818
|
-
(fileName.endsWith('.ts') || fileName.endsWith('.js')) &&
|
|
819
|
-
typeof chunk.code === 'string'
|
|
820
|
-
) {
|
|
821
|
-
// Add to optimizer for analysis
|
|
822
|
-
const code = {
|
|
823
|
-
typescript: chunk.code,
|
|
824
|
-
declaration: '',
|
|
825
|
-
metadata: {
|
|
826
|
-
generatedAt: new Date(),
|
|
827
|
-
sourceFiles: [fileName],
|
|
828
|
-
runtime: 'node' as const,
|
|
829
|
-
optimizations: ['tree-shaking'],
|
|
830
|
-
},
|
|
831
|
-
};
|
|
832
|
-
optimizer.addModule(fileName, code);
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
},
|
|
836
|
-
async writeBundle(): Promise<void> {
|
|
837
|
-
// Generate analysis report
|
|
838
|
-
await optimizer.saveAnalysisReport('bundle-analysis.json');
|
|
839
|
-
},
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
export function createWebpackPlugin(options: Partial<BundleOptions> = {}): WebpackPlugin {
|
|
844
|
-
const optimizer = new BundleOptimizer(options);
|
|
845
|
-
|
|
846
|
-
return {
|
|
847
|
-
apply(compiler: WebpackCompiler): void {
|
|
848
|
-
compiler.hooks.emit.tap('TywrapOptimizerPlugin', (compilation: WebpackCompilation) => {
|
|
849
|
-
// Process webpack assets
|
|
850
|
-
for (const [filename, asset] of Object.entries(compilation.assets)) {
|
|
851
|
-
if (filename.endsWith('.js') || filename.endsWith('.ts')) {
|
|
852
|
-
const assetSource = asset.source();
|
|
853
|
-
const sourceText =
|
|
854
|
-
typeof assetSource === 'string' ? assetSource : assetSource.toString();
|
|
855
|
-
const code = {
|
|
856
|
-
typescript: sourceText,
|
|
857
|
-
declaration: '',
|
|
858
|
-
metadata: {
|
|
859
|
-
generatedAt: new Date(),
|
|
860
|
-
sourceFiles: [filename],
|
|
861
|
-
runtime: 'node' as const,
|
|
862
|
-
optimizations: ['minification'],
|
|
863
|
-
},
|
|
864
|
-
};
|
|
865
|
-
optimizer.addModule(filename, code);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
// Generate analysis
|
|
870
|
-
optimizer.saveAnalysisReport('webpack-bundle-analysis.json').catch(error => {
|
|
871
|
-
process.stderr.write(`Failed to write webpack analysis report: ${String(error)}\n`);
|
|
872
|
-
});
|
|
873
|
-
});
|
|
874
|
-
},
|
|
875
|
-
};
|
|
876
|
-
}
|