diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index 44846932a..a43ff52cf 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -237,26 +237,40 @@ async function addExtension( redirect: DtsRedirect, dtsFile: string, path: string, - extension: string, + jsExtension: string, + dtsExtension: string, ): Promise { if (!redirect.extension) { return path; } - let redirectPath = path; - - // Only add extension if redirectPath is an absolute or relative path - if (!isAbsolute(redirectPath) && !redirectPath.startsWith('.')) { - return redirectPath; + // Only add extension if path is an absolute or relative path + if (!isAbsolute(path) && !path.startsWith('.')) { + return path; } + const candidatePaths = []; + // If the import path refers to a directory, it most likely actually refers to a `index.*` file due to Node's module resolution - if (await isDirectory(join(dirname(dtsFile), redirectPath))) { + if (await isDirectory(join(dirname(dtsFile), path))) { // This uses `/` instead of `path.join` here because `join` removes potential "./" prefixes - redirectPath = `${redirectPath.replace(/\/+$/, '')}/index`; + candidatePaths.push(`${path.replace(/\/+$/, '')}/index`); + } + + candidatePaths.push(path); + + // make sure the candidatePath exists, otherwise we may break the import, e.g. import 'foo.svg', import '../foo/index' + for (const candidatePath of candidatePaths) { + if ( + await pathExists( + join(dirname(dtsFile), `${candidatePath}${dtsExtension}`), + ) + ) { + return `${candidatePath}${jsExtension}`; + } } - return `${redirectPath}${extension}`; + return path; } export async function redirectDtsImports( @@ -334,7 +348,7 @@ export async function redirectDtsImports( e: matchNode.range().end.index, }; }); - const extension = dtsExtension + const jsExtension = dtsExtension .replace(/\.d\.ts$/, '.js') .replace(/\.d\.cts$/, '.cjs') .replace(/\.d\.mts$/, '.mjs'); @@ -393,28 +407,18 @@ export async function redirectDtsImports( if (redirect.extension) { redirectImportPath = redirectImportPath.replace( /\.[^.]+$/, - extension, + jsExtension, ); } } else { // handle the case importPath is like './foo.bar', we need to check if './foo.bar.d.ts' exists - const candidatePath = await addExtension( + redirectImportPath = await addExtension( redirect, dtsFile, redirectImportPath, - extension, + jsExtension, + dtsExtension, ); - // make sure the candidatePath exists, otherwise we may break the import, e.g. import 'foo.svg' - if ( - await pathExists( - path.join( - dirname(dtsFile), - candidatePath.replace(/\.[^.]+$/, dtsExtension), - ), - ) - ) { - redirectImportPath = candidatePath; - } } } else { if ( @@ -425,7 +429,8 @@ export async function redirectDtsImports( redirect, dtsFile, redirectImportPath, - extension, + jsExtension, + dtsExtension, ); } @@ -434,7 +439,8 @@ export async function redirectDtsImports( redirect, dtsFile, redirectImportPath, - extension, + jsExtension, + dtsExtension, ); } } diff --git a/tests/integration/redirect/dts-tsgo/src/config.ts b/tests/integration/redirect/dts-tsgo/src/config.ts new file mode 100644 index 000000000..3932f5ba9 --- /dev/null +++ b/tests/integration/redirect/dts-tsgo/src/config.ts @@ -0,0 +1 @@ +export * from './config/load'; diff --git a/tests/integration/redirect/dts-tsgo/src/config/load.ts b/tests/integration/redirect/dts-tsgo/src/config/load.ts new file mode 100644 index 000000000..b34410a12 --- /dev/null +++ b/tests/integration/redirect/dts-tsgo/src/config/load.ts @@ -0,0 +1 @@ +export const loadConfig = () => {}; diff --git a/tests/integration/redirect/dts-tsgo/src/index.ts b/tests/integration/redirect/dts-tsgo/src/index.ts index a6a576679..977528a4b 100644 --- a/tests/integration/redirect/dts-tsgo/src/index.ts +++ b/tests/integration/redirect/dts-tsgo/src/index.ts @@ -18,12 +18,13 @@ export { export * from '@src/foo'; export * from '@src/logger'; export type { Foo } from '@src/types'; -// export { Router } from 'express'; export * from 'prebundle-pkg'; +// export { Router } from 'express'; export type { Bar } from 'types'; export * from './.hidden'; export * from './.hidden-folder'; export * from './a.b'; export * from './bar.baz'; +export * from './config'; export * from './foo'; export * from './types'; diff --git a/tests/integration/redirect/dts.test.ts b/tests/integration/redirect/dts.test.ts index d1a7acb79..a9d2eb63f 100644 --- a/tests/integration/redirect/dts.test.ts +++ b/tests/integration/redirect/dts.test.ts @@ -19,6 +19,10 @@ test('redirect.dts.path: true with redirect.dts.extension: false - default', asy "/tests/integration/redirect/dts/dist/default/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts/dist/default/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts/dist/default/esm/config.d.ts": "export * from './config/load'; + ", + "/tests/integration/redirect/dts/dist/default/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts/dist/default/esm/foo/foo.d.ts": "import { logRequest } from '../logger'; import { logger } from '../../../../compile/prebundle-pkg'; @@ -44,6 +48,7 @@ test('redirect.dts.path: true with redirect.dts.extension: false - default', asy export * from './.hidden-folder'; export * from './a.b'; export * from './bar.baz'; + export * from './config'; export * from './foo'; export * from './types'; ", @@ -80,6 +85,10 @@ test('redirect.dts.path: false with redirect.dts.extension: false', async () => "/tests/integration/redirect/dts/dist/path-false/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts/dist/path-false/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts/dist/path-false/esm/config.d.ts": "export * from './config/load'; + ", + "/tests/integration/redirect/dts/dist/path-false/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts/dist/path-false/esm/foo/foo.d.ts": "import { logRequest } from '@src/logger'; import { logger } from 'prebundle-pkg'; @@ -105,6 +114,7 @@ test('redirect.dts.path: false with redirect.dts.extension: false', async () => export * from './.hidden-folder'; export * from './a.b'; export * from './bar.baz'; + export * from './config'; export * from './foo'; export * from './types'; ", @@ -141,6 +151,10 @@ test('redirect.dts.path: true with redirect.dts.extension: true', async () => { "/tests/integration/redirect/dts/dist/extension-true/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts/dist/extension-true/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts/dist/extension-true/esm/config.d.ts": "export * from './config/load.js'; + ", + "/tests/integration/redirect/dts/dist/extension-true/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts/dist/extension-true/esm/foo/foo.d.ts": "import { logRequest } from '../logger.js'; import { logger } from '../../../../compile/prebundle-pkg'; @@ -166,6 +180,7 @@ test('redirect.dts.path: true with redirect.dts.extension: true', async () => { export * from './.hidden-folder/index.js'; export * from './a.b/index.js'; export * from './bar.baz.js'; + export * from './config.js'; export * from './foo/index.js'; export * from './types.js'; ", @@ -202,6 +217,10 @@ test('redirect.dts.path: false with dts.redirect.extension: true', async () => { "/tests/integration/redirect/dts/dist/path-false-extension-true/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts/dist/path-false-extension-true/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts/dist/path-false-extension-true/esm/config.d.ts": "export * from './config/load.js'; + ", + "/tests/integration/redirect/dts/dist/path-false-extension-true/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts/dist/path-false-extension-true/esm/foo/foo.d.ts": "import { logRequest } from '@src/logger'; import { logger } from 'prebundle-pkg'; @@ -227,6 +246,7 @@ test('redirect.dts.path: false with dts.redirect.extension: true', async () => { export * from './.hidden-folder/index.js'; export * from './a.b/index.js'; export * from './bar.baz.js'; + export * from './config.js'; export * from './foo/index.js'; export * from './types.js'; ", @@ -271,6 +291,14 @@ test('redirect.dts.extension: true with dts.autoExtension: true', async () => { "/tests/integration/redirect/dts/dist/auto-extension-true/bar.baz.d.mts": "export declare const bar = "bar-baz"; ", "/tests/integration/redirect/dts/dist/auto-extension-true/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts/dist/auto-extension-true/config.d.mts": "export * from './config/load.mjs'; + ", + "/tests/integration/redirect/dts/dist/auto-extension-true/config.d.ts": "export * from './config/load.js'; + ", + "/tests/integration/redirect/dts/dist/auto-extension-true/config/load.d.mts": "export declare const loadConfig: () => void; + ", + "/tests/integration/redirect/dts/dist/auto-extension-true/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts/dist/auto-extension-true/foo/foo.d.mts": "import { logRequest } from '../logger.mjs'; import { logger } from '../../../compile/prebundle-pkg'; @@ -303,6 +331,7 @@ test('redirect.dts.extension: true with dts.autoExtension: true', async () => { export * from './.hidden-folder/index.mjs'; export * from './a.b/index.mjs'; export * from './bar.baz.mjs'; + export * from './config.mjs'; export * from './foo/index.mjs'; export * from './types.mjs'; ", @@ -323,6 +352,7 @@ test('redirect.dts.extension: true with dts.autoExtension: true', async () => { export * from './.hidden-folder/index.js'; export * from './a.b/index.js'; export * from './bar.baz.js'; + export * from './config.js'; export * from './foo/index.js'; export * from './types.js'; ", diff --git a/tests/integration/redirect/dts/src/config.ts b/tests/integration/redirect/dts/src/config.ts new file mode 100644 index 000000000..3932f5ba9 --- /dev/null +++ b/tests/integration/redirect/dts/src/config.ts @@ -0,0 +1 @@ +export * from './config/load'; diff --git a/tests/integration/redirect/dts/src/config/load.ts b/tests/integration/redirect/dts/src/config/load.ts new file mode 100644 index 000000000..b34410a12 --- /dev/null +++ b/tests/integration/redirect/dts/src/config/load.ts @@ -0,0 +1 @@ +export const loadConfig = () => {}; diff --git a/tests/integration/redirect/dts/src/index.ts b/tests/integration/redirect/dts/src/index.ts index 8c0da979f..316eda25b 100644 --- a/tests/integration/redirect/dts/src/index.ts +++ b/tests/integration/redirect/dts/src/index.ts @@ -25,5 +25,6 @@ export * from './.hidden'; export * from './.hidden-folder'; export * from './a.b'; export * from './bar.baz'; +export * from './config'; export * from './foo'; export * from './types'; diff --git a/tests/integration/redirect/dtsTsgo.test.ts b/tests/integration/redirect/dtsTsgo.test.ts index f68a3a9e4..8820b8f5d 100644 --- a/tests/integration/redirect/dtsTsgo.test.ts +++ b/tests/integration/redirect/dtsTsgo.test.ts @@ -23,6 +23,10 @@ describe.skipIf(process.version.startsWith('v18'))( "/tests/integration/redirect/dts-tsgo/dist/default/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts-tsgo/dist/default/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts-tsgo/dist/default/esm/config.d.ts": "export * from './config/load'; + ", + "/tests/integration/redirect/dts-tsgo/dist/default/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts-tsgo/dist/default/esm/foo/foo.d.ts": "import { logRequest } from '../logger'; import { logger } from '../../../../compile/prebundle-pkg'; @@ -47,6 +51,7 @@ describe.skipIf(process.version.startsWith('v18'))( export * from './.hidden-folder'; export * from './a.b'; export * from './bar.baz'; + export * from './config'; export * from './foo'; export * from './types'; ", @@ -82,6 +87,10 @@ describe.skipIf(process.version.startsWith('v18'))( "/tests/integration/redirect/dts-tsgo/dist/path-false/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts-tsgo/dist/path-false/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts-tsgo/dist/path-false/esm/config.d.ts": "export * from './config/load'; + ", + "/tests/integration/redirect/dts-tsgo/dist/path-false/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts-tsgo/dist/path-false/esm/foo/foo.d.ts": "import { logRequest } from '@src/logger'; import { logger } from 'prebundle-pkg'; @@ -106,6 +115,7 @@ describe.skipIf(process.version.startsWith('v18'))( export * from './.hidden-folder'; export * from './a.b'; export * from './bar.baz'; + export * from './config'; export * from './foo'; export * from './types'; ", @@ -141,6 +151,10 @@ describe.skipIf(process.version.startsWith('v18'))( "/tests/integration/redirect/dts-tsgo/dist/extension-true/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts-tsgo/dist/extension-true/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts-tsgo/dist/extension-true/esm/config.d.ts": "export * from './config/load.js'; + ", + "/tests/integration/redirect/dts-tsgo/dist/extension-true/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts-tsgo/dist/extension-true/esm/foo/foo.d.ts": "import { logRequest } from '../logger.js'; import { logger } from '../../../../compile/prebundle-pkg'; @@ -165,6 +179,7 @@ describe.skipIf(process.version.startsWith('v18'))( export * from './.hidden-folder/index.js'; export * from './a.b/index.js'; export * from './bar.baz.js'; + export * from './config.js'; export * from './foo/index.js'; export * from './types.js'; ", @@ -200,6 +215,10 @@ describe.skipIf(process.version.startsWith('v18'))( "/tests/integration/redirect/dts-tsgo/dist/path-false-extension-true/esm/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts-tsgo/dist/path-false-extension-true/esm/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts-tsgo/dist/path-false-extension-true/esm/config.d.ts": "export * from './config/load.js'; + ", + "/tests/integration/redirect/dts-tsgo/dist/path-false-extension-true/esm/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts-tsgo/dist/path-false-extension-true/esm/foo/foo.d.ts": "import { logRequest } from '@src/logger'; import { logger } from 'prebundle-pkg'; @@ -224,6 +243,7 @@ describe.skipIf(process.version.startsWith('v18'))( export * from './.hidden-folder/index.js'; export * from './a.b/index.js'; export * from './bar.baz.js'; + export * from './config.js'; export * from './foo/index.js'; export * from './types.js'; ", @@ -259,6 +279,10 @@ describe.skipIf(process.version.startsWith('v18'))( "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/esm/a.b/index.d.mts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/esm/bar.baz.d.mts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/esm/config.d.mts": "export * from './config/load.mjs'; + ", + "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/esm/config/load.d.mts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/esm/foo/foo.d.mts": "import { logRequest } from '../logger.mjs'; import { logger } from '../../../../compile/prebundle-pkg'; @@ -283,6 +307,7 @@ describe.skipIf(process.version.startsWith('v18'))( export * from './.hidden-folder/index.mjs'; export * from './a.b/index.mjs'; export * from './bar.baz.mjs'; + export * from './config.mjs'; export * from './foo/index.mjs'; export * from './types.mjs'; ", @@ -315,6 +340,10 @@ describe.skipIf(process.version.startsWith('v18'))( "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/cjs/a.b/index.d.ts": "export declare const ab = "a.b"; ", "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/cjs/bar.baz.d.ts": "export declare const bar = "bar-baz"; + ", + "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/cjs/config.d.ts": "export * from './config/load.js'; + ", + "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/cjs/config/load.d.ts": "export declare const loadConfig: () => void; ", "/tests/integration/redirect/dts-tsgo/dist/auto-extension-true/cjs/foo/foo.d.ts": "import { logRequest } from '../logger.js'; import { logger } from '../../../../compile/prebundle-pkg'; @@ -339,6 +368,7 @@ describe.skipIf(process.version.startsWith('v18'))( export * from './.hidden-folder/index.js'; export * from './a.b/index.js'; export * from './bar.baz.js'; + export * from './config.js'; export * from './foo/index.js'; export * from './types.js'; ", diff --git a/website/docs/en/config/lib/experiments.mdx b/website/docs/en/config/lib/experiments.mdx index 31d5bd70e..45d245d06 100644 --- a/website/docs/en/config/lib/experiments.mdx +++ b/website/docs/en/config/lib/experiments.mdx @@ -13,7 +13,7 @@ Controls whether to enable Rspack experimental ESM output. When enabled, it emit Currently this option only takes effect in bundle mode when format is `'esm'`. ::: -```js title="rslib.config.js" +```js title="rslib.config.ts" export default { lib: [ { diff --git a/website/docs/zh/config/lib/experiments.mdx b/website/docs/zh/config/lib/experiments.mdx index 545e7c055..aea5ca132 100644 --- a/website/docs/zh/config/lib/experiments.mdx +++ b/website/docs/zh/config/lib/experiments.mdx @@ -13,7 +13,7 @@ 目前该选项仅在 bundle 模式下且 format 为 `'esm'` 时生效。 ::: -```js title="rslib.config.js" +```js title="rslib.config.ts" export default { lib: [ {