diff --git a/e2e/cases/bundle-false/index.test.ts b/e2e/cases/bundle-false/index.test.ts index 48fb66de..daffa7bc 100644 --- a/e2e/cases/bundle-false/index.test.ts +++ b/e2e/cases/bundle-false/index.test.ts @@ -39,3 +39,15 @@ test('single file', async () => { ] `); }); + +test('auto add extension for relative import', async () => { + const fixturePath = join(__dirname, 'relative-import'); + const { contents } = await buildAndGetResults(fixturePath); + + expect(Object.values(contents.esm)[1]).toContain( + 'import * as __WEBPACK_EXTERNAL_MODULE__bar_js__ from "./bar.js";', + ); + expect(Object.values(contents.cjs)[1]).toContain( + 'var external_bar_cjs_namespaceObject = require("./bar.cjs");', + ); +}); diff --git a/e2e/cases/bundle-false/relative-import/package.json b/e2e/cases/bundle-false/relative-import/package.json new file mode 100644 index 00000000..c09fcee2 --- /dev/null +++ b/e2e/cases/bundle-false/relative-import/package.json @@ -0,0 +1,6 @@ +{ + "name": "bundle-false-relative-import-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/e2e/cases/bundle-false/relative-import/rslib.config.ts b/e2e/cases/bundle-false/relative-import/rslib.config.ts new file mode 100644 index 00000000..c5d9857f --- /dev/null +++ b/e2e/cases/bundle-false/relative-import/rslib.config.ts @@ -0,0 +1,18 @@ +import { generateBundleCjsConfig, generateBundleEsmConfig } from '@e2e/helper'; +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig(__dirname, { + bundle: false, + }), + generateBundleCjsConfig(__dirname, { + bundle: false, + }), + ], + source: { + entry: { + main: ['./src/**'], + }, + }, +}); diff --git a/e2e/cases/bundle-false/relative-import/src/bar.ts b/e2e/cases/bundle-false/relative-import/src/bar.ts new file mode 100644 index 00000000..9f173868 --- /dev/null +++ b/e2e/cases/bundle-false/relative-import/src/bar.ts @@ -0,0 +1 @@ +export const bar = 'bar'; diff --git a/e2e/cases/bundle-false/relative-import/src/index.ts b/e2e/cases/bundle-false/relative-import/src/index.ts new file mode 100644 index 00000000..56f92330 --- /dev/null +++ b/e2e/cases/bundle-false/relative-import/src/index.ts @@ -0,0 +1,3 @@ +import { bar } from './bar'; + +export const foo = 'foo' + bar; diff --git a/e2e/cases/bundle-false/relative-import/tsconfig.json b/e2e/cases/bundle-false/relative-import/tsconfig.json new file mode 100644 index 00000000..888d3e46 --- /dev/null +++ b/e2e/cases/bundle-false/relative-import/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["src"] +} diff --git a/e2e/scripts/shared.ts b/e2e/scripts/shared.ts index fa328c6c..2bee9296 100644 --- a/e2e/scripts/shared.ts +++ b/e2e/scripts/shared.ts @@ -55,7 +55,6 @@ type BuildResult = { export async function getResults( rslibConfig: RslibConfig, - fixturePath: string, type: 'js' | 'dts', ): Promise> { const files: Record = {}; @@ -81,7 +80,9 @@ export async function getResults( ignore: ['**/*.map'], }); - const fileSet = Object.keys(content).filter((file) => regex.test(file)); + const fileSet = Object.keys(content) + .filter((file) => regex.test(file)) + .sort(); const filterContent: Record = {}; for (const key of fileSet) { if (content[key]) { @@ -90,7 +91,7 @@ export async function getResults( } if (fileSet.length) { - files[libConfig.format!] = fileSet.sort(); + files[libConfig.format!] = fileSet; contents[libConfig.format!] = filterContent; } @@ -131,8 +132,8 @@ export async function buildAndGetResults( origin: { bundlerConfigs, rsbuildConfig }, } = await rsbuildInstance.inspectConfig({ verbose: true }); if (type === 'all') { - const jsResults = await getResults(rslibConfig, fixturePath, 'js'); - const dtsResults = await getResults(rslibConfig, fixturePath, 'dts'); + const jsResults = await getResults(rslibConfig, 'js'); + const dtsResults = await getResults(rslibConfig, 'dts'); return { js: { contents: jsResults.contents, @@ -155,7 +156,7 @@ export async function buildAndGetResults( }; } - const results = await getResults(rslibConfig, fixturePath, type); + const results = await getResults(rslibConfig, type); return { contents: results.contents, files: results.files, diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 1d065fc1..51fc3768 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -241,6 +241,7 @@ const composeAutoExtensionConfig = ( pkgJson?: PkgJson, ): { config: RsbuildConfig; + jsExtension: string; dtsExtension: string; } => { const { jsExtension, dtsExtension } = getDefaultExtension({ @@ -257,6 +258,7 @@ const composeAutoExtensionConfig = ( }, }, }, + jsExtension, dtsExtension, }; }; @@ -381,7 +383,10 @@ const composeEntryConfig = async ( }; }; -const composeBundleConfig = (bundle = true): RsbuildConfig => { +const composeBundleConfig = ( + jsExtension: string, + bundle = true, +): RsbuildConfig => { if (bundle) return {}; return { @@ -391,7 +396,14 @@ const composeBundleConfig = (bundle = true): RsbuildConfig => { // Issuer is not empty string when the module is imported by another module. // Prevent from externalizing entry modules here. if (data.contextInfo.issuer) { - return callback(null, data.request); + // Node.js ECMAScript module loader does no extension searching. + // So we add a file extension here when data.request is a relative path + return callback( + null, + data.request[0] === '.' + ? `${data.request}${jsExtension}` + : data.request, + ); } callback(); }, @@ -474,9 +486,12 @@ async function composeLibRsbuildConfig( const { format, autoExtension = true, autoExternal = true } = config; const formatConfig = composeFormatConfig(format!); - const { config: autoExtensionConfig, dtsExtension } = - composeAutoExtensionConfig(format!, autoExtension, pkgJson); - const bundleConfig = composeBundleConfig(config.bundle); + const { + config: autoExtensionConfig, + jsExtension, + dtsExtension, + } = composeAutoExtensionConfig(format!, autoExtension, pkgJson); + const bundleConfig = composeBundleConfig(jsExtension, config.bundle); const targetConfig = composeTargetConfig(config.output?.target); const syntaxConfig = composeSyntaxConfig( config.output?.syntax, diff --git a/packages/core/src/utils/helper.ts b/packages/core/src/utils/helper.ts index b4d43352..58585736 100644 --- a/packages/core/src/utils/helper.ts +++ b/packages/core/src/utils/helper.ts @@ -71,7 +71,7 @@ export const nodeBuiltInModules: Array = [ 'pnpapi', ]; -async function calcLongestCommonPath( +export async function calcLongestCommonPath( absPaths: string[], ): Promise { if (absPaths.length === 0) { @@ -126,4 +126,4 @@ export const readPackageJson = (rootPath: string): undefined | PkgJson => { export const isObject = (obj: unknown): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; -export { color, calcLongestCommonPath }; +export { color }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3132490..3f70aab7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -138,6 +138,8 @@ importers: specifier: ^18.3.1 version: 18.3.1 + e2e/cases/bundle-false/relative-import: {} + e2e/cases/dts/bundle-false/auto-extension: {} e2e/cases/dts/bundle/abort-on-error: {}