Files
next.js/test/e2e/turbopack-loader-config/webpack-loader-replace-with-stub.cjs
Benjamin WoodruffandGitHub 628540fbbc Turbopack: Add new webpack loader rule/condition syntax in config (#82857)
# This PR:

- [x] Allow a condition to be specified inline within a `rule`
- [x] Support `all`/`any`/`not` for conditions
- [x] Simple flattening/optimization logic for the generated `RuleCondition` objects
  - [x] Unit test coverage of flattening logic
- [x] Allow builtin conditions (see https://github.com/vercel/next.js/pull/82765) to be specified in condition objects, instead of requiring a separate syntax for them
- [x] Update regex serialization logic in `packages/next/src/build/swc/index.ts`
- [x] Make sure the updated syntax is fully reflected in the config schema and typedefs
- [x] Emit collectibles for configuration issues instead of hard-failing with anyhow
- [x] e2e test coverage

# Remaining Work for Subsequent PRs

- [ ] Remove the legacy `conditions` field from the `turbopack` config object
- [ ] Remove the legacy built-in condition object syntax from rules: https://github.com/vercel/next.js/pull/83068
- [ ] Allow array values in the `turbopack.rules` object: https://github.com/vercel/next.js/pull/83138
- [ ] Update user-facing documentation

This PR is the bulk of the work towards implementing the proposed new loader rule syntax:
https://www.notion.so/vercel/Turbopack-loader-rule-syntax-254e06b059c4809096d1e7c9afad278c
*(copied below)*

# Loader Syntax Proposal

## Today (documented)

[https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack#configuring-webpack-loaders](https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack#configuring-webpack-loaders)

```javascript
module.exports = {
  turbopack: {
    rules: {
      // all matched rules apply, adding their loaders in order
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },
}
```

## Today (undocumented) - User-defined conditions

You can define custom `#`-prefixed “conditions” that can act as filters for rules.

```javascript
module.exports = {
  turbopack: {
    rules: {
      '#svg-file': {
        loaders: ['@svgr/webpack'],
        // `as` is incompatible! There's no obvious glob to match against!
      },
    },
    conditions: {
      "#svg-file": {
        // this is an implicit "and"/"all": both path and content must match
        path: '*.svg', // can also be a RegExp
        content: /\<svg[^\>]*\>/, // can match the contents of the file!
      },
    },
  },
}
```

## Today (undocumented) - Built-in Conditions

These use a different syntax, and are provided by Next.js / Turbopack (are not user-defined).

```javascript
module.exports = {
  turbopack: {
    rules: {
      // This will match svg files in the project that aren't from `node_modules`
      '*.svg': {
        // These object properties are evaluated in order, and the first match
        // used. The supported keys can be found here:
        // https://github.com/vercel/next.js/pull/82765
        foreign: false, // causes us to bail out if node_modules are matched
        default: {
          loaders: ['@svgr/webpack'],
          as: '*.js',
        }
      },
    },
  },
}
```

## Proposed

- Remove (minor breaking change in Next 16, was undocumented) the `conditions` field from the `turbopack` config object. The benefit of a separate `conditions` field was that named conditions could be re-used across rules. But, there’s not much value in allowing condition re-use as the configuration file is JS and could implement re-use with local variables or functions.
    - The separate `conditions` field was also incompatible with the `as` field.
- Add an optional `condition` field to each rule object.
    - Rules must be matched by a glob first (can be `*`) before the condition is checked.
    - This allows compatibility with `as`, since there’s always an obvious glob for that to match against.
- Maintain support for `path` and `content` properties in `condition` objects.
    - These can be used with each other (as an implicit `all`)
- Extend `condition` object with `{all: [...]}`, `{any: [...]}` , and `{not: ...}`.
    - Similar to webpack’s `{and: [...]}`, `{or: [...]}` and `{not: ...}` syntax: https://webpack.js.org/configuration/module/#condition
    - Our underlying `RuleCondition` enum already supports this, it’s just a matter of translating it from the JS object.
- Allow conditions to be an object or a string. If it’s a string, require it to be a built-in condition (like `'foreign'` or `'browser'`).
- Allow values in the rules object to either be objects (what we support today) or an array of objects.

```javascript
module.exports = {
  turbopack: {
    rules: {
      // all matched rules apply, adding their loaders in order
      '*.svg': [
        // this optionally can be an array of objects (shown here), or just an object
        {
          // this condition is matched after the glob
          condition: {
            all: [
              { not: 'foreign' }, // excludes `node_modules`
              { path: /app\/.*\.svg/, content: /\<svg[^\>]*\>/ },
            ],
          },
          loaders: ['@svgr/webpack'],
          as: '*.js',
        },
      ],
    },
  },
}
```
2025-08-28 19:49:57 -07:00

9 lines
316 B
JavaScript

module.exports = function (content, _map, _meta) {
const options = this.getOptions()
if (!content.includes('untransformed')) {
throw new Error('loader matched multiple times')
}
const returnValue = options?.returnValue ?? 'default return value'
return `export default ${JSON.stringify(returnValue)}`
}