tsdown 0.2.17 → 0.3.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.
@@ -8,12 +8,10 @@ function ExternalPlugin(pkg, platform) {
8
8
  resolveId(id) {
9
9
  let shouldExternal = deps && deps.some((dep) => id === dep || id.startsWith(`${dep}/`));
10
10
  shouldExternal ||= platform === "node" && isBuiltin(id);
11
- if (shouldExternal) {
12
- return {
13
- id,
14
- external: true
15
- };
16
- }
11
+ if (shouldExternal) return {
12
+ id,
13
+ external: true
14
+ };
17
15
  }
18
16
  };
19
17
  }
@@ -0,0 +1,7 @@
1
+ import type { FSWatcher } from 'chokidar';
2
+ export interface Shortcut {
3
+ key: string;
4
+ description: string;
5
+ action: () => void | Promise<void>;
6
+ }
7
+ export declare function shortcuts(watcher: FSWatcher, rebuild: () => void): void;
@@ -1,2 +1,3 @@
1
1
  import type { ResolvedOptions } from '../options';
2
- export declare function watchBuild(options: ResolvedOptions, rebuild: () => void): Promise<void>;
2
+ import type { FSWatcher } from 'chokidar';
3
+ export declare function watchBuild(options: ResolvedOptions, rebuild: () => void): Promise<FSWatcher>;
package/dist/index.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { logger } from "./logger-e0Fr5WMR.js";
2
- import { ExternalPlugin } from "./external-m3ERo7i1.js";
3
- import { default as process, default as process$1, default as process$2 } from "node:process";
2
+ import { ExternalPlugin } from "./external-DZ7KC16d.js";
3
+ import process, { default as process$1, default as process$2, default as process$3 } from "node:process";
4
4
  import { rolldown } from "rolldown";
5
5
  import { IsolatedDecl } from "unplugin-isolated-decl";
6
6
  import { Unused } from "unplugin-unused";
7
7
  import { access, readdir, rm, stat } from "node:fs/promises";
8
- import { default as path, default as path$1, default as path$2 } from "node:path";
8
+ import path, { default as path$1, default as path$2 } from "node:path";
9
9
  import { glob, glob as glob$1 } from "tinyglobby";
10
10
  import { readPackageJSON } from "pkg-types";
11
- import { default as pc, default as pc$1 } from "picocolors";
11
+ import readline from "node:readline";
12
+ import pc, { default as pc$1, default as pc$2 } from "picocolors";
12
13
  import { loadConfig } from "unconfig";
13
14
 
14
15
  //#region src/utils/fs.ts
