tsdown 0.2.3 → 0.2.5

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.
@@ -4,10 +4,10 @@ import { isBuiltin } from "node:module";
4
4
  function ExternalPlugin(pkg, platform) {
5
5
  const deps = pkg && getProductionDeps(pkg);
6
6
  return {
7
- name: 'tsdown:external',
7
+ name: "tsdown:external",
8
8
  resolveId(id) {
9
9
  let shouldExternal = deps?.has(id);
10
- shouldExternal ||= platform === 'node' && isBuiltin(id);
10
+ shouldExternal ||= platform === "node" && isBuiltin(id);
11
11
  if (shouldExternal) {
12
12
  return {
13
13
  id,
@@ -1 +1 @@
1
- export declare function cleanOutDir(cwd: string, patterns: (string)[]): Promise<void>;
1
+ export declare function cleanOutDir(cwd: string, patterns: string[]): Promise<void>;
@@ -1,2 +1,2 @@
1
1
  import type { Options } from '../options';
2
- export declare function resolveEntry(entry: Options['entry']): Promise<(((string)[]) | (Record<string, string>))>;
2
+ export declare function resolveEntry(entry: Options['entry']): Promise<string[] | Record<string, string>>;
@@ -1,6 +1,6 @@
1
- import type { PackageJson } from 'pkg-types';
2
1
  import type { ResolvedOptions } from '../options';
2
+ import type { PackageJson } from 'pkg-types';
3
3
  import type { InputOptions, Plugin } from 'rolldown';
4
4
  export type External = InputOptions['external'];
5
- export declare function ExternalPlugin(pkg: ((PackageJson) | (undefined)), platform: ResolvedOptions['platform']): Plugin;
5
+ export declare function ExternalPlugin(pkg: PackageJson | undefined, platform: ResolvedOptions['platform']): Plugin;
6
6
  export declare function getProductionDeps(pkg: PackageJson): Set<string>;
@@ -1,2 +1,2 @@
1
1
  import type { Format } from '../options';
2
- export declare function resolveOutputExtension(pkg: any, format: Format): (('mjs') | ('cjs') | ('js'));
2
+ export declare function resolveOutputExtension(pkg: any, format: Format): 'mjs' | 'cjs' | 'js';
@@ -0,0 +1,2 @@
1
+ import type { ResolvedOptions } from '../options';
2
+ export declare function watchBuild(options: ResolvedOptions, rebuild: () => void): Promise<void>;
package/dist/index.js CHANGED
@@ -1,17 +1,111 @@
1
- import { logger } from "./logger-39fy7Hdx.js";
2
- import { ExternalPlugin } from "./external-BI3U5n3V.js";
3
- import { default as process, default as process$1 } from "node:process";
1
+ import { logger } from "./logger-e0Fr5WMR.js";
2
+ import { ExternalPlugin } from "./external-uGOHYulu.js";
3
+ import { default as process, default as process$1, default as process$2 } from "node:process";
4
4
  import { rolldown } from "rolldown";
5
5
  import { IsolatedDecl } from "unplugin-isolated-decl";
6
6
  import { access, readdir, rm, stat } from "node:fs/promises";
7
- import { default as path, default as path$1, default as path$2 } from "node:path";
8
- import { loadConfig } from "unconfig";
9
- import { globby, globby as globby$1 } from "globby";
7
+ import { default as path, default as path$1, default as path$2, default as path$3 } from "node:path";
8
+ import { default as glob, default as glob$1 } from "fast-glob";
10
9
  import { readPackageJSON } from "pkg-types";
10
+ import { loadConfig } from "unconfig";
11
11
 
12
12
  //#region src/utils/fs.ts
13
- function fsExists(path$3) {
14
- return access(path$3).then(() => true, () => false);
13
+ function fsExists(path$4) {
14
+ return access(path$4).then(() => true, () => false);
15
+ }
16
+
17
+ //#endregion
18
+ //#region src/features/clean.ts
19
+ async function cleanOutDir(cwd, patterns) {
20
+ const files = [];
21
+ if (await fsExists(cwd)) files.push(...(await readdir(cwd)).map((file) => path$3.resolve(cwd, file)));
22
+ if (patterns.length) {
23
+ files.push(...await glob$1(patterns, {
24
+ cwd,
25
+ absolute: true
26
+ }));
27
+ }
28
+ logger.info("Cleaning output folder");
29
+ for (const file of files) {
30
+ await rm(file, {
31
+ force: true,
32
+ recursive: true
33
+ });
34
+ }
35
+ }
36
+
37
+ //#endregion
38
+ //#region src/utils/package.ts
39
+ async function readPackageJson(dir) {
40
+ const packageJsonPath = path$2.join(dir, "package.json");
41
+ const exists = await fsExists(packageJsonPath);
42
+ if (!exists) return;
43
+ return readPackageJSON(packageJsonPath);
44
+ }
45
+ function getPackageType(pkg) {
46
+ if (pkg?.type) {
47
+ if (!["module", "commonjs"].includes(pkg.type)) {
48
+ throw new Error(`Invalid package.json type: ${pkg.type}`);
49
+ }
50
+ return pkg.type;
51
+ }
52
+ return "commonjs";
53
+ }
54
+
55
+ //#endregion
56
+ //#region src/features/output.ts
57
+ function resolveOutputExtension(pkg, format) {
58
+ const type = getPackageType(pkg);
59
+ const isEsmFormat = ["es", "esm", "module"].includes(format);
60
+ if (type === "module") {
61
+ if (isEsmFormat) return "js";
62
+ else return "cjs";
63
+ } else {
64
+ if (isEsmFormat) return "mjs";
65
+ return "js";
66
+ }
67
+ }
68
+
69
+ //#endregion
70
+ //#region src/utils/general.ts
71
+ function toArray(val, defaultValue) {
72
+ if (Array.isArray(val)) {
73
+ return val;
74
+ } else if (val == null) {
75
+ if (defaultValue) return [defaultValue];
76
+ return [];
77
+ } else {
78
+ return [val];
79
+ }
80
+ }
81
+ function debounce(fn, wait) {
82
+ let timeout;
83
+ return function(...args) {
84
+ if (timeout) clearTimeout(timeout);
85
+ timeout = setTimeout(() => {
86
+ timeout = undefined;
87
+ fn.apply(this, args);
88
+ }, wait);
89
+ };
90
+ }
91
+
92
+ //#endregion
93
+ //#region src/features/watch.ts
94
+ async function watchBuild(options, rebuild) {
95
+ const { watch } = await import("chokidar");
96
+ const debouncedRebuild = debounce(rebuild, 100);
97
+ const files = typeof options.watch === "boolean" ? process$2.cwd() : options.watch;
98
+ const ignored = ["**/{.git,node_modules}/**", path$1.resolve(options.outDir)];
99
+ logger.info(`Watching for changes in ${toArray(files).join(", ")}`);
100
+ const watcher = watch(files, {
101
+ ignoreInitial: true,
102
+ ignorePermissionErrors: true,
103
+ ignored
104
+ });
105
+ watcher.on("all", (type, file) => {
106
+ logger.info(`Change detected: ${type} ${file}`);
107
+ debouncedRebuild();
108
+ });
15
109
  }
16
110
 
17
111
  //#endregion
@@ -20,14 +114,14 @@ async function resolveEntry(entry) {
20
114
  if (!entry || Object.keys(entry).length === 0) {
21
115
  throw new Error(`No input files, try "tsdown <your-file>" instead`);
22
116
  }
23
- if (typeof entry === 'string') {
117
+ if (typeof entry === "string") {
24
118
  entry = [entry];
25
119
  }
26
120
  if (Array.isArray(entry)) {
27
- const resolvedEntry = await globby$1(entry);
121
+ const resolvedEntry = await glob(entry);
28
122
  if (resolvedEntry.length > 0) {
29
123
  entry = resolvedEntry;
30
- logger.info(`entry: ${entry.join(', ')}`);
124
+ logger.info(`entry: ${entry.join(", ")}`);
31
125
  } else {
32
126
  throw new Error(`Cannot find entry: ${entry}`);
33
127
  }
@@ -38,24 +132,11 @@ async function resolveEntry(entry) {
38
132
  throw new Error(`Cannot find entry: ${filename}`);
39
133
  }
40
134
  });
41
- logger.info(`entry: ${files.join(', ')}`);
135
+ logger.info(`entry: ${files.join(", ")}`);
42
136
  }
43
137
  return entry;
44
138
  }
45
139
 
46
- //#endregion
47
- //#region src/utils/general.ts
48
- function toArray(val, defaultValue) {
49
- if (Array.isArray(val)) {
50
- return val;
51
- } else if (val == null) {
52
- if (defaultValue) return [defaultValue];
53
- return [];
54
- } else {
55
- return [val];
56
- }
57
- }
58
-
59
140
  //#endregion
60
141
  //#region src/options.ts
61
142
  async function normalizeOptions(options) {
@@ -63,9 +144,9 @@ async function normalizeOptions(options) {
63
144
  ...await loadConfigFile(options),
64
145
  ...options
65
146
  };
66
- let { entry, format = ['es'], plugins = [], external = [], clean = false, treeshake = true, platform = 'node', outDir = 'dist', dts = false, alias = {} } = options;
147
+ let { entry, format = ["es"], plugins = [], external, clean = false, treeshake = true, platform = "node", outDir = "dist", sourcemap = false, dts = false, minify, alias, watch = false, inputOptions, outputOptions } = options;
67
148
  entry = await resolveEntry(entry);
68
- format = toArray(format, 'es');
149
+ format = toArray(format, "es");
69
150
  if (clean === true) clean = [];
70
151
  return {
71
152
  entry,
@@ -77,7 +158,12 @@ async function normalizeOptions(options) {
77
158
  alias,
78
159
  treeshake,
79
160
  platform,
80
- dts
161
+ sourcemap,
162
+ dts,
163
+ minify,
164
+ watch,
165
+ inputOptions,
166
+ outputOptions
81
167
  };
82
168
  }
83
169
  async function loadConfigFile(options) {
@@ -86,12 +172,12 @@ async function loadConfigFile(options) {
86
172
  let cwd = process$1.cwd();
87
173
  let overrideConfig = false;
88
174
  let stats;
89
- if (typeof filePath === 'string' && (stats = await stat(filePath).catch(() => null))) {
90
- const resolved = path$2.resolve(filePath);
175
+ if (typeof filePath === "string" && (stats = await stat(filePath).catch(() => null))) {
176
+ const resolved = path.resolve(filePath);
91
177
  if (stats.isFile()) {
92
178
  overrideConfig = true;
93
179
  filePath = resolved;
94
- cwd = path$2.dirname(filePath);
180
+ cwd = path.dirname(filePath);
95
181
  } else {
96
182
  cwd = resolved;
97
183
  }
@@ -101,10 +187,10 @@ async function loadConfigFile(options) {
101
187
  files: filePath,
102
188
  extensions: []
103
189
  }] : [{
104
- files: 'tsdown.config',
105
- extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs', 'json', '']
190
+ files: "tsdown.config",
191
+ extensions: ["ts", "mts", "cts", "js", "mjs", "cjs", "json", ""]
106
192
  }, {
107
- files: 'package.json',
193
+ files: "package.json",
108
194
  extensions: [],
109
195
  rewrite: (config$1) => config$1?.tsdown
110
196
  },],
@@ -112,89 +198,55 @@ async function loadConfigFile(options) {
112
198
  defaults: {}
113
199
  });
114
200
  if (sources.length > 0) {
115
- logger.info(`Using tsdown config: ${sources.join(', ')}`);
201
+ logger.info(`Using tsdown config: ${sources.join(", ")}`);
116
202
  }
117
203
  return config;
118
204
  }
119
205
 
120
- //#endregion
121
- //#region src/features/clean.ts
122
- async function cleanOutDir(cwd, patterns) {
123
- const files = [];
124
- if (await fsExists(cwd)) files.push(...(await readdir(cwd)).map((file) => path$1.resolve(cwd, file)));
125
- if (patterns.length) {
126
- files.push(...await globby(patterns, {
127
- cwd,
128
- absolute: true
129
- }));
130
- }
131
- logger.info('Cleaning output folder');
132
- for (const file of files) {
133
- await rm(file, {
134
- force: true,
135
- recursive: true
136
- });
137
- }
138
- }
139
-
140
- //#endregion
141
- //#region src/utils/package.ts
142
- async function readPackageJson(dir) {
143
- const packageJsonPath = path.join(dir, 'package.json');
144
- const exists = await fsExists(packageJsonPath);
145
- if (!exists) return;
146
- return readPackageJSON(packageJsonPath);
147
- }
148
- function getPackageType(pkg) {
149
- if (pkg.type) {
150
- if (!['module', 'commonjs'].includes(pkg.type)) {
151
- throw new Error(`Invalid package.json type: ${pkg.type}`);
152
- }
153
- return pkg.type;
154
- }
155
- return 'commonjs';
156
- }
157
-
158
- //#endregion
159
- //#region src/features/output.ts
160
- function resolveOutputExtension(pkg, format) {
161
- const type = getPackageType(pkg);
162
- const isEsmFormat = ['es', 'esm', 'module'].includes(format);
163
- if (type === 'module') {
164
- if (isEsmFormat) return 'js';
165
- else return 'cjs';
166
- } else {
167
- if (isEsmFormat) return 'mjs';
168
- return 'js';
169
- }
170
- }
171
-
172
206
  //#endregion
173
207
  //#region src/index.ts
174
208
  async function build(userOptions = {}) {
175
- const { entry, external, plugins, outDir, format, clean, platform, alias, treeshake, dts } = await normalizeOptions(userOptions);
209
+ const resolved = await normalizeOptions(userOptions);
210
+ const { entry, external, plugins, outDir, format, clean, platform, alias, treeshake, sourcemap, dts, minify, watch } = resolved;
176
211
  if (clean) await cleanOutDir(outDir, clean);
177
212
  const pkg = await readPackageJson(process.cwd());
213
+ let startTime = performance.now();
178
214
  const inputOptions = {
179
215
  input: entry,
180
216
  external,
181
- resolve: {alias},
217
+ resolve: { alias },
182
218
  treeshake,
183
- plugins: [ExternalPlugin(pkg, platform), dts && IsolatedDecl.rolldown(dts === true ? {} : dts), ...plugins,].filter((plugin) => !!plugin)
219
+ plugins: [ExternalPlugin(pkg, platform), dts && IsolatedDecl.rolldown(dts === true ? {} : dts), ...plugins,].filter((plugin) => !!plugin),
220
+ ...resolved.inputOptions
184
221
  };
185
222
  const build$1 = await rolldown(inputOptions);
186
- await Promise.all(format.map((format$1) => {
187
- const extension = resolveOutputExtension(pkg, format$1);
188
- return build$1.write({
189
- format: format$1,
190
- dir: outDir,
191
- entryFileNames: `[name].${extension}`,
192
- chunkFileNames: `[name]-[hash].${extension}`
193
- });
194
- }));
195
- await build$1.destroy();
196
- logger.info('Build complete');
197
- process.exit(0);
223
+ await writeBundle(true);
224
+ if (watch) {
225
+ await watchBuild(resolved, writeBundle);
226
+ } else {
227
+ await build$1.destroy();
228
+ process.exit(0);
229
+ }
230
+ async function writeBundle(first) {
231
+ if (!first) startTime = performance.now();
232
+ await Promise.all(format.map(async (format$1) => {
233
+ const extension = resolveOutputExtension(pkg, format$1);
234
+ const outputOptions = {
235
+ format: format$1,
236
+ sourcemap,
237
+ dir: outDir,
238
+ minify,
239
+ entryFileNames: `[name].${extension}`,
240
+ chunkFileNames: `[name]-[hash].${extension}`
241
+ };
242
+ const userOutputOptions = typeof resolved.outputOptions === "function" ? await resolved.outputOptions(outputOptions, format$1) : resolved.outputOptions;
243
+ return await build$1.write({
244
+ ...outputOptions,
245
+ ...userOutputOptions
246
+ });
247
+ }));
248
+ logger.success(`${first ? "Build" : "Rebuild"} complete in ${Math.round(performance.now() - startTime)}ms`);
249
+ }
198
250
  }
199
251
  function defineConfig(options) {
200
252
  return options;
@@ -1,7 +1,7 @@
1
1
  import { consola } from "consola";
2
2
 
3
3
  //#region src/utils/logger.ts
4
- const logger = consola.withTag('tsdown');
4
+ const logger = consola.withTag("tsdown");
5
5
 
6
6
  //#endregion
7
7
  export { logger };
package/dist/options.d.ts CHANGED
@@ -1,25 +1,30 @@
1
1
  import type { External } from './features/external';
2
- import type { InputOptions } from 'rolldown';
2
+ import type { MarkPartial, MaybePromise, Overwrite } from './utils/types';
3
+ import type { InputOptions, OutputOptions } from 'rolldown';
3
4
  import type { Options as IsolatedDeclOptions } from 'unplugin-isolated-decl';
4
- export type Format = (('es') | ('esm') | ('module') | ('cjs') | ('commonjs'));
5
+ export type Format = 'es' | 'esm' | 'module' | 'cjs' | 'commonjs';
6
+ export type Sourcemap = boolean | 'inline' | 'hidden';
5
7
  export interface Options {
6
8
  entry?: InputOptions['input'];
7
- format?: ((Format) | ((Format)[]));
9
+ format?: Format | Format[];
8
10
  plugins?: InputOptions['plugins'];
9
11
  external?: External;
10
12
  outDir?: string;
11
- clean?: ((boolean) | ((string)[]));
12
- config?: ((boolean) | (string));
13
+ clean?: boolean | string[];
14
+ config?: boolean | string;
15
+ sourcemap?: Sourcemap;
13
16
  alias?: Record<string, string>;
14
17
  treeshake?: boolean;
15
- platform?: (('node') | ('neutral'));
16
- dts?: ((boolean) | (IsolatedDeclOptions));
18
+ minify?: boolean;
19
+ platform?: 'node' | 'neutral';
20
+ dts?: boolean | IsolatedDeclOptions;
21
+ watch?: boolean | string | string[];
22
+ inputOptions?: InputOptions;
23
+ outputOptions?: OutputOptions | ((options: OutputOptions, format: Format) => MaybePromise<OutputOptions | void | null>);
17
24
  }
18
25
  export type OptionsWithoutConfig = Omit<Options, 'config'>;
19
- type Overwrite<T, U> = (({ [P in Exclude<keyof T, keyof U>] : T[P]}) & (U));
20
- export type ResolvedOptions = Omit<Overwrite<Required<Options>, {
21
- format: (Format)[];
22
- clean: (((string)[]) | (false));
26
+ export type ResolvedOptions = Omit<Overwrite<MarkPartial<Options, 'inputOptions' | 'outputOptions' | 'minify' | 'alias' | 'external'>, {
27
+ format: Format[];
28
+ clean: string[] | false;
23
29
  }>, 'config'>;
24
30
  export declare function normalizeOptions(options: Options): Promise<ResolvedOptions>;
25
- export {};
package/dist/plugins.js CHANGED
@@ -1,3 +1,3 @@
1
- import { ExternalPlugin } from "./external-BI3U5n3V.js";
1
+ import { ExternalPlugin } from "./external-uGOHYulu.js";
2
2
 
3
3
  export { ExternalPlugin };
package/dist/run.js CHANGED
@@ -1,25 +1,23 @@
1
- import { logger } from "./logger-39fy7Hdx.js";
1
+ import { logger } from "./logger-e0Fr5WMR.js";
2
2
  import { default as process } from "node:process";
3
3
  import { cac } from "cac";
4
4
 
5
5
  //#region package.json
6
- const version = '0.2.3';
7
- const files = ['dist'];
8
- const typesVersions = {'*': {'*': ['./dist/*', './*']}};
6
+ const version = "0.2.5";
9
7
 
10
8
  //#endregion
11
9
  //#region src/cli.ts
12
10
  async function runCLI() {
13
- const cli = cac('tsdown');
14
- cli.command('[...files]', 'Bundle files', {ignoreOptionDefaultValue: true}).option('-c, --config <filename>', 'Use a custom config file').option('--format <format>', 'Bundle format: esm, cjs, iife', {default: 'esm'}).option('--clean', 'Clean output directory').option('-d, --out-dir <dir>', 'Output directory', {default: 'dist'}).option('--treeshake', 'Tree-shake bundle', {default: true}).option('--platform <platform>', 'Target platform', {default: 'node'}).action(async (input, flags) => {
11
+ const cli = cac("tsdown");
12
+ 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("-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) => {
15
13
  logger.info(`tsdown v${version}`);
16
- const { build } = await import('./index.js');
14
+ const { build } = await import("./index.js");
17
15
  if (input.length > 0) flags.entry = input;
18
16
  await build(flags);
19
17
  });
20
18
  cli.help();
21
19
  cli.version(version);
22
- cli.parse(process.argv, {run: false});
20
+ cli.parse(process.argv, { run: false });
23
21
  try {
24
22
  await cli.runMatchedCommand();
25
23
  } catch (error) {
@@ -1 +1,2 @@
1
- export declare function toArray<T>(val: ((T) | ((T)[]) | (null) | (undefined)), defaultValue?: T): (T)[];
1
+ export declare function toArray<T>(val: T | T[] | null | undefined, defaultValue?: T): T[];
2
+ export declare function debounce<T extends (...args: any[]) => any>(fn: T, wait: number): T;
@@ -1,3 +1,3 @@
1
1
  import { type PackageJson } from 'pkg-types';
2
- export declare function readPackageJson(dir: string): Promise<((PackageJson) | (undefined))>;
3
- export declare function getPackageType(pkg: any): (('module') | ('commonjs'));
2
+ export declare function readPackageJson(dir: string): Promise<PackageJson | undefined>;
3
+ export declare function getPackageType(pkg: PackageJson | undefined): 'module' | 'commonjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsdown",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "An even faster bundler powered by Rolldown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,24 +40,27 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "cac": "^6.7.14",
43
+ "chokidar": "^3.6.0",
43
44
  "consola": "^3.2.3",
44
- "globby": "^14.0.1",
45
- "pkg-types": "^1.1.1",
45
+ "fast-glob": "^3.3.2",
46
+ "pkg-types": "^1.1.3",
46
47
  "rolldown": "nightly",
47
- "unconfig": "^0.4.4",
48
- "unplugin-isolated-decl": "^0.3.0"
48
+ "unconfig": "^0.5.5",
49
+ "unplugin-isolated-decl": "^0.4.1"
49
50
  },
50
51
  "devDependencies": {
51
- "@sxzz/eslint-config": "^3.13.0",
52
+ "@sxzz/eslint-config": "^3.16.3",
52
53
  "@sxzz/prettier-config": "^2.0.2",
53
- "@types/node": "^20.14.8",
54
- "bumpp": "^9.4.1",
55
- "eslint": "^9.5.0",
56
- "prettier": "^3.3.2",
57
- "tsup": "^8.1.0",
58
- "tsx": "^4.15.7",
59
- "typescript": "~5.5.2",
60
- "vitest": "^1.6.0"
54
+ "@types/node": "^20.14.14",
55
+ "bumpp": "^9.4.2",
56
+ "eslint": "^9.8.0",
57
+ "execa": "^9.3.0",
58
+ "fdir": "^6.2.0",
59
+ "prettier": "^3.3.3",
60
+ "tsup": "^8.2.4",
61
+ "tsx": "^4.16.5",
62
+ "typescript": "~5.5.4",
63
+ "vitest": "^2.0.5"
61
64
  },
62
65
  "engines": {
63
66
  "node": ">=18.0.0"