chore(config): retire the dead tailwind.config.js eslint ignore; reword the postcss blind-spot docblock (#4350) - #4444
Merged
Conversation
…rd the postcss blind-spot docblock (#4350) Two of the three Tailwind-migration residue items from #4350. The third (the root `tailwindcss` + `autoprefixer` devDependencies) is STOPPED on its measured stop condition and left for a ruling — see the PR body. Item 2: `**/tailwind.config.js` matched no tracked or untracked file anywhere in the tree after PR #4349 deleted the last one, and the `objectui init` scaffold is pinned to never write one. Repo-wide lint stays green (45/45 tasks, 0 errors). Item 3: the postcss-discovery narrowing stays — it is true in principle — but the sentence naming the repo-root `postcss.config.mjs` was counterfactual after #4349 deleted that file. Reworded to conditional / future-shaped, aligned with what the #4198 comment recorded: deletion retires the instance, not the gap in the derivation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Collaborator
Author
|
ACCEPT — PM 复核 (session
Flipping ready + arming auto-merge. Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #4350
Two of the three items land here. Item 1 STOPPED on its measured stop condition and is left for a ruling, so this PR says
Part ofrather thanFixes— merging it does not close the card.The PM ruling on #4350 delegated all three items with conditions attached. Items 2 and 3 met theirs. Item 1's condition turned out to be met in substance and violated in fact, by a mechanism the ruling's proxy could not see.
Item 1 — root
tailwindcss+autoprefixer: STOPPED, not removedThe ruling's stop condition:
The CSS sweep is clean — zero hits. The card's own measurement reproduces exactly. But the root copy is load-bearing anyway, for a reason that is not about resolution at all, and item 1 is stopped on it.
The anchor-hypothesis sweep
Every package whose build or stylesheet actually loads either package, against its own manifest. "Loads" means a real module-resolving form (a postcss plugin key, an
@importthat resolves the package), not a mention in a comment or docs:tailwindcssdeclaredautoprefixerdeclaredapps/consolesrc/index.css@import 'tailwindcss'apps/siteapp/global.css@import 'tailwindcss'examples/byo-backend-consolesrc/index.css@import 'tailwindcss'examples/console-startersrc/index.css@import 'tailwindcss'packages/componentssrc/index.css+postcss.config.jsnamesautoprefixerpackages/runnersrc/index.css+postcss.config.jsnamesautoprefixerpackages/fieldssrc/index.css@import 'tailwindcss/utilities.css'packages/app-shelltailwindcssstrings insrc/styles.cssare inside a docblock (lines 4 and 6); the file itself is@custom-variant/@keyframes/@utilityonly, and its build is plaintscpackage.json's own two declaration linesZero packages process CSS through either package without declaring it. Confirmed independently by
pnpm check:phantom-depsstaying green.A resolution probe is deliberately not offered as evidence here: all 47 manifests "resolve" both names through the root symlink, which is the precise trap
scripts/check-phantom-dependencies.mjswas written to document (#4394). Resolution answers the wrong question.The hit: the root manifest is the declared version anchor for the CLI scaffold
packages/cli/src/utils/scaffold-dependencies.tsexportsSCAFFOLD_DEV_DEPENDENCIES— the devDependency map written into every app generated by all three generators (objectui initand the two app generators). It declarestailwindcss: '^4.3.3'andautoprefixer: '^10.5.4', and its docblock names the anchor outright:DEPENDENCY_ANCHORSinpackages/cli/src/__tests__/app-generator.test.tsrecords it as data:autoprefixer: 'root'andtailwindcss: 'root', alongside seven toolchain siblings anchored the same way (@types/react,@types/react-dom,react,react-dom,react-router-dom,typescript,vite).So this is exactly the possibility the card raised as its reason to escalate — "a monorepo root sometimes carries a dependency deliberately — as a version anchor for the workspace [...] and that intent is not visible in the tree". The intent is in the tree; it is just not in any build config, which is where both the card's sweep and the ruling's proxy looked.
Measured, not argued. With the two devDependencies removed,
pnpm exec vitest run packages/cli/goes red on the anchoring gate, naming the anchor:That is item 1's reverse verification, and it ran in the direction nobody predicted: the removal was expected to be inert everywhere, and it is inert in the CSS pipeline while breaking a scaffold-versioning contract one package away.
Doctor before / after (captured while item 1 was applied)
The ruling asked for
objectui doctorat the root going "clean for the first time" as the acceptance evidence. It does not go clean, and I am reporting that rather than smoothing it:Reading
packages/cli/src/commands/doctor.ts: the whole Tailwind block is gated ondeps.tailwindcss, and a directory that declares notailwindcsscollectswarn('tailwind-missing', ...)instead. So removal swaps an error for a warning and the issue count stays at 1. The severity genuinely improves and the remaining line is truthful — but "clean" was not achievable this way, and the card's prediction that it would be is measurably wrong.What is now a decision rather than a task
Both options keep the scaffold emitting byte-identical ranges (
^4.3.3,^10.5.4), so this is about where the range of record lives, not about any user-visible version:tailwindcss+autoprefixerit never uses, and two references to the deleted files survive #4350 came to be filed.tailwindcss/autoprefixerinDEPENDENCY_ANCHORSfrom'root'to'in-repo'. Measured as mechanically viable: every non-root manifest agrees on exactly one range for each (^4.3.3across 7 manifests,^10.5.4across 5), which is what the'in-repo'anchor requires. Cost: it splits one deliberate convention into two, leaving seven toolchain deps anchored at the root and these two anchored elsewhere.My recommendation is B, but only as a maintainer's or PM's call, not an agent's — the anchoring discipline was established deliberately across #3742 / #3754 / #3892, and "which manifest is the source of truth for a scaffold range" is a convention question, not a mechanical one. B is the option that makes the root manifest stop claiming to be a Tailwind project while keeping every generated range anchored to a manifest that actually uses the package, which reads as the stronger long-term shape. A is the safe default and is what is committed here.
Item 2 — the dead
**/tailwind.config.jseslint ignore: REMOVEDSweep, per the ruling's condition ("ANY
tailwind.config.jsanywhere, fixtures/harnesses included"):git ls-filesmatchingtailwind.config.*— zero tracked files.find . -name 'tailwind.config.*'excludingnode_modulesand.git— zero files on disk, so untracked fixtures and harness output are covered too.packages/cli/src/__tests__/app-generator.test.ts:978("writes no tailwind.config.js, because v4 would never read it") andcli-bin.test.ts:229, which asserts bothtailwind.config.jsandtailwind.config.tsare absent from a real generated app.packages/components/shadcn-components.jsonhas"tailwind": { "config": "tailwind.config.js" }. That is shadcn's own components.json schema field, no such file has ever existed in that package, and no script in the repo reads the key (scripts/shadcn-sync.jsandscripts/shadcn-local-patches.mjscontain notailwindreference at all). It predates chore(config): delete three Tailwind config files nothing reads (#4065, #4066) #4349 and is untouched here.Zero matches, so the glob and its comment go.
Reverse verification, with my prediction stated first — and my prediction was wrong. I predicted the removal would be inert even for a hypothetical future file, reasoning that every config object in
eslint.config.jsis scoped to.ts/.tsx(files:appears three times, all TS-only), so a.jsfile would match no configuration and simply not be linted.Measured instead, with a scratch
packages/components/tailwind.config.jsholding the exact shape the deleted file had (import type ... from 'tailwindcss'plussatisfies Config):Exactly one error, exactly on the scratch file. So the original comment's rationale was accurate and mine was not: ESLint does reach
.jsby default and the default parser does choke onimport type. The card's stated expectation — "removing it turns any such file into a lint error rather than a silent skip" — is the measured truth. With zero such files today the removal is inert, and that is what makes it safe now rather than harmless in principle. Scratch file removed; the tree carries only the two intended edits.The load-bearing check that nothing was silently skipped: repo-wide
turbo run lint— 45 successful, 45 total, zero packages reporting a nonzero error count, run at the exact tree state pushed here.Item 3 — the counterfactual docblock sentence: REWORDED
scripts/__tests__/helpers/build-program.ts. Comment-only; the helper's tests are unchanged and its behavior is untouched.The narrowing stays, because it is still true in principle — postcss config discovery genuinely is not modelled. What changed is the sentence that named the repo-root
postcss.config.mjsas a present-tense fact; after #4349 deleted that file it was counterfactual as written. It is now conditional and future-shaped, aligned with what the #4198 comment recorded:So the docblock now says a package with no config of its own "would read the nearest one ABOVE its directory", records that no such file exists today (verified: all six tracked
postcss.config.*sit inside the package that uses it, and there is none at the repo root or inpackages/,apps/,examples/), credits #4065 / PR #4349 with deleting the one that used to sit above them all, and states plainly that this retires the instance and not the gap. The two measured coincidences (every vite-build CSS package carries its own config;packages/fields'index.cssnever enters the Vite graph) are kept and re-labelled as what would have to hold for a re-introduced config to stay harmless.Verification
All run in the worktree at the pushed commit, heavy steps serialized under the shared lock.
pnpm exec turbo run lint --concurrency=2— 45 successful, 45 total, no package reporting a nonzero error count. Item 2's proof.pnpm exec vitest run scripts/__tests__/ packages/cli/ --maxWorkers=2— 43 files, 998 tests passed. Covers item 3's own file (turbo-build-inputs.test.tsbuilds on that helper) and the CLI doctor / generator suites.pnpm exec tsc -p tsconfig.scripts.json— clean (build-program.tsis in that program).node scripts/check-control-bytes.mjs— OK, 4147 tracked text files scanned, 85 binary skipped. Plus a direct self-scan of both edited files for the wider control-byte range: no matches.node scripts/check-phantom-dependencies.mjs— green: 40 released packages, 2660 source files, 12777 specifiers, "Every in-scope import is declared by the package that publishes it."node scripts/check-changeset-presence.mjs— "No source of a released package changed in this range, so no changeset is owed." 2 files changed, 0 under any released package'ssrc/.check-changeset-fixed.mjsandcheck-changeset-no-major.mjsboth exit 0.pnpm install --frozen-lockfile— clean (the lockfile is untouched in the pushed state; the 6-line delta from item 1 was reverted with it).The doctor before/after and the failing anchor test above were captured while item 1 was applied, then reverted with
git checkout origin/main -- package.json pnpm-lock.yamland re-verified:packages/cligoes back to 998/998 green.Scope
No changeset: no released package's
src/is touched and the presence gate self-determines. Noskip-changesetlabel, per the same reasoning.Generated by Claude Code