tsdown 0.2.2 → 0.2.4

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