tsdown 0.13.4 → 0.14.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,1335 @@
1
+ import { LogLevels, createLogger, debounce, generateColor, globalLogger, noop, prettyFormat, prettyName, resolveComma, resolveRegex, slash, toArray } from "./logger-CGMSjTLn.mjs";
2
+ import { builtinModules } from "node:module";
3
+ import path, { dirname, normalize, sep } from "node:path";
4
+ import process from "node:process";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { blue, bold, dim, green, underline } from "ansis";
7
+ import { build } from "rolldown";
8
+ import { exec } from "tinyexec";
9
+ import treeKill from "tree-kill";
10
+ import child_process from "node:child_process";
11
+ import { access, chmod, cp, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
12
+ import { tmpdir } from "node:os";
13
+ import { promisify } from "node:util";
14
+ import debug from "debug";
15
+ import coerce from "semver/functions/coerce.js";
16
+ import satisfies from "semver/functions/satisfies.js";
17
+ import { glob } from "tinyglobby";
18
+ import { RE_CSS, RE_DTS, RE_JS } from "rolldown-plugin-dts/filename";
19
+ import { createHooks } from "hookable";
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";
24
+ import { Buffer } from "node:buffer";
25
+ import { brotliCompress, gzip } from "node:zlib";
26
+ import readline from "node:readline";
27
+ import { globalContext, invalidateContextFile } from "rolldown-plugin-dts/tsc-context";
28
+
29
+ //#region src/utils/fs.ts
30
+ function fsExists(path$1) {
31
+ return access(path$1).then(() => true, () => false);
32
+ }
33
+ function fsStat(path$1) {
34
+ return stat(path$1).catch(() => null);
35
+ }
36
+ function fsRemove(path$1) {
37
+ return rm(path$1, {
38
+ force: true,
39
+ recursive: true
40
+ }).catch(() => {});
41
+ }
42
+ function fsCopy(from, to) {
43
+ return cp(from, to, {
44
+ recursive: true,
45
+ force: true
46
+ });
47
+ }
48
+ function lowestCommonAncestor(...filepaths) {
49
+ if (filepaths.length === 0) return "";
50
+ if (filepaths.length === 1) return dirname(filepaths[0]);
51
+ filepaths = filepaths.map(normalize);
52
+ const [first, ...rest] = filepaths;
53
+ let ancestor = first.split(sep);
54
+ for (const filepath of rest) {
55
+ const directories = filepath.split(sep, ancestor.length);
56
+ let index = 0;
57
+ for (const directory of directories) if (directory === ancestor[index]) index += 1;
58
+ else {
59
+ ancestor = ancestor.slice(0, index);
60
+ break;
61
+ }
62
+ ancestor = ancestor.slice(0, index);
63
+ }
64
+ return ancestor.length <= 1 && ancestor[0] === "" ? sep + ancestor[0] : ancestor.join(sep);
65
+ }
66
+
67
+ //#endregion
68
+ //#region src/features/attw.ts
69
+ const debug$8 = debug("tsdown:attw");
70
+ const exec$1 = promisify(child_process.exec);
71
+ /**
72
+ * ATTW profiles.
73
+ * Defines the resolution modes to ignore for each profile.
74
+ *
75
+ * @see https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/packages/cli/README.md#profiles
76
+ */
77
+ const profiles = {
78
+ strict: [],
79
+ node16: ["node10"],
80
+ esmOnly: ["node10", "node16-cjs"]
81
+ };
82
+ /**
83
+ * Format an ATTW problem for display
84
+ */
85
+ function formatProblem(problem) {
86
+ const resolutionKind = "resolutionKind" in problem ? ` (${problem.resolutionKind})` : "";
87
+ const entrypoint = "entrypoint" in problem ? ` at ${problem.entrypoint}` : "";
88
+ switch (problem.kind) {
89
+ case "NoResolution": return ` ❌ No resolution${resolutionKind}${entrypoint}`;
90
+ case "UntypedResolution": return ` ⚠️ Untyped resolution${resolutionKind}${entrypoint}`;
91
+ case "FalseESM": return ` 🔄 False ESM: Types indicate ESM (${problem.typesModuleKind}) but implementation is CJS (${problem.implementationModuleKind})\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
92
+ case "FalseCJS": return ` 🔄 False CJS: Types indicate CJS (${problem.typesModuleKind}) but implementation is ESM (${problem.implementationModuleKind})\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
93
+ case "CJSResolvesToESM": return ` ⚡ CJS resolves to ESM${resolutionKind}${entrypoint}`;
94
+ case "NamedExports": {
95
+ const missingExports = problem.missing?.length > 0 ? ` Missing: ${problem.missing.join(", ")}` : "";
96
+ const allMissing = problem.isMissingAllNamed ? " (all named exports missing)" : "";
97
+ return ` 📤 Named exports problem${allMissing}${missingExports}\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
98
+ }
99
+ case "FallbackCondition": return ` 🎯 Fallback condition used${resolutionKind}${entrypoint}`;
100
+ case "FalseExportDefault": return ` 🎭 False export default\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
101
+ case "MissingExportEquals": return ` 📝 Missing export equals\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
102
+ case "InternalResolutionError": return ` 💥 Internal resolution error in ${problem.fileName} (${problem.resolutionOption})\n Module: ${problem.moduleSpecifier} | Mode: ${problem.resolutionMode}`;
103
+ case "UnexpectedModuleSyntax": return ` 📋 Unexpected module syntax in ${problem.fileName}\n Expected: ${problem.moduleKind} | Found: ${problem.syntax === 99 ? "ESM" : "CJS"}`;
104
+ case "CJSOnlyExportsDefault": return ` 🏷️ CJS only exports default in ${problem.fileName}`;
105
+ default: return ` ❓ Unknown problem: ${JSON.stringify(problem)}`;
106
+ }
107
+ }
108
+ async function attw(options) {
109
+ if (!options.attw) return;
110
+ if (!options.pkg) {
111
+ options.logger.warn("attw is enabled but package.json is not found");
112
+ return;
113
+ }
114
+ const { profile = "strict", level = "warn",...attwOptions } = options.attw === true ? {} : options.attw;
115
+ const t = performance.now();
116
+ debug$8("Running attw check");
117
+ const tempDir = await mkdtemp(path.join(tmpdir(), "tsdown-attw-"));
118
+ let attwCore;
119
+ try {
120
+ attwCore = await import("@arethetypeswrong/core");
121
+ } catch {
122
+ options.logger.error(`ATTW check requires ${blue`@arethetypeswrong/core`} to be installed.`);
123
+ return;
124
+ }
125
+ try {
126
+ const { stdout: tarballInfo } = await exec$1(`npm pack --json ----pack-destination ${tempDir}`, {
127
+ encoding: "utf8",
128
+ cwd: options.cwd
129
+ });
130
+ const parsed = JSON.parse(tarballInfo);
131
+ if (!Array.isArray(parsed) || !parsed[0]?.filename) throw new Error("Invalid npm pack output format");
132
+ const tarballPath = path.join(tempDir, parsed[0].filename);
133
+ const tarball = await readFile(tarballPath);
134
+ const pkg = attwCore.createPackageFromTarballData(tarball);
135
+ const checkResult = await attwCore.checkPackage(pkg, attwOptions);
136
+ if (checkResult.types !== false && checkResult.problems) {
137
+ const problems = checkResult.problems.filter((problem) => {
138
+ if ("resolutionKind" in problem) return !profiles[profile]?.includes(problem.resolutionKind);
139
+ return true;
140
+ });
141
+ if (problems.length) {
142
+ const problemList = problems.map(formatProblem).join("\n");
143
+ const problemMessage = `Are the types wrong problems found:\n${problemList}`;
144
+ if (level === "error") throw new Error(problemMessage);
145
+ options.logger.warn(problemMessage);
146
+ }
147
+ } else options.logger.success(`No Are the types wrong problems found`, dim`(${Math.round(performance.now() - t)}ms)`);
148
+ } catch (error) {
149
+ options.logger.error("ATTW check failed:", error);
150
+ debug$8("Found errors, setting exit code to 1");
151
+ process.exitCode = 1;
152
+ } finally {
153
+ await fsRemove(tempDir);
154
+ }
155
+ }
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
+
173
+ //#endregion
174
+ //#region src/features/clean.ts
175
+ const debug$7 = debug("tsdown:clean");
176
+ const RE_LAST_SLASH = /[/\\]$/;
177
+ async function cleanOutDir(configs) {
178
+ const removes = /* @__PURE__ */ new Set();
179
+ for (const config of configs) {
180
+ if (!config.clean.length) continue;
181
+ const files = await glob(config.clean, {
182
+ cwd: config.cwd,
183
+ absolute: true,
184
+ onlyFiles: false,
185
+ expandDirectories: false
186
+ });
187
+ const normalizedOutDir = config.outDir.replace(RE_LAST_SLASH, "");
188
+ for (const file of files) {
189
+ const normalizedFile = file.replace(RE_LAST_SLASH, "");
190
+ if (normalizedFile !== normalizedOutDir) removes.add(file);
191
+ }
192
+ }
193
+ if (!removes.size) return;
194
+ globalLogger.info(`Cleaning ${removes.size} files`);
195
+ await Promise.all([...removes].map(async (file) => {
196
+ debug$7("Removing", file);
197
+ await fsRemove(file);
198
+ }));
199
+ debug$7("Removed %d files", removes.size);
200
+ }
201
+ function resolveClean(clean, outDir, cwd) {
202
+ if (clean === true) clean = [slash(outDir)];
203
+ else if (!clean) clean = [];
204
+ if (clean.some((item) => path.resolve(item) === cwd)) throw new Error("Cannot clean the current working directory. Please specify a different path to clean option.");
205
+ return clean;
206
+ }
207
+
208
+ //#endregion
209
+ //#region src/features/copy.ts
210
+ async function copy(options) {
211
+ if (!options.copy) return;
212
+ const copy$1 = typeof options.copy === "function" ? await options.copy(options) : options.copy;
213
+ await Promise.all(toArray(copy$1).map((dir) => {
214
+ const from = typeof dir === "string" ? dir : dir.from;
215
+ const to = typeof dir === "string" ? path.resolve(options.outDir, path.basename(from)) : dir.to;
216
+ return cp$1(options.cwd, from, to);
217
+ }));
218
+ }
219
+ function cp$1(cwd, from, to) {
220
+ return fsCopy(path.resolve(cwd, from), path.resolve(cwd, to));
221
+ }
222
+
223
+ //#endregion
224
+ //#region src/features/exports.ts
225
+ async function writeExports(options, chunks) {
226
+ if (!options.exports) return;
227
+ const { outDir, pkg } = options;
228
+ if (!pkg) throw new Error("`package.json` not found, cannot write exports");
229
+ const { publishExports,...generated } = await generateExports(pkg, outDir, chunks, options.exports);
230
+ const updatedPkg = {
231
+ ...pkg,
232
+ ...generated,
233
+ packageJsonPath: void 0
234
+ };
235
+ if (publishExports) {
236
+ updatedPkg.publishConfig ||= {};
237
+ updatedPkg.publishConfig.exports = publishExports;
238
+ }
239
+ const original = await readFile(pkg.packageJsonPath, "utf8");
240
+ let contents = JSON.stringify(updatedPkg, null, original.includes(" ") ? " " : 2);
241
+ if (original.endsWith("\n")) contents += "\n";
242
+ if (contents !== original) await writeFile(pkg.packageJsonPath, contents, "utf8");
243
+ }
244
+ async function generateExports(pkg, outDir, chunks, { devExports, all, customExports }) {
245
+ const pkgJsonPath = pkg.packageJsonPath;
246
+ const pkgRoot$1 = path.dirname(pkgJsonPath);
247
+ const outDirRelative = slash(path.relative(pkgRoot$1, outDir));
248
+ let main, module$1, cjsTypes, esmTypes;
249
+ const exportsMap = /* @__PURE__ */ new Map();
250
+ for (const [format, chunksByFormat] of Object.entries(chunks)) {
251
+ if (format !== "es" && format !== "cjs") continue;
252
+ const onlyOneEntry = chunksByFormat.filter((chunk) => chunk.type === "chunk" && chunk.isEntry && !RE_DTS.test(chunk.fileName)).length === 1;
253
+ for (const chunk of chunksByFormat) {
254
+ if (chunk.type !== "chunk" || !chunk.isEntry) continue;
255
+ const ext = path.extname(chunk.fileName);
256
+ let name = chunk.fileName.slice(0, -ext.length);
257
+ const isDts = name.endsWith(".d");
258
+ if (isDts) name = name.slice(0, -2);
259
+ const isIndex = onlyOneEntry || name === "index";
260
+ const distFile = `${outDirRelative ? `./${outDirRelative}` : "."}/${chunk.fileName}`;
261
+ if (isIndex) {
262
+ name = ".";
263
+ if (format === "cjs") if (isDts) cjsTypes = distFile;
264
+ else main = distFile;
265
+ else if (format === "es") if (isDts) esmTypes = distFile;
266
+ else module$1 = distFile;
267
+ } else {
268
+ const isDirIndex = name.endsWith("/index");
269
+ name = isDirIndex ? `./${name.slice(0, -6)}` : `./${name}`;
270
+ }
271
+ let subExport = exportsMap.get(name);
272
+ if (!subExport) {
273
+ subExport = {};
274
+ exportsMap.set(name, subExport);
275
+ }
276
+ if (!isDts) {
277
+ subExport[format] = distFile;
278
+ if (chunk.facadeModuleId && !subExport.src) subExport.src = `./${slash(path.relative(pkgRoot$1, chunk.facadeModuleId))}`;
279
+ }
280
+ }
281
+ }
282
+ const sorttedExportsMap = Array.from(exportsMap.entries()).sort(([a], [b]) => {
283
+ if (a === "index") return -1;
284
+ return a.localeCompare(b);
285
+ });
286
+ let exports = Object.fromEntries(sorttedExportsMap.map(([name, subExport]) => [name, genSubExport(devExports, subExport)]));
287
+ exportMeta(exports, all);
288
+ if (customExports) exports = await customExports(exports, {
289
+ pkg,
290
+ outDir,
291
+ chunks,
292
+ isPublish: false
293
+ });
294
+ let publishExports;
295
+ if (devExports) {
296
+ publishExports = Object.fromEntries(sorttedExportsMap.map(([name, subExport]) => [name, genSubExport(false, subExport)]));
297
+ exportMeta(publishExports, all);
298
+ if (customExports) publishExports = await customExports(publishExports, {
299
+ pkg,
300
+ outDir,
301
+ chunks,
302
+ isPublish: true
303
+ });
304
+ }
305
+ return {
306
+ main: main || module$1 || pkg.main,
307
+ module: module$1 || pkg.module,
308
+ types: cjsTypes || esmTypes || pkg.types,
309
+ exports,
310
+ publishExports
311
+ };
312
+ }
313
+ function genSubExport(devExports, { src, es, cjs }) {
314
+ if (devExports === true) return src;
315
+ let value;
316
+ const dualFormat = es && cjs;
317
+ if (!dualFormat && !devExports) value = cjs || es;
318
+ else {
319
+ value = {};
320
+ if (typeof devExports === "string") value[devExports] = src;
321
+ if (es) value[dualFormat ? "import" : "default"] = es;
322
+ if (cjs) value[dualFormat ? "require" : "default"] = cjs;
323
+ }
324
+ return value;
325
+ }
326
+ function exportMeta(exports, all) {
327
+ if (all) exports["./*"] = "./*";
328
+ else exports["./package.json"] = "./package.json";
329
+ }
330
+
331
+ //#endregion
332
+ //#region src/features/hooks.ts
333
+ async function createHooks$1(options) {
334
+ const hooks = createHooks();
335
+ if (typeof options.hooks === "object") hooks.addHooks(options.hooks);
336
+ else if (typeof options.hooks === "function") await options.hooks(hooks);
337
+ const context = {
338
+ options,
339
+ hooks
340
+ };
341
+ return {
342
+ hooks,
343
+ context
344
+ };
345
+ }
346
+
347
+ //#endregion
348
+ //#region src/features/publint.ts
349
+ const debug$6 = debug("tsdown:publint");
350
+ async function publint(options) {
351
+ if (!options.publint) return;
352
+ if (!options.pkg) {
353
+ options.logger.warn("publint is enabled but package.json is not found");
354
+ return;
355
+ }
356
+ const t = performance.now();
357
+ debug$6("Running publint");
358
+ const { publint: publint$1 } = await import("publint");
359
+ const { formatMessage } = await import("publint/utils");
360
+ const { messages } = await publint$1(options.publint === true ? {} : options.publint);
361
+ debug$6("Found %d issues", messages.length);
362
+ if (!messages.length) options.logger.success(`No publint issues found`, dim`(${Math.round(performance.now() - t)}ms)`);
363
+ let hasError = false;
364
+ for (const message of messages) {
365
+ hasError ||= message.type === "error";
366
+ const formattedMessage = formatMessage(message, options.pkg);
367
+ const logType = {
368
+ error: "error",
369
+ warning: "warn",
370
+ suggestion: "info"
371
+ }[message.type];
372
+ options.logger[logType](formattedMessage);
373
+ }
374
+ if (hasError) {
375
+ debug$6("Found errors, setting exit code to 1");
376
+ process.exitCode = 1;
377
+ }
378
+ }
379
+
380
+ //#endregion
381
+ //#region src/features/entry.ts
382
+ async function resolveEntry(logger, entry, cwd, name) {
383
+ const nameLabel = name ? `[${name}] ` : "";
384
+ if (!entry || Object.keys(entry).length === 0) {
385
+ const defaultEntry = path.resolve(cwd, "src/index.ts");
386
+ if (await fsExists(defaultEntry)) entry = { index: defaultEntry };
387
+ else throw new Error(`${nameLabel}No input files, try "tsdown <your-file>" or create src/index.ts`);
388
+ }
389
+ const entryMap = await toObjectEntry(entry, cwd);
390
+ const entries = Object.values(entryMap);
391
+ if (entries.length === 0) throw new Error(`${nameLabel}Cannot find entry: ${JSON.stringify(entry)}`);
392
+ logger.info(prettyName(name), `entry: ${generateColor(name)(entries.map((entry$1) => path.relative(cwd, entry$1)).join(", "))}`);
393
+ return entryMap;
394
+ }
395
+ async function toObjectEntry(entry, cwd) {
396
+ if (typeof entry === "string") entry = [entry];
397
+ if (!Array.isArray(entry)) return entry;
398
+ const resolvedEntry = (await glob(entry, {
399
+ cwd,
400
+ expandDirectories: false
401
+ })).map((file) => path.resolve(cwd, file));
402
+ const base = lowestCommonAncestor(...resolvedEntry);
403
+ return Object.fromEntries(resolvedEntry.map((file) => {
404
+ const relative = path.relative(base, file);
405
+ return [relative.slice(0, relative.length - path.extname(relative).length), file];
406
+ }));
407
+ }
408
+
409
+ //#endregion
410
+ //#region src/features/target.ts
411
+ function resolveTarget(logger, target, pkg, name) {
412
+ if (target === false) return;
413
+ if (target == null) {
414
+ const pkgTarget = resolvePackageTarget(pkg);
415
+ if (pkgTarget) target = pkgTarget;
416
+ else return;
417
+ }
418
+ const targets = resolveComma(toArray(target));
419
+ if (targets.length) logger.info(prettyName(name), `target${targets.length > 1 ? "s" : ""}: ${generateColor(name)(targets.join(", "))}`);
420
+ return targets;
421
+ }
422
+ function resolvePackageTarget(pkg) {
423
+ const nodeVersion = pkg?.engines?.node;
424
+ if (!nodeVersion) return;
425
+ const nodeMinVersion = minVersion(nodeVersion);
426
+ if (!nodeMinVersion) return;
427
+ if (nodeMinVersion.version === "0.0.0") return;
428
+ return `node${nodeMinVersion.version}`;
429
+ }
430
+ let warned = false;
431
+ function RuntimeHelperCheckPlugin(logger, targets) {
432
+ return {
433
+ name: "tsdown:runtime-helper-check",
434
+ resolveId: {
435
+ filter: { id: /^@oxc-project\/runtime/ },
436
+ async handler(id, ...args) {
437
+ const EXTERNAL = {
438
+ id,
439
+ external: true
440
+ };
441
+ if (warned) return EXTERNAL;
442
+ const resolved = await this.resolve(id, ...args);
443
+ if (!resolved) {
444
+ if (!warned) {
445
+ warned = true;
446
+ 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`);
447
+ }
448
+ return EXTERNAL;
449
+ }
450
+ return resolved;
451
+ }
452
+ }
453
+ };
454
+ }
455
+
456
+ //#endregion
457
+ //#region src/features/tsconfig.ts
458
+ function findTsconfig(cwd, name = "tsconfig.json") {
459
+ return up(name, { cwd }) || false;
460
+ }
461
+ async function resolveTsconfig(logger, tsconfig, cwd, name) {
462
+ const original = tsconfig;
463
+ if (tsconfig !== false) {
464
+ if (tsconfig === true || tsconfig == null) {
465
+ tsconfig = findTsconfig(cwd);
466
+ if (original && !tsconfig) logger.warn(`No tsconfig found in ${blue(cwd)}`);
467
+ } else {
468
+ const tsconfigPath = path.resolve(cwd, tsconfig);
469
+ const stat$1 = await fsStat(tsconfigPath);
470
+ if (stat$1?.isFile()) tsconfig = tsconfigPath;
471
+ else if (stat$1?.isDirectory()) {
472
+ tsconfig = findTsconfig(tsconfigPath);
473
+ if (!tsconfig) logger.warn(`No tsconfig found in ${blue(tsconfigPath)}`);
474
+ } else {
475
+ tsconfig = findTsconfig(cwd, tsconfig);
476
+ if (!tsconfig) logger.warn(`tsconfig ${blue(original)} doesn't exist`);
477
+ }
478
+ }
479
+ if (tsconfig) logger.info(prettyName(name), `tsconfig: ${generateColor(name)(path.relative(cwd, tsconfig))}`);
480
+ }
481
+ return tsconfig;
482
+ }
483
+
484
+ //#endregion
485
+ //#region src/utils/package.ts
486
+ const debug$5 = debug("tsdown:package");
487
+ async function readPackageJson(dir) {
488
+ const packageJsonPath = up$1({ cwd: dir });
489
+ if (!packageJsonPath) return;
490
+ debug$5("Reading package.json:", packageJsonPath);
491
+ const contents = await readFile(packageJsonPath, "utf8");
492
+ return {
493
+ ...JSON.parse(contents),
494
+ packageJsonPath
495
+ };
496
+ }
497
+ function getPackageType(pkg) {
498
+ if (pkg?.type) {
499
+ if (!["module", "commonjs"].includes(pkg.type)) throw new Error(`Invalid package.json type: ${pkg.type}`);
500
+ return pkg.type;
501
+ }
502
+ }
503
+ function normalizeFormat(format) {
504
+ return resolveComma(toArray(format, "es")).map((format$1) => {
505
+ switch (format$1) {
506
+ case "es":
507
+ case "esm":
508
+ case "module": return "es";
509
+ case "cjs":
510
+ case "commonjs": return "cjs";
511
+ default: return format$1;
512
+ }
513
+ });
514
+ }
515
+
516
+ //#endregion
517
+ //#region src/options/config.ts
518
+ async function loadViteConfig(prefix, cwd) {
519
+ const { config, sources: [source] } = await loadConfig({
520
+ sources: [{
521
+ files: `${prefix}.config`,
522
+ extensions: [
523
+ "ts",
524
+ "mts",
525
+ "cts",
526
+ "js",
527
+ "mjs",
528
+ "cjs",
529
+ "json",
530
+ ""
531
+ ]
532
+ }],
533
+ cwd,
534
+ defaults: {}
535
+ });
536
+ if (!source) return;
537
+ globalLogger.info(`Using Vite config: ${underline(source)}`);
538
+ const resolved = await config;
539
+ if (typeof resolved === "function") return resolved({
540
+ command: "build",
541
+ mode: "production"
542
+ });
543
+ return resolved;
544
+ }
545
+ let loaded = false;
546
+ async function loadConfigFile(options, workspace) {
547
+ let cwd = options.cwd || process.cwd();
548
+ let overrideConfig = false;
549
+ let { config: filePath } = options;
550
+ if (filePath === false) return { configs: [{}] };
551
+ if (typeof filePath === "string") {
552
+ const stats = await fsStat(filePath);
553
+ if (stats) {
554
+ const resolved = path.resolve(filePath);
555
+ if (stats.isFile()) {
556
+ overrideConfig = true;
557
+ filePath = resolved;
558
+ cwd = path.dirname(filePath);
559
+ } else if (stats.isDirectory()) cwd = resolved;
560
+ }
561
+ }
562
+ const nativeTS = process.features.typescript || process.versions.bun || process.versions.deno;
563
+ let { config, sources } = await loadConfig.async({
564
+ sources: overrideConfig ? [{
565
+ files: filePath,
566
+ extensions: []
567
+ }] : [{
568
+ files: "tsdown.config",
569
+ extensions: [
570
+ "ts",
571
+ "mts",
572
+ "cts",
573
+ "js",
574
+ "mjs",
575
+ "cjs",
576
+ "json",
577
+ ""
578
+ ],
579
+ parser: loaded || !nativeTS ? "auto" : async (filepath) => {
580
+ const mod = await import(pathToFileURL(filepath).href);
581
+ const config$1 = mod.default || mod;
582
+ return config$1;
583
+ }
584
+ }, {
585
+ files: "package.json",
586
+ extensions: [],
587
+ rewrite: (config$1) => config$1?.tsdown
588
+ }],
589
+ cwd,
590
+ stopAt: workspace && path.dirname(workspace),
591
+ defaults: {}
592
+ }).finally(() => loaded = true);
593
+ if (typeof config === "function") config = await config(options);
594
+ config = toArray(config);
595
+ if (config.length === 0) config.push({});
596
+ const file = sources[0];
597
+ if (file) globalLogger.info(`Using tsdown config: ${underline(file)}`);
598
+ return {
599
+ configs: config,
600
+ file
601
+ };
602
+ }
603
+
604
+ //#endregion
605
+ //#region src/options/index.ts
606
+ const debug$4 = debug("tsdown:options");
607
+ const DEFAULT_EXCLUDE_WORKSPACE = [
608
+ "**/node_modules/**",
609
+ "**/dist/**",
610
+ "**/test?(s)/**",
611
+ "**/t?(e)mp/**"
612
+ ];
613
+ async function resolveOptions(options) {
614
+ debug$4("options %O", options);
615
+ const { configs: rootConfigs, file } = await loadConfigFile(options);
616
+ const files = [];
617
+ if (file) {
618
+ files.push(file);
619
+ debug$4("loaded root config file %s", file);
620
+ debug$4("root configs %O", rootConfigs);
621
+ } else debug$4("no root config file found");
622
+ const configs = (await Promise.all(rootConfigs.map(async (rootConfig) => {
623
+ const { configs: workspaceConfigs, files: workspaceFiles } = await resolveWorkspace(rootConfig, options);
624
+ if (workspaceFiles) files.push(...workspaceFiles);
625
+ return Promise.all(workspaceConfigs.filter((config) => !config.workspace || config.entry).map((config) => resolveConfig(config)));
626
+ }))).flat();
627
+ debug$4("resolved configs %O", configs);
628
+ return {
629
+ configs,
630
+ files
631
+ };
632
+ }
633
+ async function resolveWorkspace(config, options) {
634
+ const normalized = {
635
+ ...config,
636
+ ...options
637
+ };
638
+ const rootCwd = normalized.cwd || process.cwd();
639
+ let { workspace } = normalized;
640
+ if (!workspace) return {
641
+ configs: [normalized],
642
+ files: []
643
+ };
644
+ if (workspace === true) workspace = {};
645
+ else if (typeof workspace === "string" || Array.isArray(workspace)) workspace = { include: workspace };
646
+ let { include: packages = "auto", exclude = DEFAULT_EXCLUDE_WORKSPACE, config: workspaceConfig } = workspace;
647
+ if (packages === "auto") packages = (await glob({
648
+ patterns: "**/package.json",
649
+ ignore: exclude,
650
+ cwd: rootCwd,
651
+ expandDirectories: false
652
+ })).filter((file) => file !== "package.json").map((file) => slash(path.resolve(rootCwd, file, "..")));
653
+ else packages = (await glob({
654
+ patterns: packages,
655
+ ignore: exclude,
656
+ cwd: rootCwd,
657
+ onlyDirectories: true,
658
+ absolute: true,
659
+ expandDirectories: false
660
+ })).map((file) => slash(path.resolve(file)));
661
+ if (packages.length === 0) throw new Error("No workspace packages found, please check your config");
662
+ if (options.filter) {
663
+ options.filter = resolveRegex(options.filter);
664
+ packages = packages.filter((path$1) => {
665
+ return typeof options.filter === "string" ? path$1.includes(options.filter) : Array.isArray(options.filter) ? options.filter.some((filter) => path$1.includes(filter)) : options.filter.test(path$1);
666
+ });
667
+ if (packages.length === 0) throw new Error("No packages matched the filters");
668
+ }
669
+ const files = [];
670
+ const configs = (await Promise.all(packages.map(async (cwd) => {
671
+ debug$4("loading workspace config %s", cwd);
672
+ const { configs: configs$1, file } = await loadConfigFile({
673
+ ...options,
674
+ config: workspaceConfig,
675
+ cwd
676
+ }, cwd);
677
+ if (file) {
678
+ debug$4("loaded workspace config file %s", file);
679
+ files.push(file);
680
+ } else debug$4("no workspace config file found in %s", cwd);
681
+ return configs$1.map((config$1) => ({
682
+ ...normalized,
683
+ cwd,
684
+ ...config$1
685
+ }));
686
+ }))).flat();
687
+ return {
688
+ configs,
689
+ files
690
+ };
691
+ }
692
+ async function resolveConfig(userConfig) {
693
+ 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;
694
+ const logger = createLogger(logLevel, {
695
+ customLogger,
696
+ failOnWarn
697
+ });
698
+ if (typeof bundle === "boolean") logger.warn("`bundle` option is deprecated. Use `unbundle` instead.");
699
+ nodeProtocol = nodeProtocol ?? (removeNodeProtocol ? "strip" : false);
700
+ outDir = path.resolve(cwd, outDir);
701
+ clean = resolveClean(clean, outDir, cwd);
702
+ const pkg = await readPackageJson(cwd);
703
+ if (workspace) name ||= pkg?.name;
704
+ entry = await resolveEntry(logger, entry, cwd, name);
705
+ if (dts == null) dts = !!(pkg?.types || pkg?.typings);
706
+ target = resolveTarget(logger, target, pkg, name);
707
+ tsconfig = await resolveTsconfig(logger, tsconfig, cwd, name);
708
+ if (typeof external === "string") external = resolveRegex(external);
709
+ if (typeof noExternal === "string") noExternal = resolveRegex(noExternal);
710
+ if (publint$1 === true) publint$1 = {};
711
+ if (attw$1 === true) attw$1 = {};
712
+ if (exports === true) exports = {};
713
+ if (publicDir) if (copy$1) throw new TypeError("`publicDir` is deprecated. Cannot be used with `copy`");
714
+ else logger.warn(`${blue`publicDir`} is deprecated. Use ${blue`copy`} instead.`);
715
+ if (fromVite) {
716
+ const viteUserConfig = await loadViteConfig(fromVite === true ? "vite" : fromVite, cwd);
717
+ if (viteUserConfig) {
718
+ if (Array.isArray(alias)) throw new TypeError("Unsupported resolve.alias in Vite config. Use object instead of array");
719
+ if (viteUserConfig.plugins) plugins = [viteUserConfig.plugins, plugins];
720
+ const viteAlias = viteUserConfig.resolve?.alias;
721
+ if (viteAlias && !Array.isArray(viteAlias)) alias = viteAlias;
722
+ }
723
+ }
724
+ ignoreWatch = toArray(ignoreWatch).map((ignore) => {
725
+ ignore = resolveRegex(ignore);
726
+ if (typeof ignore === "string") return path.resolve(cwd, ignore);
727
+ return ignore;
728
+ });
729
+ const config = {
730
+ ...userConfig,
731
+ entry,
732
+ plugins,
733
+ format: normalizeFormat(format),
734
+ target,
735
+ outDir,
736
+ clean,
737
+ logger,
738
+ treeshake,
739
+ platform,
740
+ sourcemap,
741
+ dts: dts === true ? {} : dts,
742
+ report: report === true ? {} : report,
743
+ unused,
744
+ watch,
745
+ ignoreWatch,
746
+ shims,
747
+ skipNodeModulesBundle,
748
+ publint: publint$1,
749
+ attw: attw$1,
750
+ alias,
751
+ tsconfig,
752
+ cwd,
753
+ env,
754
+ pkg,
755
+ copy: publicDir || copy$1,
756
+ hash: hash ?? true,
757
+ name,
758
+ external,
759
+ noExternal,
760
+ exports,
761
+ unbundle,
762
+ nodeProtocol,
763
+ cjsDefault
764
+ };
765
+ return config;
766
+ }
767
+ async function mergeUserOptions(defaults, user, args) {
768
+ const userOutputOptions = typeof user === "function" ? await user(defaults, ...args) : user;
769
+ return {
770
+ ...defaults,
771
+ ...userOutputOptions
772
+ };
773
+ }
774
+
775
+ //#endregion
776
+ //#region src/features/external.ts
777
+ const debug$3 = debug("tsdown:external");
778
+ function ExternalPlugin(options) {
779
+ const deps = options.pkg && Array.from(getProductionDeps(options.pkg));
780
+ return {
781
+ name: "tsdown:external",
782
+ async resolveId(id, importer, extraOptions) {
783
+ if (extraOptions.isEntry) return;
784
+ if (id === shimFile) return;
785
+ const { noExternal } = options;
786
+ if (typeof noExternal === "function" && noExternal(id, importer)) return;
787
+ if (noExternal) {
788
+ const noExternalPatterns = toArray(noExternal);
789
+ if (noExternalPatterns.some((pattern) => {
790
+ if (pattern instanceof RegExp) {
791
+ pattern.lastIndex = 0;
792
+ return pattern.test(id);
793
+ }
794
+ return id === pattern;
795
+ })) return;
796
+ }
797
+ let shouldExternal = false;
798
+ if (options.skipNodeModulesBundle) {
799
+ const resolved = await this.resolve(id, importer, extraOptions);
800
+ if (!resolved) return resolved;
801
+ shouldExternal = resolved.external || /[\\/]node_modules[\\/]/.test(resolved.id);
802
+ }
803
+ if (deps) shouldExternal ||= deps.some((dep) => id === dep || id.startsWith(`${dep}/`));
804
+ if (shouldExternal) {
805
+ debug$3("External dependency:", id);
806
+ return {
807
+ id,
808
+ external: shouldExternal,
809
+ moduleSideEffects: id.startsWith("node:") || builtinModules.includes(id) ? false : void 0
810
+ };
811
+ }
812
+ }
813
+ };
814
+ }
815
+ function getProductionDeps(pkg) {
816
+ return new Set([...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})]);
817
+ }
818
+
819
+ //#endregion
820
+ //#region src/utils/lightningcss.ts
821
+ /**
822
+ * Converts esbuild target [^1] (which is also used by Rolldown [^2]) to Lightning CSS targets [^3].
823
+ *
824
+ * [^1]: https://esbuild.github.io/api/#target
825
+ * [^2]: https://github.com/rolldown/rolldown/blob/v1.0.0-beta.8/packages/rolldown/src/binding.d.ts#L1429-L1431
826
+ * [^3]: https://lightningcss.dev/transpilation.html
827
+ */
828
+ function esbuildTargetToLightningCSS(target) {
829
+ let targets;
830
+ const targetString = target.join(" ").toLowerCase();
831
+ const matches = [...targetString.matchAll(TARGET_REGEX)];
832
+ for (const match of matches) {
833
+ const name = match[1];
834
+ const browser = ESBUILD_LIGHTNINGCSS_MAPPING[name];
835
+ if (!browser) continue;
836
+ const version = match[2];
837
+ const versionInt = parseVersion(version);
838
+ if (versionInt == null) continue;
839
+ targets = targets || {};
840
+ targets[browser] = versionInt;
841
+ }
842
+ return targets;
843
+ }
844
+ const TARGET_REGEX = /([a-z]+)(\d+(?:\.\d+)*)/g;
845
+ const ESBUILD_LIGHTNINGCSS_MAPPING = {
846
+ chrome: "chrome",
847
+ edge: "edge",
848
+ firefox: "firefox",
849
+ ie: "ie",
850
+ ios: "ios_saf",
851
+ opera: "opera",
852
+ safari: "safari"
853
+ };
854
+ function parseVersion(version) {
855
+ const [major, minor = 0, patch = 0] = version.split("-")[0].split(".").map((v) => Number.parseInt(v, 10));
856
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) return null;
857
+ return major << 16 | minor << 8 | patch;
858
+ }
859
+
860
+ //#endregion
861
+ //#region src/features/lightningcss.ts
862
+ async function LightningCSSPlugin(options) {
863
+ const LightningCSS = await import("unplugin-lightningcss/rolldown").catch(() => void 0);
864
+ if (!LightningCSS) return;
865
+ const targets = options.target && esbuildTargetToLightningCSS(options.target);
866
+ if (!targets) return;
867
+ return LightningCSS.default({ options: { targets } });
868
+ }
869
+
870
+ //#endregion
871
+ //#region src/features/node-protocol.ts
872
+ /**
873
+ * The `node:` protocol was added in Node.js v14.18.0.
874
+ * @see https://nodejs.org/api/esm.html#node-imports
875
+ */
876
+ function NodeProtocolPlugin(nodeProtocolOption) {
877
+ if (nodeProtocolOption === "strip") return {
878
+ name: "tsdown:node-protocol:strip",
879
+ resolveId: {
880
+ order: "pre",
881
+ filter: { id: /^node:/ },
882
+ handler(id) {
883
+ return {
884
+ id: id.slice(5),
885
+ external: true,
886
+ moduleSideEffects: false
887
+ };
888
+ }
889
+ }
890
+ };
891
+ const builtinModulesRegex = /* @__PURE__ */ new RegExp(`^(${builtinModules.join("|")})$`);
892
+ return {
893
+ name: "tsdown:node-protocol:add",
894
+ resolveId: {
895
+ order: "pre",
896
+ filter: { id: builtinModulesRegex },
897
+ handler(id) {
898
+ return {
899
+ id: `node:${id}`,
900
+ external: true,
901
+ moduleSideEffects: false
902
+ };
903
+ }
904
+ }
905
+ };
906
+ }
907
+
908
+ //#endregion
909
+ //#region src/features/output.ts
910
+ function resolveJsOutputExtension(packageType, format, fixedExtension) {
911
+ switch (format) {
912
+ case "es": return !fixedExtension && packageType === "module" ? "js" : "mjs";
913
+ case "cjs": return fixedExtension || packageType === "module" ? "cjs" : "js";
914
+ default: return "js";
915
+ }
916
+ }
917
+ function resolveChunkFilename({ outExtensions, fixedExtension, pkg, hash }, inputOptions, format) {
918
+ const packageType = getPackageType(pkg);
919
+ let jsExtension;
920
+ let dtsExtension;
921
+ if (outExtensions) {
922
+ const { js, dts } = outExtensions({
923
+ options: inputOptions,
924
+ format,
925
+ pkgType: packageType
926
+ }) || {};
927
+ jsExtension = js;
928
+ dtsExtension = dts;
929
+ }
930
+ jsExtension ||= `.${resolveJsOutputExtension(packageType, format, fixedExtension)}`;
931
+ const suffix = format === "iife" || format === "umd" ? `.${format}` : "";
932
+ return [createChunkFilename(`[name]${suffix}`, jsExtension, dtsExtension), createChunkFilename(`[name]${suffix}${hash ? "-[hash]" : ""}`, jsExtension, dtsExtension)];
933
+ }
934
+ function createChunkFilename(basename, jsExtension, dtsExtension) {
935
+ if (!dtsExtension) return `${basename}${jsExtension}`;
936
+ return (chunk) => {
937
+ return `${basename}${chunk.name.endsWith(".d") ? dtsExtension : jsExtension}`;
938
+ };
939
+ }
940
+ function resolveChunkAddon(chunkAddon, format) {
941
+ if (!chunkAddon) return;
942
+ return (chunk) => {
943
+ if (typeof chunkAddon === "function") chunkAddon = chunkAddon({ format });
944
+ switch (true) {
945
+ case RE_JS.test(chunk.fileName): return chunkAddon?.js || "";
946
+ case RE_CSS.test(chunk.fileName): return chunkAddon?.css || "";
947
+ case RE_DTS.test(chunk.fileName): return chunkAddon?.dts || "";
948
+ default: return "";
949
+ }
950
+ };
951
+ }
952
+
953
+ //#endregion
954
+ //#region src/utils/format.ts
955
+ function formatBytes(bytes) {
956
+ if (bytes === Infinity) return void 0;
957
+ return `${(bytes / 1e3).toFixed(2)} kB`;
958
+ }
959
+
960
+ //#endregion
961
+ //#region src/features/report.ts
962
+ const debug$2 = debug("tsdown:report");
963
+ const brotliCompressAsync = promisify(brotliCompress);
964
+ const gzipAsync = promisify(gzip);
965
+ function ReportPlugin(options, logger, cwd, cjsDts, name, isMultiFormat) {
966
+ return {
967
+ name: "tsdown:report",
968
+ async writeBundle(outputOptions, bundle) {
969
+ const outDir = path.relative(cwd, outputOptions.file ? path.resolve(cwd, outputOptions.file, "..") : path.resolve(cwd, outputOptions.dir));
970
+ const sizes = [];
971
+ for (const chunk of Object.values(bundle)) {
972
+ const size = await calcSize(options, chunk);
973
+ sizes.push(size);
974
+ }
975
+ const filenameLength = Math.max(...sizes.map((size) => size.filename.length));
976
+ const rawTextLength = Math.max(...sizes.map((size) => size.rawText.length));
977
+ const gzipTextLength = Math.max(...sizes.map((size) => size.gzipText == null ? 0 : size.gzipText.length));
978
+ const brotliTextLength = Math.max(...sizes.map((size) => size.brotliText == null ? 0 : size.brotliText.length));
979
+ let totalRaw = 0;
980
+ for (const size of sizes) {
981
+ size.rawText = size.rawText.padStart(rawTextLength);
982
+ size.gzipText = size.gzipText?.padStart(gzipTextLength);
983
+ size.brotliText = size.brotliText?.padStart(brotliTextLength);
984
+ totalRaw += size.raw;
985
+ }
986
+ sizes.sort((a, b) => {
987
+ if (a.dts !== b.dts) return a.dts ? 1 : -1;
988
+ if (a.isEntry !== b.isEntry) return a.isEntry ? -1 : 1;
989
+ return b.raw - a.raw;
990
+ });
991
+ const nameLabel = prettyName(name);
992
+ const formatLabel = isMultiFormat && prettyFormat(cjsDts ? "cjs" : outputOptions.format);
993
+ for (const size of sizes) {
994
+ const filenameColor = size.dts ? green : noop;
995
+ 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}`);
996
+ }
997
+ const totalSizeText = formatBytes(totalRaw);
998
+ logger.info(nameLabel, formatLabel, `${sizes.length} files, total: ${totalSizeText}`);
999
+ }
1000
+ };
1001
+ }
1002
+ async function calcSize(options, chunk) {
1003
+ debug$2(`Calculating size for`, chunk.fileName);
1004
+ const content = chunk.type === "chunk" ? chunk.code : chunk.source;
1005
+ const raw = Buffer.byteLength(content, "utf8");
1006
+ debug$2("[size]", chunk.fileName, raw);
1007
+ let gzip$1 = Infinity;
1008
+ let brotli = Infinity;
1009
+ if (raw > (options.maxCompressSize ?? 1e6)) debug$2(chunk.fileName, "file size exceeds limit, skip gzip/brotli");
1010
+ else {
1011
+ gzip$1 = (await gzipAsync(content)).length;
1012
+ debug$2("[gzip]", chunk.fileName, gzip$1);
1013
+ if (options.brotli) {
1014
+ brotli = (await brotliCompressAsync(content)).length;
1015
+ debug$2("[brotli]", chunk.fileName, brotli);
1016
+ }
1017
+ }
1018
+ return {
1019
+ filename: chunk.fileName,
1020
+ dts: RE_DTS.test(chunk.fileName),
1021
+ isEntry: chunk.type === "chunk" && chunk.isEntry,
1022
+ raw,
1023
+ rawText: formatBytes(raw),
1024
+ gzip: gzip$1,
1025
+ gzipText: formatBytes(gzip$1),
1026
+ brotli,
1027
+ brotliText: formatBytes(brotli)
1028
+ };
1029
+ }
1030
+
1031
+ //#endregion
1032
+ //#region src/features/shebang.ts
1033
+ const RE_SHEBANG = /^#!.*/;
1034
+ function ShebangPlugin(logger, cwd, name, isMultiFormat) {
1035
+ return {
1036
+ name: "tsdown:shebang",
1037
+ async writeBundle(options, bundle) {
1038
+ for (const chunk of Object.values(bundle)) {
1039
+ if (chunk.type !== "chunk" || !chunk.isEntry) continue;
1040
+ if (!RE_SHEBANG.test(chunk.code)) continue;
1041
+ const filepath = path.resolve(cwd, options.file || path.join(options.dir, chunk.fileName));
1042
+ if (await fsExists(filepath)) {
1043
+ logger.info(prettyName(name), isMultiFormat && prettyFormat(options.format), `Granting execute permission to ${underline(path.relative(cwd, filepath))}`);
1044
+ await chmod(filepath, 493);
1045
+ }
1046
+ }
1047
+ }
1048
+ };
1049
+ }
1050
+
1051
+ //#endregion
1052
+ //#region src/features/shims.ts
1053
+ function getShimsInject(format, platform) {
1054
+ if (format === "es" && platform === "node") return {
1055
+ __dirname: [shimFile, "__dirname"],
1056
+ __filename: [shimFile, "__filename"]
1057
+ };
1058
+ }
1059
+
1060
+ //#endregion
1061
+ //#region src/features/rolldown.ts
1062
+ const debug$1 = debug("tsdown:rolldown");
1063
+ async function getBuildOptions(config, format, isMultiFormat, cjsDts = false) {
1064
+ const inputOptions = await resolveInputOptions(config, format, cjsDts, isMultiFormat);
1065
+ const outputOptions = await resolveOutputOptions(inputOptions, config, format, cjsDts);
1066
+ const rolldownConfig = {
1067
+ ...inputOptions,
1068
+ output: outputOptions
1069
+ };
1070
+ debug$1("rolldown config with format \"%s\" %O", cjsDts ? "cjs dts" : format, rolldownConfig);
1071
+ return rolldownConfig;
1072
+ }
1073
+ async function resolveInputOptions(config, format, cjsDts, isMultiFormat) {
1074
+ const { entry, external, plugins: userPlugins, platform, alias, treeshake, dts, unused, target, define, shims, tsconfig, cwd, report, env, nodeProtocol, loader, name, logger, cjsDefault } = config;
1075
+ const plugins = [];
1076
+ if (nodeProtocol) plugins.push(NodeProtocolPlugin(nodeProtocol));
1077
+ if (config.pkg || config.skipNodeModulesBundle) plugins.push(ExternalPlugin(config));
1078
+ if (dts) {
1079
+ const { dts: dtsPlugin } = await import("rolldown-plugin-dts");
1080
+ const options = {
1081
+ tsconfig,
1082
+ ...dts
1083
+ };
1084
+ if (format === "es") plugins.push(dtsPlugin(options));
1085
+ else if (cjsDts) plugins.push(dtsPlugin({
1086
+ ...options,
1087
+ emitDtsOnly: true,
1088
+ cjsDefault
1089
+ }));
1090
+ }
1091
+ if (!cjsDts) {
1092
+ if (unused) {
1093
+ const { Unused } = await import("unplugin-unused");
1094
+ plugins.push(Unused.rolldown(unused === true ? {} : unused));
1095
+ }
1096
+ if (target) plugins.push(RuntimeHelperCheckPlugin(logger, target), await LightningCSSPlugin({ target }));
1097
+ plugins.push(ShebangPlugin(logger, cwd, name, isMultiFormat));
1098
+ }
1099
+ if (report && LogLevels[logger.level] >= 3) plugins.push(ReportPlugin(report, logger, cwd, cjsDts, name, isMultiFormat));
1100
+ if (!cjsDts) plugins.push(userPlugins);
1101
+ const inputOptions = await mergeUserOptions({
1102
+ input: entry,
1103
+ cwd,
1104
+ external,
1105
+ resolve: {
1106
+ alias,
1107
+ tsconfigFilename: tsconfig || void 0
1108
+ },
1109
+ treeshake,
1110
+ platform: cjsDts || format === "cjs" ? "node" : platform,
1111
+ define: {
1112
+ ...define,
1113
+ ...Object.keys(env).reduce((acc, key) => {
1114
+ const value = JSON.stringify(env[key]);
1115
+ acc[`process.env.${key}`] = value;
1116
+ acc[`import.meta.env.${key}`] = value;
1117
+ return acc;
1118
+ }, Object.create(null))
1119
+ },
1120
+ transform: { target },
1121
+ plugins,
1122
+ inject: { ...shims && !cjsDts && getShimsInject(format, platform) },
1123
+ moduleTypes: loader,
1124
+ onLog: cjsDefault ? (level, log, defaultHandler) => {
1125
+ if (log.code === "MIXED_EXPORT") return;
1126
+ defaultHandler(level, log);
1127
+ } : void 0
1128
+ }, config.inputOptions, [format, { cjsDts }]);
1129
+ return inputOptions;
1130
+ }
1131
+ async function resolveOutputOptions(inputOptions, config, format, cjsDts) {
1132
+ const { entry, outDir, sourcemap, minify, unbundle, banner, footer, cjsDefault } = config;
1133
+ const [entryFileNames, chunkFileNames] = resolveChunkFilename(config, inputOptions, format);
1134
+ const outputOptions = await mergeUserOptions({
1135
+ format: cjsDts ? "es" : format,
1136
+ name: config.globalName,
1137
+ sourcemap,
1138
+ dir: outDir,
1139
+ exports: cjsDefault ? "auto" : "named",
1140
+ minify: !cjsDts && minify,
1141
+ entryFileNames,
1142
+ chunkFileNames,
1143
+ preserveModules: unbundle,
1144
+ preserveModulesRoot: unbundle ? lowestCommonAncestor(...Object.values(entry)) : void 0,
1145
+ banner: resolveChunkAddon(banner, format),
1146
+ footer: resolveChunkAddon(footer, format)
1147
+ }, config.outputOptions, [format, { cjsDts }]);
1148
+ return outputOptions;
1149
+ }
1150
+
1151
+ //#endregion
1152
+ //#region src/features/shortcuts.ts
1153
+ function shortcuts(restart) {
1154
+ let actionRunning = false;
1155
+ async function onInput(input) {
1156
+ if (actionRunning) return;
1157
+ const SHORTCUTS = [
1158
+ {
1159
+ key: "r",
1160
+ description: "reload config and rebuild",
1161
+ action() {
1162
+ rl.close();
1163
+ restart();
1164
+ }
1165
+ },
1166
+ {
1167
+ key: "c",
1168
+ description: "clear console",
1169
+ action() {
1170
+ console.clear();
1171
+ }
1172
+ },
1173
+ {
1174
+ key: "q",
1175
+ description: "quit",
1176
+ action() {
1177
+ process.exit(0);
1178
+ }
1179
+ }
1180
+ ];
1181
+ if (input === "h") {
1182
+ const loggedKeys = /* @__PURE__ */ new Set();
1183
+ globalLogger.info(" Shortcuts");
1184
+ for (const shortcut$1 of SHORTCUTS) {
1185
+ if (loggedKeys.has(shortcut$1.key)) continue;
1186
+ loggedKeys.add(shortcut$1.key);
1187
+ if (shortcut$1.action == null) continue;
1188
+ globalLogger.info(dim` press ` + bold`${shortcut$1.key} + enter` + dim` to ${shortcut$1.description}`);
1189
+ }
1190
+ return;
1191
+ }
1192
+ const shortcut = SHORTCUTS.find((shortcut$1) => shortcut$1.key === input);
1193
+ if (!shortcut) return;
1194
+ actionRunning = true;
1195
+ await shortcut.action();
1196
+ actionRunning = false;
1197
+ }
1198
+ const rl = readline.createInterface({ input: process.stdin });
1199
+ rl.on("line", onInput);
1200
+ }
1201
+
1202
+ //#endregion
1203
+ //#region src/features/watch.ts
1204
+ const endsWithConfig = /[\\/](?:package\.json|tsdown\.config.*)$/;
1205
+ async function watchBuild(options, configFiles, rebuild, restart) {
1206
+ 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.`);
1207
+ const files = toArray(typeof options.watch === "boolean" ? options.cwd : options.watch);
1208
+ options.logger.info(`Watching for changes in ${files.join(", ")}`);
1209
+ files.push(...configFiles);
1210
+ const { watch } = await import("chokidar");
1211
+ const debouncedRebuild = debounce(rebuild, 100);
1212
+ const watcher = watch(files, {
1213
+ ignoreInitial: true,
1214
+ ignorePermissionErrors: true,
1215
+ ignored: [
1216
+ /[\\/]\.git[\\/]/,
1217
+ /[\\/]node_modules[\\/]/,
1218
+ options.outDir,
1219
+ ...options.ignoreWatch
1220
+ ]
1221
+ });
1222
+ watcher.on("all", (type, file) => {
1223
+ if (configFiles.includes(file) || endsWithConfig.test(file)) {
1224
+ options.logger.info(`Reload config: ${file}`);
1225
+ restart();
1226
+ return;
1227
+ }
1228
+ options.logger.info(`Change detected: ${type} ${file}`);
1229
+ invalidateContextFile(globalContext, file);
1230
+ debouncedRebuild();
1231
+ });
1232
+ return watcher;
1233
+ }
1234
+
1235
+ //#endregion
1236
+ //#region src/index.ts
1237
+ /**
1238
+ * Build with tsdown.
1239
+ */
1240
+ async function build$1(userOptions = {}) {
1241
+ globalLogger.level = userOptions.logLevel || (userOptions.silent ? "silent" : "info");
1242
+ const { configs, files: configFiles } = await resolveOptions(userOptions);
1243
+ let cleanPromise;
1244
+ const clean = () => {
1245
+ if (cleanPromise) return cleanPromise;
1246
+ return cleanPromise = cleanOutDir(configs);
1247
+ };
1248
+ globalLogger.info("Build start");
1249
+ const rebuilds = await Promise.all(configs.map((options) => buildSingle(options, clean)));
1250
+ const disposeCbs = [];
1251
+ for (const [i, config] of configs.entries()) {
1252
+ const rebuild = rebuilds[i];
1253
+ if (!rebuild) continue;
1254
+ const watcher = await watchBuild(config, configFiles, rebuild, restart);
1255
+ disposeCbs.push(() => watcher.close());
1256
+ }
1257
+ if (disposeCbs.length) shortcuts(restart);
1258
+ async function restart() {
1259
+ for (const dispose of disposeCbs) await dispose();
1260
+ build$1(userOptions);
1261
+ }
1262
+ }
1263
+ const dirname$1 = path.dirname(fileURLToPath(import.meta.url));
1264
+ const pkgRoot = path.resolve(dirname$1, "..");
1265
+ /** @internal */
1266
+ const shimFile = path.resolve(pkgRoot, "esm-shims.js");
1267
+ /**
1268
+ * Build a single configuration, without watch and shortcuts features.
1269
+ *
1270
+ * Internal API, not for public use
1271
+ *
1272
+ * @private
1273
+ * @param config Resolved options
1274
+ */
1275
+ async function buildSingle(config, clean) {
1276
+ const { format: formats, dts, watch, onSuccess, logger } = config;
1277
+ let ab;
1278
+ const { hooks, context } = await createHooks$1(config);
1279
+ warnLegacyCJS(config);
1280
+ await rebuild(true);
1281
+ if (watch) return () => rebuild();
1282
+ async function rebuild(first) {
1283
+ const startTime = performance.now();
1284
+ await hooks.callHook("build:prepare", context);
1285
+ ab?.abort();
1286
+ if (first) await clean();
1287
+ else await cleanOutDir([config]);
1288
+ let hasErrors = false;
1289
+ const isMultiFormat = formats.length > 1;
1290
+ const chunks = {};
1291
+ await Promise.all(formats.map(async (format) => {
1292
+ try {
1293
+ const buildOptions = await getBuildOptions(config, format, isMultiFormat, false);
1294
+ await hooks.callHook("build:before", {
1295
+ ...context,
1296
+ buildOptions
1297
+ });
1298
+ const { output } = await build(buildOptions);
1299
+ chunks[format] = output;
1300
+ if (format === "cjs" && dts) {
1301
+ const { output: output$1 } = await build(await getBuildOptions(config, format, isMultiFormat, true));
1302
+ chunks[format].push(...output$1);
1303
+ }
1304
+ } catch (error) {
1305
+ if (watch) {
1306
+ logger.error(error);
1307
+ hasErrors = true;
1308
+ return;
1309
+ }
1310
+ throw error;
1311
+ }
1312
+ }));
1313
+ if (hasErrors) return;
1314
+ await Promise.all([writeExports(config, chunks), copy(config)]);
1315
+ await Promise.all([publint(config), attw(config)]);
1316
+ await hooks.callHook("build:done", context);
1317
+ logger.success(prettyName(config.name), `${first ? "Build" : "Rebuild"} complete in ${green(`${Math.round(performance.now() - startTime)}ms`)}`);
1318
+ ab = new AbortController();
1319
+ if (typeof onSuccess === "string") {
1320
+ const p = exec(onSuccess, [], { nodeOptions: {
1321
+ shell: true,
1322
+ stdio: "inherit"
1323
+ } });
1324
+ p.then(({ exitCode }) => {
1325
+ if (exitCode) process.exitCode = exitCode;
1326
+ });
1327
+ ab.signal.addEventListener("abort", () => {
1328
+ if (typeof p.pid === "number") treeKill(p.pid);
1329
+ });
1330
+ } else await onSuccess?.(config, ab.signal);
1331
+ }
1332
+ }
1333
+
1334
+ //#endregion
1335
+ export { ExternalPlugin, NodeProtocolPlugin, ReportPlugin, ShebangPlugin, build$1 as build, buildSingle, shimFile };