rolldown-plugin-dts 0.8.6 → 0.9.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +68 -39
  2. package/dist/index.js +329 -202
  3. package/package.json +9 -5
package/dist/index.d.ts CHANGED
@@ -3,53 +3,82 @@ 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
+ cwd?: string;
25
+ /**
26
+ * When entries are `.d.ts` files (instead of `.ts` files), this option should be set to `true`.
27
+ *
28
+ * If enabled, the plugin will skip generating a `.d.ts` file for the entry point.
29
+ */
30
+ dtsInput?: boolean;
31
+ /**
32
+ * When `true`, the plugin will only emit `.d.ts` files and remove all other chunks.
33
+ *
34
+ * This feature is particularly beneficial when you need to generate `d.ts` files for the CommonJS format as part of a separate build process.
35
+ */
36
+ emitDtsOnly?: boolean;
37
+ /**
38
+ * The path to the `tsconfig.json` file.
39
+ *
40
+ * When set to `false`, the plugin will ignore any `tsconfig.json` file.
41
+ * However, `compilerOptions` can still be specified directly in the options.
42
+ *
43
+ * @default `tsconfig.json`
44
+ */
45
+ tsconfig?: string | boolean;
46
+ /**
47
+ * The `compilerOptions` for the TypeScript compiler.
48
+ *
49
+ * @see https://www.typescriptlang.org/docs/handbook/compiler-options.html
50
+ */
51
+ compilerOptions?: TsConfigJson.CompilerOptions;
52
+ /**
53
+ * When `true`, the plugin will generate `.d.ts` files using `oxc-transform`,
54
+ * which is blazingly faster than `typescript` compiler.
55
+ *
56
+ * This option is enabled when `isolatedDeclarations` in `compilerOptions` is set to `true`.
57
+ */
58
+ isolatedDeclarations?: boolean | Omit<IsolatedDeclarationsOptions, "sourcemap">;
59
+ /**
60
+ * When `true`, the plugin will generate declaration maps for `.d.ts` files.
61
+ */
62
+ sourcemap?: boolean;
63
+ /** Resolve external types used in dts files from `node_modules` */
64
+ resolve?: boolean | (string | RegExp)[];
51
65
  }
66
+ type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
67
+ type OptionsResolved = Overwrite<Required<Options>, {
68
+ tsconfig: string | undefined;
69
+ isolatedDeclarations: IsolatedDeclarationsOptions | false;
70
+ }>;
52
71
  declare function dts(options?: Options): Plugin[];
72
+ declare function resolveOptions({
73
+ cwd,
74
+ tsconfig,
75
+ compilerOptions,
76
+ isolatedDeclarations,
77
+ sourcemap,
78
+ dtsInput,
79
+ emitDtsOnly,
80
+ resolve
81
+ }: Options): OptionsResolved;
53
82
 
54
83
  //#endregion
