From 2bc38ceff79046c12dee93e52cd5b7dc93bf302e Mon Sep 17 00:00:00 2001 From: Timeless0911 <1604889533@qq.com> Date: Thu, 20 Feb 2025 16:14:39 +0800 Subject: [PATCH 1/5] feat: warn if `declarationDir` or `outDir` is resolved outside project root --- packages/plugin-dts/src/index.ts | 18 +++++++------ packages/plugin-dts/src/utils.ts | 15 +++++++++++ pnpm-lock.yaml | 2 ++ .../dts/check/outside-root/package.json | 6 +++++ .../dts/check/outside-root/rslib.config.ts | 10 ++++++++ .../dts/check/outside-root/src/index.ts | 1 + .../dts/check/outside-root/tsconfig.json | 9 +++++++ .../dts/check/tsconfig/declarationDir.json | 6 +++++ tests/integration/dts/index.test.ts | 25 ++++++++++++++++++- 9 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 tests/integration/dts/check/outside-root/package.json create mode 100644 tests/integration/dts/check/outside-root/rslib.config.ts create mode 100644 tests/integration/dts/check/outside-root/src/index.ts create mode 100644 tests/integration/dts/check/outside-root/tsconfig.json create mode 100644 tests/integration/dts/check/tsconfig/declarationDir.json diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index a56fa320e..c543e9f04 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -1,5 +1,5 @@ import { type ChildProcess, fork } from 'node:child_process'; -import { dirname, extname, join } from 'node:path'; +import { dirname, extname, join, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; import { type RsbuildConfig, type RsbuildPlugin, logger } from '@rsbuild/core'; import color from 'picocolors'; @@ -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..0b735ba79 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -505,3 +505,18 @@ export async function cleanTsBuildInfoFile( await fsP.rm(tsbuildInfoFilePath, { force: true }); } } + +export function warnIfOutside( + cwd: string, + dir: string | undefined, + label: string, +): void { + if (dir) { + const relDir = relative(cwd, dir); + if (relDir.startsWith('..') || relDir.startsWith('/')) { + logger.warn( + `The resolved ${label} ${color.cyan(dir)} is outside the project root ${color.cyan(cwd)}, 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..812bc31ec 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, resolve } from 'node:path'; import stripAnsi from 'strip-ansi'; import { buildAndGetResults, @@ -607,3 +607,26 @@ 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)); + + expect(files.esm).toMatchInlineSnapshot('undefined'); + expect( + logStrings.some((log) => + log.includes( + `The resolved declarationDir ${normalize(join(__dirname, 'check/tsconfig/dist'))} is outside the project root ${normalize(join(__dirname, 'check/outside-root'))}, please check your tsconfig file.`, + ), + ), + ).toEqual(true); + + restore(); + }); +}); From 9909939b429ad6c2cdf50c91cb7485bc06dffc84 Mon Sep 17 00:00:00 2001 From: Timeless0911 <1604889533@qq.com> Date: Thu, 20 Feb 2025 16:15:52 +0800 Subject: [PATCH 2/5] chore: update --- tests/integration/dts/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/dts/index.test.ts b/tests/integration/dts/index.test.ts index 812bc31ec..53eaf776e 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, normalize, resolve } from 'node:path'; +import { join, normalize } from 'node:path'; import stripAnsi from 'strip-ansi'; import { buildAndGetResults, From fb95918df5e5a2385cc885136086e2c255508220 Mon Sep 17 00:00:00 2001 From: Timeless0911 <1604889533@qq.com> Date: Thu, 20 Feb 2025 16:20:24 +0800 Subject: [PATCH 3/5] chore: update --- packages/plugin-dts/src/index.ts | 2 +- packages/plugin-dts/src/utils.ts | 7 ++++++- tests/integration/dts/index.test.ts | 14 +++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index c543e9f04..5a5824b87 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -1,5 +1,5 @@ import { type ChildProcess, fork } from 'node:child_process'; -import { dirname, extname, join, relative } from 'node:path'; +import { dirname, extname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { type RsbuildConfig, type RsbuildPlugin, logger } from '@rsbuild/core'; import color from 'picocolors'; diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index 0b735ba79..39f15c18a 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -506,6 +506,11 @@ export async function cleanTsBuildInfoFile( } } +const windowsSlashRegex = /\\/g; +export function normalizeSlash(p: string): string { + return p.replace(windowsSlashRegex, '/'); +} + export function warnIfOutside( cwd: string, dir: string | undefined, @@ -515,7 +520,7 @@ export function warnIfOutside( const relDir = relative(cwd, dir); if (relDir.startsWith('..') || relDir.startsWith('/')) { logger.warn( - `The resolved ${label} ${color.cyan(dir)} is outside the project root ${color.cyan(cwd)}, please check your tsconfig file.`, + `The resolved ${label} ${color.cyan(normalizeSlash(dir))} is outside the project root ${color.cyan(normalizeSlash(cwd))}, please check your tsconfig file.`, ); } } diff --git a/tests/integration/dts/index.test.ts b/tests/integration/dts/index.test.ts index 53eaf776e..8a3c681a6 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, normalize } from 'node:path'; +import path, { join } from 'node:path'; import stripAnsi from 'strip-ansi'; import { buildAndGetResults, @@ -618,15 +618,23 @@ describe('check tsconfig.json field', async () => { }); const logStrings = logs.map((log) => stripAnsi(log)); - expect(files.esm).toMatchInlineSnapshot('undefined'); + const expectDeclarationDir = join(__dirname, 'check/tsconfig/dist') + .split(path.sep) + .join('/'); + const expectRoot = join(__dirname, 'check/outside-root') + .split(path.sep) + .join('/'); + expect( logStrings.some((log) => log.includes( - `The resolved declarationDir ${normalize(join(__dirname, 'check/tsconfig/dist'))} is outside the project root ${normalize(join(__dirname, 'check/outside-root'))}, please check your tsconfig file.`, + `The resolved declarationDir ${expectDeclarationDir} is outside the project root ${expectRoot}, please check your tsconfig file.`, ), ), ).toEqual(true); + expect(files.esm).toMatchInlineSnapshot('undefined'); + restore(); }); }); From 572446244340c9da1829903a6c213a3d538c482f Mon Sep 17 00:00:00 2001 From: Timeless0911 <1604889533@qq.com> Date: Thu, 20 Feb 2025 17:39:43 +0800 Subject: [PATCH 4/5] chore: update --- packages/plugin-dts/src/utils.ts | 13 +++++-------- tests/integration/dts/index.test.ts | 15 ++++++--------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index 39f15c18a..3c2c481d3 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -506,21 +506,18 @@ export async function cleanTsBuildInfoFile( } } -const windowsSlashRegex = /\\/g; -export function normalizeSlash(p: string): string { - return p.replace(windowsSlashRegex, '/'); -} - export function warnIfOutside( cwd: string, dir: string | undefined, label: string, ): void { if (dir) { - const relDir = relative(cwd, dir); - if (relDir.startsWith('..') || relDir.startsWith('/')) { + const normalizedCwd = normalize(cwd); + const normalizedDir = normalize(dir); + const relDir = relative(normalizedCwd, normalizedDir); + if (relDir.startsWith('..') || relDir.startsWith(path.sep)) { logger.warn( - `The resolved ${label} ${color.cyan(normalizeSlash(dir))} is outside the project root ${color.cyan(normalizeSlash(cwd))}, please check your tsconfig file.`, + `The resolved ${label} ${color.cyan(normalizedDir)} is outside the project root ${color.cyan(normalizedCwd)}, please check your tsconfig file.`, ); } } diff --git a/tests/integration/dts/index.test.ts b/tests/integration/dts/index.test.ts index 8a3c681a6..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 path, { join } from 'node:path'; +import { join, normalize } from 'node:path'; import stripAnsi from 'strip-ansi'; import { buildAndGetResults, @@ -617,13 +617,12 @@ describe('check tsconfig.json field', async () => { type: 'dts', }); const logStrings = logs.map((log) => stripAnsi(log)); + restore(); - const expectDeclarationDir = join(__dirname, 'check/tsconfig/dist') - .split(path.sep) - .join('/'); - const expectRoot = join(__dirname, 'check/outside-root') - .split(path.sep) - .join('/'); + const expectDeclarationDir = normalize( + join(__dirname, 'check/tsconfig/dist'), + ); + const expectRoot = normalize(join(__dirname, 'check/outside-root')); expect( logStrings.some((log) => @@ -634,7 +633,5 @@ describe('check tsconfig.json field', async () => { ).toEqual(true); expect(files.esm).toMatchInlineSnapshot('undefined'); - - restore(); }); }); From 6e6b732db5714172724db3263a380f4a107577ce Mon Sep 17 00:00:00 2001 From: Timeless0911 <1604889533@qq.com> Date: Fri, 21 Feb 2025 11:23:25 +0800 Subject: [PATCH 5/5] chore: update --- packages/plugin-dts/src/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index 3c2c481d3..fcca489a3 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -515,7 +515,8 @@ export function warnIfOutside( const normalizedCwd = normalize(cwd); const normalizedDir = normalize(dir); const relDir = relative(normalizedCwd, normalizedDir); - if (relDir.startsWith('..') || relDir.startsWith(path.sep)) { + + if (relDir.startsWith('..')) { logger.warn( `The resolved ${label} ${color.cyan(normalizedDir)} is outside the project root ${color.cyan(normalizedCwd)}, please check your tsconfig file.`, );