Skip to content

feat(init): detect css modules and auto-install compatibility workarounds - #3059

Open
NriotHrreion wants to merge 23 commits into
cloudflare:mainfrom
NriotHrreion:feature/vinext-init-css-modules-support
Open

NriotHrreion wants to merge 23 commits into
cloudflare:mainfrom
NriotHrreion:feature/vinext-init-css-modules-support

Conversation

@NriotHrreion

@NriotHrreion NriotHrreion commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Closes #2992

Overview

Vite scopes CSS Modules before running user PostCSS plugins, which differs from Next.js and can silently break transformations that depend on original selectors, such as postcss-extend-rule. vite-css-modules restores the expected processing order, but its default class-name hashing can produce different results across vinext's separate server and client environments (see #2992 (comment)).

This PR makes CSS Modules projects work consistently by automatically setting up vite-css-modules and deterministic, project-relative class names, preserving PostCSS behavior while preventing server/client class-name mismatches in development and production builds.

What changed

  • During vinext init, scan the original Next.js project folder for css modules, if there is, enable the css modules compatibility setup path.
  • vite-css-modules will be installed as a devDependency.
  • vite-css-modules will be added to vite config as a plugin, along with the generateScopedName() config.
  • Edge cases of creating and modifying vite config file are considered and added to the tests.

Verification

Question

Should we add an option (maybe a cli flag) to let developers decide whether to apply the css modules compatibility setup?

@pkg-pr-new

pkg-pr-new Bot commented Aug 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@3059
npm i https://pkg.pr.new/create-vinext-app@3059
npm i https://pkg.pr.new/@vinext/types@3059
npm i https://pkg.pr.new/vinext@3059

commit: 1645f3b

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 1645f3b against base 86afc11 using alternating same-runner rounds. Next.js was unchanged and skipped.

1 improved · 0 regressed · 5 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 142.4 KB 142.4 KB ⚫ -0.0%
Client entry size (gzip) vinext 129.7 KB 129.7 KB ⚫ -0.0%
Dev server cold start vinext 3.37 s 3.32 s 🟢 -1.6%
Production build time vinext 3.75 s 3.75 s ⚫ -0.0%
RSC entry closure size (gzip) vinext 125.4 KB 125.4 KB ⚫ 0.0%
Server bundle size (gzip) vinext 216.1 KB 216.1 KB ⚫ -0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1114197494

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/init-cloudflare.ts Outdated
Comment thread packages/vinext/src/init.ts Outdated
@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues. 8 min time limit.

Comment thread packages/vinext/src/init-cloudflare.ts Outdated
Comment thread packages/vinext/src/init-cloudflare.ts Outdated
@ask-bonk

ask-bonk Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Author identity unknown

*** Please tell me who you are.

Run

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: empty ident name (for runner@runnervmgx7h7.re4o1eegj32epalimfikus0oth.dx.internal.cloudapp.net) not allowed

github run

@ask-bonk

ask-bonk Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — time limit: 10 minutes; please return a concise clean verdict or actionable in-scope findings.

Comment thread packages/vinext/src/init-cloudflare.ts
Comment thread packages/vinext/src/init-cloudflare.ts
@ask-bonk

ask-bonk Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Posted 2 actionable inline findings. Targeted tests and vp check pass.

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — time limit: 10 minutes; please return a concise clean verdict or actionable in-scope findings.

Comment thread packages/vinext/src/init-cloudflare.ts
Comment thread packages/vinext/src/init-cloudflare.ts Outdated
Comment thread packages/vinext/src/init-cloudflare.ts Outdated
@ask-bonk

ask-bonk Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Posted 3 actionable inline findings. Targeted tests and vp check pass.

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — time limit: 10 minutes; please return a concise clean verdict or actionable in-scope findings.

Comment thread packages/vinext/src/init.ts Outdated
Comment thread packages/vinext/src/init.ts
Comment thread packages/vinext/src/init-cloudflare.ts Outdated
@ask-bonk

ask-bonk Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Posted 3 actionable inline findings.

github run

@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Posted 2 actionable inline findings. Targeted init tests and vp check pass.

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — time limit: 10 minutes; please return a concise clean verdict or actionable in-scope findings.

Comment thread packages/vinext/src/init-cloudflare.ts Outdated
Comment thread packages/vinext/src/init.ts Outdated
Comment thread packages/vinext/src/init-cloudflare.ts
Comment thread packages/vinext/src/init-cloudflare.ts Outdated
@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Posted 4 actionable inline findings.

github run

@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — time limit: 10 minutes; please return a concise clean verdict or actionable in-scope findings.

Comment thread packages/vinext/src/init.ts Outdated
Comment thread packages/vinext/src/init-cloudflare.ts
@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Posted 2 actionable inline findings.

github run

@james-elicx

Copy link
Copy Markdown
Member

