rolldown-plugin-dts 0.5.1 → 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.
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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IsolatedDeclarationsOptions } from "oxc-transform";
2
- import { CompilerOptions } from "typescript";
3
2
  import { Plugin } from "rolldown";
3
+ import { CompilerOptions } from "typescript";
4
4
 
5
5
  //#region src/fake-js.d.ts
6
6
  declare function createFakeJsPlugin({ dtsInput }: Pick<Options, "dtsInput">): Plugin;
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@ import path, { basename, extname } from "node:path";
4
4
  import { createResolver } from "dts-resolver";
5
5
  import { getTsconfig } from "get-tsconfig";
6
6
  import { isolatedDeclaration } from "oxc-transform";
7
- import * as ts from "typescript";
8
7
  import { createRequire } from "node:module";
8
+ import Debug from "debug";
9
9
 
10
10
  //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js
11
11
  var WalkerBase = class {
@@ -257,7 +257,6 @@ function createFakeJsPlugin({ dtsInput }) {
257
257
  preserveMap.set(id, preserved);
258
258
  const s = new MagicStringAST(code);
259
259
  for (let node of program.body) {
260
- if (node.type === "ExportAllDeclaration" && node.exported && isRelative(node.source.value)) throw new Error("`export * as foo from './...'` is not supported");
261
260
  if (rewriteImportExport(s, node)) continue;
262
261
  const sideEffect = node.type === "TSModuleDeclaration" && node.kind !== "namespace";
263
262
  const stmt = node;
@@ -316,17 +315,19 @@ function createFakeJsPlugin({ dtsInput }) {
316
315
  }
317
316
  }
318
317
  if (comments.size) s.prepend(`${[...comments].join("\n")}\n`);
318
+ const removedNodes = patchTsNamespace(s, program.body);
319
319
  for (const node of program.body) {
320
+ if (removedNodes.has(node)) continue;
320
321
  if (patchImportSource(s, node)) continue;
321
322
  if (node.type !== "VariableDeclaration" || node.declarations.length !== 1) continue;
322
323
  const [decl] = node.declarations;
323
324
  if (decl.init?.type !== "ArrayExpression" || !decl.init.elements[0]) {
324
- patchVariableDeclarator(s, node, decl);
325
+ s.removeNode(node);
325
326
  continue;
326
327
  }
327
328
  const [symbolIdNode, ...depsNodes] = decl.init.elements;
328
329
  if (symbolIdNode?.type !== "Literal" || typeof symbolIdNode.value !== "number") {
329
- patchVariableDeclarator(s, node, decl);
330
+ s.removeNode(node);
330
331
  continue;
331
332
  }
332
333
  const symbolId = symbolIdNode.value;
@@ -343,7 +344,7 @@ function createFakeJsPlugin({ dtsInput }) {
343
344
  s.overwriteNode(node, ss.toString());
344
345
  }
345
346
  const str = s.toString();
346
- if (str.trim().length === 0) return "export {}";
347
+ if (str.trim().length === 0) return "export { };";
347
348
  return str;
348
349
  }
349
350
  };
@@ -375,15 +376,14 @@ function collectDependencies(s, node, getIdentifierIndex) {
375
376
  else if (node$1.type === "TSTypeQuery") addDependency(node$1.exprName);
376
377
  else if (node$1.type === "TSImportType") {
377
378
  if (node$1.argument.type !== "TSLiteralType" || node$1.argument.literal.type !== "Literal" || typeof node$1.argument.literal.value !== "string") return;
378
- if (!node$1.qualifier) throw new Error("Import namespace is not supported");
379
379
  const source = node$1.argument.literal.value;
380
- const imported = s.sliceNode(node$1.qualifier);
380
+ const imported = node$1.qualifier && s.sliceNode(node$1.qualifier);
381
381
  const local = importNamespace(s, source, imported, getIdentifierIndex);
382
382
  addDependency({
383
383
  type: "Identifier",
384
384
  name: local,
385
385
  start: node$1.start + (node$1.isTypeOf ? 7 : 0),
386
- end: node$1.qualifier.end
386
+ end: node$1.qualifier ? node$1.qualifier.end : node$1.end
387
387
  });
388
388
  }
389
389
  } });
