rolldown-plugin-dts 0.8.6 → 0.9.1

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
@@ -31,6 +31,11 @@ You can find a real demo in [here](./rolldown.config.ts).
31
31
 
32
32
  ```ts
33
33
  interface Options {
34
+ /**
35
+ * The directory where the the plugin will look for the `tsconfig.json` file.
36
+ */
37
+ cwd?: string
38
+
34
39
  /**
35
40
  * When entries are `.d.ts` files (instead of `.ts` files), this option should be set to `true`.
36
41
  *
@@ -72,6 +77,11 @@ interface Options {
72
77
  | boolean
73
78
  | Omit<IsolatedDeclarationsOptions, 'sourcemap'>
74
79
 
80
+ /**
81
+ * When `true`, the plugin will generate declaration maps for `.d.ts` files.
82
+ */
83
+ sourcemap?: boolean
84
+
75
85
  /** Resolve external types used in dts files from `node_modules` */
76
86
  resolve?: boolean | (string | RegExp)[]
77
87
  }
package/dist/index.d.ts CHANGED
@@ -3,53 +3,85 @@ import { IsolatedDeclarationsOptions } from "oxc-transform";
3
3
  import { Plugin } from "rolldown";
4
4
 
5
5
  //#region src/fake-js.d.ts
6
- declare function createFakeJsPlugin({ dtsInput }: Pick<Options, "dtsInput">): Plugin;
6
+ declare function createFakeJsPlugin({
7
+ dtsInput,
8
+ sourcemap
9
+ }: OptionsResolved): Plugin;
7
10
 
8
11
  //#endregion
9
12
  //#region src/generate.d.ts
10
- declare function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations, resolve, emitDtsOnly }: Pick<Options, "isolatedDeclarations" | "resolve" | "emitDtsOnly" | "tsconfig" | "compilerOptions">): Plugin;
13
+ declare function createGeneratePlugin({
14
+ tsconfig,
15
+ compilerOptions,
16
+ isolatedDeclarations,
17
+ resolve,
18
+ emitDtsOnly
19
+ }: OptionsResolved): Plugin;
11
20
 
12
21
  //#endregion
13
22
  //#region src/index.d.ts
14
23
  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
- /**
22
- * When `true`, the plugin will only emit `.d.ts` files and remove all other chunks.
23
- *
24
- * This feature is particularly beneficial when you need to generate `d.ts` files for the CommonJS format as part of a separate build process.
25
- */
26
- emitDtsOnly?: boolean;
27
- /**
28
- * The path to the `tsconfig.json` file.
29
- *
30
- * When set to `false`, the plugin will ignore any `tsconfig.json` file.
31
- * However, `compilerOptions` can still be specified directly in the options.
32
- *
33
- * @default `tsconfig.json`
34
- */
35
- tsconfig?: string | boolean;
36
- /**
37
- * The `compilerOptions` for the TypeScript compiler.
38
- *
39
- * @see https://www.typescriptlang.org/docs/handbook/compiler-options.html
40
- */
41
- compilerOptions?: TsConfigJson.CompilerOptions;
42
- /**
43
- * When `true`, the plugin will generate `.d.ts` files using `oxc-transform`,
44
- * which is blazingly faster than `typescript` compiler.
45
- *
46
- * This option is enabled when `isolatedDeclarations` in `compilerOptions` is set to `true`.
47
- */
48
- isolatedDeclarations?: boolean | Omit<IsolatedDeclarationsOptions, "sourcemap">;
49
- /** Resolve external types used in dts files from `node_modules` */
50
- resolve?: boolean | (string | RegExp)[];
24
+ /**
25
+ * The directory where the the plugin will look for the `tsconfig.json` file.
26
+ */
27
+ cwd?: string;
28
+ /**
29
+ * When entries are `.d.ts` files (instead of `.ts` files), this option should be set to `true`.
30
+ *
31
+ * If enabled, the plugin will skip generating a `.d.ts` file for the entry point.
32
+ */
33
+ dtsInput?: boolean;
34
+ /**
35
+ * When `true`, the plugin will only emit `.d.ts` files and remove all other chunks.
36
+ *
37
+ * This feature is particularly beneficial when you need to generate `d.ts` files for the CommonJS format as part of a separate build process.
38
+ */
39
+ emitDtsOnly?: boolean;
40
+ /**
41
+ * The path to the `tsconfig.json` file.
42
+ *
43
+ * When set to `false`, the plugin will ignore any `tsconfig.json` file.
44
+ * However, `compilerOptions` can still be specified directly in the options.
45
+ *
46
+ * @default `tsconfig.json`
47
+ */
48
+ tsconfig?: string | boolean;
49
+ /**
50
+ * The `compilerOptions` for the TypeScript compiler.
51
+ *
52
+ * @see https://www.typescriptlang.org/docs/handbook/compiler-options.html
53
+ */
54
+ compilerOptions?: TsConfigJson.CompilerOptions;
55
+ /**
56
+ * When `true`, the plugin will generate `.d.ts` files using `oxc-transform`,
57
+ * which is blazingly faster than `typescript` compiler.
58
+ *
59
+ * This option is enabled when `isolatedDeclarations` in `compilerOptions` is set to `true`.
60
+ */
61
+ isolatedDeclarations?: boolean | Omit<IsolatedDeclarationsOptions, "sourcemap">;
62
+ /**
63
+ * When `true`, the plugin will generate declaration maps for `.d.ts` files.
64
+ */
65
+ sourcemap?: boolean;
66
+ /** Resolve external types used in dts files from `node_modules` */
67
+ resolve?: boolean | (string | RegExp)[];
51
68
  }
69
+ type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
70
+ type OptionsResolved = Overwrite<Required<Options>, {
71
+ tsconfig: string | undefined;
72
+ isolatedDeclarations: IsolatedDeclarationsOptions | false;
73
+ }>;
52
74
  declare function dts(options?: Options): Plugin[];
75
+ declare function resolveOptions({
76
+ cwd,
77
+ tsconfig,
78
+ compilerOptions,
79
+ isolatedDeclarations,
80
+ sourcemap,
81
+ dtsInput,
82
+ emitDtsOnly,
83
+ resolve
84
+ }: Options): OptionsResolved;
53
85
 
54
86
  //#endregion
55
- export { Options, createFakeJsPlugin, createGeneratePlugin, dts };
87
+ export { Options, OptionsResolved, createFakeJsPlugin, createGeneratePlugin, dts, resolveOptions };
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import path from "node:path";
2
- import { MagicStringAST } from "magic-string-ast";
3
- import { parseSync } from "oxc-parser";
4
2
  import process from "node:process";
5
- import { createResolver } from "dts-resolver";
6
3
  import { getTsconfig, parseTsconfig } from "get-tsconfig";
4
+ import _generate from "@babel/generator";
5
+ import { parse } from "@babel/parser";
6
+ import * as t from "@babel/types";
7
+ import { isDeclarationType, isTypeOf } from "ast-kit";
8
+ import { createResolver } from "dts-resolver";
7
9
  import { isolatedDeclaration } from "oxc-transform";
8
10
  import { createRequire } from "node:module";
9
11
  import Debug from "debug";
@@ -12,6 +14,7 @@ import Debug from "debug";
12
14
  const RE_JS = /\.([cm]?)jsx?$/;
13
15
  const RE_TS = /\.([cm]?)tsx?$/;
14
16
  const RE_DTS = /\.d\.([cm]?)ts$/;
17
+ const RE_DTS_MAP = /\.d\.([cm]?)ts\.map$/;
15
18
  const RE_NODE_MODULES = /[\\/]node_modules[\\/]/;
16
19
  function filename_js_to_dts(id) {
17
20
  return id.replace(RE_JS, ".d.$1ts");
@@ -43,7 +46,10 @@ function createDtsInputPlugin() {
43
46
  outputOptions(options) {
44
47
  return {
45
48
  ...options,
46
- entryFileNames: "[name].ts"
49
+ entryFileNames(chunk) {
50
+ if (chunk.name.endsWith(".d")) return "[name].ts";
51
+ return "[name].d.ts";
52
+ }
47
53
  };
48
54
  },
49
55
  resolveId: {
@@ -246,41 +252,23 @@ function walk(ast, { enter, leave }) {
246
252
  return instance.visit(ast, null);
247
253
  }
248
254
 
249
- //#endregion
250
- //#region src/utils/ast.ts
251
- function getIdentifierRange(node, offset = 0) {
252
- if ("typeAnnotation" in node && node.typeAnnotation) return [node.start + offset, node.typeAnnotation.start + offset];
253
- return [node.start + offset, node.end + offset];
254
- }
255
-
256
- //#endregion
257
- //#region src/utils/magic-string.ts
258
- function overwriteOrAppend(s, range, replacement, suffix) {
259
- if (range[0] === range[1]) {
260
- s.appendLeft(range[0], ` ${replacement}`);
261
- return;
262
- }
263
- const original = s.slice(range[0], range[1]);
264
- if (original !== replacement) s.overwrite(range[0], range[1], replacement + (suffix || ""));
265
- }
266
-
267
255
  //#endregion
268
256
  //#region src/fake-js.ts
269
- const RE_TYPE = /\btype\b/;
270
- function createFakeJsPlugin({ dtsInput }) {
257
+ const generate = _generate.default || _generate;
258
+ function createFakeJsPlugin({ dtsInput, sourcemap }) {
271
259
  let symbolIdx = 0;
272
260
  let identifierIdx = 0;
273
261
  const symbolMap = new Map();
274
- const preserveMap = new Map();
262
+ const commentsMap = new Map();
275
263
  function getIdentifierIndex() {
276
264
  return identifierIdx++;
277
265
  }
278
- function register(info) {
266
+ function registerSymbol(info) {
279
267
  const symbolId = symbolIdx++;
280
268
  symbolMap.set(symbolId, info);
281
269
  return symbolId;
282
270
  }
283
- function retrieve(symbolId) {
271
+ function getSymbol(symbolId) {
284
272
  return symbolMap.get(symbolId);
285
273
  }
286
274
  return {
@@ -307,6 +295,7 @@ function createFakeJsPlugin({ dtsInput }) {
307
295
  if (options.format === "cjs" || options.format === "commonjs") throw new Error("[rolldown-plugin-dts] Cannot bundle dts files with `cjs` format.");
308
296
  return {
309
297
  ...options,
298
+ sourcemap: sourcemap ? true : options.sourcemap,
310
299
  entryFileNames: options.entryFileNames ?? (dtsInput ? "[name].ts" : void 0),
311
300
  chunkFileNames(chunk) {
312
301
  const original = (typeof options.chunkFileNames === "function" ? options.chunkFileNames(chunk) : options.chunkFileNames) || "[name]-[hash].js";
@@ -318,137 +307,183 @@ function createFakeJsPlugin({ dtsInput }) {
318
307
  transform: {
319
308
  filter: { id: RE_DTS },
320
309
  handler(code, id) {
321
- const { program, comments } = parseSync(id, code);
322
- const preserved = collectReferenceDirectives(comments);
323
- preserveMap.set(id, preserved);
324
- const s = new MagicStringAST(code);
325
- for (let node of program.body) {
326
- if (rewriteImportExport(s, node)) continue;
327
- const sideEffect = node.type === "TSModuleDeclaration" && node.kind !== "namespace";
328
- const stmt = node;
329
- const isDefaultExport = node.type === "ExportDefaultDeclaration";
330
- if ((node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") && node.declaration) node = node.declaration;
331
- if (node.type === "VariableDeclaration" && node.declarations.length !== 1) throw new Error("Only one declaration is supported");
332
- if (node.type === "TSDeclareFunction" || node.type.endsWith("Declaration")) {
333
- const binding = node.type === "VariableDeclaration" ? node.declarations[0].id : node.id;
334
- const code$1 = s.sliceNode(node);
335
- const offset = node.start;
336
- let bindingRange;
337
- if (sideEffect) bindingRange = [0, 0];
338
- else if (binding) bindingRange = getIdentifierRange(binding, -offset);
339
- else if (isDefaultExport) {
340
- const idx = s.sliceNode(node).indexOf("function") + 8;
341
- bindingRange = [idx, idx];
342
- } else continue;
343
- const depsNodes = collectDependencies(s, node, getIdentifierIndex);
344
- const depsString = stringifyDependencies(s, depsNodes);
345
- const depsSymbols = depsNodes.map((dep) => [
346
- dep.start - offset,
347
- dep.end - offset,
348
- dep._suffix
349
- ]);
350
- const needDeclare = (node.type === "TSEnumDeclaration" || node.type === "ClassDeclaration" || node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction" || node.type === "TSModuleDeclaration" || node.type === "VariableDeclaration") && !node.declare;
351
- const symbolId = register({
352
- code: code$1,
353
- binding: bindingRange,
354
- deps: depsSymbols,
355
- needDeclare,
356
- preserveName: sideEffect
357
- });
358
- const runtime = `[${symbolId}, ${depsString}${depsString && sideEffect ? ", " : ""}${sideEffect ? "sideEffect()" : ""}]`;
359
- const bindingName = sideEffect ? `_${identifierIdx++}` : binding ? s.sliceNode(binding) : "export_default";
360
- if (isDefaultExport) s.overwriteNode(stmt, `var ${bindingName} = ${runtime};export { ${bindingName} as default }`);
361
- else s.overwriteNode(node, `var ${bindingName} = ${runtime};`);
310
+ const file = parse(code, {
311
+ plugins: [["typescript", { dts: true }]],
312
+ sourceType: "module"
313
+ });
314
+ const { program, comments } = file;
315
+ if (comments) {
316
+ const directives = collectReferenceDirectives(comments);
317
+ commentsMap.set(id, directives);
318
+ }
319
+ const prependStmts = [];
320
+ const appendStmts = [];
321
+ const prepend = (stmt) => prependStmts.push(stmt);
322
+ for (const [i, stmt] of program.body.entries()) {
323
+ const setStmt = (node) => program.body[i] = inheritNode(stmt, node);
324
+ if (rewriteImportExport(stmt, setStmt)) continue;
325
+ const sideEffect = stmt.type === "TSModuleDeclaration" && stmt.kind !== "namespace";
326
+ const isDefaultExport = stmt.type === "ExportDefaultDeclaration";
327
+ const isDecl = isTypeOf(stmt, ["ExportNamedDeclaration", "ExportDefaultDeclaration"]) && stmt.declaration;
328
+ const decl = isDecl ? stmt.declaration : stmt;
329
+ const setDecl = isDecl ? (node) => stmt.declaration = inheritNode(stmt.declaration, node) : setStmt;
330
+ if (decl.type === "VariableDeclaration" && decl.declarations.length !== 1) throw new Error("Only one declaration is supported");
331
+ if (decl.type !== "TSDeclareFunction" && !isDeclarationType(decl)) continue;
332
+ if (isTypeOf(decl, [
333
+ "TSEnumDeclaration",
334
+ "ClassDeclaration",
335
+ "FunctionDeclaration",
336
+ "TSDeclareFunction",
337
+ "TSModuleDeclaration",
338
+ "VariableDeclaration"
339
+ ])) decl.declare = true;
340
+ let binding = decl.type === "VariableDeclaration" ? decl.declarations[0].id : "id" in decl ? decl.id : null;
341
+ if (!binding) {
342
+ binding = t.identifier("export_default");
343
+ decl.id = binding;
362
344
  }
345
+ binding = sideEffect ? t.identifier(`_${identifierIdx++}`) : binding;
346
+ const deps = collectDependencies(decl, getIdentifierIndex, prepend);
347
+ const elements = [
348
+ t.numericLiteral(0),
349
+ ...deps.map((dep) => t.arrowFunctionExpression([], dep)),
350
+ ...sideEffect ? [t.callExpression(t.identifier("sideEffect"), [])] : []
351
+ ];
352
+ const runtime = t.arrayExpression(elements);
353
+ const symbolId = registerSymbol({
354
+ decl,
355
+ deps,
356
+ binding
357
+ });
358
+ elements[0] = t.numericLiteral(symbolId);
359
+ const runtimeAssignment = {
360
+ type: "VariableDeclaration",
361
+ kind: "var",
362
+ declarations: [{
363
+ type: "VariableDeclarator",
364
+ id: {
365
+ ...binding,
366
+ typeAnnotation: null
367
+ },
368
+ init: runtime
369
+ }]
370
+ };
371
+ if (isDefaultExport) {
372
+ appendStmts.push(t.exportNamedDeclaration(null, [t.exportSpecifier(binding, t.identifier("default"))]));
373
+ setStmt(runtimeAssignment);
374
+ } else setDecl(runtimeAssignment);
363
375
  }
364
- if (!s.hasChanged()) return;
365
- const str = s.toString();
366
- return str;
376
+ program.body = [
377
+ ...prependStmts,
378
+ ...program.body,
379
+ ...appendStmts
380
+ ];
381
+ const result = generate(file, {
382
+ comments: true,
383
+ sourceMaps: sourcemap,
384
+ sourceFileName: id
385
+ });
386
+ return result;
367
387
  }
368
388
  },
369
389
  renderChunk(code, chunk) {
370
390
  if (!RE_DTS.test(chunk.fileName)) return;
371
- const { program } = parseSync(chunk.fileName, code);
372
- const s = new MagicStringAST(code);
373
- const comments = new Set();
374
- for (const id of chunk.moduleIds) {
375
- const preserveComments = preserveMap.get(id);
376
- if (preserveComments) {
377
- preserveComments.forEach((c) => comments.add(c));
378
- preserveMap.delete(id);
391
+ const file = parse(code, { sourceType: "module" });
392
+ const { program } = file;
393
+ if (program.body.length) {
394
+ const comments = new Set();
395
+ const commentsValue = new Set();
396
+ for (const id of chunk.moduleIds) {
397
+ const preserveComments = commentsMap.get(id);
398
+ if (preserveComments) {
399
+ preserveComments.forEach((c) => {
400
+ const id$1 = c.type + c.value;
401
+ if (commentsValue.has(id$1)) return;
402
+ commentsValue.add(id$1);
403
+ comments.add(c);
404
+ });
405
+ commentsMap.delete(id);
406
+ }
407
+ }
408
+ if (comments.size) {
409
+ program.body[0].leadingComments ||= [];
410
+ program.body[0].leadingComments.push(...comments);
379
411
  }
380
412
  }
381
- if (comments.size) s.prepend(`${[...comments].join("\n")}\n`);
382
- const removedNodes = patchTsNamespace(s, program.body);
383
- for (const node of program.body) {
384
- if (removedNodes.has(node)) continue;
385
- if (patchImportSource(s, node)) continue;
386
- if (node.type !== "VariableDeclaration" || node.declarations.length !== 1) continue;
413
+ program.body = patchTsNamespace(program.body);
414
+ program.body = program.body.map((node) => {
415
+ if (patchImportSource(node)) return node;
416
+ if (node.type !== "VariableDeclaration" || node.declarations.length !== 1) return node;
387
417
  const [decl] = node.declarations;
388
- if (decl.init?.type !== "ArrayExpression" || !decl.init.elements[0]) {
389
- s.removeNode(node);
390
- continue;
391
- }
392
- const [symbolIdNode, ...depsNodes] = decl.init.elements;
393
- if (symbolIdNode?.type !== "Literal" || typeof symbolIdNode.value !== "number") {
394
- s.removeNode(node);
395
- continue;
396
- }
418
+ if (decl.init?.type !== "ArrayExpression" || !decl.init.elements[0]) return null;
419
+ const [symbolIdNode, ...depsFns] = decl.init.elements;
420
+ if (symbolIdNode?.type !== "NumericLiteral") return null;
397
421
  const symbolId = symbolIdNode.value;
398
- const { code: code$1, binding, deps, needDeclare, preserveName } = retrieve(symbolId);
399
- const depsRaw = depsNodes.filter((node$1) => node$1?.type === "ArrowFunctionExpression").map((dep) => s.sliceNode(dep.body));
400
- const ss = new MagicStringAST(code$1);
401
- if (!preserveName) overwriteOrAppend(ss, binding, s.sliceNode(decl.id));
402
- for (const dep of deps) {
403
- const [start, end, suffix] = dep;
404
- overwriteOrAppend(ss, [start, end], depsRaw.shift(), suffix);
422
+ const original = getSymbol(symbolId);
423
+ const transformedBinding = {
424
+ ...decl.id,
425
+ typeAnnotation: original.binding.typeAnnotation
426
+ };
427
+ overwriteNode(original.binding, transformedBinding);
428
+ const transformedDeps = depsFns.filter((node$1) => node$1?.type === "ArrowFunctionExpression").map((node$1) => node$1.body);
429
+ if (original.deps.length) for (let i = 0; i < original.deps.length; i++) {
430
+ const originalDep = original.deps[i];
431
+ if (originalDep.replace) originalDep.replace(transformedDeps[i]);
432
+ else Object.assign(originalDep, transformedDeps[i]);
405
433
  }
406
- if (needDeclare) ss.prepend("declare ");
407
- s.overwriteNode(node, ss.toString());
434
+ return inheritNode(node, original.decl);
435
+ }).filter((node) => !!node);
436
+ if (program.body.length === 0) return "export { };";
437
+ const result = generate(file, {
438
+ comments: true,
439
+ sourceMaps: sourcemap,
440
+ sourceFileName: chunk.fileName
441
+ });
442
+ return result;
443
+ },
444
+ generateBundle(options, bundle) {
445
+ for (const chunk of Object.values(bundle)) {
446
+ if (chunk.type !== "asset" || !RE_DTS_MAP.test(chunk.fileName) || typeof chunk.source !== "string") continue;
447
+ const maps = JSON.parse(chunk.source);
448
+ maps.sourcesContent = null;
449
+ chunk.source = JSON.stringify(maps);
408
450
  }
409
- const str = s.toString();
410
- if (str.trim().length === 0) return "export { };";
411
- return str;
412
451
  }
413
452
  };
414
453
  }
415
454
  const REFERENCE_RE = /\/\s*<reference\s+(?:path|types)=/;
416
455
  function collectReferenceDirectives(comment) {
417
- return comment.filter((c) => REFERENCE_RE.test(c.value)).map((c) => `//${c.value}`);
456
+ return comment.filter((c) => REFERENCE_RE.test(c.value));
418
457
  }
