rolldown-plugin-dts 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -8
  2. package/dist/index.js +73 -27
  3. package/package.json +3 -1
package/README.md CHANGED
@@ -74,17 +74,14 @@ interface Options {
74
74
  }
75
75
  ````
76
76
 
77
- ## ⚠️ Caveats
77
+ ## Differences from `rollup-plugin-dts`
78
78
 
79
- - The plugin leverages Oxc's `isolatedDeclarations` to generate `.d.ts` files when `isolatedDeclaration` is enabled,
80
- offering significantly faster performance compared to the `typescript` compiler.
79
+ ### Isolated Declarations
81
80
 
82
- - Namespaces are not supported yet.
83
- - `export * as ns from './ns'`
84
- - `import * as ns from './ns'` and then `export { ns }`
85
- - `type ns = import('./ns')`
81
+ The plugin leverages Oxc's `isolatedDeclarations` to generate `.d.ts` files when `isolatedDeclaration` is enabled,
82
+ offering significantly faster performance compared to the `typescript` compiler.
86
83
 
87
- ## Differences from `rollup-plugin-dts`
84
+ ### Single Build for ESM
88
85
 
89
86
  `rolldown-plugin-dts` generates separate chunks for `.d.ts` files, enabling both source code (`.js`)
90
87
  and type definition files (`.d.ts`) to be produced in a single build process.
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { createResolver } from "dts-resolver";
5
5
  import { getTsconfig } from "get-tsconfig";
6
6
  import { isolatedDeclaration } from "oxc-transform";
7
7
  import { createRequire } from "node:module";
8
+ import Debug from "debug";
8
9
 
9
10
  //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js
10
11
  var WalkerBase = class {
@@ -256,7 +257,6 @@ function createFakeJsPlugin({ dtsInput }) {
256
257
  preserveMap.set(id, preserved);
257
258
  const s = new MagicStringAST(code);
258
259
  for (let node of program.body) {
259
- if (node.type === "ExportAllDeclaration" && node.exported && isRelative(node.source.value)) throw new Error("`export * as foo from './...'` is not supported");
260
260
  if (rewriteImportExport(s, node)) continue;
261
261
  const sideEffect = node.type === "TSModuleDeclaration" && node.kind !== "namespace";
262
262
  const stmt = node;
@@ -315,17 +315,19 @@ function createFakeJsPlugin({ dtsInput }) {
315
315
  }
316
316
  }
317
317
  if (comments.size) s.prepend(`${[...comments].join("\n")}\n`);
318
+ const removedNodes = patchTsNamespace(s, program.body);
318
319
  for (const node of program.body) {
320
+ if (removedNodes.has(node)) continue;
319
321
  if (patchImportSource(s, node)) continue;
320
322
  if (node.type !== "VariableDeclaration" || node.declarations.length !== 1) continue;
321
323
  const [decl] = node.declarations;
322
324
  if (decl.init?.type !== "ArrayExpression" || !decl.init.elements[0]) {
323
- patchVariableDeclarator(s, node, decl);
325
+ s.removeNode(node);
324
326
  continue;
325
327
  }
326
328
  const [symbolIdNode, ...depsNodes] = decl.init.elements;
327
329
  if (symbolIdNode?.type !== "Literal" || typeof symbolIdNode.value !== "number") {
328
- patchVariableDeclarator(s, node, decl);
330
+ s.removeNode(node);
329
331
  continue;
330
332
  }
331
333
  const symbolId = symbolIdNode.value;
@@ -342,7 +344,7 @@ function createFakeJsPlugin({ dtsInput }) {
342
344
  s.overwriteNode(node, ss.toString());
343
345
  }
344
346
  const str = s.toString();
345
- if (str.trim().length === 0) return "export {}";
347
+ if (str.trim().length === 0) return "export { };";
346
348
  return str;
347
349
  }
348
350
  };
@@ -374,15 +376,14 @@ function collectDependencies(s, node, getIdentifierIndex) {
374
376
  else if (node$1.type === "TSTypeQuery") addDependency(node$1.exprName);
375
377
  else if (node$1.type === "TSImportType") {
376
378
  if (node$1.argument.type !== "TSLiteralType" || node$1.argument.literal.type !== "Literal" || typeof node$1.argument.literal.value !== "string") return;
377
- if (!node$1.qualifier) throw new Error("Import namespace is not supported");
378
379
  const source = node$1.argument.literal.value;
379
- const imported = s.sliceNode(node$1.qualifier);
380
+ const imported = node$1.qualifier && s.sliceNode(node$1.qualifier);
380
381
  const local = importNamespace(s, source, imported, getIdentifierIndex);
381
382
  addDependency({
382
383
  type: "Identifier",
383
384
  name: local,
384
385
  start: node$1.start + (node$1.isTypeOf ? 7 : 0),
385
- end: node$1.qualifier.end
386
+ end: node$1.qualifier ? node$1.qualifier.end : node$1.end
386
387
  });
387
388
  }
388
389
  } });
