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:
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").
esquery — resolves to its ESM module entry (default-only namespace), so esquery.parse/.matches are undefined ("r.parse is not a function").
- 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()).
Problem
vinext silently drops the
next.configwebpackblock (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 anIgnorePluginforeslint/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:assert— Vite maps the Nodeassertbuiltin to its empty__vite-browser-externalstub, so eslint-scope'sassert()is not callable ("r is not a function").esquery— resolves to its ESMmoduleentry (default-only namespace), soesquery.parse/.matchesareundefined("r.parse is not a function").eslint/lib/rules/index.jsand 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:assertserved as a virtual CommonJS module (a.cjsfile is rejected by rolldown after vite-plugin-commonjs injectsexport; a.jswithmodule.exportsbundles as ESM sorequire('assert')gets a non-callable namespace — only a virtual CJS module makesrequire('assert')return the callable).esqueryaliased toesquery/dist/esquery.min.js(CJS).resolveIdplugin stubbing bareeslint/lib/rules/*→export default {}(1.17 MB → 542 KB).Suggested fix
Either support a subset of
next.configwebpack(at leastresolve.alias), or document the webpack→Vite migration for these common shims, and consider shipping a callableassertbrowser polyfill (Vite's empty stub silently breaks any dep that callsassert()).