55
- export { Options, createFakeJsPlugin, createGeneratePlugin, dts };
84
+ 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) {
496
+ if (node.type === "Identifier") return node;
497
+ const left = TSEntityNameToRuntime(node.left);
498
+ return Object.assign(node, t.memberExpression(left, node.right));
499
+ }
500
+ function getIdFromTSEntityName(node) {
461
501
  if (node.type === "Identifier") return node;
462
- return resolveTSTypeName(node.left);
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;
524
598
  }
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;
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;
619
+ }
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
@@ -631,28 +741,8 @@ function getTsModule(dtsMap, tsId) {
631
741
  //#endregion
632
742
  //#region src/generate.ts
633
743
  const meta = { dtsFile: true };
634
- function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations, resolve = false, emitDtsOnly = false }) {
744
+ function createGeneratePlugin({ tsconfig, compilerOptions = {}, isolatedDeclarations, resolve = false, emitDtsOnly = false }) {
635
745
  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
746
  /**
657
747
  * A map of input id to output file name
658
748
  *
@@ -663,14 +753,12 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
663
753
  * ])
664
754
  */
665
755
  const inputAliasMap = new Map();
666
- let resolver;
667
756
  let programs = [];
757
+ const resolver = createResolver({ tsconfig: tsconfig ? tsconfig : void 0 });
758
+ if (!isolatedDeclarations) initTs();
668
759
  return {
669
760
  name: "rolldown-plugin-dts:generate",
670
761
  async buildStart(options) {
671
- resolveOptions(options.cwd);
672
- resolver = createResolver({ tsconfig: tsconfig ? tsconfig : void 0 });
673
- if (!isolatedDeclarations) initTs();
674
762
  if (!Array.isArray(options.input)) for (const [name, id] of Object.entries(options.input)) {
675
763
  let resolved = await this.resolve(id, void 0, { skipSelf: true });
676
764
  resolved ||= await this.resolve(`./${id}`, void 0, { skipSelf: true });
@@ -784,8 +872,9 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
784
872
  if (!dtsMap.has(dtsId)) return;
785
873
  const { code, id, isEntry } = dtsMap.get(dtsId);
786
874
  let dtsCode;
875
+ let map;
787
876
  if (isolatedDeclarations) {
788
- const result = isolatedDeclaration(id, code, isolatedDeclarations === true ? {} : isolatedDeclarations);
877
+ const result = isolatedDeclaration(id, code, isolatedDeclarations);
789
878
  if (result.errors.length) {
790
879
  const [error] = result.errors;
791
880
  return this.error({
@@ -794,6 +883,7 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
794
883
  });
795
884
  }
796
885
  dtsCode = result.code;
886
+ if (result.map) map = result.map;
797
887
  } else {
798
888
  const module = createOrGetTsModule(programs, compilerOptions, id, isEntry, dtsMap);
799
889
  const result = tscEmit(module);
@@ -803,12 +893,13 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
803
893
  if (!dtsCode) return this.error(new Error(`Failed to generate dts for ${id}`));
804
894
  return {
805
895
  code: dtsCode,
806
- moduleSideEffects: false
896
+ moduleSideEffects: false,
897
+ map
807
898
  };
808
899
  }
809
900
  },
810
901
  generateBundle: emitDtsOnly ? (options, bundle) => {
811
- for (const fileName of Object.keys(bundle)) if (!RE_DTS.test(fileName)) delete bundle[fileName];
902
+ 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
903
  } : void 0,
813
904
  buildEnd() {
814
905
  programs = [];
@@ -819,12 +910,48 @@ function createGeneratePlugin({ tsconfig, compilerOptions, isolatedDeclarations,
819
910
  //#endregion
820
911
  //#region src/index.ts
821
912
  function dts(options = {}) {
913
+ const resolved = resolveOptions(options);
822
914
  const plugins = [];
823
915
  if (options.dtsInput) plugins.push(createDtsInputPlugin());
824
- else plugins.push(createGeneratePlugin(options));
825
- plugins.push(createFakeJsPlugin(options));
916
+ else plugins.push(createGeneratePlugin(resolved));
917
+ plugins.push(createFakeJsPlugin(resolved));
826
918
  return plugins;
827
919
  }
920
+ function resolveOptions({ cwd = process.cwd(), tsconfig, compilerOptions = {}, isolatedDeclarations, sourcemap, dtsInput = false, emitDtsOnly = false, resolve = false }) {
921
+ if (tsconfig === true || tsconfig == null) {
922
+ const { config, path: path$1 } = getTsconfig(cwd) || {};
923
+ tsconfig = path$1;
924
+ compilerOptions = {
925
+ ...config?.compilerOptions,
926
+ ...compilerOptions
927
+ };
928
+ } else if (typeof tsconfig === "string") {
929
+ tsconfig = path.resolve(cwd || process.cwd(), tsconfig);
930
+ const config = parseTsconfig(tsconfig);
931
+ compilerOptions = {
932
+ ...config.compilerOptions,
933
+ ...compilerOptions
934
+ };
935
+ } else tsconfig = void 0;
936
+ sourcemap ??= !!compilerOptions.declarationMap;
937
+ compilerOptions.declarationMap = sourcemap;
938
+ if (isolatedDeclarations == null) isolatedDeclarations = !!compilerOptions?.isolatedDeclarations;
939
+ if (isolatedDeclarations === true) isolatedDeclarations = {};
940
+ if (isolatedDeclarations) {
941
+ isolatedDeclarations.stripInternal ??= !!compilerOptions?.stripInternal;
942
+ isolatedDeclarations.sourcemap = !!compilerOptions.declarationMap;
943
+ }
944
+ return {
945
+ cwd,
946
+ tsconfig,
947
+ compilerOptions,
948
+ isolatedDeclarations,
949
+ sourcemap,
950
+ dtsInput,
951
+ emitDtsOnly,
952
+ resolve
953
+ };
954
+ }
828
955
 
829
956
  //#endregion
830
- export { createFakeJsPlugin, createGeneratePlugin, dts };
957
+ 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.0",
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"