dtsroll 1.5.0 → 1.6.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/README.md CHANGED
@@ -1,152 +1,145 @@
1
1
  <p align="center">
2
- <img width="150" src="./.github/logo.webp">
2
+ <img width="200" src="./.github/logo.webp">
3
3
  </p>
4
+ <h2 align="center">dtsroll</h2>
4
5
 
5
- <h2 align="center">
6
- dtsroll
7
- </h2>
6
+ Are you publishing a TypeScript project where consumers encounter type-checking errors like:
8
7
 
9
- _dtsroll_ is a CLI tool for bundling TypeScript declaration (`.d.ts`) files.
8
+ ```
9
+ Cannot find module 'some-package' or its corresponding type declarations.
10
+ ```
10
11
 
11
- ### Why bundle `.d.ts` files?
12
+ When you compile with `tsc`, the emitted declaration files (`.d.ts` files) preserve imports exactly as written. So if your published types import from a `devDependency` or a private package (e.g. an internal monorepo package), those imports cannot be resolved by the consumer.
12
13
 
13
- - **Smaller distribution**
14
+ ```ts
15
+ // dist/index.d.ts (generated by tsc)
16
+ import type { SomeType } from 'my-private-dependency' // ❌ consumers can't resolve this
14
17
 
15
- Tree-shaking removes unused code, keeping only what's needed and reducing the output size.
18
+ export declare function myUtility(): SomeType
19
+ ```
16
20
 
17
- - **Bundle in private dependencies**
21
+ If you can't move the dependency to `dependencies`, or you just want its types pulled directly into your published declarations, _dtsroll_ is for you.
18
22
 
19
- Inline types from private dependencies (e.g., monorepo packages) that aren't accessible to consumers.
23
+ ## What is dtsroll?
20
24
 
21
- - **Improve TS performance**
25
+ _dtsroll_ is a TypeScript declaration (`.d.ts`) file bundler. It's zero-config and reads your `package.json` to determine how your types should be bundled.
22
26
 
23
- Flattens multiple files into one, reducing TypeScript's file resolution for type checking.
27
+ ### What dtsroll does
24
28
 
25
- <p align="center">
26
- <img width="600" src="./.github/screenshot.webp">
27
- </p>
29
+ _dtsroll_ runs *after your build when `.d.ts` files have been emitted*, and works in-place to bundle them to their entry points.
28
30
 
31
+ Since packages declared in `devDependencies` are not installed for the consumer, _dtsroll_ assumes any imports referencing them should be bundled, as they would otherwise be unresolvable.
29
32
 
30
- ## Install
31
- ```
32
- npm install --save-dev dtsroll
33
- ```
33
+ The result is a single, clean `.d.ts` output that works for consumers without extra installs.
34
34
 
