tsdown 0.12.9 → 0.13.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,1038 +1,5 @@
1
1
  import { defineConfig } from "./config-CzjtjH-U.mjs";
2
- import { ExternalPlugin, NodeProtocolPlugin, ReportPlugin, ShebangPlugin, fsCopy, fsExists, fsRemove, fsStat, lowestCommonAncestor } from "./plugins-BsFgA-ka.mjs";
3
- import { debounce, generateColor, logger, prettyName, resolveComma, resolveRegex, slash, toArray } from "./logger-CdK2zFTY.mjs";
4
- import path from "node:path";
5
- import process from "node:process";
6
- import { fileURLToPath, pathToFileURL } from "node:url";
7
- import { blue, bold, dim, green, underline } from "ansis";
8
- import { build as build$1 } from "rolldown";
9
- import { exec } from "tinyexec";
10
- import child_process from "node:child_process";
11
- import { mkdtemp, readFile, writeFile } from "node:fs/promises";
12
- import { tmpdir } from "node:os";
13
- import { promisify } from "node:util";
14
- import debug from "debug";
15
- import { glob } from "tinyglobby";
16
- import { RE_DTS } from "rolldown-plugin-dts/filename";
17
- import { createHooks } from "hookable";
18
- import { up } from "empathic/package";
19
- import readline from "node:readline";
20
- import minVersion from "semver/ranges/min-version.js";
21
- import { up as up$1 } from "empathic/find";
22
- import { loadConfig } from "unconfig";
2
+ import { build, buildSingle, shimFile } from "./src-vU2xJl3a.mjs";
3
+ import { logger } from "./logger-6IV2T7t1.mjs";
23
4
 
