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.
- package/dist/cli-BwOoKKTM.mjs +46 -0
- package/dist/cli.d.mts +4 -0
- package/dist/cli.mjs +5 -0
- package/dist/{config-CEEvti7D.d.mts → config-77IY7Jdj.d.mts} +1 -1
- package/dist/config-DsBEbB6I.mjs +7 -0
- package/dist/config.d.mts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/logger-YArsxPto.mjs +144 -0
- package/dist/{migrate-BghKfLMP.mjs → migrate-BCQUL2Gs.mjs} +1 -1
- package/dist/migrate-Cl62m5IG.mjs +126 -0
- package/dist/migrate.d.mts +10 -0
- package/dist/migrate.mjs +126 -0
- package/dist/package-DOiAuQ-5.mjs +5 -0
- package/dist/package-DwljelGW.mjs +5 -0
- package/dist/package-TDRl9aMv.mjs +5 -0
- package/dist/plugins.d.mts +1 -1
- package/dist/plugins.mjs +1 -1
- package/dist/run.mjs +2 -2
- package/dist/src-D_PDH4ZS.mjs +1335 -0
- package/dist/src-lmHdfLhi.mjs +1335 -0
- package/dist/{types-QuuoDTDl.d.mts → types-BMMC0pbr.d.mts} +107 -101
- package/package.json +5 -5
- package/dist/package-C-PL6-Mh.mjs +0 -5
|
@@ -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 };
|