35
- ## Quick start
36
-
37
- 1. Compile your TypeScript code with declaration (`.d.ts`) files
38
- - If using the TypeScript compiler (`tsc`), enable [`declaration`](https://www.typescriptlang.org/tsconfig/#declaration)
39
- - If using Vite, use a plugin like [vite-plugin-dts](https://www.npmjs.com/package/vite-plugin-dts)
40
-
41
- 2. Pass in the entry declaration file to _dtsroll_
42
-
43
- ```sh
44
- dtsroll --dry-run dist/index.d.ts
45
- ```
35
+ ```ts
36
+ // dist/index.d.ts (after dtsroll)
37
+ type SomeType = {
38
+ value: string
39
+ }
46
40
 
47
- > [!CAUTION]
48
- > _dtsroll_ is designed to run on compiled output so it modifies files in-place.
49
- > - It will modify files you pass in, and files they import.
50
- > - Only pass in backed up or generated files
51
- > - Start with `--dry-run`
41
+ export declare function myUtility(): SomeType
42
+ ```
52
43
 
53
- 3. If the changes look good, remove the `--dry-run` flag:
44
+ ### Features
54
45
 
55
- ```sh
56
- dtsroll dist/index.d.ts
57
- ```
46
+ - **Zero config** — Automatically finds entry points from `package.json`.
47
+ - **Fixes missing-type errors** — Inlines types from `devDependencies` so consumers don't need them installed.
48
+ - **Tree-shaken output** — Unused types are removed to keep files small.
49
+ - **In-place** — Designed to run directly on your `dist` folder after compilation.
58
50
 
59
- ### Recommended setup
51
+ <p align="center">
52
+ <img width="600" src="./.github/screenshot.webp">
53
+ </p>
60
54
 
61
- Running `dtsroll` without specifying input files will auto-detect them from `package.json`.
55
+ ## Install
62
56
 
63
- Update your `package.json` to reference `.d.ts` files and include `dtsroll` in the build step:
64
- ```diff
65
- {
66
- "name": "my-package",
67
- "exports": {
68
- + "types": "./dist/index.d.ts",
69
- "default": "./dist/index.js"
70
- },
71
- "scripts": {
72
- + "build": "tsc && dtsroll"
73
- }
74
- }
57
+ ```bash
58
+ npm install --save-dev dtsroll
75
59
  ```
76
60
 
77
- ### Externalization
61
+ ## Quick start
78
62
 
79
- By default, _dtsroll_ decides which dependencies to bundle or keep external by analyzing the `package.json` file. Packages in `devDependencies` are bundled, and packages in other dependency types are externalized.
63
+ ### 1. Configure `package.json`
64
+
65
+ Point your `types` or `exports` to the final `.d.ts` file you want to publish.
66
+
67
+ ```jsonc
68
+ {
69
+ "name": "my-package",
70
+ "exports": {
71
+ "types": "./dist/index.d.ts", // dtsroll targets this
72
+ "default": "./dist/index.js",
73
+ },
74
+ "scripts": {
75
+ "build": "tsc && dtsroll",
76
+ },
77
+ }
78
+ ```
80
79
 
81
- When there is no `package.json` file, you can specify packages to externalize with the `--external` flag.
80
+ ### 2. Build
82
81
 
83
- #### Handling `@types` packages
82
+ ```bash
83
+ npm run build
84
+ ```
84
85
 
85
- Some packages need separate `@types/*` packages for type definitions. In this setup, typically:
86
+ That's it.
86
87
 
87
- - The main package is in `dependencies`.
88
- - The corresponding `@types/*` package is in `devDependencies`.
88
+ > [!WARNING]
89
+ > _dtsroll_ modifies files in-place—bundled source files are removed and entry files are overwritten with bundled output.
90
+ > Use `--dry-run` first to see what it would change:
91
+ >
92
+ > ```bash
93
+ > dtsroll --dry-run
94
+ > ```
89
95
 
90
- Because the main package is in `dependencies`, _dtsroll_ externalizes it. However, consumers of your package won’t get the type definitions for it because the `@types/*` package is only in `devDependencies`.
96
+ ## Behavior
91
97
 
92
- To fix this, _dtsroll_ will display a warning suggesting you move the `@types/*` package out of `devDependencies`.
98
+ ### Automatic configuration
93
99
 
94
- ## CLI
100
+ By default, _dtsroll_ reads your `package.json` to determine which imports should be bundled and which should remain external. The recommended setup is to run _dtsroll_ without any configuration and let it infer the correct behavior based on your dependency declarations.
95
101
 
96
- ### Usage
97
- ```sh
98
- dtsroll [-flags] [...entry dts files]
99
- ```
102
+ | Dependency type | Action | Reason |
103
+ | ---------------------- | ----------- | ----------------------------- |
104
+ | `devDependencies` | Bundle | Consumers don't install these |
105
+ | `dependencies` | Externalize | Consumers already have them |
106
+ | `optionalDependencies` | Externalize | Consumers already have them |
107
+ | `peerDependencies` | Externalize | Provided by the consumer |
100
108
 
101
- The default usage is to run `dtsroll` without any flags/arguments in a directory containing a `package.json` file. This allows the configuration to be inferred automatically.
109
+ If you have a `@types/*` package in `devDependencies` but the corresponding runtime package in `dependencies`, _dtsroll_ will recommend moving the types package to `dependencies`, as this can otherwise result in missing types for consumers.
102
110
 
103
- ### --help, -h
104
- Display usage instructions.
111
+ ### Manual configuration
105
112
 
106
- ### --dry-run, -d
107
- Simulate the bundling process without modifying the disk and logs what would happen.
113
+ If your project doesn't have a `package.json` file, you can still manually specify the input files (which entry files to collapse the `imports` into), and which packages to externalize.
108
114
 
109
- ### --external, -e
110
- If there is no `package.json` file, you can specify package names to exclude from the bundle.
115
+ ### Subpath imports
111
116
 
112
- > [!NOTE]
113
- > This flag can only be used when there is no `package.json`. It's better to define dependencies appropriately in `package.json` instead of using this flag.
117
+ > [!WARNING]
118
+ > Currently, _dtsroll_ mistakenly resolves and bundles [subpath imports](https://nodejs.org/api/packages.html#subpath-imports). Subpath imports are intended to be dynamic aliases controlled by the consumer. In a future breaking release, _dtsroll_ will likely externalize them to preserve this behavior.
114
119
 
115
- ### --conditions, -C
116
- Provide resolution conditions to target specific entry points in dependencies, similar to Node’s [`--conditions`](https://nodejs.org/api/cli.html#-c-condition---conditionscondition).
120
+ ## Usage
117
121
 
118
- ## Node.js API
119
- ```ts
120
- import { dtsroll } from 'dtsroll'
122
+ _dtsroll_ can be used in several ways.
121
123
 
122
- await dtsroll(options as {
124
+ ### CLI usage
123
125
 
124
- /**
125
- * CWD to find the package.json in
126
- * @default process.cwd()
127
- */
128
- cwd?: string
129
-
130
- /**
131
- * Defaults to auto-detecting d.ts files from package.json
132
- */
133
- inputs?: string[]
126
+ ```bash
127
+ dtsroll [flags] [...entry .d.ts files]
128
+ ```
134
129
 
135
- /**
136
- * Only used if there's no package.json
137
- * Defaults to auto-detecting dependencies from package.json
138
- */
139
- external?: string[]
130
+ If no entry files are provided, _dtsroll_ reads `package.json` to determine them.
140
131
 
141
- conditions?: string[]
132
+ #### Flags
142
133
 
143
- dryRun?: boolean
144
- })
145
- ```
134
+ | Flag | Alias | Description |
135
+ | -------------- | ----- | ------------------------------------------------- |
136
+ | `--dry-run` | `-d` | Show what would be bundled without writing files |
137
+ | `--conditions` | `-C` | Resolution conditions for [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) (e.g. `production`) |
138
+ | `--external` | `-e` | *(Only when no `package.json`)* Packages to externalize |
146
139
 
147
- ## Vite plugin
140
+ ### Vite plugin
148
141
 
149
- Use it in conjunction with a plugin that generates the initial declaration files like `vite-plugin-dts`.
142
+ If you use `vite-plugin-dts`, _dtsroll_ will automatically bundle the emitted types immediately after generation:
150
143
 
151
144
  ```ts