@@ -398,16 +399,40 @@ function isReferenceId(node) {
398
399
  function stringifyDependencies(s, deps) {
399
400
  return deps.map((node) => `() => ${node.type === "Identifier" ? node.name : s.sliceNode(node)}`).join(", ");
400
401
  }
401
- function patchVariableDeclarator(s, node, decl) {
402
- if (decl.init && !decl.id.typeAnnotation) s.overwriteNode(node, `type ${s.sliceNode(decl.id)} = ${s.sliceNode(decl.init)}`);
403
- else if (!node.declare) s.prependLeft(node.start, "declare ");
404
- }
405
402
  function patchImportSource(s, node) {
406
403
  if ((node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration") && node.source?.value && RE_DTS.test(node.source.value)) {
407
404
  s.overwriteNode(node.source, JSON.stringify(filename_dts_to(node.source.value, "js")));
408
405
  return true;
409
406
  }
410
407
  }
408
+ function patchTsNamespace(s, nodes) {
409
+ const emptyObjectAssignments = new Map();
410
+ const removed = new Set();
411
+ for (const node of nodes) {
412
+ 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);
413
+ if (node.type !== "ExpressionStatement" || node.expression.type !== "CallExpression" || node.expression.callee.type !== "Identifier" || !node.expression.callee.name.startsWith("__export")) continue;
414
+ const [binding, exports] = node.expression.arguments;
415
+ const bindingText = s.sliceNode(binding);
416
+ if (emptyObjectAssignments.has(bindingText)) {
417
+ const emptyNode = emptyObjectAssignments.get(bindingText);
418
+ s.removeNode(emptyNode);
419
+ emptyObjectAssignments.delete(bindingText);
420
+ removed.add(emptyNode);
421
+ }
422
+ let code = `declare namespace ${bindingText} {
423
+ export { `;
424
+ for (const properties of exports.properties) {
425
+ if (properties.type !== "Property") continue;
426
+ const exported = s.sliceNode(properties.key);
427
+ const local = s.sliceNode(properties.value.body);
428
+ const suffix = exported !== local ? ` as ${exported}` : "";
429
+ code += `${local}${suffix}, `;
430
+ }
431
+ code += `}\n}`;
432
+ s.overwriteNode(node, code);
433
+ }
434
+ return removed;
435
+ }
411
436
  function rewriteImportExport(s, node) {
412
437
  if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" && !node.declaration) {
413
438
  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, ""));
@@ -422,7 +447,7 @@ function rewriteImportExport(s, node) {
422
447
  if (node.moduleReference.type === "TSExternalModuleReference") s.overwriteNode(node, `import ${s.sliceNode(node.id)} from ${s.sliceNode(node.moduleReference.expression)}`);
423
448
  return true;
424
449
  } else if (node.type === "TSExportAssignment") {
425
- s.overwriteNode(node, `export default ${s.sliceNode(node.expression)}`);
450
+ s.overwriteNode(node, `export { ${s.sliceNode(node.expression)} as default }`);
426
451
  return true;
427
452
  } else if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "Identifier") {
428
453
  s.overwriteNode(node, `export { ${s.sliceNode(node.declaration)} as default }`);
@@ -431,15 +456,18 @@ function rewriteImportExport(s, node) {
431
456
  }
432
457
  function importNamespace(s, source, imported, getIdentifierIndex) {
433
458
  const local = `_${getIdentifierIndex()}`;
434
- s.prepend(`import { ${imported} as ${local} } from ${JSON.stringify(source)};\n`);
459
+ const specifiers = imported ? `{ ${imported} as ${local} }` : `* as ${local}`;
460
+ s.prepend(`import ${specifiers} from ${JSON.stringify(source)};\n`);
435
461
  return local;
436
462
  }
437
463
 
438
464
  //#endregion
439
465
  //#region src/utils/tsc.ts
466
+ const debug = Debug("rolldown-plugin-dts:tsc");
440
467
  let ts;
441
468
  let formatHost;
442
469
  function initTs() {
470
+ debug("loading typescript");
443
471
  const require = createRequire(import.meta.url);
444
472
  ts = require("typescript");
445
473
  formatHost = {
@@ -447,6 +475,7 @@ function initTs() {
447
475
  getNewLine: () => ts.sys.newLine,
448
476
  getCanonicalFileName: ts.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase()
449
477
  };
478
+ debug(`loaded typescript: ${ts.version}`);
450
479
  }
451
480
  const defaultCompilerOptions = {
452
481
  declaration: true,
@@ -472,7 +501,9 @@ function createOrGetTsModule(programs, compilerOptions, id, code, isEntry) {
472
501
  file: sourceFile
473
502
  };
474
503
  }
504
+ debug(`create program for module: ${id}`);
475
505
  const module = createTsProgram(compilerOptions, id, code);
506
+ debug(`created program for module: ${id}`);
476
507
  programs.push(module.program);
477
508
  return module;
478
509
  }
@@ -511,6 +542,24 @@ function createTsProgram(compilerOptions, id, code) {
511
542
  file: sourceFile
512
543
  };
513
544
  }
