Skip to content

next.config webpack block silently dropped → in-browser ESLint linter breaks (assert stub, esquery ESM, rule bloat) #7

Description

@hyf0

Problem

vinext silently drops the next.config webpack block (logs [vinext] next.config option "webpack" is not yet supported and will be ignored). For react.dev this breaks the in-browser ESLint linter (the live rules-of-hooks / exhaustive-deps markers in editable Sandpacks).

react.dev's webpack block provided: resolve.alias (use-sync-external-store/shim→react, esquery→its CJS build), NormalModuleReplacement (raf, process), and an IgnorePlugin for eslint/lib/rules/*. With these gone:

Symptoms (verified with an in-chunk probe)

runESLint() returns {fatal:true, message:"Parsing error: <x> is not a function"} for all input. espree parses fine; the throw is post-parse:

  1. assert — Vite maps the Node assert builtin to its empty __vite-browser-external stub, so eslint-scope's assert() is not callable ("r is not a function").
  2. esquery — resolves to its ESM module entry (default-only namespace), so esquery.parse/.matches are undefined ("r.parse is not a function").
  3. bundle size — without the IgnorePlugin, rolldown follows eslint/lib/rules/index.js and bundles all ~284 core rules (1.17 MB) into the lazy chunk, though only react-hooks rules are enabled.

Workaround used (vite.config)

  • assert/node:assert served as a virtual CommonJS module (a .cjs file is rejected by rolldown after vite-plugin-commonjs injects export; a .js with module.exports bundles as ESM so require('assert') gets a non-callable namespace — only a virtual CJS module makes require('assert') return the callable).
  • esquery aliased to esquery/dist/esquery.min.js (CJS).
  • a resolveId plugin stubbing bare eslint/lib/rules/*export default {} (1.17 MB → 542 KB).

Suggested fix

Either support a subset of next.config webpack (at least resolve.alias), or document the webpack→Vite migration for these common shims, and consider shipping a callable assert browser polyfill (Vite's empty stub silently breaks any dep that calls assert()).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3: lowLow priority / deferarea: configConfig / module resolutionupstream: app-ownedKeep the app workaround; not a vinext fix

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions