diff --git a/src/constants.ts b/src/constants.ts index 6fddf155..d7c13f8e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -35,15 +35,16 @@ export const runtimeExportConventionsFallback = new Map([ // ESM only runtime ['workerd', ['import', 'default']], ['edge-light', ['import', 'default']], - ['browser', ['import', 'default']], - - // it could be CJS or ESM + // Fallback to default when unsure ['electron', ['default']], ['react-server', ['default']], ['react-native', ['default']], ['node', ['default']], ['deno', ['default']], ['bun', ['default']], + ['development', ['default']], + ['production', ['default']], + ['browser', ['import', 'require', 'default']], ]) export const optimizeConventions = new Set(['development', 'production']) export const specialExportConventions = new Set([ diff --git a/src/plugins/alias-plugin.ts b/src/plugins/alias-plugin.ts index c1cf6d89..9385e9e2 100644 --- a/src/plugins/alias-plugin.ts +++ b/src/plugins/alias-plugin.ts @@ -26,7 +26,7 @@ function findJsBundlePathCallback( conditionNames: Set }, specialCondition: string, -) { +): boolean { const hasBundle = bundlePath != null const formatCond = format === 'cjs' ? 'require' : 'import' @@ -34,6 +34,9 @@ function findJsBundlePathCallback( const hasFormatCond = conditionNames.has('import') || conditionNames.has('require') + // Check if the format condition is matched: + // if there's condition existed, check if the format condition is matched; + // if there's no condition, just return true, assuming format doesn't matter; const isMatchedFormat = hasFormatCond ? conditionNames.has(formatCond) : true const isMatchedConditionWithFormat = @@ -50,7 +53,16 @@ function findJsBundlePathCallback( if (!fallback) { return false } else { - return fallback.some((name) => conditionNames.has(name)) + // Match its own condition first, + // e.g. when import utils.js in index.js + // In output: index.browser.js should match util.browser.js, fallback to util.js + // The last guard condition is to ensure bundle condition but not types file. + return ( + isMatchedFormat && + (conditionNames.has(specialCondition) || + fallback.some((name) => conditionNames.has(name))) && + !conditionNames.has('types') + ) } } else { return match diff --git a/test/fixtures/integration-test-template/src/index.js b/test/fixtures/integration-test-template/src/index.js index d32a1bca..6de1f5a8 100644 --- a/test/fixtures/integration-test-template/src/index.js +++ b/test/fixtures/integration-test-template/src/index.js @@ -1,3 +1,3 @@ -export function Foo() { - return

Foo

+export function foo() { + return 'foo' } diff --git a/test/integration/shared-module-special-condition/package.json b/test/integration/shared-module-special-condition/package.json new file mode 100644 index 00000000..93f910e7 --- /dev/null +++ b/test/integration/shared-module-special-condition/package.json @@ -0,0 +1,13 @@ +{ + "name": "shared-module-special-condition", + "main": "./dist/index.js", + "type": "module", + "exports": { + ".": { + "import": "./dist/index.js", + "development": "./dist/index.development.js", + "production": "./dist/index.production.js", + "browser": "./dist/index.browser.js" + } + } +} diff --git a/test/integration/shared-module-special-condition/shared-module-special-condition.test.ts b/test/integration/shared-module-special-condition/shared-module-special-condition.test.ts new file mode 100644 index 00000000..6a059ec7 --- /dev/null +++ b/test/integration/shared-module-special-condition/shared-module-special-condition.test.ts @@ -0,0 +1,19 @@ +import { createJob, getFileContents } from '../../testing-utils' + +describe('integration - shared-module-special-condition', () => { + const { distDir } = createJob({ + directory: __dirname, + }) + it('should work', async () => { + const contents = await getFileContents(distDir) + const files = [ + 'index.development.js', + 'index.production.js', + 'index.browser.js', + ] + files.forEach((file) => { + expect(contents[file]).toContain('./_util.js') + expect(contents[file]).not.toContain('./_util.ts') + }) + }) +}) diff --git a/test/integration/shared-module-special-condition/src/_util.ts b/test/integration/shared-module-special-condition/src/_util.ts new file mode 100644 index 00000000..d4f000a6 --- /dev/null +++ b/test/integration/shared-module-special-condition/src/_util.ts @@ -0,0 +1 @@ +export const sharedValue = 'shared-value-text' diff --git a/test/integration/shared-module-special-condition/src/index.ts b/test/integration/shared-module-special-condition/src/index.ts new file mode 100644 index 00000000..5b6f7751 --- /dev/null +++ b/test/integration/shared-module-special-condition/src/index.ts @@ -0,0 +1,5 @@ +import { sharedValue } from './_util' + +export function index() { + return process.env.NODE_ENV + sharedValue +} diff --git a/test/testing-utils/helpers.ts b/test/testing-utils/helpers.ts index 8c779766..28163135 100644 --- a/test/testing-utils/helpers.ts +++ b/test/testing-utils/helpers.ts @@ -44,7 +44,7 @@ type FunctionCondition = (content: string) => Boolean export async function getFileContents(dir: string, filePaths?: string[]) { const results: Record = {} - const files = filePaths || (await fsp.readdir(dir)) + const files = filePaths || (await fsp.readdir(dir, { recursive: true })) for (const file of files) { const fullPath = path.resolve(dir, file) const content = await fsp.readFile(fullPath, { encoding: 'utf-8' })