diff --git a/.gitignore b/.gitignore index 3ee2a5f10..5c3449f35 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ coverage/ doc_build/ playwright-report/ tsconfig.tsbuildinfo +tsconfig.esm.tsbuildinfo +tsconfig.cjs.tsbuildinfo test-temp-* test-results diff --git a/packages/plugin-dts/src/apiExtractor.ts b/packages/plugin-dts/src/apiExtractor.ts index 2a3cf8b52..1e24c4e92 100644 --- a/packages/plugin-dts/src/apiExtractor.ts +++ b/packages/plugin-dts/src/apiExtractor.ts @@ -52,9 +52,7 @@ export async function bundleDts(options: BundleOptions): Promise { untrimmedFilePath, }, compiler: { - tsconfigFilePath: tsconfigPath.includes(cwd) - ? tsconfigPath - : join(cwd, tsconfigPath), + tsconfigFilePath: tsconfigPath, }, projectFolder: cwd, }; diff --git a/packages/plugin-dts/src/dts.ts b/packages/plugin-dts/src/dts.ts index c8cf3d783..3636e8134 100644 --- a/packages/plugin-dts/src/dts.ts +++ b/packages/plugin-dts/src/dts.ts @@ -10,14 +10,9 @@ import { } from 'node:path'; import { logger } from '@rsbuild/core'; import color from 'picocolors'; -import ts from 'typescript'; import type { DtsGenOptions } from './index'; import { emitDts } from './tsc'; -import { - calcLongestCommonPath, - ensureTempDeclarationDir, - loadTsconfig, -} from './utils'; +import { calcLongestCommonPath, ensureTempDeclarationDir } from './utils'; const isObject = (obj: unknown): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; @@ -113,9 +108,10 @@ export const calcBundledPackages = (options: { export async function generateDts(data: DtsGenOptions): Promise { const { bundle, - distPath, + dtsEmitPath, dtsEntry, tsconfigPath, + tsConfigResult, name, cwd, build, @@ -125,15 +121,10 @@ export async function generateDts(data: DtsGenOptions): Promise { userExternals, banner, footer, - rootDistPath, } = data; logger.start(`Generating DTS... ${color.gray(`(${name})`)}`); - const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, tsconfigPath); - if (!configPath) { - logger.error(`tsconfig.json not found in ${cwd}`); - throw new Error(); - } - const { options: rawCompilerOptions, fileNames } = loadTsconfig(configPath); + + const { options: rawCompilerOptions, fileNames } = tsConfigResult; // The longest common path of all non-declaration input files. // If composite is set, the default is instead the directory containing the tsconfig.json file. @@ -141,17 +132,14 @@ export async function generateDts(data: DtsGenOptions): Promise { const rootDir = rawCompilerOptions.rootDir ?? (rawCompilerOptions.composite - ? dirname(configPath) + ? dirname(tsconfigPath) : await calcLongestCommonPath( fileNames.filter((fileName) => !/\.d\.(ts|mts|cts)$/.test(fileName)), )) ?? - dirname(configPath); - - const dtsEmitPath = - distPath ?? rawCompilerOptions.declarationDir ?? rootDistPath; + dirname(tsconfigPath); const resolvedDtsEmitPath = normalize( - resolve(dirname(configPath), dtsEmitPath), + resolve(dirname(tsconfigPath), dtsEmitPath), ); if (build) { @@ -173,13 +161,15 @@ export async function generateDts(data: DtsGenOptions): Promise { : 'declarationDir'; throw Error( `Please set ${info}: "${dtsEmitPath}" in ${color.underline( - configPath, + tsconfigPath, )} to keep it same as "dts.distPath" or "output.distPath.root" field in lib config.`, ); } } - const declarationDir = bundle ? ensureTempDeclarationDir(cwd) : dtsEmitPath; + const declarationDir = bundle + ? ensureTempDeclarationDir(cwd, name) + : dtsEmitPath; const { name: entryName, path: entryPath } = dtsEntry; let entry = ''; @@ -232,7 +222,8 @@ export async function generateDts(data: DtsGenOptions): Promise { { name, cwd, - configPath, + configPath: tsconfigPath, + tsConfigResult, declarationDir, dtsExtension, banner, diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index 2cee27645..a39467ea6 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -2,7 +2,14 @@ import { fork } from 'node:child_process'; import { dirname, extname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { type RsbuildConfig, type RsbuildPlugin, logger } from '@rsbuild/core'; -import { processSourceEntry } from './utils'; +import ts from 'typescript'; +import { + cleanDtsFiles, + cleanTsBuildInfoFile, + clearTempDeclarationDir, + loadTsconfig, + processSourceEntry, +} from './utils'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -34,9 +41,10 @@ export type DtsGenOptions = PluginDtsOptions & { cwd: string; isWatch: boolean; dtsEntry: DtsEntry; - rootDistPath: string; + dtsEmitPath: string; build?: boolean; - tsconfigPath?: string; + tsconfigPath: string; + tsConfigResult: ts.ParsedCommandLine; userExternals?: NonNullable['externals']; }; @@ -62,18 +70,13 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ let promisesResult: TaskResult[] = []; api.onBeforeEnvironmentCompile( - ({ isWatch, isFirstCompile, environment }) => { + async ({ isWatch, isFirstCompile, environment }) => { if (!isFirstCompile) { return; } const { config } = environment; - const jsExtension = extname(__filename); - const childProcess = fork(join(__dirname, `./dts${jsExtension}`), [], { - stdio: 'inherit', - }); - // TODO: @microsoft/api-extractor only support single entry to bundle DTS // use first element of Record type entry config const dtsEntry = processSourceEntry( @@ -81,14 +84,53 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ config.source?.entry, ); + const cwd = api.context.rootPath; + const tsconfigPath = ts.findConfigFile( + cwd, + ts.sys.fileExists, + config.source.tsconfigPath, + ); + + if (!tsconfigPath) { + logger.error(`tsconfig.json not found in ${cwd}`); + throw new Error(); + } + + const tsConfigResult = loadTsconfig(tsconfigPath); + const dtsEmitPath = + options.distPath ?? + tsConfigResult.options.declarationDir ?? + config.output?.distPath?.root; + + const jsExtension = extname(__filename); + const childProcess = fork(join(__dirname, `./dts${jsExtension}`), [], { + stdio: 'inherit', + }); + + const { cleanDistPath } = config.output; + + // clean dts files + if (cleanDistPath !== false) { + await cleanDtsFiles(dtsEmitPath); + } + + // clean .rslib temp folder + if (options.bundle) { + await clearTempDeclarationDir(cwd); + } + + // clean tsbuildinfo file + await cleanTsBuildInfoFile(tsconfigPath, tsConfigResult); + const dtsGenOptions: DtsGenOptions = { ...options, dtsEntry, + dtsEmitPath, userExternals: config.output.externals, - rootDistPath: config.output?.distPath?.root, - tsconfigPath: config.source.tsconfigPath, + tsconfigPath, + tsConfigResult, name: environment.name, - cwd: api.context.rootPath, + cwd, isWatch, }; diff --git a/packages/plugin-dts/src/tsc.ts b/packages/plugin-dts/src/tsc.ts index 396f518c1..e3e81237c 100644 --- a/packages/plugin-dts/src/tsc.ts +++ b/packages/plugin-dts/src/tsc.ts @@ -1,17 +1,13 @@ import { logger } from '@rsbuild/core'; import color from 'picocolors'; import ts from 'typescript'; -import { - getFileLoc, - getTimeCost, - loadTsconfig, - processDtsFiles, -} from './utils'; +import { getFileLoc, getTimeCost, processDtsFiles } from './utils'; export type EmitDtsOptions = { name: string; cwd: string; configPath: string; + tsConfigResult: ts.ParsedCommandLine; declarationDir: string; dtsExtension: string; banner?: string; @@ -63,14 +59,20 @@ export async function emitDts( build = false, ): Promise { const start = Date.now(); - const { configPath, declarationDir, name, dtsExtension, banner, footer } = - options; - const configFileParseResult = loadTsconfig(configPath); + const { + configPath, + tsConfigResult, + declarationDir, + name, + dtsExtension, + banner, + footer, + } = options; const { options: rawCompilerOptions, fileNames, projectReferences, - } = configFileParseResult; + } = tsConfigResult; const compilerOptions = { ...rawCompilerOptions, @@ -160,9 +162,8 @@ export async function emitDts( options: compilerOptions, projectReferences, host, - configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics( - configFileParseResult, - ), + configFileParsingDiagnostics: + ts.getConfigFileParsingDiagnostics(tsConfigResult), }); const emitResult = program.emit(); @@ -190,9 +191,8 @@ export async function emitDts( const program = ts.createIncrementalProgram({ rootNames: fileNames, options: compilerOptions, - configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics( - configFileParseResult, - ), + configFileParsingDiagnostics: + ts.getConfigFileParsingDiagnostics(tsConfigResult), projectReferences, host, createProgram, diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index b85c1ece4..78a146866 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import fsP from 'node:fs/promises'; import { platform } from 'node:os'; -import path, { join } from 'node:path'; +import path, { basename, dirname, join, relative, resolve } from 'node:path'; import { type RsbuildConfig, logger } from '@rsbuild/core'; import MagicString from 'magic-string'; import color from 'picocolors'; @@ -23,8 +23,8 @@ export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine { export const TEMP_FOLDER = '.rslib'; export const TEMP_DTS_DIR: string = `${TEMP_FOLDER}/declarations`; -export function ensureTempDeclarationDir(cwd: string): string { - const dirPath = path.join(cwd, TEMP_DTS_DIR); +export function ensureTempDeclarationDir(cwd: string, name: string): string { + const dirPath = path.join(cwd, TEMP_DTS_DIR, name); if (fs.existsSync(dirPath)) { return dirPath; @@ -38,6 +38,37 @@ export function ensureTempDeclarationDir(cwd: string): string { return dirPath; } +export async function pathExists(path: string): Promise { + return fs.promises + .access(path) + .then(() => true) + .catch(() => false); +} + +export async function emptyDir(dir: string): Promise { + if (!(await pathExists(dir))) { + return; + } + + try { + for (const file of await fs.promises.readdir(dir)) { + await fs.promises.rm(path.resolve(dir, file), { + recursive: true, + force: true, + }); + } + } catch (err) { + logger.debug(`Failed to empty dir: ${dir}`); + logger.debug(err); + } +} + +export async function clearTempDeclarationDir(cwd: string): Promise { + const dirPath = path.join(cwd, TEMP_DTS_DIR); + + await emptyDir(dirPath); +} + export function getFileLoc( diagnostic: ts.Diagnostic, configPath: string, @@ -196,3 +227,41 @@ export async function calcLongestCommonPath( return lca; } + +export async function cleanDtsFiles(dir: string): Promise { + const patterns = ['/**/*.d.ts', '/**/*.d.cts', '/**/*.d.mts']; + const files = await Promise.all( + patterns.map((pattern) => + glob(convertPath(join(dir, pattern)), { absolute: true }), + ), + ); + + const allFiles = files.flat(); + + await Promise.all(allFiles.map((file) => fsP.rm(file, { force: true }))); +} + +export async function cleanTsBuildInfoFile( + tsconfigPath: string, + tsConfigResult: ts.ParsedCommandLine, +): Promise { + const tsconfigDir = dirname(tsconfigPath); + const { outDir, rootDir, tsBuildInfoFile } = tsConfigResult.options; + let tsbuildInfoFilePath = `${basename( + tsconfigPath, + '.json', + )}${tsBuildInfoFile ?? '.tsbuildinfo'}`; + if (outDir) { + if (rootDir) { + tsbuildInfoFilePath = join( + outDir, + relative(resolve(tsconfigDir, rootDir), tsconfigDir), + tsbuildInfoFilePath, + ); + } else { + tsbuildInfoFilePath = join(outDir, tsbuildInfoFilePath); + } + } + + await fsP.rm(tsbuildInfoFilePath, { force: true }); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 344f60058..da5afee24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -566,6 +566,8 @@ importers: tests/integration/dts/build/basic: {} + tests/integration/dts/build/clean: {} + tests/integration/dts/build/dist-path: {} tests/integration/dts/build/process-files: {} @@ -578,6 +580,8 @@ importers: tests/integration/dts/bundle-false/basic: {} + tests/integration/dts/bundle-false/clean: {} + tests/integration/dts/bundle-false/declaration-dir: {} tests/integration/dts/bundle-false/dist-path: {} @@ -596,6 +600,8 @@ importers: tests/integration/dts/bundle/bundle-name: {} + tests/integration/dts/bundle/clean: {} + tests/integration/dts/bundle/dist-path: {} tests/integration/dts/bundle/false: {} @@ -612,6 +618,8 @@ importers: tests/integration/dts/composite/basic: {} + tests/integration/dts/composite/clean: {} + tests/integration/dts/composite/dist-path: {} tests/integration/dts/composite/process-files: {} diff --git a/tests/integration/dts/build/clean/package.json b/tests/integration/dts/build/clean/package.json new file mode 100644 index 000000000..ee89c5b57 --- /dev/null +++ b/tests/integration/dts/build/clean/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-build-clean-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/build/clean/rslib.config.ts b/tests/integration/dts/build/clean/rslib.config.ts new file mode 100644 index 000000000..6a9b64547 --- /dev/null +++ b/tests/integration/dts/build/clean/rslib.config.ts @@ -0,0 +1,34 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + distPath: './dist-types/esm', + bundle: false, + build: true, + }, + source: { + tsconfigPath: './tsconfig.esm.json', + }, + }), + generateBundleCjsConfig({ + bundle: false, + dts: { + distPath: './dist-types/cjs', + bundle: false, + build: true, + }, + source: { + tsconfigPath: './tsconfig.cjs.json', + }, + }), + ], + source: { + entry: { + index: ['./src/**'], + }, + }, +}); diff --git a/tests/integration/dts/build/clean/src/index.ts b/tests/integration/dts/build/clean/src/index.ts new file mode 100644 index 000000000..cf1f61676 --- /dev/null +++ b/tests/integration/dts/build/clean/src/index.ts @@ -0,0 +1 @@ +export * from './sum'; diff --git a/tests/integration/dts/build/clean/src/sum.ts b/tests/integration/dts/build/clean/src/sum.ts new file mode 100644 index 000000000..bc0dd4eba --- /dev/null +++ b/tests/integration/dts/build/clean/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/build/clean/tsconfig.cjs.json b/tests/integration/dts/build/clean/tsconfig.cjs.json new file mode 100644 index 000000000..2d79f7049 --- /dev/null +++ b/tests/integration/dts/build/clean/tsconfig.cjs.json @@ -0,0 +1,15 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist-types/cjs" + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/build/clean/tsconfig.esm.json b/tests/integration/dts/build/clean/tsconfig.esm.json new file mode 100644 index 000000000..87dd48c4a --- /dev/null +++ b/tests/integration/dts/build/clean/tsconfig.esm.json @@ -0,0 +1,15 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist-types/esm" + }, + "include": ["src"], + "references": [ + { + "path": "../__references__" + } + ] +} diff --git a/tests/integration/dts/bundle-false/clean/package.json b/tests/integration/dts/bundle-false/clean/package.json new file mode 100644 index 000000000..f173e62c3 --- /dev/null +++ b/tests/integration/dts/bundle-false/clean/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-bundle-false-clean-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/bundle-false/clean/rslib.config.ts b/tests/integration/dts/bundle-false/clean/rslib.config.ts new file mode 100644 index 000000000..db86c3df6 --- /dev/null +++ b/tests/integration/dts/bundle-false/clean/rslib.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + bundle: false, + distPath: './dist-types/esm', + }, + }), + generateBundleCjsConfig({ + bundle: false, + dts: { + bundle: false, + distPath: './dist-types/cjs', + }, + }), + ], + source: { + entry: { + index: ['../__fixtures__/src/**'], + }, + tsconfigPath: '../__fixtures__/tsconfig.json', + }, +}); diff --git a/tests/integration/dts/bundle/clean/package.json b/tests/integration/dts/bundle/clean/package.json new file mode 100644 index 000000000..0d745a81a --- /dev/null +++ b/tests/integration/dts/bundle/clean/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-bundle-clean-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/bundle/clean/rslib.config.ts b/tests/integration/dts/bundle/clean/rslib.config.ts new file mode 100644 index 000000000..0b27a618a --- /dev/null +++ b/tests/integration/dts/bundle/clean/rslib.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + dts: { + bundle: true, + distPath: './dist-types/esm', + }, + }), + generateBundleCjsConfig({ + dts: { + bundle: true, + distPath: './dist-types/cjs', + }, + }), + ], + source: { + entry: { + index: '../__fixtures__/src/index.ts', + }, + tsconfigPath: '../__fixtures__/tsconfig.json', + }, +}); diff --git a/tests/integration/dts/composite/clean/package.json b/tests/integration/dts/composite/clean/package.json new file mode 100644 index 000000000..d787432d4 --- /dev/null +++ b/tests/integration/dts/composite/clean/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-composite-clean-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/composite/clean/rslib.config.ts b/tests/integration/dts/composite/clean/rslib.config.ts new file mode 100644 index 000000000..142989714 --- /dev/null +++ b/tests/integration/dts/composite/clean/rslib.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + bundle: false, + dts: { + bundle: false, + distPath: 'dist-types/esm', + }, + }), + generateBundleCjsConfig({ + bundle: false, + dts: { + bundle: false, + distPath: 'dist-types/cjs', + }, + }), + ], + source: { + entry: { + index: ['../__fixtures__/src/**'], + }, + }, +}); diff --git a/tests/integration/dts/composite/clean/tsconfig.json b/tests/integration/dts/composite/clean/tsconfig.json new file mode 100644 index 000000000..4bbcc6074 --- /dev/null +++ b/tests/integration/dts/composite/clean/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "rootDir": "../__fixtures__/src", + "baseUrl": "./", + "composite": true + }, + "include": ["../__fixtures__/src"] +} diff --git a/tests/integration/dts/index.test.ts b/tests/integration/dts/index.test.ts index c4d35f0e1..629a00a93 100644 --- a/tests/integration/dts/index.test.ts +++ b/tests/integration/dts/index.test.ts @@ -1,6 +1,10 @@ import { existsSync } from 'node:fs'; import { join } from 'node:path'; -import { buildAndGetResults, globContentJSON } from 'test-helper'; +import { + buildAndGetResults, + createTempFiles, + globContentJSON, +} from 'test-helper'; import { describe, expect, test } from 'vitest'; describe('dts when bundle: false', () => { @@ -111,6 +115,36 @@ describe('dts when bundle: false', () => { ] `); }); + + test('should clean dts dist files', async () => { + const fixturePath = join(__dirname, 'bundle-false', 'clean'); + + const checkFiles = await createTempFiles(fixturePath, false); + + const { files } = await buildAndGetResults({ fixturePath, type: 'dts' }); + + for (const file of checkFiles) { + expect(existsSync(file)).toBe(false); + } + + expect(files.esm).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/bundle-false/clean/dist-types/esm/index.d.ts", + "/tests/integration/dts/bundle-false/clean/dist-types/esm/sum.d.ts", + "/tests/integration/dts/bundle-false/clean/dist-types/esm/utils/numbers.d.ts", + "/tests/integration/dts/bundle-false/clean/dist-types/esm/utils/strings.d.ts", + ] + `); + + expect(files.cjs).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/bundle-false/clean/dist-types/cjs/index.d.ts", + "/tests/integration/dts/bundle-false/clean/dist-types/cjs/sum.d.ts", + "/tests/integration/dts/bundle-false/clean/dist-types/cjs/utils/numbers.d.ts", + "/tests/integration/dts/bundle-false/clean/dist-types/cjs/utils/strings.d.ts", + ] + `); + }); }); describe('dts when bundle: true', () => { @@ -264,6 +298,30 @@ describe('dts when bundle: true', () => { expect(entries).toMatchSnapshot(); }); + + test('should clean dts dist files and .rslib folder', async () => { + const fixturePath = join(__dirname, 'bundle', 'clean'); + + const checkFiles = await createTempFiles(fixturePath, true); + + const { files } = await buildAndGetResults({ fixturePath, type: 'dts' }); + + for (const file of checkFiles) { + expect(existsSync(file)).toBe(false); + } + + expect(files.esm).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/bundle/clean/dist-types/esm/index.d.ts", + ] + `); + + expect(files.cjs).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/bundle/clean/dist-types/cjs/index.d.ts", + ] + `); + }); }); describe('dts when build: true', () => { @@ -354,6 +412,44 @@ describe('dts when build: true', () => { expect(err.message).toBe('Error occurred in esm DTS generation'); } }); + + test('should clean dts dist files', async () => { + const fixturePath = join(__dirname, 'build', 'clean'); + + const checkFiles = await createTempFiles(fixturePath, false); + + const { files } = await buildAndGetResults({ fixturePath, type: 'dts' }); + + for (const file of checkFiles) { + expect(existsSync(file)).toBe(false); + } + + expect(files.esm).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/build/clean/dist-types/esm/index.d.ts", + "/tests/integration/dts/build/clean/dist-types/esm/sum.d.ts", + ] + `); + + expect(files.cjs).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/build/clean/dist-types/cjs/index.d.ts", + "/tests/integration/dts/build/clean/dist-types/cjs/sum.d.ts", + ] + `); + + const referenceDistPath = join( + fixturePath, + '../__references__/dist/index.d.ts', + ); + expect(existsSync(referenceDistPath)).toBeTruthy(); + + const cjsBuildInfoPath = join(fixturePath, 'tsconfig.cjs.tsbuildinfo'); + expect(existsSync(cjsBuildInfoPath)).toBeTruthy(); + + const esmBuildInfoPath = join(fixturePath, 'tsconfig.esm.tsbuildinfo'); + expect(existsSync(esmBuildInfoPath)).toBeTruthy(); + }); }); describe('dts when composite: true', () => { @@ -452,4 +548,28 @@ describe('dts when composite: true', () => { const buildInfoPath = join(fixturePath, 'tsconfig.tsbuildinfo'); expect(existsSync(buildInfoPath)).toBeTruthy(); }); + + test('should clean dts dist files', async () => { + const fixturePath = join(__dirname, 'composite', 'clean'); + + const checkFiles = await createTempFiles(fixturePath, false); + + const { files } = await buildAndGetResults({ fixturePath, type: 'dts' }); + + for (const file of checkFiles) { + expect(existsSync(file)).toBe(false); + } + + expect(files.esm).toMatchInlineSnapshot(` + [ + "/tests/integration/dts/composite/clean/dist-types/esm/index.d.ts", + "/tests/integration/dts/composite/clean/dist-types/esm/sum.d.ts", + "/tests/integration/dts/composite/clean/dist-types/esm/utils/numbers.d.ts", + "/tests/integration/dts/composite/clean/dist-types/esm/utils/strings.d.ts", + ] + `); + + const buildInfoPath = join(fixturePath, 'tsconfig.tsbuildinfo'); + expect(existsSync(buildInfoPath)).toBeTruthy(); + }); }); diff --git a/tests/scripts/shared.ts b/tests/scripts/shared.ts index 2b3a483a8..ce04e5057 100644 --- a/tests/scripts/shared.ts +++ b/tests/scripts/shared.ts @@ -342,3 +342,46 @@ export function queryContent( return matched[1]; } + +export async function createTempFiles( + fixturePath: string, + bundle: boolean, +): Promise { + const checkFile: string[] = []; + + const tempDirCjs = join(fixturePath, 'dist-types', 'cjs'); + const tempDirEsm = join(fixturePath, 'dist-types', 'esm'); + const tempFileCjs = join(tempDirCjs, 'tempFile.d.ts'); + const tempFileEsm = join(tempDirEsm, 'tempFile.d.ts'); + + await fs.promises.mkdir(tempDirCjs, { recursive: true }); + await fs.promises.mkdir(tempDirEsm, { recursive: true }); + + await fs.promises.writeFile(tempFileCjs, 'console.log("temp file for cjs");'); + await fs.promises.writeFile(tempFileEsm, 'console.log("temp file for esm");'); + + checkFile.push(tempFileCjs, tempFileEsm); + + if (bundle) { + const tempDirRslib = join(fixturePath, '.rslib', 'declarations', 'cjs'); + const tempDirRslibEsm = join(fixturePath, '.rslib', 'declarations', 'esm'); + const tempFileRslibCjs = join(tempDirRslib, 'tempFile.d.ts'); + const tempFileRslibEsm = join(tempDirRslibEsm, 'tempFile.d.ts'); + + await fs.promises.mkdir(tempDirRslib, { recursive: true }); + await fs.promises.mkdir(tempDirRslibEsm, { recursive: true }); + + await fs.promises.writeFile( + tempFileRslibCjs, + 'console.log("temp file for cjs");', + ); + await fs.promises.writeFile( + tempFileRslibEsm, + 'console.log("temp file for esm");', + ); + + checkFile.push(tempFileRslibCjs, tempFileRslibEsm); + } + + return checkFile; +}