419
- function collectDependencies(s, node, getIdentifierIndex) {
458
+ function collectDependencies(node, getIdentifierIndex, prepend) {
420
459
  const deps = new Set();
460
+ const seen = new Set();
421
461
  walk(node, { leave(node$1) {
422
462
  if (node$1.type === "ExportNamedDeclaration") {
423
- for (const specifier of node$1.specifiers) if (specifier.type === "ExportSpecifier") {
424
- let _suffix;
425
- if (specifier.local.start === specifier.exported.start && specifier.local.end === specifier.exported.end) _suffix = ` as ${s.sliceNode(specifier.local)}`;
426
- addDependency({
427
- ...specifier.local,
428
- _suffix
429
- });
430
- }
431
- } else if (node$1.type === "TSInterfaceDeclaration" && node$1.extends) for (const heritage of node$1.extends || []) addDependency(heritage.expression);
463
+ for (const specifier of node$1.specifiers) if (specifier.type === "ExportSpecifier") addDependency(specifier.local);
464
+ } else if (node$1.type === "TSInterfaceDeclaration" && node$1.extends) for (const heritage of node$1.extends || []) addDependency(TSEntityNameToRuntime(heritage.expression));
432
465
  else if (node$1.type === "ClassDeclaration") {
433
466
  if (node$1.superClass) addDependency(node$1.superClass);
434
- if (node$1.implements) for (const implement of node$1.implements) addDependency(implement.expression);
435
- } else if (node$1.type === "MethodDefinition" || node$1.type === "PropertyDefinition" || node$1.type === "TSPropertySignature") {
467
+ if (node$1.implements) for (const implement of node$1.implements) addDependency(TSEntityNameToRuntime(implement.expression));
468
+ } else if (isTypeOf(node$1, [
469
+ "ObjectMethod",
470
+ "ObjectProperty",
471
+ "ClassProperty",
472
+ "TSPropertySignature",
473
+ "TSDeclareMethod"
474
+ ])) {
436
475
  if (node$1.computed && isReferenceId(node$1.key)) addDependency(node$1.key);
437
476
  if ("value" in node$1 && isReferenceId(node$1.value)) addDependency(node$1.value);
438
- } else if (node$1.type === "TSTypeReference") addDependency(node$1.typeName);
477
+ } else if (node$1.type === "TSTypeReference") addDependency(TSEntityNameToRuntime(node$1.typeName));
439
478
  else if (node$1.type === "TSTypeQuery") {
440
- if (node$1.exprName.type !== "TSImportType") addDependency(node$1.exprName);
479
+ if (seen.has(node$1.exprName)) return;
480
+ if (node$1.exprName.type !== "TSImportType") addDependency(TSEntityNameToRuntime(node$1.exprName));
441
481
  } else if (node$1.type === "TSImportType") {
442
- if (node$1.argument.type !== "TSLiteralType" || node$1.argument.literal.type !== "Literal" || typeof node$1.argument.literal.value !== "string") return;
443
- const source = node$1.argument.literal.value;
444
- const imported = node$1.qualifier && resolveTSTypeName(node$1.qualifier);
445
- const local = importNamespace(s, source, imported && s.sliceNode(imported), getIdentifierIndex);
446
- addDependency({
447
- type: "Identifier",
448
- name: local,
449
- start: node$1.start,
450
- end: imported ? imported.end : node$1.end
451
- });
482
+ seen.add(node$1);
483
+ const source = node$1.argument;
484
+ const imported = node$1.qualifier;
485
+ const dep = importNamespace(node$1, imported, source, getIdentifierIndex, prepend);
486
+ addDependency(dep);
452
487
  }
453
488
  } });
