Turbopack: cross-module constants (#90300)
Closes https://github.com/vercel/next.js/issues/92082
This is now a proper compile-time constant:
```js
import { IS_DEV } from './other'
if (IS_DEV) { // statically evaluates to `true`
console.log('x')
} else {
require("library") // not bundled
}
console.log(IS_DEV); // is replaced with console.log(true);
```
```typescript
// other.ts
export const SOME_VALUE = 'x'
const node_env = process.env.NODE_ENV
const development_ent = 'development'
export const IS_DEV = node_env === development_ent
```
You can use code to compute constants just fine, and use any existing
constants such as `process.env.NODE_ENV`.
Currently, you can't use imports to other constants modules, but we can
add that later on.
We can't perform this constants check for every single import, so you
need to either
- have `UPPER_CASE` import names as in the example above
- or use `import { lower } from './other' with { turbopackConstants:
'true' }`
Then that referenced module will be analyzed for constants, and if the
referenced export is a constant, it will participate in constant
inlining just as `process.env.FOO`.
This also works fine with barrel imports, you can still do `import {
IS_DEV } from './barrel.js'` and it will find the `constants.js` file
which in itself will indeed only have constants exports.
Because it's not always opt-in at the import site, we can't
automatically make non-constant exports an error. For that you have to
add `'use turbopack: constants'` at the top of the module, which will
make it an error if any constant import references that module, and the
module has any non-constant exports:
```
error - [analysis] /turbopack/crates/turbopack-tests/tests/snapshot/comptime/cross-module/input/other.constants.js:8:7
Export NO_CONSTANT is not a constant
4 | const development_ent = 'development'
5 |
6 | export const IS_DEV = node_env === development_ent
7 |
+ v--------------------------------v
8 + export const NO_CONSTANT = globalThis.foo
+ ^--------------------------------^
9 |
It was analyzed to be FreeVar(globalThis)["foo"]
Import trace:
test:
./turbopack/crates/turbopack-tests/tests/snapshot/comptime/cross-module/input/other.constants.js
./turbopack/crates/turbopack-tests/tests/snapshot/comptime/cross-module/input/index.js
```
Some prior art:
https://rspack.rs/blog/announcing-1-5#const-inline-optimization,
https://rspack.rs/config/optimization#optimizationinlineexports
No compile-time impact on a big app:
```
canary dfbc3dc6b7:
438.11s user, 68.35s system, 801% cpu, 1:03.22 total
439.44s user, 71.47s system, 756% cpu, 1:07.56 total
440.32s user, 68.57s system, 750% cpu, 1:07.81 total
constants 2d7eb8220d298133212813b7267a5847eed03a22
433.22s user, 65.44s system, 800% cpu, 1:02.32 total
440.52s user, 67.68s system, 770% cpu, 1:05.95 total
433.13s user, 69.47s system, 776% cpu, 1:04.74 total
```
a8620556af64ef550373cca1e1fc701a1a8583cc