diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index a56fa320e..5a5824b87 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -10,6 +10,7 @@ import { clearTempDeclarationDir, loadTsconfig, processSourceEntry, + warnIfOutside, } from './utils'; const __filename = fileURLToPath(import.meta.url); @@ -110,11 +111,18 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ const tsConfigResult = loadTsconfig(tsconfigPath); const { options: rawCompilerOptions } = tsConfigResult; + const { declarationDir, outDir, composite, incremental } = + rawCompilerOptions; const dtsEmitPath = options.distPath ?? - rawCompilerOptions.declarationDir ?? + declarationDir ?? + outDir ?? config.output?.distPath?.root; + // check whether declarationDir or outDir is outside from current project + warnIfOutside(cwd, declarationDir, 'declarationDir'); + warnIfOutside(cwd, outDir, 'outDir'); + // clean dts files if (config.output.cleanDistPath !== false) { await cleanDtsFiles(dtsEmitPath); @@ -126,11 +134,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ } // clean tsbuildinfo file - if ( - rawCompilerOptions.composite || - rawCompilerOptions.incremental || - options.build - ) { + if (composite || incremental || options.build) { await cleanTsBuildInfoFile(tsconfigPath, rawCompilerOptions); } diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index e01e7c5fa..fcca489a3 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -505,3 +505,21 @@ export async function cleanTsBuildInfoFile( await fsP.rm(tsbuildInfoFilePath, { force: true }); } } + +export function warnIfOutside( + cwd: string, + dir: string | undefined, + label: string, +): void { + if (dir) { + const normalizedCwd = normalize(cwd); + const normalizedDir = normalize(dir); + const relDir = relative(normalizedCwd, normalizedDir); + + if (relDir.startsWith('..')) { + logger.warn( + `The resolved ${label} ${color.cyan(normalizedDir)} is outside the project root ${color.cyan(normalizedCwd)}, please check your tsconfig file.`, + ); + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 383db5119..ad88b41c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -675,6 +675,8 @@ importers: tests/integration/dts/bundle/true: {} + tests/integration/dts/check/outside-root: {} + tests/integration/dts/composite/abort-on-error: {} tests/integration/dts/composite/basic: {} diff --git a/tests/integration/dts/check/outside-root/package.json b/tests/integration/dts/check/outside-root/package.json new file mode 100644 index 000000000..befadd662 --- /dev/null +++ b/tests/integration/dts/check/outside-root/package.json @@ -0,0 +1,6 @@ +{ + "name": "dts-check-outdise-root-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/dts/check/outside-root/rslib.config.ts b/tests/integration/dts/check/outside-root/rslib.config.ts new file mode 100644 index 000000000..f3070aa8a --- /dev/null +++ b/tests/integration/dts/check/outside-root/rslib.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + dts: true, + }), + ], +}); diff --git a/tests/integration/dts/check/outside-root/src/index.ts b/tests/integration/dts/check/outside-root/src/index.ts new file mode 100644 index 000000000..3329a7d97 --- /dev/null +++ b/tests/integration/dts/check/outside-root/src/index.ts @@ -0,0 +1 @@ +export const foo = 'foo'; diff --git a/tests/integration/dts/check/outside-root/tsconfig.json b/tests/integration/dts/check/outside-root/tsconfig.json new file mode 100644 index 000000000..4afb884a9 --- /dev/null +++ b/tests/integration/dts/check/outside-root/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig/declarationDir.json", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "composite": true + }, + "include": ["src"] +} diff --git a/tests/integration/dts/check/tsconfig/declarationDir.json b/tests/integration/dts/check/tsconfig/declarationDir.json new file mode 100644 index 000000000..3d99c9aa3 --- /dev/null +++ b/tests/integration/dts/check/tsconfig/declarationDir.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationDir": "dist" + } +} diff --git a/tests/integration/dts/index.test.ts b/tests/integration/dts/index.test.ts index 6ae3d852b..ea48205c9 100644 --- a/tests/integration/dts/index.test.ts +++ b/tests/integration/dts/index.test.ts @@ -1,5 +1,5 @@ import { existsSync } from 'node:fs'; -import { join } from 'node:path'; +import { join, normalize } from 'node:path'; import stripAnsi from 'strip-ansi'; import { buildAndGetResults, @@ -607,3 +607,31 @@ describe('use with other features', async () => { `); }); }); + +describe('check tsconfig.json field', async () => { + test('check whether declarationDir is resolved outside from project root', async () => { + const fixturePath = join(__dirname, 'check', 'outside-root'); + const { logs, restore } = proxyConsole(); + const { files } = await buildAndGetResults({ + fixturePath, + type: 'dts', + }); + const logStrings = logs.map((log) => stripAnsi(log)); + restore(); + + const expectDeclarationDir = normalize( + join(__dirname, 'check/tsconfig/dist'), + ); + const expectRoot = normalize(join(__dirname, 'check/outside-root')); + + expect( + logStrings.some((log) => + log.includes( + `The resolved declarationDir ${expectDeclarationDir} is outside the project root ${expectRoot}, please check your tsconfig file.`, + ), + ), + ).toEqual(true); + + expect(files.esm).toMatchInlineSnapshot('undefined'); + }); +});