152
145
  import { defineConfig } from 'vitest/config'
@@ -154,16 +147,24 @@ import dts from 'vite-plugin-dts'
154
147
  import { dtsroll } from 'dtsroll/vite'
155
148
 
156
149
  export default defineConfig({
157
- // ...
158
150
  plugins: [
159
- // ...
160
151
  dts(),
161
152
  dtsroll()
162
153
  ]
163
154
  })
164
155
  ```
165
156
 
157
+ ### Node API
158
+
159
+ ```ts
160
+ import { dtsroll } from 'dtsroll'
161
+
162
+ await dtsroll({
163
+ cwd: process.cwd(),
164
+ dryRun: false
165
+ })
166
+ ```
167
+
166
168
  ## Related
167
169
 
168
- ### pkgroll
169
- For package bundling (along with dts bundling), check out [pkgroll](https://github.com/privatenumber/pkgroll).
170
+ - [pkgroll](https://github.com/privatenumber/pkgroll) — Zero-config JS + DTS bundler
package/dist/cli.mjs CHANGED
@@ -1,18 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { cli } from 'cleye';
3
- import { b as bgYellow, a as black, d as dtsroll, l as logOutput } from './index-DfxmCmBK.mjs';
3
+ import { b as bgYellow, a as black, d as dtsroll, l as logOutput, D as DtsrollBuildError } from './index-Du7Kzot4.mjs';
4
4
  import 'node:path';
5
5
  import 'node:fs/promises';
6
6
  import 'rollup';
7
7
  import 'rollup-plugin-dts';
8
8
  import '@rollup/plugin-node-resolve';
9
- import 'node:fs';
10
9
  import 'empathic/find';
11
10
  import 'resolve-pkg-maps';
12
11
  import 'byte-size';
13
12
 
14
13
  var name = "dtsroll";
15
- var version = "1.5.0";
14
+ var version = "1.6.0";
16
15
  var description = "Bundle dts files";
17
16
 
18
17
  const argv = cli({
@@ -21,6 +20,7 @@ const argv = cli({
21
20
  help: {
22
21
  description
23
22
  },
23
+ strictFlags: true,
24
24
  parameters: ["[input files...]"],
25
25
  flags: {
26
26
  conditions: {
@@ -37,32 +37,46 @@ const argv = cli({
37
37
  type: [String],
38
38
  alias: "e",
39
39
  description: "Dependency to externalize"
40
+ },
41
+ sourcemap: {
42
+ type: Boolean,
43
+ alias: "s",
44
+ description: "Generate sourcemaps"
40
45
  }
41
- // sourcemap: {
42
- // type: Boolean,
43
- // description: 'Generate sourcemaps',
44
- // },
45
46
  }
46
47
  });
47
48
  const { flags } = argv;
48
- const dryMode = flags.dryRun;
49
- if (dryMode) {
49
+ if (flags.dryRun) {
50
50
  console.log(bgYellow(black(" Dry run - No files will be written ")));
51
51
  }
52
52
  dtsroll({
53
53
  inputs: argv._.inputFiles,
54
54
  external: flags.external,
55
55
  conditions: flags.conditions,
56
- dryRun: flags.dryRun
56
+ dryRun: flags.dryRun,
57
+ sourcemap: flags.sourcemap
57
58
  }).then(
58
59
  (output) => {
59
60
  if ("error" in output) {
60
61
  process.exitCode = 1;
61
62
  }
62
63
  logOutput(output);
63
- },
64
+ }
65
+ ).catch(
64
66
  (error) => {
65
- console.error("\nFailed to build:", error.message);
67
+ let errorMessage = "\nFailed to build";
68
+ if (error instanceof DtsrollBuildError) {
69
+ errorMessage += `
70
+ File: ${error.id}`;
71
+ if (error.importChain.length > 1) {
72
+ errorMessage += "\n\n Import chain:\n ";
73
+ errorMessage += error.importChain.join("\n \u2192 ");
74
+ }
75
+ }
76
+ errorMessage += `
77
+
78
+ ${error instanceof Error ? error.message : String(error)}`;
79
+ console.error(errorMessage);
66
80
  process.exitCode = 1;
67
81
  }
68
82
  );
@@ -3,7 +3,6 @@ import fs from 'node:fs/promises';
3
3
  import { rollup } from 'rollup';
4
4
  import { dts } from 'rollup-plugin-dts';
5
5
  import nodeResolve from '@rollup/plugin-node-resolve';
6
- import fs$1 from 'node:fs';
7
6
  import { up } from 'empathic/find';
8
7
  import { resolveImports } from 'resolve-pkg-maps';
9
8
  import byteSize from 'byte-size';
@@ -72,7 +71,13 @@ const bgYellow = kolorist(43, 49);
72
71
  const cwd = process.cwd();
73
72
 
74
73
  const isPath = (filePath) => filePath[0] === "." || path.isAbsolute(filePath);
75
- const normalizePath = (filepath) => filepath.replaceAll("\\", "/");
74
+ const normalizePath$1 = (filepath) => filepath.replaceAll("\\", "/");
75
+ const getDisplayPath = (fullPath) => {
76
+ const relativePath = path.relative(cwd, fullPath);
77
+ return normalizePath$1(
78
+ relativePath.length < fullPath.length ? relativePath : fullPath
79
+ );
80
+ };
76
81
 
77
82
  const warningSignUnicode = "\u26A0";
78
83
  const warningPrefix = yellow("Warning:");
@@ -84,10 +89,7 @@ const logOutput = (dtsOutput) => {
84
89
  \u{1F4E5} Entry points${isCliInput ? "" : " in package.json"}`));
