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

Add turbo.define option #71802

Open
wants to merge 2 commits into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/02-app/02-api-reference/05-next-config-js/turbo.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ The following options are available for the `turbo` configuration:
| `useSwcCss` | Use `swc_css` instead of `lightningcss` for Turbopack |
| `treeshaking` | Enable tree shaking for the turbopack dev server and build. |
| `memoryLimit` | A target memory limit for turbo, in bytes. |
| `define` | Replace variables in your code during the build. |

### Supported loaders

Expand Down Expand Up @@ -168,6 +169,24 @@ module.exports = {
}
```

### Replacing variables during build

The `define` option allows you to statically replace variables in your code at build-time.
The option takes an object as key-value pairs, where the keys are the variables that should be replaced with the corresponding values.

Use the `define` field in `next.config.js`:

```js filename="next.config.js"
module.exports = {
experimental: {
turbo: {
MY_STRING_VARIABLE: JSON.stringify('my-string'),
MY_NUMBER_VARIABLE: '42',
},
},
}
```

## Version History

| Version | Changes |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function getDefineEnv({
}),
'process.turbopack': isTurbopack,
'process.env.TURBOPACK': isTurbopack,
...(isTurbopack ? config.experimental.turbo?.define : {}),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be handled slightly differently to make sure it doesn't add keys that conflict with built-in features. Something like this to error:

const userDefines = isTurbopack ? config.experimental.turbo?.define : {}
for (const key in userDefines) {
	if(defineEnv.hasOwnProperty(key)) {
		throw new Error('descriptive error message about the defined key trying to override a built-in value')
	}
        
   	defineEnv[key] = userDefines[key]
}

// TODO: enforce `NODE_ENV` on `process.env`, and add a test:
'process.env.NODE_ENV':
dev || config.experimental.allowDevelopmentBuild
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ export const configSchema: zod.ZodType<NextConfig> = z.lazy(() =>
.optional(),
memoryLimit: z.number().optional(),
moduleIdStrategy: z.enum(['named', 'deterministic']).optional(),
define: z.record(z.string(), z.string()).optional(),
})
.optional(),
optimizePackageImports: z.array(z.string()).optional(),
Expand Down
6 changes: 6 additions & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ export interface ExperimentalTurboOptions {
* directory can be resolved by turbopack.
*/
root?: string

/**
* Replaces variables in your code during compile time. Each key will be
* replaced with the respective values.
*/
define?: Record<string, string>
}

export interface WebpackConfigContext {
Expand Down
9 changes: 9 additions & 0 deletions test/e2e/define/app/client-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use client'

declare const MY_MAGIC_VARIABLE: string

export function ClientValue() {
return (
<>{typeof MY_MAGIC_VARIABLE === 'string' ? MY_MAGIC_VARIABLE : 'not set'}</>
)
}
7 changes: 7 additions & 0 deletions test/e2e/define/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html>
<body>{children}</body>
</html>
)
}
17 changes: 17 additions & 0 deletions test/e2e/define/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ClientValue } from './client-component'

declare const MY_MAGIC_VARIABLE: string

export default function Page() {
return (
<ul>
<li>
Server value:{' '}
{typeof MY_MAGIC_VARIABLE === 'string' ? MY_MAGIC_VARIABLE : 'not set'}
</li>
<li>
Client value: <ClientValue />
</li>
</ul>
)
}
21 changes: 21 additions & 0 deletions test/e2e/define/define.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { nextTestSetup } from 'e2e-utils'

describe('define', () => {
const { next } = nextTestSetup({
files: __dirname,
})

it('should render the magic variable on server side', async () => {
const res = await next.fetch('/')
const html = (await res.text()).replaceAll(/<!-- -->/g, '')
expect(html).toContain('Server value: foobar')
expect(html).toContain('Client value: foobar')
})

it('should render the magic variable on client side', async () => {
const browser = await next.browser('/')
const text = await browser.elementByCss('body').text()
expect(text).toContain('Server value: foobar')
expect(text).toContain('Client value: foobar')
})
})
9 changes: 9 additions & 0 deletions test/e2e/define/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
experimental: {
turbo: {
define: {
MY_MAGIC_VARIABLE: JSON.stringify('foobar'),
},
},
},
}
Loading