Skip to content

Commit 0f09c40

Browse files
authored
Turbopack: Better error for sassOptions.functions as it's unsupported (#85073)
## What? Implements an error for the case where Turbopack is used with sassOptions.functions. Fixes #65241
1 parent 700fecf commit 0f09c40

File tree

5 files changed

+85
-12
lines changed

5 files changed

+85
-12
lines changed

docs/01-app/03-api-reference/05-config/01-next-config-js/sassOptions.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ const nextConfig = {
4343
module.exports = nextConfig
4444
```
4545

46-
> **Good to know:** `sassOptions` are not typed outside of `implementation` because Next.js does not maintain the other possible properties.
46+
> **Good to know:**
47+
>
48+
> - `sassOptions` are not typed outside of `implementation` because Next.js does not maintain the other possible properties.
49+
> - The `functions` property for defining custom Sass functions is only supported with webpack. When using Turbopack, custom Sass functions are not available because Turbopack's Rust-based architecture cannot directly execute JavaScript functions passed through this option.

docs/01-app/03-api-reference/08-turbopack.mdx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,16 @@ Turbopack in Next.js has **zero-configuration** for the common use cases. Below
7272

7373
### CSS and styling
7474

75-
| Feature | Status | Notes |
76-
| ------------------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77-
| **Global CSS** | **Supported** | Import `.css` files directly in your application. |
78-
| **CSS Modules** | **Supported** | `.module.css` files work natively (Lightning CSS). |
79-
| **CSS Nesting** | **Supported** | Lightning CSS supports [modern CSS nesting](https://lightningcss.dev/). |
80-
| **@import syntax** | **Supported** | Combine multiple CSS files. |
81-
| **PostCSS** | **Supported** | Automatically processes `postcss.config.js` in a Node.js worker pool. Useful for Tailwind, Autoprefixer, etc. |
82-
| **Sass / SCSS** | **Supported** (Next.js) | For Next.js, Sass is supported out of the box. In the future, Turbopack standalone usage will likely require a loader config. |
83-
| **Less** | Planned via plugins | Not yet supported by default. Will likely require a loader config once custom loaders are stable. |
84-
| **Lightning CSS** | **In Use** | Handles CSS transformations. Some low-usage CSS Modules features (like `:local/:global` as standalone pseudo-classes) are not yet supported. [See below for more details.](#unsupported-and-unplanned-features) |
75+
| Feature | Status | Notes |
76+
| ------------------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77+
| **Global CSS** | **Supported** | Import `.css` files directly in your application. |
78+
| **CSS Modules** | **Supported** | `.module.css` files work natively (Lightning CSS). |
79+
| **CSS Nesting** | **Supported** | Lightning CSS supports [modern CSS nesting](https://lightningcss.dev/). |
80+
| **@import syntax** | **Supported** | Combine multiple CSS files. |
81+
| **PostCSS** | **Supported** | Automatically processes `postcss.config.js` in a Node.js worker pool. Useful for Tailwind, Autoprefixer, etc. |
82+
| **Sass / SCSS** | **Supported** (Next.js) | For Next.js, Sass is supported out of the box. Custom Sass functions (`sassOptions.functions`) are not supported because Turbopack's Rust-based architecture cannot directly execute JavaScript functions, unlike webpack's Node.js environment. Use webpack if you need this feature. In the future, Turbopack standalone usage will likely require a loader config. |
83+
| **Less** | Planned via plugins | Not yet supported by default. Will likely require a loader config once custom loaders are stable. |
84+
| **Lightning CSS** | **In Use** | Handles CSS transformations. Some low-usage CSS Modules features (like `:local/:global` as standalone pseudo-classes) are not yet supported. [See below for more details.](#unsupported-and-unplanned-features) |
8585

8686
### Assets
8787

@@ -175,6 +175,8 @@ Some features are not yet implemented or not planned:
175175
- `:import` and `:export` ICSS rules.
176176
- `composes` in `.module.css` composing a `.css` file. In webpack this would treat the `.css` file as a CSS Module, with Turbopack the `.css` file will always be global. This means that if you want to use `composes` in a CSS Module, you need to change the `.css` file to a `.module.css` file.
177177
- `@import` in CSS Modules importing `.css` as a CSS Module. In webpack this would treat the `.css` file as a CSS Module, with Turbopack the `.css` file will always be global. This means that if you want to use `@import` in a CSS Module, you need to change the `.css` file to a `.module.css` file.
178+
- **`sassOptions.functions`**
179+
Custom Sass functions defined in `sassOptions.functions` are not supported. This feature allows defining JavaScript functions that can be called from Sass code during compilation. Turbopack's Rust-based architecture cannot directly execute JavaScript functions passed through `sassOptions.functions`, unlike webpack's Node.js-based sass-loader which runs entirely in JavaScript. If you're using custom Sass functions, you'll need to use webpack instead of Turbopack.
178180
- **`webpack()` configuration** in `next.config.js`
179181
Turbopack replaces webpack, so `webpack()` configs are not recognized. Use the [`turbopack` config](/docs/app/api-reference/config/next-config-js/turbopack) instead.
180182
- **Yarn PnP**

packages/next/errors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,5 +890,6 @@
890890
"889": "Unknown \\`cacheLife()\\` profile \"%s\" is not configured in next.config.js\\nmodule.exports = {\n cacheLife: {\n \"%s\": ...\\n }\n}",
891891
"890": "Received an underlying cookies object that does not match either `cookies` or `mutableCookies`",
892892
"891": "Failed to read build paths file \"%s\": %s",
893-
"892": "Failed to resolve glob pattern \"%s\": %s"
893+
"892": "Failed to resolve glob pattern \"%s\": %s",
894+
"893": "The \"sassOptions.functions\" option is not supported when using Turbopack. Custom Sass functions are only available with webpack. Please remove the \"functions\" property from your sassOptions in %s."
894895
}

packages/next/src/server/config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,19 @@ function assignDefaultsAndValidate(
378378
)
379379
}
380380

381+
// Validate sassOptions.functions is not used with Turbopack
382+
if (
383+
process.env.TURBOPACK &&
384+
result.sassOptions &&
385+
'functions' in result.sassOptions
386+
) {
387+
throw new Error(
388+
`The "sassOptions.functions" option is not supported when using Turbopack. ` +
389+
`Custom Sass functions are only available with webpack. ` +
390+
`Please remove the "functions" property from your sassOptions in ${configFileName}.`
391+
)
392+
}
393+
381394
if (isStableBuild()) {
382395
// Prevents usage of certain experimental features outside of canary
383396
if (result.experimental?.turbopackFileSystemCacheForBuild) {

test/unit/isolated/config.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,60 @@ describe('config', () => {
102102
)
103103
})
104104

105+
it('Should throw an error when sassOptions.functions is used with Turbopack', async () => {
106+
const originalTurbopack = process.env.TURBOPACK
107+
process.env.TURBOPACK = '1'
108+
109+
try {
110+
await expect(async () => {
111+
// Use a unique directory to avoid cache conflicts
112+
await loadConfig(PHASE_DEVELOPMENT_SERVER, '<rootDir>-turbopack-test', {
113+
customConfig: {
114+
sassOptions: {
115+
functions: {
116+
'get($keys)': function (keys) {
117+
return 'test'
118+
},
119+
},
120+
},
121+
},
122+
})
123+
}).rejects.toThrow(
124+
/The "sassOptions\.functions" option is not supported when using Turbopack/
125+
)
126+
} finally {
127+
if (originalTurbopack === undefined) {
128+
delete process.env.TURBOPACK
129+
} else {
130+
process.env.TURBOPACK = originalTurbopack
131+
}
132+
}
133+
})
134+
135+
it('Should allow sassOptions.functions when not using Turbopack', async () => {
136+
const originalTurbopack = process.env.TURBOPACK
137+
delete process.env.TURBOPACK
138+
139+
try {
140+
const config = await loadConfig(PHASE_DEVELOPMENT_SERVER, '<rootDir>', {
141+
customConfig: {
142+
sassOptions: {
143+
functions: {
144+
'get($keys)': function (keys) {
145+
return 'test'
146+
},
147+
},
148+
},
149+
},
150+
})
151+
expect((config as any).sassOptions.functions).toBeDefined()
152+
} finally {
153+
if (originalTurbopack !== undefined) {
154+
process.env.TURBOPACK = originalTurbopack
155+
}
156+
}
157+
})
158+
105159
it('Should not throw an error when two versions of next.config.js are present', async () => {
106160
const config = await loadConfig(
107161
PHASE_DEVELOPMENT_SERVER,

0 commit comments

Comments
 (0)