@@ -21,19 +22,15 @@ function fsExists(path$3) {
21
22
  async function cleanOutDir(cwd, patterns) {
22
23
  const files = [];
23
24
  if (await fsExists(cwd)) files.push(...(await readdir(cwd)).map((file) => path$2.resolve(cwd, file)));
24
- if (patterns.length) {
25
- files.push(...await glob$1(patterns, {
26
- cwd,
27
- absolute: true
28
- }));
29
- }
25
+ if (patterns.length) files.push(...await glob$1(patterns, {
26
+ cwd,
27
+ absolute: true
28
+ }));
30
29
  logger.info("Cleaning output folder");
31
- for (const file of files) {
32
- await rm(file, {
33
- force: true,
34
- recursive: true
35
- });
36
- }
30
+ for (const file of files) await rm(file, {
31
+ force: true,
32
+ recursive: true
33
+ });
37
34
  }
38
35
 
39
36
  //#endregion
@@ -46,9 +43,7 @@ async function readPackageJson(dir) {
46
43
  }
47
44
  function getPackageType(pkg) {
48
45
  if (pkg?.type) {
49
- if (!["module", "commonjs"].includes(pkg.type)) {
50
- throw new Error(`Invalid package.json type: ${pkg.type}`);
51
- }
46
+ if (!["module", "commonjs"].includes(pkg.type)) throw new Error(`Invalid package.json type: ${pkg.type}`);
52
47
  return pkg.type;
53
48
  }
54
49
  return "commonjs";
@@ -62,27 +57,71 @@ function resolveOutputExtension(pkg, format) {
62
57
  case "iife": return "js";
63
58
  case "es":
64
59
  case "esm":
65
- case "module": {
66
- return moduleType === "module" ? "js" : "mjs";
67
- }
60
+ case "module": return moduleType === "module" ? "js" : "mjs";
68
61
  case "cjs":
69
- case "commonjs": {
70
- return moduleType === "module" ? "cjs" : "js";
62
+ case "commonjs": return moduleType === "module" ? "cjs" : "js";
63
+ }
64
+ }
65
+
66
+ //#endregion
67
+ //#region src/features/shortcuts.ts
68
+ function shortcuts(watcher, rebuild) {
69
+ let actionRunning = false;
70
+ async function onInput(input) {
71
+ if (actionRunning) return;
72
+ const SHORTCUTS = [
73
+ {
74
+ key: "r",
75
+ description: "rebuild",
76
+ action() {
77
+ rebuild();
78
+ }
79
+ },
80
+ {
81
+ key: "c",
82
+ description: "clear console",
83
+ action() {
84
+ console.clear();
85
+ }
86
+ },
87
+ {
88
+ key: "q",
89
+ description: "quit",
90
+ action() {
91
+ rl.close();
92
+ return watcher.close();
93
+ }
94
+ }
95
+ ];
96
+ if (input === "h") {
97
+ const loggedKeys = new Set();
98
+ logger.info(" Shortcuts");
99
+ for (const shortcut$1 of SHORTCUTS) {
100
+ if (loggedKeys.has(shortcut$1.key)) continue;
101
+ loggedKeys.add(shortcut$1.key);
102
+ if (shortcut$1.action == null) continue;
103
+ logger.info(pc$2.dim(" press ") + pc$2.bold(`${shortcut$1.key} + enter`) + pc$2.dim(` to ${shortcut$1.description}`));
104
+ }
105
+ return;
71
106
  }
107
+ const shortcut = SHORTCUTS.find((shortcut$1) => shortcut$1.key === input);
108
+ if (!shortcut) return;
109
+ actionRunning = true;
110
+ await shortcut.action();
111
+ actionRunning = false;
72
112
  }
113
+ const rl = readline.createInterface({ input: process$3.stdin });
114
+ rl.on("line", onInput);
73
115
  }
74
116
 
75
117
  //#endregion
76
118
  //#region src/utils/general.ts
77
119
  function toArray(val, defaultValue) {
78
- if (Array.isArray(val)) {
79
- return val;
80
- } else if (val == null) {
120
+ if (Array.isArray(val)) return val;
121
+ else if (val == null) {
81
122
  if (defaultValue) return [defaultValue];
82
123
  return [];
83
- } else {
84
- return [val];
85
- }
124
+ } else return [val];
86
125
  }
87
126
  function debounce(fn, wait) {
88
127
  let timeout;
@@ -114,31 +153,24 @@ async function watchBuild(options, rebuild) {
114
153
  logger.info(`Change detected: ${type} ${file}`);
115
154
  debouncedRebuild();
116
155
  });
156
+ return watcher;
117
157
  }
118
158
 
119
159
  //#endregion
120
160
  //#region src/features/entry.ts
121
161
  async function resolveEntry(entry) {
122
- if (!entry || Object.keys(entry).length === 0) {
123
- throw new Error(`No input files, try "tsdown <your-file>" instead`);
124
- }
125
- if (typeof entry === "string") {
126
- entry = [entry];
127
- }
162
+ if (!entry || Object.keys(entry).length === 0) throw new Error(`No input files, try "tsdown <your-file>" instead`);
163
+ if (typeof entry === "string") entry = [entry];
128
164
  if (Array.isArray(entry)) {
129
165
  const resolvedEntry = await glob(entry);
130
166
  if (resolvedEntry.length > 0) {
131
167
  entry = resolvedEntry;
132
168
  logger.info(`entry: ${pc$1.blue(entry.join(", "))}`);
133
- } else {
134
- throw new Error(`Cannot find entry: ${entry}`);
135
- }
169
+ } else throw new Error(`Cannot find entry: ${entry}`);
136
170
  } else {
137
171
  const files = Object.values(entry);
138
172
  files.forEach((filename) => {
139
- if (!fsExists(filename)) {
140
- throw new Error(`Cannot find entry: ${filename}`);
141
- }
173
+ if (!fsExists(filename)) throw new Error(`Cannot find entry: ${filename}`);
142
174
  });
143
175
  logger.info(`entry: ${pc$1.blue(files.join(", "))}`);
144
176
  }
@@ -152,7 +184,7 @@ async function normalizeOptions(options) {
152
184
  ...await loadConfigFile(options),
153
185
  ...options
154
186
  };
155
- let { entry, format = ["es"], plugins = [], external, clean = false, silent = false, treeshake = true, platform = "node", outDir = "dist", sourcemap = false, dts = false, unused = false, minify, alias, watch = false, inputOptions, outputOptions } = options;
187
+ let { entry, format = ["es"], plugins = [], external, clean = false, silent = false, treeshake = true, platform = "node", outDir = "dist", sourcemap = false, dts = false, unused = false, minify, alias, watch = false, inputOptions, outputOptions, onSuccess } = options;
156
188
  entry = await resolveEntry(entry);
157
189
  format = toArray(format, "es");
158
190
  if (clean === true) clean = [];
@@ -173,7 +205,8 @@ async function normalizeOptions(options) {
173
205
  minify,
174
206
  watch,
175
207
  inputOptions,
176
- outputOptions
208
+ outputOptions,
209
+ onSuccess
177
210
  };
178
211
  }