85
90
  console.log(
86
91
  inputs.map(([inputFile, inputSource, error]) => {
87
- const relativeInputFile = path.relative(cwd, inputFile);
88
- const logPath2 = normalizePath(
89
- relativeInputFile.length < inputFile.length ? relativeInputFile : inputFile
90
- );
92
+ const logPath2 = getDisplayPath(inputFile);
91
93
  if (error) {
92
94
  return ` ${lightYellow(`${warningSignUnicode} ${logPath2} ${dim(error)}`)}`;
93
95
  }
@@ -107,10 +109,7 @@ const logOutput = (dtsOutput) => {
107
109
  size,
108
110
  externals
109
111
  } = dtsOutput;
110
- const outputDirectoryRelative = path.relative(cwd, outputDirectory);
111
- const logPath = `${normalizePath(
112
- outputDirectoryRelative.length < outputDirectory.length ? outputDirectoryRelative : outputDirectory
113
- )}/`;
112
+ const logPath = `${getDisplayPath(outputDirectory)}/`;
114
113
  const logChunk = ({
115
114
  file,
116
115
  indent,
@@ -124,10 +123,7 @@ const logOutput = (dtsOutput) => {
124
123
  ${moduleIds.sort().map((moduleId, index) => {
125
124
  const isLast = index === moduleIds.length - 1;
126
125
  const prefix = `${indent} ${isLast ? "\u2514\u2500 " : "\u251C\u2500 "}`;
127
- const relativeModuleId = path.relative(cwd, moduleId);
128
- const logModuleId = normalizePath(
129
- relativeModuleId.length < moduleId.length ? relativeModuleId : moduleId
130
- );
126
+ const logModuleId = getDisplayPath(moduleId);
131
127
  const bareSpecifier = moduleToPackage[moduleId];
132
128
  if (bareSpecifier) {
133
129
  return `${prefix}${dim(`${magenta(bareSpecifier)} (${logModuleId})`)}`;
@@ -177,6 +173,17 @@ ${moduleIds.sort().map((moduleId, index) => {
177
173
  }
178
174
  };
179
175
 
176
+ class DtsrollBuildError extends Error {
177
+ id;
178
+ importChain;
179
+ constructor(message, id, importChain) {
180
+ super(message);
181
+ this.name = "DtsrollBuildError";
182
+ this.id = id;
183
+ this.importChain = importChain;
184
+ }
185
+ }
186
+
180
187
  const dtsExtensions = [".d.ts", ".d.cts", ".d.mts"];
181
188
  const isDts = (fileName) => dtsExtensions.some((extension) => fileName.endsWith(extension));
182
189
 
@@ -269,6 +276,7 @@ const getPackageName = (id) => {
269
276
  return id.slice(0, indexOfSlash);
270
277
  };
271
278
 
279
+ const normalizePath = (filePath) => filePath.replaceAll("\\", "/");
272
280
  const getAllFiles = async (directoryPath, dontShortenPath) => {
273
281
  const directoryFiles = await fs.readdir(directoryPath, { withFileTypes: true });
274
282
  const fileTree = await Promise.all(
@@ -276,9 +284,9 @@ const getAllFiles = async (directoryPath, dontShortenPath) => {
276
284
  const filePath = path.join(directoryPath, entry.name);
277
285
  if (entry.isDirectory()) {
278
286
  const files = await getAllFiles(filePath, true);
279
- return dontShortenPath ? files : files.map((file) => `./${path.relative(directoryPath, file)}`);
287
+ return dontShortenPath ? files : files.map((file) => `./${normalizePath(path.relative(directoryPath, file))}`);
280
288
  }
281
- return dontShortenPath ? filePath : `./${path.relative(directoryPath, filePath)}`;
289
+ return dontShortenPath ? filePath : `./${normalizePath(path.relative(directoryPath, filePath))}`;
282
290
  })
283
291
  );
284
292
  return fileTree.flat();
@@ -473,6 +481,39 @@ const createExternalizePlugin = (configuredExternals) => {
473
481
  };
474
482
  };
475
483
 
484
+ const createImportChainPlugin = () => {
485
+ const importerMap = /* @__PURE__ */ new Map();
486
+ const plugin = {
487
+ name: "import-chain-tracker",
488
+ buildStart: () => {
489
+ importerMap.clear();
490
+ },
491
+ async resolveId(source, importer) {
492
+ if (!importer) {
493
+ return null;
494
+ }
495
+ const resolved = await this.resolve(source, importer, { skipSelf: true });
496
+ if (resolved && !resolved.external && !importerMap.has(resolved.id)) {
497
+ importerMap.set(resolved.id, importer);
498
+ }
499
+ return null;
500
+ }
501
+ };
502
+ const getImportChain = (errorFileId) => {
503
+ const chain = [];
504
+ let current = errorFileId;
505
+ while (current) {
506
+ chain.unshift(current);
507
+ current = importerMap.get(current);
508
+ }
509
+ return chain;
510
+ };
511
+ return {
512
+ plugin,
513
+ getImportChain
514
+ };
515
+ };
516
+
476
517
  const nodeModules = `${path.sep}node_modules${path.sep}`;
477
518
  const removeBundledModulesPlugin = (outputDirectory, sizeRef) => {
478
519
  let deleteFiles = [];
@@ -490,14 +531,17 @@ const removeBundledModulesPlugin = (outputDirectory, sizeRef) => {
490
531
  async generateBundle(options, bundle) {
491
532
  const modules = Object.values(bundle);
492
533
  const bundledFiles = Array.from(new Set(modules.flatMap(({ moduleIds }) => moduleIds)));
493
- const fileSizes = bundledFiles.map((moduleId) => this.getModuleInfo(moduleId).meta);
494
- const totalSize = fileSizes.reduce((total, { size }) => total + size, 0);
534
+ let totalSize = 0;
535
+ for (const moduleId of bundledFiles) {
536
+ const moduleInfo = this.getModuleInfo(moduleId);
537
+ const size = moduleInfo?.meta?.size;
538
+ if (typeof size === "number") {
539
+ totalSize += size;
540
+ }
541
+ }
495
542
  sizeRef.value = totalSize;
496
543
  const outputFiles = new Set(modules.map(({ fileName }) => path.join(options.dir, fileName)));
497
- deleteFiles = bundledFiles.filter((moduleId) => (
498
- // To avoid deleting files from symlinked dependencies
499
- moduleId.startsWith(outputDirectory) && !moduleId.includes(nodeModules) && !outputFiles.has(moduleId)
500
- ));
544
+ deleteFiles = bundledFiles.filter((moduleId) => moduleId && moduleId.startsWith(outputDirectory) && !moduleId.includes(nodeModules) && !outputFiles.has(moduleId));
501
545
  },
502
546
  writeBundle: async () => {
503
547
  await Promise.all(
@@ -508,7 +552,7 @@ const removeBundledModulesPlugin = (outputDirectory, sizeRef) => {
508
552
  };
509
553
 
510
554
  const packageJsonCache = /* @__PURE__ */ new Map();
511
- const findPackageJsonUp = (cwd) => {
555
+ const findPackageJsonUp = async (cwd) => {
512
556
  const packageJsonPath = up("package.json", { cwd });
513
557
  if (!packageJsonPath) {
514
558
  return void 0;
@@ -517,7 +561,7 @@ const findPackageJsonUp = (cwd) => {
517
561
  let packageJson = packageJsonCache.get(packageRoot);
518
562
  if (!packageJson) {
519
563
  try {
520
- const content = fs$1.readFileSync(packageJsonPath, "utf8");
564
+ const content = await fs.readFile(packageJsonPath, "utf8");
521
565
  packageJson = JSON.parse(content);
522
566
  packageJsonCache.set(packageRoot, packageJson);
523
567
  } catch {
@@ -537,7 +581,7 @@ const resolveSubpathImportsPlugin = () => ({
537
581
  if (id[0] !== "#" || !importer) {
538
582
  return null;
539
583
  }
540
- const result = findPackageJsonUp(path.dirname(importer));
584
+ const result = await findPackageJsonUp(path.dirname(importer));
541
585
  if (!result) {
542
586
  return null;
543
587
  }
@@ -565,22 +609,24 @@ const createInputMap = (input, outputDirectory) => Object.fromEntries(
565
609
  inputFile
566
610
  ])
567
611
  );
568
- const build = async (input, outputDirectory, externals, mode, conditions) => {
612
+ const build = async (input, outputDirectory, externals, mode, conditions, sourcemap) => {
569
613
  const {
570
614
  externalizePlugin,
571
615
  externalized,
572
616
  getPackageEntryPoint
573
617
  } = createExternalizePlugin(externals);
618
+ const { plugin: importChainPlugin, getImportChain } = createImportChainPlugin();
574
619
  const sizeRef = {};
575
620
  const rollupConfig = {
576
621
  input: createInputMap(input, outputDirectory),
577
622
  output: {
578
- // sourcemap: true,
623
+ sourcemap,
579
624
  dir: outputDirectory,
580
625
  entryFileNames: "[name]",
581
626
  chunkFileNames: "_dtsroll-chunks/[hash]-[name].ts"
582
627
  },
583
628
  plugins: [
629
+ importChainPlugin,
584
630
  externalizePlugin,
585
631
  removeBundledModulesPlugin(outputDirectory, sizeRef),
586
632
  resolveSubpathImportsPlugin(),
@@ -600,15 +646,26 @@ const build = async (input, outputDirectory, externals, mode, conditions) => {
600
646
  })
601
647
  ]
602
648
  };
603
- const rollupBuild = await rollup(rollupConfig);
604
- const built = await rollupBuild[mode](rollupConfig.output);
605
- await rollupBuild.close();
606
- return {
607
- built,
608
- externalized,
609
- getPackageEntryPoint,
610
- sourceSize: sizeRef.value
611
- };
649
+ try {
650
+ const rollupBuild = await rollup(rollupConfig);
651
+ const built = await rollupBuild[mode](rollupConfig.output);
652
+ await rollupBuild.close();
653
+ return {
654
+ built,
655
+ externalized,
656
+ getPackageEntryPoint,
657
+ sourceSize: sizeRef.value ?? 0
658
+ };
659
+ } catch (error) {
660
+ if (error instanceof Error && "id" in error && typeof error.id === "string") {
661
+ throw new DtsrollBuildError(
662
+ error.message,
663
+ error.id,
664
+ getImportChain(error.id)
665
+ );
666
+ }
667
+ throw error;
668
+ }
612
669
  };
613
670
 
614
671
  const dtsroll = async ({
@@ -616,7 +673,8 @@ const dtsroll = async ({
616
673
  inputs,
617
674
  external,
618
675
  conditions,
619
- dryRun
676
+ dryRun,
677
+ sourcemap
620
678
  } = {}) => {
621
679
  const pkgJson = await getPackageJson(cwd);
622
680
  const externals = pkgJson ? pkgJson.getExternals() : /* @__PURE__ */ new Map();
@@ -651,13 +709,15 @@ const dtsroll = async ({
651
709
  outputDirectory,
652
710
  externals,
653
711
  dryRun ? "generate" : "write",
654
- conditions
712
+ conditions,
713
+ sourcemap
655
714
  );
656
715
  let outputSize = 0;
657
716
  const outputEntries = [];
658
717
  const outputChunks = [];
659
718
  const moduleImports = /* @__PURE__ */ new Set();
660
- for (const file of built.output) {
719
+ const chunks = built.output.filter((file) => file.type === "chunk");
720
+ for (const file of chunks) {
661
721
  const size = Buffer.byteLength(file.code);
662
722
  outputSize += size;
663
723
  const moduleToPackage = Object.fromEntries(
@@ -702,4 +762,4 @@ const dtsroll = async ({
702
762
  };
703
763
  };
704
764
 
705
- export { black as a, bgYellow as b, dtsroll as d, logOutput as l };
765
+ export { DtsrollBuildError as D, black as a, bgYellow as b, dtsroll as d, logOutput as l };
package/dist/index.d.ts CHANGED
@@ -1,19 +1,36 @@
1
1
  import { OutputChunk } from 'rollup';
2
2
 
3
+ /**
4
+ * Extended output chunk with additional metadata.
5
+ */
3
6
  type Output = OutputChunk & {
7
+ /** Size of the output file in bytes. */
4
8
  size: number;
9
+ /** Map of module IDs to their package names. */
5
10
  moduleToPackage: Record<string, string | undefined>;
6
11
  };
12
+ /**
13
+ * List of externalized packages with metadata.
14
+ * Each entry is [packageName, reason, warning?].
15
+ */
7
16
  type Externals = [
8
17
  packageName: string,
9
18
  reason?: string,
10
19
  warning?: string
11
20
  ][];
21
+ /**
22
+ * Validated input file with source info and optional error.
23
+ * Tuple format: [inputPath, inputSource, error?].
24
+ */
12
25
  type ValidatedInput = [
13
26
  inputPath: string,
14
27
  inputSource: string | undefined,
15
28
  error?: string
16
29
  ];
30
+ /**
31
+ * Output from the dtsroll build process.
32
+ * Returns either an error state or successful build results.
33
+ */
17
34
  type DtsrollOutput = {
18
35
  inputs: ValidatedInput[];
19
36
  error: string;
@@ -31,14 +48,30 @@ type DtsrollOutput = {
31
48
  externals: Externals;
32
49
  };
33
50
 
51
+ /**
52
+ * Configuration options for dtsroll.
53
+ */
34
54
  type Options = {
55
+ /** Working directory. Defaults to process.cwd(). */
35
56
  cwd?: string;
57
+ /** Input .d.ts files to bundle. If not provided, auto-detects from package.json. */
36
58
  inputs?: string[];
59
+ /** Packages to externalize (only used when no package.json is present). */
37
60
  external?: string[];
61
+ /** Export conditions for module resolution. */
38
62
  conditions?: string[];
63
+ /** If true, generates output without writing files. */
39
64
  dryRun?: boolean;
65
+ /** If true, generates source maps (.d.ts.map files). */
66
+ sourcemap?: boolean;
40
67
  };
41
- declare const dtsroll: ({ cwd, inputs, external, conditions, dryRun, }?: Options) => Promise<DtsrollOutput>;
68
+ /**
69
+ * Bundle TypeScript declaration files using Rollup.
70
+ *
71
+ * @param options - Configuration options
72
+ * @returns Build output including bundled files, sizes, and externalized packages
73
+ */
74
+ declare const dtsroll: ({ cwd, inputs, external, conditions, dryRun, sourcemap, }?: Options) => Promise<DtsrollOutput>;
42
75
 
43
76
  export { dtsroll };
44
77
  export type { DtsrollOutput, Options };
package/dist/index.mjs CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import 'node:path';
3
- export { d as dtsroll } from './index-DfxmCmBK.mjs';
3
+ export { d as dtsroll } from './index-Du7Kzot4.mjs';
4
4
  import 'node:fs/promises';
5
5
  import 'rollup';
6
6
  import 'rollup-plugin-dts';
7
7
  import '@rollup/plugin-node-resolve';
8
- import 'node:fs';
9
8
  import 'empathic/find';
10
9
  import 'resolve-pkg-maps';
11
10
  import 'byte-size';
package/dist/vite.d.ts CHANGED
@@ -2,6 +2,13 @@ import { Plugin } from 'vite';
2
2
  import { Options } from './index.js';
3
3
  import 'rollup';
4
4
 
5
+ /**
6
+ * Vite plugin for bundling TypeScript declaration files.
7
+ * Runs after vite-plugin-dts in the writeBundle hook.
8
+ *
9
+ * @param options - Configuration options (same as dtsroll function)
10
+ * @returns Vite plugin instance
11
+ */
5
12
  declare const dtsrollPlugin: (options?: Options) => Plugin;
6
13
 
7
14
  export { dtsrollPlugin as dtsroll };
package/dist/vite.mjs CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { d as dtsroll, l as logOutput } from './index-DfxmCmBK.mjs';
2
+ import { d as dtsroll, l as logOutput } from './index-Du7Kzot4.mjs';
3
3
  import 'node:path';
4
4
  import 'node:fs/promises';
5
5
  import 'rollup';
6
6
  import 'rollup-plugin-dts';
7
7
  import '@rollup/plugin-node-resolve';
8
- import 'node:fs';
9
8
  import 'empathic/find';
10
9
  import 'resolve-pkg-maps';
11
10
  import 'byte-size';
@@ -29,14 +28,22 @@ const dtsrollPlugin = (options) => {
29
28
  if (built) {
30
29
  return;
31
30
  }
32
- const output = await dtsroll({
33
- cwd,
34
- ...options
35
- });
36
- built = true;
37
- if (!noLog) {
38
- logOutput(output);
39
- console.log();
31
+ try {
32
+ const output = await dtsroll({
33
+ cwd,
34
+ ...options
35
+ });
36
+ built = true;
37
+ if (!noLog) {
38
+ logOutput(output);
39
+ console.log();
40
+ }
41
+ } catch (error) {
42
+ built = true;
43
+ throw new Error(
44
+ `dtsroll failed: ${error instanceof Error ? error.message : String(error)}`,
45
+ { cause: error }
46
+ );
40
47
  }
41
48
  }
42
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dtsroll",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Bundle dts files",
5
5
  "keywords": [
6
6
  "bundle",