tsdown 0.13.2 → 0.13.4

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.
@@ -1,4 +1,4 @@
1
- import { debounce, generateColor, logger, noop, prettyFormat, prettyName, resolveComma, resolveRegex, slash, toArray } from "./logger-6IV2T7t1.mjs";
1
+ import { LogLevels, createLogger, debounce, generateColor, globalLogger, noop, prettyFormat, prettyName, resolveComma, resolveRegex, slash, toArray } from "./logger-CGMSjTLn.mjs";
2
2
  import { builtinModules } from "node:module";
3
3
  import path, { dirname, normalize, sep } from "node:path";
4
4
  import process from "node:process";
@@ -11,18 +11,20 @@ import child_process from "node:child_process";
11
11
  import { access, chmod, cp, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
12
12
  import { tmpdir } from "node:os";
13
13
  import { promisify } from "node:util";
14
- import Debug from "debug";
14
+ import debug from "debug";
15
+ import coerce from "semver/functions/coerce.js";
16
+ import satisfies from "semver/functions/satisfies.js";
15
17
  import { glob } from "tinyglobby";
16
18
  import { RE_CSS, RE_DTS, RE_JS } from "rolldown-plugin-dts/filename";
17
19
  import { createHooks } from "hookable";
18
- import { up } from "empathic/package";
20
+ import minVersion from "semver/ranges/min-version.js";
21
+ import { up } from "empathic/find";
22
+ import { up as up$1 } from "empathic/package";
23
+ import { loadConfig } from "unconfig";
19
24
  import { Buffer } from "node:buffer";
20
25
  import { brotliCompress, gzip } from "node:zlib";
21
26
  import readline from "node:readline";
22
- import minVersion from "semver/ranges/min-version.js";
23
27
  import { globalContext, invalidateContextFile } from "rolldown-plugin-dts/tsc-context";
24
- import { up as up$1 } from "empathic/find";
25
- import { loadConfig } from "unconfig";
26
28
 
27
29
  //#region src/utils/fs.ts
28
30
  function fsExists(path$1) {
@@ -64,7 +66,7 @@ function lowestCommonAncestor(...filepaths) {
64
66
 
65
67
  //#endregion
66
68
  //#region src/features/attw.ts
67
- const debug$6 = Debug("tsdown:attw");
69
+ const debug$8 = debug("tsdown:attw");
68
70
  const exec$1 = promisify(child_process.exec);
69
71
  /**
70
72
  * ATTW profiles.
@@ -106,18 +108,18 @@ function formatProblem(problem) {
106
108
  async function attw(options) {
107
109
  if (!options.attw) return;
108
110
  if (!options.pkg) {
109
- logger.warn("attw is enabled but package.json is not found");
111
+ options.logger.warn("attw is enabled but package.json is not found");
110
112
  return;
111
113
  }
112
114
  const { profile = "strict", level = "warn",...attwOptions } = options.attw === true ? {} : options.attw;
113
115
  const t = performance.now();
114
- debug$6("Running attw check");
116
+ debug$8("Running attw check");
115
117
  const tempDir = await mkdtemp(path.join(tmpdir(), "tsdown-attw-"));
116
118
  let attwCore;
117
119
  try {
118
120
  attwCore = await import("@arethetypeswrong/core");
119
121
  } catch {
120
- logger.error(`ATTW check requires ${blue`@arethetypeswrong/core`} to be installed.`);
122
+ options.logger.error(`ATTW check requires ${blue`@arethetypeswrong/core`} to be installed.`);
121
123
  return;
122
124
  }
123
125
  try {
@@ -140,21 +142,37 @@ async function attw(options) {
140
142
  const problemList = problems.map(formatProblem).join("\n");
141
143
  const problemMessage = `Are the types wrong problems found:\n${problemList}`;
142
144
  if (level === "error") throw new Error(problemMessage);
143
- logger.warn(problemMessage);
145
+ options.logger.warn(problemMessage);
144
146
  }
145
- } else logger.success(`No Are the types wrong problems found`, dim`(${Math.round(performance.now() - t)}ms)`);
147
+ } else options.logger.success(`No Are the types wrong problems found`, dim`(${Math.round(performance.now() - t)}ms)`);
146
148
  } catch (error) {
147
- logger.error("ATTW check failed:", error);
148
- debug$6("Found errors, setting exit code to 1");
149
+ options.logger.error("ATTW check failed:", error);
150
+ debug$8("Found errors, setting exit code to 1");
149
151
  process.exitCode = 1;
150
152
  } finally {
151
153
  await fsRemove(tempDir);
152
154
  }
153
155
  }
154
156
 
157
+ //#endregion
158
+ //#region src/features/cjs.ts
159
+ /**
160
+ * If the config includes the `cjs` format and
161
+ * one of its target >= node 23.0.0 / 22.12.0,
162
+ * warn the user about the deprecation of CommonJS.
163
+ */
164
+ function warnLegacyCJS(config) {
165
+ if (!config.format.includes("cjs") || !config.target) return;
166
+ const legacy = config.target.some((t) => {
167
+ const version = coerce(t.split("node")[1]);
168
+ return version && satisfies(version, ">=23.0.0 || >=22.12.0");
169
+ });
170
+ if (legacy) config.logger.warnOnce("We recommend using the ESM format instead of CommonJS.\nThe ESM format is compatible with modern platforms and runtimes, and most new libraries are now distributed only in ESM format.\nLearn more at https://nodejs.org/en/learn/modules/publishing-a-package#how-did-we-get-here");
171
+ }
172
+
155
173
  //#endregion
156
174
  //#region src/features/clean.ts
157
- const debug$5 = Debug("tsdown:clean");
175
+ const debug$7 = debug("tsdown:clean");
158
176
  const RE_LAST_SLASH = /[/\\]$/;
159
177
  async function cleanOutDir(configs) {
160
178
  const removes = /* @__PURE__ */ new Set();
@@ -172,12 +190,12 @@ async function cleanOutDir(configs) {
172
190
  }
173
191
  }
174
192
  if (!removes.size) return;
175
- logger.info(`Cleaning ${removes.size} files`);
193
+ globalLogger.info(`Cleaning ${removes.size} files`);
176
194
  await Promise.all([...removes].map(async (file) => {
177
- debug$5("Removing", file);
195
+ debug$7("Removing", file);
178
196
  await fsRemove(file);
179
197
  }));
180
- debug$5("Removed %d files", removes.size);
198
+ debug$7("Removed %d files", removes.size);
181
199
  }
182
200
  function resolveClean(clean, outDir, cwd) {
183
201
  if (clean === true) clean = [slash(outDir)];
@@ -309,50 +327,6 @@ function exportMeta(exports, all) {
309
327
  else exports["./package.json"] = "./package.json";
310
328
  }
311
329
 
312
- //#endregion
313
- //#region src/features/external.ts
314
- const debug$4 = Debug("tsdown:external");
315
- function ExternalPlugin(options) {
316
- const deps = options.pkg && Array.from(getProductionDeps(options.pkg));
317
- return {
318
- name: "tsdown:external",
319
- async resolveId(id, importer, extraOptions) {
320
- if (extraOptions.isEntry) return;
321
- if (id === shimFile) return;
322
- const { noExternal } = options;
323
- if (typeof noExternal === "function" && noExternal(id, importer)) return;
324
- if (noExternal) {
325
- const noExternalPatterns = toArray(noExternal);
326
- if (noExternalPatterns.some((pattern) => {
327
- if (pattern instanceof RegExp) {
328
- pattern.lastIndex = 0;
329
- return pattern.test(id);
330
- }
331
- return id === pattern;
332
- })) return;
333
- }
334
- let shouldExternal = false;
335
- if (options.skipNodeModulesBundle) {
336
- const resolved = await this.resolve(id, importer, extraOptions);
337
- if (!resolved) return resolved;
338
- shouldExternal = resolved.external || /[\\/]node_modules[\\/]/.test(resolved.id);
339
- }
340
- if (deps) shouldExternal ||= deps.some((dep) => id === dep || id.startsWith(`${dep}/`));
341
- if (shouldExternal) {
342
- debug$4("External dependency:", id);
343
- return {
344
- id,
345
- external: shouldExternal,
346
- moduleSideEffects: id.startsWith("node:") || builtinModules.includes(id) ? false : void 0
347
- };
348
- }
349
- }
350
- };
351
- }
352
- function getProductionDeps(pkg) {
353
- return new Set([...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})]);
354
- }
355
-
356
330
  //#endregion
357
331
  //#region src/features/hooks.ts
358
332
  async function createHooks$1(options) {
@@ -370,101 +344,146 @@ async function createHooks$1(options) {
370
344
  }
371
345
 
372
346
  //#endregion
373
- //#region src/utils/lightningcss.ts
374
- /**
375
- * Converts esbuild target [^1] (which is also used by Rolldown [^2]) to Lightning CSS targets [^3].
376
- *
377
- * [^1]: https://esbuild.github.io/api/#target
378
- * [^2]: https://github.com/rolldown/rolldown/blob/v1.0.0-beta.8/packages/rolldown/src/binding.d.ts#L1429-L1431
379
- * [^3]: https://lightningcss.dev/transpilation.html
380
- */
381
- function esbuildTargetToLightningCSS(target) {
382
- let targets;
383
- const targetString = target.join(" ").toLowerCase();
384
- const matches = [...targetString.matchAll(TARGET_REGEX)];
385
- for (const match of matches) {
386
- const name = match[1];
387
- const browser = ESBUILD_LIGHTNINGCSS_MAPPING[name];
388
- if (!browser) continue;
389
- const version = match[2];
390
- const versionInt = parseVersion(version);
391
- if (versionInt == null) continue;
392
- targets = targets || {};
393
- targets[browser] = versionInt;
347
+ //#region src/features/publint.ts
348
+ const debug$6 = debug("tsdown:publint");
349
+ async function publint(options) {
350
+ if (!options.publint) return;
351
+ if (!options.pkg) {
352
+ options.logger.warn("publint is enabled but package.json is not found");
353
+ return;
354
+ }
355
+ const t = performance.now();
356
+ debug$6("Running publint");
357
+ const { publint: publint$1 } = await import("publint");
358
+ const { formatMessage } = await import("publint/utils");
359
+ const { messages } = await publint$1(options.publint === true ? {} : options.publint);
360
+ debug$6("Found %d issues", messages.length);
361
+ if (!messages.length) options.logger.success(`No publint issues found`, dim`(${Math.round(performance.now() - t)}ms)`);
362
+ let hasError = false;
363
+ for (const message of messages) {
364
+ hasError ||= message.type === "error";
365
+ const formattedMessage = formatMessage(message, options.pkg);
366
+ const logType = {
367
+ error: "error",
368
+ warning: "warn",
369
+ suggestion: "info"
370
+ }[message.type];
371
+ options.logger[logType](formattedMessage);
372
+ }
373
+ if (hasError) {
374
+ debug$6("Found errors, setting exit code to 1");
375
+ process.exitCode = 1;
394
376
  }
395
- return targets;
396
- }
397
- const TARGET_REGEX = /([a-z]+)(\d+(?:\.\d+)*)/g;
398
- const ESBUILD_LIGHTNINGCSS_MAPPING = {
399
- chrome: "chrome",
400
- edge: "edge",
401
- firefox: "firefox",
402
- ie: "ie",
403
- ios: "ios_saf",
404
- opera: "opera",
405
- safari: "safari"
406
- };
407
- function parseVersion(version) {
408
- const [major, minor = 0, patch = 0] = version.split("-")[0].split(".").map((v) => Number.parseInt(v, 10));
409
- if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) return null;
410
- return major << 16 | minor << 8 | patch;
411
377
  }
412
378
 
413
379
  //#endregion
414
- //#region src/features/lightningcss.ts
415
- async function LightningCSSPlugin(options) {
416
- const LightningCSS = await import("unplugin-lightningcss/rolldown").catch(() => void 0);
417
- if (!LightningCSS) return;
418
- const targets = options.target && esbuildTargetToLightningCSS(options.target);
419
- if (!targets) return;
420
- return LightningCSS.default({ options: { targets } });
380
+ //#region src/features/entry.ts
381
+ async function resolveEntry(logger, entry, cwd, name) {
382
+ const nameLabel = name ? `[${name}] ` : "";
383
+ if (!entry || Object.keys(entry).length === 0) {
384
+ const defaultEntry = path.resolve(cwd, "src/index.ts");
385
+ if (await fsExists(defaultEntry)) entry = { index: defaultEntry };
386
+ else throw new Error(`${nameLabel}No input files, try "tsdown <your-file>" or create src/index.ts`);
387
+ }
388
+ const entryMap = await toObjectEntry(entry, cwd);
389
+ const entries = Object.values(entryMap);
390
+ if (entries.length === 0) throw new Error(`${nameLabel}Cannot find entry: ${JSON.stringify(entry)}`);
391
+ logger.info(prettyName(name), `entry: ${generateColor(name)(entries.map((entry$1) => path.relative(cwd, entry$1)).join(", "))}`);
392
+ return entryMap;
393
+ }
394
+ async function toObjectEntry(entry, cwd) {
395
+ if (typeof entry === "string") entry = [entry];
396
+ if (!Array.isArray(entry)) return entry;
397
+ const resolvedEntry = (await glob(entry, { cwd })).map((file) => path.resolve(cwd, file));
398
+ const base = lowestCommonAncestor(...resolvedEntry);
399
+ return Object.fromEntries(resolvedEntry.map((file) => {
400
+ const relative = path.relative(base, file);
401
+ return [relative.slice(0, relative.length - path.extname(relative).length), file];
402
+ }));
421
403
  }
422
404
 
423
405
  //#endregion
424
- //#region src/features/node-protocol.ts
425
- /**
426
- * The `node:` protocol was added in Node.js v14.18.0.
427
- * @see https://nodejs.org/api/esm.html#node-imports
428
- */
429
- function NodeProtocolPlugin(nodeProtocolOption) {
430
- if (nodeProtocolOption === "strip") return {
431
- name: "tsdown:node-protocol:strip",
406
+ //#region src/features/target.ts
407
+ function resolveTarget(logger, target, pkg, name) {
408
+ if (target === false) return;
409
+ if (target == null) {
410
+ const pkgTarget = resolvePackageTarget(pkg);
411
+ if (pkgTarget) target = pkgTarget;
412
+ else return;
413
+ }
414
+ const targets = resolveComma(toArray(target));
415
+ if (targets.length) logger.info(prettyName(name), `target${targets.length > 1 ? "s" : ""}: ${generateColor(name)(targets.join(", "))}`);
416
+ return targets;
417
+ }
418
+ function resolvePackageTarget(pkg) {
419
+ const nodeVersion = pkg?.engines?.node;
420
+ if (!nodeVersion) return;
421
+ const nodeMinVersion = minVersion(nodeVersion);
422
+ if (!nodeMinVersion) return;
423
+ if (nodeMinVersion.version === "0.0.0") return;
424
+ return `node${nodeMinVersion.version}`;
425
+ }
426
+ let warned = false;
427
+ function RuntimeHelperCheckPlugin(logger, targets) {
428
+ return {
429
+ name: "tsdown:runtime-helper-check",
432
430
  resolveId: {
433
- order: "pre",
434
- filter: { id: /^node:/ },
435
- handler(id) {
436
- return {
437
- id: id.slice(5),
438
- external: true,
439
- moduleSideEffects: false
431
+ filter: { id: /^@oxc-project\/runtime/ },
432
+ async handler(id, ...args) {
433
+ const EXTERNAL = {
434
+ id,
435
+ external: true
440
436
  };
437
+ if (warned) return EXTERNAL;
438
+ const resolved = await this.resolve(id, ...args);
439
+ if (!resolved) {
440
+ if (!warned) {
441
+ warned = true;
442
+ logger.warn(`The target environment (${targets.join(", ")}) requires runtime helpers from ${blue`@oxc-project/runtime`}. Please install it to ensure all necessary polyfills are included.\nFor more information, visit: https://tsdown.dev/options/target#runtime-helpers`);
443
+ }
444
+ return EXTERNAL;
445
+ }
446
+ return resolved;
441
447
  }
442
448
  }
443
449
  };
444
- const builtinModulesRegex = /* @__PURE__ */ new RegExp(`^(${builtinModules.join("|")})$`);
445
- return {
446
- name: "tsdown:node-protocol:add",
447
- resolveId: {
448
- order: "pre",
449
- filter: { id: builtinModulesRegex },
450
- handler(id) {
451
- return {
452
- id: `node:${id}`,
453
- external: true,
454
- moduleSideEffects: false
455
- };
450
+ }
451
+
452
+ //#endregion
453
+ //#region src/features/tsconfig.ts
454
+ function findTsconfig(cwd, name = "tsconfig.json") {
455
+ return up(name, { cwd }) || false;
456
+ }
457
+ async function resolveTsconfig(logger, tsconfig, cwd, name) {
458
+ const original = tsconfig;
459
+ if (tsconfig !== false) {
460
+ if (tsconfig === true || tsconfig == null) {
461
+ tsconfig = findTsconfig(cwd);
462
+ if (original && !tsconfig) logger.warn(`No tsconfig found in ${blue(cwd)}`);
463
+ } else {
464
+ const tsconfigPath = path.resolve(cwd, tsconfig);
465
+ const stat$1 = await fsStat(tsconfigPath);
466
+ if (stat$1?.isFile()) tsconfig = tsconfigPath;
467
+ else if (stat$1?.isDirectory()) {
468
+ tsconfig = findTsconfig(tsconfigPath);
469
+ if (!tsconfig) logger.warn(`No tsconfig found in ${blue(tsconfigPath)}`);
470
+ } else {
471
+ tsconfig = findTsconfig(cwd, tsconfig);
472
+ if (!tsconfig) logger.warn(`tsconfig ${blue(original)} doesn't exist`);
456
473
  }
457
474
  }
458
- };
475
+ if (tsconfig) logger.info(prettyName(name), `tsconfig: ${generateColor(name)(path.relative(cwd, tsconfig))}`);
476
+ }
477
+ return tsconfig;
459
478
  }
460
479
 
461
480
  //#endregion
462
481
  //#region src/utils/package.ts
463
- const debug$3 = Debug("tsdown:package");
482
+ const debug$5 = debug("tsdown:package");
464
483
  async function readPackageJson(dir) {
465
- const packageJsonPath = up({ cwd: dir });
484
+ const packageJsonPath = up$1({ cwd: dir });
466
485
  if (!packageJsonPath) return;
467
- debug$3("Reading package.json:", packageJsonPath);
486
+ debug$5("Reading package.json:", packageJsonPath);
468
487
  const contents = await readFile(packageJsonPath, "utf8");
469
488
  return {
470
489
  ...JSON.parse(contents),
@@ -490,356 +509,6 @@ function normalizeFormat(format) {
490
509
  });
491
510
  }
492
511
 
493
- //#endregion
494
- //#region src/features/output.ts
495
- function resolveJsOutputExtension(packageType, format, fixedExtension) {
496
- switch (format) {
497
- case "es": return !fixedExtension && packageType === "module" ? "js" : "mjs";
498
- case "cjs": return fixedExtension || packageType === "module" ? "cjs" : "js";
499
- default: return "js";
500
- }
501
- }
502
- function resolveChunkFilename({ outExtensions, fixedExtension, pkg, hash }, inputOptions, format) {
503
- const packageType = getPackageType(pkg);
504
- let jsExtension;
505
- let dtsExtension;
506
- if (outExtensions) {
507
- const { js, dts } = outExtensions({
508
- options: inputOptions,
509
- format,
510
- pkgType: packageType
511
- }) || {};
512
- jsExtension = js;
513
- dtsExtension = dts;
514
- }
515
- jsExtension ||= `.${resolveJsOutputExtension(packageType, format, fixedExtension)}`;
516
- const suffix = format === "iife" || format === "umd" ? `.${format}` : "";
517
- return [createChunkFilename(`[name]${suffix}`, jsExtension, dtsExtension), createChunkFilename(`[name]${suffix}${hash ? "-[hash]" : ""}`, jsExtension, dtsExtension)];
518
- }
519
- function createChunkFilename(basename, jsExtension, dtsExtension) {
520
- if (!dtsExtension) return `${basename}${jsExtension}`;
521
- return (chunk) => {
522
- return `${basename}${chunk.name.endsWith(".d") ? dtsExtension : jsExtension}`;
523
- };
524
- }
525
- function resolveChunkAddon(chunkAddon, format) {
526
- if (!chunkAddon) return;
527
- return (chunk) => {
528
- if (typeof chunkAddon === "function") chunkAddon = chunkAddon({ format });
529
- switch (true) {
530
- case RE_JS.test(chunk.fileName): return chunkAddon?.js || "";
531
- case RE_CSS.test(chunk.fileName): return chunkAddon?.css || "";
532
- case RE_DTS.test(chunk.fileName): return chunkAddon?.dts || "";
533
- default: return "";
534
- }
535
- };
536
- }
537
-
538
- //#endregion
539
- //#region src/features/publint.ts
540
- const debug$2 = Debug("tsdown:publint");
541
- async function publint(options) {
542
- if (!options.publint) return;
543
- if (!options.pkg) {
544
- logger.warn("publint is enabled but package.json is not found");
545
- return;
546
- }
547
- const t = performance.now();
548
- debug$2("Running publint");
549
- const { publint: publint$1 } = await import("publint");
550
- const { formatMessage } = await import("publint/utils");
551
- const { messages } = await publint$1(options.publint === true ? {} : options.publint);
552
- debug$2("Found %d issues", messages.length);
553
- if (!messages.length) logger.success(`No publint issues found`, dim`(${Math.round(performance.now() - t)}ms)`);
554
- let hasError = false;
555
- for (const message of messages) {
556
- hasError ||= message.type === "error";
557
- const formattedMessage = formatMessage(message, options.pkg);
558
- const logType = {
559
- error: "error",
560
- warning: "warn",
561
- suggestion: "info"
562
- }[message.type];
563
- logger[logType](formattedMessage);
564
- }
565
- if (hasError) {
566
- debug$2("Found errors, setting exit code to 1");
567
- process.exitCode = 1;
568
- }
569
- }
570
-
571
- //#endregion
572
- //#region src/utils/format.ts
573
- function formatBytes(bytes) {
574
- if (bytes === Infinity) return void 0;
575
- return `${(bytes / 1e3).toFixed(2)} kB`;
576
- }
577
-
578
- //#endregion
579
- //#region src/features/report.ts
580
- const debug$1 = Debug("tsdown:report");
581
- const brotliCompressAsync = promisify(brotliCompress);
582
- const gzipAsync = promisify(gzip);
583
- function ReportPlugin(options, cwd, cjsDts, name, isMultiFormat) {
584
- return {
585
- name: "tsdown:report",
586
- async writeBundle(outputOptions, bundle) {
587
- const outDir = path.relative(cwd, outputOptions.file ? path.resolve(cwd, outputOptions.file, "..") : path.resolve(cwd, outputOptions.dir));
588
- const sizes = [];
589
- for (const chunk of Object.values(bundle)) {
590
- const size = await calcSize(options, chunk);
591
- sizes.push(size);
592
- }
593
- const filenameLength = Math.max(...sizes.map((size) => size.filename.length));
594
- const rawTextLength = Math.max(...sizes.map((size) => size.rawText.length));
595
- const gzipTextLength = Math.max(...sizes.map((size) => size.gzipText == null ? 0 : size.gzipText.length));
596
- const brotliTextLength = Math.max(...sizes.map((size) => size.brotliText == null ? 0 : size.brotliText.length));
597
- let totalRaw = 0;
598
- for (const size of sizes) {
599
- size.rawText = size.rawText.padStart(rawTextLength);
600
- size.gzipText = size.gzipText?.padStart(gzipTextLength);
601
- size.brotliText = size.brotliText?.padStart(brotliTextLength);
602
- totalRaw += size.raw;
603
- }
604
- sizes.sort((a, b) => {
605
- if (a.dts !== b.dts) return a.dts ? 1 : -1;
606
- if (a.isEntry !== b.isEntry) return a.isEntry ? -1 : 1;
607
- return b.raw - a.raw;
608
- });
609
- const nameLabel = prettyName(name);
610
- const formatLabel = isMultiFormat && prettyFormat(cjsDts ? "cjs" : outputOptions.format);
611
- for (const size of sizes) {
612
- const filenameColor = size.dts ? green : noop;
613
- logger.info(nameLabel, formatLabel, dim(outDir + path.sep) + filenameColor((size.isEntry ? bold : noop)(size.filename)), ` `.repeat(filenameLength - size.filename.length), dim(size.rawText), size.gzipText && dim`│ gzip: ${size.gzipText}`, options.brotli && size.brotliText && dim`│ brotli: ${size.brotliText}`);
614
- }
615
- const totalSizeText = formatBytes(totalRaw);
616
- logger.info(nameLabel, formatLabel, `${sizes.length} files, total: ${totalSizeText}`);
617
- }
618
- };
619
- }
620
- async function calcSize(options, chunk) {
621
- debug$1(`Calculating size for`, chunk.fileName);
622
- const content = chunk.type === "chunk" ? chunk.code : chunk.source;
623
- const raw = Buffer.byteLength(content, "utf8");
624
- debug$1("[size]", chunk.fileName, raw);
625
- let gzip$1 = Infinity;
626
- let brotli = Infinity;
627
- if (raw > (options.maxCompressSize ?? 1e6)) debug$1(chunk.fileName, "file size exceeds limit, skip gzip/brotli");
628
- else {
629
- gzip$1 = (await gzipAsync(content)).length;
630
- debug$1("[gzip]", chunk.fileName, gzip$1);
631
- if (options.brotli) {
632
- brotli = (await brotliCompressAsync(content)).length;
633
- debug$1("[brotli]", chunk.fileName, brotli);
634
- }
635
- }
636
- return {
637
- filename: chunk.fileName,
638
- dts: RE_DTS.test(chunk.fileName),
639
- isEntry: chunk.type === "chunk" && chunk.isEntry,
640
- raw,
641
- rawText: formatBytes(raw),
642
- gzip: gzip$1,
643
- gzipText: formatBytes(gzip$1),
644
- brotli,
645
- brotliText: formatBytes(brotli)
646
- };
647
- }
648
-
649
- //#endregion
650
- //#region src/features/shims.ts
651
- function getShimsInject(format, platform) {
652
- if (format === "es" && platform === "node") return {
653
- __dirname: [shimFile, "__dirname"],
654
- __filename: [shimFile, "__filename"]
655
- };
656
- }
657
-
658
- //#endregion
659
- //#region src/features/shortcuts.ts
660
- function shortcuts(restart) {
661
- let actionRunning = false;
662
- async function onInput(input) {
663
- if (actionRunning) return;
664
- const SHORTCUTS = [
665
- {
666
- key: "r",
667
- description: "reload config and rebuild",
668
- action() {
669
- rl.close();
670
- restart();
671
- }
672
- },
673
- {
674
- key: "c",
675
- description: "clear console",
676
- action() {
677
- console.clear();
678
- }
679
- },
680
- {
681
- key: "q",
682
- description: "quit",
683
- action() {
684
- process.exit(0);
685
- }
686
- }
687
- ];
688
- if (input === "h") {
689
- const loggedKeys = /* @__PURE__ */ new Set();
690
- logger.info(" Shortcuts");
691
- for (const shortcut$1 of SHORTCUTS) {
692
- if (loggedKeys.has(shortcut$1.key)) continue;
693
- loggedKeys.add(shortcut$1.key);
694
- if (shortcut$1.action == null) continue;
695
- logger.info(dim` press ` + bold`${shortcut$1.key} + enter` + dim` to ${shortcut$1.description}`);
696
- }
697
- return;
698
- }
699
- const shortcut = SHORTCUTS.find((shortcut$1) => shortcut$1.key === input);
700
- if (!shortcut) return;
701
- actionRunning = true;
702
- await shortcut.action();
703
- actionRunning = false;
704
- }
705
- const rl = readline.createInterface({ input: process.stdin });
706
- rl.on("line", onInput);
707
- }
708
-
709
- //#endregion
710
- //#region src/features/target.ts
711
- function resolveTarget(target, pkg, name) {
712
- if (target === false) return;
713
- if (target == null) {
714
- const pkgTarget = resolvePackageTarget(pkg);
715
- if (pkgTarget) target = pkgTarget;
716
- else return;
717
- }
718
- const targets = resolveComma(toArray(target));
719
- if (targets.length) logger.info(prettyName(name), `target${targets.length > 1 ? "s" : ""}: ${generateColor(name)(targets.join(", "))}`);
720
- return targets;
721
- }
722
- function resolvePackageTarget(pkg) {
723
- const nodeVersion = pkg?.engines?.node;
724
- if (!nodeVersion) return;
725
- const nodeMinVersion = minVersion(nodeVersion);
726
- if (!nodeMinVersion) return;
727
- if (nodeMinVersion.version === "0.0.0") return;
728
- return `node${nodeMinVersion.version}`;
729
- }
730
- let warned = false;
731
- function RuntimeHelperCheckPlugin(targets) {
732
- return {
733
- name: "tsdown:runtime-helper-check",
734
- resolveId: {
735
- filter: { id: /^@oxc-project\/runtime/ },
736
- async handler(id, ...args) {
737
- const EXTERNAL = {
738
- id,
739
- external: true
740
- };
741
- if (warned) return EXTERNAL;
742
- const resolved = await this.resolve(id, ...args);
743
- if (!resolved) {
744
- if (!warned) {
745
- warned = true;
746
- logger.warn(`The target environment (${targets.join(", ")}) requires runtime helpers from ${blue`@oxc-project/runtime`}. Please install it to ensure all necessary polyfills are included.\nFor more information, visit: https://tsdown.dev/options/target#runtime-helpers`);
747
- }
748
- return EXTERNAL;
749
- }
750
- return resolved;
751
- }
752
- }
753
- };
754
- }
755
-
756
- //#endregion
757
- //#region src/features/watch.ts
758
- const endsWithConfig = /[\\/](?:package\.json|tsdown\.config.*)$/;
759
- async function watchBuild(options, configFiles, rebuild, restart) {
760
- if (typeof options.watch === "boolean" && options.outDir === options.cwd) throw new Error(`Watch is enabled, but output directory is the same as the current working directory.Please specify a different watch directory using ${blue`watch`} option,or set ${blue`outDir`} to a different directory.`);
761
- const files = toArray(typeof options.watch === "boolean" ? options.cwd : options.watch);
762
- logger.info(`Watching for changes in ${files.join(", ")}`);
763
- files.push(...configFiles);
764
- const { watch } = await import("chokidar");
765
- const debouncedRebuild = debounce(rebuild, 100);
766
- const watcher = watch(files, {
767
- ignoreInitial: true,
768
- ignorePermissionErrors: true,
769
- ignored: [
770
- /[\\/]\.git[\\/]/,
771
- /[\\/]node_modules[\\/]/,
772
- options.outDir,
773
- ...toArray(options.ignoreWatch)
774
- ]
775
- });
776
- watcher.on("all", (type, file) => {
777
- if (configFiles.includes(file) || endsWithConfig.test(file)) {
778
- logger.info(`Reload config: ${file}`);
779
- restart();
780
- return;
781
- }
782
- logger.info(`Change detected: ${type} ${file}`);
783
- invalidateContextFile(globalContext, file);
784
- debouncedRebuild();
785
- });
786
- return watcher;
787
- }
788
-
789
- //#endregion
790
- //#region src/features/entry.ts
791
- async function resolveEntry(entry, cwd, name) {
792
- const nameLabel = name ? `[${name}] ` : "";
793
- if (!entry || Object.keys(entry).length === 0) {
794
- const defaultEntry = path.resolve(cwd, "src/index.ts");
795
- if (await fsExists(defaultEntry)) entry = { index: defaultEntry };
796
- else throw new Error(`${nameLabel}No input files, try "tsdown <your-file>" or create src/index.ts`);
797
- }
798
- const entryMap = await toObjectEntry(entry, cwd);
799
- const entries = Object.values(entryMap);
800
- if (entries.length === 0) throw new Error(`${nameLabel}Cannot find entry: ${JSON.stringify(entry)}`);
801
- logger.info(prettyName(name), `entry: ${generateColor(name)(entries.map((entry$1) => path.relative(cwd, entry$1)).join(", "))}`);
802
- return entryMap;
803
- }
804
- async function toObjectEntry(entry, cwd) {
805
- if (typeof entry === "string") entry = [entry];
806
- if (!Array.isArray(entry)) return entry;
807
- const resolvedEntry = (await glob(entry, { cwd })).map((file) => path.resolve(cwd, file));
808
- const base = lowestCommonAncestor(...resolvedEntry);
809
- return Object.fromEntries(resolvedEntry.map((file) => {
810
- const relative = path.relative(base, file);
811
- return [relative.slice(0, relative.length - path.extname(relative).length), file];
812
- }));
813
- }
814
-
815
- //#endregion
816
- //#region src/features/tsconfig.ts
817
- function findTsconfig(cwd, name = "tsconfig.json") {
818
- return up$1(name, { cwd }) || false;
819
- }
820
- async function resolveTsconfig(tsconfig, cwd, name) {
821
- const original = tsconfig;
822
- if (tsconfig !== false) {
823
- if (tsconfig === true || tsconfig == null) {
824
- tsconfig = findTsconfig(cwd);
825
- if (original && !tsconfig) logger.warn(`No tsconfig found in ${blue(cwd)}`);
826
- } else {
827
- const tsconfigPath = path.resolve(cwd, tsconfig);
828
- const stat$1 = await fsStat(tsconfigPath);
829
- if (stat$1?.isFile()) tsconfig = tsconfigPath;
830
- else if (stat$1?.isDirectory()) {
831
- tsconfig = findTsconfig(tsconfigPath);
832
- if (!tsconfig) logger.warn(`No tsconfig found in ${blue(tsconfigPath)}`);
833
- } else {
834
- tsconfig = findTsconfig(cwd, tsconfig);
835
- if (!tsconfig) logger.warn(`tsconfig ${blue(original)} doesn't exist`);
836
- }
837
- }
838
- if (tsconfig) logger.info(prettyName(name), `tsconfig: ${generateColor(name)(path.relative(cwd, tsconfig))}`);
839
- }
840
- return tsconfig;
841
- }
842
-
843
512
  //#endregion
844
513
  //#region src/options/config.ts
845
514
  async function loadViteConfig(prefix, cwd) {
@@ -861,7 +530,7 @@ async function loadViteConfig(prefix, cwd) {
861
530
  defaults: {}
862
531
  });
863
532
  if (!source) return;
864
- logger.info(`Using Vite config: ${underline(source)}`);
533
+ globalLogger.info(`Using Vite config: ${underline(source)}`);
865
534
  const resolved = await config;
866
535
  if (typeof resolved === "function") return resolved({
867
536
  command: "build",
@@ -921,7 +590,7 @@ async function loadConfigFile(options, workspace) {
921
590
  config = toArray(config);
922
591
  if (config.length === 0) config.push({});
923
592
  const file = sources[0];
924
- if (file) logger.info(`Using tsdown config: ${underline(file)}`);
593
+ if (file) globalLogger.info(`Using tsdown config: ${underline(file)}`);
925
594
  return {
926
595
  configs: config,
927
596
  file
@@ -930,7 +599,7 @@ async function loadConfigFile(options, workspace) {
930
599
 
931
600
  //#endregion
932
601
  //#region src/options/index.ts
933
- const debug = Debug("tsdown:options");
602
+ const debug$4 = debug("tsdown:options");
934
603
  const DEFAULT_EXCLUDE_WORKSPACE = [
935
604
  "**/node_modules/**",
936
605
  "**/dist/**",
@@ -938,21 +607,20 @@ const DEFAULT_EXCLUDE_WORKSPACE = [
938
607
  "**/t?(e)mp/**"
939
608
  ];
940
609
  async function resolveOptions(options) {
941
- const files = [];
942
- debug("options %O", options);
943
- debug("loading config file: %s", options.config);
610
+ debug$4("options %O", options);
944
611
  const { configs: rootConfigs, file } = await loadConfigFile(options);
612
+ const files = [];
945
613
  if (file) {
946
614
  files.push(file);
947
- debug("loaded root config file %s", file);
948
- debug("root configs %o", rootConfigs);
949
- } else debug("no root config file found");
615
+ debug$4("loaded root config file %s", file);
616
+ debug$4("root configs %O", rootConfigs);
617
+ } else debug$4("no root config file found");
950
618
  const configs = (await Promise.all(rootConfigs.map(async (rootConfig) => {
951
619
  const { configs: workspaceConfigs, files: workspaceFiles } = await resolveWorkspace(rootConfig, options);
952
620
  if (workspaceFiles) files.push(...workspaceFiles);
953
621
  return Promise.all(workspaceConfigs.filter((config) => !config.workspace || config.entry).map((config) => resolveConfig(config)));
954
622
  }))).flat();
955
- debug("resolved configs %O", configs);
623
+ debug$4("resolved configs %O", configs);
956
624
  return {
957
625
  configs,
958
626
  files
@@ -994,16 +662,16 @@ async function resolveWorkspace(config, options) {
994
662
  }
995
663
  const files = [];
996
664
  const configs = (await Promise.all(packages.map(async (cwd) => {
997
- debug("loading workspace config %s", cwd);
665
+ debug$4("loading workspace config %s", cwd);
998
666
  const { configs: configs$1, file } = await loadConfigFile({
999
667
  ...options,
1000
668
  config: workspaceConfig,
1001
669
  cwd
1002
670
  }, cwd);
1003
671
  if (file) {
1004
- debug("loaded workspace config file %s", file);
672
+ debug$4("loaded workspace config file %s", file);
1005
673
  files.push(file);
1006
- } else debug("no workspace config file found in %s", cwd);
674
+ } else debug$4("no workspace config file found in %s", cwd);
1007
675
  return configs$1.map((config$1) => ({
1008
676
  ...normalized,
1009
677
  cwd,
@@ -1016,18 +684,21 @@ async function resolveWorkspace(config, options) {
1016
684
  };
1017
685
  }
1018
686
  async function resolveConfig(userConfig) {
1019
- let { entry, format = ["es"], plugins = [], clean = true, silent = false, treeshake = true, platform = "node", outDir = "dist", sourcemap = false, dts, unused = false, watch = false, ignoreWatch = [], shims = false, skipNodeModulesBundle = false, publint: publint$1 = false, attw: attw$1 = false, fromVite, alias, tsconfig, report = true, target, env = {}, copy: copy$1, publicDir, hash, cwd = process.cwd(), name, workspace, external, noExternal, exports = false, bundle, unbundle = typeof bundle === "boolean" ? !bundle : false, removeNodeProtocol, nodeProtocol } = userConfig;
1020
- if (silent) logger.setSilent(true);
687
+ let { entry, format = ["es"], plugins = [], clean = true, silent = false, logLevel = silent ? "silent" : "info", failOnWarn = false, customLogger, treeshake = true, platform = "node", outDir = "dist", sourcemap = false, dts, unused = false, watch = false, ignoreWatch = [], shims = false, skipNodeModulesBundle = false, publint: publint$1 = false, attw: attw$1 = false, fromVite, alias, tsconfig, report = true, target, env = {}, copy: copy$1, publicDir, hash, cwd = process.cwd(), name, workspace, external, noExternal, exports = false, bundle, unbundle = typeof bundle === "boolean" ? !bundle : false, removeNodeProtocol, nodeProtocol, cjsDefault = true } = userConfig;
688
+ const logger = createLogger(logLevel, {
689
+ customLogger,
690
+ failOnWarn
691
+ });
1021
692
  if (typeof bundle === "boolean") logger.warn("`bundle` option is deprecated. Use `unbundle` instead.");
1022
693
  nodeProtocol = nodeProtocol ?? (removeNodeProtocol ? "strip" : false);
1023
694
  outDir = path.resolve(cwd, outDir);
1024
695
  clean = resolveClean(clean, outDir, cwd);
1025
696
  const pkg = await readPackageJson(cwd);
1026
697
  if (workspace) name ||= pkg?.name;
1027
- entry = await resolveEntry(entry, cwd, name);
698
+ entry = await resolveEntry(logger, entry, cwd, name);
1028
699
  if (dts == null) dts = !!(pkg?.types || pkg?.typings);
1029
- target = resolveTarget(target, pkg, name);
1030
- tsconfig = await resolveTsconfig(tsconfig, cwd, name);
700
+ target = resolveTarget(logger, target, pkg, name);
701
+ tsconfig = await resolveTsconfig(logger, tsconfig, cwd, name);
1031
702
  if (typeof external === "string") external = resolveRegex(external);
1032
703
  if (typeof noExternal === "string") noExternal = resolveRegex(noExternal);
1033
704
  if (publint$1 === true) publint$1 = {};
@@ -1044,6 +715,11 @@ async function resolveConfig(userConfig) {
1044
715
  if (viteAlias && !Array.isArray(viteAlias)) alias = viteAlias;
1045
716
  }
1046
717
  }
718
+ ignoreWatch = toArray(ignoreWatch).map((ignore) => {
719
+ ignore = resolveRegex(ignore);
720
+ if (typeof ignore === "string") return path.resolve(cwd, ignore);
721
+ return ignore;
722
+ });
1047
723
  const config = {
1048
724
  ...userConfig,
1049
725
  entry,
@@ -1052,7 +728,7 @@ async function resolveConfig(userConfig) {
1052
728
  target,
1053
729
  outDir,
1054
730
  clean,
1055
- silent,
731
+ logger,
1056
732
  treeshake,
1057
733
  platform,
1058
734
  sourcemap,
@@ -1077,22 +753,279 @@ async function resolveConfig(userConfig) {
1077
753
  noExternal,
1078
754
  exports,
1079
755
  unbundle,
1080
- nodeProtocol
756
+ nodeProtocol,
757
+ cjsDefault
758
+ };
759
+ return config;
760
+ }
761
+ async function mergeUserOptions(defaults, user, args) {
762
+ const userOutputOptions = typeof user === "function" ? await user(defaults, ...args) : user;
763
+ return {
764
+ ...defaults,
765
+ ...userOutputOptions
766
+ };
767
+ }
768
+
769
+ //#endregion
770
+ //#region src/features/external.ts
771
+ const debug$3 = debug("tsdown:external");
772
+ function ExternalPlugin(options) {
773
+ const deps = options.pkg && Array.from(getProductionDeps(options.pkg));
774
+ return {
775
+ name: "tsdown:external",
776
+ async resolveId(id, importer, extraOptions) {
777
+ if (extraOptions.isEntry) return;
778
+ if (id === shimFile) return;
779
+ const { noExternal } = options;
780
+ if (typeof noExternal === "function" && noExternal(id, importer)) return;
781
+ if (noExternal) {
782
+ const noExternalPatterns = toArray(noExternal);
783
+ if (noExternalPatterns.some((pattern) => {
784
+ if (pattern instanceof RegExp) {
785
+ pattern.lastIndex = 0;
786
+ return pattern.test(id);
787
+ }
788
+ return id === pattern;
789
+ })) return;
790
+ }
791
+ let shouldExternal = false;
792
+ if (options.skipNodeModulesBundle) {
793
+ const resolved = await this.resolve(id, importer, extraOptions);
794
+ if (!resolved) return resolved;
795
+ shouldExternal = resolved.external || /[\\/]node_modules[\\/]/.test(resolved.id);
796
+ }
797
+ if (deps) shouldExternal ||= deps.some((dep) => id === dep || id.startsWith(`${dep}/`));
798
+ if (shouldExternal) {
799
+ debug$3("External dependency:", id);
800
+ return {
801
+ id,
802
+ external: shouldExternal,
803
+ moduleSideEffects: id.startsWith("node:") || builtinModules.includes(id) ? false : void 0
804
+ };
805
+ }
806
+ }
807
+ };
808
+ }
809
+ function getProductionDeps(pkg) {
810
+ return new Set([...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})]);
811
+ }
812
+
813
+ //#endregion
814
+ //#region src/utils/lightningcss.ts
815
+ /**
816
+ * Converts esbuild target [^1] (which is also used by Rolldown [^2]) to Lightning CSS targets [^3].
817
+ *
818
+ * [^1]: https://esbuild.github.io/api/#target
819
+ * [^2]: https://github.com/rolldown/rolldown/blob/v1.0.0-beta.8/packages/rolldown/src/binding.d.ts#L1429-L1431
820
+ * [^3]: https://lightningcss.dev/transpilation.html
821
+ */
822
+ function esbuildTargetToLightningCSS(target) {
823
+ let targets;
824
+ const targetString = target.join(" ").toLowerCase();
825
+ const matches = [...targetString.matchAll(TARGET_REGEX)];
826
+ for (const match of matches) {
827
+ const name = match[1];
828
+ const browser = ESBUILD_LIGHTNINGCSS_MAPPING[name];
829
+ if (!browser) continue;
830
+ const version = match[2];
831
+ const versionInt = parseVersion(version);
832
+ if (versionInt == null) continue;
833
+ targets = targets || {};
834
+ targets[browser] = versionInt;
835
+ }
836
+ return targets;
837
+ }
838
+ const TARGET_REGEX = /([a-z]+)(\d+(?:\.\d+)*)/g;
839
+ const ESBUILD_LIGHTNINGCSS_MAPPING = {
840
+ chrome: "chrome",
841
+ edge: "edge",
842
+ firefox: "firefox",
843
+ ie: "ie",
844
+ ios: "ios_saf",
845
+ opera: "opera",
846
+ safari: "safari"
847
+ };
848
+ function parseVersion(version) {
849
+ const [major, minor = 0, patch = 0] = version.split("-")[0].split(".").map((v) => Number.parseInt(v, 10));
850
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) return null;
851
+ return major << 16 | minor << 8 | patch;
852
+ }
853
+
854
+ //#endregion
855
+ //#region src/features/lightningcss.ts
856
+ async function LightningCSSPlugin(options) {
857
+ const LightningCSS = await import("unplugin-lightningcss/rolldown").catch(() => void 0);
858
+ if (!LightningCSS) return;
859
+ const targets = options.target && esbuildTargetToLightningCSS(options.target);
860
+ if (!targets) return;
861
+ return LightningCSS.default({ options: { targets } });
862
+ }
863
+
864
+ //#endregion
865
+ //#region src/features/node-protocol.ts
866
+ /**
867
+ * The `node:` protocol was added in Node.js v14.18.0.
868
+ * @see https://nodejs.org/api/esm.html#node-imports
869
+ */
870
+ function NodeProtocolPlugin(nodeProtocolOption) {
871
+ if (nodeProtocolOption === "strip") return {
872
+ name: "tsdown:node-protocol:strip",
873
+ resolveId: {
874
+ order: "pre",
875
+ filter: { id: /^node:/ },
876
+ handler(id) {
877
+ return {
878
+ id: id.slice(5),
879
+ external: true,
880
+ moduleSideEffects: false
881
+ };
882
+ }
883
+ }
884
+ };
885
+ const builtinModulesRegex = /* @__PURE__ */ new RegExp(`^(${builtinModules.join("|")})$`);
886
+ return {
887
+ name: "tsdown:node-protocol:add",
888
+ resolveId: {
889
+ order: "pre",
890
+ filter: { id: builtinModulesRegex },
891
+ handler(id) {
892
+ return {
893
+ id: `node:${id}`,
894
+ external: true,
895
+ moduleSideEffects: false
896
+ };
897
+ }
898
+ }
899
+ };
900
+ }
901
+
902
+ //#endregion
903
+ //#region src/features/output.ts
904
+ function resolveJsOutputExtension(packageType, format, fixedExtension) {
905
+ switch (format) {
906
+ case "es": return !fixedExtension && packageType === "module" ? "js" : "mjs";
907
+ case "cjs": return fixedExtension || packageType === "module" ? "cjs" : "js";
908
+ default: return "js";
909
+ }
910
+ }
911
+ function resolveChunkFilename({ outExtensions, fixedExtension, pkg, hash }, inputOptions, format) {
912
+ const packageType = getPackageType(pkg);
913
+ let jsExtension;
914
+ let dtsExtension;
915
+ if (outExtensions) {
916
+ const { js, dts } = outExtensions({
917
+ options: inputOptions,
918
+ format,
919
+ pkgType: packageType
920
+ }) || {};
921
+ jsExtension = js;
922
+ dtsExtension = dts;
923
+ }
924
+ jsExtension ||= `.${resolveJsOutputExtension(packageType, format, fixedExtension)}`;
925
+ const suffix = format === "iife" || format === "umd" ? `.${format}` : "";
926
+ return [createChunkFilename(`[name]${suffix}`, jsExtension, dtsExtension), createChunkFilename(`[name]${suffix}${hash ? "-[hash]" : ""}`, jsExtension, dtsExtension)];
927
+ }
928
+ function createChunkFilename(basename, jsExtension, dtsExtension) {
929
+ if (!dtsExtension) return `${basename}${jsExtension}`;
930
+ return (chunk) => {
931
+ return `${basename}${chunk.name.endsWith(".d") ? dtsExtension : jsExtension}`;
932
+ };
933
+ }
934
+ function resolveChunkAddon(chunkAddon, format) {
935
+ if (!chunkAddon) return;
936
+ return (chunk) => {
937
+ if (typeof chunkAddon === "function") chunkAddon = chunkAddon({ format });
938
+ switch (true) {
939
+ case RE_JS.test(chunk.fileName): return chunkAddon?.js || "";
940
+ case RE_CSS.test(chunk.fileName): return chunkAddon?.css || "";
941
+ case RE_DTS.test(chunk.fileName): return chunkAddon?.dts || "";
942
+ default: return "";
943
+ }
944
+ };
945
+ }
946
+
947
+ //#endregion
948
+ //#region src/utils/format.ts
949
+ function formatBytes(bytes) {
950
+ if (bytes === Infinity) return void 0;
951
+ return `${(bytes / 1e3).toFixed(2)} kB`;
952
+ }
953
+
954
+ //#endregion
955
+ //#region src/features/report.ts
956
+ const debug$2 = debug("tsdown:report");
957
+ const brotliCompressAsync = promisify(brotliCompress);
958
+ const gzipAsync = promisify(gzip);
959
+ function ReportPlugin(options, logger, cwd, cjsDts, name, isMultiFormat) {
960
+ return {
961
+ name: "tsdown:report",
962
+ async writeBundle(outputOptions, bundle) {
963
+ const outDir = path.relative(cwd, outputOptions.file ? path.resolve(cwd, outputOptions.file, "..") : path.resolve(cwd, outputOptions.dir));
964
+ const sizes = [];
965
+ for (const chunk of Object.values(bundle)) {
966
+ const size = await calcSize(options, chunk);
967
+ sizes.push(size);
968
+ }
969
+ const filenameLength = Math.max(...sizes.map((size) => size.filename.length));
970
+ const rawTextLength = Math.max(...sizes.map((size) => size.rawText.length));
971
+ const gzipTextLength = Math.max(...sizes.map((size) => size.gzipText == null ? 0 : size.gzipText.length));
972
+ const brotliTextLength = Math.max(...sizes.map((size) => size.brotliText == null ? 0 : size.brotliText.length));
973
+ let totalRaw = 0;
974
+ for (const size of sizes) {
975
+ size.rawText = size.rawText.padStart(rawTextLength);
976
+ size.gzipText = size.gzipText?.padStart(gzipTextLength);
977
+ size.brotliText = size.brotliText?.padStart(brotliTextLength);
978
+ totalRaw += size.raw;
979
+ }
980
+ sizes.sort((a, b) => {
981
+ if (a.dts !== b.dts) return a.dts ? 1 : -1;
982
+ if (a.isEntry !== b.isEntry) return a.isEntry ? -1 : 1;
983
+ return b.raw - a.raw;
984
+ });
985
+ const nameLabel = prettyName(name);
986
+ const formatLabel = isMultiFormat && prettyFormat(cjsDts ? "cjs" : outputOptions.format);
987
+ for (const size of sizes) {
988
+ const filenameColor = size.dts ? green : noop;
989
+ logger.info(nameLabel, formatLabel, dim(outDir + path.sep) + filenameColor((size.isEntry ? bold : noop)(size.filename)), ` `.repeat(filenameLength - size.filename.length), dim(size.rawText), size.gzipText && dim`│ gzip: ${size.gzipText}`, options.brotli && size.brotliText && dim`│ brotli: ${size.brotliText}`);
990
+ }
991
+ const totalSizeText = formatBytes(totalRaw);
992
+ logger.info(nameLabel, formatLabel, `${sizes.length} files, total: ${totalSizeText}`);
993
+ }
1081
994
  };
1082
- return config;
1083
995
  }
1084
- async function mergeUserOptions(defaults, user, args) {
1085
- const userOutputOptions = typeof user === "function" ? await user(defaults, ...args) : user;
996
+ async function calcSize(options, chunk) {
997
+ debug$2(`Calculating size for`, chunk.fileName);
998
+ const content = chunk.type === "chunk" ? chunk.code : chunk.source;
999
+ const raw = Buffer.byteLength(content, "utf8");
1000
+ debug$2("[size]", chunk.fileName, raw);
1001
+ let gzip$1 = Infinity;
1002
+ let brotli = Infinity;
1003
+ if (raw > (options.maxCompressSize ?? 1e6)) debug$2(chunk.fileName, "file size exceeds limit, skip gzip/brotli");
1004
+ else {
1005
+ gzip$1 = (await gzipAsync(content)).length;
1006
+ debug$2("[gzip]", chunk.fileName, gzip$1);
1007
+ if (options.brotli) {
1008
+ brotli = (await brotliCompressAsync(content)).length;
1009
+ debug$2("[brotli]", chunk.fileName, brotli);
1010
+ }
1011
+ }
1086
1012
  return {
1087
- ...defaults,
1088
- ...userOutputOptions
1013
+ filename: chunk.fileName,
1014
+ dts: RE_DTS.test(chunk.fileName),
1015
+ isEntry: chunk.type === "chunk" && chunk.isEntry,
1016
+ raw,
1017
+ rawText: formatBytes(raw),
1018
+ gzip: gzip$1,
1019
+ gzipText: formatBytes(gzip$1),
1020
+ brotli,
1021
+ brotliText: formatBytes(brotli)
1089
1022
  };
1090
1023
  }
1091
1024
 
1092
1025
  //#endregion
1093
1026
  //#region src/features/shebang.ts
1094
1027
  const RE_SHEBANG = /^#!.*/;
1095
- function ShebangPlugin(cwd, name, isMultiFormat) {
1028
+ function ShebangPlugin(logger, cwd, name, isMultiFormat) {
1096
1029
  return {
1097
1030
  name: "tsdown:shebang",
1098
1031
  async writeBundle(options, bundle) {
@@ -1109,19 +1042,204 @@ function ShebangPlugin(cwd, name, isMultiFormat) {
1109
1042
  };
1110
1043
  }
1111
1044
 
1045
+ //#endregion
1046
+ //#region src/features/shims.ts
1047
+ function getShimsInject(format, platform) {
1048
+ if (format === "es" && platform === "node") return {
1049
+ __dirname: [shimFile, "__dirname"],
1050
+ __filename: [shimFile, "__filename"]
1051
+ };
1052
+ }
1053
+
1054
+ //#endregion
1055
+ //#region src/features/rolldown.ts
1056
+ const debug$1 = debug("tsdown:rolldown");
1057
+ async function getBuildOptions(config, format, isMultiFormat, cjsDts = false) {
1058
+ const inputOptions = await resolveInputOptions(config, format, cjsDts, isMultiFormat);
1059
+ const outputOptions = await resolveOutputOptions(inputOptions, config, format, cjsDts);
1060
+ const rolldownConfig = {
1061
+ ...inputOptions,
1062
+ output: outputOptions
1063
+ };
1064
+ debug$1("rolldown config with format \"%s\" %O", cjsDts ? "cjs dts" : format, rolldownConfig);
1065
+ return rolldownConfig;
1066
+ }
1067
+ async function resolveInputOptions(config, format, cjsDts, isMultiFormat) {
1068
+ const { entry, external, plugins: userPlugins, platform, alias, treeshake, dts, unused, target, define, shims, tsconfig, cwd, report, env, nodeProtocol, loader, name, logger, cjsDefault } = config;
1069
+ const plugins = [];
1070
+ if (nodeProtocol) plugins.push(NodeProtocolPlugin(nodeProtocol));
1071
+ if (config.pkg || config.skipNodeModulesBundle) plugins.push(ExternalPlugin(config));
1072
+ if (dts) {
1073
+ const { dts: dtsPlugin } = await import("rolldown-plugin-dts");
1074
+ const options = {
1075
+ tsconfig,
1076
+ ...dts
1077
+ };
1078
+ if (format === "es") plugins.push(dtsPlugin(options));
1079
+ else if (cjsDts) plugins.push(dtsPlugin({
1080
+ ...options,
1081
+ emitDtsOnly: true,
1082
+ cjsDefault
1083
+ }));
1084
+ }
1085
+ if (!cjsDts) {
1086
+ if (unused) {
1087
+ const { Unused } = await import("unplugin-unused");
1088
+ plugins.push(Unused.rolldown(unused === true ? {} : unused));
1089
+ }
1090
+ if (target) plugins.push(RuntimeHelperCheckPlugin(logger, target), await LightningCSSPlugin({ target }));
1091
+ plugins.push(ShebangPlugin(logger, cwd, name, isMultiFormat));
1092
+ }
1093
+ if (report && LogLevels[logger.level] >= 3) plugins.push(ReportPlugin(report, logger, cwd, cjsDts, name, isMultiFormat));
1094
+ if (!cjsDts) plugins.push(userPlugins);
1095
+ const inputOptions = await mergeUserOptions({
1096
+ input: entry,
1097
+ cwd,
1098
+ external,
1099
+ resolve: {
1100
+ alias,
1101
+ tsconfigFilename: tsconfig || void 0
1102
+ },
1103
+ treeshake,
1104
+ platform: cjsDts || format === "cjs" ? "node" : platform,
1105
+ define: {
1106
+ ...define,
1107
+ ...Object.keys(env).reduce((acc, key) => {
1108
+ const value = JSON.stringify(env[key]);
1109
+ acc[`process.env.${key}`] = value;
1110
+ acc[`import.meta.env.${key}`] = value;
1111
+ return acc;
1112
+ }, Object.create(null))
1113
+ },
1114
+ transform: { target },
1115
+ plugins,
1116
+ inject: { ...shims && !cjsDts && getShimsInject(format, platform) },
1117
+ moduleTypes: loader,
1118
+ onLog: cjsDefault ? (level, log, defaultHandler) => {
1119
+ if (log.code === "MIXED_EXPORT") return;
1120
+ defaultHandler(level, log);
1121
+ } : void 0
1122
+ }, config.inputOptions, [format, { cjsDts }]);
1123
+ return inputOptions;
1124
+ }
1125
+ async function resolveOutputOptions(inputOptions, config, format, cjsDts) {
1126
+ const { entry, outDir, sourcemap, minify, unbundle, banner, footer, cjsDefault } = config;
1127
+ const [entryFileNames, chunkFileNames] = resolveChunkFilename(config, inputOptions, format);
1128
+ const outputOptions = await mergeUserOptions({
1129
+ format: cjsDts ? "es" : format,
1130
+ name: config.globalName,
1131
+ sourcemap,
1132
+ dir: outDir,
1133
+ exports: cjsDefault ? "auto" : "named",
1134
+ minify: !cjsDts && minify,
1135
+ entryFileNames,
1136
+ chunkFileNames,
1137
+ preserveModules: unbundle,
1138
+ preserveModulesRoot: unbundle ? lowestCommonAncestor(...Object.values(entry)) : void 0,
1139
+ banner: resolveChunkAddon(banner, format),
1140
+ footer: resolveChunkAddon(footer, format)
1141
+ }, config.outputOptions, [format, { cjsDts }]);
1142
+ return outputOptions;
1143
+ }
1144
+
1145
+ //#endregion
1146
+ //#region src/features/shortcuts.ts
1147
+ function shortcuts(restart) {
1148
+ let actionRunning = false;
1149
+ async function onInput(input) {
1150
+ if (actionRunning) return;
1151
+ const SHORTCUTS = [
1152
+ {
1153
+ key: "r",
1154
+ description: "reload config and rebuild",
1155
+ action() {
1156
+ rl.close();
1157
+ restart();
1158
+ }
1159
+ },
1160
+ {
1161
+ key: "c",
1162
+ description: "clear console",
1163
+ action() {
1164
+ console.clear();
1165
+ }
1166
+ },
1167
+ {
1168
+ key: "q",
1169
+ description: "quit",
1170
+ action() {
1171
+ process.exit(0);
1172
+ }
1173
+ }
1174
+ ];
1175
+ if (input === "h") {
1176
+ const loggedKeys = /* @__PURE__ */ new Set();
1177
+ globalLogger.info(" Shortcuts");
1178
+ for (const shortcut$1 of SHORTCUTS) {
1179
+ if (loggedKeys.has(shortcut$1.key)) continue;
1180
+ loggedKeys.add(shortcut$1.key);
1181
+ if (shortcut$1.action == null) continue;
1182
+ globalLogger.info(dim` press ` + bold`${shortcut$1.key} + enter` + dim` to ${shortcut$1.description}`);
1183
+ }
1184
+ return;
1185
+ }
1186
+ const shortcut = SHORTCUTS.find((shortcut$1) => shortcut$1.key === input);
1187
+ if (!shortcut) return;
1188
+ actionRunning = true;
1189
+ await shortcut.action();
1190
+ actionRunning = false;
1191
+ }
1192
+ const rl = readline.createInterface({ input: process.stdin });
1193
+ rl.on("line", onInput);
1194
+ }
1195
+
1196
+ //#endregion
1197
+ //#region src/features/watch.ts
1198
+ const endsWithConfig = /[\\/](?:package\.json|tsdown\.config.*)$/;
1199
+ async function watchBuild(options, configFiles, rebuild, restart) {
1200
+ if (typeof options.watch === "boolean" && options.outDir === options.cwd) throw new Error(`Watch is enabled, but output directory is the same as the current working directory.Please specify a different watch directory using ${blue`watch`} option,or set ${blue`outDir`} to a different directory.`);
1201
+ const files = toArray(typeof options.watch === "boolean" ? options.cwd : options.watch);
1202
+ options.logger.info(`Watching for changes in ${files.join(", ")}`);
1203
+ files.push(...configFiles);
1204
+ const { watch } = await import("chokidar");
1205
+ const debouncedRebuild = debounce(rebuild, 100);
1206
+ const watcher = watch(files, {
1207
+ ignoreInitial: true,
1208
+ ignorePermissionErrors: true,
1209
+ ignored: [
1210
+ /[\\/]\.git[\\/]/,
1211
+ /[\\/]node_modules[\\/]/,
1212
+ options.outDir,
1213
+ ...options.ignoreWatch
1214
+ ]
1215
+ });
1216
+ watcher.on("all", (type, file) => {
1217
+ if (configFiles.includes(file) || endsWithConfig.test(file)) {
1218
+ options.logger.info(`Reload config: ${file}`);
1219
+ restart();
1220
+ return;
1221
+ }
1222
+ options.logger.info(`Change detected: ${type} ${file}`);
1223
+ invalidateContextFile(globalContext, file);
1224
+ debouncedRebuild();
1225
+ });
1226
+ return watcher;
1227
+ }
1228
+
1112
1229
  //#endregion
1113
1230
  //#region src/index.ts
1114
1231
  /**
1115
1232
  * Build with tsdown.
1116
1233
  */
1117
1234
  async function build$1(userOptions = {}) {
1235
+ globalLogger.level = userOptions.logLevel || (userOptions.silent ? "silent" : "info");
1118
1236
  const { configs, files: configFiles } = await resolveOptions(userOptions);
1119
1237
  let cleanPromise;
1120
1238
  const clean = () => {
1121
1239
  if (cleanPromise) return cleanPromise;
1122
1240
  return cleanPromise = cleanOutDir(configs);
1123
1241
  };
1124
- logger.info("Build start");
1242
+ globalLogger.info("Build start");
1125
1243
  const rebuilds = await Promise.all(configs.map((options) => buildSingle(options, clean)));
1126
1244
  const disposeCbs = [];
1127
1245
  for (const [i, config] of configs.entries()) {
@@ -1149,9 +1267,10 @@ const shimFile = path.resolve(pkgRoot, "esm-shims.js");
1149
1267
  * @param config Resolved options
1150
1268
  */
1151
1269
  async function buildSingle(config, clean) {
1152
- const { format: formats, dts, watch, onSuccess } = config;
1270
+ const { format: formats, dts, watch, onSuccess, logger } = config;
1153
1271
  let ab;
1154
1272
  const { hooks, context } = await createHooks$1(config);
1273
+ warnLegacyCJS(config);
1155
1274
  await rebuild(true);
1156
1275
  if (watch) return () => rebuild();
1157
1276
  async function rebuild(first) {
@@ -1205,77 +1324,6 @@ async function buildSingle(config, clean) {
1205
1324
  } else await onSuccess?.(config, ab.signal);
1206
1325
  }
1207
1326
  }
1208
- async function getBuildOptions(config, format, isMultiFormat, cjsDts) {
1209
- const { entry, external, plugins: userPlugins, outDir, platform, alias, treeshake, sourcemap, dts, minify, unused, target, define, shims, tsconfig, cwd, report, env, nodeProtocol, loader, name, unbundle, banner, footer } = config;
1210
- const plugins = [];
1211
- if (nodeProtocol) plugins.push(NodeProtocolPlugin(nodeProtocol));
1212
- if (config.pkg || config.skipNodeModulesBundle) plugins.push(ExternalPlugin(config));
1213
- if (dts) {
1214
- const { dts: dtsPlugin } = await import("rolldown-plugin-dts");
1215
- const options = {
1216
- tsconfig,
1217
- ...dts
1218
- };
1219
- if (format === "es") plugins.push(dtsPlugin(options));
1220
- else if (cjsDts) plugins.push(dtsPlugin({
1221
- ...options,
1222
- emitDtsOnly: true
1223
- }));
1224
- }
1225
- if (!cjsDts) {
1226
- if (unused) {
1227
- const { Unused } = await import("unplugin-unused");
1228
- plugins.push(Unused.rolldown(unused === true ? {} : unused));
1229
- }
1230
- if (target) plugins.push(RuntimeHelperCheckPlugin(target), await LightningCSSPlugin({ target }));
1231
- plugins.push(ShebangPlugin(cwd, name, isMultiFormat));
1232
- }
1233
- if (report && !logger.silent) plugins.push(ReportPlugin(report, cwd, cjsDts, name, isMultiFormat));
1234
- if (!cjsDts) plugins.push(userPlugins);
1235
- cjsDts = !!cjsDts;
1236
- const inputOptions = await mergeUserOptions({
1237
- input: entry,
1238
- cwd,
1239
- external,
1240
- resolve: {
1241
- alias,
1242
- tsconfigFilename: tsconfig || void 0
1243
- },
1244
- treeshake,
1245
- platform: cjsDts || format === "cjs" ? "node" : platform,
1246
- define: {
1247
- ...define,
1248
- ...Object.keys(env).reduce((acc, key) => {
1249
- const value = JSON.stringify(env[key]);
1250
- acc[`process.env.${key}`] = value;
1251
- acc[`import.meta.env.${key}`] = value;
1252
- return acc;
1253
- }, Object.create(null))
1254
- },
1255
- transform: { target },
1256
- plugins,
1257
- inject: { ...shims && !cjsDts && getShimsInject(format, platform) },
1258
- moduleTypes: loader
1259
- }, config.inputOptions, [format, { cjsDts }]);
1260
- const [entryFileNames, chunkFileNames] = resolveChunkFilename(config, inputOptions, format);
1261
- const outputOptions = await mergeUserOptions({
1262
- format: cjsDts ? "es" : format,
1263
- name: config.globalName,
1264
- sourcemap,
1265
- dir: outDir,
1266
- minify: !cjsDts && minify,
1267
- entryFileNames,
1268
- chunkFileNames,
1269
- preserveModules: unbundle,
1270
- preserveModulesRoot: unbundle ? lowestCommonAncestor(...Object.values(entry)) : void 0,
1271
- banner: resolveChunkAddon(banner, format),
1272
- footer: resolveChunkAddon(footer, format)
1273
- }, config.outputOptions, [format, { cjsDts }]);
1274
- return {
1275
- ...inputOptions,
1276
- output: outputOptions
1277
- };
1278
- }
1279
1327
 
1280
1328
  //#endregion
1281
1329
  export { ExternalPlugin, NodeProtocolPlugin, ReportPlugin, ShebangPlugin, build$1 as build, buildSingle, shimFile };