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