tsdown 0.2.16 → 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 ? [{
@@ -198,7 +229,16 @@ async function loadConfigFile(options) {
198
229
  extensions: []
199
230
  }] : [{
200
231
  files: "tsdown.config",
201
- extensions: ["ts", "mts", "cts", "js", "mjs", "cjs", "json", ""]
232
+ extensions: [
233
+ "ts",
234
+ "mts",
235
+ "cts",
236
+ "js",
237
+ "mjs",
238
+ "cjs",
239
+ "json",
240
+ ""
241
+ ]
202
242
  }, {
203
243
  files: "package.json",
204
244
  extensions: [],
@@ -207,9 +247,7 @@ async function loadConfigFile(options) {
207
247
  cwd,
208
248
  defaults: {}
209
249
  });
210
- if (sources.length > 0) {
211
- logger.info(`Using tsdown config: ${pc.underline(sources.join(", "))}`);
212
- }
250
+ if (sources.length > 0) logger.info(`Using tsdown config: ${pc.underline(sources.join(", "))}`);
213
251
  return config;
214
252
  }
215
253
 
@@ -217,7 +255,7 @@ async function loadConfigFile(options) {
217
255
  //#region src/index.ts
218
256
  async function build(userOptions = {}) {
219
257
  const resolved = await normalizeOptions(userOptions);
220
- 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;
221
259
  if (clean) await cleanOutDir(outDir, clean);
222
260
  const pkg = await readPackageJson(process.cwd());
223
261
  let startTime = performance.now();
@@ -227,35 +265,41 @@ async function build(userOptions = {}) {
227
265
  resolve: { alias },
228
266
  treeshake,
229
267
  platform,
230
- plugins: [ExternalPlugin(pkg, platform), dts && IsolatedDecl.rolldown(dts === true ? {} : dts), unused && Unused.rolldown(unused === true ? {} : unused), ...plugins].filter((plugin) => !!plugin),
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),
231
274
  ...resolved.inputOptions
232
275
  };
233
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
291
+ };
292
+ }));
234
293
  await writeBundle(true);
235
294
  if (watch) {
236
- await watchBuild(resolved, writeBundle);
237
- } else {
238
- await build$1.close();
239
- }
295
+ const watcher = await watchBuild(resolved, writeBundle);
296
+ shortcuts(watcher, writeBundle);
297
+ } else await build$1.close();
240
298
  async function writeBundle(first) {
241
299
  if (!first) startTime = performance.now();
242
- await Promise.all(format.map(async (format$1) => {
243
- const extension = resolveOutputExtension(pkg, format$1);
244
- const outputOptions = {
245
- format: format$1,
246
- sourcemap,
247
- dir: outDir,
248
- minify,
249
- entryFileNames: `[name].${extension}`,
250
- chunkFileNames: `[name]-[hash].${extension}`
251
- };
252
- const userOutputOptions = typeof resolved.outputOptions === "function" ? await resolved.outputOptions(outputOptions, format$1) : resolved.outputOptions;
253
- return await build$1.write({
254
- ...outputOptions,
255
- ...userOutputOptions
256
- });
257
- }));
300
+ await Promise.all(outputOptions.map((outputOptions$1) => build$1.write(outputOptions$1)));
258
301
  logger.success(`${first ? "Build" : "Rebuild"} complete in ${Math.round(performance.now() - startTime)}ms`);
302
+ await onSuccess?.();
259
303
  }
260
304
  }
261
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.16";
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.16",
3
+ "version": "0.3.0",
4
4
  "description": "An even faster bundler powered by Rolldown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,29 +40,29 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "cac": "^6.7.14",
43
- "chokidar": "^4.0.0",
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",
49
- "unconfig": "^0.5.5",
50
- "unplugin-isolated-decl": "^0.6.2",
48
+ "tinyglobby": "^0.2.10",
49
+ "unconfig": "^0.6.0",
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.5.5",
57
- "bumpp": "^9.5.2",
58
- "eslint": "^9.10.0",
59
- "execa": "^9.3.1",
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.2.4",
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"