rolldown-plugin-dts 0.4.0 → 0.5.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
@@ -45,8 +45,19 @@ interface Options {
45
45
  */
46
46
  emitDtsOnly?: boolean
47
47
 
48
- isolatedDeclaration?: Omit<IsolatedDeclarationsOptions, 'sourcemap'>
49
-
48
+ /**
49
+ * The `compilerOptions` for the TypeScript compiler.
50
+ *
51
+ * @see https://www.typescriptlang.org/docs/handbook/compiler-options.html
52
+ */
53
+ compilerOptions?: CompilerOptions
54
+ /**
55
+ * When `true`, the plugin will generate `.d.ts` files using `oxc-transform`,
56
+ * which is blazingly faster than `typescript` compiler.
57
+ *
58
+ * This option is enabled when `isolatedDeclaration` in `tsconfig.json` is set to `true`.
59
+ */
60
+ isolatedDeclaration?: boolean | Omit<IsolatedDeclarationsOptions, 'sourcemap'>
50
61
  /**
51
62
  * dts file name alias `{ [filename]: path }`
52
63
  *
@@ -65,14 +76,24 @@ interface Options {
65
76
 
66
77
  ## ⚠️ Caveats
67
78
 
68
- - The plugin uses Oxc's `isolatedDeclarations` to generate `.d.ts` files,
69
- which means you need to set `isolatedDeclarations: true` in your `tsconfig.json` and ensure there are no errors.
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.
70
81
 
71
82
  - Namespaces are not supported yet.
72
83
  - `export * as ns from './ns'`
73
84
  - `import * as ns from './ns'` and then `export { ns }`
74
85
  - `type ns = import('./ns')`
75
86
 
87
+ ## Differences from `rollup-plugin-dts`
88
+
89
+ `rolldown-plugin-dts` generates separate chunks for `.d.ts` files, enabling both source code (`.js`)
90
+ and type definition files (`.d.ts`) to be produced in a single build process.
91
+
92
+ However, this functionality is limited to ESM output format. Consequently,
93
+ **two** distinct build processes are required for CommonJS source code (`.cjs`)
94
+ and its corresponding type definition files (`.d.cts`).
95
+ In such cases, the `emitDtsOnly` option can be particularly helpful.
96
+
76
97
  ## Credits
77
98
 
78
99
  The project is inspired by [rollup-plugin-dts](https://github.com/Swatinem/rollup-plugin-dts)
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { IsolatedDeclarationsOptions } from "oxc-transform";
2
+ import { CompilerOptions } from "typescript";
2
3
  import { Plugin } from "rolldown";
3
4
 
4
5
  //#region src/fake-js.d.ts
@@ -6,7 +7,7 @@ declare function createFakeJsPlugin({ dtsInput }: Pick<Options, "dtsInput">): Pl
6
7
 
7
8
  //#endregion
8
9
  //#region src/generate.d.ts
9
- declare function createGeneratePlugin({ isolatedDeclaration, inputAlias, resolve, emitDtsOnly }: Pick<Options, "isolatedDeclaration" | "inputAlias" | "resolve" | "emitDtsOnly">): Plugin;
10
+ declare function createGeneratePlugin({ compilerOptions, isolatedDeclaration, inputAlias, resolve, emitDtsOnly }: Pick<Options, "isolatedDeclaration" | "inputAlias" | "resolve" | "emitDtsOnly" | "compilerOptions">): Plugin;
10
11
 
11
12
  //#endregion
12
13
  //#region src/index.d.ts
@@ -23,7 +24,19 @@ interface Options {
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.
24
25
  */
25
26
  emitDtsOnly?: boolean;
26
- isolatedDeclaration?: Omit<IsolatedDeclarationsOptions, "sourcemap">;
27
+ /**
28
+ * The `compilerOptions` for the TypeScript compiler.
29
+ *
30
+ * @see https://www.typescriptlang.org/docs/handbook/compiler-options.html
31
+ */
32
+ compilerOptions?: CompilerOptions;
33
+ /**
34
+ * When `true`, the plugin will generate `.d.ts` files using `oxc-transform`,
35
+ * which is blazingly faster than `typescript` compiler.
36
+ *
37
+ * This option is enabled when `isolatedDeclaration` in `tsconfig.json` is set to `true`.
38
+ */
39
+ isolatedDeclaration?: boolean | Omit<IsolatedDeclarationsOptions, "sourcemap">;
27
40
  /**
28
41
  * dts file name alias `{ [filename]: path }`
29
42
  *
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import { MagicStringAST } from "magic-string-ast";
2
- import { parseAsync } from "oxc-parser";
2
+ import { parseSync } from "oxc-parser";
3
3
  import path, { basename, extname } from "node:path";
4
4
  import { createResolver } from "dts-resolver";
5
+ import { getTsconfig } from "get-tsconfig";
5
6
  import { isolatedDeclaration } from "oxc-transform";
7
+ import * as ts from "typescript";
8
+ import { createRequire } from "node:module";
6
9
 
7
10
  //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js
8
11
  var WalkerBase = class {
@@ -248,8 +251,8 @@ function createFakeJsPlugin({ dtsInput }) {
248
251
  },
249
252
  transform: {
250
253
  filter: { id: RE_DTS },
251
- async handler(code, id) {
252
- const { program, comments } = await parseAsync(id, code);
254
+ handler(code, id) {
255
+ const { program, comments } = parseSync(id, code);
253
256
  const preserved = collectReferenceDirectives(comments);
254
257
  preserveMap.set(id, preserved);
255
258
  const s = new MagicStringAST(code);
@@ -300,9 +303,9 @@ function createFakeJsPlugin({ dtsInput }) {
300
303
  return str;
301
304
  }
302
305
  },
303
- async renderChunk(code, chunk) {
306
+ renderChunk(code, chunk) {
304
307
  if (!RE_DTS.test(chunk.fileName)) return;
305
- const { program } = await parseAsync(chunk.fileName, code);
308
+ const { program } = parseSync(chunk.fileName, code);
306
309
  const s = new MagicStringAST(code);
307
310
  const comments = new Set();
308
311
  for (const id of chunk.moduleIds) {
@@ -433,16 +436,113 @@ function importNamespace(s, source, imported, getIdentifierIndex) {
433
436
  return local;
434
437
  }
435
438
 
439
+ //#endregion
440
+ //#region src/utils/tsc.ts
441
+ let ts$1;
442
+ let formatHost;
443
+ function initTs() {
444
+ const require = createRequire(import.meta.url);
445
+ ts$1 = require("typescript");
446
+ formatHost = {
447
+ getCurrentDirectory: () => ts$1.sys.getCurrentDirectory(),
448
+ getNewLine: () => ts$1.sys.newLine,
449
+ getCanonicalFileName: ts$1.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase()
450
+ };
451
+ }
452
+ const defaultCompilerOptions = {
453
+ declaration: true,
454
+ noEmit: false,
455
+ emitDeclarationOnly: true,
456
+ noEmitOnError: true,
457
+ checkJs: false,
458
+ declarationMap: false,
459
+ skipLibCheck: true,
460
+ preserveSymlinks: true,
461
+ target: 99,
462
+ resolveJsonModule: true
463
+ };
464
+ function createOrGetTsModule(programs, compilerOptions, id, code, isEntry) {
465
+ const tsProgram = programs.find(({ program }) => {
466
+ if (isEntry) return program.getRootFileNames().includes(id);
467
+ return program.getSourceFile(id);
468
+ });
469
+ if (tsProgram) {
470
+ const sourceFile = tsProgram.program.getSourceFile(id);
471
+ if (sourceFile) return {
472
+ program: tsProgram,
473
+ file: sourceFile
474
+ };
475
+ }
476
+ const module = createTsProgram(compilerOptions, id, code);
477
+ programs.push(module.program);
478
+ return module;
479
+ }
480
+ function createTsProgram(compilerOptions, id, code) {
481
+ const files = new Map([[id, code]]);
482
+ const options = {
483
+ ...defaultCompilerOptions,
484
+ ...loadTsconfig(id),
485
+ ...compilerOptions
486
+ };
487
+ const host = ts$1.createCompilerHost(options, true);
488
+ const { readFile: _readFile, fileExists: _fileExists } = host;
489
+ host.fileExists = (fileName) => {
490
+ if (files.has(fileName)) return true;
491
+ return _fileExists(fileName);
492
+ };
493
+ host.readFile = (fileName) => {
494
+ if (files.has(fileName)) return files.get(fileName);
495
+ return _readFile(fileName);
496
+ };
497
+ const program = ts$1.createProgram([id], {
498
+ ...compilerOptions,
499
+ moduleResolution: ts$1.ModuleResolutionKind.Node10,
500
+ declaration: true,
501
+ emitDeclarationOnly: true,
502
+ outDir: void 0,
503
+ declarationDir: void 0
504
+ }, host);
505
+ const sourceFile = program.getSourceFile(id);
506
+ if (!sourceFile) throw new Error(`Source file not found: ${id}`);
507
+ return {
508
+ program: {
509
+ program,
510
+ files
511
+ },
512
+ file: sourceFile
513
+ };
514
+ }
515
+ const tsconfigCache = new Map();
516
+ function loadTsconfig(id) {
517
+ const configPath = ts$1.findConfigFile(path.dirname(id), ts$1.sys.fileExists);
518
+ if (!configPath) return {};
519
+ 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);
524
+ tsconfigCache.set(configPath, configContents.options);
525
+ return configContents.options;
526
+ }
527
+
436
528
  //#endregion
437
529
  //#region src/generate.ts
438
530
  const meta = { dtsFile: true };
439
- function createGeneratePlugin({ isolatedDeclaration: isolatedDeclaration$1, inputAlias, resolve = false, emitDtsOnly = false }) {
531
+ function createGeneratePlugin({ compilerOptions, isolatedDeclaration: isolatedDeclaration$1, inputAlias, resolve = false, emitDtsOnly = false }) {
440
532
  const dtsMap = new Map();
441
533
  const inputAliasMap = new Map(inputAlias && Object.entries(inputAlias));
442
534
  const resolver = createResolver();
535
+ let programs = [];
443
536
  let inputOption;
444
537
  return {
445
538
  name: "rolldown-plugin-dts:generate",
539
+ buildStart(options) {
540
+ if (isolatedDeclaration$1 == null) {
541
+ const { config } = getTsconfig(options.cwd) || {};
542
+ if (config?.compilerOptions?.isolatedDeclarations) isolatedDeclaration$1 = { stripInternal: !!config?.compilerOptions.stripInternal };
543
+ }
544
+ if (!isolatedDeclaration$1) initTs();
545
+ },
446
546
  options({ input }) {
447
547
  if (isPlainObject(input)) inputOption = { ...input };
448
548
  },
@@ -463,15 +563,35 @@ function createGeneratePlugin({ isolatedDeclaration: isolatedDeclaration$1, inpu
463
563
  exclude: [RE_DTS, RE_NODE_MODULES]
464
564
  } },
465
565
  handler(code, id) {
466
- const { code: dtsCode, errors } = isolatedDeclaration(id, code, isolatedDeclaration$1);
467
- if (errors.length) return this.error(errors[0]);
566
+ let dtsCode;
567
+ const mod = this.getModuleInfo(id);
568
+ const isEntry = mod?.isEntry;
569
+ if (isolatedDeclaration$1) {
570
+ const result = isolatedDeclaration(id, code, isolatedDeclaration$1 === true ? {} : isolatedDeclaration$1);
571
+ if (result.errors.length) return this.error(result.errors[0]);
572
+ dtsCode = result.code;
573
+ } 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));
587
+ }
588
+ if (!dtsCode) return this.error(new Error(`Failed to generate dts for ${id}`));
468
589
  const dtsId = filename_ts_to_dts(id);
469
590
  dtsMap.set(dtsId, {
470
591
  code: dtsCode,
471
592
  src: id
472
593
  });
473
- const mod = this.getModuleInfo(id);
474
- if (mod?.isEntry) {
594
+ if (isEntry) {
475
595
  let name = basename(dtsId, extname(dtsId));
476
596
  if (inputAliasMap.has(name)) name = inputAliasMap.get(name);
477
597
  else if (inputAliasMap.has(dtsId)) name = inputAliasMap.get(dtsId);
@@ -544,7 +664,10 @@ function createGeneratePlugin({ isolatedDeclaration: isolatedDeclaration$1, inpu
544
664
  },
545
665
  generateBundle: emitDtsOnly ? (options, bundle) => {
546
666
  for (const fileName of Object.keys(bundle)) if (!RE_DTS.test(fileName)) delete bundle[fileName];
547
- } : void 0
667
+ } : void 0,
668
+ buildEnd() {
669
+ programs = [];
670
+ }
548
671
  };
549
672
  }
550
673
  function isPlainObject(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rolldown-plugin-dts",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A Rolldown plugin to bundle dts files",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,10 +28,17 @@
28
28
  "access": "public"
29
29
  },
30
30
  "peerDependencies": {
31
- "rolldown": "^1.0.0-beta.7"
31
+ "rolldown": "^1.0.0-beta.7",
32
+ "typescript": "^5.0.0"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "typescript": {
36
+ "optional": true
37
+ }
32
38
  },
33
39
  "dependencies": {
34
40
  "dts-resolver": "^0.1.0",
41
+ "get-tsconfig": "^4.10.0",
35
42
  "magic-string-ast": "^0.9.1",
36
43
  "oxc-parser": "^0.62.0",
37
44
  "oxc-transform": "^0.62.0"