@@ -399,16 +399,40 @@ function isReferenceId(node) {
399
399
  function stringifyDependencies(s, deps) {
400
400
  return deps.map((node) => `() => ${node.type === "Identifier" ? node.name : s.sliceNode(node)}`).join(", ");
401
401
  }
402
- function patchVariableDeclarator(s, node, decl) {
403
- if (decl.init && !decl.id.typeAnnotation) s.overwriteNode(node, `type ${s.sliceNode(decl.id)} = ${s.sliceNode(decl.init)}`);
404
- else if (!node.declare) s.prependLeft(node.start, "declare ");
405
- }
406
402
  function patchImportSource(s, node) {
407
403
  if ((node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration") && node.source?.value && RE_DTS.test(node.source.value)) {
408
404
  s.overwriteNode(node.source, JSON.stringify(filename_dts_to(node.source.value, "js")));
409
405
  return true;
410
406
  }
411
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
+ }
412
436
  function rewriteImportExport(s, node) {
413
437
  if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" && !node.declaration) {
414
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, ""));
@@ -423,7 +447,7 @@ function rewriteImportExport(s, node) {
423
447
  if (node.moduleReference.type === "TSExternalModuleReference") s.overwriteNode(node, `import ${s.sliceNode(node.id)} from ${s.sliceNode(node.moduleReference.expression)}`);
424
448
  return true;
425
449
  } else if (node.type === "TSExportAssignment") {
426
- s.overwriteNode(node, `export default ${s.sliceNode(node.expression)}`);
450
+ s.overwriteNode(node, `export { ${s.sliceNode(node.expression)} as default }`);
427
451
  return true;
428
452
  } else if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "Identifier") {
429
453
  s.overwriteNode(node, `export { ${s.sliceNode(node.declaration)} as default }`);
@@ -432,22 +456,26 @@ function rewriteImportExport(s, node) {
432
456
  }
433
457
  function importNamespace(s, source, imported, getIdentifierIndex) {
434
458
  const local = `_${getIdentifierIndex()}`;
435
- 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`);
436
461
  return local;
437
462
  }
438
463
 
439
464
  //#endregion
440
465
  //#region src/utils/tsc.ts
441
- let ts$1;
466
+ const debug = Debug("rolldown-plugin-dts:tsc");
467
+ let ts;
442
468
  let formatHost;
443
469
  function initTs() {
470
+ debug("loading typescript");
444
471
  const require = createRequire(import.meta.url);
445
- ts$1 = require("typescript");
472
+ ts = require("typescript");
446
473
  formatHost = {
447
- getCurrentDirectory: () => ts$1.sys.getCurrentDirectory(),
448
- getNewLine: () => ts$1.sys.newLine,
449
- getCanonicalFileName: ts$1.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase()
474
+ getCurrentDirectory: () => ts.sys.getCurrentDirectory(),
475
+ getNewLine: () => ts.sys.newLine,
476
+ getCanonicalFileName: ts.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase()
450
477
  };
478
+ debug(`loaded typescript: ${ts.version}`);
451
479
  }
452
480
  const defaultCompilerOptions = {
453
481
  declaration: true,
@@ -473,7 +501,9 @@ function createOrGetTsModule(programs, compilerOptions, id, code, isEntry) {
473
501
  file: sourceFile
474
502
  };
475
503
  }
504
+ debug(`create program for module: ${id}`);
476
505
  const module = createTsProgram(compilerOptions, id, code);
506
+ debug(`created program for module: ${id}`);
477
507
  programs.push(module.program);
478
508
  return module;
479
509
  }
@@ -484,7 +514,7 @@ function createTsProgram(compilerOptions, id, code) {
484
514
  ...loadTsconfig(id),
485
515
  ...compilerOptions
486
516
  };
487
- const host = ts$1.createCompilerHost(options, true);
517
+ const host = ts.createCompilerHost(options, true);
488
518
  const { readFile: _readFile, fileExists: _fileExists } = host;
489
519
  host.fileExists = (fileName) => {
490
520
  if (files.has(fileName)) return true;
@@ -494,9 +524,9 @@ function createTsProgram(compilerOptions, id, code) {
494
524
  if (files.has(fileName)) return files.get(fileName);
495
525
  return _readFile(fileName);
496
526
  };
497
- const program = ts$1.createProgram([id], {
527
+ const program = ts.createProgram([id], {
498
528
  ...compilerOptions,
499
- moduleResolution: ts$1.ModuleResolutionKind.Node10,
529
+ moduleResolution: ts.ModuleResolutionKind.Node10,
500
530
  declaration: true,
501
531
  emitDeclarationOnly: true,
502
532
  outDir: void 0,
@@ -512,15 +542,33 @@ function createTsProgram(compilerOptions, id, code) {
512
542
  file: sourceFile
513
543
  };
514
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
+ }
515
563
  const tsconfigCache = new Map();
516
564
  function loadTsconfig(id) {
517
- const configPath = ts$1.findConfigFile(path.dirname(id), ts$1.sys.fileExists);
565
+ const configPath = ts.findConfigFile(path.dirname(id), ts.sys.fileExists);
518
566
  if (!configPath) return {};
519
567
  if (tsconfigCache.has(configPath)) return tsconfigCache.get(configPath);
520
- const { config, error } = ts$1.readConfigFile(configPath, ts$1.sys.readFile);
521
- if (error) throw ts$1.formatDiagnostic(error, formatHost);
522
- const configContents = ts$1.parseJsonConfigFileContent(config, ts$1.sys, path.dirname(configPath));
523
- if (configContents.errors.length) throw ts$1.formatDiagnostics(configContents.errors, formatHost);
568
+ const { config, error } = ts.readConfigFile(configPath, ts.sys.readFile);
569
+ if (error) throw ts.formatDiagnostic(error, formatHost);
570
+ const configContents = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configPath));
571
+ if (configContents.errors.length) throw ts.formatDiagnostics(configContents.errors, formatHost);
524
572
  tsconfigCache.set(configPath, configContents.options);
525
573
  return configContents.options;
526
574
  }
@@ -568,22 +616,19 @@ function createGeneratePlugin({ compilerOptions, isolatedDeclaration: isolatedDe
568
616
  const isEntry = mod?.isEntry;
569
617
  if (isolatedDeclaration$1) {
570
618
  const result = isolatedDeclaration(id, code, isolatedDeclaration$1 === true ? {} : isolatedDeclaration$1);
571
- 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
+ }
572
626
  dtsCode = result.code;
573
627
  } else {
574
- const { program: { program }, file } = createOrGetTsModule(programs, compilerOptions, id, code, isEntry);
575
- const { emitSkipped, diagnostics } = program.emit(
576
- file,
577
- (_, code$1) => {
578
- dtsCode = code$1;
579
- },
580
- void 0,
581
- true,
582
- void 0,
583
- // @ts-expect-error private API: forceDtsEmit
584
- true
585
- );
586
- 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;
587
632
  }
588
633
  if (!dtsCode) return this.error(new Error(`Failed to generate dts for ${id}`));
589
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.1",
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",