susee 1.6.2 → 2.0.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/index.mjs DELETED
@@ -1,3436 +0,0 @@
1
- /*! *****************************************************************************
2
- Copyright (c) Pho Thin Mg <phothinmg@disroot.org>
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
5
- this file except in compliance with the License. You may obtain a copy of the
6
- License at http://www.apache.org/licenses/LICENSE-2.0
7
- ***************************************************************************** */
8
-
9
- import fs from "node:fs";
10
- import module from "node:module";
11
- import path from "node:path";
12
- import process from "node:process";
13
- import readline from "node:readline/promises";
14
- import tcolor from "@suseejs/color";
15
- import ts6 from "@suseejs/ts6";
16
- //src/helpers/files.ts
17
- var files;
18
- (function (files_1) {
19
- const root = process.cwd();
20
- function resolvePath(pathStr) {
21
- return path.resolve(root, pathStr);
22
- }
23
- files_1.resolvePath = resolvePath;
24
- function relativePath(pathStr) {
25
- return path.relative(root, pathStr);
26
- }
27
- files_1.relativePath = relativePath;
28
- function joinPath(...paths) {
29
- return path.join(...paths);
30
- }
31
- files_1.joinPath = joinPath;
32
- function existsPath(pathStr) {
33
- return fs.existsSync(resolvePath(pathStr));
34
- }
35
- files_1.existsPath = existsPath;
36
- async function deleteFile(filePath) {
37
- if (existsPath(filePath)) {
38
- await fs.promises.unlink(filePath);
39
- }
40
- }
41
- files_1.deleteFile = deleteFile;
42
- async function readFile(filePath) {
43
- if (!existsPath(filePath)) {
44
- console.error(tcolor.magenta(`> ${filePath} does not exists `));
45
- process.exit(1);
46
- }
47
- filePath = resolvePath(filePath);
48
- const readContent = await fs.promises.readFile(filePath);
49
- return {
50
- str: readContent.toString("utf8"),
51
- bytes: readContent.byteLength,
52
- };
53
- }
54
- files_1.readFile = readFile;
55
- async function readJsonFile(filePath) {
56
- const read = await readFile(filePath);
57
- return JSON.parse(read.str);
58
- }
59
- files_1.readJsonFile = readJsonFile;
60
- async function createDirectory(dirPath) {
61
- dirPath = resolvePath(dirPath);
62
- if (!existsPath(dirPath)) {
63
- await fs.promises.mkdir(dirPath, { recursive: true });
64
- }
65
- }
66
- files_1.createDirectory = createDirectory;
67
- function parentPath(filePath) {
68
- return path.dirname(resolvePath(filePath));
69
- }
70
- files_1.parentPath = parentPath;
71
- async function writeFile(filePath, content) {
72
- if (existsPath(filePath))
73
- await deleteFile(filePath);
74
- await createDirectory(parentPath(filePath));
75
- filePath = resolvePath(filePath);
76
- await fs.promises.writeFile(filePath, content);
77
- }
78
- files_1.writeFile = writeFile;
79
- async function clearFolder(folderPath) {
80
- folderPath = resolvePath(folderPath);
81
- try {
82
- const entries = await fs.promises.readdir(folderPath, {
83
- withFileTypes: true,
84
- });
85
- await Promise.all(entries.map((entry) => fs.promises.rm(path.join(folderPath, entry.name), {
86
- recursive: true,
87
- })));
88
- }
89
- catch (error) {
90
- // biome-ignore lint/suspicious/noExplicitAny: error code
91
- if (error.code !== "ENOENT") {
92
- throw error;
93
- }
94
- }
95
- }
96
- files_1.clearFolder = clearFolder;
97
- // -------------------------------------------------------------------------------------------//
98
- const isCjs = (files) => files.commonjs && files.commonjsTypes;
99
- const isEsm = (files) => files.esm && files.esmTypes;
100
- function getExports(files, exportPath) {
101
- return isCjs(files) && isEsm(files)
102
- ? {
103
- [exportPath]: {
104
- import: {
105
- types: `./${path.relative(process.cwd(), files.esmTypes)}`,
106
- default: `./${path.relative(process.cwd(), files.esm)}`,
107
- },
108
- require: {
109
- types: `./${path.relative(process.cwd(), files.commonjsTypes)}`,
110
- default: `./${path.relative(process.cwd(), files.commonjs)}`,
111
- },
112
- },
113
- }
114
- : isCjs(files) && !isEsm(files)
115
- ? {
116
- [exportPath]: {
117
- require: {
118
- types: `./${path.relative(process.cwd(), files.commonjsTypes)}`,
119
- default: `./${path.relative(process.cwd(), files.commonjs)}`,
120
- },
121
- },
122
- }
123
- : !isCjs(files) && isEsm(files)
124
- ? {
125
- [exportPath]: {
126
- import: {
127
- types: `./${path.relative(process.cwd(), files.esmTypes)}`,
128
- default: `./${path.relative(process.cwd(), files.esm)}`,
129
- },
130
- },
131
- }
132
- : {};
133
- }
134
- async function writePackageJson(files, exportPath) {
135
- let isMain = true;
136
- if (exportPath !== ".") {
137
- isMain = false;
138
- }
139
- const pkgFile = resolvePath("package.json");
140
- const pkgtext = await readJsonFile(pkgFile);
141
- let { name, version, description, main, module, type, types, exports, ...rest } = pkgtext;
142
- type = "module";
143
- let _main = {};
144
- let _module = {};
145
- let _types = {};
146
- let _exports = {};
147
- if (isMain) {
148
- _main = files.main
149
- ? { main: path.relative(process.cwd(), files.main) }
150
- : {};
151
- _module = files.module
152
- ? { module: path.relative(process.cwd(), files.module) }
153
- : {};
154
- _types = files.types
155
- ? { types: path.relative(process.cwd(), files.types) }
156
- : {};
157
- _exports = { exports: { ...getExports(files, exportPath) } };
158
- }
159
- else {
160
- _main = main ? { main: main } : {};
161
- _module = module ? { module: module } : {};
162
- _types = types ? { types: types } : {};
163
- const normalizedExports = exports && typeof exports === "object" && !Array.isArray(exports)
164
- ? { ...exports }
165
- : {};
166
- _exports = {
167
- exports: { ...normalizedExports, ...getExports(files, exportPath) },
168
- };
169
- }
170
- const pkgJson = {
171
- name,
172
- version,
173
- description,
174
- type,
175
- ..._main,
176
- ..._types,
177
- ..._module,
178
- ..._exports,
179
- ...rest,
180
- };
181
- await writeFile(pkgFile, JSON.stringify(pkgJson, null, 2));
182
- }
183
- files_1.writePackageJson = writePackageJson;
184
- })(files || (files = {}));
185
- //src/helpers/utilities.ts
186
- var utils;
187
- (function (utils) {
188
- let checks;
189
- (function (checks) {
190
- checks.moduleType = (content, file) => {
191
- let _esmCount = 0;
192
- let cjsCount = 0;
193
- let unknownCount = 0;
194
- const sourceFile = ts6.createSourceFile(file, content, ts6.ScriptTarget.Latest, true);
195
- try {
196
- let hasESMImports = false;
197
- let hasCommonJS = false;
198
- // Walk through the AST to detect module syntax
199
- function walk(node) {
200
- // Check for ESM import/export syntax
201
- if (ts6.isImportDeclaration(node) ||
202
- ts6.isImportEqualsDeclaration(node) ||
203
- ts6.isExportDeclaration(node) ||
204
- ts6.isExportSpecifier(node) ||
205
- ts6.isExportAssignment(node)) {
206
- hasESMImports = true;
207
- }
208
- // Check for export modifier on declarations
209
- if ((ts6.isVariableStatement(node) ||
210
- ts6.isFunctionDeclaration(node) ||
211
- ts6.isInterfaceDeclaration(node) ||
212
- ts6.isTypeAliasDeclaration(node) ||
213
- ts6.isEnumDeclaration(node) ||
214
- ts6.isClassDeclaration(node)) &&
215
- node.modifiers?.some((mod) => mod.kind === ts6.SyntaxKind.ExportKeyword)) {
216
- hasESMImports = true;
217
- }
218
- // Check for CommonJS require/exports
219
- if (ts6.isCallExpression(node)) {
220
- if (ts6.isIdentifier(node.expression) &&
221
- node.expression.text === "require" &&
222
- node.arguments.length > 0) {
223
- hasCommonJS = true;
224
- }
225
- }
226
- // Check for module.exports or exports.xxx
227
- if (ts6.isPropertyAccessExpression(node)) {
228
- const text = node.getText(sourceFile);
229
- if (text.startsWith("module.exports") ||
230
- text.startsWith("exports.")) {
231
- hasCommonJS = true;
232
- }
233
- }
234
- // Continue walking the AST
235
- ts6.forEachChild(node, walk);
236
- } //---
237
- walk(sourceFile);
238
- // Determine the module format based on what we found
239
- if (hasESMImports && !hasCommonJS) {
240
- _esmCount++;
241
- }
242
- else if (hasCommonJS && !hasESMImports) {
243
- cjsCount++;
244
- }
245
- else if (hasESMImports && hasCommonJS) {
246
- // Mixed - probably ESM with dynamic imports or similar
247
- _esmCount++;
248
- }
249
- }
250
- catch (error) {
251
- console.error(tcolor.magenta(`Error checking module format for ${file} : \n ${error}`));
252
- unknownCount++;
253
- }
254
- if (unknownCount > 0) {
255
- console.error(tcolor.magenta(`Error checking module format.`));
256
- ts6.sys.exit(1);
257
- }
258
- return {
259
- isCommonJs: cjsCount > 0,
260
- isEsm: _esmCount > 0,
261
- };
262
- };
263
- /**
264
- * Checks if the given code string contains JSX syntax.
265
- * @param code The content of the file as a string.
266
- * @returns true if the file contains JSX, false otherwise.
267
- */
268
- function isJsxContent(code) {
269
- const sourceFile = ts6.createSourceFile("file.tsx", code, ts6.ScriptTarget.Latest,
270
- /*setParentNodes*/ true, ts6.ScriptKind.TSX);
271
- let containsJsx = false;
272
- function visitor(node) {
273
- // Check for JSX Elements, Self Closing Elements, or JSX Fragments
274
- if (ts6.isJsxElement(node) ||
275
- ts6.isJsxSelfClosingElement(node) ||
276
- ts6.isJsxFragment(node)) {
277
- containsJsx = true;
278
- return;
279
- }
280
- ts6.forEachChild(node, visitor);
281
- }
282
- visitor(sourceFile);
283
- return containsJsx;
284
- }
285
- checks.isJsxContent = isJsxContent;
286
- /**
287
- * Checks if a given node is inside a namespace declaration.
288
- * It does this by traversing up the parent nodes until it finds a module declaration with the namespace flag set.
289
- * @param n The node to check.
290
- * @returns true if the node is inside a namespace declaration, false otherwise.
291
- */
292
- checks.isInsideNamespace = (n) => {
293
- let current = n.parent;
294
- while (current) {
295
- if (ts6.isModuleDeclaration(current) &&
296
- current.flags === ts6.NodeFlags.Namespace) {
297
- return true;
298
- }
299
- current = current.parent;
300
- }
301
- return false;
302
- };
303
- /**
304
- * Check if a given module is a Node.js built-in module.
305
- * @param {string} input - The module to check.
306
- * @returns {boolean} True if the module is a Node.js built-in module, false otherwise.
307
- */
308
- checks.isNodeBuiltinModule = (input) => {
309
- const nodeModuleSpecifier = "node:";
310
- const nodeBuiltinModules = new Set(module.builtinModules);
311
- return (input.startsWith(nodeModuleSpecifier) || nodeBuiltinModules.has(input));
312
- };
313
- })(checks = utils.checks || (utils.checks = {})); // namespace checks
314
- let promises;
315
- (function (promises) {
316
- function isPromiseFun(fun) {
317
- return (Object.prototype.toString.call(fun) === "[object AsyncFunction]" ||
318
- fun.constructor.name === "AsyncFunction");
319
- }
320
- function walkPromise(param) {
321
- const fn = param[0];
322
- const args = param.slice(1);
323
- if (isPromiseFun(fn)) {
324
- return async () => await fn(...args);
325
- }
326
- else {
327
- return async () => fn(...args);
328
- }
329
- }
330
- function resolve(params) {
331
- const funs = params.map((w) => walkPromise(w));
332
- const series = async () => {
333
- const results = [];
334
- for (const [index, task] of funs.entries()) {
335
- try {
336
- const result = await task();
337
- results.push(result);
338
- }
339
- catch (error) {
340
- console.error(`Error in task ${index + 1}`);
341
- throw error;
342
- }
343
- }
344
- return results;
345
- };
346
- const concurrent = async () => {
347
- try {
348
- return await Promise.all(funs.map((f) => f()));
349
- }
350
- catch (error) {
351
- console.error("One of the functions rejected:", error);
352
- throw error;
353
- }
354
- };
355
- const allSettled = async () => {
356
- try {
357
- const settled = await Promise.allSettled(funs.map((f) => f()));
358
- const fulfilled = settled.filter((re) => re.status === "fulfilled");
359
- const rejected = settled.filter((re) => re.status === "rejected");
360
- if (rejected.length > 0) {
361
- console.warn("One of the functions rejected:", rejected[0]?.reason);
362
- process.exit(1);
363
- }
364
- return fulfilled.map((re) => re.value);
365
- }
366
- catch (error) {
367
- console.error("One of the functions rejected:", error);
368
- throw error;
369
- }
370
- };
371
- return {
372
- series: series,
373
- concurrent: concurrent,
374
- allSettled: allSettled,
375
- };
376
- }
377
- promises.resolve = resolve;
378
- async function run(fun, time, ...args) {
379
- return new Promise((resolve, reject) => {
380
- try {
381
- const t = time ? 0 : time;
382
- const result = fun(...args);
383
- setTimeout(() => resolve(result), t);
384
- }
385
- catch (error) {
386
- reject(error);
387
- }
388
- });
389
- }
390
- promises.run = run;
391
- // biome-ignore-end lint/suspicious/noExplicitAny: unknown
392
- })(promises = utils.promises || (utils.promises = {})); // namespace promises
393
- let gen;
394
- (function (gen) {
395
- gen.mergeStringArr = (input) => {
396
- return input.reduce((prev, curr) => prev.concat(curr), []);
397
- };
398
- function splitCamelCase(str) {
399
- const splitString = str
400
- .replace(/([a-z])([A-Z])/g, "$1 $2")
401
- .replace(/(_|-|\/)([a-z] || [A-Z])/g, " ")
402
- .replace(/([A-Z])/g, (match) => match.toLowerCase())
403
- .replace(/^([a-z])/, (match) => match.toUpperCase());
404
- return splitString;
405
- }
406
- gen.splitCamelCase = splitCamelCase;
407
- function packageJson() {
408
- const packageContent = fs.readFileSync(path.resolve(process.cwd(), "package.json"), "utf8");
409
- const pkg = JSON.parse(packageContent);
410
- const name = pkg.name ?? "";
411
- const version = pkg.version ?? "";
412
- /**
413
- * Get package name and version
414
- * @returns {string}
415
- */
416
- const pkgNameVersion = () => {
417
- let pkg_nv = "";
418
- if (name !== "" && version !== "") {
419
- pkg_nv = `${name}@${version}`;
420
- }
421
- else if (name !== "" && version === "") {
422
- pkg_nv = `${name}`;
423
- }
424
- else if (name === "" && version !== "") {
425
- pkg_nv = `the project@${version}`;
426
- }
427
- else {
428
- pkg_nv = "the project";
429
- }
430
- return pkg_nv;
431
- };
432
- const dependencies = () => {
433
- const deps = Object.keys(pkg.dependencies ?? {});
434
- const devDeps = Object.keys(pkg.devDependencies ?? {});
435
- return [...deps, ...devDeps];
436
- };
437
- // -----------------------------------------
438
- return { pkgNameVersion, dependencies };
439
- }
440
- gen.packageJson = packageJson;
441
- /**
442
- * Merge a list of import statements into a minimal set of import statements.
443
- * The algorithm works by grouping imports by module path and then merging them into a single import statement.
444
- * Type imports are processed first, and then regular imports are processed.
445
- * The resulting import statements are sorted alphabetically by module path.
446
- * @param imports - A list of import statements to merge.
447
- * @returns A list of merged import statements.
448
- */
449
- function mergeImportsStatement(imports) {
450
- const importMap = new Map();
451
- const typeImportMap = new Map();
452
- const defaultImports = new Map();
453
- const typeDefaultImports = new Map();
454
- const namespaceImports = new Map();
455
- // Parse each import statement
456
- for (const importStr of imports) {
457
- const importMatch = importStr.match(/import\s+(?:type\s+)?(?:(.*?)\s+from\s+)?["']([^"']+)["'];?/);
458
- if (!importMatch)
459
- continue;
460
- const [, importClause, _modulePath] = importMatch;
461
- const isTypeImport = importStr.includes("import type");
462
- const modulePath = _modulePath;
463
- if (!importClause) {
464
- // Default import or side-effect import
465
- const defaultMatch = importStr.match(/import\s+(?:type\s+)?(\w+)/);
466
- if (defaultMatch) {
467
- const importName = defaultMatch[1];
468
- const targetMap = isTypeImport
469
- ? typeDefaultImports
470
- : defaultImports;
471
- if (!targetMap.has(modulePath))
472
- targetMap.set(modulePath, new Set());
473
- targetMap.get(modulePath)?.add(importName);
474
- }
475
- continue;
476
- }
477
- if (importClause.startsWith("{")) {
478
- // Named imports: import { a, b } from 'module'
479
- const targetMap = isTypeImport ? typeImportMap : importMap;
480
- if (!targetMap.has(modulePath))
481
- targetMap.set(modulePath, new Set());
482
- const names = importClause
483
- .replace(/[{}]/g, "")
484
- .split(",")
485
- .map((s) => s.trim())
486
- .filter(Boolean);
487
- names.forEach((name) => targetMap.get(modulePath)?.add(name));
488
- }
489
- else if (importClause.startsWith("* as")) {
490
- // Namespace import: import * as name from 'module'
491
- const namespaceMatch = importClause.match(/\*\s+as\s+(\w+)/);
492
- if (namespaceMatch) {
493
- const namespaceName = namespaceMatch[1];
494
- if (!namespaceImports.has(modulePath))
495
- namespaceImports.set(modulePath, new Set());
496
- namespaceImports.get(modulePath)?.add(namespaceName);
497
- }
498
- }
499
- else {
500
- // Default import: import name from 'module'
501
- const targetMap = isTypeImport ? typeDefaultImports : defaultImports;
502
- if (!targetMap.has(modulePath))
503
- targetMap.set(modulePath, new Set());
504
- targetMap.get(modulePath)?.add(importClause.trim());
505
- }
506
- }
507
- const mergedImports = [];
508
- // Process named imports - remove type imports that have regular imports
509
- for (const [modulePath, regularNames] of importMap) {
510
- const typeNames = typeImportMap.get(modulePath) || new Set();
511
- // Only include type names that don't have regular imports
512
- const finalNames = new Set([...regularNames]);
513
- for (const typeName of typeNames) {
514
- if (!regularNames.has(typeName)) {
515
- finalNames.add(typeName);
516
- }
517
- }
518
- if (finalNames.size > 0) {
519
- const importNames = Array.from(finalNames).sort().join(", ");
520
- mergedImports.push(`import { ${importNames} } from "${modulePath}";`);
521
- }
522
- }
523
- // Add remaining type-only imports (where no regular imports exist for the module)
524
- for (const [modulePath, typeNames] of typeImportMap) {
525
- if (!importMap.has(modulePath) && typeNames.size > 0) {
526
- const importNames = Array.from(typeNames).sort().join(", ");
527
- mergedImports.push(`import type { ${importNames} } from "${modulePath}";`);
528
- }
529
- }
530
- // Process default imports - remove type default imports that have regular default imports
531
- for (const [modulePath, regularDefaultNames] of defaultImports) {
532
- const typeDefaultNames = typeDefaultImports.get(modulePath) || new Set();
533
- // Only include type default names that don't have regular default imports
534
- const finalNames = new Set([...regularDefaultNames]);
535
- for (const typeName of typeDefaultNames) {
536
- if (!regularDefaultNames.has(typeName)) {
537
- finalNames.add(typeName);
538
- }
539
- }
540
- if (finalNames.size > 0) {
541
- const importNames = Array.from(finalNames).join(", ");
542
- mergedImports.push(`import ${importNames} from "${modulePath}";`);
543
- }
544
- }
545
- // Add remaining type-only default imports
546
- for (const [modulePath, typeDefaultNames] of typeDefaultImports) {
547
- if (!defaultImports.has(modulePath) && typeDefaultNames.size > 0) {
548
- const importNames = Array.from(typeDefaultNames).join(", ");
549
- mergedImports.push(`import type ${importNames} from "${modulePath}";`);
550
- }
551
- }
552
- // Process namespace imports
553
- for (const [modulePath, names] of namespaceImports) {
554
- if (names.size > 0) {
555
- const importNames = Array.from(names).join(", ");
556
- mergedImports.push(`import * as ${importNames} from "${modulePath}";`);
557
- }
558
- }
559
- return mergedImports.sort();
560
- } //--
561
- gen.mergeImportsStatement = mergeImportsStatement;
562
- /**
563
- * Applies a given transformer to a source file and returns the modified code.
564
- * @param transformer A transformer factory that will be called with the source file.
565
- * @param sourceFile The source file to which the transformer will be applied.
566
- * @param compilerOptions Compiler options to use when applying the transformer.
567
- * @returns The modified code after applying the transformer.
568
- */
569
- function transformFunction(transformer, sourceFile, compilerOptions) {
570
- const transformationResult = ts6.transform(sourceFile, [transformer], compilerOptions);
571
- const transformedSourceFile = transformationResult.transformed[0];
572
- const printer = ts6.createPrinter({
573
- newLine: ts6.NewLineKind.LineFeed,
574
- removeComments: false,
575
- });
576
- const modifiedCode = printer.printFile(transformedSourceFile);
577
- transformationResult.dispose();
578
- return modifiedCode;
579
- } //--
580
- gen.transformFunction = transformFunction;
581
- /**
582
- * Finds all the properties accessed in the given node.
583
- * @param {ts6.Node} node - The node to search through.
584
- * @returns {string[]} - An array of all the properties accessed.
585
- */
586
- function findProperty(node) {
587
- const properties = [];
588
- function walk(n) {
589
- if (ts6.isPropertyAccessExpression(n) &&
590
- ts6.isIdentifier(n.expression)) {
591
- properties.push(n.expression.text);
592
- }
593
- n.forEachChild(walk);
594
- }
595
- walk(node);
596
- return properties;
597
- }
598
- gen.findProperty = findProperty;
599
- })(gen = utils.gen || (utils.gen = {})); // namespace gen
600
- })(utils || (utils = {})); // namespace utils
601
- const getScopeNodeLabel = (sourceFile, node, index) => {
602
- if (ts6.isModuleDeclaration(node)) {
603
- return `namespace:${node.name.getText(sourceFile)}`;
604
- }
605
- if (ts6.isClassDeclaration(node)) {
606
- return `class:${node.name?.text ?? `anonymous-${index}`}`;
607
- }
608
- if (ts6.isFunctionDeclaration(node) || ts6.isFunctionExpression(node)) {
609
- return `function:${node.name?.text ?? `anonymous-${index}`}`;
610
- }
611
- if (ts6.isArrowFunction(node)) {
612
- return `arrow:${index}`;
613
- }
614
- if (ts6.isMethodDeclaration(node)) {
615
- return `method:${node.name.getText(sourceFile)}`;
616
- }
617
- if (ts6.isBlock(node)) {
618
- return `block:${index}`;
619
- }
620
- return `${ts6.SyntaxKind[node.kind].toLowerCase()}:${index}`;
621
- };
622
- const getScopeKey = (file, scopeStack) => {
623
- if (scopeStack.length === 0) {
624
- return "global";
625
- }
626
- return `${file}::${scopeStack.join(" > ")}`;
627
- };
628
- const isScopeNode = (node) => ts6.isModuleDeclaration(node) ||
629
- ts6.isClassDeclaration(node) ||
630
- ts6.isFunctionDeclaration(node) ||
631
- ts6.isFunctionExpression(node) ||
632
- ts6.isArrowFunction(node) ||
633
- ts6.isMethodDeclaration(node) ||
634
- ts6.isBlock(node);
635
- const collectDeclarationNames = (node) => {
636
- if (ts6.isVariableStatement(node)) {
637
- return node.declarationList.declarations.flatMap((decl) => {
638
- if (!ts6.isIdentifier(decl.name)) {
639
- return [];
640
- }
641
- return [{ name: decl.name.text, positionNode: decl.name }];
642
- });
643
- }
644
- if (ts6.isFunctionDeclaration(node) ||
645
- ts6.isClassDeclaration(node) ||
646
- ts6.isEnumDeclaration(node) ||
647
- ts6.isInterfaceDeclaration(node) ||
648
- ts6.isTypeAliasDeclaration(node)) {
649
- if (node.name) {
650
- return [{ name: node.name.text, positionNode: node.name }];
651
- }
652
- }
653
- return [];
654
- };
655
- const collectDuplicateDeclarations = (deps, bundledSourceFile) => {
656
- const duplicateNameMap = new Map();
657
- const addDuplicateDeclaration = (scopeKey, name, file, sourceFile, positionNode) => {
658
- const { line, character } = sourceFile.getLineAndCharacterOfPosition(positionNode.getStart(sourceFile));
659
- const location = {
660
- file,
661
- line: line + 1,
662
- column: character + 1,
663
- };
664
- const duplicateKey = `${scopeKey}::${name}`;
665
- if (!duplicateNameMap.has(duplicateKey)) {
666
- duplicateNameMap.set(duplicateKey, {
667
- name,
668
- locations: new Set([location]),
669
- });
670
- return;
671
- }
672
- duplicateNameMap.get(duplicateKey)?.locations.add(location);
673
- };
674
- const collectFile = (file, sourceFile, node, scopeStack = []) => {
675
- const scopeKey = getScopeKey(file, scopeStack);
676
- for (const declaration of collectDeclarationNames(node)) {
677
- addDuplicateDeclaration(scopeKey, declaration.name, file, sourceFile, declaration.positionNode);
678
- }
679
- if (isScopeNode(node)) {
680
- const nextScopeStack = [
681
- ...scopeStack,
682
- getScopeNodeLabel(sourceFile, node, node.getStart(sourceFile)),
683
- ];
684
- ts6.forEachChild(node, (child) => {
685
- collectFile(file, sourceFile, child, nextScopeStack);
686
- });
687
- return;
688
- }
689
- ts6.forEachChild(node, (child) => {
690
- collectFile(file, sourceFile, child, scopeStack);
691
- });
692
- };
693
- for (const dep of deps) {
694
- const sourceFile = bundledSourceFile(dep.file, dep.content);
695
- collectFile(dep.file, sourceFile, sourceFile);
696
- }
697
- return duplicateNameMap;
698
- };
699
- const checkDuplicates = (tree, bundledSourceFile) => {
700
- let _err = false;
701
- const duplicateNameMap = collectDuplicateDeclarations(tree.depFiles, bundledSourceFile);
702
- duplicateNameMap.forEach(({ name, locations }) => {
703
- if (locations.size > 1) {
704
- _err = true;
705
- console.warn(tcolor.yellow("[susee:error]"));
706
- console.warn(" Duplicate declarations found in your dependencies tree as follows:");
707
- console.warn(` - "${tcolor.magenta(name)}" declared in multiple files : `);
708
- locations.forEach((f) => console.warn(` - ${f.file}:${f.line}:${f.column}`));
709
- console.info("Please rename these with different names to avoid duplicate declarations.");
710
- }
711
- });
712
- if (_err) {
713
- process.exit(1);
714
- }
715
- return tree;
716
- };
717
- //src/dependencies/graph.ts
718
- // ----------------------------------------------------Handlers------------------------------------------------------//
719
- function handleImports(node, processFn) {
720
- // Handle : import declaration
721
- if (ts6.isImportDeclaration(node) && node.moduleSpecifier) {
722
- const moduleText = node.moduleSpecifier
723
- .getText()
724
- .replace(/^['"`]|['"`]$/g, "");
725
- processFn(moduleText);
726
- return;
727
- } //--
728
- // Recursively visit all children
729
- ts6.forEachChild(node, (n) => handleImports(n, processFn));
730
- }
731
- function handleImportEqual(node, processFn) {
732
- // Handle : import equal declaration
733
- if (ts6.isImportEqualsDeclaration(node) &&
734
- ts6.isExternalModuleReference(node.moduleReference) &&
735
- ts6.isStringLiteral(node.moduleReference.expression)) {
736
- const moduleText = node.moduleReference.expression.text;
737
- processFn(moduleText);
738
- return;
739
- } //--
740
- // Recursively visit all children
741
- ts6.forEachChild(node, (n) => handleImportEqual(n, processFn));
742
- }
743
- function handleAwaitImport(node, processFn) {
744
- // Handle : import equal declaration
745
- if (ts6.isAwaitExpression(node) &&
746
- ts6.isCallExpression(node.expression) &&
747
- node.expression.expression.kind === ts6.SyntaxKind.ImportKeyword) {
748
- const firstArg = node.expression.arguments[0];
749
- if (firstArg && ts6.isStringLiteral(firstArg)) {
750
- processFn(firstArg.text);
751
- }
752
- return;
753
- } //--
754
- // Recursively visit all children
755
- ts6.forEachChild(node, (n) => handleAwaitImport(n, processFn));
756
- }
757
- function handleRequire(node, processFn) {
758
- // Handle : require calls , `var foo = require("foo")`
759
- // can't handle import equal statement like `import foo = require("foo")`
760
- if (ts6.isCallExpression(node) &&
761
- ts6.isIdentifier(node.expression) &&
762
- node.expression.text === "require" &&
763
- node.arguments.length > 0) {
764
- // if expression callExpression node's text equal to require
765
- // index 0 of arguments is moduleText
766
- // I didn't use forEach or for-off loop to avoid multiple processing.
767
- const firstArg = node.arguments[0];
768
- if (firstArg && ts6.isStringLiteral(firstArg)) {
769
- processFn(firstArg.text);
770
- }
771
- return; // Skip children for property access require calls
772
- }
773
- // Handle : property access like `var foo = require("foo").foo`
774
- if (ts6.isPropertyAccessExpression(node) &&
775
- ts6.isCallExpression(node.expression) &&
776
- ts6.isIdentifier(node.expression.expression) &&
777
- node.expression.expression.text === "require" &&
778
- node.expression.arguments.length > 0) {
779
- const firstArg = node.expression.arguments[0];
780
- if (firstArg && ts6.isStringLiteral(firstArg)) {
781
- processFn(firstArg.text);
782
- }
783
- return; // Skip children for property access require calls
784
- }
785
- // Recursively visit all children (except for require calls we already processed)
786
- ts6.forEachChild(node, (n) => handleRequire(n, processFn));
787
- }
788
- function handlers(node, processFn) {
789
- Promise.all([
790
- handleImports(node, processFn),
791
- handleRequire(node, processFn),
792
- handleImportEqual(node, processFn),
793
- handleAwaitImport(node, processFn),
794
- ]);
795
- }
796
- // resolved extensions
797
- const allowedExtensions = new Set([
798
- "js",
799
- "cjs",
800
- "mjs",
801
- "ts",
802
- "mts",
803
- "cts",
804
- "jsx",
805
- "tsx",
806
- "json",
807
- ]);
808
- function isDir(filePath) {
809
- try {
810
- const stat = fs.lstatSync(filePath);
811
- return stat.isDirectory();
812
- }
813
- catch (err) {
814
- if (typeof err === "object" &&
815
- err !== null &&
816
- "code" in err &&
817
- // biome-ignore lint/suspicious/noExplicitAny: for error log only
818
- err.code === "ENOENT") {
819
- return false;
820
- }
821
- throw err;
822
- }
823
- }
824
- function getFileName(input) {
825
- const namePart = path.basename(input).split(".")[0];
826
- return namePart ? namePart.trim() : "";
827
- }
828
- function getExtensionName(input) {
829
- return path.basename(input).split(".")[1]?.trim() || "";
830
- }
831
- function resolveExtension(filePath) {
832
- let result;
833
- let ext;
834
- let isDirPath = false;
835
- // If it's a directory, look for index file
836
- if (isDir(filePath)) {
837
- const files = fs.readdirSync(filePath);
838
- const found = files.find((file) => getFileName(file) === "index" &&
839
- allowedExtensions.has(getExtensionName(file)));
840
- if (found) {
841
- result = path.join(filePath, found);
842
- ext = getExtensionName(found);
843
- isDirPath = true;
844
- }
845
- else {
846
- console.error(`${filePath} is a directory and no index file with JS/TS extension found.`); // ----------------------------------------------------------------------------------------------------------//
847
- process.exit(1);
848
- }
849
- }
850
- else {
851
- // Not a directory: try to resolve extension
852
- const dirName = path.dirname(filePath);
853
- const baseName = path.basename(filePath);
854
- const [fileName, extName = ""] = baseName.split(".");
855
- // const files = fs.globSync(
856
- // `${dirName}/**/*.{js,cjs,mjs,ts,cts,mts,jsx,tsx}`
857
- // );
858
- const files = ts6.sys.readDirectory(dirName);
859
- const match = files
860
- .map((f) => {
861
- const [name, ext = ""] = path.basename(f).split(".");
862
- return { name, ext };
863
- })
864
- .find((f) => f.name === fileName && allowedExtensions.has(f.ext));
865
- if (match) {
866
- if (!extName) {
867
- result = `${filePath}.${match.ext}`;
868
- ext = match.ext;
869
- }
870
- else if (extName === match.ext) {
871
- result = filePath;
872
- ext = match.ext;
873
- }
874
- else {
875
- const safeExtName = extName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
876
- result = filePath.replace(new RegExp(`\\.${safeExtName}$`), `.${match.ext}`);
877
- ext = match.ext;
878
- }
879
- }
880
- else {
881
- // If not found, maybe it's a directory import (e.g. ./lib)
882
- if (isDir(filePath)) {
883
- const files = fs.readdirSync(filePath);
884
- const found = files.find((file) => getFileName(file) === "index" &&
885
- allowedExtensions.has(getExtensionName(file)));
886
- if (found) {
887
- result = path.join(filePath, found);
888
- ext = getExtensionName(found);
889
- isDirPath = true;
890
- }
891
- }
892
- }
893
- }
894
- if (!(result && ext)) {
895
- console.error(`When checking ${filePath}, it's not a file or file with unsupported extension`);
896
- process.exit(1);
897
- }
898
- return { result, ext, isDirPath };
899
- }
900
- function collectDependencies(entry, collectedDependencies, root) {
901
- const dependencies = [];
902
- const visited = new Set();
903
- const collectedNpmModules = [];
904
- const collectedNodeModules = [];
905
- const collectedWarning = [];
906
- function visit(file, index) {
907
- const absPath = path.resolve(root, file);
908
- if (visited.has(absPath))
909
- return;
910
- visited.add(absPath);
911
- const { result: checkedAbsPath } = resolveExtension(absPath);
912
- if (!fs.existsSync(checkedAbsPath)) {
913
- dependencies.push({
914
- file: absPath,
915
- index,
916
- importFiles: [],
917
- });
918
- collectedWarning.push([`File not found: ${checkedAbsPath}`]);
919
- }
920
- const content = fs.readFileSync(checkedAbsPath, "utf8");
921
- const sourceFile = ts6.createSourceFile(file, content, ts6.ScriptTarget.Latest, true);
922
- const importFiles = [];
923
- const warn = [];
924
- const npmModules = [];
925
- const nodeModules = [];
926
- function processModule(moduleText) {
927
- // Handle : Imported local dependencies of a file.
928
- if (moduleText.startsWith(".") || moduleText.startsWith("..")) {
929
- // Try to resolve as file or directory
930
- const resolvedModulePath = path.resolve(path.dirname(checkedAbsPath), moduleText);
931
- // biome-ignore lint/suspicious/noExplicitAny: just let
932
- let resolved = {};
933
- try {
934
- resolved = resolveExtension(resolvedModulePath);
935
- }
936
- catch {
937
- // fallback: treat as file with extension
938
- resolved = {
939
- result: resolvedModulePath,
940
- ext: path.extname(resolvedModulePath).slice(1),
941
- isDirPath: false,
942
- };
943
- }
944
- const relImport = path.relative(root, resolved.result);
945
- importFiles.push(relImport);
946
- }
947
- // Handle : Imported dependencies of node builtin modules of a file.
948
- else if (module.builtinModules.includes(moduleText)) {
949
- nodeModules.push(moduleText);
950
- }
951
- // Handle : Imported npm dependencies of a file
952
- // Handle : Imported npm dependencies of a file, by checking local package.json
953
- // currently only check for these dependencies are installed or not, depend on project's package.json
954
- // TODO try for provide information such as exported files , to use in bundle process
955
- else if (collectedDependencies.includes(moduleText)) {
956
- npmModules.push(moduleText);
957
- }
958
- // Unknown dependencies
959
- // local dependencies are checked before by resolveExtension function.
960
- // TODO try for analyze these errors and provide analyzed report.
961
- else {
962
- warn.push(moduleText);
963
- }
964
- }
965
- ts6.forEachChild(sourceFile, (node) => handlers(node, processModule));
966
- dependencies.push({
967
- file: absPath,
968
- index,
969
- importFiles,
970
- });
971
- collectedNpmModules.push(npmModules);
972
- collectedNodeModules.push(nodeModules);
973
- collectedWarning.push(warn);
974
- importFiles.forEach((depFile) => visit(depFile, dependencies.length));
975
- }
976
- visit(entry, 0);
977
- return {
978
- dependencies,
979
- collectedNodeModules,
980
- collectedNpmModules,
981
- collectedWarning,
982
- };
983
- }
984
- //
985
- function getPackageJson() {
986
- const packageContent = fs.readFileSync(path.resolve(process.cwd(), "package.json"), "utf8");
987
- const pkg = JSON.parse(packageContent);
988
- const deps = Object.keys(pkg.dependencies ?? {});
989
- const devDeps = Object.keys(pkg.devDependencies ?? {});
990
- return [...deps, ...devDeps];
991
- }
992
- // Topological sort of a directed acyclic graph (DAG)
993
- function topoSort(tree) {
994
- const visited = new Set();
995
- const sorted = [];
996
- function visit(node) {
997
- if (visited.has(node))
998
- return;
999
- visited.add(node);
1000
- (tree[node] || []).forEach(visit);
1001
- sorted.push(node);
1002
- }
1003
- Object.keys(tree).forEach(visit);
1004
- return sorted; // reverse for correct order
1005
- }
1006
- // Create a dependency graph from a list of collected dependencies
1007
- const createGraph = (deps) => {
1008
- const graph = {};
1009
- for (const dep of deps) {
1010
- const _name = path.relative(process.cwd(), dep.file);
1011
- graph[_name] = dep.importFiles;
1012
- }
1013
- return graph;
1014
- };
1015
- function generateGraph(entry) {
1016
- const root = process.cwd();
1017
- const collectedDependencies = getPackageJson();
1018
- const collectedData = collectDependencies(entry, collectedDependencies, root);
1019
- const graphObj = collectedData.dependencies;
1020
- const npmModules = utils.gen.mergeStringArr(collectedData.collectedNpmModules);
1021
- const nodeModules = utils.gen.mergeStringArr(collectedData.collectedNodeModules);
1022
- const warning = utils.gen.mergeStringArr(collectedData.collectedWarning);
1023
- const depsObj = createGraph(graphObj);
1024
- const sortedGraph = topoSort(depsObj);
1025
- return {
1026
- sort: () => sortedGraph,
1027
- npm: () => npmModules,
1028
- node: () => nodeModules,
1029
- deps: () => depsObj,
1030
- warn: () => warning,
1031
- };
1032
- }
1033
- //src/dependencies/index.ts
1034
- async function generateDependencies(entry, bundledSourceFile) {
1035
- const graph = generateGraph(entry);
1036
- const sorted = graph.sort();
1037
- const npm = graph.npm();
1038
- const nodes = graph.node();
1039
- const warns = graph.warn();
1040
- const tree = {
1041
- entry,
1042
- npm,
1043
- nodes,
1044
- warns,
1045
- depFiles: [],
1046
- };
1047
- const entryBase = path.basename(entry);
1048
- for (const file of sorted) {
1049
- const fileBase = path.basename(file);
1050
- const fileExt = path.extname(file);
1051
- const read = await files.readFile(file);
1052
- const content = read.str;
1053
- const bytes = read.bytes;
1054
- const mt = utils.checks.moduleType(content, file);
1055
- const moduleType = fileExt === ".json" ? "json" : mt.isCommonJs ? "cjs" : "esm";
1056
- const isJsx = utils.checks.isJsxContent(content);
1057
- const isEntry = entryBase === fileBase;
1058
- tree.depFiles.push({
1059
- file,
1060
- content,
1061
- bytes,
1062
- moduleType,
1063
- fileExt: fileExt,
1064
- is_jsx: isJsx,
1065
- is_entry: isEntry,
1066
- });
1067
- }
1068
- return checkDuplicates(tree, bundledSourceFile);
1069
- }
1070
- //src/helpers/profile.ts
1071
- const profileEnvName = "SUSEE_PROFILE";
1072
- const isProfileEnabled = () => {
1073
- const value = process.env[profileEnvName];
1074
- return value === "1" || value === "true";
1075
- };
1076
- const setProfileEnabled = (enabled) => {
1077
- if (enabled) {
1078
- process.env[profileEnvName] = "1";
1079
- return;
1080
- }
1081
- delete process.env[profileEnvName];
1082
- };
1083
- const formatProfileMs = (start) => {
1084
- return `${(Number(process.hrtime.bigint() - start) / 1000000).toFixed(1)}ms`;
1085
- };
1086
- const logProfilePhase = (scope, phase, start) => {
1087
- if (!isProfileEnabled())
1088
- return;
1089
- console.log(`[SUSEE_PROFILE][${scope}] ${phase}: ${formatProfileMs(start)}`);
1090
- };
1091
- //src/bundler/lib/helpers.ts
1092
- const isJSON = (tree) => {
1093
- const json = tree.depFiles.find((file) => file.fileExt === ".json" && file.moduleType === "json");
1094
- return !!json;
1095
- };
1096
- const jsonExtToTs = (file) => {
1097
- if (path.extname(file) === ".json") {
1098
- return file.replace(/.json/g, ".ts");
1099
- }
1100
- else {
1101
- return file;
1102
- }
1103
- };
1104
- const createBundledSourceFile = (file, content) => {
1105
- return ts6.createSourceFile(jsonExtToTs(file), content, ts6.ScriptTarget.Latest, true);
1106
- };
1107
- const transformBundledSource = (sourceFile, compilerOptions, transformer) => {
1108
- return utils.gen.transformFunction(transformer, sourceFile, compilerOptions);
1109
- };
1110
- const normalizePathKey = (filePath) => {
1111
- const parsed = path.parse(filePath);
1112
- let noExt = path.join(parsed.dir, parsed.name);
1113
- if (parsed.name === "index") {
1114
- noExt = parsed.dir;
1115
- }
1116
- return path.normalize(noExt);
1117
- };
1118
- const getFileKey = (filePath) => normalizePathKey(filePath);
1119
- const getModuleKeyFromSpecifier = (moduleSpecifier, sourceFile, containingFile) => {
1120
- let spec = "";
1121
- if (ts6.isStringLiteral(moduleSpecifier)) {
1122
- spec = moduleSpecifier.text;
1123
- }
1124
- else {
1125
- spec = moduleSpecifier.getText(sourceFile).replace(/^['"]|['"]$/g, "");
1126
- }
1127
- if (spec.startsWith(".") || spec.startsWith("/")) {
1128
- const baseDir = path.dirname(containingFile);
1129
- const resolved = path.isAbsolute(containingFile)
1130
- ? path.resolve(baseDir, spec)
1131
- : path.normalize(path.join(baseDir, spec));
1132
- return normalizePathKey(resolved);
1133
- }
1134
- return spec;
1135
- };
1136
- //src/bundler/lib/uniqueName.ts
1137
- class UniqueName {
1138
- _storedPrefix;
1139
- constructor() {
1140
- this._storedPrefix = new Map();
1141
- }
1142
- setPrefix({ key, value }) {
1143
- if (this._storedPrefix.has(key)) {
1144
- const [_prefix, count] = this._storedPrefix.get(key);
1145
- this._storedPrefix.set(key, [value, count + 1]);
1146
- }
1147
- else {
1148
- this._storedPrefix.set(key, [value, 0]);
1149
- }
1150
- return this;
1151
- }
1152
- getName(key, input) {
1153
- const [prefix, count] = this._storedPrefix.get(key) || [];
1154
- const _name = prefix
1155
- ? `${prefix}${input}_${(count ?? 0) + 1}`
1156
- : `__susee__${input}_${(count ?? 0) + 1}`;
1157
- this._storedPrefix.set(key, [prefix ?? "__susee__", (count ?? 0) + 1]);
1158
- return _name;
1159
- }
1160
- getPrefix(key) {
1161
- const [prefix] = this._storedPrefix.get(key) || [];
1162
- return prefix;
1163
- }
1164
- }
1165
- const uniqueName = new UniqueName();
1166
- //src/bundler/lib/anonymous.ts
1167
- const anonymousExportNameMap = [];
1168
- const anonymousImportNameMap = [];
1169
- const anonymousPrefixKey = "AnonymousName";
1170
- const createAnonymousNameGenerator = () => uniqueName.setPrefix({
1171
- key: anonymousPrefixKey,
1172
- value: "susee__anonymous__",
1173
- });
1174
- let anonymousName = createAnonymousNameGenerator();
1175
- function anonymousCallExpressionHandler(compilerOptions) {
1176
- return ({ file, content, ...rest }) => {
1177
- const sourceFile = createBundledSourceFile(file, content);
1178
- const transformer = (context) => {
1179
- const { factory } = context;
1180
- function visitor(node) {
1181
- if (ts6.isCallExpression(node)) {
1182
- if (ts6.isIdentifier(node.expression)) {
1183
- const base = node.expression.text;
1184
- const mapping = anonymousImportNameMap.find((m) => m.base === base && m.file === file);
1185
- if (mapping) {
1186
- return factory.updateCallExpression(node, factory.createIdentifier(mapping.newName), node.typeArguments, node.arguments);
1187
- }
1188
- }
1189
- }
1190
- else if (ts6.isPropertyAccessExpression(node)) {
1191
- if (ts6.isIdentifier(node.expression)) {
1192
- const base = node.expression.text;
1193
- const mapping = anonymousImportNameMap.find((m) => m.base === base && m.file === file);
1194
- if (mapping) {
1195
- return factory.updatePropertyAccessExpression(node, factory.createIdentifier(mapping.newName), node.name);
1196
- }
1197
- }
1198
- }
1199
- else if (ts6.isNewExpression(node)) {
1200
- if (ts6.isIdentifier(node.expression)) {
1201
- const base = node.expression.text;
1202
- const mapping = anonymousImportNameMap.find((m) => m.base === base && m.file === file);
1203
- if (mapping) {
1204
- return factory.updateNewExpression(node, factory.createIdentifier(mapping.newName), node.typeArguments, node.arguments);
1205
- }
1206
- }
1207
- // for export specifier it is focus on entry file
1208
- }
1209
- else if (ts6.isExportSpecifier(node)) {
1210
- if (ts6.isIdentifier(node.name)) {
1211
- const base = node.name.text;
1212
- const mapping = anonymousImportNameMap.find((m) => m.base === base && m.file === file);
1213
- if (mapping) {
1214
- return factory.updateExportSpecifier(node, node.isTypeOnly, node.propertyName, factory.createIdentifier(mapping.newName));
1215
- }
1216
- }
1217
- }
1218
- return ts6.visitEachChild(node, visitor, context);
1219
- }
1220
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1221
- };
1222
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1223
- return { file, content: _content, ...rest };
1224
- };
1225
- }
1226
- //--
1227
- function anonymousExportHandler(compilerOptions) {
1228
- return ({ file, content, ...rest }) => {
1229
- const sourceFile = createBundledSourceFile(file, content);
1230
- /**
1231
- * A transformer that handles anonymous default exports by assigning them a name
1232
- *
1233
- * @param {ts6.TransformationContext} context - transformation context
1234
- * @returns {ts6.Transformer<ts6.SourceFile>} - transformer
1235
- */
1236
- const transformer = (context) => {
1237
- const { factory } = context;
1238
- function visitor(node) {
1239
- const fileName = path.basename(file).split(".")[0];
1240
- if ((ts6.isFunctionDeclaration(node) || ts6.isClassDeclaration(node)) &&
1241
- node.name === undefined) {
1242
- let exp = false;
1243
- let def = false;
1244
- node.modifiers?.forEach((mod) => {
1245
- if (mod.kind === ts6.SyntaxKind.ExportKeyword) {
1246
- exp = true;
1247
- }
1248
- if (mod.kind === ts6.SyntaxKind.DefaultKeyword) {
1249
- def = true;
1250
- }
1251
- });
1252
- if (exp && def) {
1253
- const base = anonymousName.getName(anonymousPrefixKey, fileName);
1254
- anonymousExportNameMap.push({
1255
- base,
1256
- file: fileName,
1257
- newName: base,
1258
- isEd: true,
1259
- });
1260
- if (ts6.isFunctionDeclaration(node)) {
1261
- return factory.updateFunctionDeclaration(node, node.modifiers, node.asteriskToken, factory.createIdentifier(base), node.typeParameters, node.parameters, node.type, node.body);
1262
- }
1263
- else if (ts6.isClassDeclaration(node)) {
1264
- return factory.updateClassDeclaration(node, node.modifiers, factory.createIdentifier(base), node.typeParameters, node.heritageClauses, node.members);
1265
- }
1266
- }
1267
- }
1268
- else if (ts6.isExportAssignment(node) &&
1269
- !node.isExportEquals &&
1270
- node.name === undefined) {
1271
- if (ts6.isArrowFunction(node.expression)) {
1272
- const base = anonymousName.getName(anonymousPrefixKey, fileName);
1273
- const arrowFunctionNode = factory.createArrowFunction(node.expression.modifiers, node.expression.typeParameters, node.expression.parameters, node.expression.type, node.expression.equalsGreaterThanToken, node.expression.body);
1274
- const variableDeclarationNode = factory.createVariableDeclaration(factory.createIdentifier(base), node.expression.exclamationToken, node.expression.type, arrowFunctionNode);
1275
- const variableDeclarationListNode = factory.createVariableDeclarationList([variableDeclarationNode], ts6.NodeFlags.Const);
1276
- const variableStatementNode = factory.createVariableStatement(node.expression.modifiers, variableDeclarationListNode);
1277
- const exportAssignmentNode = factory.createExportAssignment(undefined, undefined, factory.createIdentifier(base));
1278
- anonymousExportNameMap.push({
1279
- base,
1280
- file: fileName,
1281
- newName: base,
1282
- isEd: true,
1283
- });
1284
- return factory.updateSourceFile(sourceFile, [variableStatementNode, exportAssignmentNode], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
1285
- }
1286
- else if (ts6.isObjectLiteralExpression(node.expression)) {
1287
- const base = anonymousName.getName(anonymousPrefixKey, fileName);
1288
- const variableDeclarationNode = factory.createVariableDeclaration(factory.createIdentifier(base), undefined, undefined, node.expression);
1289
- const variableDeclarationListNode = factory.createVariableDeclarationList([variableDeclarationNode], ts6.NodeFlags.Const);
1290
- const variableStatementNode = factory.createVariableStatement(undefined, variableDeclarationListNode);
1291
- const exportAssignmentNode = factory.createExportAssignment(undefined, undefined, factory.createIdentifier(base));
1292
- anonymousExportNameMap.push({
1293
- base,
1294
- file: fileName,
1295
- newName: base,
1296
- isEd: true,
1297
- });
1298
- return factory.updateSourceFile(sourceFile, [variableStatementNode, exportAssignmentNode], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
1299
- }
1300
- else if (ts6.isArrayLiteralExpression(node.expression)) {
1301
- const base = anonymousName.getName(anonymousPrefixKey, fileName);
1302
- const arrayLiteralExpressionNode = factory.createArrayLiteralExpression(node.expression.elements, true);
1303
- const variableDeclarationNode = factory.createVariableDeclaration(factory.createIdentifier(base), undefined, undefined, arrayLiteralExpressionNode);
1304
- const variableDeclarationListNode = factory.createVariableDeclarationList([variableDeclarationNode], ts6.NodeFlags.Const);
1305
- const variableStatementNode = factory.createVariableStatement(undefined, variableDeclarationListNode);
1306
- const exportAssignmentNode = factory.createExportAssignment(undefined, undefined, factory.createIdentifier(base));
1307
- anonymousExportNameMap.push({
1308
- base,
1309
- file: fileName,
1310
- newName: base,
1311
- isEd: true,
1312
- });
1313
- return factory.updateSourceFile(sourceFile, [variableStatementNode, exportAssignmentNode], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
1314
- }
1315
- else if (ts6.isStringLiteral(node.expression)) {
1316
- const base = anonymousName.getName(anonymousPrefixKey, fileName);
1317
- const stringLiteralNode = factory.createStringLiteral(node.expression.text);
1318
- const variableDeclarationNode = factory.createVariableDeclaration(factory.createIdentifier(base), undefined, undefined, stringLiteralNode);
1319
- const variableDeclarationListNode = factory.createVariableDeclarationList([variableDeclarationNode], ts6.NodeFlags.Const);
1320
- const variableStatementNode = factory.createVariableStatement(undefined, variableDeclarationListNode);
1321
- const exportAssignmentNode = factory.createExportAssignment(undefined, undefined, factory.createIdentifier(base));
1322
- anonymousExportNameMap.push({
1323
- base,
1324
- file: fileName,
1325
- newName: base,
1326
- isEd: true,
1327
- });
1328
- return factory.updateSourceFile(sourceFile, [variableStatementNode, exportAssignmentNode], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
1329
- }
1330
- else if (ts6.isNumericLiteral(node.expression)) {
1331
- const base = anonymousName.getName(anonymousPrefixKey, fileName);
1332
- const numericLiteralNode = factory.createNumericLiteral(node.expression.text);
1333
- const variableDeclarationNode = factory.createVariableDeclaration(factory.createIdentifier(base), undefined, undefined, numericLiteralNode);
1334
- const variableDeclarationListNode = factory.createVariableDeclarationList([variableDeclarationNode], ts6.NodeFlags.Const);
1335
- const variableStatementNode = factory.createVariableStatement(undefined, variableDeclarationListNode);
1336
- const exportAssignmentNode = factory.createExportAssignment(undefined, undefined, factory.createIdentifier(base));
1337
- anonymousExportNameMap.push({
1338
- base,
1339
- file: fileName,
1340
- newName: base,
1341
- isEd: true,
1342
- });
1343
- return factory.updateSourceFile(sourceFile, [variableStatementNode, exportAssignmentNode], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
1344
- }
1345
- } //
1346
- return ts6.visitEachChild(node, visitor, context);
1347
- }
1348
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1349
- };
1350
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1351
- return { file, content: _content, ...rest };
1352
- };
1353
- }
1354
- //--
1355
- function anonymousImportHandler(compilerOptions) {
1356
- return ({ file, content, ...rest }) => {
1357
- const sourceFile = createBundledSourceFile(file, content);
1358
- const transformer = (context) => {
1359
- const { factory } = context;
1360
- function visitor(node) {
1361
- if (ts6.isImportDeclaration(node)) {
1362
- const fileName = node.moduleSpecifier.getText(sourceFile);
1363
- const _name = path.basename(fileName).split(".")[0].trim();
1364
- // check only import default expression
1365
- if (node.importClause?.name &&
1366
- ts6.isIdentifier(node.importClause.name)) {
1367
- const base = node.importClause.name.text.trim();
1368
- const mapping = anonymousExportNameMap.find((v) => v.file === _name);
1369
- if (mapping) {
1370
- anonymousImportNameMap.push({
1371
- base,
1372
- file,
1373
- newName: mapping.newName,
1374
- isEd: true,
1375
- });
1376
- const newImportClause = factory.updateImportClause(node.importClause, node.importClause.phaseModifier, factory.createIdentifier(mapping.newName), node.importClause.namedBindings);
1377
- return factory.updateImportDeclaration(node, node.modifiers, newImportClause, node.moduleSpecifier, node.attributes);
1378
- }
1379
- }
1380
- }
1381
- return ts6.visitEachChild(node, visitor, context);
1382
- }
1383
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1384
- };
1385
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1386
- return { file, content: _content, ...rest };
1387
- };
1388
- }
1389
- //--
1390
- function resetAnonymousState() {
1391
- anonymousExportNameMap.length = 0;
1392
- anonymousImportNameMap.length = 0;
1393
- anonymousName = createAnonymousNameGenerator();
1394
- }
1395
- const anonymousHandler = async (deps, compilerOptions) => {
1396
- resetAnonymousState();
1397
- const anonymous = utils.promises.resolve([
1398
- [anonymousExportHandler, compilerOptions],
1399
- [anonymousImportHandler, compilerOptions],
1400
- [anonymousCallExpressionHandler, compilerOptions],
1401
- ]);
1402
- const anons = await anonymous.concurrent();
1403
- for (const anon of anons) {
1404
- deps = deps.map(anon);
1405
- }
1406
- return deps;
1407
- };
1408
- //src/bundler/lib/exportDefault.ts
1409
- const exportDefaultExportNameMap = [];
1410
- const exportDefaultImportNameMap = [];
1411
- const exportDefaultPrefixKey = "ExportDefault";
1412
- const createExportDefaultNameGenerator = () => uniqueName.setPrefix({
1413
- key: exportDefaultPrefixKey,
1414
- value: "susee__exportDefault__",
1415
- });
1416
- let exportDefaultName = createExportDefaultNameGenerator();
1417
- const toNameLookupKey = (file, base) => `${file}\u0000${base}`;
1418
- const createNameLookup = (sets) => {
1419
- const lookup = new Map();
1420
- for (const set of sets) {
1421
- lookup.set(toNameLookupKey(set.file, set.base), set.newName);
1422
- }
1423
- return lookup;
1424
- };
1425
- const getMappedName = (lookup, file, base) => {
1426
- return lookup.get(toNameLookupKey(file, base));
1427
- };
1428
- const hasExportDefaultModifiers = (node) => {
1429
- let exp = false;
1430
- let def = false;
1431
- node.modifiers?.forEach((mod) => {
1432
- if (mod.kind === ts6.SyntaxKind.ExportKeyword) {
1433
- exp = true;
1434
- }
1435
- if (mod.kind === ts6.SyntaxKind.DefaultKeyword) {
1436
- def = true;
1437
- }
1438
- });
1439
- return exp && def;
1440
- };
1441
- const collectExportDefaultMappings = (deps) => {
1442
- for (const dep of deps) {
1443
- if (dep.fileExt === ".json" || dep.is_entry) {
1444
- continue;
1445
- }
1446
- const fileKey = getFileKey(dep.file);
1447
- const sourceFile = createBundledSourceFile(dep.file, dep.content);
1448
- for (const statement of sourceFile.statements) {
1449
- if ((ts6.isFunctionDeclaration(statement) ||
1450
- ts6.isClassDeclaration(statement)) &&
1451
- statement.name &&
1452
- ts6.isIdentifier(statement.name) &&
1453
- hasExportDefaultModifiers(statement)) {
1454
- const baseName = statement.name.text;
1455
- const newName = exportDefaultName.getName(exportDefaultPrefixKey, baseName);
1456
- exportDefaultExportNameMap.push({
1457
- base: baseName,
1458
- file: fileKey,
1459
- newName,
1460
- isEd: true,
1461
- });
1462
- break;
1463
- }
1464
- if (ts6.isExportAssignment(statement) &&
1465
- !statement.isExportEquals &&
1466
- ts6.isIdentifier(statement.expression)) {
1467
- const baseName = statement.expression.text;
1468
- const newName = exportDefaultName.getName(exportDefaultPrefixKey, baseName);
1469
- exportDefaultExportNameMap.push({
1470
- base: baseName,
1471
- file: fileKey,
1472
- newName,
1473
- isEd: true,
1474
- });
1475
- break;
1476
- }
1477
- }
1478
- }
1479
- };
1480
- // -----------------------
1481
- function exportDefaultImportAndUsageHandler(compilerOptions) {
1482
- return ({ file, content, fileExt, ...rest }) => {
1483
- if (fileExt === ".json")
1484
- return { file, content, fileExt, ...rest };
1485
- const sourceFile = createBundledSourceFile(file, content);
1486
- const transformer = (context) => {
1487
- const { factory } = context;
1488
- const exportLookup = createNameLookup(exportDefaultExportNameMap);
1489
- const importLookup = new Map();
1490
- const resolveMappedName = (base) => importLookup.get(base);
1491
- const isDeclarationName = (node) => {
1492
- const parent = node.parent;
1493
- if ((ts6.isVariableDeclaration(parent) ||
1494
- ts6.isFunctionDeclaration(parent) ||
1495
- ts6.isClassDeclaration(parent) ||
1496
- ts6.isParameter(parent) ||
1497
- ts6.isTypeAliasDeclaration(parent) ||
1498
- ts6.isInterfaceDeclaration(parent) ||
1499
- ts6.isEnumDeclaration(parent) ||
1500
- ts6.isImportClause(parent) ||
1501
- ts6.isNamespaceImport(parent) ||
1502
- ts6.isImportSpecifier(parent) ||
1503
- ts6.isExportSpecifier(parent) ||
1504
- ts6.isTypeParameterDeclaration(parent)) &&
1505
- parent.name === node) {
1506
- return true;
1507
- }
1508
- if ((ts6.isPropertyDeclaration(parent) ||
1509
- ts6.isMethodDeclaration(parent)) &&
1510
- parent.name === node) {
1511
- return true;
1512
- }
1513
- return false;
1514
- };
1515
- function visitor(node) {
1516
- if (ts6.isImportDeclaration(node)) {
1517
- const moduleKey = getModuleKeyFromSpecifier(node.moduleSpecifier, sourceFile, file);
1518
- if (node.importClause?.name &&
1519
- ts6.isIdentifier(node.importClause.name)) {
1520
- const base = node.importClause.name.text.trim();
1521
- const mappedName = getMappedName(exportLookup, moduleKey, base);
1522
- if (mappedName) {
1523
- importLookup.set(base, mappedName);
1524
- exportDefaultImportNameMap.push({
1525
- base,
1526
- file,
1527
- newName: mappedName,
1528
- isEd: true,
1529
- });
1530
- const newImportClause = factory.updateImportClause(node.importClause, node.importClause.phaseModifier, factory.createIdentifier(mappedName), node.importClause.namedBindings);
1531
- return factory.updateImportDeclaration(node, node.modifiers, newImportClause, node.moduleSpecifier, node.attributes);
1532
- }
1533
- }
1534
- }
1535
- if (ts6.isCallExpression(node)) {
1536
- if (ts6.isIdentifier(node.expression)) {
1537
- const newName = resolveMappedName(node.expression.text);
1538
- if (newName) {
1539
- return factory.updateCallExpression(node, factory.createIdentifier(newName), node.typeArguments, node.arguments);
1540
- }
1541
- }
1542
- }
1543
- else if (ts6.isPropertyAccessExpression(node)) {
1544
- if (ts6.isIdentifier(node.expression)) {
1545
- const newName = resolveMappedName(node.expression.text);
1546
- if (newName) {
1547
- return factory.updatePropertyAccessExpression(node, factory.createIdentifier(newName), node.name);
1548
- }
1549
- }
1550
- }
1551
- else if (ts6.isNewExpression(node)) {
1552
- if (ts6.isIdentifier(node.expression)) {
1553
- const newName = resolveMappedName(node.expression.text);
1554
- if (newName) {
1555
- return factory.updateNewExpression(node, factory.createIdentifier(newName), node.typeArguments, node.arguments);
1556
- }
1557
- }
1558
- // for export specifier it is focus on entry file
1559
- }
1560
- else if (ts6.isExportSpecifier(node)) {
1561
- if (ts6.isIdentifier(node.name)) {
1562
- const newName = resolveMappedName(node.name.text);
1563
- if (newName) {
1564
- return factory.updateExportSpecifier(node, node.isTypeOnly, node.propertyName, factory.createIdentifier(newName));
1565
- }
1566
- }
1567
- }
1568
- else if (ts6.isIdentifier(node) && !isDeclarationName(node)) {
1569
- if (ts6.isPropertyAccessExpression(node.parent) &&
1570
- node.parent.name === node) {
1571
- return node;
1572
- }
1573
- if (ts6.isPropertyAssignment(node.parent) &&
1574
- node.parent.name === node) {
1575
- return node;
1576
- }
1577
- const newName = resolveMappedName(node.text);
1578
- if (newName) {
1579
- if (ts6.isShorthandPropertyAssignment(node.parent) &&
1580
- node.parent.name === node) {
1581
- return factory.createPropertyAssignment(factory.createIdentifier(node.text), factory.createIdentifier(newName));
1582
- }
1583
- return factory.createIdentifier(newName);
1584
- }
1585
- }
1586
- // return : visitor
1587
- return ts6.visitEachChild(node, visitor, context);
1588
- }
1589
- // return : transform
1590
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1591
- };
1592
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1593
- // return : handler
1594
- return { file, content: _content, fileExt, ...rest };
1595
- };
1596
- }
1597
- //--
1598
- function exportDefaultLocalHandler(compilerOptions) {
1599
- return ({ file, content, fileExt, is_entry, ...rest }) => {
1600
- if (fileExt === ".json")
1601
- return { file, content, fileExt, is_entry, ...rest };
1602
- const fileName = getFileKey(file);
1603
- // const exportLookup = createNameLookup(exportDefaultExportNameMap);
1604
- // const mappedName = getMappedName(exportLookup, fileName, fileName);
1605
- const localMapping = exportDefaultExportNameMap.find((n) => n.file === fileName);
1606
- if (is_entry || !localMapping)
1607
- return { file, content, fileExt, is_entry, ...rest };
1608
- const sourceFile = createBundledSourceFile(file, content);
1609
- const transformer = (context) => {
1610
- const { factory } = context;
1611
- const { base: baseName, newName } = localMapping;
1612
- const isDeclarationName = (node) => {
1613
- const parent = node.parent;
1614
- if ((ts6.isVariableDeclaration(parent) ||
1615
- ts6.isFunctionDeclaration(parent) ||
1616
- ts6.isClassDeclaration(parent) ||
1617
- ts6.isParameter(parent) ||
1618
- ts6.isTypeAliasDeclaration(parent) ||
1619
- ts6.isInterfaceDeclaration(parent) ||
1620
- ts6.isEnumDeclaration(parent) ||
1621
- ts6.isImportClause(parent) ||
1622
- ts6.isNamespaceImport(parent) ||
1623
- ts6.isImportSpecifier(parent) ||
1624
- ts6.isExportSpecifier(parent) ||
1625
- ts6.isTypeParameterDeclaration(parent)) &&
1626
- parent.name === node) {
1627
- return true;
1628
- }
1629
- if ((ts6.isPropertyDeclaration(parent) ||
1630
- ts6.isMethodDeclaration(parent)) &&
1631
- parent.name === node) {
1632
- return true;
1633
- }
1634
- return false;
1635
- };
1636
- function visitor(node) {
1637
- if (ts6.isExportAssignment(node) &&
1638
- !node.isExportEquals &&
1639
- ts6.isIdentifier(node.expression) &&
1640
- node.expression.text === baseName) {
1641
- return factory.updateExportAssignment(node, node.modifiers, factory.createIdentifier(newName));
1642
- }
1643
- if (ts6.isCallExpression(node)) {
1644
- if (ts6.isIdentifier(node.expression) &&
1645
- node.expression.text === baseName) {
1646
- return factory.updateCallExpression(node, factory.createIdentifier(newName), node.typeArguments, node.arguments);
1647
- }
1648
- }
1649
- else if (ts6.isPropertyAccessExpression(node)) {
1650
- if (ts6.isIdentifier(node.expression) &&
1651
- node.expression.text === baseName) {
1652
- return factory.updatePropertyAccessExpression(node, factory.createIdentifier(newName), node.name);
1653
- }
1654
- }
1655
- else if (ts6.isNewExpression(node)) {
1656
- if (ts6.isIdentifier(node.expression) &&
1657
- node.expression.text === baseName) {
1658
- return factory.updateNewExpression(node, factory.createIdentifier(newName), node.typeArguments, node.arguments);
1659
- }
1660
- }
1661
- else if (ts6.isIdentifier(node) &&
1662
- node.text === baseName &&
1663
- !isDeclarationName(node)) {
1664
- if (ts6.isPropertyAccessExpression(node.parent) &&
1665
- node.parent.name === node) {
1666
- return node;
1667
- }
1668
- if (ts6.isPropertyAssignment(node.parent) &&
1669
- node.parent.name === node) {
1670
- return node;
1671
- }
1672
- if (ts6.isShorthandPropertyAssignment(node.parent) &&
1673
- node.parent.name === node) {
1674
- return factory.createPropertyAssignment(factory.createIdentifier(node.text), factory.createIdentifier(newName));
1675
- }
1676
- return factory.createIdentifier(newName);
1677
- }
1678
- if (ts6.isFunctionDeclaration(node) || ts6.isClassDeclaration(node)) {
1679
- if (node.name &&
1680
- ts6.isIdentifier(node.name) &&
1681
- node.name.text === baseName) {
1682
- if (ts6.isFunctionDeclaration(node)) {
1683
- const visitedNode = ts6.visitEachChild(node, visitor, context);
1684
- return factory.updateFunctionDeclaration(visitedNode, visitedNode.modifiers, visitedNode.asteriskToken, factory.createIdentifier(newName), visitedNode.typeParameters, visitedNode.parameters, visitedNode.type, visitedNode.body);
1685
- }
1686
- const visitedNode = ts6.visitEachChild(node, visitor, context);
1687
- return factory.updateClassDeclaration(visitedNode, visitedNode.modifiers, factory.createIdentifier(newName), visitedNode.typeParameters, visitedNode.heritageClauses, visitedNode.members);
1688
- }
1689
- }
1690
- else if (ts6.isVariableStatement(node)) {
1691
- const declarations = node.declarationList.declarations;
1692
- let changed = false;
1693
- const updatedDeclarations = declarations.map((decl) => {
1694
- if (ts6.isIdentifier(decl.name) && decl.name.text === baseName) {
1695
- changed = true;
1696
- return factory.updateVariableDeclaration(decl, factory.createIdentifier(newName), decl.exclamationToken, decl.type, decl.initializer);
1697
- }
1698
- return decl;
1699
- });
1700
- if (changed) {
1701
- return factory.updateVariableStatement(node, node.modifiers, factory.updateVariableDeclarationList(node.declarationList, updatedDeclarations));
1702
- }
1703
- }
1704
- return ts6.visitEachChild(node, visitor, context);
1705
- }
1706
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1707
- };
1708
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1709
- return { file, content: _content, fileExt, is_entry, ...rest };
1710
- };
1711
- }
1712
- //--
1713
- function resetExportDefaultState() {
1714
- exportDefaultExportNameMap.length = 0;
1715
- exportDefaultImportNameMap.length = 0;
1716
- exportDefaultName = createExportDefaultNameGenerator();
1717
- }
1718
- const exportDefaultHandler = async (deps, compilerOptions) => {
1719
- resetExportDefaultState();
1720
- collectExportDefaultMappings(deps);
1721
- deps = deps.map(exportDefaultLocalHandler(compilerOptions));
1722
- deps = deps.map(exportDefaultImportAndUsageHandler(compilerOptions));
1723
- return deps;
1724
- };
1725
- //src/bundler/lib/remove.ts
1726
- const properties = [];
1727
- const propertiesSet = new Set();
1728
- const typeObj = {};
1729
- const typesNames = new Set();
1730
- function esmExportRemoveHandler(compilerOptions) {
1731
- return ({ file, content, ...rest }) => {
1732
- const sourceFile = createBundledSourceFile(file, content);
1733
- const transformer = (context) => {
1734
- const { factory } = context;
1735
- const visitor = (node) => {
1736
- // --- Case 1: Strip "export" modifiers ---
1737
- const inside_nameSpace = utils.checks.isInsideNamespace(node);
1738
- if (!inside_nameSpace) {
1739
- if (ts6.isFunctionDeclaration(node) ||
1740
- ts6.isClassDeclaration(node) ||
1741
- ts6.isInterfaceDeclaration(node) ||
1742
- ts6.isTypeAliasDeclaration(node) ||
1743
- ts6.isEnumDeclaration(node) ||
1744
- ts6.isVariableStatement(node)) {
1745
- const modifiers = node.modifiers?.filter((m) => m.kind !== ts6.SyntaxKind.ExportKeyword &&
1746
- m.kind !== ts6.SyntaxKind.DefaultKeyword);
1747
- if (modifiers?.length !== node.modifiers?.length) {
1748
- // If the node has an export modifier, remove it.
1749
- // If the node is a function, class, interface, type alias, enum or variable declaration,
1750
- // update the declaration by removing the export modifier.
1751
- if (ts6.isFunctionDeclaration(node)) {
1752
- return factory.updateFunctionDeclaration(node, modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, node.body);
1753
- } // function
1754
- if (ts6.isClassDeclaration(node)) {
1755
- return factory.updateClassDeclaration(node, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members);
1756
- } // class
1757
- if (ts6.isInterfaceDeclaration(node)) {
1758
- return factory.updateInterfaceDeclaration(node, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members);
1759
- } // interface
1760
- if (ts6.isTypeAliasDeclaration(node)) {
1761
- return factory.updateTypeAliasDeclaration(node, modifiers, node.name, node.typeParameters, node.type);
1762
- } // types
1763
- if (ts6.isEnumDeclaration(node)) {
1764
- return factory.updateEnumDeclaration(node, modifiers, node.name, node.members);
1765
- } //enum
1766
- if (ts6.isVariableStatement(node)) {
1767
- return factory.updateVariableStatement(node, modifiers, node.declarationList);
1768
- } // vars
1769
- } //--
1770
- } // --- Case 1
1771
- }
1772
- // --- Case 2: Remove "export { foo }" entirely ---
1773
- if (ts6.isExportDeclaration(node)) {
1774
- // If the node is an export declaration, remove it.
1775
- return factory.createEmptyStatement();
1776
- }
1777
- // --- Case 3: Handle "export default ..." ---
1778
- if (ts6.isExportAssignment(node)) {
1779
- const expr = node.expression;
1780
- // export default Foo; -> remove line
1781
- if (ts6.isIdentifier(expr)) {
1782
- return factory.createEmptyStatement();
1783
- }
1784
- }
1785
- /* ----------------------Returns for visitor function------------------------------- */
1786
- return ts6.visitEachChild(node, visitor, context);
1787
- };
1788
- /* --------------------Returns for transformer function--------------------------------- */
1789
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1790
- };
1791
- /* --------------------Returns for main handler function--------------------------------- */
1792
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1793
- return { file, content: _content, ...rest };
1794
- };
1795
- }
1796
- function importAllRemoveHandler(removedStatements, compilerOptions) {
1797
- return ({ file, content, ...rest }) => {
1798
- const sourceFile = createBundledSourceFile(file, content);
1799
- const isCommonJsFile = utils.checks.moduleType(content, file).isCommonJs;
1800
- const transformer = (context) => {
1801
- // Pre-scan: collect names of type-only import-equals (these are namespace-type aliases)
1802
- // import type NameSpace = require("foo")
1803
- const typeOnlyImportEquals = new Set();
1804
- for (const stmt of sourceFile.statements) {
1805
- if (ts6.isImportEqualsDeclaration(stmt) && stmt.isTypeOnly) {
1806
- const moduleReference = stmt.moduleReference;
1807
- if (ts6.isExternalModuleReference(moduleReference) &&
1808
- ts6.isStringLiteral(moduleReference.expression)) {
1809
- typeOnlyImportEquals.add(stmt.name.text);
1810
- }
1811
- }
1812
- }
1813
- const { factory } = context;
1814
- const visitor = (node) => {
1815
- if (ts6.isPropertyAccessExpression(node) &&
1816
- ts6.isIdentifier(node.expression)) {
1817
- properties.push(node.expression.text);
1818
- propertiesSet.add(node.expression.text);
1819
- }
1820
- const obj = {
1821
- isNamespace: false,
1822
- isTypeOnly: false,
1823
- isTypeNamespace: false,
1824
- source: "",
1825
- importedString: undefined,
1826
- importedObject: undefined,
1827
- };
1828
- // --- Case: TypeReference with QualifiedName (collect type usage)
1829
- if (ts6.isTypeReferenceNode(node) &&
1830
- ts6.isQualifiedName(node.typeName) &&
1831
- ts6.isIdentifier(node.typeName.left) &&
1832
- ts6.isIdentifier(node.typeName.right)) {
1833
- const left = node.typeName.left.text;
1834
- const right = node.typeName.right.text;
1835
- typesNames.add(left);
1836
- if (left in typeObj) {
1837
- typeObj[left]?.push(right);
1838
- }
1839
- else {
1840
- typeObj[left] = [right];
1841
- }
1842
- // If this qualified name refers to a type-only import-equals alias, DO NOT rewrite.
1843
- // Rewriting (Foo.Bar -> Bar) was intended to support converting to named imports,
1844
- // but for type-only namespace imports we will emit `import type * as Foo from "..."`.
1845
- if (isCommonJsFile) {
1846
- if (left !== "ts" && !typeOnlyImportEquals.has(left)) {
1847
- return factory.updateTypeReferenceNode(node, factory.createIdentifier(right), undefined);
1848
- }
1849
- }
1850
- }
1851
- // ------------------------
1852
- if (ts6.isImportDeclaration(node)) {
1853
- // --- Case 1: Import declarations
1854
- const text = node.getText(sourceFile);
1855
- removedStatements.push(text);
1856
- return factory.createEmptyStatement();
1857
- }
1858
- //--- Case 2: Import equals declarations
1859
- if (ts6.isImportEqualsDeclaration(node)) {
1860
- const name = node.name.text;
1861
- const moduleReference = node.moduleReference;
1862
- if (node.isTypeOnly) {
1863
- obj.isTypeOnly = true;
1864
- }
1865
- obj.importedString = name;
1866
- if (!obj.isTypeOnly) {
1867
- if (propertiesSet.has(name)) {
1868
- obj.isNamespace = true;
1869
- }
1870
- }
1871
- if (ts6.isExternalModuleReference(moduleReference) &&
1872
- ts6.isStringLiteral(moduleReference.expression)) {
1873
- obj.source = moduleReference.expression.text;
1874
- }
1875
- let t;
1876
- if (obj.importedString && !obj.importedObject) {
1877
- if (obj.isTypeOnly) {
1878
- // If this import-equals was a type-only namespace alias, emit a namespace type import
1879
- if (typeOnlyImportEquals.has(obj.importedString)) {
1880
- t = `import type * as ${obj.importedString} from "${obj.source}";`;
1881
- }
1882
- else {
1883
- // otherwise try to emit a named/default type import (existing behavior)
1884
- if (typesNames.has(obj.importedString)) {
1885
- t = `import type { ${typeObj[obj.importedString]?.join(",")} } from "${obj.source}";`;
1886
- }
1887
- else {
1888
- t = `import type ${obj.importedString} from "${obj.source}";`;
1889
- }
1890
- }
1891
- }
1892
- else {
1893
- if (obj.isNamespace &&
1894
- obj.source &&
1895
- obj.source !== "typescript") {
1896
- t = `import * as ${obj.importedString} from "${obj.source}";`;
1897
- }
1898
- else {
1899
- t = `import ${obj.importedString} from "${obj.source}";`;
1900
- }
1901
- }
1902
- }
1903
- if (!obj.importedString && obj.importedObject) {
1904
- t = `import { ${obj.importedObject.join(", ")} } from "${obj.source}";`;
1905
- }
1906
- // removed
1907
- if (t) {
1908
- removedStatements.push(t);
1909
- return factory.createEmptyStatement();
1910
- }
1911
- }
1912
- // --- Case 3: Require imports
1913
- if (ts6.isVariableStatement(node)) {
1914
- const decls = node.declarationList.declarations;
1915
- if (decls.length === 1) {
1916
- const decl = decls[0];
1917
- if (decl.initializer &&
1918
- ts6.isCallExpression(decl.initializer) &&
1919
- ts6.isIdentifier(decl.initializer.expression) &&
1920
- decl.initializer.expression.escapedText === "require") {
1921
- // imported from
1922
- const arg = decl.initializer.arguments[0];
1923
- if (ts6.isStringLiteral(arg)) {
1924
- obj.source = arg.text;
1925
- }
1926
- if (ts6.isIdentifier(decl.name)) {
1927
- const _n = decl.name.text;
1928
- obj.importedString = _n;
1929
- if (propertiesSet.has(_n)) {
1930
- obj.isNamespace = true;
1931
- }
1932
- }
1933
- else if (ts6.isObjectBindingPattern(decl.name)) {
1934
- const _names = [];
1935
- for (const ele of decl.name.elements) {
1936
- if (ts6.isIdentifier(ele.name)) {
1937
- _names.push(ele.name.text);
1938
- }
1939
- }
1940
- if (_names.length > 0) {
1941
- obj.importedObject = _names;
1942
- }
1943
- }
1944
- let tt;
1945
- if (obj.importedString && !obj.importedObject) {
1946
- if (obj.isNamespace) {
1947
- tt = `import * as ${obj.importedString} from "${obj.source}";`;
1948
- }
1949
- else {
1950
- tt = `import ${obj.importedString} from "${obj.source}";`;
1951
- }
1952
- }
1953
- if (!obj.importedString && obj.importedObject) {
1954
- tt = `import { ${obj.importedObject.join(", ")} } from "${obj.source}";`;
1955
- }
1956
- if (tt) {
1957
- removedStatements.push(tt);
1958
- return factory.createEmptyStatement();
1959
- }
1960
- }
1961
- }
1962
- }
1963
- /* ----------------------Returns for visitor function------------------------------- */
1964
- return ts6.visitEachChild(node, visitor, context);
1965
- };
1966
- /* --------------------Returns for transformer function--------------------------------- */
1967
- return (rootNode) => ts6.visitNode(rootNode, visitor);
1968
- };
1969
- /* --------------------Returns for main handler function--------------------------------- */
1970
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
1971
- return { file, content: _content, ...rest };
1972
- };
1973
- }
1974
- const removeHandlers = async (removedStatements, compilerOptions) => {
1975
- const resolved = utils.promises.resolve([
1976
- [importAllRemoveHandler, removedStatements, compilerOptions],
1977
- [esmExportRemoveHandler, compilerOptions],
1978
- ]);
1979
- return await resolved.series();
1980
- };
1981
- //src/bundler/lib/resolveJSON.ts
1982
- const jsonPrefix = "__jsonModule__";
1983
- const jsonModuleExportNameMap = [];
1984
- const jsonModuleImportNameMap = [];
1985
- const toIdentifier = (input) => {
1986
- const cleaned = input.replace(/[^A-Za-z0-9_$]/g, "_");
1987
- const startsValid = /^[A-Za-z_$]/.test(cleaned);
1988
- return `${jsonPrefix}${startsValid ? cleaned : `_${cleaned}`}`;
1989
- };
1990
- const toJsonModuleCode = (varName, content, file) => {
1991
- let parsed;
1992
- try {
1993
- parsed = JSON.parse(content);
1994
- }
1995
- catch {
1996
- throw new Error(`Invalid JSON syntax in dependency file: ${file}`);
1997
- }
1998
- const jsonObject = JSON.stringify(parsed);
1999
- return `const ${varName} = ${jsonObject};\nexport default ${varName}`;
2000
- };
2001
- const resolveJSONHandler = async (deps) => {
2002
- const scopedNameCount = new Map();
2003
- const nextDeps = deps.map((dep) => {
2004
- if (dep.moduleType !== "json" || dep.fileExt !== ".json") {
2005
- return dep;
2006
- }
2007
- const fileName = path.basename(dep.file).split(".")[0];
2008
- const fileKey = getFileKey(dep.file);
2009
- const keyName = toIdentifier(fileKey);
2010
- const count = scopedNameCount.get(keyName) ?? 0;
2011
- const jsonVarName = count === 0 ? keyName : `${keyName}_${count + 1}`;
2012
- scopedNameCount.set(keyName, count + 1);
2013
- jsonModuleExportNameMap.push({
2014
- base: jsonVarName,
2015
- file: fileName,
2016
- newName: jsonVarName,
2017
- isEd: true,
2018
- });
2019
- return {
2020
- ...dep,
2021
- content: toJsonModuleCode(jsonVarName, dep.content, dep.file),
2022
- moduleType: "esm",
2023
- };
2024
- });
2025
- return nextDeps;
2026
- };
2027
- //--
2028
- function jsonModuleImportHandler(compilerOptions) {
2029
- return ({ file, content, fileExt, ...rest }) => {
2030
- const sourceFile = createBundledSourceFile(file, content);
2031
- const transformer = (context) => {
2032
- const { factory } = context;
2033
- function visitor(node) {
2034
- if (ts6.isImportDeclaration(node)) {
2035
- const fileName = node.moduleSpecifier.getText(sourceFile);
2036
- const _name = path.basename(fileName).split(".")[0].trim();
2037
- // check only import default expression
2038
- if (node.importClause?.name &&
2039
- ts6.isIdentifier(node.importClause.name)) {
2040
- const base = node.importClause.name.text.trim();
2041
- const mapping = jsonModuleExportNameMap.find((v) => v.file === _name);
2042
- if (mapping) {
2043
- jsonModuleImportNameMap.push({
2044
- base,
2045
- file,
2046
- newName: mapping.newName,
2047
- isEd: true,
2048
- });
2049
- const newImportClause = factory.updateImportClause(node.importClause, node.importClause.phaseModifier, factory.createIdentifier(mapping.newName), node.importClause.namedBindings);
2050
- return factory.updateImportDeclaration(node, node.modifiers, newImportClause, node.moduleSpecifier, node.attributes);
2051
- }
2052
- }
2053
- }
2054
- return ts6.visitEachChild(node, visitor, context);
2055
- }
2056
- return (rootNode) => ts6.visitNode(rootNode, visitor);
2057
- };
2058
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
2059
- return { file, content: _content, fileExt, ...rest };
2060
- };
2061
- }
2062
- //--
2063
- function jsonModuleCallExpressionHandler(compilerOptions) {
2064
- return ({ file, content, fileExt, ...rest }) => {
2065
- const sourceFile = createBundledSourceFile(file, content);
2066
- const transformer = (context) => {
2067
- const { factory } = context;
2068
- function visitor(node) {
2069
- if (ts6.isCallExpression(node)) {
2070
- if (ts6.isIdentifier(node.expression)) {
2071
- const base = node.expression.text;
2072
- const mapping = jsonModuleImportNameMap.find((m) => m.base === base && m.file === file);
2073
- if (mapping) {
2074
- return factory.updateCallExpression(node, factory.createIdentifier(mapping.newName), node.typeArguments, node.arguments);
2075
- }
2076
- }
2077
- }
2078
- else if (ts6.isPropertyAccessExpression(node)) {
2079
- if (ts6.isIdentifier(node.expression)) {
2080
- const base = node.expression.text;
2081
- const mapping = jsonModuleImportNameMap.find((m) => m.base === base && m.file === file);
2082
- if (mapping) {
2083
- return factory.updatePropertyAccessExpression(node, factory.createIdentifier(mapping.newName), node.name);
2084
- }
2085
- }
2086
- }
2087
- else if (ts6.isNewExpression(node)) {
2088
- if (ts6.isIdentifier(node.expression)) {
2089
- const base = node.expression.text;
2090
- const mapping = jsonModuleImportNameMap.find((m) => m.base === base && m.file === file);
2091
- if (mapping) {
2092
- return factory.updateNewExpression(node, factory.createIdentifier(mapping.newName), node.typeArguments, node.arguments);
2093
- }
2094
- }
2095
- // for export specifier it is focus on entry file
2096
- }
2097
- else if (ts6.isExportSpecifier(node)) {
2098
- if (ts6.isIdentifier(node.name)) {
2099
- const base = node.name.text;
2100
- const mapping = jsonModuleImportNameMap.find((m) => m.base === base && m.file === file);
2101
- if (mapping) {
2102
- return factory.updateExportSpecifier(node, node.isTypeOnly, node.propertyName, factory.createIdentifier(mapping.newName));
2103
- }
2104
- }
2105
- }
2106
- return ts6.visitEachChild(node, visitor, context);
2107
- }
2108
- return (rootNode) => ts6.visitNode(rootNode, visitor);
2109
- };
2110
- const _content = transformBundledSource(sourceFile, compilerOptions, transformer);
2111
- return { file, content: _content, fileExt, ...rest };
2112
- };
2113
- }
2114
- //--
2115
- async function jsonModuleHandlers(deps, compilerOptions) {
2116
- deps = await resolveJSONHandler(deps);
2117
- deps = deps.map((dep) => jsonModuleImportHandler(compilerOptions)(dep));
2118
- deps = deps.map((dep) => jsonModuleCallExpressionHandler(compilerOptions)(dep));
2119
- return deps;
2120
- }
2121
- function collectBindingNames(name, out) {
2122
- if (ts6.isIdentifier(name))
2123
- out.push(name.text);
2124
- else if (ts6.isObjectBindingPattern(name) ||
2125
- ts6.isArrayBindingPattern(name)) {
2126
- name.elements.forEach((el) => {
2127
- if (ts6.isBindingElement(el) && el.name)
2128
- collectBindingNames(el.name, out);
2129
- });
2130
- }
2131
- }
2132
- /**
2133
- * Clear unused top-level declarations from a TypeScript source string.
2134
- * - Removes only unused named import specifiers.
2135
- * - Removes entire import declarations when an unused default or namespace import is present.
2136
- * - Removes function and class declarations when their name is unused.
2137
- * - Removes entire variable statements when none of the declared identifiers are used.
2138
- *
2139
- * Limitations: this works on a single-file basis and does not analyze cross-file usages.
2140
- */
2141
- function susee__anonymous__unusedCode_2(content, file, compilerOptions, options = { treatExportsAsUsed: true }) {
2142
- const sourceFile = createBundledSourceFile(file, content);
2143
- const defined = new Map();
2144
- const used = new Set();
2145
- const markDefined = (name, exported = false) => {
2146
- const prev = defined.get(name);
2147
- defined.set(name, { exported: !!prev?.exported || exported });
2148
- };
2149
- // First pass: collect defined names (imports, vars, funcs, classes) and used identifiers
2150
- const collect = (node) => {
2151
- // Definitions
2152
- if (ts6.isImportDeclaration(node) && node.importClause) {
2153
- const ic = node.importClause;
2154
- if (ic.name && ts6.isIdentifier(ic.name))
2155
- markDefined(ic.name.text, false);
2156
- if (ic.namedBindings) {
2157
- if (ts6.isNamedImports(ic.namedBindings)) {
2158
- ic.namedBindings.elements.forEach((ele) => {
2159
- if (ts6.isImportSpecifier(ele) && ts6.isIdentifier(ele.name))
2160
- markDefined(ele.name.text, false);
2161
- });
2162
- }
2163
- else if (ts6.isNamespaceImport(ic.namedBindings) &&
2164
- ts6.isIdentifier(ic.namedBindings.name)) {
2165
- markDefined(ic.namedBindings.name.text, false);
2166
- }
2167
- }
2168
- }
2169
- else if (ts6.isImportEqualsDeclaration(node) &&
2170
- ts6.isIdentifier(node.name)) {
2171
- markDefined(node.name.text, false);
2172
- }
2173
- else if (ts6.isVariableStatement(node)) {
2174
- const exported = node.modifiers?.some((m) => m.kind === ts6.SyntaxKind.ExportKeyword) ??
2175
- false;
2176
- node.declarationList.declarations.forEach((d) => {
2177
- collectBindingNames(d.name, []);
2178
- const names = [];
2179
- collectBindingNames(d.name, names);
2180
- names.forEach((n) => markDefined(n, exported));
2181
- });
2182
- }
2183
- else if (ts6.isFunctionDeclaration(node) &&
2184
- node.name &&
2185
- ts6.isIdentifier(node.name)) {
2186
- const exported = node.modifiers?.some((m) => m.kind === ts6.SyntaxKind.ExportKeyword) ??
2187
- false;
2188
- markDefined(node.name.text, exported);
2189
- }
2190
- else if (ts6.isClassDeclaration(node) &&
2191
- node.name &&
2192
- ts6.isIdentifier(node.name)) {
2193
- const exported = node.modifiers?.some((m) => m.kind === ts6.SyntaxKind.ExportKeyword) ??
2194
- false;
2195
- markDefined(node.name.text, exported);
2196
- }
2197
- // Usage: any identifier that is not a declaration name is considered a use
2198
- if (ts6.isIdentifier(node)) {
2199
- const parent = node.parent;
2200
- const isDeclarationName = (ts6.isVariableDeclaration(parent) && parent.name === node) ||
2201
- (ts6.isFunctionDeclaration(parent) && parent.name === node) ||
2202
- (ts6.isClassDeclaration(parent) && parent.name === node) ||
2203
- (ts6.isImportClause(parent) && parent.name === node) ||
2204
- (ts6.isImportSpecifier(parent) && parent.name === node) ||
2205
- (ts6.isNamespaceImport(parent) && parent.name === node) ||
2206
- (ts6.isBindingElement(parent) && parent.name === node) ||
2207
- (ts6.isParameter(parent) && parent.name === node);
2208
- if (!isDeclarationName)
2209
- used.add(node.text);
2210
- }
2211
- ts6.forEachChild(node, collect);
2212
- };
2213
- collect(sourceFile);
2214
- // Determine unused names
2215
- const unused = new Set();
2216
- defined.forEach((meta, name) => {
2217
- if (used.has(name))
2218
- return;
2219
- if (options.treatExportsAsUsed && meta.exported)
2220
- return;
2221
- unused.add(name);
2222
- });
2223
- // Transformer: remove nodes that are unused according to rules
2224
- const transformer = (context) => {
2225
- const visitor = (node) => {
2226
- // ImportDeclaration:
2227
- // - remove whole statement when default/namespace import is unused
2228
- // - otherwise remove only unused named specifiers
2229
- if (ts6.isImportDeclaration(node) && node.importClause) {
2230
- const ic = node.importClause;
2231
- const defaultName = ic.name && ts6.isIdentifier(ic.name) ? ic.name.text : undefined;
2232
- let namespaceName;
2233
- const namedElements = [];
2234
- if (ic.namedBindings) {
2235
- if (ts6.isNamedImports(ic.namedBindings)) {
2236
- ic.namedBindings.elements.forEach((ele) => {
2237
- if (ts6.isImportSpecifier(ele) && ts6.isIdentifier(ele.name))
2238
- namedElements.push(ele);
2239
- });
2240
- }
2241
- else if (ts6.isNamespaceImport(ic.namedBindings) &&
2242
- ts6.isIdentifier(ic.namedBindings.name)) {
2243
- namespaceName = ic.namedBindings.name.text;
2244
- }
2245
- }
2246
- const defaultUsed = defaultName ? !unused.has(defaultName) : false;
2247
- const namespaceUsed = namespaceName
2248
- ? !unused.has(namespaceName)
2249
- : false;
2250
- const keptNamed = namedElements.filter((ele) => !unused.has(ele.name.text));
2251
- if ((defaultName && !defaultUsed) ||
2252
- (namespaceName && !namespaceUsed)) {
2253
- return ts6.factory.createNotEmittedStatement(node);
2254
- }
2255
- if (namedElements.length > 0 &&
2256
- keptNamed.length === 0 &&
2257
- !defaultName) {
2258
- return ts6.factory.createNotEmittedStatement(node);
2259
- }
2260
- if (keptNamed.length !== namedElements.length) {
2261
- const newImportClause = ts6.factory.createImportClause(false, defaultName ? ts6.factory.createIdentifier(defaultName) : undefined, ts6.factory.createNamedImports(keptNamed));
2262
- return ts6.factory.updateImportDeclaration(node, node.modifiers, newImportClause, node.moduleSpecifier,
2263
- // biome-ignore lint/suspicious/noExplicitAny : ts
2264
- node.assertClause);
2265
- }
2266
- return node;
2267
- }
2268
- // FunctionDeclaration / ClassDeclaration: remove if named and unused
2269
- if ((ts6.isFunctionDeclaration(node) || ts6.isClassDeclaration(node)) &&
2270
- node.name &&
2271
- ts6.isIdentifier(node.name)) {
2272
- if (unused.has(node.name.text))
2273
- return ts6.factory.createNotEmittedStatement(node);
2274
- return node;
2275
- }
2276
- // VariableStatement: remove whole statement only if none of declared names are used
2277
- if (ts6.isVariableStatement(node)) {
2278
- const names = [];
2279
- node.declarationList.declarations.forEach((d) => collectBindingNames(d.name, names));
2280
- const anyUsed = names.some((n) => !unused.has(n));
2281
- if (!anyUsed)
2282
- return ts6.factory.createNotEmittedStatement(node);
2283
- return node;
2284
- }
2285
- return ts6.visitEachChild(node, visitor, context);
2286
- };
2287
- return (root) => ts6.visitNode(root, visitor);
2288
- };
2289
- const output = transformBundledSource(sourceFile, compilerOptions, transformer);
2290
- return output;
2291
- }
2292
- //src/bundler/index.ts
2293
- const logBundlerPhase = (entry, phase, start) => {
2294
- logProfilePhase(`bundler:${path.basename(entry)}`, phase, start);
2295
- };
2296
- async function bundler(entry, plugins = [], warning = false) {
2297
- const bundlerStart = process.hrtime.bigint();
2298
- let removedStatements = [];
2299
- const compilerOptions = ts6.getDefaultCompilerOptions();
2300
- let phaseStart = process.hrtime.bigint();
2301
- const tree = await generateDependencies(entry, createBundledSourceFile);
2302
- logBundlerPhase(entry, "generateDependencies", phaseStart);
2303
- // check for warning from generated dependencies graph
2304
- if (warning && tree.warns.length > 0) {
2305
- console.warn(tree.warns.join("\n"));
2306
- process.exit(1);
2307
- }
2308
- let depsFiles = tree.depFiles;
2309
- // 1. Resolve JSON Modules
2310
- if (isJSON(tree)) {
2311
- phaseStart = process.hrtime.bigint();
2312
- depsFiles = await jsonModuleHandlers(depsFiles, compilerOptions);
2313
- logBundlerPhase(entry, "resolveJSON", phaseStart);
2314
- }
2315
- // 2. Parse Dependency Plugins
2316
- if (plugins.length > 0) {
2317
- for (const plugin of plugins) {
2318
- const _plugin = typeof plugin === "function" ? plugin() : plugin;
2319
- if (_plugin.type === "dependency") {
2320
- phaseStart = process.hrtime.bigint();
2321
- if (_plugin.async) {
2322
- depsFiles = await _plugin.func(depsFiles, compilerOptions);
2323
- }
2324
- else {
2325
- depsFiles = _plugin.func(depsFiles, compilerOptions);
2326
- }
2327
- logBundlerPhase(entry, `dependencyPlugin:${_plugin.name ?? "anonymous"}`, phaseStart);
2328
- }
2329
- }
2330
- }
2331
- // 3. Check for commonjs modules
2332
- const isCommonjs = depsFiles.find((file) => file.moduleType === "cjs");
2333
- if (isCommonjs) {
2334
- console.error(`Bundler found commonjs module/modules in dependencies tree.Please use "@suseejs/commonjs-plugin" to solve it.`);
2335
- process.exit(1);
2336
- }
2337
- // 4. Handling Export Default
2338
- phaseStart = process.hrtime.bigint();
2339
- depsFiles = await exportDefaultHandler(depsFiles, compilerOptions);
2340
- logBundlerPhase(entry, "exportDefault", phaseStart);
2341
- // 5. Handling Anonymous Imports/Exports
2342
- phaseStart = process.hrtime.bigint();
2343
- depsFiles = await anonymousHandler(depsFiles, compilerOptions);
2344
- logBundlerPhase(entry, "anonymous", phaseStart);
2345
- // 6. Duplicate top-level declarations are validated during dependency generation.
2346
- // Susee fails fast and asks callers to fix conflicting names in source files.
2347
- // 7. Handling Remove Imports/Exports
2348
- phaseStart = process.hrtime.bigint();
2349
- const removed = await removeHandlers(removedStatements, compilerOptions);
2350
- // 7.1 Remove Imports
2351
- depsFiles = depsFiles.map(removed[0]);
2352
- // 7.2 Remove Exports
2353
- // Remove Exports from dependency files only
2354
- // not remove exports from entry file
2355
- const deps_files = depsFiles.slice(0, -1).map(removed[1]);
2356
- const mainFile = depsFiles.slice(-1);
2357
- logBundlerPhase(entry, "removeImportsExports", phaseStart);
2358
- // 8. Handling Imported Statements
2359
- // filter removed statements , that not from local like `./` or `../`
2360
- phaseStart = process.hrtime.bigint();
2361
- const regexp = /^\s*import(?:[\s\S]*?\sfrom\s+)?["']((?!\.{1,2}\/)[^"']+)["']/;
2362
- removedStatements = removedStatements.filter((i) => regexp.test(i));
2363
- removedStatements = utils.gen.mergeImportsStatement(removedStatements);
2364
- const importStatements = removedStatements.join("\n").trim();
2365
- logBundlerPhase(entry, "mergeImports", phaseStart);
2366
- // 9. Merge all content from dependencies tree
2367
- // 9.1 Merge dependency files content.
2368
- phaseStart = process.hrtime.bigint();
2369
- const depFilesContent = deps_files
2370
- .map((i) => {
2371
- const file = `//${path.relative(process.cwd(), i.file)}`;
2372
- return `${file}\n${i.content}`;
2373
- })
2374
- .join("\n")
2375
- .trim();
2376
- // 9.2 Create entry content
2377
- const mainFileContent = mainFile
2378
- .map((i) => {
2379
- const file = `//${path.relative(process.cwd(), i.file)}`;
2380
- return `${file}\n${i.content}`;
2381
- })
2382
- .join("\n")
2383
- .trim();
2384
- // 9.3 Merge all into one
2385
- // text join order is important here
2386
- // make sure all imports are at the top of file
2387
- let content = `${importStatements}\n${depFilesContent}\n${mainFileContent}`;
2388
- // some additional steps
2389
- // remove ";" that are remain after removing imports
2390
- content = content.replace(/^s*;\s*$/gm, "").trim();
2391
- logBundlerPhase(entry, "mergeContent", phaseStart);
2392
- // clean unused code
2393
- phaseStart = process.hrtime.bigint();
2394
- content = susee__anonymous__unusedCode_2(content, tree.entry, compilerOptions);
2395
- logBundlerPhase(entry, "cleanUnusedCode", phaseStart);
2396
- // 10. Call pre-process plugins
2397
- if (plugins.length > 0) {
2398
- for (const plugin of plugins) {
2399
- const _plugin = typeof plugin === "function" ? plugin() : plugin;
2400
- if (_plugin.type === "pre-process") {
2401
- phaseStart = process.hrtime.bigint();
2402
- if (_plugin.async) {
2403
- content = await _plugin.func(content, tree.entry);
2404
- }
2405
- else {
2406
- content = _plugin.func(content, tree.entry);
2407
- }
2408
- logBundlerPhase(entry, `preProcessPlugin:${_plugin.name ?? "anonymous"}`, phaseStart);
2409
- }
2410
- }
2411
- }
2412
- logBundlerPhase(entry, "total", bundlerStart);
2413
- // Returns
2414
- return content;
2415
- }
2416
- async function bundle(entry) {
2417
- return await bundler(entry);
2418
- }
2419
- //package.json
2420
- const __jsonModule__package = { "name": "susee", "version": "1.6.2", "description": "TypeScript-first bundler for library packages", "type": "module", "main": "dist/index.cjs", "types": "dist/index.d.cts", "module": "dist/index.mjs", "exports": { ".": { "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "bin": { "susee": "bin/susee" }, "scripts": { "build": "tsx build.ts", "lint": "biome lint ./src ./__tests__ --write", "fmt": "biome format --write", "test": "tsx --test", "hooks:install": "bash scripts/install-hooks.sh", "commit": "bash scripts/commit.sh", "v:patch": "npm version patch --no-git-tag-version", "v:minor": "npm version minor --no-git-tag-version", "v:major": "npm version major --no-git-tag-version" }, "keywords": ["bundler", "susee", "suseejs"], "author": "Pho Thin Mg <phothinmg@disroot.org> (https://phothinmg.github.io/)", "license": "Apache-2.0", "files": ["package.json", "README.md", "LICENSE", "dist/**/*", "bin/**/*"], "publishConfig": { "provenance": true, "access": "public" }, "repository": { "url": "git+https://github.com/phothinmg/susee.git", "type": "git" }, "homepage": "https://susee.js.org/", "bugs": { "url": "https://github.com/phothinmg/susee/issues" }, "dependencies": { "@suseejs/color": "^0.0.9", "@suseejs/ts6": "^1.0.0" }, "devDependencies": { "@biomejs/biome": "^2.4.12", "@suseejs/banner-text-plugin": "^0.0.9", "@suseejs/type": "^0.0.9", "@types/node": "^25.6.0", "tsx": "^4.23.1", "typescript": "^7.0.2" }, "allowScripts": { "esbuild@0.28.1": true }, "workspaces": ["susee-docs"] };
2421
- /**
2422
- * Finds the path of the susee.config file if it exists.
2423
- * It checks for the existence of "susee.config.ts", "susee.config.js", and "susee.config.mjs" in the current working directory.
2424
- * The first file found is returned.
2425
- * @returns {string | undefined} - path to the susee.config file or undefined if it does not exist.
2426
- */
2427
- const getSuseeConfigPath = () => {
2428
- const fileNames = ["susee.config.ts", "susee.config.js", "susee.config.mjs"];
2429
- let configFile;
2430
- for (const file of fileNames) {
2431
- const _file = ts6.sys.resolvePath(file);
2432
- if (ts6.sys.fileExists(_file)) {
2433
- configFile = _file;
2434
- break;
2435
- }
2436
- }
2437
- return configFile;
2438
- };
2439
- /**
2440
- * Checks if the given entries have at least one entry and if there are any duplicate export paths.
2441
- * If there are no entries, it will exit with code 1 and print an error message.
2442
- * If there are any duplicate export paths, it will exit with code 1 and print an error message.
2443
- * It will also check if each entry file exists, if not, it will exit with code 1 and print an error message.
2444
- * @param {EntryPoint[]} entries - array of entry points
2445
- */
2446
- function checkEntries(entries) {
2447
- if (entries.length < 1) {
2448
- console.error(tcolor.magenta(`No entry found in susee.config file or build options, at least one entry required`));
2449
- ts6.sys.exit(1);
2450
- }
2451
- const objectStore = {};
2452
- const duplicateExportPaths = [];
2453
- for (const obj of entries) {
2454
- const value = obj.exportPath;
2455
- if (objectStore[value]) {
2456
- duplicateExportPaths.push(`"${value}"`);
2457
- }
2458
- else {
2459
- objectStore[value] = true;
2460
- }
2461
- }
2462
- if (duplicateExportPaths.length > 0) {
2463
- console.error(tcolor.magenta(`Duplicate export paths/path (${duplicateExportPaths.join(",")}) found in your susee.config file or build options , that will error for bundled output`));
2464
- ts6.sys.exit(1);
2465
- }
2466
- for (const obj of entries) {
2467
- if (!ts6.sys.fileExists(ts6.sys.resolvePath(obj.entry))) {
2468
- console.error(tcolor.magenta(`Entry file ${obj.entry} dose not exists.`));
2469
- ts6.sys.exit(1);
2470
- }
2471
- }
2472
- }
2473
- /**
2474
- * Generates normalized build options from the user config.
2475
- * It validates entry points, applies default values, removes duplicate formats,
2476
- * resolves the output directory for each export path, and keeps duplicate declaration handling fail-fast.
2477
- * @param {SuSeeConfig} config - raw susee configuration object.
2478
- * @returns {BuildOptions} normalized build options for the compiler.
2479
- */
2480
- function generateBuildOptions(config) {
2481
- const outDir = config.outDir ?? "dist";
2482
- const points = [];
2483
- checkEntries(config.entryPoints);
2484
- for (const ent of config.entryPoints) {
2485
- const entry = ent.entry;
2486
- const exportPath = ent.exportPath;
2487
- const format = ent.format
2488
- ? [...new Set(ent.format)]
2489
- : ["esm"];
2490
- const warning = ent.warning ?? false;
2491
- const plugins = ent.plugins ?? [];
2492
- const tsconfigFilePath = ent.tsconfigFilePath ?? undefined;
2493
- const outputDirectoryPath = ent.exportPath === "." ? outDir : `${outDir}${ent.exportPath.slice(1)}`;
2494
- points.push({
2495
- entry,
2496
- exportPath,
2497
- format,
2498
- plugins,
2499
- warning,
2500
- outputDirectoryPath,
2501
- tsconfigFilePath,
2502
- });
2503
- }
2504
- return {
2505
- buildEntryPoints: points,
2506
- updatePackage: config.allowUpdatePackageJson ?? false,
2507
- outDir,
2508
- };
2509
- }
2510
- /**
2511
- * Loads the susee config file from the current working directory and converts it into build options.
2512
- * If no supported config file is found, it returns `undefined`.
2513
- * @returns {Promise<BuildOptions | undefined>} normalized build options or undefined when no config file exists.
2514
- */
2515
- async function finalSuseeConfig() {
2516
- const configPath = getSuseeConfigPath();
2517
- if (configPath) {
2518
- const _default = await import(configPath);
2519
- const config = _default.default;
2520
- return generateBuildOptions(config);
2521
- }
2522
- }
2523
- /**
2524
- * Normalizes TypeScript compiler options when JSX compilation is requested.
2525
- *
2526
- * For JSX input, this validates that the source imports either React runtime
2527
- * modules or the configured `jsxImportSource` runtime package. When validation
2528
- * passes, it enables DOM libs and defaults `jsx` to `ReactJSX` if unset.
2529
- *
2530
- * @param {string} sourceCode - Source text to inspect for JSX runtime imports.
2531
- * @param {ts6.CompilerOptions} compilerOptions - User-provided compiler options.
2532
- * @param {boolean} isJsx - Whether JSX mode is enabled for this compilation.
2533
- * @returns {ts6.CompilerOptions} Compiler options to pass into program creation.
2534
- */
2535
- function jsxCompilerOptions(sourceCode, compilerOptions, isJsx) {
2536
- if (!isJsx) {
2537
- return compilerOptions;
2538
- }
2539
- const reactRegexp = /import\s+(?:.*?)\s+from\s+(?:"react"|"react\/.*"|"react-dom\/.*"|"react-dom")/gm;
2540
- if (!reactRegexp.test(sourceCode)) {
2541
- if (!compilerOptions.jsxImportSource) {
2542
- console.error("[jsx-runtime-error]:\nJSX syntax found in bundled code,but its not react runtime,you need to be set jsxImportSource in tsconfig.");
2543
- process.exit(1);
2544
- }
2545
- const txt = compilerOptions.jsxImportSource;
2546
- const pattern = `import\\s+(?:.*?)\\s+from\\s+("${txt}"|"${txt}\\/.*")`;
2547
- const re = new RegExp(pattern, "gm");
2548
- if (!re.test(sourceCode)) {
2549
- console.error("[jsx-runtime-mismatch-error]:\nJSX syntax found in bundled code,but its not react runtime and jsx-runtime from bundled code and jsxImportSource from tsconfig are mismatched.`");
2550
- process.exit(1);
2551
- }
2552
- }
2553
- const { jsx, lib, ...rest } = compilerOptions;
2554
- const _jsx = jsx ?? ts6.JsxEmit.ReactJSX;
2555
- return {
2556
- lib: ["dom", "dom.iterable", "esnext"],
2557
- jsx: _jsx,
2558
- ...rest,
2559
- };
2560
- }
2561
- /**
2562
- * Creates a ts.CompilerHost that can be used with the typescript compiler.
2563
- * This host is designed to be used with in-memory compilation and will
2564
- * return the source file for the given fileName and will write all output
2565
- * files to the createdFiles object.
2566
- * @param {string} sourceCode - the source code to compile
2567
- * @param {string} fileName - the name of the file to compile
2568
- * @returns {{createdFiles: Record<string, string>, host: ts.CompilerHost}}
2569
- */
2570
- function createHost(sourceCode, fileName) {
2571
- const createdFiles = {};
2572
- const host = {
2573
- getSourceFile: (file, languageVersion) => {
2574
- if (file === fileName) {
2575
- return ts6.createSourceFile(file, sourceCode, languageVersion);
2576
- }
2577
- return undefined;
2578
- },
2579
- writeFile: (fileName, contents) => {
2580
- createdFiles[fileName] = contents;
2581
- },
2582
- getDefaultLibFileName: (options) => ts6.getDefaultLibFilePath(options),
2583
- getCurrentDirectory: () => "",
2584
- getDirectories: () => [],
2585
- fileExists: (file) => file === fileName,
2586
- readFile: (file) => (file === fileName ? sourceCode : undefined),
2587
- getCanonicalFileName: (file) => file,
2588
- useCaseSensitiveFileNames: () => true,
2589
- getNewLine: () => "\n",
2590
- };
2591
- return { createdFiles, host };
2592
- }
2593
- function suseeCompiler({ sourceCode, fileName, compilerOptions, isJsx = false, }) {
2594
- compilerOptions = jsxCompilerOptions(sourceCode, compilerOptions, isJsx);
2595
- // create host
2596
- const _host = createHost(sourceCode, fileName);
2597
- const createdFiles = _host.createdFiles;
2598
- const host = _host.host;
2599
- const program = ts6.createProgram([fileName], compilerOptions, host);
2600
- program.emit();
2601
- let dts;
2602
- let map;
2603
- let code = "";
2604
- let file_name = "";
2605
- let out_dir = "";
2606
- for (const key of Object.keys(createdFiles)) {
2607
- if (key.endsWith(".js"))
2608
- code = createdFiles[key];
2609
- if (key.endsWith(".d.ts"))
2610
- dts = createdFiles[key];
2611
- if (key.endsWith(".js.map"))
2612
- map = createdFiles[key];
2613
- file_name = path.basename(key).split(".")[0];
2614
- out_dir = path.dirname(key);
2615
- }
2616
- return { code, file_name, out_dir, dts, map };
2617
- }
2618
- //src/compiler/tsoptions.ts
2619
- /**
2620
- * Get the path of the configuration file.
2621
- * If customConfigPath is provided and exists, use it.
2622
- * If customConfigPath is not provided or does not exist, use the default configuration file.
2623
- * @param {string | undefined} customConfigPath path of the custom configuration file.
2624
- * @returns {string | undefined} path of the configuration file or undefined if customConfigPath does not exist.
2625
- */
2626
- function getTsConfigPath(customConfigPath) {
2627
- let config_path;
2628
- if (customConfigPath) {
2629
- if (!ts6.sys.fileExists(ts6.sys.resolvePath(customConfigPath))) {
2630
- console.error(`> ${tcolor.magenta(`Given custom file ${customConfigPath} does not exists`)}`);
2631
- ts6.sys.exit(1);
2632
- }
2633
- config_path = customConfigPath;
2634
- return config_path;
2635
- }
2636
- else {
2637
- config_path = ts6.findConfigFile(ts6.sys.getCurrentDirectory(), ts6.sys.fileExists);
2638
- return config_path;
2639
- }
2640
- }
2641
- /**
2642
- * Get the TypeScript compiler options for susee bundler.
2643
- * @param {string | undefined} customConfigPath path of the custom configuration file.
2644
- */
2645
- function getCompilerOptions(customConfigPath) {
2646
- let tsconfig_opts;
2647
- const config_path = getTsConfigPath(customConfigPath);
2648
- if (config_path) {
2649
- const config = ts6.readConfigFile(config_path, ts6.sys.readFile);
2650
- const basePath = path.dirname(config_path);
2651
- const parsed = ts6.parseJsonConfigFileContent(config.config, ts6.sys, basePath);
2652
- tsconfig_opts = { ...parsed.options };
2653
- }
2654
- const commonjs = (out_dir) => {
2655
- const _out = out_dir ? out_dir : "dist";
2656
- if (tsconfig_opts !== undefined) {
2657
- const { rootDir, outDir, module, allowJs, declarationDir, ...rest } = tsconfig_opts;
2658
- return {
2659
- outDir: _out,
2660
- module: ts6.ModuleKind.CommonJS,
2661
- allowJs: true,
2662
- ...rest,
2663
- };
2664
- }
2665
- else {
2666
- return {
2667
- outDir: _out,
2668
- module: ts6.ModuleKind.CommonJS,
2669
- target: ts6.ScriptTarget.Latest,
2670
- };
2671
- }
2672
- };
2673
- const esm = (out_dir) => {
2674
- const _out = out_dir ? out_dir : "dist";
2675
- if (tsconfig_opts !== undefined) {
2676
- const { rootDir, outDir, module, allowJs, declarationDir, ...rest } = tsconfig_opts;
2677
- return {
2678
- outDir: _out,
2679
- module: ts6.ModuleKind.ES2020,
2680
- allowJs: true,
2681
- ...rest,
2682
- };
2683
- }
2684
- else {
2685
- return {
2686
- outDir: _out,
2687
- module: ts6.ModuleKind.ES2020,
2688
- target: ts6.ScriptTarget.Latest,
2689
- };
2690
- }
2691
- };
2692
- const defaultOptions = ts6.getDefaultCompilerOptions;
2693
- return { commonjs, esm, defaultOptions };
2694
- }
2695
- //src/compiler/index.ts
2696
- const logCompilerPhase = (entry, format, phase, start) => {
2697
- logProfilePhase(`compiler:${format}:${entry}`, phase, start);
2698
- };
2699
- /**
2700
- * Compiler for the JavaScript API.
2701
- * It bundles each configured entry point, emits CommonJS and ESM outputs,
2702
- * and optionally updates package export metadata.
2703
- */
2704
- class Compiler {
2705
- _files;
2706
- _object;
2707
- _bundledCodeCache;
2708
- /**
2709
- * Creates a compiler instance with normalized build options.
2710
- * @param {BuildOptions} object - build options generated from the susee config.
2711
- */
2712
- constructor(object) {
2713
- this._object = object;
2714
- this._bundledCodeCache = new WeakMap();
2715
- this._files = {
2716
- commonjs: undefined,
2717
- commonjsTypes: undefined,
2718
- esm: undefined,
2719
- esmTypes: undefined,
2720
- main: undefined,
2721
- module: undefined,
2722
- types: undefined,
2723
- };
2724
- }
2725
- _update() {
2726
- return this._object.updatePackage;
2727
- }
2728
- async _bundle(point) {
2729
- let bundledCode = this._bundledCodeCache.get(point);
2730
- if (!bundledCode) {
2731
- bundledCode = bundler(point.entry, point.plugins, point.warning);
2732
- this._bundledCodeCache.set(point, bundledCode);
2733
- }
2734
- return bundledCode;
2735
- }
2736
- async _commonjs(point) {
2737
- const isMain = point.exportPath === ".";
2738
- const opts = getCompilerOptions(point.tsconfigFilePath);
2739
- const compilerOptions = opts.commonjs(point.outputDirectoryPath);
2740
- let phaseStart = process.hrtime.bigint();
2741
- const bundledCode = await this._bundle(point);
2742
- logCompilerPhase(point.entry, "commonjs", "bundle", phaseStart);
2743
- const is_jsx = utils.checks.isJsxContent(bundledCode);
2744
- phaseStart = process.hrtime.bigint();
2745
- const compiled = suseeCompiler({
2746
- sourceCode: bundledCode,
2747
- fileName: point.entry,
2748
- compilerOptions,
2749
- isJsx: is_jsx,
2750
- });
2751
- logCompilerPhase(point.entry, "commonjs", "typescriptEmit", phaseStart);
2752
- let compiledCode = compiled.code;
2753
- const mainFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.cjs`);
2754
- const dtsFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.d.cts`);
2755
- const mapFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.cjs.map`);
2756
- // replace source mapping url
2757
- compiledCode = compiledCode.replace(new RegExp(`${compiled.file_name}.js.map`, "gm"), `${compiled.file_name}.cjs.map`);
2758
- // call post-process plugin
2759
- if (point.plugins.length > 0) {
2760
- for (const plugin of point.plugins) {
2761
- const _plugin = typeof plugin === "function" ? plugin() : plugin;
2762
- if (_plugin.type === "post-process") {
2763
- phaseStart = process.hrtime.bigint();
2764
- if (_plugin.async) {
2765
- compiledCode = await _plugin.func(compiledCode, point.entry);
2766
- }
2767
- else {
2768
- compiledCode = _plugin.func(compiledCode, point.entry);
2769
- }
2770
- logCompilerPhase(point.entry, "commonjs", `postProcessPlugin:${_plugin.name ?? "anonymous"}`, phaseStart);
2771
- }
2772
- }
2773
- }
2774
- // if allow update create file object
2775
- if (this._update()) {
2776
- this._files.commonjs = mainFilePath;
2777
- if (compiled.dts) {
2778
- this._files.commonjsTypes = dtsFilePath;
2779
- }
2780
- if (isMain && point.format.includes("commonjs")) {
2781
- if (this._files.commonjs)
2782
- this._files.main = this._files.commonjs;
2783
- if (this._files.commonjsTypes)
2784
- this._files.types = this._files.commonjsTypes;
2785
- }
2786
- } //update
2787
- phaseStart = process.hrtime.bigint();
2788
- await files.writeFile(mainFilePath, compiledCode);
2789
- if (compiled.dts)
2790
- await files.writeFile(dtsFilePath, compiled.dts);
2791
- if (compiled.map)
2792
- await files.writeFile(mapFilePath, compiled.map);
2793
- logCompilerPhase(point.entry, "commonjs", "writeFiles", phaseStart);
2794
- }
2795
- async _esm(point) {
2796
- const isMain = point.exportPath === ".";
2797
- const opts = getCompilerOptions(point.tsconfigFilePath);
2798
- const compilerOptions = opts.esm(point.outputDirectoryPath);
2799
- let phaseStart = process.hrtime.bigint();
2800
- const bundledCode = await this._bundle(point);
2801
- logCompilerPhase(point.entry, "esm", "bundle", phaseStart);
2802
- const is_jsx = utils.checks.isJsxContent(bundledCode);
2803
- phaseStart = process.hrtime.bigint();
2804
- const compiled = suseeCompiler({
2805
- sourceCode: bundledCode,
2806
- fileName: point.entry,
2807
- compilerOptions,
2808
- isJsx: is_jsx,
2809
- });
2810
- logCompilerPhase(point.entry, "esm", "typescriptEmit", phaseStart);
2811
- let compiledCode = compiled.code;
2812
- const mainFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.mjs`);
2813
- const dtsFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.d.mts`);
2814
- const mapFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.mjs.map`);
2815
- // replace source mapping url
2816
- compiledCode = compiledCode.replace(new RegExp(`${compiled.file_name}.js.map`, "gm"), `${compiled.file_name}.mjs.map`);
2817
- // call post-process plugin
2818
- if (point.plugins.length > 0) {
2819
- for (const plugin of point.plugins) {
2820
- const _plugin = typeof plugin === "function" ? plugin() : plugin;
2821
- if (_plugin.type === "post-process") {
2822
- phaseStart = process.hrtime.bigint();
2823
- if (_plugin.async) {
2824
- compiledCode = await _plugin.func(compiledCode, point.entry);
2825
- }
2826
- else {
2827
- compiledCode = _plugin.func(compiledCode, point.entry);
2828
- }
2829
- logCompilerPhase(point.entry, "esm", `postProcessPlugin:${_plugin.name ?? "anonymous"}`, phaseStart);
2830
- }
2831
- }
2832
- }
2833
- if (this._update()) {
2834
- this._files.esm = mainFilePath;
2835
- if (compiled.dts) {
2836
- this._files.esmTypes = dtsFilePath;
2837
- }
2838
- if (isMain && this._files.esm) {
2839
- this._files.module = this._files.esm;
2840
- }
2841
- } //update
2842
- phaseStart = process.hrtime.bigint();
2843
- await files.writeFile(mainFilePath, compiledCode);
2844
- if (compiled.dts)
2845
- await files.writeFile(dtsFilePath, compiled.dts);
2846
- if (compiled.map)
2847
- await files.writeFile(mapFilePath, compiled.map);
2848
- logCompilerPhase(point.entry, "esm", "writeFiles", phaseStart);
2849
- }
2850
- /**
2851
- * Clears the output directory and compiles all configured entry points.
2852
- * It also updates package.json export fields when package updates are enabled.
2853
- * @returns {Promise<void>}
2854
- */
2855
- async compile() {
2856
- await files.clearFolder(this._object.outDir);
2857
- for (const point of this._object.buildEntryPoints) {
2858
- for (const format of point.format) {
2859
- switch (format) {
2860
- case "commonjs":
2861
- await this._commonjs(point);
2862
- if (this._update()) {
2863
- await files.writePackageJson(this._files, point.exportPath);
2864
- }
2865
- break;
2866
- case "esm":
2867
- await this._esm(point);
2868
- if (this._update()) {
2869
- await files.writePackageJson(this._files, point.exportPath);
2870
- }
2871
- break;
2872
- }
2873
- }
2874
- }
2875
- }
2876
- }
2877
- //src/cli/build.ts
2878
- async function cliBuild() {
2879
- console.time(tcolor.cyan("[Build] "));
2880
- const buildOptions = await finalSuseeConfig();
2881
- if (!buildOptions) {
2882
- console.error(tcolor.magenta(`No susee.config file ("susee.config.ts", "susee.config.js", "susee.config.mjs") found`));
2883
- ts6.sys.exit(1);
2884
- }
2885
- const compiler = new Compiler(buildOptions);
2886
- await compiler.compile();
2887
- console.timeEnd(tcolor.cyan("[Build] "));
2888
- }
2889
- //src/cli/lib/fail.ts
2890
- function fail(message) {
2891
- console.error(`${tcolor.magenta("[Error]")} : ${tcolor.gray(message)}`);
2892
- process.exit(1);
2893
- }
2894
- function isFile(entry) {
2895
- const exts = [".js", ".ts", ".mts", ".mjs", ".cjs", ".cts"];
2896
- return exts.includes(path.extname(entry));
2897
- }
2898
- // biome-ignore lint/suspicious/noExplicitAny: unknown
2899
- function isEmptyObject(entry) {
2900
- return (typeof entry === "object" &&
2901
- !Array.isArray(entry) &&
2902
- Object.keys(entry).length === 0);
2903
- }
2904
- function parseBooleanFlag(flag, value) {
2905
- if (value === "true")
2906
- return true;
2907
- if (value === "false")
2908
- return false;
2909
- fail(`Type of ${flag} must be boolean.`);
2910
- }
2911
- // biome-ignore lint/suspicious/noExplicitAny: unknown
2912
- function parseArgs(argv) {
2913
- const opts = {
2914
- entry: "",
2915
- };
2916
- for (let index = 0; index < argv.length; index += 1) {
2917
- const argument = argv[index];
2918
- if (index === 0 && !argument.startsWith("--") && isFile(argument)) {
2919
- opts.entry = argument;
2920
- continue;
2921
- }
2922
- const [flag, inlineValue] = argument.split("=", 2);
2923
- const nextValue = argv[index + 1];
2924
- const value = inlineValue ?? nextValue;
2925
- switch (flag) {
2926
- case "--entry":
2927
- if (!value || value.startsWith("--"))
2928
- fail("Entry point required.");
2929
- if (opts.entry !== "" && isFile(opts.entry))
2930
- fail("Entry point already exists.");
2931
- opts.entry = value;
2932
- if (inlineValue === undefined) {
2933
- index += 1;
2934
- }
2935
- break;
2936
- case "--outdir":
2937
- if (!value || value.startsWith("--"))
2938
- fail("Output directory required.");
2939
- opts.outDir = value;
2940
- if (inlineValue === undefined) {
2941
- index += 1;
2942
- }
2943
- break;
2944
- case "--format":
2945
- if (value !== "cjs" && value !== "commonjs" && value !== "esm") {
2946
- fail("Format must be cjs, commonjs, or esm.");
2947
- }
2948
- opts.format =
2949
- value === "cjs"
2950
- ? "commonjs"
2951
- : value;
2952
- if (inlineValue === undefined) {
2953
- index += 1;
2954
- }
2955
- break;
2956
- case "--tsconfig":
2957
- if (!value || value.startsWith("--"))
2958
- fail("Tsconfig path required.");
2959
- opts.tsconfig = value;
2960
- if (inlineValue === undefined) {
2961
- index += 1;
2962
- }
2963
- break;
2964
- case "--allow-update":
2965
- if (inlineValue !== undefined) {
2966
- opts.allowUpdate = parseBooleanFlag("allow update", inlineValue);
2967
- }
2968
- else if (nextValue === "true" || nextValue === "false") {
2969
- opts.allowUpdate = parseBooleanFlag("allow update", nextValue);
2970
- index += 1;
2971
- }
2972
- else {
2973
- opts.allowUpdate = true;
2974
- }
2975
- break;
2976
- case "--warning":
2977
- if (inlineValue !== undefined) {
2978
- opts.warning = parseBooleanFlag("warning", inlineValue);
2979
- }
2980
- else if (nextValue === "true" || nextValue === "false") {
2981
- opts.warning = parseBooleanFlag("warning", nextValue);
2982
- index += 1;
2983
- }
2984
- else {
2985
- opts.warning = true;
2986
- }
2987
- break;
2988
- case "--profile":
2989
- if (inlineValue !== undefined) {
2990
- opts.profile = parseBooleanFlag("profile", inlineValue);
2991
- }
2992
- else if (nextValue === "true" || nextValue === "false") {
2993
- opts.profile = parseBooleanFlag("profile", nextValue);
2994
- index += 1;
2995
- }
2996
- else {
2997
- opts.profile = true;
2998
- }
2999
- break;
3000
- }
3001
- }
3002
- if (isEmptyObject(opts) || opts.entry === "") {
3003
- fail("Entry point required");
3004
- }
3005
- return opts;
3006
- }
3007
- function getDefaultOptions(args) {
3008
- const entry = args.entry;
3009
- const outDir = args.outDir ?? "dist";
3010
- const format = args.format ?? "esm";
3011
- const tsconfig = args.tsconfig ?? undefined;
3012
- const allowUpdate = args.allowUpdate ?? false;
3013
- const warning = args.warning ?? false;
3014
- const profile = args.profile ?? false;
3015
- return {
3016
- entry,
3017
- outDir,
3018
- format,
3019
- tsconfig,
3020
- allowUpdate,
3021
- warning,
3022
- profile,
3023
- plugins: [],
3024
- };
3025
- }
3026
- //src/cli/cli.ts
3027
- const logCliCompilerPhase = (entry, format, phase, start) => {
3028
- logProfilePhase(`compiler:${format}:${entry}`, phase, start);
3029
- };
3030
- class CliCompiler {
3031
- _files;
3032
- _update;
3033
- constructor() {
3034
- this._files = {
3035
- commonjs: undefined,
3036
- commonjsTypes: undefined,
3037
- esm: undefined,
3038
- esmTypes: undefined,
3039
- main: undefined,
3040
- module: undefined,
3041
- types: undefined,
3042
- };
3043
- this._update = false;
3044
- }
3045
- async _commonjs(opts) {
3046
- this._update = opts.allowUpdate;
3047
- const _opts = getCompilerOptions(opts.tsconfig);
3048
- const compilerOptions = _opts.commonjs(opts.outDir);
3049
- let phaseStart = process.hrtime.bigint();
3050
- const bundledCode = await bundler(opts.entry, opts.plugins, opts.warning);
3051
- logCliCompilerPhase(opts.entry, "commonjs", "bundle", phaseStart);
3052
- const is_jsx = utils.checks.isJsxContent(bundledCode);
3053
- phaseStart = process.hrtime.bigint();
3054
- const compiled = suseeCompiler({
3055
- sourceCode: bundledCode,
3056
- fileName: opts.entry,
3057
- compilerOptions,
3058
- isJsx: is_jsx,
3059
- });
3060
- logCliCompilerPhase(opts.entry, "commonjs", "typescriptEmit", phaseStart);
3061
- let compiledCode = compiled.code;
3062
- const mainFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.cjs`);
3063
- const dtsFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.d.cts`);
3064
- const mapFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.cjs.map`);
3065
- compiledCode = compiledCode.replace(new RegExp(`${compiled.file_name}.js.map`, "gm"), `${compiled.file_name}.cjs.map`);
3066
- // --
3067
- // call post-process plugin
3068
- if (opts.plugins.length > 0) {
3069
- for (const plugin of opts.plugins) {
3070
- const _plugin = typeof plugin === "function" ? plugin() : plugin;
3071
- if (_plugin.type === "post-process") {
3072
- phaseStart = process.hrtime.bigint();
3073
- if (_plugin.async) {
3074
- compiledCode = await _plugin.func(compiledCode, opts.entry);
3075
- }
3076
- else {
3077
- compiledCode = _plugin.func(compiledCode, opts.entry);
3078
- }
3079
- logCliCompilerPhase(opts.entry, "commonjs", `postProcessPlugin:${_plugin.name ?? "anonymous"}`, phaseStart);
3080
- }
3081
- }
3082
- } //-----------
3083
- if (this._update) {
3084
- this._files.commonjs = mainFilePath;
3085
- if (compiled.dts) {
3086
- this._files.commonjsTypes = dtsFilePath;
3087
- }
3088
- if (opts.format.includes("commonjs")) {
3089
- if (this._files.commonjs)
3090
- this._files.main = this._files.commonjs;
3091
- if (this._files.commonjsTypes)
3092
- this._files.types = this._files.commonjsTypes;
3093
- }
3094
- } //update
3095
- phaseStart = process.hrtime.bigint();
3096
- await files.writeFile(mainFilePath, compiledCode);
3097
- if (compiled.dts)
3098
- await files.writeFile(dtsFilePath, compiled.dts);
3099
- if (compiled.map)
3100
- await files.writeFile(mapFilePath, compiled.map);
3101
- logCliCompilerPhase(opts.entry, "commonjs", "writeFiles", phaseStart);
3102
- }
3103
- //-----------------------------------------------------------------//
3104
- async _esm(opts) {
3105
- this._update = opts.allowUpdate;
3106
- const _opts = getCompilerOptions(opts.tsconfig);
3107
- const compilerOptions = _opts.esm(opts.outDir);
3108
- let phaseStart = process.hrtime.bigint();
3109
- const bundledCode = await bundler(opts.entry, opts.plugins, opts.warning);
3110
- logCliCompilerPhase(opts.entry, "esm", "bundle", phaseStart);
3111
- const is_jsx = utils.checks.isJsxContent(bundledCode);
3112
- phaseStart = process.hrtime.bigint();
3113
- const compiled = suseeCompiler({
3114
- sourceCode: bundledCode,
3115
- fileName: opts.entry,
3116
- compilerOptions,
3117
- isJsx: is_jsx,
3118
- });
3119
- logCliCompilerPhase(opts.entry, "esm", "typescriptEmit", phaseStart);
3120
- let compiledCode = compiled.code;
3121
- const mainFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.mjs`);
3122
- const dtsFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.d.mts`);
3123
- const mapFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.mjs.map`);
3124
- compiledCode = compiledCode.replace(new RegExp(`${compiled.file_name}.js.map`, "gm"), `${compiled.file_name}.mjs.map`);
3125
- // call post-process plugin
3126
- if (opts.plugins.length > 0) {
3127
- for (const plugin of opts.plugins) {
3128
- const _plugin = typeof plugin === "function" ? plugin() : plugin;
3129
- if (_plugin.type === "post-process") {
3130
- phaseStart = process.hrtime.bigint();
3131
- if (_plugin.async) {
3132
- compiledCode = await _plugin.func(compiledCode, opts.entry);
3133
- }
3134
- else {
3135
- compiledCode = _plugin.func(compiledCode, opts.entry);
3136
- }
3137
- logCliCompilerPhase(opts.entry, "esm", `postProcessPlugin:${_plugin.name ?? "anonymous"}`, phaseStart);
3138
- }
3139
- }
3140
- } //-----------
3141
- if (this._update) {
3142
- this._files.esm = mainFilePath;
3143
- if (compiled.dts) {
3144
- this._files.esmTypes = dtsFilePath;
3145
- }
3146
- if (this._files.esm) {
3147
- this._files.module = this._files.esm;
3148
- }
3149
- } //update
3150
- phaseStart = process.hrtime.bigint();
3151
- await files.writeFile(mainFilePath, compiledCode);
3152
- if (compiled.dts)
3153
- await files.writeFile(dtsFilePath, compiled.dts);
3154
- if (compiled.map)
3155
- await files.writeFile(mapFilePath, compiled.map);
3156
- logCliCompilerPhase(opts.entry, "esm", "writeFiles", phaseStart);
3157
- }
3158
- //--
3159
- async compile(opts) {
3160
- await files.clearFolder(opts.outDir);
3161
- switch (opts.format) {
3162
- case "commonjs":
3163
- await this._commonjs(opts);
3164
- if (this._update) {
3165
- await files.writePackageJson(this._files, ".");
3166
- }
3167
- break;
3168
- case "esm":
3169
- await this._esm(opts);
3170
- if (this._update) {
3171
- await files.writePackageJson(this._files, ".");
3172
- }
3173
- break;
3174
- }
3175
- }
3176
- }
3177
- const cliCompiler = new CliCompiler();
3178
- //src/cli/lib/print_help.ts
3179
- function printHelp() {
3180
- console.log(`Susee CLI.
3181
-
3182
- Usage:
3183
- susee Build using susee.config.{ts,js,mjs}
3184
- susee init Generate susee.config.{ts,js,mjs}
3185
- susee --help Show this message
3186
- susee build <entry> [options] Build from a single entry file
3187
-
3188
- Options:
3189
- --entry <path> Entry file (optional if provided as positional <entry>)
3190
- --outdir <path> Output directory
3191
- --format <cjs|commonjs|esm> Output module format
3192
- --tsconfig <path> Custom tsconfig path
3193
- --allow-update[=true|false] Enable/disable dependency update
3194
- --warning[=true|false] Treat dependency graph warnings as fatal
3195
- --profile[=true|false] Print bundler/compiler phase timings
3196
-
3197
- Notes:
3198
- Duplicate top-level declarations fail the build with file and location output.
3199
- Rename conflicting declarations in source files before bundling.
3200
-
3201
- Examples:
3202
- susee build src/index.ts --outdir dist
3203
- susee build src/index.ts --format commonjs
3204
- susee build --entry src/index.ts --format esm --tsconfig tsconfig.build.json
3205
- susee build src/index.ts --profile
3206
- susee --profile
3207
-
3208
- `);
3209
- }
3210
- //src/cli/index.ts
3211
- const tsFileText = `
3212
- import type { SuSeeConfig } from "susee";
3213
-
3214
- const config: SuSeeConfig = {
3215
- // Array of entry point objects.
3216
- // ----------------------------
3217
- entryPoints: [
3218
- // You can add more entry points for different export paths.
3219
- // NOTE: duplicate export paths are not allowed.
3220
- // --------------------------------------------
3221
- {
3222
- // (required) Entry file path.
3223
- entry: "src/index.ts", // replace with your entry file
3224
- // (required) Export path for this entry.
3225
- exportPath: ".", // "." stands for the main export path and can be set to "./foo", "./bar", etc.
3226
- // (optional) Output module formats ["commonjs"] or ["esm", "commonjs"], default: ["esm"].
3227
- // Uncomment the following line to edit.
3228
- //format: ["esm"],
3229
- // Duplicate top-level declarations are checked during bundling.
3230
- // Rename conflicting declarations in source files before building.
3231
- // (optional) Custom tsconfig.json path, default: undefined.
3232
- // Uncomment the following line to edit.
3233
- //tsconfigFilePath: undefined,
3234
- // (optional) Array of susee plugins, default: [].
3235
- // Uncomment the following line to edit.
3236
- //plugins: [],
3237
- // (optional) Warning messages, if it true and warning message exist(1), default: false.
3238
- // Uncomment the following line to edit.
3239
- //warning: false,
3240
- },
3241
- ],
3242
- // NOTE: the following options apply to all entry points.
3243
- // ----------------------------------------------------------
3244
- // (optional) Output directory, default: dist.
3245
- // Uncomment the following line to edit.
3246
- //outDir: "dist",
3247
- // (optional) Allow susee to update your package.json, default: false.
3248
- // Uncomment the following line to edit.
3249
- //allowUpdatePackageJson: false,
3250
- };
3251
-
3252
- export default config;
3253
- `.trim();
3254
- const jsFileText = `
3255
- /**
3256
- * @type {import("susee").SuSeeConfig}
3257
- */
3258
- const config = {
3259
- // Array of entry point objects.
3260
- // ----------------------------
3261
- entryPoints: [
3262
- // You can add more entry points for different export paths.
3263
- // NOTE: duplicate export paths are not allowed.
3264
- // --------------------------------------------
3265
- {
3266
- // (required) Entry file path.
3267
- entry: "src/index.ts", // replace with your entry file
3268
- // (required) Export path for this entry.
3269
- exportPath: ".", // "." stands for the main export path and can be set to "./foo", "./bar", etc.
3270
- // (optional) Output module formats ["commonjs"] or ["esm", "commonjs"], default: ["esm"].
3271
- // Uncomment the following line to edit.
3272
- //format: ["esm"],
3273
- // Duplicate top-level declarations are checked during bundling.
3274
- // Rename conflicting declarations in source files before building.
3275
- // (optional) Custom tsconfig.json path, default: undefined.
3276
- // Uncomment the following line to edit.
3277
- //tsconfigFilePath: undefined,
3278
- // (optional) Array of susee plugins, default: [].
3279
- // Uncomment the following line to edit.
3280
- //plugins: [],
3281
- // (optional) Warning messages, if it true and warning message exist(1), default: false.
3282
- // Uncomment the following line to edit.
3283
- //warning: false,
3284
- },
3285
- ],
3286
- // NOTE: the following options apply to all entry points.
3287
- // ----------------------------------------------------------
3288
- // (optional) Output directory, default: dist.
3289
- // Uncomment the following line to edit.
3290
- //outDir: "dist",
3291
- // (optional) Allow susee to update your package.json, default: false.
3292
- // Uncomment the following line to edit.
3293
- //allowUpdatePackageJson: false,
3294
- };
3295
-
3296
- export default config;
3297
- `.trim();
3298
- async function getPackageType() {
3299
- const pkgPath = path.resolve(process.cwd(), "package.json");
3300
- const _pkg = await fs.promises.readFile(pkgPath, "utf8");
3301
- const pkg = JSON.parse(_pkg);
3302
- return __jsonModule__package.type === "module" ? "esm" : "commonjs";
3303
- }
3304
- async function cliInit() {
3305
- const rl = readline.createInterface({
3306
- input: process.stdin,
3307
- output: process.stdout,
3308
- });
3309
- console.log(`${tcolor.gray("┌")} ${tcolor.green("Welcome to Susee!")}`);
3310
- console.log("");
3311
- console.log(`${tcolor.gray("│")}`);
3312
- const is_ts = await rl.question(`${tcolor.cyan("◇")} Is TypeScript Project(y/n) : `);
3313
- const isTs = !!(is_ts === "y" || is_ts === "Y" || is_ts === "");
3314
- rl.close();
3315
- let configFile = "";
3316
- let str = "";
3317
- if (isTs) {
3318
- configFile = "susee.config.ts";
3319
- str = tsFileText;
3320
- }
3321
- else {
3322
- str = jsFileText;
3323
- const pkgType = await getPackageType();
3324
- switch (pkgType) {
3325
- case "commonjs":
3326
- configFile = "susee.config.mjs";
3327
- break;
3328
- case "esm":
3329
- configFile = "susee.config.js";
3330
- break;
3331
- }
3332
- }
3333
- const configFilePath = path.resolve(process.cwd(), configFile);
3334
- if (fs.existsSync(configFilePath))
3335
- await fs.promises.unlink(configFilePath);
3336
- await fs.promises.writeFile(configFilePath, str);
3337
- console.log("");
3338
- console.log(`${tcolor.gray("│")}`);
3339
- console.log("");
3340
- console.info(`${tcolor.gray("└")} Done! Susee config file ${tcolor.cyan(configFile)} is created at project root`);
3341
- }
3342
- function extractProfileFlag(args) {
3343
- const nextArgs = [];
3344
- let profile = false;
3345
- for (let index = 0; index < args.length; index += 1) {
3346
- const argument = args[index];
3347
- const [flag, inlineValue] = argument.split("=", 2);
3348
- if (flag !== "--profile") {
3349
- nextArgs.push(argument);
3350
- continue;
3351
- }
3352
- const nextValue = args[index + 1];
3353
- if (inlineValue !== undefined) {
3354
- profile = parseBooleanFlag("profile", inlineValue);
3355
- continue;
3356
- }
3357
- if (nextValue === "true" || nextValue === "false") {
3358
- profile = parseBooleanFlag("profile", nextValue);
3359
- index += 1;
3360
- continue;
3361
- }
3362
- profile = true;
3363
- }
3364
- return { args: nextArgs, profile };
3365
- }
3366
- async function suseeCliBuild() {
3367
- const rawArgs = process.argv.slice(2);
3368
- const { args, profile } = extractProfileFlag(rawArgs);
3369
- if (profile) {
3370
- setProfileEnabled(true);
3371
- }
3372
- if (args.length === 0) {
3373
- await cliBuild();
3374
- }
3375
- else if (args.length === 1) {
3376
- if (args[0] === "--version" || args[0] === "-v") {
3377
- console.log(tcolor.cyan(`susee v${__jsonModule__package.version}`));
3378
- }
3379
- if (args[0] === "--help" || args[0] === "-h") {
3380
- printHelp();
3381
- }
3382
- if (args[0] === "init") {
3383
- await cliInit();
3384
- }
3385
- if (args[0] === "build") {
3386
- printHelp();
3387
- }
3388
- }
3389
- else if (args.length > 1 &&
3390
- args[0] === "build" &&
3391
- (args[1] === "--help" || args[1] === "-h")) {
3392
- printHelp();
3393
- }
3394
- else if (args.length > 1 && args[0] === "build") {
3395
- const _r = parseArgs(args.slice(1));
3396
- const options = getDefaultOptions(_r);
3397
- await cliCompiler.compile(options);
3398
- }
3399
- else {
3400
- console.error("Unknown CLI usage");
3401
- process.exit(1);
3402
- }
3403
- }
3404
- if (import.meta.main) {
3405
- await suseeCliBuild();
3406
- }
3407
- //src/index.ts
3408
- /**
3409
- * Run a Susee build.
3410
- *
3411
- * Resolution order:
3412
- * 1. Use `options` when provided.
3413
- * 2. Otherwise try loading root config via `finalSuseeConfig()`.
3414
- *
3415
- * If neither source is available, this logs an error and exits with code 1.
3416
- */
3417
- async function build(options) {
3418
- console.time(tcolor.cyan("[Build] "));
3419
- let buildOptions = {};
3420
- const _buildOptions = await finalSuseeConfig();
3421
- if (!options && !_buildOptions) {
3422
- console.error(`${tcolor.magenta("[Error]")} : Required build options or susee config file at root.\n Use ${tcolor.bold("npx susee init")} to create config file.`);
3423
- process.exit(1);
3424
- }
3425
- if (options) {
3426
- buildOptions = generateBuildOptions(options);
3427
- }
3428
- else if (_buildOptions) {
3429
- buildOptions = _buildOptions;
3430
- }
3431
- const compiler = new Compiler(buildOptions);
3432
- await compiler.compile();
3433
- console.timeEnd(tcolor.cyan("[Build] "));
3434
- }
3435
- export { build, bundle as suseeBundler, suseeCliBuild };
3436
- //# sourceMappingURL=index.mjs.map