susee 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +340 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +14 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +340 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -7
package/dist/index.mjs
CHANGED
|
@@ -15,6 +15,14 @@ import transformFunction from "@suseejs/transformer";
|
|
|
15
15
|
import ts from "typescript";
|
|
16
16
|
import utilities from "@suseejs/utils";
|
|
17
17
|
import { Buffer } from "node:buffer";
|
|
18
|
+
//src/lib/bundle/bundleCreator.ts
|
|
19
|
+
/**
|
|
20
|
+
* Creates a BundleCreator function that transforms a DepsFile with a given BundleVisitor and typescript compiler options.
|
|
21
|
+
* @param {BundleVisitor} bundleVisitor - a BundleVisitor function that takes a context, depsTree, sourceFile, and any number of arguments.
|
|
22
|
+
* @param {ts.CompilerOptions} compilerOptions - typescript compiler options.
|
|
23
|
+
* @param {...any} args - any number of arguments to pass to the BundleVisitor function.
|
|
24
|
+
* @returns {BundleCreator} a BundleCreator function that takes a DepsFile and returns a transformed DepsFile.
|
|
25
|
+
*/
|
|
18
26
|
const bundleCreator = (bundleVisitor, compilerOptions, ...args) => {
|
|
19
27
|
return (depsTree) => {
|
|
20
28
|
const sourceFile = ts.createSourceFile(depsTree.file, depsTree.content, ts.ScriptTarget.Latest, true);
|
|
@@ -28,12 +36,14 @@ const bundleCreator = (bundleVisitor, compilerOptions, ...args) => {
|
|
|
28
36
|
return { content: _content, ...rest };
|
|
29
37
|
};
|
|
30
38
|
};
|
|
39
|
+
//src/lib/bundle/mergeImportsStatement.ts
|
|
31
40
|
function mergeImportsStatement(imports) {
|
|
32
41
|
const importMap = new Map();
|
|
33
42
|
const typeImportMap = new Map();
|
|
34
43
|
const defaultImports = new Map();
|
|
35
44
|
const typeDefaultImports = new Map();
|
|
36
45
|
const namespaceImports = new Map();
|
|
46
|
+
// Parse each import statement
|
|
37
47
|
for (const importStr of imports) {
|
|
38
48
|
const importMatch = importStr.match(/import\s+(?:type\s+)?(?:(.*?)\s+from\s+)?["']([^"']+)["'];?/);
|
|
39
49
|
if (!importMatch)
|
|
@@ -42,6 +52,7 @@ function mergeImportsStatement(imports) {
|
|
|
42
52
|
const isTypeImport = importStr.includes("import type");
|
|
43
53
|
const modulePath = _modulePath;
|
|
44
54
|
if (!importClause) {
|
|
55
|
+
// Default import or side-effect import
|
|
45
56
|
const defaultMatch = importStr.match(/import\s+(?:type\s+)?(\w+)/);
|
|
46
57
|
if (defaultMatch) {
|
|
47
58
|
const importName = defaultMatch[1];
|
|
@@ -53,6 +64,7 @@ function mergeImportsStatement(imports) {
|
|
|
53
64
|
continue;
|
|
54
65
|
}
|
|
55
66
|
if (importClause.startsWith("{")) {
|
|
67
|
+
// Named imports: import { a, b } from 'module'
|
|
56
68
|
const targetMap = isTypeImport ? typeImportMap : importMap;
|
|
57
69
|
if (!targetMap.has(modulePath))
|
|
58
70
|
targetMap.set(modulePath, new Set());
|
|
@@ -61,9 +73,11 @@ function mergeImportsStatement(imports) {
|
|
|
61
73
|
.split(",")
|
|
62
74
|
.map((s) => s.trim())
|
|
63
75
|
.filter(Boolean);
|
|
76
|
+
// biome-ignore lint/suspicious/useIterableCallbackReturn : just add name for names each
|
|
64
77
|
names.forEach((name) => targetMap.get(modulePath)?.add(name));
|
|
65
78
|
}
|
|
66
79
|
else if (importClause.startsWith("* as")) {
|
|
80
|
+
// Namespace import: import * as name from 'module'
|
|
67
81
|
const namespaceMatch = importClause.match(/\*\s+as\s+(\w+)/);
|
|
68
82
|
if (namespaceMatch) {
|
|
69
83
|
const namespaceName = namespaceMatch[1];
|
|
@@ -73,6 +87,7 @@ function mergeImportsStatement(imports) {
|
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
else {
|
|
90
|
+
// Default import: import name from 'module'
|
|
76
91
|
const targetMap = isTypeImport ? typeDefaultImports : defaultImports;
|
|
77
92
|
if (!targetMap.has(modulePath))
|
|
78
93
|
targetMap.set(modulePath, new Set());
|
|
@@ -80,8 +95,10 @@ function mergeImportsStatement(imports) {
|
|
|
80
95
|
}
|
|
81
96
|
}
|
|
82
97
|
const mergedImports = [];
|
|
98
|
+
// Process named imports - remove type imports that have regular imports
|
|
83
99
|
for (const [modulePath, regularNames] of importMap) {
|
|
84
100
|
const typeNames = typeImportMap.get(modulePath) || new Set();
|
|
101
|
+
// Only include type names that don't have regular imports
|
|
85
102
|
const finalNames = new Set([...regularNames]);
|
|
86
103
|
for (const typeName of typeNames) {
|
|
87
104
|
if (!regularNames.has(typeName)) {
|
|
@@ -93,14 +110,17 @@ function mergeImportsStatement(imports) {
|
|
|
93
110
|
mergedImports.push(`import { ${importNames} } from "${modulePath}";`);
|
|
94
111
|
}
|
|
95
112
|
}
|
|
113
|
+
// Add remaining type-only imports (where no regular imports exist for the module)
|
|
96
114
|
for (const [modulePath, typeNames] of typeImportMap) {
|
|
97
115
|
if (!importMap.has(modulePath) && typeNames.size > 0) {
|
|
98
116
|
const importNames = Array.from(typeNames).sort().join(", ");
|
|
99
117
|
mergedImports.push(`import type { ${importNames} } from "${modulePath}";`);
|
|
100
118
|
}
|
|
101
119
|
}
|
|
120
|
+
// Process default imports - remove type default imports that have regular default imports
|
|
102
121
|
for (const [modulePath, regularDefaultNames] of defaultImports) {
|
|
103
122
|
const typeDefaultNames = typeDefaultImports.get(modulePath) || new Set();
|
|
123
|
+
// Only include type default names that don't have regular default imports
|
|
104
124
|
const finalNames = new Set([...regularDefaultNames]);
|
|
105
125
|
for (const typeName of typeDefaultNames) {
|
|
106
126
|
if (!regularDefaultNames.has(typeName)) {
|
|
@@ -112,12 +132,14 @@ function mergeImportsStatement(imports) {
|
|
|
112
132
|
mergedImports.push(`import ${importNames} from "${modulePath}";`);
|
|
113
133
|
}
|
|
114
134
|
}
|
|
135
|
+
// Add remaining type-only default imports
|
|
115
136
|
for (const [modulePath, typeDefaultNames] of typeDefaultImports) {
|
|
116
137
|
if (!defaultImports.has(modulePath) && typeDefaultNames.size > 0) {
|
|
117
138
|
const importNames = Array.from(typeDefaultNames).join(", ");
|
|
118
139
|
mergedImports.push(`import type ${importNames} from "${modulePath}";`);
|
|
119
140
|
}
|
|
120
141
|
}
|
|
142
|
+
// Process namespace imports
|
|
121
143
|
for (const [modulePath, names] of namespaceImports) {
|
|
122
144
|
if (names.size > 0) {
|
|
123
145
|
const importNames = Array.from(names).join(", ");
|
|
@@ -126,6 +148,11 @@ function mergeImportsStatement(imports) {
|
|
|
126
148
|
}
|
|
127
149
|
return mergedImports.sort();
|
|
128
150
|
}
|
|
151
|
+
//src/lib/bundle/visitors/anonymousCallExpression.ts
|
|
152
|
+
/**
|
|
153
|
+
* A BundleVisitor that updates the call expression, property access expression, and new expression
|
|
154
|
+
* with the anonymous default import name.
|
|
155
|
+
*/
|
|
129
156
|
const anonymousCallExpressionVisitor = (context, depsTree, _sourceFile, exportDefaultImportNameMap) => {
|
|
130
157
|
const { factory } = context;
|
|
131
158
|
const visit = (node) => {
|
|
@@ -155,6 +182,7 @@ const anonymousCallExpressionVisitor = (context, depsTree, _sourceFile, exportDe
|
|
|
155
182
|
return factory.updateNewExpression(node, factory.createIdentifier(mapping.newName), node.typeArguments, node.arguments);
|
|
156
183
|
}
|
|
157
184
|
}
|
|
185
|
+
// for export specifier it is focus on entry file
|
|
158
186
|
}
|
|
159
187
|
else if (ts.isExportSpecifier(node)) {
|
|
160
188
|
if (ts.isIdentifier(node.name)) {
|
|
@@ -169,6 +197,7 @@ const anonymousCallExpressionVisitor = (context, depsTree, _sourceFile, exportDe
|
|
|
169
197
|
};
|
|
170
198
|
return visit;
|
|
171
199
|
};
|
|
200
|
+
//src/lib/bundle/visitors/visitorHelpers.ts
|
|
172
201
|
const normalizePathKey = (filePath) => {
|
|
173
202
|
const parsed = path.parse(filePath);
|
|
174
203
|
let noExt = path.join(parsed.dir, parsed.name);
|
|
@@ -224,8 +253,13 @@ function uniqueName() {
|
|
|
224
253
|
};
|
|
225
254
|
return obj;
|
|
226
255
|
}
|
|
256
|
+
//src/lib/bundle/visitors/anonymousExport.ts
|
|
227
257
|
const prefixKey = "AnonymousName";
|
|
228
258
|
const genName = uniqueName().setPrefix({ key: prefixKey, value: "a_" });
|
|
259
|
+
/**
|
|
260
|
+
* A BundleVisitor that updates the call expression, property access expression, and new expression
|
|
261
|
+
* with the anonymous default import name.
|
|
262
|
+
*/
|
|
229
263
|
const anonymousExportVisitor = (context, depsTree, sourceFile, exportDefaultExportNameMap) => {
|
|
230
264
|
const { factory } = context;
|
|
231
265
|
const visit = (node) => {
|
|
@@ -335,17 +369,23 @@ const anonymousExportVisitor = (context, depsTree, sourceFile, exportDefaultExpo
|
|
|
335
369
|
});
|
|
336
370
|
return factory.updateSourceFile(sourceFile, [variableStatementNode, exportAssignmentNode], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
|
|
337
371
|
}
|
|
338
|
-
}
|
|
372
|
+
} //
|
|
339
373
|
return ts.visitEachChild(node, visit, context);
|
|
340
374
|
};
|
|
341
375
|
return visit;
|
|
342
376
|
};
|
|
377
|
+
//src/lib/bundle/visitors/anonymousImport.ts
|
|
378
|
+
/**
|
|
379
|
+
* A BundleVisitor that updates the import declaration, property access expression, and new expression
|
|
380
|
+
* with the anonymous default import name.
|
|
381
|
+
*/
|
|
343
382
|
const anonymousImportVisitor = (context, depsTree, sourceFile, exportDefaultExportNameMap, exportDefaultImportNameMap) => {
|
|
344
383
|
const { factory } = context;
|
|
345
384
|
const visit = (node) => {
|
|
346
385
|
if (ts.isImportDeclaration(node)) {
|
|
347
386
|
const fileName = node.moduleSpecifier.getText(sourceFile);
|
|
348
387
|
const _name = path.basename(fileName).split(".")[0].trim();
|
|
388
|
+
// check only import default expression
|
|
349
389
|
if (node.importClause?.name && ts.isIdentifier(node.importClause.name)) {
|
|
350
390
|
const base = node.importClause.name.text.trim();
|
|
351
391
|
const mapping = exportDefaultExportNameMap.find((v) => v.file === _name);
|
|
@@ -365,6 +405,11 @@ const anonymousImportVisitor = (context, depsTree, sourceFile, exportDefaultExpo
|
|
|
365
405
|
};
|
|
366
406
|
return visit;
|
|
367
407
|
};
|
|
408
|
+
//src/lib/bundle/visitors/duplicateCallExpression.ts
|
|
409
|
+
/**
|
|
410
|
+
* A BundleVisitor that updates the call expression, property access expression, and new expression
|
|
411
|
+
* with the anonymous default import name.
|
|
412
|
+
*/
|
|
368
413
|
const duplicateCallExpressionVisitor = (context, depsTree, _sourceFile, callNameMap, importNameMap) => {
|
|
369
414
|
const { factory } = context;
|
|
370
415
|
const visit = (node) => {
|
|
@@ -379,6 +424,7 @@ const duplicateCallExpressionVisitor = (context, depsTree, _sourceFile, callName
|
|
|
379
424
|
}
|
|
380
425
|
else if (importMapping) {
|
|
381
426
|
new_name = importMapping.newName;
|
|
427
|
+
//flag.push(new_name);
|
|
382
428
|
}
|
|
383
429
|
if (new_name) {
|
|
384
430
|
return factory.updateCallExpression(node, factory.createIdentifier(new_name), node.typeArguments, node.arguments);
|
|
@@ -419,13 +465,28 @@ const duplicateCallExpressionVisitor = (context, depsTree, _sourceFile, callName
|
|
|
419
465
|
}
|
|
420
466
|
}
|
|
421
467
|
}
|
|
468
|
+
/* ----------------------Returns for visitor function------------------------------- */
|
|
422
469
|
return ts.visitEachChild(node, visit, context);
|
|
423
470
|
};
|
|
424
471
|
return visit;
|
|
425
472
|
};
|
|
473
|
+
//src/lib/bundle/visitors/duplicateCollection.ts
|
|
474
|
+
/**
|
|
475
|
+
* A BundleVisitor that updates the collection (variable, function, class, etc)
|
|
476
|
+
* declaration with the anonymous default import name.
|
|
477
|
+
*
|
|
478
|
+
* @param {BundleVisitor} context - The BundleVisitor context.
|
|
479
|
+
* @param {DepsTree} depsTree - The deps tree object.
|
|
480
|
+
* @param {SourceFile} sourceFile - The source file object.
|
|
481
|
+
* @param {DuplicatesNameMap} namesMap - The DuplicatesNameMap object.
|
|
482
|
+
* @return {NodeVisit} visit - The NodeVisit function.
|
|
483
|
+
*/
|
|
426
484
|
const duplicateCollectionVisitor = (context, depsTree, _sourceFile, namesMap) => {
|
|
485
|
+
//const { factory } = context;
|
|
427
486
|
const visit = (node, isGlobalScope = true) => {
|
|
487
|
+
// Global declarations များကိုသာ collect လုပ်မယ်
|
|
428
488
|
if (isGlobalScope) {
|
|
489
|
+
// Variable statements (const, let, var)
|
|
429
490
|
if (ts.isVariableStatement(node)) {
|
|
430
491
|
node.declarationList.declarations.forEach((decl) => {
|
|
431
492
|
if (ts.isIdentifier(decl.name)) {
|
|
@@ -434,11 +495,13 @@ const duplicateCollectionVisitor = (context, depsTree, _sourceFile, namesMap) =>
|
|
|
434
495
|
namesMap.set($name, new Set([{ file: depsTree.file }]));
|
|
435
496
|
}
|
|
436
497
|
else {
|
|
498
|
+
// biome-ignore lint/style/noNonNullAssertion : !namesMap.has($name) before
|
|
437
499
|
namesMap.get($name).add({ file: depsTree.file });
|
|
438
500
|
}
|
|
439
501
|
}
|
|
440
502
|
});
|
|
441
503
|
}
|
|
504
|
+
// Function, Class, Enum, Interface, Type declarations
|
|
442
505
|
else if (ts.isFunctionDeclaration(node) ||
|
|
443
506
|
ts.isClassDeclaration(node) ||
|
|
444
507
|
ts.isEnumDeclaration(node) ||
|
|
@@ -450,17 +513,20 @@ const duplicateCollectionVisitor = (context, depsTree, _sourceFile, namesMap) =>
|
|
|
450
513
|
namesMap.set($name, new Set([{ file: depsTree.file }]));
|
|
451
514
|
}
|
|
452
515
|
else {
|
|
516
|
+
// biome-ignore lint/style/noNonNullAssertion : !namesMap.has($name) before
|
|
453
517
|
namesMap.get($name).add({ file: depsTree.file });
|
|
454
518
|
}
|
|
455
519
|
}
|
|
456
520
|
}
|
|
457
521
|
}
|
|
522
|
+
// Local scope ထဲရောက်သွားတဲ့ node တွေအတွက် recursive visit
|
|
458
523
|
if (ts.isBlock(node) ||
|
|
459
524
|
ts.isFunctionDeclaration(node) ||
|
|
460
525
|
ts.isFunctionExpression(node) ||
|
|
461
526
|
ts.isArrowFunction(node) ||
|
|
462
527
|
ts.isMethodDeclaration(node) ||
|
|
463
528
|
ts.isClassDeclaration(node)) {
|
|
529
|
+
// Local scope ထဲကို ဝင်သွားပြီဆိုတာနဲ့ isGlobalScope = false
|
|
464
530
|
if (ts.isBlock(node)) {
|
|
465
531
|
ts.visitNodes(node.statements, (child) => visit(child, false));
|
|
466
532
|
}
|
|
@@ -471,12 +537,19 @@ const duplicateCollectionVisitor = (context, depsTree, _sourceFile, namesMap) =>
|
|
|
471
537
|
}
|
|
472
538
|
}
|
|
473
539
|
else {
|
|
540
|
+
// Global scope ထဲဆက်ရှိနေတဲ့ node တွေအတွက်
|
|
474
541
|
return ts.visitEachChild(node, (child) => visit(child, isGlobalScope), context);
|
|
475
542
|
}
|
|
543
|
+
/* ----------------------Returns for visitNode function------------------------------- */
|
|
476
544
|
return node;
|
|
477
545
|
};
|
|
478
546
|
return visit;
|
|
479
547
|
};
|
|
548
|
+
//src/lib/bundle/visitors/duplicateExportExpression.ts
|
|
549
|
+
/**
|
|
550
|
+
* A BundleVisitor that updates the call expression, property access expression, and new expression
|
|
551
|
+
* with the anonymous default import name.
|
|
552
|
+
*/
|
|
480
553
|
const duplicateExportExpressionVisitor = (context, depsTree, _sourceFile, callNameMap, importNameMap, exportNameMap) => {
|
|
481
554
|
const { factory } = context;
|
|
482
555
|
const visit = (node) => {
|
|
@@ -525,10 +598,23 @@ const duplicateExportExpressionVisitor = (context, depsTree, _sourceFile, callNa
|
|
|
525
598
|
}
|
|
526
599
|
}
|
|
527
600
|
}
|
|
601
|
+
/* ----------------------Returns for visitor function------------------------------- */
|
|
528
602
|
return ts.visitEachChild(node, visit, context);
|
|
529
603
|
};
|
|
530
604
|
return visit;
|
|
531
605
|
};
|
|
606
|
+
//src/lib/bundle/visitors/duplicateImportExpression.ts
|
|
607
|
+
/**
|
|
608
|
+
* A BundleVisitor that updates the import declaration, property access expression, and new expression
|
|
609
|
+
* with the anonymous default import name.
|
|
610
|
+
*
|
|
611
|
+
* @param {BundleVisitor} context - The BundleVisitor context.
|
|
612
|
+
* @param {DepsTree} depsTree - The deps tree object.
|
|
613
|
+
* @param {SourceFile} sourceFile - The source file object.
|
|
614
|
+
* @param {NamesSets} exportNameMap - The export name map object.
|
|
615
|
+
* @param {NamesSets} importNameMap - The import name map object.
|
|
616
|
+
* @return {NodeVisit} visit - The NodeVisit function.
|
|
617
|
+
*/
|
|
532
618
|
const duplicateImportExpressionVisitor = (context, depsTree, sourceFile, exportNameMap, importNameMap) => {
|
|
533
619
|
const { factory } = context;
|
|
534
620
|
const visit = (node) => {
|
|
@@ -539,6 +625,7 @@ const duplicateImportExpressionVisitor = (context, depsTree, sourceFile, exportN
|
|
|
539
625
|
ts.isNamedImports(node.importClause.namedBindings)) {
|
|
540
626
|
baseNames = node.importClause.namedBindings.elements.map((el) => el.name.text.trim());
|
|
541
627
|
}
|
|
628
|
+
// import default expression
|
|
542
629
|
if (node.importClause?.name && ts.isIdentifier(node.importClause.name)) {
|
|
543
630
|
const base = node.importClause.name.text.trim();
|
|
544
631
|
const mapping = exportNameMap.find((m) => m.base === base && m.file === moduleKey);
|
|
@@ -552,6 +639,7 @@ const duplicateImportExpressionVisitor = (context, depsTree, sourceFile, exportN
|
|
|
552
639
|
return factory.updateImportDeclaration(node, node.modifiers, newImportClause, node.moduleSpecifier, node.attributes);
|
|
553
640
|
}
|
|
554
641
|
}
|
|
642
|
+
// import name , `import{ ... }`
|
|
555
643
|
if (baseNames.length > 0 &&
|
|
556
644
|
node.importClause &&
|
|
557
645
|
node.importClause.namedBindings &&
|
|
@@ -572,15 +660,28 @@ const duplicateImportExpressionVisitor = (context, depsTree, sourceFile, exportN
|
|
|
572
660
|
const newImportClause = factory.updateImportClause(node.importClause, node.importClause.phaseModifier, node.importClause.name, newNamedImports);
|
|
573
661
|
return factory.updateImportDeclaration(node, node.modifiers, newImportClause, node.moduleSpecifier, node.attributes);
|
|
574
662
|
}
|
|
575
|
-
}
|
|
663
|
+
} //&&
|
|
664
|
+
/* ----------------------Returns for visitor function------------------------------- */
|
|
576
665
|
return ts.visitEachChild(node, visit, context);
|
|
577
666
|
};
|
|
578
667
|
return visit;
|
|
579
668
|
};
|
|
669
|
+
//src/lib/bundle/visitors/duplicateUpdate.ts
|
|
580
670
|
const dupName = uniqueName().setPrefix({
|
|
581
671
|
key: "DuplicatesNames",
|
|
582
672
|
value: "d_",
|
|
583
673
|
});
|
|
674
|
+
/**
|
|
675
|
+
* A BundleVisitor that updates the variable declaration, function declaration, and class declaration
|
|
676
|
+
* with the duplicates name.
|
|
677
|
+
*
|
|
678
|
+
* @param {BundleVisitor} context - The BundleVisitor context.
|
|
679
|
+
* @param {DepsTree} depsTree - The deps tree object.
|
|
680
|
+
* @param {SourceFile} sourceFile - The source file object.
|
|
681
|
+
* @param {DuplicatesNameMap} namesMap - The DuplicatesNameMap object.
|
|
682
|
+
* @param {NamesSets} callNameMap - The NamesSets object.
|
|
683
|
+
* @return {NodeVisit} visit - The NodeVisit function.
|
|
684
|
+
*/
|
|
584
685
|
const duplicateUpdateVisitor = (context, depsTree, _sourceFile, namesMap, callNameMap) => {
|
|
585
686
|
const { factory } = context;
|
|
586
687
|
const visit = (node) => {
|
|
@@ -588,6 +689,7 @@ const duplicateUpdateVisitor = (context, depsTree, _sourceFile, namesMap, callNa
|
|
|
588
689
|
const newDeclarations = node.declarationList.declarations.map((decl) => {
|
|
589
690
|
if (ts.isIdentifier(decl.name)) {
|
|
590
691
|
const base = decl.name.text;
|
|
692
|
+
// biome-ignore lint/style/noNonNullAssertion : namesMap.has(base) before that get just only size
|
|
591
693
|
if (namesMap.has(base) && namesMap.get(base).size > 1) {
|
|
592
694
|
const newName = dupName.getName(base);
|
|
593
695
|
callNameMap.push({ base, file: depsTree.file, newName });
|
|
@@ -602,6 +704,7 @@ const duplicateUpdateVisitor = (context, depsTree, _sourceFile, namesMap, callNa
|
|
|
602
704
|
else if (ts.isFunctionDeclaration(node)) {
|
|
603
705
|
if (node.name && ts.isIdentifier(node.name)) {
|
|
604
706
|
const base = node.name.text;
|
|
707
|
+
// biome-ignore lint/style/noNonNullAssertion : namesMap.has(base) before that get just only size
|
|
605
708
|
if (namesMap.has(base) && namesMap.get(base).size > 1) {
|
|
606
709
|
const newName = dupName.getName(base);
|
|
607
710
|
callNameMap.push({ base, file: depsTree.file, newName });
|
|
@@ -612,6 +715,7 @@ const duplicateUpdateVisitor = (context, depsTree, _sourceFile, namesMap, callNa
|
|
|
612
715
|
else if (ts.isClassDeclaration(node)) {
|
|
613
716
|
if (node.name && ts.isIdentifier(node.name)) {
|
|
614
717
|
const base = node.name.text;
|
|
718
|
+
// biome-ignore lint/style/noNonNullAssertion : namesMap.has(base) before that get just only size
|
|
615
719
|
if (namesMap.has(base) && namesMap.get(base).size > 1) {
|
|
616
720
|
const newName = dupName.getName(base);
|
|
617
721
|
callNameMap.push({ base, file: depsTree.file, newName });
|
|
@@ -619,13 +723,21 @@ const duplicateUpdateVisitor = (context, depsTree, _sourceFile, namesMap, callNa
|
|
|
619
723
|
}
|
|
620
724
|
}
|
|
621
725
|
}
|
|
726
|
+
/* ----------------------Returns for visitor function------------------------------- */
|
|
622
727
|
return ts.visitEachChild(node, visit, context);
|
|
623
728
|
};
|
|
624
729
|
return visit;
|
|
625
730
|
};
|
|
731
|
+
//src/lib/bundle/visitors/removeExports.ts
|
|
732
|
+
/**
|
|
733
|
+
* A BundleVisitor that removes all exports from the given source file.
|
|
734
|
+
* It does so by stripping "export" modifiers from function, class, interface, type alias, enum, and variable declarations,
|
|
735
|
+
* and by removing "export { foo }" and "export default" declarations entirely.
|
|
736
|
+
*/
|
|
626
737
|
const removeExportsVisitor = (context) => {
|
|
627
738
|
const { factory } = context;
|
|
628
739
|
const visit = (node) => {
|
|
740
|
+
// --- Case 1: Strip "export" modifiers ---
|
|
629
741
|
const inside_nameSpace = utilities.isInsideNamespace(node);
|
|
630
742
|
if (!inside_nameSpace) {
|
|
631
743
|
if (ts.isFunctionDeclaration(node) ||
|
|
@@ -637,40 +749,49 @@ const removeExportsVisitor = (context) => {
|
|
|
637
749
|
const modifiers = node.modifiers?.filter((m) => m.kind !== ts.SyntaxKind.ExportKeyword &&
|
|
638
750
|
m.kind !== ts.SyntaxKind.DefaultKeyword);
|
|
639
751
|
if (modifiers?.length !== node.modifiers?.length) {
|
|
752
|
+
// If the node has an export modifier, remove it.
|
|
753
|
+
// If the node is a function, class, interface, type alias, enum or variable declaration,
|
|
754
|
+
// update the declaration by removing the export modifier.
|
|
640
755
|
if (ts.isFunctionDeclaration(node)) {
|
|
641
756
|
return factory.updateFunctionDeclaration(node, modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, node.body);
|
|
642
|
-
}
|
|
757
|
+
} // function
|
|
643
758
|
if (ts.isClassDeclaration(node)) {
|
|
644
759
|
return factory.updateClassDeclaration(node, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members);
|
|
645
|
-
}
|
|
760
|
+
} // class
|
|
646
761
|
if (ts.isInterfaceDeclaration(node)) {
|
|
647
762
|
return factory.updateInterfaceDeclaration(node, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members);
|
|
648
|
-
}
|
|
763
|
+
} // interface
|
|
649
764
|
if (ts.isTypeAliasDeclaration(node)) {
|
|
650
765
|
return factory.updateTypeAliasDeclaration(node, modifiers, node.name, node.typeParameters, node.type);
|
|
651
|
-
}
|
|
766
|
+
} // types
|
|
652
767
|
if (ts.isEnumDeclaration(node)) {
|
|
653
768
|
return factory.updateEnumDeclaration(node, modifiers, node.name, node.members);
|
|
654
|
-
}
|
|
769
|
+
} //enum
|
|
655
770
|
if (ts.isVariableStatement(node)) {
|
|
656
771
|
return factory.updateVariableStatement(node, modifiers, node.declarationList);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
772
|
+
} // vars
|
|
773
|
+
} //--
|
|
774
|
+
} // --- Case 1
|
|
660
775
|
}
|
|
776
|
+
// --- Case 2: Remove "export { foo }" entirely ---
|
|
661
777
|
if (ts.isExportDeclaration(node)) {
|
|
778
|
+
// If the node is an export declaration, remove it.
|
|
662
779
|
return factory.createEmptyStatement();
|
|
663
780
|
}
|
|
781
|
+
// --- Case 3: Handle "export default ..." ---
|
|
664
782
|
if (ts.isExportAssignment(node)) {
|
|
665
783
|
const expr = node.expression;
|
|
784
|
+
// export default Foo; -> remove line
|
|
666
785
|
if (ts.isIdentifier(expr)) {
|
|
667
786
|
return factory.createEmptyStatement();
|
|
668
787
|
}
|
|
669
788
|
}
|
|
789
|
+
// --------- Visitor Return ------------------//
|
|
670
790
|
return ts.visitEachChild(node, visit, context);
|
|
671
791
|
};
|
|
672
792
|
return visit;
|
|
673
793
|
};
|
|
794
|
+
//src/lib/bundle/visitors/removeImports.ts
|
|
674
795
|
let properties = [];
|
|
675
796
|
const typeObj = {};
|
|
676
797
|
const typesNames = [];
|
|
@@ -682,7 +803,14 @@ function findProperty(node) {
|
|
|
682
803
|
node.forEachChild((n) => findProperty(n));
|
|
683
804
|
return properties;
|
|
684
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* A BundleVisitor that removes import declarations and import equals declarations from a TypeScript program.
|
|
808
|
+
* The visitor collects the names of type-only import-equals and emits a named/default type import if the type-only import-equals is not a namespace-type alias.
|
|
809
|
+
* The visitor also collects the names of type-only imports of namespace-type aliases and emits a namespace type import if the type-only import-equals is not a namespace-type alias.
|
|
810
|
+
*/
|
|
685
811
|
const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements) => {
|
|
812
|
+
// Pre-scan: collect names of type-only import-equals (these are namespace-type aliases)
|
|
813
|
+
// import type NameSpace = require("foo")
|
|
686
814
|
const typeOnlyImportEquals = new Set();
|
|
687
815
|
for (const stmt of sourceFile.statements) {
|
|
688
816
|
if (ts.isImportEqualsDeclaration(stmt) && stmt.isTypeOnly) {
|
|
@@ -704,6 +832,7 @@ const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements)
|
|
|
704
832
|
importedString: undefined,
|
|
705
833
|
importedObject: undefined,
|
|
706
834
|
};
|
|
835
|
+
// --- Case: TypeReference with QualifiedName (collect type usage)
|
|
707
836
|
if (ts.isTypeReferenceNode(node) &&
|
|
708
837
|
ts.isQualifiedName(node.typeName) &&
|
|
709
838
|
ts.isIdentifier(node.typeName.left) &&
|
|
@@ -717,17 +846,23 @@ const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements)
|
|
|
717
846
|
else {
|
|
718
847
|
typeObj[left] = [right];
|
|
719
848
|
}
|
|
849
|
+
// If this qualified name refers to a type-only import-equals alias, DO NOT rewrite.
|
|
850
|
+
// Rewriting (Foo.Bar -> Bar) was intended to support converting to named imports,
|
|
851
|
+
// but for type-only namespace imports we will emit `import type * as Foo from "..."`.
|
|
720
852
|
if (utilities.checkModuleType(sourceFile, depsTree.file).isCommonJs) {
|
|
721
853
|
if (left !== "ts" && !typeOnlyImportEquals.has(left)) {
|
|
722
854
|
return factory.updateTypeReferenceNode(node, factory.createIdentifier(right), undefined);
|
|
723
855
|
}
|
|
724
856
|
}
|
|
725
857
|
}
|
|
858
|
+
// ------------------------
|
|
726
859
|
if (ts.isImportDeclaration(node)) {
|
|
860
|
+
// --- Case 1: Import declarations
|
|
727
861
|
const text = node.getText(sourceFile);
|
|
728
862
|
removedStatements.push(text);
|
|
729
863
|
return factory.createEmptyStatement();
|
|
730
864
|
}
|
|
865
|
+
//--- Case 2: Import equals declarations
|
|
731
866
|
if (ts.isImportEqualsDeclaration(node)) {
|
|
732
867
|
const name = node.name.text;
|
|
733
868
|
const moduleReference = node.moduleReference;
|
|
@@ -747,10 +882,12 @@ const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements)
|
|
|
747
882
|
let t;
|
|
748
883
|
if (obj.importedString && !obj.importedObject) {
|
|
749
884
|
if (obj.isTypeOnly) {
|
|
885
|
+
// If this import-equals was a type-only namespace alias, emit a namespace type import
|
|
750
886
|
if (typeOnlyImportEquals.has(obj.importedString)) {
|
|
751
887
|
t = `import type * as ${obj.importedString} from "${obj.source}";`;
|
|
752
888
|
}
|
|
753
889
|
else {
|
|
890
|
+
// otherwise try to emit a named/default type import (existing behavior)
|
|
754
891
|
if (typesNames.includes(obj.importedString)) {
|
|
755
892
|
t = `import type { ${typeObj[obj.importedString]?.join(",")} } from "${obj.source}";`;
|
|
756
893
|
}
|
|
@@ -771,11 +908,13 @@ const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements)
|
|
|
771
908
|
if (!obj.importedString && obj.importedObject) {
|
|
772
909
|
t = `import { ${obj.importedObject.join(", ")} } from "${obj.source}";`;
|
|
773
910
|
}
|
|
911
|
+
// removed
|
|
774
912
|
if (t) {
|
|
775
913
|
removedStatements.push(t);
|
|
776
914
|
return factory.createEmptyStatement();
|
|
777
915
|
}
|
|
778
916
|
}
|
|
917
|
+
// --- Case 3: Require imports
|
|
779
918
|
if (ts.isVariableStatement(node)) {
|
|
780
919
|
const decls = node.declarationList.declarations;
|
|
781
920
|
if (decls.length === 1) {
|
|
@@ -784,6 +923,7 @@ const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements)
|
|
|
784
923
|
ts.isCallExpression(decl.initializer) &&
|
|
785
924
|
ts.isIdentifier(decl.initializer.expression) &&
|
|
786
925
|
decl.initializer.expression.escapedText === "require") {
|
|
926
|
+
// imported from
|
|
787
927
|
const arg = decl.initializer.arguments[0];
|
|
788
928
|
if (ts.isStringLiteral(arg)) {
|
|
789
929
|
obj.source = arg.text;
|
|
@@ -825,15 +965,35 @@ const removeImportsVisitor = (context, depsTree, sourceFile, removedStatements)
|
|
|
825
965
|
}
|
|
826
966
|
}
|
|
827
967
|
}
|
|
968
|
+
// --------- Visitor Return ------------------//
|
|
828
969
|
return ts.visitEachChild(node, visit, context);
|
|
829
970
|
};
|
|
830
971
|
return visit;
|
|
831
972
|
};
|
|
973
|
+
//src/lib/bundle/index.ts
|
|
974
|
+
// ------------------------------------------------------------------------------------//
|
|
975
|
+
/**
|
|
976
|
+
* Bundles a TypeScript project into a single file.
|
|
977
|
+
* This function takes a {@link CollatedPoint} object as input and returns a {@link BundleResultPoint} object.
|
|
978
|
+
* The function applies the following steps:
|
|
979
|
+
* 1. Call dependency plugins.
|
|
980
|
+
* 2. Handle duplicates.
|
|
981
|
+
* 3. Handling anonymous imports and exports.
|
|
982
|
+
* 4. Remove Imports.
|
|
983
|
+
* 5. Remove Exports from dependencies only.
|
|
984
|
+
* 6. Handle imported statements.
|
|
985
|
+
* 7. Create final content.
|
|
986
|
+
* 8. Call pre-process plugins.
|
|
987
|
+
* 9. Returns.
|
|
988
|
+
* @param {CollatedPoint} point - A {@link CollatedPoint} object.
|
|
989
|
+
* @returns {Promise<BundleResultPoint>} - A promise resolves with a {@link BundleResultPoint} object.
|
|
990
|
+
*/
|
|
832
991
|
async function bundler(point) {
|
|
833
992
|
let depsFiles = point.depFiles;
|
|
834
993
|
const reName = point.rename;
|
|
835
994
|
const compilerOptions = point.tsOptions.default;
|
|
836
995
|
const plugins = point.plugins;
|
|
996
|
+
// construct maps
|
|
837
997
|
const namesMap = new Map();
|
|
838
998
|
const callNameMap = [];
|
|
839
999
|
const importNameMap = [];
|
|
@@ -843,8 +1003,11 @@ async function bundler(point) {
|
|
|
843
1003
|
let removedStatements = [];
|
|
844
1004
|
const duplicate = async (reName) => {
|
|
845
1005
|
if (reName) {
|
|
1006
|
+
// order is important here
|
|
846
1007
|
const re_name = resolves([
|
|
1008
|
+
// collector
|
|
847
1009
|
[bundleCreator, duplicateCollectionVisitor, compilerOptions, namesMap],
|
|
1010
|
+
// update
|
|
848
1011
|
[
|
|
849
1012
|
bundleCreator,
|
|
850
1013
|
duplicateUpdateVisitor,
|
|
@@ -852,6 +1015,7 @@ async function bundler(point) {
|
|
|
852
1015
|
namesMap,
|
|
853
1016
|
callNameMap,
|
|
854
1017
|
],
|
|
1018
|
+
// call exp
|
|
855
1019
|
[
|
|
856
1020
|
bundleCreator,
|
|
857
1021
|
duplicateCallExpressionVisitor,
|
|
@@ -859,6 +1023,7 @@ async function bundler(point) {
|
|
|
859
1023
|
callNameMap,
|
|
860
1024
|
importNameMap,
|
|
861
1025
|
],
|
|
1026
|
+
// export exp
|
|
862
1027
|
[
|
|
863
1028
|
bundleCreator,
|
|
864
1029
|
duplicateExportExpressionVisitor,
|
|
@@ -866,6 +1031,7 @@ async function bundler(point) {
|
|
|
866
1031
|
importNameMap,
|
|
867
1032
|
exportNameMap,
|
|
868
1033
|
],
|
|
1034
|
+
// import exp
|
|
869
1035
|
[
|
|
870
1036
|
bundleCreator,
|
|
871
1037
|
duplicateImportExpressionVisitor,
|
|
@@ -873,6 +1039,7 @@ async function bundler(point) {
|
|
|
873
1039
|
exportNameMap,
|
|
874
1040
|
importNameMap,
|
|
875
1041
|
],
|
|
1042
|
+
// export exp again
|
|
876
1043
|
[
|
|
877
1044
|
bundleCreator,
|
|
878
1045
|
duplicateExportExpressionVisitor,
|
|
@@ -880,6 +1047,7 @@ async function bundler(point) {
|
|
|
880
1047
|
importNameMap,
|
|
881
1048
|
exportNameMap,
|
|
882
1049
|
],
|
|
1050
|
+
// export exp again
|
|
883
1051
|
[
|
|
884
1052
|
bundleCreator,
|
|
885
1053
|
duplicateExportExpressionVisitor,
|
|
@@ -887,7 +1055,7 @@ async function bundler(point) {
|
|
|
887
1055
|
importNameMap,
|
|
888
1056
|
exportNameMap,
|
|
889
1057
|
],
|
|
890
|
-
]);
|
|
1058
|
+
]); // re_name
|
|
891
1059
|
const re_name_call = await re_name.concurrent();
|
|
892
1060
|
for (const call of re_name_call) {
|
|
893
1061
|
await utilities.wait(500);
|
|
@@ -897,6 +1065,7 @@ async function bundler(point) {
|
|
|
897
1065
|
else {
|
|
898
1066
|
let _err = false;
|
|
899
1067
|
const un_rename = resolves([
|
|
1068
|
+
// collector
|
|
900
1069
|
[bundleCreator, duplicateCollectionVisitor, compilerOptions, namesMap],
|
|
901
1070
|
]);
|
|
902
1071
|
const un_rename_call = await un_rename.concurrent();
|
|
@@ -906,6 +1075,7 @@ async function bundler(point) {
|
|
|
906
1075
|
if (files.size > 1) {
|
|
907
1076
|
_err = true;
|
|
908
1077
|
console.warn(`Name -> ${name} declared in multiple files :`);
|
|
1078
|
+
// biome-ignore lint/suspicious/useIterableCallbackReturn : just log warn
|
|
909
1079
|
files.forEach((f) => console.warn(` - ${f.file}`));
|
|
910
1080
|
}
|
|
911
1081
|
});
|
|
@@ -915,6 +1085,7 @@ async function bundler(point) {
|
|
|
915
1085
|
}
|
|
916
1086
|
}
|
|
917
1087
|
};
|
|
1088
|
+
// 1. Call dependency plugins
|
|
918
1089
|
if (plugins.length) {
|
|
919
1090
|
for (let plugin of plugins) {
|
|
920
1091
|
plugin = typeof plugin === "function" ? plugin() : plugin;
|
|
@@ -927,9 +1098,11 @@ async function bundler(point) {
|
|
|
927
1098
|
}
|
|
928
1099
|
}
|
|
929
1100
|
}
|
|
930
|
-
}
|
|
1101
|
+
} //--
|
|
931
1102
|
await utilities.wait(1000);
|
|
1103
|
+
// 2. Handle duplicates
|
|
932
1104
|
await duplicate(reName);
|
|
1105
|
+
// 3. Handling anonymous imports and exports
|
|
933
1106
|
const anonymous = resolves([
|
|
934
1107
|
[
|
|
935
1108
|
bundleCreator,
|
|
@@ -956,21 +1129,28 @@ async function bundler(point) {
|
|
|
956
1129
|
depsFiles = depsFiles.map(call);
|
|
957
1130
|
}
|
|
958
1131
|
await utilities.wait(1000);
|
|
1132
|
+
// 4. Remove Imports
|
|
959
1133
|
const removeImports = resolves([
|
|
960
1134
|
[bundleCreator, removeImportsVisitor, compilerOptions, removedStatements],
|
|
961
1135
|
]);
|
|
962
1136
|
const removeImport = await removeImports.concurrent();
|
|
963
1137
|
depsFiles = depsFiles.map(removeImport[0]);
|
|
964
1138
|
await utilities.wait(500);
|
|
1139
|
+
// 5. Remove Exports from dependencies only
|
|
965
1140
|
const removeExports = resolves([
|
|
966
1141
|
[bundleCreator, removeExportsVisitor, compilerOptions],
|
|
967
1142
|
]);
|
|
968
1143
|
const removeExport = await removeExports.concurrent();
|
|
1144
|
+
// not remove exports from entry file
|
|
969
1145
|
const deps_files = depsFiles.slice(0, -1).map(removeExport[0]);
|
|
970
1146
|
const mainFile = depsFiles.slice(-1);
|
|
1147
|
+
// 6. Handle imported statements
|
|
1148
|
+
// filter removed statements , that not from local like `./` or `../`
|
|
971
1149
|
const regexp = /["']((?!\.\/|\.\.\/)[^"']+)["']/;
|
|
972
1150
|
removedStatements = removedStatements.filter((i) => regexp.test(i));
|
|
973
1151
|
removedStatements = mergeImportsStatement(removedStatements);
|
|
1152
|
+
// 7. Create final content
|
|
1153
|
+
// make sure all imports are at the top of file
|
|
974
1154
|
const importStatements = removedStatements.join("\n").trim();
|
|
975
1155
|
const depFilesContent = deps_files
|
|
976
1156
|
.map((i) => {
|
|
@@ -987,7 +1167,9 @@ async function bundler(point) {
|
|
|
987
1167
|
.join("\n")
|
|
988
1168
|
.trim();
|
|
989
1169
|
await utilities.wait(1000);
|
|
1170
|
+
// text join order is important here
|
|
990
1171
|
let content = `${importStatements}\n${depFilesContent}\n${mainFileContent}`;
|
|
1172
|
+
// 8. Call pre-process plugins
|
|
991
1173
|
if (plugins.length) {
|
|
992
1174
|
for (let plugin of plugins) {
|
|
993
1175
|
plugin = typeof plugin === "function" ? plugin() : plugin;
|
|
@@ -1000,7 +1182,8 @@ async function bundler(point) {
|
|
|
1000
1182
|
}
|
|
1001
1183
|
}
|
|
1002
1184
|
}
|
|
1003
|
-
}
|
|
1185
|
+
} //--
|
|
1186
|
+
// 9. Returns
|
|
1004
1187
|
return { bundledContent: content, ...point };
|
|
1005
1188
|
}
|
|
1006
1189
|
async function bundle(object) {
|
|
@@ -1014,6 +1197,16 @@ async function bundle(object) {
|
|
|
1014
1197
|
allowUpdatePackageJson: object.allowUpdatePackageJson,
|
|
1015
1198
|
};
|
|
1016
1199
|
}
|
|
1200
|
+
//src/lib/compile/host.ts
|
|
1201
|
+
/**
|
|
1202
|
+
* Creates a ts.CompilerHost that can be used with the typescript compiler.
|
|
1203
|
+
* This host is designed to be used with in-memory compilation and will
|
|
1204
|
+
* return the source file for the given fileName and will write all output
|
|
1205
|
+
* files to the createdFiles object.
|
|
1206
|
+
* @param {string} sourceCode - the source code to compile
|
|
1207
|
+
* @param {string} fileName - the name of the file to compile
|
|
1208
|
+
* @returns {{createdFiles: Record<string, string>, host: ts.CompilerHost}}
|
|
1209
|
+
*/
|
|
1017
1210
|
function createHost(sourceCode, fileName) {
|
|
1018
1211
|
const createdFiles = {};
|
|
1019
1212
|
const host = {
|
|
@@ -1037,19 +1230,31 @@ function createHost(sourceCode, fileName) {
|
|
|
1037
1230
|
};
|
|
1038
1231
|
return { createdFiles, host };
|
|
1039
1232
|
}
|
|
1233
|
+
//src/lib/compile/package.ts
|
|
1040
1234
|
const isCjs = (files) => files.commonjs && files.commonjsTypes;
|
|
1041
1235
|
const isEsm = (files) => files.esm && files.esmTypes;
|
|
1236
|
+
/**
|
|
1237
|
+
* Builds a package exports mapping for the given output files and export path.
|
|
1238
|
+
*
|
|
1239
|
+
* Produces the appropriate export shape based on whether CommonJS and/or ESM
|
|
1240
|
+
* artifacts are present, including their default entry points and type
|
|
1241
|
+
* definitions. If neither format is available, returns an empty object.
|
|
1242
|
+
*
|
|
1243
|
+
* @param files - The build output file paths for CommonJS/ESM and their types.
|
|
1244
|
+
* @param exportPath - The subpath export key (e.g. "." or "./feature").
|
|
1245
|
+
* @returns A {@link Exports} object describing the package exports map.
|
|
1246
|
+
*/
|
|
1042
1247
|
function getExports(files, exportPath) {
|
|
1043
1248
|
return isCjs(files) && isEsm(files)
|
|
1044
1249
|
? {
|
|
1045
1250
|
[exportPath]: {
|
|
1046
1251
|
import: {
|
|
1047
|
-
default: `./${path.relative(process.cwd(), files.esm)}`,
|
|
1048
1252
|
types: `./${path.relative(process.cwd(), files.esmTypes)}`,
|
|
1253
|
+
default: `./${path.relative(process.cwd(), files.esm)}`,
|
|
1049
1254
|
},
|
|
1050
1255
|
require: {
|
|
1051
|
-
default: `./${path.relative(process.cwd(), files.commonjs)}`,
|
|
1052
1256
|
types: `./${path.relative(process.cwd(), files.commonjsTypes)}`,
|
|
1257
|
+
default: `./${path.relative(process.cwd(), files.commonjs)}`,
|
|
1053
1258
|
},
|
|
1054
1259
|
},
|
|
1055
1260
|
}
|
|
@@ -1057,8 +1262,8 @@ function getExports(files, exportPath) {
|
|
|
1057
1262
|
? {
|
|
1058
1263
|
[exportPath]: {
|
|
1059
1264
|
require: {
|
|
1060
|
-
default: `./${path.relative(process.cwd(), files.commonjs)}`,
|
|
1061
1265
|
types: `./${path.relative(process.cwd(), files.commonjsTypes)}`,
|
|
1266
|
+
default: `./${path.relative(process.cwd(), files.commonjs)}`,
|
|
1062
1267
|
},
|
|
1063
1268
|
},
|
|
1064
1269
|
}
|
|
@@ -1066,13 +1271,23 @@ function getExports(files, exportPath) {
|
|
|
1066
1271
|
? {
|
|
1067
1272
|
[exportPath]: {
|
|
1068
1273
|
import: {
|
|
1069
|
-
default: `./${path.relative(process.cwd(), files.esm)}`,
|
|
1070
1274
|
types: `./${path.relative(process.cwd(), files.esmTypes)}`,
|
|
1275
|
+
default: `./${path.relative(process.cwd(), files.esm)}`,
|
|
1071
1276
|
},
|
|
1072
1277
|
},
|
|
1073
1278
|
}
|
|
1074
1279
|
: {};
|
|
1075
1280
|
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Writes an updated `package.json` based on output files and export path.
|
|
1283
|
+
*
|
|
1284
|
+
* Determines module type (ESM/CommonJS), adjusts `main`, `module`, `types`,
|
|
1285
|
+
* and `exports` fields, and preserves other existing fields from the
|
|
1286
|
+
* current `package.json`.
|
|
1287
|
+
*
|
|
1288
|
+
* @param files - The generated output files used to populate entry points.
|
|
1289
|
+
* @param exportPath - The export path for subpath exports; "." denotes main export.
|
|
1290
|
+
*/
|
|
1076
1291
|
async function writePackage(files, exportPath) {
|
|
1077
1292
|
let isMain = true;
|
|
1078
1293
|
if (exportPath !== ".") {
|
|
@@ -1125,6 +1340,7 @@ async function writePackage(files, exportPath) {
|
|
|
1125
1340
|
};
|
|
1126
1341
|
utilities.writeCompileFile(pkgFile, JSON.stringify(pkgJson, null, 2));
|
|
1127
1342
|
}
|
|
1343
|
+
//src/lib/compile/index.ts
|
|
1128
1344
|
function splitCamelCase(str) {
|
|
1129
1345
|
const splitString = str
|
|
1130
1346
|
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
@@ -1153,17 +1369,20 @@ class Compiler {
|
|
|
1153
1369
|
const isMain = point.exportPath === ".";
|
|
1154
1370
|
const _name = isMain ? "Main" : splitCamelCase(point.exportPath.slice(2));
|
|
1155
1371
|
console.time(tcolor.cyan(`Compiled commonjs for ${_name} export path`));
|
|
1372
|
+
// init
|
|
1156
1373
|
const fileName = point.fileName;
|
|
1157
1374
|
const sourceCode = point.bundledContent;
|
|
1158
1375
|
const format = point.format;
|
|
1159
1376
|
const plugins = point.plugins;
|
|
1160
1377
|
const compilerOptions = point.tsOptions.cjs;
|
|
1378
|
+
// create host
|
|
1161
1379
|
const _host = createHost(sourceCode, fileName);
|
|
1162
1380
|
const createdFiles = _host.createdFiles;
|
|
1163
1381
|
const host = _host.host;
|
|
1164
1382
|
const program = ts.createProgram([fileName], compilerOptions, host);
|
|
1165
1383
|
program.emit();
|
|
1166
1384
|
Object.entries(createdFiles).map(async ([outName, content]) => {
|
|
1385
|
+
// ------------------------------------
|
|
1167
1386
|
if (plugins.length) {
|
|
1168
1387
|
for (let plugin of plugins) {
|
|
1169
1388
|
plugin = typeof plugin === "function" ? plugin() : plugin;
|
|
@@ -1206,11 +1425,13 @@ class Compiler {
|
|
|
1206
1425
|
const isMain = point.exportPath === ".";
|
|
1207
1426
|
const _name = isMain ? "Main" : splitCamelCase(point.exportPath.slice(2));
|
|
1208
1427
|
console.time(tcolor.cyan(`Compiled esm for ${_name} export path`));
|
|
1428
|
+
// init
|
|
1209
1429
|
const fileName = point.fileName;
|
|
1210
1430
|
const sourceCode = point.bundledContent;
|
|
1211
1431
|
const format = point.format;
|
|
1212
1432
|
const plugins = point.plugins;
|
|
1213
1433
|
const compilerOptions = point.tsOptions.esm;
|
|
1434
|
+
// create host
|
|
1214
1435
|
const _host = createHost(sourceCode, fileName);
|
|
1215
1436
|
const createdFiles = _host.createdFiles;
|
|
1216
1437
|
const host = _host.host;
|
|
@@ -1230,6 +1451,7 @@ class Compiler {
|
|
|
1230
1451
|
}
|
|
1231
1452
|
}
|
|
1232
1453
|
}
|
|
1454
|
+
// ------------------------------------------
|
|
1233
1455
|
if (this._isUpdate()) {
|
|
1234
1456
|
if (outName.match(/.js/g)) {
|
|
1235
1457
|
this.files.esm = outName.replace(/.js/g, ".mjs");
|
|
@@ -1252,6 +1474,14 @@ class Compiler {
|
|
|
1252
1474
|
});
|
|
1253
1475
|
console.timeEnd(tcolor.cyan(`Compiled esm for ${_name} export path`));
|
|
1254
1476
|
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Compile bundled code for each entry point.
|
|
1479
|
+
* This function will iterate through each entry point and compile code according to the format specified.
|
|
1480
|
+
* If the format is "commonjs", it will compile the code into commonjs format.
|
|
1481
|
+
* If the format is "esm", it will compile the code into esm format.
|
|
1482
|
+
* If the format is "both", it will compile the code into both commonjs and esm formats.
|
|
1483
|
+
* If the allowUpdatePackageJson flag is set to true, it will update the package.json according to the compiled file paths.
|
|
1484
|
+
*/
|
|
1255
1485
|
async compile() {
|
|
1256
1486
|
for (const point of this.object.points) {
|
|
1257
1487
|
await utilities.wait(500);
|
|
@@ -1281,13 +1511,23 @@ class Compiler {
|
|
|
1281
1511
|
}
|
|
1282
1512
|
}
|
|
1283
1513
|
}
|
|
1514
|
+
//src/lib/init/checks.ts
|
|
1284
1515
|
var checks;
|
|
1285
1516
|
(function (checks) {
|
|
1517
|
+
/**
|
|
1518
|
+
* Checks the given dependencies for type errors. If any type errors are found,
|
|
1519
|
+
* an error message is printed to the console and the process exits with a code of 1.
|
|
1520
|
+
* @param dep The dependencies to check for type errors
|
|
1521
|
+
* @param compilerOptions The compiler options to use when checking for type errors
|
|
1522
|
+
* @returns true if no type errors are found, false otherwise
|
|
1523
|
+
*/
|
|
1286
1524
|
function typesCheck(dep, compilerOptions) {
|
|
1287
1525
|
if (!compilerOptions.noCheck) {
|
|
1288
1526
|
const filePaths = dep.map((i) => i.file);
|
|
1289
1527
|
let _err = false;
|
|
1528
|
+
// Create program
|
|
1290
1529
|
const program = ts.createProgram(filePaths, compilerOptions);
|
|
1530
|
+
// Check each file individually for immediate feedback
|
|
1291
1531
|
for (const filePath of filePaths) {
|
|
1292
1532
|
const sourceFile = program.getSourceFile(filePath);
|
|
1293
1533
|
if (!sourceFile) {
|
|
@@ -1317,16 +1557,24 @@ var checks;
|
|
|
1317
1557
|
}
|
|
1318
1558
|
}
|
|
1319
1559
|
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Check the module type of the given dependencies.
|
|
1562
|
+
* @param _dep The dependencies to check for module type
|
|
1563
|
+
* @returns true if all dependencies are ESM, false otherwise
|
|
1564
|
+
*/
|
|
1320
1565
|
function moduleType(_dep) {
|
|
1321
1566
|
let _esmCount = 0;
|
|
1322
1567
|
let cjsCount = 0;
|
|
1323
1568
|
let unknownCount = 0;
|
|
1324
1569
|
for (const dep of _dep) {
|
|
1325
1570
|
try {
|
|
1571
|
+
// Create a TypeScript source file
|
|
1326
1572
|
const sourceFile = ts.createSourceFile(dep.file, dep.content, ts.ScriptTarget.Latest, true);
|
|
1327
1573
|
let hasESMImports = false;
|
|
1328
1574
|
let hasCommonJS = false;
|
|
1575
|
+
// Walk through the AST to detect module syntax
|
|
1329
1576
|
function walk(node) {
|
|
1577
|
+
// Check for ESM import/export syntax
|
|
1330
1578
|
if (ts.isImportDeclaration(node) ||
|
|
1331
1579
|
ts.isImportEqualsDeclaration(node) ||
|
|
1332
1580
|
ts.isExportDeclaration(node) ||
|
|
@@ -1334,6 +1582,7 @@ var checks;
|
|
|
1334
1582
|
ts.isExportAssignment(node)) {
|
|
1335
1583
|
hasESMImports = true;
|
|
1336
1584
|
}
|
|
1585
|
+
// Check for export modifier on declarations
|
|
1337
1586
|
if ((ts.isVariableStatement(node) ||
|
|
1338
1587
|
ts.isFunctionDeclaration(node) ||
|
|
1339
1588
|
ts.isInterfaceDeclaration(node) ||
|
|
@@ -1343,6 +1592,7 @@ var checks;
|
|
|
1343
1592
|
node.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
1344
1593
|
hasESMImports = true;
|
|
1345
1594
|
}
|
|
1595
|
+
// Check for CommonJS require/exports
|
|
1346
1596
|
if (ts.isCallExpression(node)) {
|
|
1347
1597
|
if (ts.isIdentifier(node.expression) &&
|
|
1348
1598
|
node.expression.text === "require" &&
|
|
@@ -1350,6 +1600,7 @@ var checks;
|
|
|
1350
1600
|
hasCommonJS = true;
|
|
1351
1601
|
}
|
|
1352
1602
|
}
|
|
1603
|
+
// Check for module.exports or exports.xxx
|
|
1353
1604
|
if (ts.isPropertyAccessExpression(node)) {
|
|
1354
1605
|
const text = node.getText(sourceFile);
|
|
1355
1606
|
if (text.startsWith("module.exports") ||
|
|
@@ -1357,9 +1608,11 @@ var checks;
|
|
|
1357
1608
|
hasCommonJS = true;
|
|
1358
1609
|
}
|
|
1359
1610
|
}
|
|
1611
|
+
// Continue walking the AST
|
|
1360
1612
|
ts.forEachChild(node, walk);
|
|
1361
1613
|
}
|
|
1362
1614
|
walk(sourceFile);
|
|
1615
|
+
// Determine the module format based on what we found
|
|
1363
1616
|
if (hasESMImports && !hasCommonJS) {
|
|
1364
1617
|
_esmCount++;
|
|
1365
1618
|
}
|
|
@@ -1367,6 +1620,7 @@ var checks;
|
|
|
1367
1620
|
cjsCount++;
|
|
1368
1621
|
}
|
|
1369
1622
|
else if (hasESMImports && hasCommonJS) {
|
|
1623
|
+
// Mixed - probably ESM with dynamic imports or similar
|
|
1370
1624
|
_esmCount++;
|
|
1371
1625
|
}
|
|
1372
1626
|
}
|
|
@@ -1407,6 +1661,8 @@ var checks;
|
|
|
1407
1661
|
}
|
|
1408
1662
|
checks.init = init;
|
|
1409
1663
|
})(checks || (checks = {}));
|
|
1664
|
+
//src/lib/init/config.ts
|
|
1665
|
+
// -------------
|
|
1410
1666
|
const getConfigPath = () => {
|
|
1411
1667
|
const fileNames = ["susee.config.ts", "susee.config.js", "susee.config.mjs"];
|
|
1412
1668
|
let configFile;
|
|
@@ -1419,6 +1675,7 @@ const getConfigPath = () => {
|
|
|
1419
1675
|
}
|
|
1420
1676
|
return configFile;
|
|
1421
1677
|
};
|
|
1678
|
+
//---------
|
|
1422
1679
|
function checkEntries(entries) {
|
|
1423
1680
|
if (entries.length < 1) {
|
|
1424
1681
|
console.error(tcolor.magenta(`No entry found in susee.config file, at least one entry required`));
|
|
@@ -1446,6 +1703,11 @@ function checkEntries(entries) {
|
|
|
1446
1703
|
}
|
|
1447
1704
|
}
|
|
1448
1705
|
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Get SuSee configuration from susee.config file (susee.config.ts, susee.config.js, susee.config.mjs)
|
|
1708
|
+
* @returns {Promise<ConfigReturns>} - SuSee configuration
|
|
1709
|
+
* @throws {Error} - when no susee.config file found
|
|
1710
|
+
*/
|
|
1449
1711
|
async function getConfig() {
|
|
1450
1712
|
const configPath = getConfigPath();
|
|
1451
1713
|
if (configPath === undefined) {
|
|
@@ -1465,6 +1727,7 @@ async function getConfig() {
|
|
|
1465
1727
|
format: ent.format ?? "esm",
|
|
1466
1728
|
tsconfigFilePath: ent.tsconfigFilePath ?? undefined,
|
|
1467
1729
|
renameDuplicates: ent.renameDuplicates ?? true,
|
|
1730
|
+
// TODO check for defined out dir here or in config.ts
|
|
1468
1731
|
outDir: config.outDir ?? "dist",
|
|
1469
1732
|
};
|
|
1470
1733
|
points.push(point);
|
|
@@ -1475,10 +1738,12 @@ async function getConfig() {
|
|
|
1475
1738
|
allowUpdatePackageJson: config.allowUpdatePackageJson ?? true,
|
|
1476
1739
|
};
|
|
1477
1740
|
}
|
|
1741
|
+
//src/lib/init/deps.ts
|
|
1742
|
+
//---------------
|
|
1478
1743
|
async function fileSizes(path) {
|
|
1479
1744
|
const s = await fs.promises.stat(path);
|
|
1480
|
-
const logical = s.size;
|
|
1481
|
-
const allocated = s.blocks !== null ? s.blocks * 512 : null;
|
|
1745
|
+
const logical = s.size; // bytes in file
|
|
1746
|
+
const allocated = s.blocks !== null ? s.blocks * 512 : null; // bytes actually allocated (POSIX)
|
|
1482
1747
|
return { logical, allocated };
|
|
1483
1748
|
}
|
|
1484
1749
|
const checkExport = (str, file) => {
|
|
@@ -1492,9 +1757,28 @@ const checkExport = (str, file) => {
|
|
|
1492
1757
|
return false;
|
|
1493
1758
|
}
|
|
1494
1759
|
};
|
|
1760
|
+
/**
|
|
1761
|
+
* Generate dependencies graph for given entry file.
|
|
1762
|
+
*
|
|
1763
|
+
* This function will return an array of dependencies file objects.
|
|
1764
|
+
* Each object will contain the following properties:
|
|
1765
|
+
* - file: path to the file
|
|
1766
|
+
* - content: content of the file
|
|
1767
|
+
* - length: length of the content in bytes
|
|
1768
|
+
* - includeDefExport: whether the file includes export default or export = statement
|
|
1769
|
+
* - size: an object containing the following properties:
|
|
1770
|
+
* - logical: size of the file in bytes
|
|
1771
|
+
* - allocated: size of the file in bytes on disk
|
|
1772
|
+
* - utf8: size of the file in bytes when encoded in utf8
|
|
1773
|
+
* - buffBytes: size of the file in bytes when encoded in buffer
|
|
1774
|
+
*
|
|
1775
|
+
* @param {string} entryFile - path to the entry file
|
|
1776
|
+
* @param {SuseePlugins} plugins - array of plugins
|
|
1777
|
+
* @returns {Promise<DepsFiles>}
|
|
1778
|
+
*/
|
|
1495
1779
|
async function generateDependencies(entryFile, plugins) {
|
|
1496
1780
|
const deps = await dependencies(entryFile);
|
|
1497
|
-
const sorted = deps.sort();
|
|
1781
|
+
const sorted = deps.sort(); // get dependencies graph
|
|
1498
1782
|
let depsFiles = [];
|
|
1499
1783
|
await utilities.wait(1000);
|
|
1500
1784
|
for (const dep of sorted) {
|
|
@@ -1517,6 +1801,7 @@ async function generateDependencies(entryFile, plugins) {
|
|
|
1517
1801
|
};
|
|
1518
1802
|
depsFiles.push(_files);
|
|
1519
1803
|
}
|
|
1804
|
+
// call dependency plugins
|
|
1520
1805
|
if (plugins.length) {
|
|
1521
1806
|
for (const plugin of plugins) {
|
|
1522
1807
|
const _plug = typeof plugin === "function" ? plugin() : plugin;
|
|
@@ -1533,6 +1818,7 @@ async function generateDependencies(entryFile, plugins) {
|
|
|
1533
1818
|
}
|
|
1534
1819
|
return depsFiles;
|
|
1535
1820
|
}
|
|
1821
|
+
//src/lib/init/tsCompilerOptions.ts
|
|
1536
1822
|
class GetOptions {
|
|
1537
1823
|
constructor(point) {
|
|
1538
1824
|
this._point = point;
|
|
@@ -1554,6 +1840,7 @@ class GetOptions {
|
|
|
1554
1840
|
__init2() {
|
|
1555
1841
|
this.__init();
|
|
1556
1842
|
let { types, lib, ...restOptions } = this._options;
|
|
1843
|
+
// normalize types into an array
|
|
1557
1844
|
if (types) {
|
|
1558
1845
|
if (!types.includes("node")) {
|
|
1559
1846
|
types = ["node", ...types];
|
|
@@ -1584,9 +1871,27 @@ class GetOptions {
|
|
|
1584
1871
|
return this.__init2();
|
|
1585
1872
|
}
|
|
1586
1873
|
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Returns an instance of GetOptions, which provides various methods
|
|
1876
|
+
* to generate different sets of compiler options based on the
|
|
1877
|
+
* given Point.
|
|
1878
|
+
*
|
|
1879
|
+
* @param {Point} point - The point to generate compiler options for.
|
|
1880
|
+
* @returns {GetOptions}
|
|
1881
|
+
*/
|
|
1587
1882
|
function getOptions(point) {
|
|
1588
1883
|
return new GetOptions(point);
|
|
1589
1884
|
}
|
|
1885
|
+
//src/lib/init/index.ts
|
|
1886
|
+
/**
|
|
1887
|
+
* This function takes a susee configuration object and returns a promise that resolves with a `CollatedReturn` object.
|
|
1888
|
+
* The function iterates over the `points` array in the susee configuration object.
|
|
1889
|
+
* For each point, it generates the dependencies using the `generateDependencies` function.
|
|
1890
|
+
* It then checks if the dependencies are valid using the `checks.init` function.
|
|
1891
|
+
* If the dependencies are invalid, it exits the process with code 1.
|
|
1892
|
+
* If the dependencies are valid, it constructs a `CollatedPoint` object and adds it to the result array.
|
|
1893
|
+
* Finally, it returns a `CollatedReturn` object with the result array and the `allowUpdatePackageJson` flag.
|
|
1894
|
+
*/
|
|
1590
1895
|
async function collections() {
|
|
1591
1896
|
const __config = await getConfig();
|
|
1592
1897
|
const points = __config.points;
|
|
@@ -1619,6 +1924,21 @@ async function collections() {
|
|
|
1619
1924
|
allowUpdatePackageJson: __config.allowUpdatePackageJson,
|
|
1620
1925
|
};
|
|
1621
1926
|
}
|
|
1927
|
+
//src/index.ts
|
|
1928
|
+
/**
|
|
1929
|
+
* Bundles a TypeScript project into a single file.
|
|
1930
|
+
* The function takes a {@link SuSeeConfig} object as input and returns a promise resolves with a bundled result.
|
|
1931
|
+
* The function applies the following steps:
|
|
1932
|
+
* 1. Call dependency plugins.
|
|
1933
|
+
* 2. Handle duplicates.
|
|
1934
|
+
* 3. Handling anonymous imports and exports.
|
|
1935
|
+
* 4. Remove Imports.
|
|
1936
|
+
* 5. Remove Exports from dependencies only.
|
|
1937
|
+
* 6. Handle imported statements.
|
|
1938
|
+
* 7. Create final content.
|
|
1939
|
+
* 8. Call pre-process plugins.
|
|
1940
|
+
* 9. Returns.
|
|
1941
|
+
*/
|
|
1622
1942
|
async function susee() {
|
|
1623
1943
|
console.info(`${tcolor.green("Start")} : ${tcolor.cyan("bundling")}`);
|
|
1624
1944
|
const collected = await collections();
|