545
+ function tscEmit(module) {
546
+ const { program: { program }, file } = module;
547
+ let dtsCode;
548
+ const { emitSkipped, diagnostics } = program.emit(
549
+ file,
550
+ (_, code) => {
551
+ debug(`emit dts: ${file.fileName}`);
552
+ dtsCode = code;
553
+ },
554
+ void 0,
555
+ true,
556
+ void 0,
557
+ // @ts-expect-error private API: forceDtsEmit
558
+ true
559
+ );
560
+ if (emitSkipped && diagnostics.length) return { error: ts.formatDiagnostics(diagnostics, formatHost) };
561
+ return { code: dtsCode };
562
+ }
514
563
  const tsconfigCache = new Map();
515
564
  function loadTsconfig(id) {
516
565
  const configPath = ts.findConfigFile(path.dirname(id), ts.sys.fileExists);
@@ -567,22 +616,19 @@ function createGeneratePlugin({ compilerOptions, isolatedDeclaration: isolatedDe
567
616
  const isEntry = mod?.isEntry;
568
617
  if (isolatedDeclaration$1) {
569
618
  const result = isolatedDeclaration(id, code, isolatedDeclaration$1 === true ? {} : isolatedDeclaration$1);
570
- if (result.errors.length) return this.error(result.errors[0]);
619
+ if (result.errors.length) {
620
+ const [error] = result.errors;
621
+ return this.error({
622
+ message: error.message,
623
+ frame: error.codeframe
624
+ });
625
+ }
571
626
  dtsCode = result.code;
572
627
  } else {
573
- const { program: { program }, file } = createOrGetTsModule(programs, compilerOptions, id, code, isEntry);
574
- const { emitSkipped, diagnostics } = program.emit(
575
- file,
576
- (_, code$1) => {
577
- dtsCode = code$1;
578
- },
579
- void 0,
580
- true,
581
- void 0,
582
- // @ts-expect-error private API: forceDtsEmit
583
- true
584
- );
585
- if (emitSkipped && diagnostics.length) return this.error(ts.formatDiagnostics(diagnostics, formatHost));
628
+ const module = createOrGetTsModule(programs, compilerOptions, id, code, isEntry);
629
+ const result = tscEmit(module);
630
+ if (result.error) return this.error(result.error);
631
+ dtsCode = result.code;
586
632
  }
587
633
  if (!dtsCode) return this.error(new Error(`Failed to generate dts for ${id}`));
588
634
  const dtsId = filename_ts_to_dts(id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rolldown-plugin-dts",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "A Rolldown plugin to bundle dts files",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,6 +37,7 @@
37
37
  }
38
38
  },
39
39
  "dependencies": {
40
+ "debug": "^4.4.0",
40
41
  "dts-resolver": "^0.1.0",
41
42
  "get-tsconfig": "^4.10.0",
42
43
  "magic-string-ast": "^0.9.1",
@@ -47,6 +48,7 @@
47
48
  "@sxzz/eslint-config": "^6.1.1",
48
49
  "@sxzz/prettier-config": "^2.2.1",
49
50
  "@sxzz/test-utils": "^0.5.4",
51
+ "@types/debug": "^4.1.12",
50
52
  "@types/diff": "^7.0.2",
51
53
  "@types/node": "^22.14.0",
52
54
  "bumpp": "^10.1.0",