susee 2.0.4 → 2.1.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.
@@ -0,0 +1,989 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import readline from 'node:readline/promises';
5
+ import tcolor from '@suseejs/color';
6
+ import ts6 from '@suseejs/ts6';
7
+ import { LogTimer, logError, logInfo, suseeBundler } from '@suseejs/susee_bundler';
8
+ import { minify } from 'oxc-minify';
9
+ //src/cli/init.ts
10
+ const tsFileText = `
11
+ import type { SuSeeConfig } from "susee";
12
+ const config: SuSeeConfig = {
13
+ // Array of entry point objects.
14
+ // ----------------------------
15
+ entryPoints: [
16
+ // You can add more entry points for different export paths.
17
+ // NOTE: duplicate export paths are not allowed.
18
+ // --------------------------------------------
19
+ {
20
+ // (required) Entry file path.
21
+ entry: "src/index.ts", // replace with your entry file
22
+ // (required) Export path for this entry.
23
+ exportPath: ".", // "." stands for the main export path and can be set to "./foo", "./bar", etc.
24
+ // (optional) Output module formats ["commonjs"] or ["esm", "commonjs"], default: ["esm"].
25
+ // Uncomment the following line to edit.
26
+ //format: ["esm"],
27
+ // (optional) Custom tsconfig.json path, default: undefined.
28
+ // Uncomment the following line to edit.
29
+ //tsconfigFilePath: undefined,
30
+ // (optional)Lint checks to run on the bundled output.
31
+ // Uncomment the following line to edit.
32
+ //checks:{ checkAnonymous: false, checkDefaultExports: false, checkNpmInstalled: false }.
33
+ // (optional) Minify the bundled output.
34
+ // Pass true for default minification, or an object with custom MinifyOptions.
35
+ // Uncomment the following line to edit.
36
+ //minify: false,
37
+ },
38
+ ],
39
+ // NOTE: the following options apply to all entry points.
40
+ // ----------------------------------------------------------
41
+ // (optional) Output directory, default: dist.
42
+ // Uncomment the following line to edit.
43
+ //outDir: "dist",
44
+ // (optional) Allow susee to update your package.json, default: false.
45
+ // Uncomment the following line to edit.
46
+ //allowUpdatePackageJson: false,
47
+ };
48
+ export default config;
49
+ `.trim();
50
+ const jsFileText = `
51
+ /**
52
+ * @type {import("susee").SuSeeConfig}
53
+ */
54
+ const config = {
55
+ // Array of entry point objects.
56
+ // ----------------------------
57
+ entryPoints: [
58
+ // You can add more entry points for different export paths.
59
+ // NOTE: duplicate export paths are not allowed.
60
+ // --------------------------------------------
61
+ {
62
+ // (required) Entry file path.
63
+ entry: "src/index.ts", // replace with your entry file
64
+ // (required) Export path for this entry.
65
+ exportPath: ".", // "." stands for the main export path and can be set to "./foo", "./bar", etc.
66
+ // (optional) Output module formats ["commonjs"] or ["esm", "commonjs"], default: ["esm"].
67
+ // Uncomment the following line to edit.
68
+ //format: ["esm"],
69
+ // (optional) Custom tsconfig.json path, default: undefined.
70
+ // Uncomment the following line to edit.
71
+ //tsconfigFilePath: undefined,
72
+ // (optional)Lint checks to run on the bundled output.
73
+ // Uncomment the following line to edit.
74
+ //checks:{ checkAnonymous: false, checkDefaultExports: false, checkNpmInstalled: false }.
75
+ // (optional) Minify the bundled output.
76
+ // Pass true for default minification, or an object with custom MinifyOptions.
77
+ // Uncomment the following line to edit.
78
+ //minify: false,
79
+ },
80
+ ],
81
+ // NOTE: the following options apply to all entry points.
82
+ // ----------------------------------------------------------
83
+ // (optional) Output directory, default: dist.
84
+ // Uncomment the following line to edit.
85
+ //outDir: "dist",
86
+ // (optional) Allow susee to update your package.json, default: false.
87
+ // Uncomment the following line to edit.
88
+ //allowUpdatePackageJson: false,
89
+ };
90
+ export default config;
91
+ `.trim();
92
+ async function getPackageType() {
93
+ const pkgPath = path.resolve(process.cwd(), 'package.json');
94
+ const _pkg = await fs.promises.readFile(pkgPath, 'utf8');
95
+ const pkg = JSON.parse(_pkg);
96
+ return pkg.type === 'module' ? 'esm' : 'commonjs';
97
+ }
98
+ async function cliInit() {
99
+ const rl = readline.createInterface({
100
+ input: process.stdin,
101
+ output: process.stdout
102
+ });
103
+ const is_ts = await rl.question('Is TypeScript Project(y/n) : ');
104
+ const isTs = !!(is_ts === 'y' || is_ts === 'Y' || is_ts === '');
105
+ rl.close();
106
+ let configFile = '';
107
+ let str = '';
108
+ if (isTs) {
109
+ configFile = 'susee.config.ts';
110
+ str = tsFileText;
111
+ }
112
+ else {
113
+ str = jsFileText;
114
+ const pkgType = await getPackageType();
115
+ switch (pkgType) {
116
+ case 'commonjs':
117
+ configFile = 'susee.config.mjs';
118
+ break;
119
+ case 'esm':
120
+ configFile = 'susee.config.js';
121
+ break;
122
+ }
123
+ }
124
+ const configFilePath = path.resolve(process.cwd(), configFile);
125
+ if (fs.existsSync(configFilePath))
126
+ await fs.promises.unlink(configFilePath);
127
+ await fs.promises.writeFile(configFilePath, str);
128
+ logInfo(`Done! Susee config file ${configFile} is created at project root.`);
129
+ }
130
+ /**
131
+ * Finds the path of the susee.config file if it exists.
132
+ * It checks for the existence of "susee.config.ts", "susee.config.js", and "susee.config.mjs" in the current working directory.
133
+ * The first file found is returned.
134
+ * @returns {string | undefined} - path to the susee.config file or undefined if it does not exist.
135
+ */
136
+ const getSuseeConfigPath = () => {
137
+ const fileNames = [
138
+ 'susee.config.ts',
139
+ 'susee.config.js',
140
+ 'susee.config.mjs'
141
+ ];
142
+ let configFile;
143
+ for (const file of fileNames) {
144
+ const filePath = path.resolve(process.cwd(), file);
145
+ if (fs.existsSync(filePath)) {
146
+ configFile = filePath;
147
+ break;
148
+ }
149
+ }
150
+ return configFile;
151
+ };
152
+ /**
153
+ * Checks if the given entries have at least one entry and if there are any duplicate export paths.
154
+ * If there are no entries, it will exit with code 1 and print an error message.
155
+ * If there are any duplicate export paths, it will exit with code 1 and print an error message.
156
+ * It will also check if each entry file exists, if not, it will exit with code 1 and print an error message.
157
+ * @param {EntryPoint[]} entries - array of entry points
158
+ */
159
+ function checkEntries(entries) {
160
+ if (entries.length < 1) {
161
+ const info = 'At least one entry required';
162
+ const cause = 'No entry found in susee.config file or build options';
163
+ logError(info, cause, true);
164
+ }
165
+ const objectStore = {};
166
+ const duplicateExportPaths = [];
167
+ for (const obj of entries) {
168
+ const value = obj.exportPath;
169
+ if (objectStore[value]) {
170
+ duplicateExportPaths.push(`"${value}"`);
171
+ }
172
+ else {
173
+ objectStore[value] = true;
174
+ }
175
+ }
176
+ if (duplicateExportPaths.length > 0) {
177
+ const info = 'Found duplicated export paths/path';
178
+ const cause = `Duplicate export paths/path (${duplicateExportPaths.join(',')}) found in your susee.config file or build options , that will error for bundled output`;
179
+ logError(info, cause, true);
180
+ }
181
+ for (const obj of entries) {
182
+ if (!fs.existsSync(path.resolve(process.cwd(), obj.entry))) {
183
+ const info = 'Entry file error';
184
+ const cause = `Entry file ${obj.entry} dose not exists.`;
185
+ logError(info, cause, true);
186
+ }
187
+ }
188
+ }
189
+ /**
190
+ * Generates normalized build options from the user config.
191
+ * It validates entry points, applies default values, removes duplicate formats,
192
+ * resolves the output directory for each export path, and keeps duplicate declaration handling fail-fast.
193
+ * @param {SuSeeConfig} config - raw susee configuration object.
194
+ * @returns {BuildOptions} normalized build options for the compiler.
195
+ */
196
+ function generateBuildOptions(config) {
197
+ const outDir = config.outDir ?? 'dist';
198
+ const points = [];
199
+ checkEntries(config.entryPoints);
200
+ for (const ent of config.entryPoints) {
201
+ const entry = ent.entry;
202
+ const exportPath = ent.exportPath;
203
+ const format = ent.format ? [...new Set(ent.format)] : ['esm'];
204
+ const tsconfigFilePath = ent.tsconfigFilePath ?? undefined;
205
+ const outputDirectoryPath = ent.exportPath === '.' ? outDir : `${outDir}${ent.exportPath.slice(1)}`;
206
+ const checks = {
207
+ checkAnonymous: ent.checks?.checkAnonymous ?? false,
208
+ checkDefaultExports: ent.checks?.checkDefaultExports ?? false,
209
+ checkNpmInstalled: ent.checks?.checkNpmInstalled ?? false
210
+ };
211
+ const minify = ent.minify ?? false;
212
+ points.push({
213
+ entry,
214
+ exportPath,
215
+ format,
216
+ outputDirectoryPath,
217
+ tsconfigFilePath,
218
+ checks,
219
+ minify
220
+ });
221
+ }
222
+ return {
223
+ buildEntryPoints: points,
224
+ updatePackage: config.allowUpdatePackageJson ?? false,
225
+ outDir
226
+ };
227
+ }
228
+ /**
229
+ * Loads the susee config file from the current working directory and converts it into build options.
230
+ * If no supported config file is found, it returns `undefined`.
231
+ * @returns {Promise<BuildOptions | undefined>} normalized build options or undefined when no config file exists.
232
+ */
233
+ async function finalSuseeConfig() {
234
+ const configPath = getSuseeConfigPath();
235
+ if (configPath) {
236
+ const _default = await import(configPath);
237
+ const config = _default.default;
238
+ return generateBuildOptions(config);
239
+ }
240
+ }
241
+ function fail(message) {
242
+ const info = message;
243
+ const cause = '';
244
+ logError(info, cause, true);
245
+ }
246
+ function isFile(entry) {
247
+ const exts = [
248
+ '.js',
249
+ '.ts',
250
+ '.mts',
251
+ '.mjs',
252
+ '.cjs',
253
+ '.cts',
254
+ '.tsx',
255
+ '.jsx'
256
+ ];
257
+ return exts.includes(path.extname(entry));
258
+ }
259
+ function isEmptyObject(entry) {
260
+ return typeof entry === 'object' && !Array.isArray(entry) && Object.keys(entry).length === 0;
261
+ }
262
+ function parseBooleanFlag(flag, value) {
263
+ if (value === 'true')
264
+ return true;
265
+ if (value === 'false')
266
+ return false;
267
+ fail(`Type of ${flag} must be boolean.`);
268
+ }
269
+ function parseArgs(argv) {
270
+ const opts = {};
271
+ for (let index = 0; index < argv.length; index += 1) {
272
+ const argument = argv[index];
273
+ if (index === 0 && !argument.startsWith('--') && isFile(argument)) {
274
+ opts.entry = argument;
275
+ continue;
276
+ }
277
+ const [flag, inlineValue] = argument.split('=', 2);
278
+ const nextValue = argv[index + 1];
279
+ const value = inlineValue ?? nextValue;
280
+ switch (flag) {
281
+ case '--entry':
282
+ if (!value || value.startsWith('--'))
283
+ fail('Entry point required.');
284
+ if (opts.entry && isFile(opts.entry))
285
+ fail('Entry point already exists.');
286
+ opts.entry = value;
287
+ if (inlineValue === undefined) {
288
+ index += 1;
289
+ }
290
+ break;
291
+ case '--outdir':
292
+ if (!value || value.startsWith('--'))
293
+ fail('Output directory required.');
294
+ opts.outDir = value;
295
+ if (inlineValue === undefined) {
296
+ index += 1;
297
+ }
298
+ break;
299
+ case '--format':
300
+ if (value !== 'cjs' && value !== 'commonjs' && value !== 'esm') {
301
+ fail('Format must be cjs, commonjs, esm, both.');
302
+ }
303
+ opts.format = value === 'cjs' || value === 'commonjs' ? ['commonjs'] : value === 'esm' ? ['esm'] : value === 'both' ? ['commonjs', 'esm'] : undefined;
304
+ if (inlineValue === undefined) {
305
+ index += 1;
306
+ }
307
+ break;
308
+ case '--tsconfig':
309
+ if (!value || value.startsWith('--'))
310
+ fail('Tsconfig path required.');
311
+ opts.tsconfig = value;
312
+ if (inlineValue === undefined) {
313
+ index += 1;
314
+ }
315
+ break;
316
+ case '--allow-update':
317
+ if (inlineValue !== undefined) {
318
+ opts.allowUpdate = parseBooleanFlag('allow update', inlineValue);
319
+ }
320
+ else if (nextValue === 'true' || nextValue === 'false') {
321
+ opts.allowUpdate = parseBooleanFlag('allow update', nextValue);
322
+ index += 1;
323
+ }
324
+ else {
325
+ opts.allowUpdate = true;
326
+ }
327
+ break;
328
+ case '--check':
329
+ if (inlineValue !== undefined) {
330
+ opts.check = parseBooleanFlag('check', inlineValue);
331
+ }
332
+ else if (nextValue === 'true' || nextValue === 'false') {
333
+ opts.check = parseBooleanFlag('check', nextValue);
334
+ index += 1;
335
+ }
336
+ else {
337
+ opts.check = true;
338
+ }
339
+ break;
340
+ case '--minify':
341
+ if (inlineValue !== undefined) {
342
+ opts.minify = parseBooleanFlag('minify', inlineValue);
343
+ }
344
+ else if (nextValue === 'true' || nextValue === 'false') {
345
+ opts.minify = parseBooleanFlag('minify', nextValue);
346
+ index += 1;
347
+ }
348
+ else {
349
+ opts.minify = true;
350
+ }
351
+ break;
352
+ }
353
+ }
354
+ return opts;
355
+ }
356
+ function cliConfig(argv) {
357
+ const cliOpts = parseArgs(argv);
358
+ if (isEmptyObject(cliOpts))
359
+ return undefined;
360
+ const point = {
361
+ entry: cliOpts.entry ?? '',
362
+ exportPath: '.',
363
+ format: cliOpts.format ?? ['esm'],
364
+ tsconfigFilePath: cliOpts.tsconfig ?? undefined,
365
+ minify: cliOpts.minify ?? false,
366
+ checks: {
367
+ checkAnonymous: cliOpts.check ? true : false,
368
+ checkDefaultExports: cliOpts.check ? true : false,
369
+ checkNpmInstalled: cliOpts.check ? true : false
370
+ }
371
+ };
372
+ if (point.entry === '')
373
+ return undefined;
374
+ const config = {
375
+ entryPoints: [point],
376
+ outDir: cliOpts.outDir ?? 'dist',
377
+ allowUpdatePackageJson: cliOpts.allowUpdate ?? false
378
+ };
379
+ return config;
380
+ }
381
+ //src/cli/print_help.ts
382
+ function printHelp() {
383
+ console.log(`Susee CLI.
384
+ Usage:
385
+ susee build Build using susee.config.{ts,js,mjs}
386
+ susee init Generate susee.config.{ts,js,mjs}
387
+ susee --help Show this message
388
+ susee build <entry> [options] Build from a single entry file
389
+ Options:
390
+ --entry <path> Entry file (optional if provided as positional <entry>)
391
+ --outdir <path> Output directory. (default to "dist")
392
+ --format <cjs|commonjs|esm> Output module format. (default to ["esm"])
393
+ --tsconfig <path> Custom tsconfig path. (default to undefined)
394
+ --allow-update[=true|false] Enable package.json update. (default to false)
395
+ --minify[=true|false] Enable minify to output JS code.(default to false)
396
+ --check[=true|false] Enable minify to output JS code.(default to false)
397
+ Notes:
398
+ Duplicate top-level declarations fail the build with file and location output.
399
+ Rename conflicting declarations in source files before bundling.
400
+ Examples:
401
+ susee build src/index.ts --outdir dist
402
+ susee build src/index.ts --format commonjs
403
+ susee build --entry src/index.ts --format esm --tsconfig tsconfig.build.json
404
+ susee build src/index.ts --profile
405
+ `);
406
+ }
407
+ //src/helpers/files.ts
408
+ var files;
409
+ (function (files_1) {
410
+ const root = process.cwd();
411
+ function resolvePath(pathStr) {
412
+ return path.resolve(root, pathStr);
413
+ }
414
+ files_1.resolvePath = resolvePath;
415
+ function relativePath(pathStr) {
416
+ return path.relative(root, pathStr);
417
+ }
418
+ files_1.relativePath = relativePath;
419
+ function joinPath(...paths) {
420
+ return path.join(...paths);
421
+ }
422
+ files_1.joinPath = joinPath;
423
+ function existsPath(pathStr) {
424
+ return fs.existsSync(resolvePath(pathStr));
425
+ }
426
+ files_1.existsPath = existsPath;
427
+ async function deleteFile(filePath) {
428
+ if (existsPath(filePath)) {
429
+ await fs.promises.unlink(filePath);
430
+ }
431
+ }
432
+ files_1.deleteFile = deleteFile;
433
+ async function readFile(filePath) {
434
+ if (!existsPath(filePath)) {
435
+ console.error(tcolor.magenta(`> ${filePath} does not exists `));
436
+ process.exit(1);
437
+ }
438
+ filePath = resolvePath(filePath);
439
+ const readContent = await fs.promises.readFile(filePath);
440
+ return {
441
+ str: readContent.toString('utf8'),
442
+ bytes: readContent.byteLength
443
+ };
444
+ }
445
+ files_1.readFile = readFile;
446
+ async function readJsonFile(filePath) {
447
+ const read = await readFile(filePath);
448
+ return JSON.parse(read.str);
449
+ }
450
+ files_1.readJsonFile = readJsonFile;
451
+ async function createDirectory(dirPath) {
452
+ dirPath = resolvePath(dirPath);
453
+ if (!existsPath(dirPath)) {
454
+ await fs.promises.mkdir(dirPath, { recursive: true });
455
+ }
456
+ }
457
+ files_1.createDirectory = createDirectory;
458
+ function parentPath(filePath) {
459
+ return path.dirname(resolvePath(filePath));
460
+ }
461
+ files_1.parentPath = parentPath;
462
+ async function writeFile(filePath, content) {
463
+ if (existsPath(filePath))
464
+ await deleteFile(filePath);
465
+ await createDirectory(parentPath(filePath));
466
+ filePath = resolvePath(filePath);
467
+ await fs.promises.writeFile(filePath, content);
468
+ }
469
+ files_1.writeFile = writeFile;
470
+ async function clearFolder(folderPath) {
471
+ folderPath = resolvePath(folderPath);
472
+ try {
473
+ const entries = await fs.promises.readdir(folderPath, { withFileTypes: true });
474
+ await Promise.all(entries.map((entry) => fs.promises.rm(path.join(folderPath, entry.name), { recursive: true })));
475
+ }
476
+ catch (error) {
477
+ // biome-ignore lint/suspicious/noExplicitAny: error code
478
+ if (error.code !== 'ENOENT') {
479
+ throw error;
480
+ }
481
+ }
482
+ }
483
+ files_1.clearFolder = clearFolder;
484
+ // -------------------------------------------------------------------------------------------//
485
+ const isCjs = (files) => files.commonjs && files.commonjsTypes;
486
+ const isEsm = (files) => files.esm && files.esmTypes;
487
+ function getExports(files, exportPath) {
488
+ return isCjs(files) && isEsm(files) ? { [exportPath]: {
489
+ import: {
490
+ types: `./${path.relative(process.cwd(), files.esmTypes)}`,
491
+ default: `./${path.relative(process.cwd(), files.esm)}`
492
+ },
493
+ require: {
494
+ types: `./${path.relative(process.cwd(), files.commonjsTypes)}`,
495
+ default: `./${path.relative(process.cwd(), files.commonjs)}`
496
+ }
497
+ } } : isCjs(files) && !isEsm(files) ? { [exportPath]: { require: {
498
+ types: `./${path.relative(process.cwd(), files.commonjsTypes)}`,
499
+ default: `./${path.relative(process.cwd(), files.commonjs)}`
500
+ } } } : !isCjs(files) && isEsm(files) ? { [exportPath]: { import: {
501
+ types: `./${path.relative(process.cwd(), files.esmTypes)}`,
502
+ default: `./${path.relative(process.cwd(), files.esm)}`
503
+ } } } : {};
504
+ }
505
+ async function writePackageJson(files, exportPath) {
506
+ let isMain = true;
507
+ if (exportPath !== '.') {
508
+ isMain = false;
509
+ }
510
+ const pkgFile = resolvePath('package.json');
511
+ const pkgtext = await readJsonFile(pkgFile);
512
+ let { name, version, description, main, module, type, types, exports, ...rest } = pkgtext;
513
+ type = 'module';
514
+ let _main = {};
515
+ let _module = {};
516
+ let _types = {};
517
+ let _exports = {};
518
+ if (isMain) {
519
+ _main = files.main ? { main: path.relative(process.cwd(), files.main) } : {};
520
+ _module = files.module ? { module: path.relative(process.cwd(), files.module) } : {};
521
+ _types = files.types ? { types: path.relative(process.cwd(), files.types) } : {};
522
+ _exports = { exports: { ...getExports(files, exportPath) } };
523
+ }
524
+ else {
525
+ _main = main ? { main } : {};
526
+ _module = module ? { module } : {};
527
+ _types = types ? { types } : {};
528
+ const normalizedExports = exports && typeof exports === 'object' && !Array.isArray(exports) ? { ...exports } : {};
529
+ _exports = { exports: {
530
+ ...normalizedExports,
531
+ ...getExports(files, exportPath)
532
+ } };
533
+ }
534
+ const pkgJson = {
535
+ name,
536
+ version,
537
+ description,
538
+ type,
539
+ ..._main,
540
+ ..._types,
541
+ ..._module,
542
+ ..._exports,
543
+ ...rest
544
+ };
545
+ await writeFile(pkgFile, JSON.stringify(pkgJson, null, 2));
546
+ }
547
+ files_1.writePackageJson = writePackageJson;
548
+ })(files || (files = {}));
549
+ /**
550
+ * Normalizes TypeScript compiler options when JSX compilation is requested.
551
+ *
552
+ * For JSX input, this validates that the source imports either React runtime
553
+ * modules or the configured `jsxImportSource` runtime package. When validation
554
+ * passes, it enables DOM libs and defaults `jsx` to `ReactJSX` if unset.
555
+ *
556
+ * @param {string} sourceCode - Source text to inspect for JSX runtime imports.
557
+ * @param {ts6.CompilerOptions} compilerOptions - User-provided compiler options.
558
+ * @param {boolean} isJsx - Whether JSX mode is enabled for this compilation.
559
+ * @returns {ts6.CompilerOptions} Compiler options to pass into program creation.
560
+ */
561
+ function jsxCompilerOptions(sourceCode, compilerOptions, isJsx) {
562
+ if (!isJsx) {
563
+ return compilerOptions;
564
+ }
565
+ const reactRegexp = /import\s+(?:.*?)\s+from\s+(?:"react"|"react\/.*"|"react-dom\/.*"|"react-dom")/gm;
566
+ if (!reactRegexp.test(sourceCode)) {
567
+ if (!compilerOptions.jsxImportSource) {
568
+ console.error('[jsx-runtime-error]:\nJSX syntax found in bundled code,but its not react runtime,you need to be set jsxImportSource in tsconfig.');
569
+ process.exit(1);
570
+ }
571
+ const txt = compilerOptions.jsxImportSource;
572
+ const pattern = `import\\s+(?:.*?)\\s+from\\s+("${txt}"|"${txt}\\/.*")`;
573
+ const re = new RegExp(pattern, 'gm');
574
+ if (!re.test(sourceCode)) {
575
+ 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.`');
576
+ process.exit(1);
577
+ }
578
+ }
579
+ // oxlint-disable-next-line no-unused-vars
580
+ const { jsx, lib, ...rest } = compilerOptions;
581
+ const _jsx = jsx ?? ts6.JsxEmit.ReactJSX;
582
+ return {
583
+ lib: [
584
+ 'dom',
585
+ 'dom.iterable',
586
+ 'esnext'
587
+ ],
588
+ jsx: _jsx,
589
+ ...rest
590
+ };
591
+ }
592
+ /**
593
+ * Creates a ts.CompilerHost that can be used with the typescript compiler.
594
+ * This host is designed to be used with in-memory compilation and will
595
+ * return the source file for the given fileName and will write all output
596
+ * files to the createdFiles object.
597
+ * @param {string} sourceCode - the source code to compile
598
+ * @param {string} fileName - the name of the file to compile
599
+ * @returns {{createdFiles: Record<string, string>, host: ts.CompilerHost}}
600
+ */
601
+ function createHost(sourceCode, fileName) {
602
+ const createdFiles = {};
603
+ const host = {
604
+ getSourceFile: (file, languageVersion) => {
605
+ if (file === fileName) {
606
+ return ts6.createSourceFile(file, sourceCode, languageVersion);
607
+ }
608
+ return undefined;
609
+ },
610
+ writeFile: (fileName, contents) => {
611
+ createdFiles[fileName] = contents;
612
+ },
613
+ getDefaultLibFileName: (options) => ts6.getDefaultLibFilePath(options),
614
+ getCurrentDirectory: () => '',
615
+ getDirectories: () => [],
616
+ fileExists: (file) => file === fileName,
617
+ readFile: (file) => file === fileName ? sourceCode : undefined,
618
+ getCanonicalFileName: (file) => file,
619
+ useCaseSensitiveFileNames: () => true,
620
+ getNewLine: () => '\n'
621
+ };
622
+ return {
623
+ createdFiles,
624
+ host
625
+ };
626
+ }
627
+ function suseeCompiler({ sourceCode, fileName, compilerOptions, isJsx = false }) {
628
+ compilerOptions = jsxCompilerOptions(sourceCode, compilerOptions, isJsx);
629
+ // create host
630
+ const _host = createHost(sourceCode, fileName);
631
+ const createdFiles = _host.createdFiles;
632
+ const host = _host.host;
633
+ const program = ts6.createProgram([fileName], compilerOptions, host);
634
+ program.emit();
635
+ let dts;
636
+ let map;
637
+ let code = '';
638
+ let file_name = '';
639
+ let out_dir = '';
640
+ for (const key of Object.keys(createdFiles)) {
641
+ if (key.endsWith('.js'))
642
+ code = createdFiles[key];
643
+ if (key.endsWith('.d.ts'))
644
+ dts = createdFiles[key];
645
+ if (key.endsWith('.js.map'))
646
+ map = createdFiles[key];
647
+ file_name = path.basename(key).split('.')[0];
648
+ out_dir = path.dirname(key);
649
+ }
650
+ return {
651
+ code,
652
+ file_name,
653
+ out_dir,
654
+ dts,
655
+ map
656
+ };
657
+ }
658
+ //src/compiler/tsoptions.ts
659
+ /**
660
+ * Get the path of the configuration file.
661
+ * If customConfigPath is provided and exists, use it.
662
+ * If customConfigPath is not provided or does not exist, use the default configuration file.
663
+ * @param {string | undefined} customConfigPath path of the custom configuration file.
664
+ * @returns {string | undefined} path of the configuration file or undefined if customConfigPath does not exist.
665
+ */
666
+ function getTsConfigPath(customConfigPath) {
667
+ let config_path;
668
+ if (customConfigPath) {
669
+ if (!ts6.sys.fileExists(ts6.sys.resolvePath(customConfigPath))) {
670
+ console.error(`> ${tcolor.magenta(`Given custom file ${customConfigPath} does not exists`)}`);
671
+ ts6.sys.exit(1);
672
+ }
673
+ config_path = customConfigPath;
674
+ return config_path;
675
+ }
676
+ else {
677
+ config_path = ts6.findConfigFile(ts6.sys.getCurrentDirectory(), ts6.sys.fileExists);
678
+ return config_path;
679
+ }
680
+ }
681
+ /**
682
+ * Get the TypeScript compiler options for susee bundler.
683
+ * @param {string | undefined} customConfigPath path of the custom configuration file.
684
+ */
685
+ function getCompilerOptions(customConfigPath) {
686
+ let tsconfig_opts;
687
+ const config_path = getTsConfigPath(customConfigPath);
688
+ if (config_path) {
689
+ const config = ts6.readConfigFile(config_path, ts6.sys.readFile);
690
+ const basePath = path.dirname(config_path);
691
+ const parsed = ts6.parseJsonConfigFileContent(config.config, ts6.sys, basePath);
692
+ tsconfig_opts = { ...parsed.options };
693
+ }
694
+ const commonjs = (out_dir) => {
695
+ const _out = out_dir ? out_dir : 'dist';
696
+ if (tsconfig_opts !== undefined) {
697
+ // oxlint-disable-next-line no-unused-vars
698
+ const { rootDir, outDir, module, allowJs, declarationDir, ...rest } = tsconfig_opts;
699
+ return {
700
+ outDir: _out,
701
+ module: ts6.ModuleKind.CommonJS,
702
+ allowJs: true,
703
+ ...rest
704
+ };
705
+ }
706
+ else {
707
+ return {
708
+ outDir: _out,
709
+ module: ts6.ModuleKind.CommonJS,
710
+ target: ts6.ScriptTarget.Latest
711
+ };
712
+ }
713
+ };
714
+ const esm = (out_dir) => {
715
+ const _out = out_dir ? out_dir : 'dist';
716
+ if (tsconfig_opts !== undefined) {
717
+ // oxlint-disable-next-line no-unused-vars
718
+ const { rootDir, outDir, module, allowJs, declarationDir, ...rest } = tsconfig_opts;
719
+ return {
720
+ outDir: _out,
721
+ module: ts6.ModuleKind.ES2020,
722
+ allowJs: true,
723
+ ...rest
724
+ };
725
+ }
726
+ else {
727
+ return {
728
+ outDir: _out,
729
+ module: ts6.ModuleKind.ES2020,
730
+ target: ts6.ScriptTarget.Latest
731
+ };
732
+ }
733
+ };
734
+ const defaultOptions = ts6.getDefaultCompilerOptions;
735
+ return {
736
+ commonjs,
737
+ esm,
738
+ defaultOptions
739
+ };
740
+ }
741
+ //src/bundler.ts
742
+ function bundler(point) {
743
+ const bundledCodeCache = new WeakMap();
744
+ const root = process.cwd();
745
+ let bundledCode = bundledCodeCache.get(point);
746
+ if (!bundledCode) {
747
+ bundledCode = suseeBundler(point.entry, root, point.checks).bundledCode;
748
+ bundledCodeCache.set(point, bundledCode);
749
+ }
750
+ return bundledCode;
751
+ }
752
+ //src/helpers/minify.ts
753
+ async function oxcMinify(fileName, code, point) {
754
+ const options = typeof point.minify === 'object' && typeof point.minify !== 'boolean' ? point.minify.options : undefined;
755
+ const result = await minify(fileName, code, options);
756
+ return result.code;
757
+ }
758
+ //src/compiler/index.ts
759
+ //import { utils } from "../helpers/utilities.js";
760
+ /**
761
+ * Checks if the given code string contains JSX syntax.
762
+ * @param code The content of the file as a string.
763
+ * @returns true if the file contains JSX, false otherwise.
764
+ */
765
+ function isJsxContent(code) {
766
+ const sourceFile = ts6.createSourceFile('file.tsx', code, ts6.ScriptTarget.Latest,
767
+ /*setParentNodes*/
768
+ true, ts6.ScriptKind.TSX);
769
+ let containsJsx = false;
770
+ function visitor(node) {
771
+ // Check for JSX Elements, Self Closing Elements, or JSX Fragments
772
+ if (ts6.isJsxElement(node) || ts6.isJsxSelfClosingElement(node) || ts6.isJsxFragment(node)) {
773
+ containsJsx = true;
774
+ return;
775
+ }
776
+ ts6.forEachChild(node, visitor);
777
+ }
778
+ visitor(sourceFile);
779
+ return containsJsx;
780
+ }
781
+ /**
782
+ * Compiler for the JavaScript API.
783
+ * It bundles each configured entry point, emits CommonJS and ESM outputs,
784
+ * and optionally updates package export metadata.
785
+ */
786
+ class Compiler {
787
+ _files;
788
+ _object;
789
+ /**
790
+ * Creates a compiler instance with normalized build options.
791
+ * @param {BuildOptions} object - build options generated from the susee config.
792
+ */
793
+ constructor(object) {
794
+ this._object = object;
795
+ this._files = {
796
+ commonjs: undefined,
797
+ commonjsTypes: undefined,
798
+ esm: undefined,
799
+ esmTypes: undefined,
800
+ main: undefined,
801
+ module: undefined,
802
+ types: undefined
803
+ };
804
+ }
805
+ _update() {
806
+ return this._object.updatePackage;
807
+ }
808
+ async _commonjs(point, bundledCode) {
809
+ const isMain = point.exportPath === '.';
810
+ const opts = getCompilerOptions(point.tsconfigFilePath);
811
+ const compilerOptions = opts.commonjs(point.outputDirectoryPath);
812
+ const is_jsx = isJsxContent(bundledCode);
813
+ const compiled = suseeCompiler({
814
+ sourceCode: bundledCode,
815
+ fileName: point.entry,
816
+ compilerOptions,
817
+ isJsx: is_jsx
818
+ });
819
+ let compiledCode = compiled.code;
820
+ const mainFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.cjs`);
821
+ const dtsFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.d.cts`);
822
+ const mapFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.cjs.map`);
823
+ // replace source mapping url
824
+ compiledCode = compiledCode.replace(new RegExp(`${compiled.file_name}.js.map`, 'gm'), `${compiled.file_name}.cjs.map`);
825
+ if (point.minify) {
826
+ compiledCode = await oxcMinify(`${compiled.file_name}.cjs`, compiledCode, point);
827
+ }
828
+ // if allow update create file object
829
+ if (this._update()) {
830
+ this._files.commonjs = mainFilePath;
831
+ if (compiled.dts) {
832
+ this._files.commonjsTypes = dtsFilePath;
833
+ }
834
+ if (isMain && point.format.includes('commonjs')) {
835
+ if (this._files.commonjs)
836
+ this._files.main = this._files.commonjs;
837
+ if (this._files.commonjsTypes)
838
+ this._files.types = this._files.commonjsTypes;
839
+ }
840
+ }
841
+ await files.writeFile(mainFilePath, compiledCode);
842
+ if (compiled.dts)
843
+ await files.writeFile(dtsFilePath, compiled.dts);
844
+ if (compiled.map)
845
+ await files.writeFile(mapFilePath, compiled.map);
846
+ }
847
+ async _esm(point, bundledCode) {
848
+ const isMain = point.exportPath === '.';
849
+ const opts = getCompilerOptions(point.tsconfigFilePath);
850
+ const compilerOptions = opts.esm(point.outputDirectoryPath);
851
+ const is_jsx = isJsxContent(bundledCode);
852
+ const compiled = suseeCompiler({
853
+ sourceCode: bundledCode,
854
+ fileName: point.entry,
855
+ compilerOptions,
856
+ isJsx: is_jsx
857
+ });
858
+ let compiledCode = compiled.code;
859
+ const mainFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.mjs`);
860
+ const dtsFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.d.mts`);
861
+ const mapFilePath = files.joinPath(compiled.out_dir, `${compiled.file_name}.mjs.map`);
862
+ compiledCode = compiledCode.replace(new RegExp(`${compiled.file_name}.js.map`, 'gm'), `${compiled.file_name}.mjs.map`);
863
+ if (point.minify) {
864
+ compiledCode = await oxcMinify(`${compiled.file_name}.mjs`, compiledCode, point);
865
+ }
866
+ if (this._update()) {
867
+ this._files.esm = mainFilePath;
868
+ if (compiled.dts) {
869
+ this._files.esmTypes = dtsFilePath;
870
+ }
871
+ if (isMain && this._files.esm) {
872
+ this._files.module = this._files.esm;
873
+ }
874
+ }
875
+ await files.writeFile(mainFilePath, compiledCode);
876
+ if (compiled.dts)
877
+ await files.writeFile(dtsFilePath, compiled.dts);
878
+ if (compiled.map)
879
+ await files.writeFile(mapFilePath, compiled.map);
880
+ }
881
+ /**
882
+ * Clears the output directory and compiles all configured entry points.
883
+ * It also updates package.json export fields when package updates are enabled.
884
+ * @returns {Promise<void>}
885
+ */
886
+ async compile() {
887
+ await files.clearFolder(this._object.outDir);
888
+ for (const point of this._object.buildEntryPoints) {
889
+ const bundleCode = bundler(point);
890
+ for (const format of point.format) {
891
+ switch (format) {
892
+ case 'commonjs':
893
+ await this._commonjs(point, bundleCode);
894
+ if (this._update()) {
895
+ await files.writePackageJson(this._files, point.exportPath);
896
+ }
897
+ break;
898
+ case 'esm':
899
+ await this._esm(point, bundleCode);
900
+ if (this._update()) {
901
+ await files.writePackageJson(this._files, point.exportPath);
902
+ }
903
+ break;
904
+ }
905
+ }
906
+ }
907
+ }
908
+ }
909
+ //src/build.ts
910
+ /**
911
+ * Run a Susee build.
912
+ *
913
+ * Resolution order:
914
+ * 1. Use `options` when provided.
915
+ * 2. Otherwise try loading root config via `finalSuseeConfig()`.
916
+ *
917
+ * If neither source is available, this logs an error and exits with code 1.
918
+ */
919
+ async function build(options) {
920
+ const buildTime = new LogTimer();
921
+ let buildOptions = {};
922
+ const _buildOptions = await finalSuseeConfig();
923
+ if (!options && !_buildOptions) {
924
+ const info = 'Required build options or susee config file at root.You can use `npx susee init` to create susee config file at root';
925
+ const cause = 'No build options or susee config file at root.';
926
+ logError(info, cause, true);
927
+ }
928
+ if (options) {
929
+ buildOptions = generateBuildOptions(options);
930
+ }
931
+ else if (_buildOptions) {
932
+ buildOptions = _buildOptions;
933
+ }
934
+ const compiler = new Compiler(buildOptions);
935
+ await compiler.compile();
936
+ buildTime.buildTime();
937
+ }
938
+ //src/cli/index.ts
939
+ async function getPackageVersion() {
940
+ const pkgPath = path.resolve(process.cwd(), 'package.json');
941
+ const _pkg = await fs.promises.readFile(pkgPath, 'utf8');
942
+ const pkg = JSON.parse(_pkg);
943
+ return pkg.version;
944
+ }
945
+ function errorLog() {
946
+ printHelp();
947
+ const info = 'Unknown CLI usage';
948
+ const cause = '';
949
+ logError(info, cause, true);
950
+ }
951
+ async function cliBuild() {
952
+ const args = process.argv.slice(2);
953
+ const version = await getPackageVersion();
954
+ if (args.length === 0) {
955
+ errorLog();
956
+ }
957
+ else if (args.length === 1) {
958
+ const arg0 = args[0];
959
+ switch (arg0) {
960
+ case 'build':
961
+ await build();
962
+ break;
963
+ case 'init':
964
+ await cliInit();
965
+ break;
966
+ case '--version':
967
+ case '-v':
968
+ logInfo(`susee v${version}`);
969
+ break;
970
+ case '--help':
971
+ case '-h':
972
+ printHelp();
973
+ break;
974
+ default:
975
+ printHelp();
976
+ break;
977
+ }
978
+ }
979
+ else if (args.length > 1 && args[0] === 'build') {
980
+ const restArgs = args.slice(1);
981
+ const config = cliConfig(restArgs);
982
+ await build(config);
983
+ }
984
+ else {
985
+ errorLog();
986
+ }
987
+ }
988
+ cliBuild();
989
+ //# sourceMappingURL=index.mjs.map