rolldown-plugin-dts 0.0.0 → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright © 2025 三咲智子 Kevin Deng (https://github.com/sxzz)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # rolldown-plugin-dts [![npm](https://img.shields.io/npm/v/rolldown-plugin-dts.svg)](https://npmjs.com/package/rolldown-plugin-dts)
2
+
3
+ [![Unit Test](https://github.com/sxzz/rolldown-plugin-dts/actions/workflows/unit-test.yml/badge.svg)](https://github.com/sxzz/rolldown-plugin-dts/actions/workflows/unit-test.yml)
4
+
5
+ A Rolldown plugin to generate and bundle dts files.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm i rolldown-plugin-dts
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Add the plugin to your `rolldown.config.js`:
16
+
17
+ ```js
18
+ // rolldown.config.js
19
+ import { dts } from 'rolldown-plugin-dts'
20
+
21
+ export default {
22
+ input: './src/index.ts',
23
+ plugins: [dts()],
24
+ output: [{ dir: 'dist', format: 'es' }],
25
+ }
26
+ ```
27
+
28
+ You can find a real demo in [here](./rolldown.config.ts).
29
+
30
+ ## Options
31
+
32
+ ````ts
33
+ interface Options {
34
+ /**
35
+ * When entries are `.d.ts` files (instead of `.ts` files), this option should be set to `true`.
36
+ *
37
+ * If enabled, the plugin will skip generating a `.d.ts` file for the entry point.
38
+ */
39
+ dtsInput?: boolean
40
+
41
+ isolatedDeclaration?: Omit<IsolatedDeclarationsOptions, 'sourcemap'>
42
+
43
+ /**
44
+ * dts file name alias `{ [filename]: path }`
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * inputAlias: {
49
+ * 'foo.d.ts': 'foo/index.d.ts',
50
+ * }
51
+ */
52
+ inputAlias?: Record<string, string>
53
+
54
+ /**
55
+ * Determines whether the module imported by `.d.ts` files should be treated as external or not.
56
+ */
57
+ external?: (
58
+ id: string,
59
+ importer: string,
60
+ extraOptions: ResolveIdExtraOptions,
61
+ ) => boolean | void
62
+ }
63
+ ````
64
+
65
+ ## Caveats
66
+
67
+ - The plugin uses Oxc's `isolatedDeclarations` to generate `.d.ts` files,
68
+ which means you need to set `isolatedDeclarations: true` in your `tsconfig.json` and ensure there are no errors.
69
+
70
+ - Namespaces are not supported yet.
71
+ - `export * as ns from './ns'`
72
+ - `import * as ns from './ns'` and then `export { ns }`
73
+ - `type ns = import('./ns')`
74
+
75
+ ## Credits
76
+
77
+ The project is inspired by [rollup-plugin-dts](https://github.com/Swatinem/rollup-plugin-dts)
78
+ but has been independently implemented.
79
+ We extend our gratitude to the original creators for their contributions.
80
+ Furthermore, the test suite is authorized by them and distributed under the MIT license.
81
+
82
+ ## Sponsors
83
+
84
+ <p align="center">
85
+ <a href="https://cdn.jsdelivr.net/gh/sxzz/sponsors/sponsors.svg">
86
+ <img src='https://cdn.jsdelivr.net/gh/sxzz/sponsors/sponsors.svg'/>
87
+ </a>
88
+ </p>
89
+
90
+ ## License
91
+
92
+ [MIT](./LICENSE) License © 2025 [三咲智子 Kevin Deng](https://github.com/sxzz)
@@ -0,0 +1,40 @@
1
+ import { IsolatedDeclarationsOptions } from "oxc-transform";
2
+ import { FunctionPluginHooks, Plugin } from "rolldown";
3
+
4
+ //#region src/fake-js.d.ts
5
+ declare function createFakeJsPlugin({ dtsInput }: Pick<Options, "dtsInput">): Plugin;
6
+
7
+ //#endregion
8
+ //#region src/generate.d.ts
9
+ declare function createGeneratePlugin({ isolatedDeclaration, inputAlias, external }: Pick<Options, "external" | "isolatedDeclaration" | "inputAlias">): Plugin;
10
+
11
+ //#endregion
12
+ //#region src/index.d.ts
13
+ type ResolveIdExtraOptions = Parameters<FunctionPluginHooks["resolveId"]>[2];
14
+ interface Options {
15
+ /**
16
+ * When entries are `.d.ts` files (instead of `.ts` files), this option should be set to `true`.
17
+ *
18
+ * If enabled, the plugin will skip generating a `.d.ts` file for the entry point.
19
+ */
20
+ dtsInput?: boolean;
21
+ isolatedDeclaration?: Omit<IsolatedDeclarationsOptions, "sourcemap">;
22
+ /**
23
+ * dts file name alias `{ [filename]: path }`
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * inputAlias: {
28
+ * 'foo.d.ts': 'foo/index.d.ts',
29
+ * }
30
+ */
31
+ inputAlias?: Record<string, string>;
32
+ /**
33
+ * Determines whether the module imported by `.d.ts` files should be treated as external or not.
34
+ */
35
+ external?: (id: string, importer: string, extraOptions: ResolveIdExtraOptions) => boolean | void;
36
+ }
37
+ declare function dts(options?: Options): Plugin[];
38
+
39
+ //#endregion
40
+ export { Options, createFakeJsPlugin, createGeneratePlugin, dts };
package/dist/index.js ADDED
@@ -0,0 +1,516 @@
1
+ import { MagicStringAST } from "magic-string-ast";
2
+ import { parseAsync } from "oxc-parser";
3
+ import path, { basename } from "node:path";
4
+ import { isolatedDeclaration } from "oxc-transform";
5
+
6
+ //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js
7
+ var WalkerBase = class {
8
+ constructor() {
9
+ /** @type {boolean} */
10
+ this.should_skip = false;
11
+ /** @type {boolean} */
12
+ this.should_remove = false;
13
+ /** @type {Node | null} */
14
+ this.replacement = null;
15
+ /** @type {WalkerContext} */
16
+ this.context = {
17
+ skip: () => this.should_skip = true,
18
+ remove: () => this.should_remove = true,
19
+ replace: (node) => this.replacement = node
20
+ };
21
+ }
22
+ /**
23
+ * @template {Node} Parent
24
+ * @param {Parent | null | undefined} parent
25
+ * @param {keyof Parent | null | undefined} prop
26
+ * @param {number | null | undefined} index
27
+ * @param {Node} node
28
+ */
29
+ replace(parent, prop, index, node) {
30
+ if (parent && prop) if (index != null)
31
+ /** @type {Array<Node>} */ parent[prop][index] = node;
32
+ else
33
+ /** @type {Node} */ parent[prop] = node;
34
+ }
35
+ /**
36
+ * @template {Node} Parent
37
+ * @param {Parent | null | undefined} parent
38
+ * @param {keyof Parent | null | undefined} prop
39
+ * @param {number | null | undefined} index
40
+ */
41
+ remove(parent, prop, index) {
42
+ if (parent && prop) if (index !== null && index !== void 0)
43
+ /** @type {Array<Node>} */ parent[prop].splice(index, 1);
44
+ else delete parent[prop];
45
+ }
46
+ };
47
+
48
+ //#endregion
49
+ //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/sync.js
50
+ var SyncWalker = class extends WalkerBase {
51
+ /**
52
+ *
53
+ * @param {SyncHandler} [enter]
54
+ * @param {SyncHandler} [leave]
55
+ */
56
+ constructor(enter, leave) {
57
+ super();
58
+ /** @type {boolean} */
59
+ this.should_skip = false;
60
+ /** @type {boolean} */
61
+ this.should_remove = false;
62
+ /** @type {Node | null} */
63
+ this.replacement = null;
64
+ /** @type {WalkerContext} */
65
+ this.context = {
66
+ skip: () => this.should_skip = true,
67
+ remove: () => this.should_remove = true,
68
+ replace: (node) => this.replacement = node
69
+ };
70
+ /** @type {SyncHandler | undefined} */
71
+ this.enter = enter;
72
+ /** @type {SyncHandler | undefined} */
73
+ this.leave = leave;
74
+ }
75
+ /**
76
+ * @template {Node} Parent
77
+ * @param {Node} node
78
+ * @param {Parent | null} parent
79
+ * @param {keyof Parent} [prop]
80
+ * @param {number | null} [index]
81
+ * @returns {Node | null}
82
+ */
83
+ visit(node, parent, prop, index) {
84
+ if (node) {
85
+ if (this.enter) {
86
+ const _should_skip = this.should_skip;
87
+ const _should_remove = this.should_remove;
88
+ const _replacement = this.replacement;
89
+ this.should_skip = false;
90
+ this.should_remove = false;
91
+ this.replacement = null;
92
+ this.enter.call(this.context, node, parent, prop, index);
93
+ if (this.replacement) {
94
+ node = this.replacement;
95
+ this.replace(parent, prop, index, node);
96
+ }
97
+ if (this.should_remove) this.remove(parent, prop, index);
98
+ const skipped = this.should_skip;
99
+ const removed = this.should_remove;
100
+ this.should_skip = _should_skip;
101
+ this.should_remove = _should_remove;
102
+ this.replacement = _replacement;
103
+ if (skipped) return node;
104
+ if (removed) return null;
105
+ }
106
+ /** @type {keyof Node} */
107
+ let key;
108
+ for (key in node) {
109
+ /** @type {unknown} */
110
+ const value = node[key];
111
+ if (value && typeof value === "object") {
112
+ if (Array.isArray(value)) {
113
+ const nodes = value;
114
+ for (let i = 0; i < nodes.length; i += 1) {
115
+ const item = nodes[i];
116
+ if (isNode(item)) {
117
+ if (!this.visit(item, node, key, i)) i--;
118
+ }
119
+ }
120
+ } else if (isNode(value)) this.visit(value, node, key, null);
121
+ }
122
+ }
123
+ if (this.leave) {
124
+ const _replacement = this.replacement;
125
+ const _should_remove = this.should_remove;
126
+ this.replacement = null;
127
+ this.should_remove = false;
128
+ this.leave.call(this.context, node, parent, prop, index);
129
+ if (this.replacement) {
130
+ node = this.replacement;
131
+ this.replace(parent, prop, index, node);
132
+ }
133
+ if (this.should_remove) this.remove(parent, prop, index);
134
+ const removed = this.should_remove;
135
+ this.replacement = _replacement;
136
+ this.should_remove = _should_remove;
137
+ if (removed) return null;
138
+ }
139
+ }
140
+ return node;
141
+ }
142
+ };
143
+ /**
144
+ * Ducktype a node.
145
+ *
146
+ * @param {unknown} value
147
+ * @returns {value is Node}
148
+ */
149
+ function isNode(value) {
150
+ return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
151
+ }
152
+
153
+ //#endregion
154
+ //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/index.js
155
+ function walk(ast, { enter, leave }) {
156
+ const instance = new SyncWalker(enter, leave);
157
+ return instance.visit(ast, null);
158
+ }
159
+
160
+ //#endregion
161
+ //#region src/utils/ast.ts
162
+ function getIdentifierRange(node, offset = 0) {
163
+ if ("typeAnnotation" in node && node.typeAnnotation) return [node.start + offset, node.typeAnnotation.start + offset];
164
+ return [node.start + offset, node.end + offset];
165
+ }
166
+
167
+ //#endregion
168
+ //#region src/utils/filename.ts
169
+ const RE_JS = /\.([cm]?)js$/;
170
+ const RE_TS = /\.([cm]?)ts$/;
171
+ const RE_DTS = /\.d\.([cm]?)ts$/;
172
+ const RE_NODE_MODULES = /node_modules/;
173
+ function filename_js_to_dts(id) {
174
+ return id.replace(RE_JS, ".d.$1ts");
175
+ }
176
+ function filename_ts_to_dts(id) {
177
+ return id.replace(RE_TS, ".d.$1ts");
178
+ }
179
+ function filename_dts_to(id, ext) {
180
+ return id.replace(RE_DTS, `.$1${ext}`);
181
+ }
182
+ function isRelative(id) {
183
+ return path.isAbsolute(id) || id[0] === ".";
184
+ }
185
+
186
+ //#endregion
187
+ //#region src/utils/magic-string.ts
188
+ function overwriteOrAppend(s, range, replacement, suffix) {
189
+ if (range[0] === range[1]) {
190
+ s.appendLeft(range[0], ` ${replacement}`);
191
+ return;
192
+ }
193
+ const original = s.slice(range[0], range[1]);
194
+ if (original !== replacement) s.overwrite(range[0], range[1], replacement + (suffix || ""));
195
+ }
196
+
197
+ //#endregion
198
+ //#region src/fake-js.ts
199
+ const RE_TYPE = /\btype\b/;
200
+ function createFakeJsPlugin({ dtsInput }) {
201
+ let symbolIdx = 0;
202
+ let identifierIdx = 0;
203
+ const symbolMap = new Map();
204
+ const preserveMap = new Map();
205
+ function getIdentifierIndex() {
206
+ return identifierIdx++;
207
+ }
208
+ function register(info) {
209
+ const symbolId = symbolIdx++;
210
+ symbolMap.set(symbolId, info);
211
+ return symbolId;
212
+ }
213
+ function retrieve(symbolId) {
214
+ return symbolMap.get(symbolId);
215
+ }
216
+ return {
217
+ name: "rolldown-plugin-dts:fake-js",
218
+ options: dtsInput ? (options) => {
219
+ return {
220
+ ...options,
221
+ resolve: {
222
+ extensions: [
223
+ ".d.ts",
224
+ ".d.mts",
225
+ ".d.cts"
226
+ ],
227
+ extensionAlias: {
228
+ ".js": [".d.ts"],
229
+ ".mjs": [".d.mts"],
230
+ ".cjs": [".d.cts"]
231
+ },
232
+ ...options.resolve
233
+ }
234
+ };
235
+ } : void 0,
236
+ outputOptions(options) {
237
+ return {
238
+ ...options,
239
+ entryFileNames: options.entryFileNames ?? (dtsInput ? "[name].ts" : void 0),
240
+ chunkFileNames(chunk) {
241
+ const original = (typeof options.chunkFileNames === "function" ? options.chunkFileNames(chunk) : options.chunkFileNames) || "[name]-[hash].js";
242
+ if (chunk.name.endsWith(".d")) return filename_js_to_dts(original);
243
+ return original;
244
+ }
245
+ };
246
+ },
247
+ transform: {
248
+ filter: { id: {
249
+ include: [RE_DTS],
250
+ exclude: [RE_NODE_MODULES]
251
+ } },
252
+ async handler(code, id) {
253
+ const { program, comments } = await parseAsync(id, code);
254
+ const preserved = collectReferenceDirectives(comments);
255
+ preserveMap.set(id, preserved);
256
+ const s = new MagicStringAST(code);
257
+ for (let node of program.body) {
258
+ if (node.type === "ExportAllDeclaration" && node.exported && isRelative(node.source.value)) throw new Error("`export * as foo from './...'` is not supported");
259
+ if (rewriteImportExport(s, node)) continue;
260
+ const sideEffect = node.type === "TSModuleDeclaration" && node.kind !== "namespace";
261
+ const stmt = node;
262
+ const isDefaultExport = node.type === "ExportDefaultDeclaration";
263
+ if ((node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") && node.declaration) node = node.declaration;
264
+ if (node.type === "VariableDeclaration" && node.declarations.length !== 1) throw new Error("Only one declaration is supported");
265
+ if (node.type === "TSDeclareFunction" || node.type.endsWith("Declaration")) {
266
+ const binding = node.type === "VariableDeclaration" ? node.declarations[0].id : node.id;
267
+ const code$1 = s.sliceNode(node);
268
+ const jsdoc = comments.find((c) => c.type === "Block" && c.value[0] === "*" && c.start < node.start && stmt.start - c.end <= 1);
269
+ const offset = node.start;
270
+ let bindingRange;
271
+ if (sideEffect) bindingRange = [0, 0];
272
+ else if (binding) bindingRange = getIdentifierRange(binding, -offset);
273
+ else if (isDefaultExport) {
274
+ const idx = s.sliceNode(node).indexOf("function") + 8;
275
+ bindingRange = [idx, idx];
276
+ } else continue;
277
+ const depsNodes = collectDependencies(s, node, getIdentifierIndex);
278
+ const depsString = stringifyDependencies(s, depsNodes);
279
+ const depsSymbols = depsNodes.map((dep) => [
280
+ dep.start - offset,
281
+ dep.end - offset,
282
+ dep._suffix
283
+ ]);
284
+ const needDeclare = (node.type === "TSEnumDeclaration" || node.type === "ClassDeclaration" || node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction" || node.type === "TSModuleDeclaration" || node.type === "VariableDeclaration") && !node.declare;
285
+ const symbolId = register({
286
+ code: code$1,
287
+ binding: bindingRange,
288
+ deps: depsSymbols,
289
+ needDeclare,
290
+ jsdoc: jsdoc ? s.sliceNode(jsdoc) : void 0,
291
+ preserveName: sideEffect
292
+ });
293
+ const runtime = `[${symbolId}, ${depsString}${depsString && sideEffect ? ", " : ""}${sideEffect ? "sideEffect()" : ""}]`;
294
+ const bindingName = sideEffect ? `_${identifierIdx++}` : binding ? s.sliceNode(binding) : "export_default";
295
+ if (isDefaultExport) s.overwriteNode(stmt, `var ${bindingName} = ${runtime};export { ${bindingName} as default }`);
296
+ else s.overwriteNode(node, `var ${bindingName} = ${runtime};`);
297
+ }
298
+ }
299
+ if (!s.hasChanged()) return;
300
+ const str = s.toString();
301
+ return str;
302
+ }
303
+ },
304
+ async renderChunk(code, chunk) {
305
+ if (!RE_DTS.test(chunk.fileName)) return;
306
+ const { program } = await parseAsync(chunk.fileName, code);
307
+ const s = new MagicStringAST(code);
308
+ const comments = new Set();
309
+ for (const id of chunk.moduleIds) {
310
+ const preserveComments = preserveMap.get(id);
311
+ if (preserveComments) {
312
+ preserveComments.forEach((c) => comments.add(c));
313
+ preserveMap.delete(id);
314
+ }
315
+ }
316
+ if (comments.size) s.prepend(`${[...comments].join("\n")}\n`);
317
+ for (const node of program.body) {
318
+ if (patchImportSource(s, node)) continue;
319
+ if (node.type !== "VariableDeclaration" || node.declarations.length !== 1) continue;
320
+ const [decl] = node.declarations;
321
+ if (decl.init?.type !== "ArrayExpression" || !decl.init.elements[0]) {
322
+ patchVariableDeclarator(s, node, decl);
323
+ continue;
324
+ }
325
+ const [symbolIdNode, ...depsNodes] = decl.init.elements;
326
+ if (symbolIdNode?.type !== "Literal" || typeof symbolIdNode.value !== "number") {
327
+ patchVariableDeclarator(s, node, decl);
328
+ continue;
329
+ }
330
+ const symbolId = symbolIdNode.value;
331
+ const { code: code$1, binding, deps, needDeclare, jsdoc, preserveName } = retrieve(symbolId);
332
+ const depsRaw = depsNodes.filter((node$1) => node$1?.type === "ArrowFunctionExpression").map((dep) => s.sliceNode(dep.body));
333
+ const ss = new MagicStringAST(code$1);
334
+ if (!preserveName) overwriteOrAppend(ss, binding, s.sliceNode(decl.id));
335
+ for (const dep of deps) {
336
+ const [start, end, suffix] = dep;
337
+ overwriteOrAppend(ss, [start, end], depsRaw.shift(), suffix);
338
+ }
339
+ if (needDeclare) ss.prepend("declare ");
340
+ if (jsdoc) ss.prepend(`${jsdoc}\n`);
341
+ s.overwriteNode(node, ss.toString());
342
+ }
343
+ const str = s.toString();
344
+ if (str.trim().length === 0) return "export {}";
345
+ return str;
346
+ }
347
+ };
348
+ }
349
+ const REFERENCE_RE = /\/\s*<reference\s+(?:path|types)=/;
350
+ function collectReferenceDirectives(comment) {
351
+ return comment.filter((c) => REFERENCE_RE.test(c.value)).map((c) => `//${c.value}`);
352
+ }
353
+ function collectDependencies(s, node, getIdentifierIndex) {
354
+ const deps = new Set();
355
+ walk(node, { leave(node$1) {
356
+ if (node$1.type === "ExportNamedDeclaration") {
357
+ for (const specifier of node$1.specifiers) if (specifier.type === "ExportSpecifier") {
358
+ let _suffix;
359
+ if (specifier.local.start === specifier.exported.start && specifier.local.end === specifier.exported.end) _suffix = ` as ${s.sliceNode(specifier.local)}`;
360
+ addDependency({
361
+ ...specifier.local,
362
+ _suffix
363
+ });
364
+ }
365
+ } else if (node$1.type === "TSInterfaceDeclaration" && node$1.extends) for (const heritage of node$1.extends || []) addDependency(heritage.expression);
366
+ else if (node$1.type === "ClassDeclaration") {
367
+ if (node$1.superClass) addDependency(node$1.superClass);
368
+ if (node$1.implements) for (const implement of node$1.implements) addDependency(implement.expression);
369
+ } else if (node$1.type === "MethodDefinition" || node$1.type === "PropertyDefinition" || node$1.type === "TSPropertySignature") {
370
+ if (node$1.computed && isReferenceId(node$1.key)) addDependency(node$1.key);
371
+ if ("value" in node$1 && isReferenceId(node$1.value)) addDependency(node$1.value);
372
+ } else if (node$1.type === "TSTypeReference") addDependency(node$1.typeName);
373
+ else if (node$1.type === "TSTypeQuery") addDependency(node$1.exprName);
374
+ else if (node$1.type === "TSImportType") {
375
+ if (node$1.argument.type !== "TSLiteralType" || node$1.argument.literal.type !== "Literal" || typeof node$1.argument.literal.value !== "string") return;
376
+ if (!node$1.qualifier) throw new Error("Import namespace is not supported");
377
+ const source = node$1.argument.literal.value;
378
+ const imported = s.sliceNode(node$1.qualifier);
379
+ const local = importNamespace(s, source, imported, getIdentifierIndex);
380
+ addDependency({
381
+ type: "Identifier",
382
+ name: local,
383
+ start: node$1.start + (node$1.isTypeOf ? 7 : 0),
384
+ end: node$1.qualifier.end
385
+ });
386
+ }
387
+ } });
388
+ return Array.from(deps);
389
+ function addDependency(node$1) {
390
+ if (node$1.type === "Identifier" && node$1.name === "this") return;
391
+ deps.add(node$1);
392
+ }
393
+ }
394
+ function isReferenceId(node) {
395
+ return !!node && (node.type === "Identifier" || node.type === "MemberExpression");
396
+ }
397
+ function stringifyDependencies(s, deps) {
398
+ return deps.map((node) => `() => ${node.type === "Identifier" ? node.name : s.sliceNode(node)}`).join(", ");
399
+ }
400
+ function patchVariableDeclarator(s, node, decl) {
401
+ if (decl.init && !decl.id.typeAnnotation) s.overwriteNode(node, `type ${s.sliceNode(decl.id)} = ${s.sliceNode(decl.init)}`);
402
+ else if (!node.declare) s.prependLeft(node.start, "declare ");
403
+ }
404
+ function patchImportSource(s, node) {
405
+ if ((node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration") && node.source?.value && RE_DTS.test(node.source.value)) {
406
+ s.overwriteNode(node.source, JSON.stringify(filename_dts_to(node.source.value, "js")));
407
+ return true;
408
+ }
409
+ }
410
+ function rewriteImportExport(s, node) {
411
+ if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" && !node.declaration) {
412
+ for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier" && specifier.importKind === "type" || specifier.type === "ExportSpecifier" && specifier.exportKind === "type") s.overwriteNode(specifier, s.sliceNode(specifier).replace(RE_TYPE, ""));
413
+ const firstSpecifier = node.specifiers[0];
414
+ const kind = node.type === "ImportDeclaration" ? node.importKind : node.exportKind;
415
+ if (kind === "type" && firstSpecifier) s.overwrite(node.start, firstSpecifier.start, s.slice(node.start, firstSpecifier.start).replace(RE_TYPE, ""));
416
+ return true;
417
+ } else if (node.type === "ExportAllDeclaration") {
418
+ if (node.exportKind === "type") s.overwrite(node.start, node.source.start, s.slice(node.start, node.source.start).replace(RE_TYPE, ""));
419
+ return true;
420
+ } else if (node.type === "TSImportEqualsDeclaration") {
421
+ if (node.moduleReference.type === "TSExternalModuleReference") s.overwriteNode(node, `import ${s.sliceNode(node.id)} from ${s.sliceNode(node.moduleReference.expression)}`);
422
+ return true;
423
+ } else if (node.type === "TSExportAssignment") {
424
+ s.overwriteNode(node, `export default ${s.sliceNode(node.expression)}`);
425
+ return true;
426
+ } else if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "Identifier") {
427
+ s.overwriteNode(node, `export { ${s.sliceNode(node.declaration)} as default }`);
428
+ return true;
429
+ }
430
+ }
431
+ function importNamespace(s, source, imported, getIdentifierIndex) {
432
+ const local = `_${getIdentifierIndex()}`;
433
+ s.prepend(`import { ${imported} as ${local} } from ${JSON.stringify(source)};\n`);
434
+ return local;
435
+ }
436
+
437
+ //#endregion
438
+ //#region src/generate.ts
439
+ function createGeneratePlugin({ isolatedDeclaration: isolatedDeclaration$1, inputAlias, external }) {
440
+ const dtsMap = new Map();
441
+ return {
442
+ name: "rolldown-plugin-dts:generate",
443
+ transform: {
444
+ order: "pre",
445
+ filter: { id: {
446
+ include: [RE_TS],
447
+ exclude: [RE_DTS, RE_NODE_MODULES]
448
+ } },
449
+ handler(code, id) {
450
+ const { code: dtsCode, errors } = isolatedDeclaration(id, code, isolatedDeclaration$1);
451
+ if (errors.length) return this.error(errors[0]);
452
+ const dtsId = filename_ts_to_dts(id);
453
+ dtsMap.set(dtsId, dtsCode);
454
+ const mod = this.getModuleInfo(id);
455
+ if (mod?.isEntry) {
456
+ let fileName = basename(dtsId);
457
+ if (inputAlias?.[fileName]) fileName = inputAlias[fileName];
458
+ this.emitFile({
459
+ type: "chunk",
460
+ id: dtsId,
461
+ fileName
462
+ });
463
+ }
464
+ }
465
+ },
466
+ async resolveId(id, importer, extraOptions) {
467
+ if (dtsMap.has(id)) return {
468
+ id,
469
+ meta: { dtsFile: true }
470
+ };
471
+ const importerMod = importer ? this.getModuleInfo(importer) : null;
472
+ if (importerMod?.meta.dtsFile) {
473
+ if (!isRelative(id) || external?.(id, importer, extraOptions) === true) return {
474
+ id,
475
+ external: true
476
+ };
477
+ const resolution = await this.resolve(id, filename_dts_to(importer, "ts"));
478
+ if (!resolution || resolution.external) return;
479
+ const dtsId = filename_ts_to_dts(resolution.id);
480
+ if (dtsMap.has(dtsId)) return {
481
+ id: dtsId,
482
+ meta: { dtsFile: true }
483
+ };
484
+ await this.load(resolution);
485
+ if (dtsMap.has(dtsId)) return {
486
+ id: dtsId,
487
+ meta: { dtsFile: true }
488
+ };
489
+ }
490
+ },
491
+ load: {
492
+ filter: { id: {
493
+ include: [RE_DTS],
494
+ exclude: [RE_NODE_MODULES]
495
+ } },
496
+ handler(id) {
497
+ if (dtsMap.has(id)) return {
498
+ code: dtsMap.get(id),
499
+ moduleSideEffects: false
500
+ };
501
+ }
502
+ }
503
+ };
504
+ }
505
+
506
+ //#endregion
507
+ //#region src/index.ts
508
+ function dts(options = {}) {
509
+ const plugins = [];
510
+ if (!options.dtsInput) plugins.push(createGeneratePlugin(options));
511
+ plugins.push(createFakeJsPlugin(options));
512
+ return plugins;
513
+ }
514
+
515
+ //#endregion
516
+ export { createFakeJsPlugin, createGeneratePlugin, dts };
package/package.json CHANGED
@@ -1,12 +1,67 @@
1
1
  {
2
2
  "name": "rolldown-plugin-dts",
3
- "version": "0.0.0",
4
- "description": "",
5
- "main": "index.js",
6
- "keywords": [],
7
- "author": "",
8
- "license": "ISC",
3
+ "version": "0.2.0",
4
+ "description": "A Rolldown plugin to bundle dts files",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/sxzz/rolldown-plugin-dts#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/sxzz/rolldown-plugin-dts/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/sxzz/rolldown-plugin-dts.git"
14
+ },
15
+ "author": "三咲智子 Kevin Deng <sxzz@sxzz.moe>",
16
+ "funding": "https://github.com/sponsors/sxzz",
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "main": "./dist/index.js",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": "./dist/index.js",
25
+ "./package.json": "./package.json"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "magic-string-ast": "^0.9.1",
32
+ "oxc-parser": "^0.62.0",
33
+ "oxc-transform": "^0.62.0"
34
+ },
35
+ "devDependencies": {
36
+ "@sxzz/eslint-config": "^6.1.1",
37
+ "@sxzz/prettier-config": "^2.2.1",
38
+ "@sxzz/test-utils": "^0.5.4",
39
+ "@types/diff": "^7.0.2",
40
+ "@types/node": "^22.14.0",
41
+ "bumpp": "^10.1.0",
42
+ "diff": "^7.0.0",
43
+ "eslint": "^9.24.0",
44
+ "estree-walker": "^3.0.3",
45
+ "prettier": "^3.5.3",
46
+ "rolldown": "1.0.0-beta.7",
47
+ "rollup-plugin-dts": "^6.2.1",
48
+ "tsdown": "^0.8.0-beta.1",
49
+ "tsx": "^4.19.3",
50
+ "typescript": "^5.8.3",
51
+ "vitest": "^3.1.1"
52
+ },
53
+ "engines": {
54
+ "node": ">=20.18.0"
55
+ },
56
+ "prettier": "@sxzz/prettier-config",
9
57
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
58
+ "lint": "eslint --cache .",
59
+ "lint:fix": "pnpm run lint --fix",
60
+ "build": "tsdown",
61
+ "dev": "tsdown --watch",
62
+ "test": "vitest",
63
+ "typecheck": "tsc --noEmit",
64
+ "format": "prettier --cache --write .",
65
+ "release": "bumpp && pnpm publish"
11
66
  }
12
67
  }
@@ -1,5 +0,0 @@
1
- {
2
- "cSpell.words": [
3
- "rolldown"
4
- ]
5
- }