179
212
  async function loadConfigFile(options) {
@@ -188,9 +221,7 @@ async function loadConfigFile(options) {
188
221
  overrideConfig = true;
189
222
  filePath = resolved;
190
223
  cwd = path.dirname(filePath);
191
- } else {
192
- cwd = resolved;
193
- }
224
+ } else cwd = resolved;
194
225
  }
195
226
  const { config, sources } = await loadConfig({
196
227
  sources: overrideConfig ? [{
@@ -216,9 +247,7 @@ async function loadConfigFile(options) {
216
247
  cwd,
217
248
  defaults: {}
218
249
  });
219
- if (sources.length > 0) {
220
- logger.info(`Using tsdown config: ${pc.underline(sources.join(", "))}`);
221
- }
250
+ if (sources.length > 0) logger.info(`Using tsdown config: ${pc.underline(sources.join(", "))}`);
222
251
  return config;
223
252
  }
224
253
 
@@ -226,49 +255,51 @@ async function loadConfigFile(options) {
226
255
  //#region src/index.ts
227
256
  async function build(userOptions = {}) {
228
257
  const resolved = await normalizeOptions(userOptions);
229
- const { entry, external, plugins, outDir, format, clean, platform, alias, treeshake, sourcemap, dts, minify, watch, unused } = resolved;
258
+ const { entry, external, plugins, outDir, format, clean, platform, alias, treeshake, sourcemap, dts, minify, watch, unused, onSuccess } = resolved;
230
259
  if (clean) await cleanOutDir(outDir, clean);
231
260
  const pkg = await readPackageJson(process.cwd());
232
- let startTime;
233
- await rebuild(true);
234
- if (watch) {
235
- await watchBuild(resolved, rebuild);
236
- }
237
- async function rebuild(first) {
238
- startTime = performance.now();
239
- const inputOptions = {
240
- input: entry,
241
- external,
242
- resolve: { alias },
243
- treeshake,
244
- platform,
245
- plugins: [
246
- ExternalPlugin(pkg, platform),
247
- dts && IsolatedDecl.rolldown(dts === true ? {} : dts),
248
- unused && Unused.rolldown(unused === true ? {} : unused),
249
- ...plugins
250
- ].filter((plugin) => !!plugin),
251
- ...resolved.inputOptions
261
+ let startTime = performance.now();
262
+ const inputOptions = {
263
+ input: entry,
264
+ external,
265
+ resolve: { alias },
266
+ treeshake,
267
+ platform,
268
+ plugins: [
269
+ ExternalPlugin(pkg, platform),
270
+ dts && IsolatedDecl.rolldown(dts === true ? {} : dts),
271
+ unused && Unused.rolldown(unused === true ? {} : unused),
272
+ ...plugins
273
+ ].filter((plugin) => !!plugin),
274
+ ...resolved.inputOptions
275
+ };
276
+ const build$1 = await rolldown(inputOptions);
277
+ const outputOptions = await Promise.all(format.map(async (format$1) => {
278
+ const extension = resolveOutputExtension(pkg, format$1);
279
+ const outputOptions$1 = {
280
+ format: format$1,
281
+ sourcemap,
282
+ dir: outDir,
283
+ minify,
284
+ entryFileNames: `[name].${extension}`,
285
+ chunkFileNames: `[name]-[hash].${extension}`
286
+ };
287
+ const userOutputOptions = typeof resolved.outputOptions === "function" ? await resolved.outputOptions(outputOptions$1, format$1) : resolved.outputOptions;
288
+ return {
289
+ ...outputOptions$1,
290
+ ...userOutputOptions
252
291
  };
253
- const build$1 = await rolldown(inputOptions);
254
- await Promise.all(format.map(async (format$1) => {
255
- const extension = resolveOutputExtension(pkg, format$1);
256
- const outputOptions = {
257
- format: format$1,
258
- sourcemap,
259
- dir: outDir,
260
- minify,
261
- entryFileNames: `[name].${extension}`,
262
- chunkFileNames: `[name]-[hash].${extension}`
263
- };
264
- const userOutputOptions = typeof resolved.outputOptions === "function" ? await resolved.outputOptions(outputOptions, format$1) : resolved.outputOptions;
265
- return await build$1.write({
266
- ...outputOptions,
267
- ...userOutputOptions
268
- });
269
- }));
270
- await build$1.close();
292
+ }));
293
+ await writeBundle(true);
294
+ if (watch) {
295
+ const watcher = await watchBuild(resolved, writeBundle);
296
+ shortcuts(watcher, writeBundle);
297
+ } else await build$1.close();
298
+ async function writeBundle(first) {
299
+ if (!first) startTime = performance.now();
300
+ await Promise.all(outputOptions.map((outputOptions$1) => build$1.write(outputOptions$1)));
271
301
  logger.success(`${first ? "Build" : "Rebuild"} complete in ${Math.round(performance.now() - startTime)}ms`);
302
+ await onSuccess?.();
272
303
  }
273
304
  }
274
305
  function defineConfig(options) {
package/dist/options.d.ts CHANGED
@@ -36,12 +36,13 @@ export interface Options {
36
36
  watch?: boolean | string | string[];
37
37
  inputOptions?: InputOptions;
38
38
  outputOptions?: OutputOptions | ((options: OutputOptions, format: Format) => MaybePromise<OutputOptions | void | null>);
39
+ onSuccess?: () => void | Promise<void>;
39
40
  }
40
41
  /**
41
42
  * Options without specifying config file path.
42
43
  */
43
44
  export type OptionsWithoutConfig = Omit<Options, 'config'>;
44
- export type ResolvedOptions = Omit<Overwrite<MarkPartial<Options, 'inputOptions' | 'outputOptions' | 'minify' | 'alias' | 'external'>, {
45
+ export type ResolvedOptions = Omit<Overwrite<MarkPartial<Options, 'inputOptions' | 'outputOptions' | 'minify' | 'alias' | 'external' | 'onSuccess'>, {
45
46
  format: Format[];
46
47
  clean: string[] | false;
47
48
  }>, 'config'>;
package/dist/plugins.js CHANGED
@@ -1,3 +1,3 @@
1
- import { ExternalPlugin } from "./external-m3ERo7i1.js";
1
+ import { ExternalPlugin } from "./external-DZ7KC16d.js";
2
2
 
3
3
  export { ExternalPlugin };
package/dist/run.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import { logger } from "./logger-e0Fr5WMR.js";
2
- import { default as process } from "node:process";
2
+ import process from "node:process";
3
3
  import { VERSION as rolldownVersion } from "rolldown";
4
- import { default as pc } from "picocolors";
4
+ import pc from "picocolors";
5
5
  import { cac } from "cac";
6
6
 
7
7
  //#region package.json
8
- const version = "0.2.17";
8
+ const version = "0.3.0";
9
9
 
10
10
  //#endregion
11
11
  //#region src/cli.ts
12
12
  async function runCLI() {
13
13
  const cli = cac("tsdown");
14
- cli.command("[...files]", "Bundle files", { ignoreOptionDefaultValue: true }).option("-c, --config <filename>", "Use a custom config file").option("--no-config", "Disable config file").option("--format <format>", "Bundle format: esm, cjs, iife", { default: "esm" }).option("--clean", "Clean output directory").option("--minify", "Minify output").option("--silent", "Suppress non-error logs").option("-d, --out-dir <dir>", "Output directory", { default: "dist" }).option("--treeshake", "Tree-shake bundle", { default: true }).option("--sourcemap", "Generate source map", { default: false }).option("--platform <platform>", "Target platform", { default: "node" }).option("--watch", "Watch mode").action(async (input, flags) => {
14
+ cli.command("[...files]", "Bundle files", { ignoreOptionDefaultValue: true }).option("-c, --config <filename>", "Use a custom config file").option("--no-config", "Disable config file").option("--format <format>", "Bundle format: esm, cjs, iife", { default: "esm" }).option("--clean", "Clean output directory").option("--minify", "Minify output").option("--silent", "Suppress non-error logs").option("-d, --out-dir <dir>", "Output directory", { default: "dist" }).option("--treeshake", "Tree-shake bundle", { default: true }).option("--sourcemap", "Generate source map", { default: false }).option("--platform <platform>", "Target platform", { default: "node" }).option("-w, --watch", "Watch mode").action(async (input, flags) => {
15
15
  logger.level = flags.silent ? 0 : 3;
16
16
  logger.info(`tsdown ${pc.dim(`v${version}`)} powered by rolldown ${pc.dim(`v${rolldownVersion}`)}`);
17
17
  const { build } = await import("./index.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsdown",
3
- "version": "0.2.17",
3
+ "version": "0.3.0",
4
4
  "description": "An even faster bundler powered by Rolldown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,27 +42,27 @@
42
42
  "cac": "^6.7.14",
43
43
  "chokidar": "^4.0.1",
44
44
  "consola": "^3.2.3",
45
- "picocolors": "^1.1.0",
46
- "pkg-types": "^1.2.0",
45
+ "picocolors": "^1.1.1",
46
+ "pkg-types": "^1.2.1",
47
47
  "rolldown": "nightly",
48
- "tinyglobby": "^0.2.6",
48
+ "tinyglobby": "^0.2.10",
49
49
  "unconfig": "^0.6.0",
50
- "unplugin-isolated-decl": "^0.6.5",
50
+ "unplugin-isolated-decl": "^0.6.8",
51
51
  "unplugin-unused": "^0.2.3"
52
52
  },
53
53
  "devDependencies": {
54
- "@sxzz/eslint-config": "^4.2.0",
54
+ "@sxzz/eslint-config": "^4.4.0",
55
55
  "@sxzz/prettier-config": "^2.0.2",
56
- "@types/node": "^22.7.4",
57
- "bumpp": "^9.6.1",
58
- "eslint": "^9.11.1",
59
- "execa": "^9.4.0",
60
- "fdir": "^6.3.0",
56
+ "@types/node": "^22.8.2",
57
+ "bumpp": "^9.8.0",
58
+ "eslint": "^9.13.0",
59
+ "execa": "^9.5.1",
60
+ "fdir": "^6.4.2",
61
61
  "prettier": "^3.3.3",
62
- "tsup": "^8.3.0",
63
- "tsx": "^4.19.1",
64
- "typescript": "~5.6.2",
65
- "vitest": "^2.1.1"
62
+ "tsup": "^8.3.5",
63
+ "tsx": "^4.19.2",
64
+ "typescript": "~5.6.3",
65
+ "vitest": "^2.1.4"
66
66
  },
67
67
  "engines": {
68
68
  "node": ">=18.0.0"