24
- //#region src/features/attw.ts
25
- const debug$5 = debug("tsdown:attw");
26
- const exec$1 = promisify(child_process.exec);
27
- /**
28
- * ATTW profiles.
29
- * Defines the resolution modes to ignore for each profile.
30
- *
31
- * @see https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/packages/cli/README.md#profiles
32
- */
33
- const profiles = {
34
- strict: [],
35
- node16: ["node10"],
36
- esmOnly: ["node10", "node16-cjs"]
37
- };
38
- /**
39
- * Format an ATTW problem for display
40
- */
41
- function formatProblem(problem) {
42
- const resolutionKind = "resolutionKind" in problem ? ` (${problem.resolutionKind})` : "";
43
- const entrypoint = "entrypoint" in problem ? ` at ${problem.entrypoint}` : "";
44
- switch (problem.kind) {
45
- case "NoResolution": return ` ❌ No resolution${resolutionKind}${entrypoint}`;
46
- case "UntypedResolution": return ` ⚠️ Untyped resolution${resolutionKind}${entrypoint}`;
47
- case "FalseESM": return ` 🔄 False ESM: Types indicate ESM (${problem.typesModuleKind}) but implementation is CJS (${problem.implementationModuleKind})\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
48
- case "FalseCJS": return ` 🔄 False CJS: Types indicate CJS (${problem.typesModuleKind}) but implementation is ESM (${problem.implementationModuleKind})\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
49
- case "CJSResolvesToESM": return ` ⚡ CJS resolves to ESM${resolutionKind}${entrypoint}`;
50
- case "NamedExports": {
51
- const missingExports = problem.missing?.length > 0 ? ` Missing: ${problem.missing.join(", ")}` : "";
52
- const allMissing = problem.isMissingAllNamed ? " (all named exports missing)" : "";
53
- return ` 📤 Named exports problem${allMissing}${missingExports}\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
54
- }
55
- case "FallbackCondition": return ` 🎯 Fallback condition used${resolutionKind}${entrypoint}`;
56
- case "FalseExportDefault": return ` 🎭 False export default\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
57
- case "MissingExportEquals": return ` 📝 Missing export equals\n Types: ${problem.typesFileName} | Implementation: ${problem.implementationFileName}`;
58
- case "InternalResolutionError": return ` 💥 Internal resolution error in ${problem.fileName} (${problem.resolutionOption})\n Module: ${problem.moduleSpecifier} | Mode: ${problem.resolutionMode}`;
59
- case "UnexpectedModuleSyntax": return ` 📋 Unexpected module syntax in ${problem.fileName}\n Expected: ${problem.moduleKind} | Found: ${problem.syntax === 99 ? "ESM" : "CJS"}`;
60
- case "CJSOnlyExportsDefault": return ` 🏷️ CJS only exports default in ${problem.fileName}`;
61
- default: return ` ❓ Unknown problem: ${JSON.stringify(problem)}`;
62
- }
63
- }
64
- async function attw(options) {
65
- if (!options.attw) return;
66
- if (!options.pkg) {
67
- logger.warn("attw is enabled but package.json is not found");
68
- return;
69
- }
70
- const { profile = "strict", level = "warn",...attwOptions } = options.attw === true ? {} : options.attw;
71
- const t = performance.now();
72
- debug$5("Running attw check");
73
- const tempDir = await mkdtemp(path.join(tmpdir(), "tsdown-attw-"));
74
- let attwCore;
75
- try {
76
- attwCore = await import("@arethetypeswrong/core");
77
- } catch {
78
- logger.error(`ATTW check requires ${blue`@arethetypeswrong/core`} to be installed.`);
79
- return;
80
- }
81
- try {
82
- const { stdout: tarballInfo } = await exec$1(`npm pack --json ----pack-destination ${tempDir}`, {
83
- encoding: "utf8",
84
- cwd: options.cwd
85
- });
86
- const parsed = JSON.parse(tarballInfo);
87
- if (!Array.isArray(parsed) || !parsed[0]?.filename) throw new Error("Invalid npm pack output format");
88
- const tarballPath = path.join(tempDir, parsed[0].filename);
89
- const tarball = await readFile(tarballPath);
90
- const pkg = attwCore.createPackageFromTarballData(tarball);
91
- const checkResult = await attwCore.checkPackage(pkg, attwOptions);
92
- if (checkResult.types !== false && checkResult.problems) {
93
- const problems = checkResult.problems.filter((problem) => {
94
- if ("resolutionKind" in problem) return !profiles[profile]?.includes(problem.resolutionKind);
95
- return true;
96
- });
97
- if (problems.length) {
98
- const problemList = problems.map(formatProblem).join("\n");
99
- const problemMessage = `Are the types wrong problems found:\n${problemList}`;
100
- if (level === "error") throw new Error(problemMessage);
101
- logger.warn(problemMessage);
102
- }
103
- } else logger.success(`No Are the types wrong problems found`, dim`(${Math.round(performance.now() - t)}ms)`);
104
- } catch (error) {
105
- logger.error("ATTW check failed:", error);
106
- debug$5("Found errors, setting exit code to 1");
107
- process.exitCode = 1;
108
- } finally {
109
- await fsRemove(tempDir);
110
- }
111
- }
112
-
113
- //#endregion
114
- //#region src/features/clean.ts
115
- const debug$4 = debug("tsdown:clean");
116
- const RE_LAST_SLASH = /[/\\]$/;
117
- async function cleanOutDir(configs) {
118
- const removes = /* @__PURE__ */ new Set();
119
- for (const config of configs) {
120
- if (!config.clean.length) continue;
121
- const files = await glob(config.clean, {
122
- cwd: config.cwd,
123
- absolute: true,
124
- onlyFiles: false
125
- });
126
- const normalizedOutDir = config.outDir.replace(RE_LAST_SLASH, "");
127
- for (const file of files) {
128
- const normalizedFile = file.replace(RE_LAST_SLASH, "");
129
- if (normalizedFile !== normalizedOutDir) removes.add(file);
130
- }
131
- }
132
- if (!removes.size) return;
133
- logger.info(`Cleaning ${removes.size} files`);
134
- await Promise.all([...removes].map(async (file) => {
135
- debug$4("Removing", file);
136
- await fsRemove(file);
137
- }));
138
- debug$4("Removed %d files", removes.size);
139
- }
140
- function resolveClean(clean, outDir, cwd) {
141
- if (clean === true) clean = [slash(outDir)];
142
- else if (!clean) clean = [];
143
- 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.");
144
- return clean;
145
- }
146
-
147
- //#endregion
148
- //#region src/features/copy.ts
149
- async function copy(options) {
150
- if (!options.copy) return;
151
- const copy$1 = typeof options.copy === "function" ? await options.copy(options) : options.copy;
152
- await Promise.all(toArray(copy$1).map((dir) => {
153
- const from = typeof dir === "string" ? dir : dir.from;
154
- const to = typeof dir === "string" ? path.resolve(options.outDir, path.basename(from)) : dir.to;
155
- return cp$1(options.cwd, from, to);
156
- }));
157
- }
158
- function cp$1(cwd, from, to) {
159
- return fsCopy(path.resolve(cwd, from), path.resolve(cwd, to));
160
- }
161
-
162
- //#endregion
163
- //#region src/features/exports.ts
164
- async function writeExports(options, chunks) {
165
- if (!options.exports) return;
166
- const { outDir, pkg } = options;
167
- if (!pkg) throw new Error("`package.json` not found, cannot write exports");
168
- const { publishExports,...generated } = await generateExports(pkg, outDir, chunks, options.exports);
169
- const updatedPkg = {
170
- ...pkg,
171
- ...generated,
172
- packageJsonPath: void 0
173
- };
174
- if (publishExports) {
175
- updatedPkg.publishConfig ||= {};
176
- updatedPkg.publishConfig.exports = publishExports;
177
- }
178
- const original = await readFile(pkg.packageJsonPath, "utf8");
179
- let contents = JSON.stringify(updatedPkg, null, original.includes(" ") ? " " : 2);
180
- if (original.endsWith("\n")) contents += "\n";
181
- if (contents !== original) await writeFile(pkg.packageJsonPath, contents, "utf8");
182
- }
183
- async function generateExports(pkg, outDir, chunks, { devExports, all, customExports }) {
184
- const pkgJsonPath = pkg.packageJsonPath;
185
- const pkgRoot$1 = path.dirname(pkgJsonPath);
186
- const outDirRelative = slash(path.relative(pkgRoot$1, outDir));
187
- let main, module, cjsTypes, esmTypes;
188
- const exportsMap = /* @__PURE__ */ new Map();
189
- for (const [format, chunksByFormat] of Object.entries(chunks)) {
190
- if (format !== "es" && format !== "cjs") continue;
191
- const onlyOneEntry = chunksByFormat.filter((chunk) => chunk.type === "chunk" && chunk.isEntry && !RE_DTS.test(chunk.fileName)).length === 1;
192
- for (const chunk of chunksByFormat) {
193
- if (chunk.type !== "chunk" || !chunk.isEntry) continue;
194
- const ext = path.extname(chunk.fileName);
195
- let name = chunk.fileName.slice(0, -ext.length);
196
- const isDts = name.endsWith(".d");
197
- if (isDts) name = name.slice(0, -2);
198
- const isIndex = onlyOneEntry || name === "index";
199
- const distFile = `${outDirRelative ? `./${outDirRelative}` : "."}/${chunk.fileName}`;
200
- if (isIndex) {
201
- name = ".";
202
- if (format === "cjs") if (isDts) cjsTypes = distFile;
203
- else main = distFile;
204
- else if (format === "es") if (isDts) esmTypes = distFile;
205
- else module = distFile;
206
- } else name = `./${name}`;
207
- let subExport = exportsMap.get(name);
208
- if (!subExport) {
209
- subExport = {};
210
- exportsMap.set(name, subExport);
211
- }
212
- if (!isDts) {
213
- subExport[format] = distFile;
214
- if (chunk.facadeModuleId && !subExport.src) subExport.src = `./${slash(path.relative(pkgRoot$1, chunk.facadeModuleId))}`;
215
- }
216
- }
217
- }
218
- const sorttedExportsMap = Array.from(exportsMap.entries()).sort(([a], [b]) => {
219
- if (a === "index") return -1;
220
- return a.localeCompare(b);
221
- });
222
- let exports = Object.fromEntries(sorttedExportsMap.map(([name, subExport]) => [name, genSubExport(devExports, subExport)]));
223
- exportMeta(exports, all);
224
- if (customExports) exports = await customExports(exports, {
225
- pkg,
226
- outDir,
227
- chunks,
228
- isPublish: false
229
- });
230
- let publishExports;
231
- if (devExports) {
232
- publishExports = Object.fromEntries(sorttedExportsMap.map(([name, subExport]) => [name, genSubExport(false, subExport)]));
233
- exportMeta(publishExports, all);
234
- if (customExports) publishExports = await customExports(publishExports, {
235
- pkg,
236
- outDir,
237
- chunks,
238
- isPublish: true
239
- });
240
- }
241
- return {
242
- main: main || module || pkg.main,
243
- module: module || pkg.module,
244
- types: cjsTypes || esmTypes || pkg.types,
245
- exports,
246
- publishExports
247
- };
248
- }
249
- function genSubExport(devExports, { src, es, cjs }) {
250
- if (devExports === true) return src;
251
- let value;
252
- const dualFormat = es && cjs;
253
- if (!dualFormat && !devExports) value = cjs || es;
254
- else {
255
- value = {};
256
- if (typeof devExports === "string") value[devExports] = src;
257
- if (es) value[dualFormat ? "import" : "default"] = es;
258
- if (cjs) value[dualFormat ? "require" : "default"] = cjs;
259
- }
260
- return value;
261
- }
262
- function exportMeta(exports, all) {
263
- if (all) exports["./*"] = "./*";
264
- else exports["./package.json"] = "./package.json";
265
- }
266
-
267
- //#endregion
268
- //#region src/features/hooks.ts
269
- async function createHooks$1(options) {
270
- const hooks = createHooks();
271
- if (typeof options.hooks === "object") hooks.addHooks(options.hooks);
272
- else if (typeof options.hooks === "function") await options.hooks(hooks);
273
- const context = {
274
- options,
275
- hooks
276
- };
277
- return {
278
- hooks,
279
- context
280
- };
281
- }
282
-
283
- //#endregion
284
- //#region src/utils/lightningcss.ts
285
- /**
286
- * Converts esbuild target [^1] (which is also used by Rolldown [^2]) to Lightning CSS targets [^3].
287
- *
288
- * [^1]: https://esbuild.github.io/api/#target
289
- * [^2]: https://github.com/rolldown/rolldown/blob/v1.0.0-beta.8/packages/rolldown/src/binding.d.ts#L1429-L1431
290
- * [^3]: https://lightningcss.dev/transpilation.html
291
- */
292
- function esbuildTargetToLightningCSS(target) {
293
- let targets;
294
- const targetString = target.join(" ").toLowerCase();
295
- const matches = [...targetString.matchAll(TARGET_REGEX)];
296
- for (const match of matches) {
297
- const name = match[1];
298
- const browser = ESBUILD_LIGHTNINGCSS_MAPPING[name];
299
- if (!browser) continue;
300
- const version = match[2];
301
- const versionInt = parseVersion(version);
302
- if (versionInt == null) continue;
303
- targets = targets || {};
304
- targets[browser] = versionInt;
305
- }
306
- return targets;
307
- }
308
- const TARGET_REGEX = /([a-z]+)(\d+(?:\.\d+)*)/g;
309
- const ESBUILD_LIGHTNINGCSS_MAPPING = {
310
- chrome: "chrome",
311
- edge: "edge",
312
- firefox: "firefox",
313
- ie: "ie",
314
- ios: "ios_saf",
315
- opera: "opera",
316
- safari: "safari"
317
- };
318
- function parseVersion(version) {
319
- const [major, minor = 0, patch = 0] = version.split("-")[0].split(".").map((v) => Number.parseInt(v, 10));
320
- if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) return null;
321
- return major << 16 | minor << 8 | patch;
322
- }
323
-
324
- //#endregion
325
- //#region src/features/lightningcss.ts
326
- async function LightningCSSPlugin(options) {
327
- const LightningCSS = await import("unplugin-lightningcss/rolldown").catch(() => void 0);
328
- if (!LightningCSS) return;
329
- const targets = options.target && esbuildTargetToLightningCSS(options.target);
330
- if (!targets) return;
331
- return LightningCSS.default({ options: { targets } });
332
- }
333
-
334
- //#endregion
335
- //#region src/utils/package.ts
336
- const debug$3 = debug("tsdown:package");
337
- async function readPackageJson(dir) {
338
- const packageJsonPath = up({ cwd: dir });
339
- if (!packageJsonPath) return;
340
- debug$3("Reading package.json:", packageJsonPath);
341
- const contents = await readFile(packageJsonPath, "utf8");
342
- return {
343
- ...JSON.parse(contents),
344
- packageJsonPath
345
- };
346
- }
347
- function getPackageType(pkg) {
348
- if (pkg?.type) {
349
- if (!["module", "commonjs"].includes(pkg.type)) throw new Error(`Invalid package.json type: ${pkg.type}`);
350
- return pkg.type;
351
- }
352
- }
353
- function normalizeFormat(format) {
354
- return resolveComma(toArray(format, "es")).map((format$1) => {
355
- switch (format$1) {
356
- case "es":
357
- case "esm":
358
- case "module": return "es";
359
- case "cjs":
360
- case "commonjs": return "cjs";
361
- default: return format$1;
362
- }
363
- });
364
- }
365
-
366
- //#endregion
367
- //#region src/features/output.ts
368
- function resolveJsOutputExtension(packageType, format, fixedExtension) {
369
- switch (format) {
370
- case "es": return !fixedExtension && packageType === "module" ? "js" : "mjs";
371
- case "cjs": return fixedExtension || packageType === "module" ? "cjs" : "js";
372
- default: return "js";
373
- }
374
- }
375
- function resolveChunkFilename({ outExtensions, fixedExtension, pkg, hash }, inputOptions, format) {
376
- const packageType = getPackageType(pkg);
377
- let jsExtension;
378
- let dtsExtension;
379
- if (outExtensions) {
380
- const { js, dts } = outExtensions({
381
- options: inputOptions,
382
- format,
383
- pkgType: packageType
384
- }) || {};
385
- jsExtension = js;
386
- dtsExtension = dts;
387
- }
388
- jsExtension ||= `.${resolveJsOutputExtension(packageType, format, fixedExtension)}`;
389
- const suffix = format === "iife" || format === "umd" ? `.${format}` : "";
390
- return [createChunkFilename(`[name]${suffix}`, jsExtension, dtsExtension), createChunkFilename(`[name]${suffix}${hash ? "-[hash]" : ""}`, jsExtension, dtsExtension)];
391
- }
392
- function createChunkFilename(basename, jsExtension, dtsExtension) {
393
- if (!dtsExtension) return `${basename}${jsExtension}`;
394
- return (chunk) => {
395
- return `${basename}${chunk.name.endsWith(".d") ? dtsExtension : jsExtension}`;
396
- };
397
- }
398
-
399
- //#endregion
400
- //#region src/features/publint.ts
401
- const debug$2 = debug("tsdown:publint");
402
- async function publint(options) {
403
- if (!options.publint) return;
404
- if (!options.pkg) {
405
- logger.warn("publint is enabled but package.json is not found");
406
- return;
407
- }
408
- const t = performance.now();
409
- debug$2("Running publint");
410
- const { publint: publint$1 } = await import("publint");
411
- const { formatMessage } = await import("publint/utils");
412
- const { messages } = await publint$1(options.publint === true ? {} : options.publint);
413
- debug$2("Found %d issues", messages.length);
414
- if (!messages.length) logger.success(`No publint issues found`, dim`(${Math.round(performance.now() - t)}ms)`);
415
- let hasError = false;
416
- for (const message of messages) {
417
- hasError ||= message.type === "error";
418
- const formattedMessage = formatMessage(message, options.pkg);
419
- const logType = {
420
- error: "error",
421
- warning: "warn",
422
- suggestion: "info"
423
- }[message.type];
424
- logger[logType](formattedMessage);
425
- }
426
- if (hasError) {
427
- debug$2("Found errors, setting exit code to 1");
428
- process.exitCode = 1;
429
- }
430
- }
431
-
432
- //#endregion
433
- //#region src/features/shims.ts
434
- function getShimsInject(format, platform) {
435
- if (format === "es" && platform === "node") {
436
- const shimFile = path.resolve(pkgRoot, "esm-shims.js");
437
- return {
438
- __dirname: [shimFile, "__dirname"],
439
- __filename: [shimFile, "__filename"]
440
- };
441
- }
442
- }
443
-
444
- //#endregion
445
- //#region src/features/shortcuts.ts
446
- function shortcuts(restart) {
447
- let actionRunning = false;
448
- async function onInput(input) {
449
- if (actionRunning) return;
450
- const SHORTCUTS = [
451
- {
452
- key: "r",
453
- description: "reload config and rebuild",
454
- action() {
455
- rl.close();
456
- restart();
457
- }
458
- },
459
- {
460
- key: "c",
461
- description: "clear console",
462
- action() {
463
- console.clear();
464
- }
465
- },
466
- {
467
- key: "q",
468
- description: "quit",
469
- action() {
470
- process.exit(0);
471
- }
472
- }
473
- ];
474
- if (input === "h") {
475
- const loggedKeys = /* @__PURE__ */ new Set();
476
- logger.info(" Shortcuts");
477
- for (const shortcut$1 of SHORTCUTS) {
478
- if (loggedKeys.has(shortcut$1.key)) continue;
479
- loggedKeys.add(shortcut$1.key);
480
- if (shortcut$1.action == null) continue;
481
- logger.info(dim` press ` + bold`${shortcut$1.key} + enter` + dim` to ${shortcut$1.description}`);
482
- }
483
- return;
484
- }
485
- const shortcut = SHORTCUTS.find((shortcut$1) => shortcut$1.key === input);
486
- if (!shortcut) return;
487
- actionRunning = true;
488
- await shortcut.action();
489
- actionRunning = false;
490
- }
491
- const rl = readline.createInterface({ input: process.stdin });
492
- rl.on("line", onInput);
493
- }
494
-
495
- //#endregion
496
- //#region src/features/target.ts
497
- function resolveTarget(target, pkg, name) {
498
- if (target === false) return;
499
- if (target == null) {
500
- const pkgTarget = resolvePackageTarget(pkg);
501
- if (pkgTarget) target = pkgTarget;
502
- else return;
503
- }
504
- const targets = resolveComma(toArray(target));
505
- if (targets.length) logger.info(prettyName(name), `target${targets.length > 1 ? "s" : ""}: ${generateColor(name)(targets.join(", "))}`);
506
- return targets;
507
- }
508
- function resolvePackageTarget(pkg) {
509
- const nodeVersion = pkg?.engines?.node;
510
- if (!nodeVersion) return;
511
- const nodeMinVersion = minVersion(nodeVersion);
512
- if (!nodeMinVersion) return;
513
- if (nodeMinVersion.version === "0.0.0") return;
514
- return `node${nodeMinVersion.version}`;
515
- }
516
- let warned = false;
517
- function RuntimeHelperCheckPlugin(targets) {
518
- return {
519
- name: "tsdown:runtime-helper-check",
520
- resolveId: {
521
- filter: { id: /^@oxc-project\/runtime/ },
522
- async handler(id, ...args) {
523
- const EXTERNAL = {
524
- id,
525
- external: true
526
- };
527
- if (warned) return EXTERNAL;
528
- const resolved = await this.resolve(id, ...args);
529
- if (!resolved) {
530
- if (!warned) {
531
- warned = true;
532
- 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`);
533
- }
534
- return EXTERNAL;
535
- }
536
- return resolved;
537
- }
538
- }
539
- };
540
- }
541
-
542
- //#endregion
543
- //#region src/features/watch.ts
544
- const endsWithConfig = /[\\/](?:package\.json|tsdown\.config.*)$/;
545
- async function watchBuild(options, configFiles, rebuild, restart) {
546
- 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.`);
547
- const files = toArray(typeof options.watch === "boolean" ? options.cwd : options.watch);
548
- logger.info(`Watching for changes in ${files.join(", ")}`);
549
- files.push(...configFiles);
550
- const { watch } = await import("chokidar");
551
- const debouncedRebuild = debounce(rebuild, 100);
552
- const watcher = watch(files, {
553
- ignoreInitial: true,
554
- ignorePermissionErrors: true,
555
- ignored: [
556
- /[\\/]\.git[\\/]/,
557
- /[\\/]node_modules[\\/]/,
558
- options.outDir,
559
- ...toArray(options.ignoreWatch)
560
- ]
561
- });
562
- watcher.on("all", (type, file) => {
563
- if (configFiles.includes(file) || endsWithConfig.test(file)) {
564
- logger.info(`Reload config: ${file}`);
565
- restart();
566
- return;
567
- }
568
- logger.info(`Change detected: ${type} ${file}`);
569
- debouncedRebuild();
570
- });
571
- return watcher;
572
- }
573
-
574
- //#endregion
575
- //#region src/features/entry.ts
576
- async function resolveEntry(entry, cwd, name) {
577
- const nameLabel = name ? `[${name}] ` : "";
578
- if (!entry || Object.keys(entry).length === 0) {
579
- const defaultEntry = path.resolve(cwd, "src/index.ts");
580
- if (await fsExists(defaultEntry)) entry = { index: defaultEntry };
581
- else throw new Error(`${nameLabel}No input files, try "tsdown <your-file>" or create src/index.ts`);
582
- }
583
- const entryMap = await toObjectEntry(entry, cwd);
584
- const entries = Object.values(entryMap);
585
- if (entries.length === 0) throw new Error(`${nameLabel}Cannot find entry: ${JSON.stringify(entry)}`);
586
- logger.info(prettyName(name), `entry: ${generateColor(name)(entries.map((entry$1) => path.relative(cwd, entry$1)).join(", "))}`);
587
- return entryMap;
588
- }
589
- async function toObjectEntry(entry, cwd) {
590
- if (typeof entry === "string") entry = [entry];
591
- if (!Array.isArray(entry)) return entry;
592
- const resolvedEntry = (await glob(entry, { cwd })).map((file) => path.resolve(cwd, file));
593
- const base = lowestCommonAncestor(...resolvedEntry);
594
- return Object.fromEntries(resolvedEntry.map((file) => {
595
- const relative = path.relative(base, file);
596
- return [relative.slice(0, relative.length - path.extname(relative).length), file];
597
- }));
598
- }
599
-
600
- //#endregion
601
- //#region src/features/tsconfig.ts
602
- function findTsconfig(cwd, name = "tsconfig.json") {
603
- return up$1(name, { cwd }) || false;
604
- }
605
- async function resolveTsconfig(tsconfig, cwd, name) {
606
- const original = tsconfig;
607
- if (tsconfig !== false) {
608
- if (tsconfig === true || tsconfig == null) {
609
- tsconfig = findTsconfig(cwd);
610
- if (original && !tsconfig) logger.warn(`No tsconfig found in ${blue(cwd)}`);
611
- } else {
612
- const tsconfigPath = path.resolve(cwd, tsconfig);
613
- const stat$1 = await fsStat(tsconfigPath);
614
- if (stat$1?.isFile()) tsconfig = tsconfigPath;
615
- else if (stat$1?.isDirectory()) {
616
- tsconfig = findTsconfig(tsconfigPath);
617
- if (!tsconfig) logger.warn(`No tsconfig found in ${blue(tsconfigPath)}`);
618
- } else {
619
- tsconfig = findTsconfig(cwd, tsconfig);
620
- if (!tsconfig) logger.warn(`tsconfig ${blue(original)} doesn't exist`);
621
- }
622
- }
623
- if (tsconfig) logger.info(prettyName(name), `tsconfig: ${generateColor(name)(path.relative(cwd, tsconfig))}`);
624
- }
625
- return tsconfig;
626
- }
627
-
628
- //#endregion
629
- //#region src/options/config.ts
630
- async function loadViteConfig(prefix, cwd) {
631
- const { config, sources: [source] } = await loadConfig({
632
- sources: [{
633
- files: `${prefix}.config`,
634
- extensions: [
635
- "ts",
636
- "mts",
637
- "cts",
638
- "js",
639
- "mjs",
640
- "cjs",
641
- "json",
642
- ""
643
- ]
644
- }],
645
- cwd,
646
- defaults: {}
647
- });
648
- if (!source) return;
649
- logger.info(`Using Vite config: ${underline(source)}`);
650
- const resolved = await config;
651
- if (typeof resolved === "function") return resolved({
652
- command: "build",
653
- mode: "production"
654
- });
655
- return resolved;
656
- }
657
- let loaded = false;
658
- async function loadConfigFile(options, workspace) {
659
- let cwd = options.cwd || process.cwd();
660
- let overrideConfig = false;
661
- let { config: filePath } = options;
662
- if (filePath === false) return { configs: [{}] };
663
- if (typeof filePath === "string") {
664
- const stats = await fsStat(filePath);
665
- if (stats) {
666
- const resolved = path.resolve(filePath);
667
- if (stats.isFile()) {
668
- overrideConfig = true;
669
- filePath = resolved;
670
- cwd = path.dirname(filePath);
671
- } else if (stats.isDirectory()) cwd = resolved;
672
- }
673
- }
674
- const nativeTS = process.features.typescript || process.versions.bun || process.versions.deno;
675
- let { config, sources } = await loadConfig.async({
676
- sources: overrideConfig ? [{
677
- files: filePath,
678
- extensions: []
679
- }] : [{
680
- files: "tsdown.config",
681
- extensions: [
682
- "ts",
683
- "mts",
684
- "cts",
685
- "js",
686
- "mjs",
687
- "cjs",
688
- "json",
689
- ""
690
- ],
691
- parser: loaded || !nativeTS ? "auto" : async (filepath) => {
692
- const mod = await import(pathToFileURL(filepath).href);
693
- const config$1 = mod.default || mod;
694
- return config$1;
695
- }
696
- }, {
697
- files: "package.json",
698
- extensions: [],
699
- rewrite: (config$1) => config$1?.tsdown
700
- }],
701
- cwd,
702
- stopAt: workspace && path.dirname(workspace),
703
- defaults: {}
704
- }).finally(() => loaded = true);
705
- const file = sources[0];
706
- if (file) logger.info(`Using tsdown config: ${underline(file)}`);
707
- if (typeof config === "function") config = await config(options);
708
- config = toArray(config);
709
- if (config.length === 0) config.push({});
710
- return {
711
- configs: config,
712
- file
713
- };
714
- }
715
-
716
- //#endregion
717
- //#region src/options/index.ts
718
- const debug$1 = debug("tsdown:options");
719
- const DEFAULT_EXCLUDE_WORKSPACE = [
720
- "**/node_modules/**",
721
- "**/dist/**",
722
- "**/test?(s)/**",
723
- "**/t?(e)mp/**"
724
- ];
725
- async function resolveOptions(options) {
726
- const files = [];
727
- debug$1("options %O", options);
728
- debug$1("loading config file: %s", options.config);
729
- const { configs: rootConfigs, file } = await loadConfigFile(options);
730
- if (file) {
731
- files.push(file);
732
- debug$1("loaded root config file %s", file);
733
- debug$1("root configs %o", rootConfigs);
734
- } else debug$1("no root config file found");
735
- const configs = (await Promise.all(rootConfigs.map(async (rootConfig) => {
736
- const { configs: workspaceConfigs, files: workspaceFiles } = await resolveWorkspace(rootConfig, options);
737
- if (workspaceFiles) files.push(...workspaceFiles);
738
- return Promise.all(workspaceConfigs.filter((config) => !config.workspace || config.entry).map((config) => resolveConfig(config)));
739
- }))).flat();
740
- debug$1("resolved configs %O", configs);
741
- return {
742
- configs,
743
- files
744
- };
745
- }
746
- async function resolveWorkspace(config, options) {
747
- const normalized = {
748
- ...config,
749
- ...options
750
- };
751
- const rootCwd = normalized.cwd || process.cwd();
752
- let { workspace } = normalized;
753
- if (!workspace) return {
754
- configs: [normalized],
755
- files: []
756
- };
757
- if (workspace === true) workspace = {};
758
- else if (typeof workspace === "string" || Array.isArray(workspace)) workspace = { include: workspace };
759
- let { include: packages = "auto", exclude = DEFAULT_EXCLUDE_WORKSPACE, config: workspaceConfig } = workspace;
760
- if (packages === "auto") packages = (await glob({
761
- patterns: "**/package.json",
762
- ignore: exclude,
763
- cwd: rootCwd
764
- })).filter((file) => file !== "package.json").map((file) => path.resolve(rootCwd, file, ".."));
765
- else packages = (await glob({
766
- patterns: packages,
767
- ignore: exclude,
768
- cwd: rootCwd,
769
- onlyDirectories: true,
770
- absolute: true
771
- })).map((file) => path.resolve(file));
772
- if (packages.length === 0) throw new Error("No workspace packages found, please check your config");
773
- if (options.filter) {
774
- if (typeof options.filter === "string" && options.filter.length > 2 && options.filter[0] === "/" && options.filter.at(-1) === "/") options.filter = new RegExp(options.filter.slice(1, -1));
775
- packages = packages.filter((path$1) => {
776
- 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);
777
- });
778
- if (packages.length === 0) throw new Error("No packages matched the filters");
779
- }
780
- const files = [];
781
- const configs = (await Promise.all(packages.map(async (cwd) => {
782
- debug$1("loading workspace config %s", cwd);
783
- const { configs: configs$1, file } = await loadConfigFile({
784
- ...options,
785
- config: workspaceConfig,
786
- cwd
787
- }, cwd);
788
- if (file) {
789
- debug$1("loaded workspace config file %s", file);
790
- files.push(file);
791
- } else debug$1("no workspace config file found in %s", cwd);
792
- return configs$1.map((config$1) => ({
793
- ...normalized,
794
- cwd,
795
- ...config$1
796
- }));
797
- }))).flat();
798
- return {
799
- configs,
800
- files
801
- };
802
- }
803
- async function resolveConfig(userConfig) {
804
- 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;
805
- if (typeof bundle === "boolean") logger.warn("`bundle` option is deprecated. Use `unbundle` instead.");
806
- nodeProtocol = nodeProtocol ?? (removeNodeProtocol ? "strip" : false);
807
- outDir = path.resolve(cwd, outDir);
808
- clean = resolveClean(clean, outDir, cwd);
809
- const pkg = await readPackageJson(cwd);
810
- if (workspace) name ||= pkg?.name;
811
- entry = await resolveEntry(entry, cwd, name);
812
- if (dts == null) dts = !!(pkg?.types || pkg?.typings);
813
- target = resolveTarget(target, pkg, name);
814
- tsconfig = await resolveTsconfig(tsconfig, cwd, name);
815
- if (typeof external === "string") external = resolveRegex(external);
816
- if (typeof noExternal === "string") noExternal = resolveRegex(noExternal);
817
- if (publint$1 === true) publint$1 = {};
818
- if (attw$1 === true) attw$1 = {};
819
- if (exports === true) exports = {};
820
- if (publicDir) if (copy$1) throw new TypeError("`publicDir` is deprecated. Cannot be used with `copy`");
821
- else logger.warn(`${blue`publicDir`} is deprecated. Use ${blue`copy`} instead.`);
822
- if (fromVite) {
823
- const viteUserConfig = await loadViteConfig(fromVite === true ? "vite" : fromVite, cwd);
824
- if (viteUserConfig) {
825
- if (Array.isArray(alias)) throw new TypeError("Unsupported resolve.alias in Vite config. Use object instead of array");
826
- if (viteUserConfig.plugins) plugins = [viteUserConfig.plugins, plugins];
827
- const viteAlias = viteUserConfig.resolve?.alias;
828
- if (viteAlias && !Array.isArray(viteAlias)) alias = viteAlias;
829
- }
830
- }
831
- const config = {
832
- ...userConfig,
833
- entry,
834
- plugins,
835
- format: normalizeFormat(format),
836
- target,
837
- outDir,
838
- clean,
839
- silent,
840
- treeshake,
841
- platform,
842
- sourcemap,
843
- dts: dts === true ? {} : dts,
844
- report: report === true ? {} : report,
845
- unused,
846
- watch,
847
- ignoreWatch,
848
- shims,
849
- skipNodeModulesBundle,
850
- publint: publint$1,
851
- attw: attw$1,
852
- alias,
853
- tsconfig,
854
- cwd,
855
- env,
856
- pkg,
857
- copy: publicDir || copy$1,
858
- hash: hash ?? true,
859
- name,
860
- external,
861
- noExternal,
862
- exports,
863
- unbundle,
864
- nodeProtocol
865
- };
866
- return config;
867
- }
868
- async function mergeUserOptions(defaults, user, args) {
869
- const userOutputOptions = typeof user === "function" ? await user(defaults, ...args) : user;
870
- return {
871
- ...defaults,
872
- ...userOutputOptions
873
- };
874
- }
875
-
876
- //#endregion
877
- //#region src/index.ts
878
- /**
879
- * Build with tsdown.
880
- */
881
- async function build(userOptions = {}) {
882
- if (typeof userOptions.silent === "boolean") logger.setSilent(userOptions.silent);
883
- const { configs, files: configFiles } = await resolveOptions(userOptions);
884
- let cleanPromise;
885
- const clean = () => {
886
- if (cleanPromise) return cleanPromise;
887
- return cleanPromise = cleanOutDir(configs);
888
- };
889
- logger.info("Build start");
890
- const rebuilds = await Promise.all(configs.map((options) => buildSingle(options, clean)));
891
- const cleanCbs = [];
892
- for (const [i, config] of configs.entries()) {
893
- const rebuild = rebuilds[i];
894
- if (!rebuild) continue;
895
- const watcher = await watchBuild(config, configFiles, rebuild, restart);
896
- cleanCbs.push(() => watcher.close());
897
- }
898
- if (cleanCbs.length) shortcuts(restart);
899
- async function restart() {
900
- for (const clean$1 of cleanCbs) await clean$1();
901
- build(userOptions);
902
- }
903
- }
904
- const dirname$1 = path.dirname(fileURLToPath(import.meta.url));
905
- const pkgRoot = path.resolve(dirname$1, "..");
906
- /**
907
- * Build a single configuration, without watch and shortcuts features.
908
- *
909
- * Internal API, not for public use
910
- *
911
- * @private
912
- * @param config Resolved options
913
- */
914
- async function buildSingle(config, clean) {
915
- const { format: formats, dts, watch, onSuccess } = config;
916
- let ab;
917
- const { hooks, context } = await createHooks$1(config);
918
- await rebuild(true);
919
- if (watch) return () => rebuild();
920
- async function rebuild(first) {
921
- const startTime = performance.now();
922
- await hooks.callHook("build:prepare", context);
923
- ab?.abort();
924
- await clean();
925
- let hasErrors = false;
926
- const isMultiFormat = formats.length > 1;
927
- const chunks = {};
928
- await Promise.all(formats.map(async (format) => {
929
- try {
930
- const buildOptions = await getBuildOptions(config, format, isMultiFormat, false);
931
- await hooks.callHook("build:before", {
932
- ...context,
933
- buildOptions
934
- });
935
- const { output } = await build$1(buildOptions);
936
- chunks[format] = output;
937
- if (format === "cjs" && dts) {
938
- const { output: output$1 } = await build$1(await getBuildOptions(config, format, isMultiFormat, true));
939
- chunks[format].push(...output$1);
940
- }
941
- } catch (error) {
942
- if (watch) {
943
- logger.error(error);
944
- hasErrors = true;
945
- return;
946
- }
947
- throw error;
948
- }
949
- }));
950
- if (hasErrors) return;
951
- await Promise.all([writeExports(config, chunks), copy(config)]);
952
- await Promise.all([publint(config), attw(config)]);
953
- await hooks.callHook("build:done", context);
954
- logger.success(prettyName(config.name), `${first ? "Build" : "Rebuild"} complete in ${green(`${Math.round(performance.now() - startTime)}ms`)}`);
955
- ab = new AbortController();
956
- if (typeof onSuccess === "string") {
957
- const p = exec(onSuccess, [], { nodeOptions: {
958
- shell: true,
959
- stdio: "inherit",
960
- signal: ab.signal
961
- } });
962
- p.then(({ exitCode }) => {
963
- if (exitCode) process.exitCode = exitCode;
964
- });
965
- } else await onSuccess?.(config, ab.signal);
966
- }
967
- }
968
- async function getBuildOptions(config, format, isMultiFormat, cjsDts) {
969
- const { entry, external, plugins: userPlugins, outDir, platform, alias, treeshake, sourcemap, dts, minify, unused, target, define, shims, tsconfig, cwd, report, env, nodeProtocol, loader, name, unbundle } = config;
970
- const plugins = [];
971
- if (nodeProtocol) plugins.push(NodeProtocolPlugin(nodeProtocol));
972
- if (config.pkg || config.skipNodeModulesBundle) plugins.push(ExternalPlugin(config));
973
- if (dts) {
974
- const { dts: dtsPlugin } = await import("rolldown-plugin-dts");
975
- const options = {
976
- tsconfig,
977
- ...dts
978
- };
979
- if (format === "es") plugins.push(dtsPlugin(options));
980
- else if (cjsDts) plugins.push(dtsPlugin({
981
- ...options,
982
- emitDtsOnly: true
983
- }));
984
- }
985
- if (!cjsDts) {
986
- if (unused) {
987
- const { Unused } = await import("unplugin-unused");
988
- plugins.push(Unused.rolldown(unused === true ? {} : unused));
989
- }
990
- if (target) plugins.push(RuntimeHelperCheckPlugin(target), await LightningCSSPlugin({ target }));
991
- plugins.push(ShebangPlugin(cwd, name, isMultiFormat));
992
- }
993
- if (report && !logger.silent) plugins.push(ReportPlugin(report, cwd, cjsDts, name, isMultiFormat));
994
- if (!cjsDts) plugins.push(userPlugins);
995
- const inputOptions = await mergeUserOptions({
996
- input: entry,
997
- cwd,
998
- external,
999
- resolve: {
1000
- alias,
1001
- tsconfigFilename: tsconfig || void 0
1002
- },
1003
- treeshake,
1004
- platform: cjsDts || format === "cjs" ? "node" : platform,
1005
- define: {
1006
- ...define,
1007
- ...Object.keys(env).reduce((acc, key) => {
1008
- const value = JSON.stringify(env[key]);
1009
- acc[`process.env.${key}`] = value;
1010
- acc[`import.meta.env.${key}`] = value;
1011
- return acc;
1012
- }, Object.create(null))
1013
- },
1014
- transform: { target },
1015
- plugins,
1016
- inject: { ...shims && !cjsDts && getShimsInject(format, platform) },
1017
- moduleTypes: loader
1018
- }, config.inputOptions, [format]);
1019
- const [entryFileNames, chunkFileNames] = resolveChunkFilename(config, inputOptions, format);
1020
- const outputOptions = await mergeUserOptions({
1021
- format: cjsDts ? "es" : format,
1022
- name: config.globalName,
1023
- sourcemap,
1024
- dir: outDir,
1025
- minify: !cjsDts && minify,
1026
- entryFileNames,
1027
- chunkFileNames,
1028
- preserveModules: unbundle,
1029
- preserveModulesRoot: unbundle ? lowestCommonAncestor(...Object.values(entry)) : void 0
1030
- }, config.outputOptions, [format]);
1031
- return {
1032
- ...inputOptions,
1033
- output: outputOptions
1034
- };
1035
- }
1036
-
1037
- //#endregion
1038
- export { build, buildSingle, defineConfig, logger, pkgRoot };
5
+ export { build, buildSingle, defineConfig, logger, shimFile };