-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
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
feat(define): handle replacement with esbuild #11151
Conversation
Co-authored-by: Tony Trinh <tony19@gmail.com>
expect(await transform('const version = __APP_VERSION__;')).toBe( | ||
'const version = "1.0";' | ||
'const version = "1.0";\n' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Esbuild transform tend to semi-format the entire code so this is changed here. That also means we can't do stable sourcemaps transform.
'import.meta.env.': `({}).`, | ||
'import.meta.env': JSON.stringify(config.env), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is fine as esbuild will share a single reference to import.meta.env
object as a variable, so it shouldn't increase the bundle size by much after bundling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: not exactly fine, as in the bundle size will technically increase, but multiple usages of import.meta.env
should be deduplicated now. There isn't a way to fix the former with esbuild define restriction
expect(await page.textContent('.import-meta-env-undefined')).toBe( | ||
isBuild ? '({}).UNDEFINED' : 'import.meta.env.UNDEFINED' | ||
) | ||
expect(await page.textContent('.process-env-undefined')).toBe( | ||
isBuild ? '({}).UNDEFINED' : 'process.env.UNDEFINED' | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test isn't relevant for esbuild transform anymore.
So close 😢 Turns out esbuild strips out comments by default and there's no way to prevent that. That means This currently also happens when the user uses a TypeScript file instead as esbuild transpiles it. Interestingly comments within Blocked by evanw/esbuild#221 |
I realize this is a breaking change as Setting this aside for now as the complexity is growing. |
Adding this one to the Vite 5 milestone, if we move forward with it I think we should break compat. @bluwy maybe we could start issuing a warning for these cases in 4.3 or 4.4 to give time for folks to update their configs? |
is this going into v5 ? |
@@ -38,7 +38,6 @@ export default defineConfig({ | |||
'import.meta.env.VITE_NUMBER': 5173, | |||
'import.meta.env.VITE_STRING': JSON.stringify('string'), | |||
'import.meta.env.VITE_OBJECT_STRING': '{ "foo": "bar" }', | |||
'import.meta.env.VITE_TEMPLATE_LITERAL': '`template literal`', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this handling as it seems that this isn't a valid specifier for esbuild. We also documented this in the docs:
To be consistent with esbuild behavior, expressions must either be a JSON object (null, boolean, number, string, array, or object) or a single identifier.
This was added in #13425
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
/ecosystem-ci run |
📝 Ran ecosystem CI on
|
// may contain values with raw identifiers, making it non-JSON-serializable, | ||
// we replace it with a temporary marker and then replace it back after to | ||
// workaround it. This means that esbuild is unable to optimize the `import.meta.env` | ||
// access, but that's a tradeoff for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is an example of may contain values with raw identifiers
?
What is the cost of esbuild is unable to optimize
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is an example of
may contain values with raw identifiers
?
We have this in plugin-legacy:
vite/packages/plugin-legacy/src/index.ts
Lines 211 to 214 in 297b425
'import.meta.env.LEGACY': | |
env.command === 'serve' || config.build?.ssr | |
? false | |
: legacyEnvVarMarker, |
Where legacyEnvVarMarker
is an __VITE_IS_LEGACY__
identifier to be replaced after transform in renderChunk
. That means when we create a define
for import.meta.env
, the value is { ..., LEGACY: __VITE_IS_LEGACY__ }
which is not valid JSON for esbuild. So we need to work-around it here.
What is the cost of
esbuild is unable to optimize
?
Here's an esbuild example. As you can notice, replacing with a JSON-object vs an identifier, JSON-objects can be deduplicated as a single variable. And perhaps among future optimizations where esbuild could treeshake object key access (now it doesn't).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And you think this would be to breaking to limit Vite define to the esbuild one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand. We would have to continue making our plugins work, and the issue is only specific to import.meta.env
, so a workaround is needed for import.meta.env
specifically. The rest user-facing define
rules still need to follow esbuild's rule.
I wonder if we could have both |
Yeah I've also thought about that, but I think we could hold it for later if there's a common need for it. So far I've not seen many frameworks needing to use |
Something is wrong with the macos test. It's consistently failing with a |
Update: The CI fail is unrelated and fixed at #14751 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is a good idea to merge this one soon if we want it in Vite 5 to get more time to test it
Description
Use esbuild to handle define replacements
fix #9790
fix #3304
fix #8663
fix #6295
fix #9829
fix #6653
supersedes and closes #9791
define
replacements are now replaced with esbuild. This matches the semantics ofdefine
vsreplace
, wheredefine
replace identifiers with a certain value (AST-informed), andreplace
does a simple find-and-replace (not AST-informed).This may break if you specify
define
keys that are not single identifiers, and values that doesn't match esbuild's requirements. This former restriction is a breaking change, but the latter restriction is already documented today. We also (used to) passdefine
to esbuild for the optimizer too.Configurations that didn't follow this rule before used to indirectly work (especially in SSR), and they will not work with this PR. If they still need the previous behaviour, they can use https://www.npmjs.com/package/@rollup/plugin-replace that does
replace
instead.If you've hit with parsing errors because you wrote
import.meta.env.*
in markdown before, this will now fix it.Additional context
Tests are mostly copied from #9791 (Thanks Tony 😬)
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).