diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 3c5cadef3..ac109619a 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -930,6 +930,7 @@ const composeDtsConfig = async ( // Only setting ⁠dts.bundle to true will generate the bundled d.ts. bundle: dts?.bundle ?? false, distPath: dts?.distPath ?? output?.distPath?.root ?? './dist', + build: dts?.build ?? false, abortOnError: dts?.abortOnError ?? true, dtsExtension: dts?.autoExtension ? dtsExtension : '.d.ts', autoExternal, diff --git a/packages/core/src/types/config/index.ts b/packages/core/src/types/config/index.ts index 3f4247426..7b159240d 100644 --- a/packages/core/src/types/config/index.ts +++ b/packages/core/src/types/config/index.ts @@ -29,7 +29,10 @@ export type Syntax = | string[]; export type Dts = - | (Pick & { + | (Pick< + PluginDtsOptions, + 'bundle' | 'distPath' | 'abortOnError' | 'build' + > & { autoExtension?: boolean; }) | boolean; diff --git a/packages/plugin-dts/src/dts.ts b/packages/plugin-dts/src/dts.ts index d689f335a..4759c0b46 100644 --- a/packages/plugin-dts/src/dts.ts +++ b/packages/plugin-dts/src/dts.ts @@ -1,5 +1,13 @@ import fs from 'node:fs'; -import { basename, dirname, isAbsolute, join, relative } from 'node:path'; +import { + basename, + dirname, + isAbsolute, + join, + normalize, + relative, + resolve, +} from 'node:path'; import { logger } from '@rsbuild/core'; import color from 'picocolors'; import ts from 'typescript'; @@ -110,6 +118,7 @@ export async function generateDts(data: DtsGenOptions): Promise { tsconfigPath, name, cwd, + build, isWatch, dtsExtension = '.d.ts', autoExternal = true, @@ -133,6 +142,33 @@ export async function generateDts(data: DtsGenOptions): Promise { ? distPath : rawCompilerOptions.declarationDir || './dist'; + if (build) { + // do not allow to use bundle DTS when 'build: true' since temp declarationDir should be set by user in tsconfig + if (bundle) { + throw Error(`Can not set "dts.bundle: true" when "dts.build: true"`); + } + + // can not set '--declarationDir' or '--outDir' when 'build: true'. + if ( + (!rawCompilerOptions.outDir || + normalize(rawCompilerOptions.outDir) !== + normalize(resolve(dirname(configPath), outDir))) && + (!rawCompilerOptions.declarationDir || + normalize(rawCompilerOptions.declarationDir) !== + normalize(resolve(dirname(configPath), outDir))) + ) { + const info = + rawCompilerOptions.outDir && !rawCompilerOptions.declarationDir + ? 'outDir' + : 'declarationDir'; + throw Error( + `Please set ${info}: "${outDir}" in ${color.underline( + configPath, + )} to keep it same as "dts.distPath" or "output.distPath" field in lib config.`, + ); + } + } + const getDeclarationDir = (bundle: boolean, distPath?: string) => { if (bundle) { return ensureTempDeclarationDir(cwd); @@ -204,6 +240,7 @@ export async function generateDts(data: DtsGenOptions): Promise { onComplete, bundle, isWatch, + build, ); if (!isWatch) { diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index 362992cf8..3767dd915 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -10,6 +10,7 @@ const __dirname = dirname(__filename); export type PluginDtsOptions = { bundle?: boolean; distPath?: string; + build?: boolean; abortOnError?: boolean; dtsExtension?: string; autoExternal?: @@ -33,6 +34,7 @@ export type DtsGenOptions = PluginDtsOptions & { cwd: string; isWatch: boolean; dtsEntry: DtsEntry; + build?: boolean; tsconfigPath?: string; userExternals?: NonNullable['externals']; }; @@ -46,7 +48,6 @@ export const PLUGIN_DTS_NAME = 'rsbuild:dts'; // use ts compiler API to generate bundleless dts // use ts compiler API and api-extractor to generate dts bundle -// TODO: support incremental build, to build one or more projects and their dependencies // TODO: deal alias in dts export const pluginDts = (options: PluginDtsOptions): RsbuildPlugin => ({ name: PLUGIN_DTS_NAME, @@ -54,6 +55,7 @@ export const pluginDts = (options: PluginDtsOptions): RsbuildPlugin => ({ setup(api) { options.bundle = options.bundle ?? false; options.abortOnError = options.abortOnError ?? true; + options.build = options.build ?? false; const dtsPromises: Promise[] = []; let promisesResult: TaskResult[] = []; diff --git a/packages/plugin-dts/src/tsc.ts b/packages/plugin-dts/src/tsc.ts index 48e8529e6..712d9cfd4 100644 --- a/packages/plugin-dts/src/tsc.ts +++ b/packages/plugin-dts/src/tsc.ts @@ -23,6 +23,7 @@ export async function emitDts( onComplete: (isSuccess: boolean) => void, bundle = false, isWatch = false, + build = false, ): Promise { const start = Date.now(); const { configPath, declarationDir, name, dtsExtension, banner, footer } = @@ -42,131 +43,199 @@ export async function emitDts( emitDeclarationOnly: true, }; - if (!isWatch) { - const host: ts.CompilerHost = ts.createCompilerHost(compilerOptions); - - const program: ts.Program = ts.createProgram({ - rootNames: fileNames, - options: compilerOptions, - projectReferences, - host, - configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics( - configFileParseResult, - ), - }); - - const emitResult = program.emit(); - - const allDiagnostics = ts - .getPreEmitDiagnostics(program) - .concat(emitResult.diagnostics); + const createProgram = ts.createSemanticDiagnosticsBuilderProgram; + const formatHost: ts.FormatDiagnosticsHost = { + getCanonicalFileName: (path) => path, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => ts.sys.newLine, + }; - const diagnosticMessages: string[] = []; + const reportDiagnostic = (diagnostic: ts.Diagnostic) => { + const fileLoc = getFileLoc(diagnostic, configPath); - for (const diagnostic of allDiagnostics) { - const fileLoc = getFileLoc(diagnostic, configPath); - const message = `${fileLoc} - ${color.red('error')} ${color.gray(`TS${diagnostic.code}:`)} ${ts.flattenDiagnosticMessageText( + logger.error( + `${fileLoc} - ${color.red('error')} ${color.gray(`TS${diagnostic.code}:`)}`, + ts.flattenDiagnosticMessageText( diagnostic.messageText, - host.getNewLine(), - )}`; - diagnosticMessages.push(message); - } - - await processDtsFiles(bundle, declarationDir, dtsExtension, banner, footer); + formatHost.getNewLine(), + ), + ); + }; - if (diagnosticMessages.length) { - logger.error( - `Failed to emit declaration files. ${color.gray(`(${name})`)}`, - ); + const reportWatchStatusChanged: ts.WatchStatusReporter = async ( + diagnostic: ts.Diagnostic, + _newLine: string, + _options: ts.CompilerOptions, + errorCount?: number, + ) => { + const message = `${ts.flattenDiagnosticMessageText( + diagnostic.messageText, + formatHost.getNewLine(), + )} ${color.gray(`(${name})`)}`; + + // 6031: File change detected. Starting incremental compilation... + // 6032: Starting compilation in watch mode... + if (diagnostic.code === 6031 || diagnostic.code === 6032) { + logger.info(message); + } - for (const message of diagnosticMessages) { + // 6194: 0 errors or 2+ errors! + if (diagnostic.code === 6194) { + if (errorCount === 0 || !errorCount) { + logger.info(message); + onComplete(true); + } else { logger.error(message); } + await processDtsFiles( + bundle, + declarationDir, + dtsExtension, + banner, + footer, + ); + } - throw new Error('DTS generation failed'); + // 6193: 1 error + if (diagnostic.code === 6193) { + logger.error(message); + await processDtsFiles( + bundle, + declarationDir, + dtsExtension, + banner, + footer, + ); } + }; - logger.ready( - `DTS generated in ${getTimeCost(start)} ${color.gray(`(${name})`)}`, - ); - } else { - const createProgram = ts.createSemanticDiagnosticsBuilderProgram; - const formatHost: ts.FormatDiagnosticsHost = { - getCanonicalFileName: (path) => path, - getCurrentDirectory: ts.sys.getCurrentDirectory, - getNewLine: () => ts.sys.newLine, - }; - - const reportDiagnostic = (diagnostic: ts.Diagnostic) => { - const fileLoc = getFileLoc(diagnostic, configPath); - - logger.error( - `${fileLoc} - ${color.red('error')} ${color.gray(`TS${diagnostic.code}:`)}`, - ts.flattenDiagnosticMessageText( - diagnostic.messageText, - formatHost.getNewLine(), + const system = { ...ts.sys }; + + if (!isWatch) { + // build mode + if (!build) { + const host: ts.CompilerHost = ts.createCompilerHost(compilerOptions); + + const program: ts.Program = ts.createProgram({ + rootNames: fileNames, + options: compilerOptions, + projectReferences, + host, + configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics( + configFileParseResult, ), - ); - }; - - const reportWatchStatusChanged: ts.WatchStatusReporter = async ( - diagnostic: ts.Diagnostic, - _newLine: string, - _options: ts.CompilerOptions, - errorCount?: number, - ) => { - const message = `${ts.flattenDiagnosticMessageText( - diagnostic.messageText, - formatHost.getNewLine(), - )} ${color.gray(`(${name})`)}`; + }); - // 6031: File change detected. Starting incremental compilation... - // 6032: Starting compilation in watch mode... - if (diagnostic.code === 6031 || diagnostic.code === 6032) { - logger.info(message); + const emitResult = program.emit(); + + const allDiagnostics = ts + .getPreEmitDiagnostics(program) + .concat(emitResult.diagnostics); + + const diagnosticMessages: string[] = []; + + for (const diagnostic of allDiagnostics) { + const fileLoc = getFileLoc(diagnostic, configPath); + const message = `${fileLoc} - ${color.red('error')} ${color.gray(`TS${diagnostic.code}:`)} ${ts.flattenDiagnosticMessageText( + diagnostic.messageText, + host.getNewLine(), + )}`; + diagnosticMessages.push(message); } - // 6194: 0 errors or 2+ errors! - if (diagnostic.code === 6194) { - if (errorCount === 0) { - logger.info(message); - onComplete(true); - } else { + await processDtsFiles( + bundle, + declarationDir, + dtsExtension, + banner, + footer, + ); + + if (diagnosticMessages.length) { + logger.error( + `Failed to emit declaration files. ${color.gray(`(${name})`)}`, + ); + + for (const message of diagnosticMessages) { logger.error(message); } - await processDtsFiles( - bundle, - declarationDir, - dtsExtension, - banner, - footer, - ); + + throw new Error('DTS generation failed'); } + } else { + // incremental build with project references + let errorNumber = 0; + const reportErrorSummary = (errorCount: number) => { + errorNumber = errorCount; + }; + + const host = ts.createSolutionBuilderHost( + system, + createProgram, + reportDiagnostic, + undefined, + reportErrorSummary, + ); - // 6193: 1 error - if (diagnostic.code === 6193) { - logger.error(message); - await processDtsFiles( - bundle, - declarationDir, - dtsExtension, - banner, - footer, + const solutionBuilder = ts.createSolutionBuilder( + host, + [configPath], + compilerOptions, + ); + + solutionBuilder.build(); + + await processDtsFiles( + bundle, + declarationDir, + dtsExtension, + banner, + footer, + ); + + if (errorNumber > 0) { + logger.error( + `Failed to emit declaration files. ${color.gray(`(${name})`)}`, ); - } - }; - const system = { ...ts.sys }; + throw new Error('DTS generation failed'); + } + } - const host = ts.createWatchCompilerHost( - configPath, - compilerOptions, - system, - createProgram, - reportDiagnostic, - reportWatchStatusChanged, + logger.ready( + `DTS generated in ${getTimeCost(start)} ${color.gray(`(${name})`)}`, ); + } else { + // watch mode + if (!build) { + const host = ts.createWatchCompilerHost( + configPath, + compilerOptions, + system, + createProgram, + reportDiagnostic, + reportWatchStatusChanged, + ); + + ts.createWatchProgram(host); + } else { + // incremental build with project references + const host = ts.createSolutionBuilderWithWatchHost( + system, + createProgram, + reportDiagnostic, + undefined, + reportWatchStatusChanged, + ); - ts.createWatchProgram(host); + const solutionBuilder = ts.createSolutionBuilderWithWatch( + host, + [configPath], + compilerOptions, + { watch: true }, + ); + + solutionBuilder.build(); + } } } diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index f56eef55d..b85c1ece4 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -95,10 +95,17 @@ export async function addBannerAndFooter( const content = await fsP.readFile(file, 'utf-8'); const code = new MagicString(content); - banner && code.prepend(`${banner}\n`); - footer && code.append(`\n${footer}\n`); + if (banner && !content.trimStart().startsWith(banner.trim())) { + code.prepend(`${banner}\n`); + } + + if (footer && !content.trimEnd().endsWith(footer.trim())) { + code.append(`\n${footer}\n`); + } - await fsP.writeFile(file, code.toString()); + if (code.hasChanged()) { + await fsP.writeFile(file, code.toString()); + } } export async function processDtsFiles( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0ea079e2..d64613ff6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -545,6 +545,18 @@ importers: tests/integration/dts/bundle/true: {} + tests/integration/dts/composite/__references__: {} + + tests/integration/dts/composite/abort-on-error: {} + + tests/integration/dts/composite/basic: {} + + tests/integration/dts/composite/dist-path: {} + + tests/integration/dts/composite/process-files: {} + + tests/integration/dts/composite/tsconfig: {} + tests/integration/entry/glob: {} tests/integration/entry/multiple: {} diff --git a/tests/README.md b/tests/README.md index 0fd2eb55c..aa485d818 100644 --- a/tests/README.md +++ b/tests/README.md @@ -20,7 +20,7 @@ Rslib will try to cover the common scenarios in the [integration test cases of M | decorator | 🟢 | | | define | 🟢 | | | dts | 🟢 | | -| dts-composite | ⚪️ | | +| dts-composite | 🟢 | | | esbuildOptions | ⚫️ | | | externals | 🟢 | | | format | 🟢 | | diff --git a/tests/integration/dts/composite/__references__/package.json b/tests/integration/dts/composite/__references__/package.json new file mode 100644 index 000000000..737f903ca --- /dev/null +++ b/tests/integration/dts/composite/__references__/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-composite-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/composite/__references__/src/index.ts b/tests/integration/dts/composite/__references__/src/index.ts new file mode 100644 index 000000000..37537151f --- /dev/null +++ b/tests/integration/dts/composite/__references__/src/index.ts @@ -0,0 +1 @@ +export const b = 'hello world'; diff --git a/tests/integration/dts/composite/__references__/tsconfig.json b/tests/integration/dts/composite/__references__/tsconfig.json new file mode 100644 index 000000000..24fd1e106 --- /dev/null +++ b/tests/integration/dts/composite/__references__/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "rootDir": "src", + "baseUrl": "./", + "outDir": "dist", + "composite": true + }, + "include": ["src"] +} diff --git a/tests/integration/dts/composite/abort-on-error/package.json b/tests/integration/dts/composite/abort-on-error/package.json new file mode 100644 index 000000000..e5bd62dfd --- /dev/null +++ b/tests/integration/dts/composite/abort-on-error/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-composite-abort-on-error-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/composite/abort-on-error/rslib.config.ts b/tests/integration/dts/composite/abort-on-error/rslib.config.ts new file mode 100644 index 000000000..ea21dd23d --- /dev/null +++ b/tests/integration/dts/composite/abort-on-error/rslib.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + bundle: false, + build: true, + abortOnError: false, + }, + }), + ], + source: { + entry: { + index: ['./src/**'], + }, + }, +}); diff --git a/tests/integration/dts/composite/abort-on-error/src/const.ts b/tests/integration/dts/composite/abort-on-error/src/const.ts new file mode 100644 index 000000000..88a6d19f4 --- /dev/null +++ b/tests/integration/dts/composite/abort-on-error/src/const.ts @@ -0,0 +1,3 @@ +export interface A { + a: number; +} diff --git a/tests/integration/dts/composite/abort-on-error/src/index.ts b/tests/integration/dts/composite/abort-on-error/src/index.ts new file mode 100644 index 000000000..2595bdbbf --- /dev/null +++ b/tests/integration/dts/composite/abort-on-error/src/index.ts @@ -0,0 +1,6 @@ +import type { A } from './const'; + +export const getA = (item: A) => { + item.a = '0'; + return item; +}; diff --git a/tests/integration/dts/composite/abort-on-error/tsconfig.json b/tests/integration/dts/composite/abort-on-error/tsconfig.json new file mode 100644 index 000000000..ede350bc7 --- /dev/null +++ b/tests/integration/dts/composite/abort-on-error/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist/esm" + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/composite/basic/package.json b/tests/integration/dts/composite/basic/package.json new file mode 100644 index 000000000..eeb3b1190 --- /dev/null +++ b/tests/integration/dts/composite/basic/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-composite-basic-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/composite/basic/rslib.config.ts b/tests/integration/dts/composite/basic/rslib.config.ts new file mode 100644 index 000000000..7fa687cb9 --- /dev/null +++ b/tests/integration/dts/composite/basic/rslib.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + bundle: false, + build: true, + }, + }), + ], + source: { + entry: { + index: ['./src/**'], + }, + }, +}); diff --git a/tests/integration/dts/composite/basic/src/index.ts b/tests/integration/dts/composite/basic/src/index.ts new file mode 100644 index 000000000..cf1f61676 --- /dev/null +++ b/tests/integration/dts/composite/basic/src/index.ts @@ -0,0 +1 @@ +export * from './sum'; diff --git a/tests/integration/dts/composite/basic/src/sum.ts b/tests/integration/dts/composite/basic/src/sum.ts new file mode 100644 index 000000000..bc0dd4eba --- /dev/null +++ b/tests/integration/dts/composite/basic/src/sum.ts @@ -0,0 +1,10 @@ +export const num1 = 1; +export const num2 = 2; +export const num3 = 3; + +export const str1 = 'str1'; +export const str2 = 'str2'; +export const str3 = 'str3'; + +export const numSum = num1 + num2 + num3; +export const strSum = str1 + str2 + str3; diff --git a/tests/integration/dts/composite/basic/tsconfig.json b/tests/integration/dts/composite/basic/tsconfig.json new file mode 100644 index 000000000..ede350bc7 --- /dev/null +++ b/tests/integration/dts/composite/basic/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist/esm" + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/composite/dist-path/package.json b/tests/integration/dts/composite/dist-path/package.json new file mode 100644 index 000000000..6cbd0e34a --- /dev/null +++ b/tests/integration/dts/composite/dist-path/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-composite-dist-path-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/composite/dist-path/rslib.config.ts b/tests/integration/dts/composite/dist-path/rslib.config.ts new file mode 100644 index 000000000..98f7f60a6 --- /dev/null +++ b/tests/integration/dts/composite/dist-path/rslib.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + bundle: false, + build: true, + distPath: './dist/custom', + }, + }), + ], + source: { + entry: { + index: ['./src/**'], + }, + }, +}); diff --git a/tests/integration/dts/composite/dist-path/src/index.ts b/tests/integration/dts/composite/dist-path/src/index.ts new file mode 100644 index 000000000..dfa3258cb --- /dev/null +++ b/tests/integration/dts/composite/dist-path/src/index.ts @@ -0,0 +1 @@ +export const num1 = 1; diff --git a/tests/integration/dts/composite/dist-path/tsconfig.json b/tests/integration/dts/composite/dist-path/tsconfig.json new file mode 100644 index 000000000..ba70a04b4 --- /dev/null +++ b/tests/integration/dts/composite/dist-path/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist/custom" + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/composite/process-files/package.json b/tests/integration/dts/composite/process-files/package.json new file mode 100644 index 000000000..7f6d70ab5 --- /dev/null +++ b/tests/integration/dts/composite/process-files/package.json @@ -0,0 +1,5 @@ +{ + "name": "dts-composite-process-files-test", + "version": "1.0.0", + "private": true +} diff --git a/tests/integration/dts/composite/process-files/rslib.config.ts b/tests/integration/dts/composite/process-files/rslib.config.ts new file mode 100644 index 000000000..c4d35fcb4 --- /dev/null +++ b/tests/integration/dts/composite/process-files/rslib.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + banner: { + dts: '/*! hello banner dts composite*/', + }, + footer: { + dts: '/*! hello banner dts composite*/', + }, + dts: { + bundle: false, + build: true, + autoExtension: true, + }, + }), + ], + source: { + entry: { + index: ['./src/**'], + }, + }, +}); diff --git a/tests/integration/dts/composite/process-files/src/index.ts b/tests/integration/dts/composite/process-files/src/index.ts new file mode 100644 index 000000000..dfa3258cb --- /dev/null +++ b/tests/integration/dts/composite/process-files/src/index.ts @@ -0,0 +1 @@ +export const num1 = 1; diff --git a/tests/integration/dts/composite/process-files/tsconfig.json b/tests/integration/dts/composite/process-files/tsconfig.json new file mode 100644 index 000000000..ede350bc7 --- /dev/null +++ b/tests/integration/dts/composite/process-files/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist/esm" + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/composite/tsconfig/package.json b/tests/integration/dts/composite/tsconfig/package.json new file mode 100644 index 000000000..df16da111 --- /dev/null +++ b/tests/integration/dts/composite/tsconfig/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-composite-tsconfig-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/composite/tsconfig/rslib.config.ts b/tests/integration/dts/composite/tsconfig/rslib.config.ts new file mode 100644 index 000000000..7fa687cb9 --- /dev/null +++ b/tests/integration/dts/composite/tsconfig/rslib.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + bundle: false, + build: true, + }, + }), + ], + source: { + entry: { + index: ['./src/**'], + }, + }, +}); diff --git a/tests/integration/dts/composite/tsconfig/src/index.ts b/tests/integration/dts/composite/tsconfig/src/index.ts new file mode 100644 index 000000000..cf1f61676 --- /dev/null +++ b/tests/integration/dts/composite/tsconfig/src/index.ts @@ -0,0 +1 @@ +export * from './sum'; diff --git a/tests/integration/dts/composite/tsconfig/src/sum.ts b/tests/integration/dts/composite/tsconfig/src/sum.ts new file mode 100644 index 000000000..bc0dd4eba --- /dev/null +++ b/tests/integration/dts/composite/tsconfig/src/sum.ts @@ -0,0 +1,10 @@ +export const num1 = 1; +export const num2 = 2; +export const num3 = 3; + +export const str1 = 'str1'; +export const str2 = 'str2'; +export const str3 = 'str3'; + +export const numSum = num1 + num2 + num3; +export const strSum = str1 + str2 + str3; diff --git a/tests/integration/dts/composite/tsconfig/tsconfig.json b/tests/integration/dts/composite/tsconfig/tsconfig.json new file mode 100644 index 000000000..6bf173365 --- /dev/null +++ b/tests/integration/dts/composite/tsconfig/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/index.test.ts b/tests/integration/dts/index.test.ts index 81446f37b..ce46cc55e 100644 --- a/tests/integration/dts/index.test.ts +++ b/tests/integration/dts/index.test.ts @@ -1,3 +1,4 @@ +import { existsSync } from 'node:fs'; import { join } from 'node:path'; import { buildAndGetResults } from 'test-helper'; import { describe, expect, test } from 'vitest'; @@ -221,3 +222,81 @@ describe('dts when bundle: true', () => { ); }); }); + +describe('dts when build: true', () => { + test('basic', async () => { + const fixturePath = join(__dirname, 'composite', 'basic'); + const { files } = await buildAndGetResults({ + fixturePath, + type: 'dts', + }); + + expect(files.esm).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/composite/basic/dist/esm/index.d.ts", + "/tests/integration/dts/composite/basic/dist/esm/sum.d.ts", + ] + `); + + const compositeDistPath = join( + fixturePath, + '../__references__/dist/index.d.ts', + ); + expect(existsSync(compositeDistPath)).toBeTruthy(); + }); + + test('distPath', async () => { + const fixturePath = join(__dirname, 'composite', 'dist-path'); + const { files } = await buildAndGetResults({ + fixturePath, + type: 'dts', + }); + + expect(files.esm).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/composite/dist-path/dist/custom/index.d.ts", + ] + `); + }); + + test('process files - auto extension and banner / footer', async () => { + const fixturePath = join(__dirname, 'composite', 'process-files'); + const { contents } = await buildAndGetResults({ + fixturePath, + type: 'dts', + }); + + expect(contents.esm).toMatchInlineSnapshot(` + { + "/tests/integration/dts/composite/process-files/dist/esm/index.d.mts": "/*! hello banner dts composite*/ + export declare const num1 = 1; + + /*! hello banner dts composite*/ + ", + } + `); + }); + + test('abortOnError: false', async () => { + const fixturePath = join(__dirname, 'composite', 'abort-on-error'); + const { isSuccess } = await buildAndGetResults({ + fixturePath, + type: 'dts', + }); + + expect(isSuccess).toBe(true); + }); + + test('tsconfig missing some fields', async () => { + const fixturePath = join(__dirname, 'composite', 'tsconfig'); + try { + await buildAndGetResults({ + fixturePath, + type: 'dts', + }); + } catch (err: any) { + // not easy to proxy child process stdout + expect(err.message).toBe('Error occurred in esm DTS generation'); + } + }); +});