Addressed both timed Big Bonk findings in ba59f326e: CSS Module discovery now traverses project-owned hidden source directories while excluding explicit generated/cache directories, and static callback-body resolution now also works through variable-bound ESM and CommonJS defineConfig calls. Focused init suites pass (235/235), targeted format/lint/type checks pass, and git diff --check is clean.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — hard time limit: 10 minutes; review exact head ba59f32 after resolving the hidden-source-directory scan and variable-bound ESM/CommonJS defineConfig callback findings. Be verdict-first and return explicit LGTM/no findings or only concise actionable in-scope findings before the deadline.

Comment thread packages/vinext/src/init-cloudflare.ts
@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

1 actionable inline finding posted.

github run

@james-elicx

Copy link
Copy Markdown
Member

Addressed the callback-shadowing finding in 42fab6a58. Existing ESM imports and CommonJS requires are now given stable top-level aliases when the resolved config callback shadows them; plugin, createHash, and path bindings are covered, including updater idempotency. Focused init suites pass (237/237), changed-file checks and git diff --check are clean.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — hard time limit: 10 minutes; final review of exact head 42fab6a after resolving callback-shadowed helper imports/requires. Be verdict-first: verify only hidden-source CSS Module discovery and static config callback resolution/binding safety for ESM and CommonJS, then return explicit LGTM/no findings or concise actionable findings before the deadline.

Comment thread packages/vinext/src/init-cloudflare.ts
@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Not LGTM: 1 actionable CommonJS binding-safety finding posted inline. Hidden-source discovery and ESM handling had no findings; 237 targeted tests pass.

github run

@james-elicx

Copy link
Copy Markdown
Member

Addressed the CommonJS directive-prologue/TDZ finding in a96c2841c. Each generated alias is now inserted after the top-level declaration that initializes its reused binding. A directive-prefixed CommonJS callback regression covers all three helpers and repeat-update idempotency. Focused init suites remain 237/237 green; changed-file checks and git diff --check pass.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — hard time limit: 10 minutes; final exact-head review of a96c284 after placing CommonJS helper aliases after their source declarations. Verdict first: review only the cumulative hidden-source CSS Module scan and static ESM/CommonJS config callback resolution/binding safety, then post explicit LGTM/no findings or concise actionable findings before the deadline.

Comment thread packages/vinext/src/init.ts Outdated
@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Not LGTM: 1 actionable hidden-source scan finding posted inline. ESM/CommonJS callback resolution and binding safety had no findings.

github run

@james-elicx

Copy link
Copy Markdown
Member

Addressed the hidden-filename finding in 1645f3bad. CSS Module discovery now explicitly includes dot-prefixed filenames at the project root, below ordinary directories, and below project-owned hidden directories while retaining the known generated/cache exclusions. Focused init suites pass (239/239); changed-file checks and git diff --check are clean.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues — hard time limit: 10 minutes; final exact-head review of 1645f3b after adding root and nested dot-prefixed CSS Module filename discovery. Verdict first: inspect only the cumulative hidden-source scan and static ESM/CommonJS config callback resolution/binding safety, then post explicit LGTM/no findings or concise actionable findings before the deadline.

if (firstArgument.type === "SpreadElement") return undefined;
const argumentObject = unwrapObject(firstArgument);
if (argumentObject) return argumentObject;
if (firstArgument.type === "Identifier") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Resolve identifiers bound to static config callbacks

A valid static form such as const makeConfig = () => ({ plugins: [] }); export default defineConfig(makeConfig) still aborts init. This identifier is sent to findVariableObject(), but that resolver only accepts object/identifier/defineConfig initializers, so the arrow/function initializer returns undefined; the CommonJS equivalent fails the same way. Resolve function-valued bindings through findCallbackObject() before rejecting the config.

if (callback.type === "FunctionExpression" && callback.id) bindings.add(callback.id.name);
for (const parameter of callback.params) collectPatternBindings(parameter, bindings);
if (callback.body.type === "BlockStatement") {
for (const statement of callback.body.body) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Include nested function-scoped var bindings

Only direct callback-body declarations are recorded here, but var declarations in nested statements are hoisted to the callback scope. For example, with an existing patchCssModules import, defineConfig(() => { if (false) { var patchCssModules; } return { plugins: [] }; }) reuses that imported name even though every generated reference resolves to the hoisted local undefined; nested var createHash/path break the scoped-name method similarly. Recursively collect function-scoped var declarations (without crossing nested functions), or allocate aliases conservatively.

commonJs,
)
: commonJs
? ensureNamedRequire(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Preserve CommonJS directive prologues when adding requires

For the common input "use strict"; const { defineConfig } = require("vite"); ..., this new helper require is inserted at offset 0 because requireInsertionOffset() stops on the leading expression statement. That moves "use strict" out of the directive prologue and silently changes the config runtime semantics; the later crypto/path requires do the same. Skip directive-prologue statements before scanning leading requires.

@ask-bonk

ask-bonk Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Not LGTM: 3 actionable inline findings posted. Hidden-source discovery passed review; all 239 targeted tests pass.

github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSS Modules: @extend (postcss-extend-rule) silently dropped — CSS-Modules plugin runs before user PostCSS plugins

2 participants