454
489
  return Array.from(deps);
@@ -457,76 +492,151 @@ function collectDependencies(s, node, getIdentifierIndex) {
457
492
  deps.add(node$1);
458
493
  }
459
494
  }
460
- function resolveTSTypeName(node) {
495
+ function TSEntityNameToRuntime(node) {
461
496
  if (node.type === "Identifier") return node;
462
- return resolveTSTypeName(node.left);
497
+ const left = TSEntityNameToRuntime(node.left);
498
+ return Object.assign(node, t.memberExpression(left, node.right));
499
+ }
500
+ function getIdFromTSEntityName(node) {
501
+ if (node.type === "Identifier") return node;
502
+ return getIdFromTSEntityName(node.left);
463
503
  }
464
504
  function isReferenceId(node) {
465
505
  return !!node && (node.type === "Identifier" || node.type === "MemberExpression");
466
506
  }
467
- function stringifyDependencies(s, deps) {
468
- return deps.map((node) => `() => ${node.type === "Identifier" ? node.name : s.sliceNode(node)}`).join(", ");
469
- }
470
- function patchImportSource(s, node) {
471
- if ((node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration") && node.source?.value && RE_DTS.test(node.source.value)) {
472
- s.overwriteNode(node.source, JSON.stringify(filename_dts_to(node.source.value, "js")));
507
+ function patchImportSource(node) {
508
+ if (isTypeOf(node, [
509
+ "ImportDeclaration",
510
+ "ExportAllDeclaration",
511
+ "ExportNamedDeclaration"
512
+ ]) && node.source?.value && RE_DTS.test(node.source.value)) {
513
+ node.source.value = filename_dts_to(node.source.value, "js");
473
514
  return true;
474
515
  }
475
516
  }
476
- function patchTsNamespace(s, nodes) {
517
+ function patchTsNamespace(nodes) {
477
518
  const emptyObjectAssignments = new Map();
478
519
  const removed = new Set();
479
- for (const node of nodes) {
480
- if (node.type === "VariableDeclaration" && node.declarations.length === 1 && node.declarations[0].init?.type === "ObjectExpression" && node.declarations[0].init.properties.length === 0) emptyObjectAssignments.set(s.sliceNode(node.declarations[0].id), node);
520
+ for (const [i, node] of nodes.entries()) {
521
+ if (node.type === "VariableDeclaration" && node.declarations.length === 1 && node.declarations[0].id.type === "Identifier" && node.declarations[0].init?.type === "ObjectExpression" && node.declarations[0].init.properties.length === 0) emptyObjectAssignments.set(node.declarations[0].id.name, node);
481
522
  if (node.type !== "ExpressionStatement" || node.expression.type !== "CallExpression" || node.expression.callee.type !== "Identifier" || !node.expression.callee.name.startsWith("__export")) continue;
482
523
  const [binding, exports] = node.expression.arguments;
483
- const bindingText = s.sliceNode(binding);
524
+ if (binding.type !== "Identifier") continue;
525
+ const bindingText = binding.name;
484
526
  if (emptyObjectAssignments.has(bindingText)) {
485
527
  const emptyNode = emptyObjectAssignments.get(bindingText);
486
- s.removeNode(emptyNode);
487
528
  emptyObjectAssignments.delete(bindingText);
488
529
  removed.add(emptyNode);
489
530
  }
490
- let code = `declare namespace ${bindingText} {
491
- export { `;
492
- for (const properties of exports.properties) {
493
- if (properties.type !== "Property") continue;
494
- const exported = s.sliceNode(properties.key);
495
- const local = s.sliceNode(properties.value.body);
496
- const suffix = exported !== local ? ` as ${exported}` : "";
497
- code += `${local}${suffix}, `;
498
- }
499
- code += `}\n}`;
500
- s.overwriteNode(node, code);
531
+ nodes[i] = {
532
+ type: "TSModuleDeclaration",
533
+ id: binding,
534
+ kind: "namespace",
535
+ declare: true,
536
+ body: {
537
+ type: "TSModuleBlock",
538
+ body: [{
539
+ type: "ExportNamedDeclaration",
540
+ specifiers: exports.properties.filter((property) => property.type === "ObjectProperty").map((property) => {
541
+ const local = property.value.body;
542
+ const exported = property.key;
543
+ return t.exportSpecifier(local, exported);
544
+ }),
545
+ source: null,
546
+ declaration: null
547
+ }]
548
+ }
549
+ };
501
550
  }
502
- return removed;
551
+ return nodes.filter((node) => !removed.has(node));
503
552
  }
504
- function rewriteImportExport(s, node) {
553
+ function rewriteImportExport(node, set) {
505
554
  if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" && !node.declaration) {
506
- 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, ""));
507
- const firstSpecifier = node.specifiers[0];
508
- const kind = node.type === "ImportDeclaration" ? node.importKind : node.exportKind;
509
- if (kind === "type" && firstSpecifier) s.overwrite(node.start, firstSpecifier.start, s.slice(node.start, firstSpecifier.start).replace(RE_TYPE, ""));
555
+ for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier") specifier.importKind = "value";
556
+ else if (specifier.type === "ExportSpecifier") specifier.exportKind = "value";
557
+ if (node.type === "ImportDeclaration") node.importKind = "value";
558
+ else if (node.type === "ExportNamedDeclaration") node.exportKind = "value";
510
559
  return true;
511
560
  } else if (node.type === "ExportAllDeclaration") {
512
- if (node.exportKind === "type") s.overwrite(node.start, node.source.start, s.slice(node.start, node.source.start).replace(RE_TYPE, ""));
561
+ node.exportKind = "value";
513
562
  return true;
514
563
  } else if (node.type === "TSImportEqualsDeclaration") {
515
- if (node.moduleReference.type === "TSExternalModuleReference") s.overwriteNode(node, `import ${s.sliceNode(node.id)} from ${s.sliceNode(node.moduleReference.expression)}`);
564
+ if (node.moduleReference.type === "TSExternalModuleReference") set({
565
+ type: "ImportDeclaration",
566
+ specifiers: [{
567
+ type: "ImportDefaultSpecifier",
568
+ local: node.id
569
+ }],
570
+ source: node.moduleReference.expression
571
+ });
516
572
  return true;
517
- } else if (node.type === "TSExportAssignment") {
518
- s.overwriteNode(node, `export { ${s.sliceNode(node.expression)} as default }`);
573
+ } else if (node.type === "TSExportAssignment" && node.expression.type === "Identifier") {
574
+ set({
575
+ type: "ExportNamedDeclaration",
576
+ specifiers: [{
577
+ type: "ExportSpecifier",
578
+ local: node.expression,
579
+ exported: {
580
+ type: "Identifier",
581
+ name: "default"
582
+ }
583
+ }]
584
+ });
519
585
  return true;
520
586
  } else if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "Identifier") {
521
- s.overwriteNode(node, `export { ${s.sliceNode(node.declaration)} as default }`);
587
+ set({
588
+ type: "ExportNamedDeclaration",
589
+ specifiers: [{
590
+ type: "ExportSpecifier",
591
+ local: node.declaration,
592
+ exported: t.identifier("default")
593
+ }]
594
+ });
522
595
  return true;
523
596
  }
597
+ return false;
598
+ }
599
+ function importNamespace(node, imported, source, getIdentifierIndex, prepend) {
600
+ let local = t.identifier(`_${getIdentifierIndex()}`);
601
+ prepend(t.importDeclaration([t.importNamespaceSpecifier(local)], source));
602
+ if (imported) {
603
+ const importedLeft = getIdFromTSEntityName(imported);
604
+ overwriteNode(importedLeft, t.tsQualifiedName(local, { ...importedLeft }));
605
+ local = imported;
606
+ }
607
+ let replacement = node;
608
+ if (node.typeParameters) {
609
+ overwriteNode(node, t.tsTypeReference(local, node.typeParameters));
610
+ replacement = local;
611
+ } else overwriteNode(node, local);
612
+ const dep = {
613
+ ...TSEntityNameToRuntime(local),
614
+ replace(newNode) {
615
+ overwriteNode(replacement, newNode);
616
+ }
617
+ };
618
+ return dep;
524
619
  }
525
- function importNamespace(s, source, imported, getIdentifierIndex) {
526
- const local = `_${getIdentifierIndex()}`;
527
- const specifiers = imported ? `{ ${imported} as ${local} }` : `* as ${local}`;
528
- s.prepend(`import ${specifiers} from ${JSON.stringify(source)};\n`);
529
- return local;
620
+ function inheritNode(oldValue, newValue) {
621
+ return {
622
+ ...newValue,
623
+ leadingComments: oldValue.leadingComments,
624
+ innerComments: oldValue.innerComments,
625
+ trailingComments: oldValue.trailingComments
626
+ };
627
+ }
628
+ function overwriteNode(node, newNode) {
629
+ const preserve = [
630
+ "leadingComments",
631
+ "innerComments",
632
+ "trailingComments"
633
+ ];
634
+ for (const key of Object.keys(node)) {
635
+ if (preserve.includes(key)) continue;
636
+ delete node[key];
637
+ }
638
+ Object.assign(node, newNode, { ...node });
639
+ return node;
530
640
  }
531
641
 
532
642
  //#endregion
@@ -607,11 +717,17 @@ function createTsProgram(compilerOptions, dtsMap, id) {
607
717
  function tscEmit(module) {
608
718
  const { program, file } = module;
609
719
  let dtsCode;
720
+ let map;
610
721
  const { emitSkipped, diagnostics } = program.emit(
611
722
  file,
612
- (_, code) => {
613
- debug(`emit dts: ${file.fileName}`);
614
- dtsCode = code;
723
+ (fileName, code) => {
724
+ if (fileName.endsWith(".map")) {
725
+ debug(`emit dts sourcemap: ${fileName}`);
726
+ map = JSON.parse(code);
727
+ } else {
728
+ debug(`emit dts: ${fileName}`);
729
+ dtsCode = code;
730
+ }
615
731
  },
616
732
  void 0,
617
733
  true,
@@ -620,7 +736,10 @@ function tscEmit(module) {
620
736
  true
621
737
  );
622
738
  if (emitSkipped && diagnostics.length) return { error: ts.formatDiagnostics(diagnostics, formatHost) };
623
- return { code: dtsCode };
739
+ return {
740
+ code: dtsCode,
741
+ map
742
+ };
624
743
  }
625
744
  function getTsModule(dtsMap, tsId) {
626
745
  const module = Array.from(dtsMap.values()).find((dts$1) => dts$1.id === tsId);
@@ -631,28 +750,8 @@ function getTsModule(dtsMap, tsId) {
631
750
  //#endregion
632
751
  //#region src/generate.ts
633
752
  const meta = { dtsFile: true };
634
- function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations, resolve = false, emitDtsOnly = false }) {
753
+ function createGeneratePlugin({ tsconfig, compilerOptions = {}, isolatedDeclarations, resolve = false, emitDtsOnly = false }) {
635
754
  const dtsMap = new Map();
636
- function resolveOptions(cwd) {
637
- if (tsconfig === true || tsconfig == null) {
638
- const { config, path: path$1 } = getTsconfig(cwd) || {};
639
- tsconfig = path$1;
640
- compilerOptions = {
641
- ...config?.compilerOptions,
642
- ...compilerOptions
643
- };
644
- } else if (typeof tsconfig === "string") {
645
- tsconfig = path.resolve(cwd || process.cwd(), tsconfig);
646
- const config = parseTsconfig(tsconfig);
647
- compilerOptions = {
648
- ...config.compilerOptions,
649
- ...compilerOptions
650
- };
651
- }
652
- if (isolatedDeclarations == null) isolatedDeclarations = !!compilerOptions?.isolatedDeclarations;
653
- if (isolatedDeclarations === true) isolatedDeclarations = {};
654
- if (isolatedDeclarations && isolatedDeclarations.stripInternal == null) isolatedDeclarations.stripInternal = !!compilerOptions?.stripInternal;
655
- }
656
755
  /**
657
756
  * A map of input id to output file name
658
757
  *
@@ -663,14 +762,12 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
663
762
  * ])
664
763
  */
665
764
  const inputAliasMap = new Map();
666
- let resolver;
667
765
  let programs = [];
766
+ const resolver = createResolver({ tsconfig: tsconfig ? tsconfig : void 0 });
767
+ if (!isolatedDeclarations) initTs();
668
768
  return {
669
769
  name: "rolldown-plugin-dts:generate",
670
770
  async buildStart(options) {
671
- resolveOptions(options.cwd);
672
- resolver = createResolver({ tsconfig: tsconfig ? tsconfig : void 0 });
673
- if (!isolatedDeclarations) initTs();
674
771
  if (!Array.isArray(options.input)) for (const [name, id] of Object.entries(options.input)) {
675
772
  let resolved = await this.resolve(id, void 0, { skipSelf: true });
676
773
  resolved ||= await this.resolve(`./${id}`, void 0, { skipSelf: true });
@@ -784,8 +881,9 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
784
881
  if (!dtsMap.has(dtsId)) return;
785
882
  const { code, id, isEntry } = dtsMap.get(dtsId);
786
883
  let dtsCode;
884
+ let map;
787
885
  if (isolatedDeclarations) {
788
- const result = isolatedDeclaration(id, code, isolatedDeclarations === true ? {} : isolatedDeclarations);
886
+ const result = isolatedDeclaration(id, code, isolatedDeclarations);
789
887
  if (result.errors.length) {
790
888
  const [error] = result.errors;
791
889
  return this.error({
@@ -794,21 +892,24 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
794
892
  });
795
893
  }
796
894
  dtsCode = result.code;
895
+ if (result.map) map = result.map;
797
896
  } else {
798
897
  const module = createOrGetTsModule(programs, compilerOptions, id, isEntry, dtsMap);
799
898
  const result = tscEmit(module);
800
899
  if (result.error) return this.error(result.error);
801
900
  dtsCode = result.code;
901
+ map = result.map;
802
902
  }
803
903
  if (!dtsCode) return this.error(new Error(`Failed to generate dts for ${id}`));
804
904
  return {
805
905
  code: dtsCode,
806
- moduleSideEffects: false
906
+ moduleSideEffects: false,
907
+ map
807
908
  };
808
909
  }
809
910
  },
810
911
  generateBundle: emitDtsOnly ? (options, bundle) => {
811
- for (const fileName of Object.keys(bundle)) if (!RE_DTS.test(fileName)) delete bundle[fileName];
912
+ for (const fileName of Object.keys(bundle)) if (bundle[fileName].type === "chunk" && !RE_DTS.test(fileName) && !RE_DTS_MAP.test(fileName)) delete bundle[fileName];
812
913
  } : void 0,
813
914
  buildEnd() {
814
915
  programs = [];
@@ -819,12 +920,48 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
819
920
  //#endregion
820
921
  //#region src/index.ts
821
922
  function dts(options = {}) {
923
+ const resolved = resolveOptions(options);
822
924
  const plugins = [];
823
925
  if (options.dtsInput) plugins.push(createDtsInputPlugin());
824
- else plugins.push(createGeneratePlugin(options));
825
- plugins.push(createFakeJsPlugin(options));
926
+ else plugins.push(createGeneratePlugin(resolved));
927
+ plugins.push(createFakeJsPlugin(resolved));
826
928
  return plugins;
827
929
  }
930
+ function resolveOptions({ cwd = process.cwd(), tsconfig, compilerOptions = {}, isolatedDeclarations, sourcemap, dtsInput = false, emitDtsOnly = false, resolve = false }) {
931
+ if (tsconfig === true || tsconfig == null) {
932
+ const { config, path: path$1 } = getTsconfig(cwd) || {};
933
+ tsconfig = path$1;
934
+ compilerOptions = {
935
+ ...config?.compilerOptions,
936
+ ...compilerOptions
937
+ };
938
+ } else if (typeof tsconfig === "string") {
939
+ tsconfig = path.resolve(cwd || process.cwd(), tsconfig);
940
+ const config = parseTsconfig(tsconfig);
941
+ compilerOptions = {
942
+ ...config.compilerOptions,
943
+ ...compilerOptions
944
+ };
945
+ } else tsconfig = void 0;
946
+ sourcemap ??= !!compilerOptions.declarationMap;
947
+ compilerOptions.declarationMap = sourcemap;
948
+ if (isolatedDeclarations == null) isolatedDeclarations = !!compilerOptions?.isolatedDeclarations;
949
+ if (isolatedDeclarations === true) isolatedDeclarations = {};
950
+ if (isolatedDeclarations) {
951
+ isolatedDeclarations.stripInternal ??= !!compilerOptions?.stripInternal;
952
+ isolatedDeclarations.sourcemap = !!compilerOptions.declarationMap;
953
+ }
954
+ return {
955
+ cwd,
956
+ tsconfig,
957
+ compilerOptions,
958
+ isolatedDeclarations,
959
+ sourcemap,
960
+ dtsInput,
961
+ emitDtsOnly,
962
+ resolve
963
+ };
964
+ }
828
965
 
829
966
  //#endregion
830
- export { createFakeJsPlugin, createGeneratePlugin, dts };
967
+ export { createFakeJsPlugin, createGeneratePlugin, dts, resolveOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rolldown-plugin-dts",
3
- "version": "0.8.6",
3
+ "version": "0.9.1",
4
4
  "description": "A Rolldown plugin to bundle dts files",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,20 +37,23 @@
37
37
  }
38
38
  },
39
39
  "dependencies": {
40
+ "@babel/generator": "^7.27.0",
41
+ "@babel/parser": "^7.27.0",
42
+ "@babel/types": "^7.27.0",
43
+ "ast-kit": "^1.4.3",
40
44
  "debug": "^4.4.0",
41
45
  "dts-resolver": "^1.0.1",
42
46
  "get-tsconfig": "^4.10.0",
43
- "magic-string-ast": "^0.9.1",
44
- "oxc-parser": "^0.66.0",
45
47
  "oxc-transform": "^0.66.0"
46
48
  },
47
49
  "devDependencies": {
48
50
  "@sxzz/eslint-config": "^6.1.2",
49
51
  "@sxzz/prettier-config": "^2.2.1",
50
52
  "@sxzz/test-utils": "^0.5.5",
53
+ "@types/babel__generator": "^7.27.0",
51
54
  "@types/debug": "^4.1.12",
52
55
  "@types/diff": "^7.0.2",
53
- "@types/node": "^22.14.1",
56
+ "@types/node": "^22.15.2",
54
57
  "bumpp": "^10.1.0",
55
58
  "diff": "^7.0.0",
56
59
  "eslint": "^9.25.1",
@@ -58,7 +61,8 @@
58
61
  "prettier": "^3.5.3",
59
62
  "rolldown": "1.0.0-beta.8-commit.6aca0ce",
60
63
  "rollup-plugin-dts": "^6.2.1",
61
- "tsdown": "^0.9.6",
64
+ "tinyglobby": "^0.2.13",
65
+ "tsdown": "^0.9.8",
62
66
  "tsx": "^4.19.3",
63
67
  "typescript": "^5.8.3",
64
68
  "vitest": "^3.1.2"