Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate exports via export * which map to the same externalized implementation disappear from output #2658

Open
dzearing opened this issue Nov 4, 2022 · 10 comments

Comments

@dzearing
Copy link

dzearing commented Nov 4, 2022

Repro (not this is not specific to a library, just using my case as an example):

index.js: export * from './path1.js'; export * from './path2.js';
path1.js: export { DateTime } from '@fluentui/date-time-utilities';
path2.js: export { DateTime } from '@fluentui/date-time-utilities';

In this setup, the single index barrel file exports both path1 and path2. Each of them export a single named export from a library. This does mean that DateTime is exported twice, but resolves to the same symbolic representation.

This works fine in a normal bundle, but when the library is externalized, this export disappears completely.

Expected:

Running this:

esbuild index.js --bundle --external:@fluentui/date-time-utilities --format=esm --outdir=dist2

Should emit this in the output:

export { DateTime } from '@fluentui/date-time-utilities';

Repros with 0.15.13.

@dzearing
Copy link
Author

dzearing commented Nov 5, 2022

Strangely, this throws an error:

index.js: export { DateTime } from './DateTime.js';
DateTime.js: export * from './path1.js'; export * from './path2.js';
path1.js: export { DateTime } from '@fluentui/date-time-utilities';
path2.js: export { DateTime } from '@fluentui/date-time-utilities';

If instead of the barrel file exporting *, we export * a level deeper, esbuild throws this error:

X [ERROR] Ambiguous import "DateTime" has multiple matching exports

Which also seems wrong, because the named export DateTime comes from the same externalized library.

@hyrious
Copy link

hyrious commented Nov 5, 2022

The problem is caused by esbuild not supporting merging duplicated imports.

@dzearing
Copy link
Author

dzearing commented Nov 5, 2022

Well specifically, duplicated external exports. If the library is not externalized, then esbuild handles it correctly. (at least the original case, i haven't tried the nested duplication.)

@hinell
Copy link

hinell commented Nov 5, 2022

@dzearing I have the following output for two different runs of the esbuild v0.15.13 As you can see FOO is got deduped but not exported.

Inputs

// a.js 
export * from './x.js';
export * from './y.js';
// x.js
export { FOO, BAR } from '@external';
// y.js
export { FOO, BAZ } from '@external';

Outputs

$ esbuild a.js --bundle --external:@external --format=esm --outdir=dist/
$ cat dist/a.js
//dist/a.js
// x.js
import { FOO, BAR } from "@external";

// y.js
import { FOO as FOO2, BAZ } from "@external";
export {
  BAR,
  BAZ
};
$ esbuild a.js --format=esm --outdir=dist/
$ cat dist/a.js
// dist/a.js
export * from "./x.js";
export * from "./y.js";
Reproduce
Quick dirty script to reproduce the issue
#!/usr/bin/env bash

TEMPDIR="/tmp/tmp.caNZEeqAHT"

[[ -d $TEMPDIR ]] || mkdir "${TEMPDIR}";
pushd ${TEMPDIR}

[[ -d node_modules/esbuild ]] || pnpm i esbuild;

PATHEXTERNAL=node_modules/@external
[[ -d $PATHEXTERNAL ]] || {
  mkdir $PATHEXTERNAL
}
pushd node_modules/@external
tee index.js <<-EOL
export let FOO = function(){}
export let BAR = "Bar"
export class BAZ {}
EOL
  popd



tee a.js <<EOL
export * from './x.js';
export * from './y.js';
EOL

tee x.js <<EOL
export { FOO, BAR } from '@external';
EOL

tee y.js <<EOL
export { FOO, BAZ } from '@external';
EOL

echo
echo "esbuild version = $(node_modules/.bin/esbuild --version)"

node_modules/.bin/esbuild a.js --bundle --external:@external --format=esm --outdir=dist/
echo "// esbuild a.js --bundle --external:@external --format=esm --outdir=dist/"
echo "// dist/a.js"
cat dist/a.js

node_modules/.bin/esbuild a.js --format=esm --outdir=dist/
echo "// esbuild a.js --format=esm --outdir=dist/"
echo "// dist/a.js"
cat dist/a.js

popd

@hyrious
Copy link

hyrious commented Nov 5, 2022

@hinell FOO is missing, which is not being "deduped", it is wrong.

Possible correct output: Rollup Repl

export { BAR, BAZ, FOO } from '@external';

@hyrious
Copy link

hyrious commented Nov 5, 2022

@hinell It was renamed but is missing from the exported names (i.e. missing export { FOO }). Considering some other guy is importing FOO from your a.js:

import { FOO } from './dist/a.js'

The code will fail because the runtime cannot find the name.

@hinell
Copy link

hinell commented Nov 5, 2022

@hyrious Yeah, FOO is not exported by the dist/a.js. It got renamed but not exported eventually. This is a definitely a bug.

@dzearing
Copy link
Author

dzearing commented Nov 6, 2022

Yup @hyrious you got it. I expect FOO, BAR, and BAZ to be exported in that case.

This is actually what we've been observing. Usually, you don't have this case where two separate star exports emit the same name, so... most of the time things work. In the above case, the non-duplicated keys (BAR and BAZ) are emitted, but the duplicate FOO is imported twice, and then unused and un-exported.

@hinell
Copy link

hinell commented Sep 12, 2023

Any progress on this? esbuild should be able to recognize the same module and mrge all its exports requested by the importer.

@lukeed
Copy link
Contributor

lukeed commented Sep 12, 2023

~> esbuild REPL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants