Conversation
`astryx docs styling` told readers the working Next.js path is an SWC StyleX transform and to see `apps/example-nextjs-stylex` "for a complete, working Next.js + StyleX + SWC configuration". That app carries no SWC transform: it compiles StyleX with @stylexjs/babel-plugin and @stylexjs/postcss-plugin, the setup the same section tells readers not to use on App Router. A reader who follows the citation copies the configuration the docs just warned against. Point the reader at the snippet that follows instead, and say what the example app is actually good for. Add a drift guard so a doc sentence can only offer an in-repo example app as an SWC setup when that app declares a @stylexswc/* compiler.
Adds an end-to-end variant over the astryx docs styling output (the text a reader actually receives) plus controls for the dependency-field scope, the multi-app loop, the prose-only corpus and the two known sentence-scope limits.
VerificationAdversarial verification by a fresh run, at head Re-derived independently. Head moved, production diff did not. Tests added here (5, on top of the author's 2): one discriminating test over the rendered CLI output — the other code path the fix claims to cover — and four controls, for the Base arm — $ vitest run packages/cli/test/drift/docs-example-apps.test.mjs --project node
❯ node packages/cli/test/drift/docs-example-apps.test.mjs (7 tests | 2 failed) 2401ms
× offers an example app as an SWC setup only when it carries one 360ms
× renders no such citation through `astryx docs styling` 1985ms
✓ (control) reads the example apps that back the claim 2ms
✓ (control) reads an SWC compiler from either dependency field 1ms
✓ (control) checks every app a sentence names, not just the first 1ms
✓ (control) scans prose only, and no other block type cites an app as SWC 45ms
✓ (control) is sentence-scoped, so a split claim and a bare "not" slip through 1ms
FAIL ... > offers an example app as an SWC setup only when it carries one
AssertionError: expected [ Array(1) ] to deeply equal []
+ [
+ "packages/cli/assets/docs/styling.doc.mjs: offers apps/example-nextjs-stylex as an SWC configuration, but that app declares no @stylexswc/* compiler",
+ ]
❯ packages/cli/test/drift/docs-example-apps.test.mjs:129:19
FAIL ... > renders no such citation through `astryx docs styling`
AssertionError: expected [ Array(1) ] to deeply equal []
+ [
+ "astryx docs styling: offers apps/example-nextjs-stylex as an SWC configuration, but that app declares no @stylexswc/* compiler",
+ ]
❯ packages/cli/test/drift/docs-example-apps.test.mjs:141:64
Test Files 1 failed (1)
Tests 2 failed | 5 passed (7)
Duration 3.27sHead arm — $ vitest run packages/cli/test/drift/docs-example-apps.test.mjs --project node
✓ node packages/cli/test/drift/docs-example-apps.test.mjs (7 tests) 2473ms
✓ offers an example app as an SWC setup only when it carries one 335ms
✓ renders no such citation through `astryx docs styling` 2080ms
Test Files 1 passed (1)
Tests 7 passed (7)
Duration 3.44sRendered CLI, both arms — the user-visible half of the fix: $ # BASE: styling.doc.mjs from origin/main
$ node packages/cli/clients/cli/bin/astryx.mjs docs styling | grep -n "The working path on Next.js"
257:...which keeps SWC and `next/font` intact. See the example app `apps/example-nextjs-stylex` in the repo for a complete, working Next.js + StyleX + SWC configuration.
$ # HEAD 4493e26
$ node packages/cli/clients/cli/bin/astryx.mjs docs styling | grep -n "The working path on Next.js"
257:...which keeps SWC and `next/font` intact. The snippet below is the whole wiring. The repo ships no SWC example app: `apps/example-nextjs-stylex` compiles StyleX with the Babel plugin and PostCSS, so read it for the PostCSS layer setup, not for the transform.Sibling harness file, untouched by this PR, green at head: $ vitest run packages/cli/test/drift/docs-drift.test.mjs --project node
✓ node packages/cli/test/drift/docs-drift.test.mjs (1 test) 2843ms
✓ every doc parses and stays in sync with its source of truth 2840ms
Test Files 1 passed (1)
Tests 1 passed (1)Ledger holes found and closed. The previous ledger left rows 9, 15, 17 and 21 (multi-app loop; Behaviour outside the stated bug: none. The production change is a single string literal; the rest of the diff is one new test file. The one-line correction Redline will read is byte-identical to the author's. Tooling at this head: |
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: APPROVED — ready for the operator to submit; no blocking issues found.
I verified the one-line documentation correction against the base source at 0faf070d8ba2ecbb4ea424e4c0b9c3e85adf87f5: the old packages/cli/assets/docs/styling.doc.mjs:369 text explicitly presented apps/example-nextjs-stylex as “a complete, working Next.js + StyleX + SWC configuration,” while the cited app is the Babel/PostCSS example described in the evidence. The changed packages/cli/assets/docs/styling.doc.mjs:369 text now says the adjacent snippet is the SWC wiring and accurately limits that app to its PostCSS-layer role.
The regression coverage is appropriate and discriminating: packages/cli/test/drift/docs-example-apps.test.mjs:122-137 rejects an SWC citation whose referenced app lacks a @stylexswc/* dependency, and :142-154 separately exercises the rendered astryx docs styling output. The supplied same-test-file base/head evidence shows both variants fail on base and pass at this head. I also checked the candidate facts sheet, base reproduction, commit text, upstream prior-art searches (no matching open upstream PR), policy evidence, scope, and stated boundary ledger. No CI is configured on this fork (gh pr checks reports no checks), but no check is pending or failing.
What's good: the production change is narrowly scoped, preserves the correct SWC recommendation, and makes the user-facing CLI path a tested behavior rather than relying only on the source document.
Notes for the operator:
- On upstream submission, open the PR as a draft and select the applicable upstream primary-intent template, as the candidate’s policy evidence records.
- Include the concise rendered-output and before/after test evidence from the facts sheet in the upstream PR description.
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (second opinion, non-gating; the gating review is posted separately).
Verdict: READY — the citation correction is accurate; no blocking correctness issue found at 4493e2603acf1245d15679bc674548c53256e628.
Findings
None. I independently confirmed the original documentation defect from the removed sentence and the unchanged example configuration: example-nextjs-stylex declares and configures the Babel plugin, its PostCSS configuration imports that Babel configuration, and its Next configuration is empty. It is not the complete SWC example the base sentence promised. The replacement at packages/cli/assets/docs/styling.doc.mjs:369 accurately distinguishes the PostCSS layer example from the transform and points to the existing snippet. This does not establish a fix for upstream facebook#6376's rendering difference.
Maintainer's-eye read
- Keep the counter-example; do not require block-wide matching. Naming the Babel example specifically for its
useCSSLayerssetup is useful and supported by its actualpostcss.config.js. A block-wide SWC/app co-occurrence rule would reject that useful explanation. The current sentence heuristic is a bounded regression guard, not a natural-language truth checker. Its split-sentence and incidental-notblind spots are explicitly tested and disclosed; they do not prevent it from detecting the actual removed claim on either the source or rendered-output path. - Scope is reasonable, though the test machinery is optional complexity an upstream maintainer may question. The closest precedent, #5605, corrected this very styling payload with one line and rendered-CLI evidence, without a new test or changeset. Conversely, the existing
scripts/check-theme-template.test.mjsexplicitly favors live-source drift checks, and #4714 established the neighboring Vitest drift harness. The new test follows that live-source/reporting shape. I found no existing SWC-citation helper to reuse in those harnesses. A smaller regression assertion is a possible maintainer preference, not a readiness defect. - Submission notes, not findings: the underlying
fix(cli): ...title matches facebook#5605 and the scoped titles of recently merged outside contributions #5657 and #6196. Remove the fork's candidate marker when submitting upstream. Those latter behavior fixes include changesets; the directly comparable documentation-only facebook#5605 does not. I would not insist on a changeset here on that evidence. Followdocs/contributing/pull-requests.md's one-primary-intent/template guidance in the upstream submission body. - Prior art: independently searched upstream PRs for SWC and SWC/example, and issues for
example-nextjs-stylexand SWC/StyleX. Reviewed closed #4268, which adds a Turbopack CSS-loading example rather than correcting this citation. Searches surfaced facebook#3475/facebook#5260 and issue facebook#6376 as adjacent work, not an existing replacement for this one-line fix. Search results are not an exhaustive proof of absence.
Independently rebuilt boundary ledger
The production change is prose only. Below are the new test's decision points, derived from the diff. T1–T7 refer to the seven tests in file order. Inputs are repository-authored modules, strings and manifests, not a public arbitrary-JSON API; numeric limits, negative indices and platform/protocol branches are not introduced here.
| Changed decision/expression | Boundary behavior | Pin / scope |
|---|---|---|
.endsWith('.mjs') |
Non-module names excluded; empty result cannot silently pass | T1 nonempty-doc assertion; extension rejection not isolated |
mod.doc ?? mod.docs |
Null/undefined first export falls back; other values retained | Live imports in T1/T6; alternate export cases not isolated |
doc != null |
Neither export means module omitted | Live corpus; dense/translated alternate exports are outside this guard, as disclosed |
doc.sections ?? [] |
Absent/null sections produce no blocks; empty list produces none | Corpus traversal T1/T6, no malformed-doc fixture |
section.content ?? [] |
Absent/null content produces no blocks | Corpus traversal T1/T6, no malformed-section fixture |
block?.type === 'prose' |
Null/non-prose excluded | T1 scans prose; T6 separately checks real non-prose corpus |
typeof block.text === 'string' |
Empty string accepted, non-string excluded | T1 real strings; no isolated malformed block test |
existsSync(pkgPath) |
Missing manifest returns false before parsing | T3 missing app |
| dependency/devDependency spreads | Missing fields yield empty keys; either field can supply SWC | T4 empty manifest and both positive fields |
.some(...) |
Zero keys/no match false; one matching key true | T3/T4 |
.startsWith('@stylexswc/') |
Babel scope rejected; SWC scope accepted | T4 near-miss and both SWC fixtures; dependency presence is a proxy, not execution proof |
text.split(/(?<=\.)\s+/) |
Dot+whitespace splits; split claim loses association; e.g. also splits |
T1/T2 actual paragraph; T7 split-claim control |
/\bSWC\b/ |
Empty/no-token/lowercase-only text skipped; whole uppercase token checked | T1/T2/T5/T7; lowercase is outside stated corpus convention |
/\b(not|rather than|instead of|no SWC)\b/ |
Disclaimer excludes sentence, including incidental not |
T1/T2 corrected paragraph; T7 incidental-not control; alternatives not individually isolated |
!SWC_CLAIM.test(...) || DISCLAIMED.test(...) |
No claim or disclaimer skips app inspection | T1/T2/T7; plain positive control keeps rule live |
matchAll(/apps\/(example-[a-z0-9-]+)/g) / destructuring |
No match yields none; all matching paths inspected, capture is app name | T5 two-app case and positive-only case; actual paths T1/T2 |
!hasSwc(app) |
False emits diagnostic; true does not | T5 exact mixed result and SWC-only empty result; T7 positive diagnostic count |
docs.length > 0 |
Zero corpus fails | T1 assertion itself |
child status === 0 |
Failure/null status fails before trusting output | T2 assertion itself |
stdout /@stylexswc\/nextjs-plugin/ |
Missing topic anchor fails rather than vacuously accepting empty output | T2 assertion itself |
e.isDirectory() && e.name.startsWith('example-') |
Files/non-example dirs excluded | T3 requires real named example; false limbs not isolated |
fixture callback app === 'example-swc' |
Exact name true, Babel name false | Both limbs asserted in T5 |
block?.type !== 'prose' |
Other block kinds scanned as serialized data | T6 nonempty non-prose assertion plus no invalid citations |
nonProse.length > 0 |
Empty secondary corpus fails | T6 assertion itself |
I read every new assertion: T1 and T2 are the two discriminating regressions. The status/anchor assertions inside T2 are sanity checks, not claimed independent regressions. T3–T7 are explicitly named controls and intentionally hold with either version of the prose. T7's final positive assertion demonstrates the detector is not simply dead. I found no omitted reachable bug variant of the actual citation correction; this is not a claim that every possible future prose formulation is covered.
What's good: a single production string changes, no compiler recommendation/configuration changes, and both source and user-visible CLI output receive a regression check backed by real manifests.
Validation limits: read the full two-file diff, surrounding snippet, example configs, test precedents, module history and upstream PR evidence. gh pr checks reports no checks. I did not run tests or reproduce the browser/compiler rendering issue; the PR's A/B transcripts remain author-provided execution evidence.
SECOND READ: READY
|
Submitted upstream for review. |
Summary
astryx docs stylingtells readers the working Next.js App Router path is an SWC StyleX transform, then citesapps/example-nextjs-stylex"for a complete, working Next.js + StyleX + SWC configuration". That app has no SWC transform — it compiles StyleX with@stylexjs/babel-plugin+@stylexjs/postcss-plugin.apps/example-*declare zero@stylexswc/*dependencies. The recommendation itself is right; only the pointer is wrong.packages/cli/assets/docs/styling.doc.mjs: point at thenext.configsnippet that already follows in the same section, and say what the example app is actually for.packages/cli/test/drift/docs-example-apps.test.mjsgoes in the repo's existing drift-harness directory: a doc sentence may offer an in-repo example app as an SWC setup only when that app'spackage.jsondeclares a@stylexswc/*compiler. The file holds 7 tests at this head — 2 discriminating, 5 controls — and checks both ends of the pipeline: the doc source, and the text the shipped CLI actually prints.Current head
4493e26, both arms run against the same final test file:Upstream
facebook/astryxmain0faf070d8ba2ecbb4ea424e4c0b9c3e85adf87f5(docs(markdown): define extensible plugin protocol (#6338))packages/cli/assets/docs/styling.doc.mjs(section "StyleX Build Setup (required for swizzled components)", the fourthproseblock — line 369) and newpackages/cli/test/drift/docs-example-apps.test.mjs(228 lines)4493e26— two commits,8d7bfab(the one-line fix + the first 2 tests) and4493e26(5 further tests added during adversarial verification, test-only:git diff 8d7bfab 4493e26 -- packages/cli/assets/docs/styling.doc.mjsis empty)## Bug, last paragraph, for the exact relationship; this PR does not claim to fix The documented Turbopack/StyleX setup produces different layout output from the Babel StyleX setup for the same Astryx template. facebook/astryx#6376's rendering symptom).Bug
packages/cli/assets/docs/styling.doc.mjs:369reads: "The working path on Next.js is an SWC-based StyleX transform (e.g. the community@stylexswc/nextjs-plugin) wired intonext.config, which keeps SWC andnext/fontintact. See the example appapps/example-nextjs-stylexin the repo for a complete, working Next.js + StyleX + SWC configuration." The trigger is simply reading that sentence and following it.apps/example-nextjs-stylexdeclares@stylexjs/babel-pluginand@stylexjs/postcss-pluginindevDependencies, shipsbabel.config.jsandpostcss.config.js, has an emptynext.config.mjs, and its own README titles it "Next.js + StyleX (Dist)";grep -ril swc apps/example-nextjs-stylexmatches nothing. The wrong outcome is that a reader looking for the SWC wiring the paragraph just recommended finds, and copies, a Babel config — the setup the same section explicitly tells them not to use on App Router, because it disables SWC and breaksnext/font. The blast radius is every consumer and every coding agent: this file is not an internal comment, it is the payload of the shipped@astryxdesign/cli, rendered verbatim byastryx docs styling, andAGENTS.mdmakesastryx docsthe mandated bootstrap on every branch ("RULE: always run bootstrap on each branch — docs reflect the branch's actual API"). The section is the one a reader reaches after the documented silent-failure symptom ("swizzled component renders with no styles, but no build or runtime error"), i.e. exactly when they are already stuck.git log -S"example-nextjs-stylex" -- packages/cli/assets/docs/styling.doc.mjsdates the claim to19a921d43(facebook#4623).Relationship to facebook#6376, stated plainly: facebook#6376 reports that the same Astryx template renders with different grid spacing under
@stylexswc/nextjs-plugin/Turbopack than under@stylexjs/babel-plugin. That difference is between two third-party StyleX compilers Astryx does not ship, and it could not be reproduced here (no Next.js/Turbopack browser harness in this environment), so this PR makes no claim about it. What the hunt did establish is that Astryx's own docs send readers toward the SWC path while citing a Babel app as the reference for it — a defect in this repo, provable in this repo, and the reason a reader ends up hand-rolling the Turbopack config that facebook#6376 describes. facebook#6376 should stay open.Repro
No app fixture needed — the claim is false against the repo's own tree. On base
0faf070d8:Every example app, for the same question — nothing in the repo demonstrates SWC:
The claim reaches users through the shipped CLI, not just the source file. Both renders below were re-run at the current head
4493e26(base arm produced by checkingstyling.doc.mjsout oforigin/main, then restoring). The line number is whatevergrep -nreports for this terminal; the earlier body quotedsed -n '259p'from the first run's environment —257is the number measured here:That rendered path is now a test, not just a transcript — see test 2 in
## Test evidence.Fix
One prose string changed; the SWC recommendation and the
next.configcode block are untouched.This is minimal because the only false thing in the section is the citation. The paragraph already contains the correct answer — the
codeblock immediately after it is the fullnext.config.mjsSWC wiring — so the fix redirects the reader three lines down instead of out of the file, and states whatexample-nextjs-stylexis good for so the app is not orphaned from the docs.Alternatives rejected:
apps/example-nextjs-swc). That is what closed PR #4268 attempted for the adjacent Turbopack case; its author closed it with "Closing as not planned; I'm not going to carry this example forward." A new example app is a maintained artefact with a CI and dependency cost, it needs@stylexswc/*(a community package) in the workspace, and perCONTRIBUTING.md/docs/contributing/pull-requests.mdit is a different primary intent. Not a bug fix.example-nextjs-stylexgenuinely is the reference for the PostCSS/useCSSLayerslayer ordering, which the reader still needs.apps/example-nextjs-source. Also Babel (@astryxdesign/build'sbabel()/postcss()helpers); it would move the false claim, not remove it.package.json. Hardcoding the corrected wording would pass forever regardless of what the apps do, which is the failure mode the repo's other drift guards (scripts/check-theme-template.test.mjs) explicitly avoid: "all of it derived from live sources, never hardcoded".Test evidence
One new file,
packages/cli/test/drift/docs-example-apps.test.mjs(228 lines at head4493e26), beside the existingdocs-drift.test.mjsin the repo's drift-harness directory, following the conventions there and inscripts/check-theme-template.test.mjs(live sources, readable failure report,describe/itwith vitest). Tests 1 and 3 came with the fix at8d7bfab; tests 2, 4, 5, 6 and 7 were added at4493e26by an independent adversarial verification run that rebuilt the boundary ledger from the diff.offers an example app as an SWC setup only when it carries oneapps/example-nextjs-stylexfromstyling.doc.mjsrenders no such citation through `astryx docs styling`(control) reads the example apps that back the claim(control) reads an SWC compiler from either dependency fielddependenciesvsdevDependencies, and the@stylexjs/vs@stylexswc/near-miss(control) checks every app a sentence names, not just the firstmatchAllloop(control) scans prose only, and no other block type cites an app as SWC(control) is sentence-scoped, so a split claim and a bare "not" slip throughEvery control is named
(control)and passes on base by design; neither discriminating test passes on base. What each control controls for:carriesSwcTransform()returnsfalsefor a missingpackage.jsonexactly as it does for a Babel app, so a corpus that silently went empty would make test 1 green for the wrong reason.os.tmpdir(): an SWC dep independencies→true, indevDependencies→true,@stylexjs/babel-plugin→false, apackage.jsonwith no dependency fields at all →false(no throw on the twoundefinedspreads).proseBlocks()drops tables/lists/code and that no non-prose block in the live corpus makes an SWC example-app claim today, so the omission is bounded rather than assumed.not), plus the positive case proving the rule is not simply dead.Fails-before — base arm at head
4493e26:styling.doc.mjschecked out fromorigin/main, the final 7-test file unchanged, then restored. Verbatim:Passes-after — head
4493e26, same command, immediately after restoring the file:Sibling file in the same harness directory, untouched by this PR, run at head to show the change does not disturb it:
Tooling:
pnpm check:repo+lint-staged(via the repo's own huskypre-commit, on both real commits)check:sync,check:knowledge,check:package-boundaries,check:changesets,check:demo-media,check:executable-bits,check:cli-structure,check:use-client,check:portable-scripts,check:i18n-catalog,check:cldr-weekdays,check:fixtures,check:setup-contracteslint packages/cli/test/drift/docs-example-apps.test.mjsprettier --check packages/cli/test/drift/docs-example-apps.test.mjsAll matched files use Prettier code style!prettier --check packages/cli/assets/docs/styling.doc.mjsgit show HEAD:...to a temp path and running prettier there). Pre-existing;lint-stagedcovers only*.{ts,tsx,md}, so.mjsdocs are not prettier-enforced. Deliberately not reformatted: that would be an unrelated whole-file diff.No changeset added. Precedent:
964368929(facebook#5605) "fix(cli): correct Dialog onClose → onOpenChange in styling docs" — same file, same shape of one-line doc correction — added none, andpnpm check:changesetspasses as committed.Verification method
executed, in this container, twice by two independent runs. Nodev24.19.0, pnpm11.10.0onPATH(via/agent-workspace/bin; the repo's husky hook callspnpmand fails127without it), vitest4.1.10, dependencies installed from the committedpnpm-lock.yaml.8d7bfab(author run): the fix, tests 1 and 3, and every## Reproconsole block above.4493e26(adversarial verification run): the boundary ledger below rebuilt from the diff rather than from this body; tests 2, 4, 5, 6 and 7 written for the reachable rows that had none; both arms re-run against the same final 7-test file (transcripts above are that run's, verbatim); the siblingdocs-drift.test.mjs; the base/head renders ofastryx docs styling; eslint; prettier; and the repo's own pre-commit gate on the commit. Every boundary row marked measured was probed withnode -eagainst the real regexes and the real corpus at this head.Head integrity measured, not assumed:
git diff --statempty at4493e26;git diff 8d7bfab 4493e26 -- packages/cli/assets/docs/styling.doc.mjsempty (the verification added no production change);git diff origin/main...4493e26 --stat= 2 files, +229/−1.Not verified here, and nothing in this change depends on it: the rendering behaviour reported in facebook#6376 (needs a Next.js + Turbopack browser run). This change is text in a
.doc.mjsplus a node-project test; it has no runtime, platform, or browser surface.Fork CI at this sha:
gh pr checks 1 --repo askalf/astryx→no checks reported on the 'fix/docs-nextjs-swc-example-reference' branch. The fork's Actions were never enabled (operator card filed at fork creation), so that is absence, not failure; upstream's own CI has not run this branch. Nothing is pending and nothing is red.Prior art
gh search prs --repo facebook/astryx "stylexswc"gh search prs --repo facebook/astryx "example-nextjs-stylex"gh search prs --repo facebook/astryx "styling.doc"gh search issues --repo facebook/astryx "stylexswc"gh pr list --repo facebook/astryx --search "6376 in:body" --state allgit log -S"example-nextjs-stylex" -- packages/cli/assets/docs/styling.doc.mjs19a921d43(facebook#4623) — where the claim entered.git log --oneline -15 -- packages/cli/assets/docs/styling.doc.mjs964368929(facebook#5605) is the precedent for a one-line correction to this exact file.Closed-unmerged PR read before proceeding, as it is the nearest neighbour: #4268 "feat(examples): add Next.js 16 + Turbopack example (fixes silent unstyled-CSS trap)". It was closed by its own author, not rejected by a maintainer — "Closing as not planned; I'm not going to carry this example forward." It proposed adding a new example app and never touched
styling.doc.mjs. Different intent, different files, no overlap with this change; its stated finding (Turbopack does not link a CSS@importof a package stylesheet) is a separate matter and is not asserted here.No open PR fixes this. Open issue facebook#6376 has no linked PR and is cited, not pre-empted.
Policy
Read:
CONTRIBUTING.md,AGENTS.md,CODE_OF_CONDUCT.md,.github/pull_request_template.md,docs/contributing/pull-requests.md, and the wiki pageCONTRIBUTING.mdlinks, Contributing with AI Assistants.AI_POLICY.md,AI.md,AGENT_POLICY.md,.github/AI_POLICY.md,.github/CONTRIBUTING.mdare all absent (404). No CLA gate found.AI-assisted contribution is permitted with process, quoted from the wiki's "For AI Assistants" section:
Spec protocol, quoted: "Any change to component APIs, new components, or behavioral changes follows this protocol. No exceptions — even for 'small' prop additions." This change is none of those: no component API, no new component, no behavioural change — one documentation string plus a test.
docs/contributing/pull-requests.mdlists "Correct contributor or consumer documentation" as its own primary intent (documentation.md, minimum evidence: "reader impact, source of truth, rendered or generated result" — supplied above as blast radius,package.json/app tree, and theastryx docs stylingrender at both shas) and "Restore broken behavior" (bug-fix.md: "reproduction, expected authority, before/after result, unchanged representative path"). Issue-first is satisfied by the existing open issue facebook#6376 on this surface; this PR does not open an upstream issue.Also honoured: "Keep the intent atomic" — no tagalongs; the prettier noise in
styling.doc.mjsis pre-existing and left alone precisely because fixing it would be an independently removable cleanup. "Public repository boundary" — nothing internal in the diff, commit, or this description.Required tooling run:
pnpm check:repo+lint-staged(via the repo's own pre-commit hook),eslint,prettier, and the affected test file undervitest --project node. PerCONTRIBUTING.mdthe whole suite ispnpm test; it was not run (single-surface change, and thenodeproject's globalSetup builds@astryxdesign/corefor every run).Note for the operator on submission: the wiki requires the upstream PR to be opened as a draft (
gh pr create --draft), and.github/pull_request_template.mdrequires choosing one primary-intent template from.github/PULL_REQUEST_TEMPLATE/—bug-fix.mdordocumentation.mdfits this change.Disclosure facts for the operator
Plain facts, for you to write your own disclosure in your own words:
packages/cli/test/drift/docs-example-apps.test.mjs.astryx docs stylingrenders at both shas, thepackage.jsonprobes, the sibling drift suite, eslint, prettier, and the repo's owncheck:repopre-commit gate. Every console block is copy-pasted output, not reconstructed.askalf; no AI attribution lines are in the commits or the branch name.facebook/astryxin any way: no issue, comment, reaction, or PR. All work is in theaskalf/astryxfork.Boundaries
Every predicate, comparison, guard, index expression and truthiness check the diff adds or changes. Rows marked measured were run through the actual regexes/functions at head
4493e26(node -eprobe against the real source strings and the real corpus), not reasoned about. The production diff adds no predicate at all — it is one string literal — so every row below belongs to the new test file.SWC_CLAIM = /\bSWC\b/true→ app is checked → reportedSWC_CLAIMtrue(word present) — not sufficient alone; row 8 decidesSWC_CLAIM""(empty sentence from a trailing split)false→ skipped, no crashSWC_CLAIM"swc"lowercasefalse→ skipped. Deliberate: the docs write the initialism uppercase; a lowercase match would fire on@stylexswc/...package names in ordinary proseSWC_CLAIMword boundary"SWCONFIG"(SWC as prefix of a longer token)false→ skipped.\bis load-bearingDISCLAIMEDfalse→ sentence checked → base redDISCLAIMEDfalse positivenot:"Do not skip this: apps/example-babel is the complete SWC setup."true→ not reported. Known limit of the heuristicnotis reportedSWC_CLAIM && !DISCLAIMEDcombined"Use an SWC transform."truebutmatchAllyields zero apps → nothing reportedEXAMPLE_APP = /apps\/(example-[a-z0-9-]+)/gEXAMPLE_APPcarriesSwcTransform→false→ reported. Correct: citing a nonexistent app as SWC is also wrongcarriesSwcTransform('example-does-not-exist') === false— measuredEXAMPLE_APPapps/pathtext.split(/(?<=\.)\s+/)sentence unit"(e.g."splits mid-sentence, harmless — the fragment carries neither an app nor a disclaimer contradictiontext.split(...)— known limitation"The working path is SWC. See apps/example-babel."fs.existsSync(pkgPath)guardapps/<name>/package.jsonmissingreturn false, no throw from the laterreadFileSync/JSON.parse{...pkg.dependencies, ...pkg.devDependencies}package.jsonwith neither fieldundefinedspreads →{}→.some()on empty →false, no throwexample-emptyfixture).some(name => name.startsWith('@stylexswc/'))false.some(...)positive case@stylexswc/nextjs-plugin(the real SWC case) — independencies, and separately indevDependenciestrue→ a sentence citing it is accepted. This is the row that lets a future correct citation pass without editing the test.some(...)near-miss@stylexjs/babel-plugin(present in the cited app)startsWith('@stylexswc/')→false. The two scopes differ by two characters and the whole check rests on thisexample-babelfixture — measureddocs.lengthassets/docsreadable,.mjsfilter.mjsfiles, 21 with a doc export;expect(docs.length).toBeGreaterThan(0)guards a silently empty corpusmod.doc ?? mod.docs.mjsexporting neitherdoc == null→ file skipped, no crash. Reached today: 7 of the 28 (layout.doc.dense.mjs,principles.doc.{dense,zh}.mjs,theme.doc.{dense,zh}.mjs,tokens.doc.{dense,zh}.mjs) exportdocsZh/docsDenseinstead and are skipped, so dense and Chinese variants are not covered by this guard. Measured at head: all 7 contain zero occurrences ofSWCand zero ofexample-, so none carries a citation to miss; widening the guard to them is a separate changeproseBlocks()block-type filtertable,listorcodeblockspawnSync(node, [BIN, 'docs', 'styling'])exit statusexpect(res.status).toBe(0)→ the test fails loudly rather than scanning empty stdoutexpect(res.stdout).toMatch(/@stylexswc\/nextjs-plugin/)→ a vacuously clean stdout cannot passRows 13 and 7b are the honest gaps, and both are now pinned by test 7 rather than left implicit. They are a deliberate trade-off: widening the unit from the sentence to the whole prose block is the obvious alternative and it is worse, because the corrected text recommends SWC and names the Babel app in the same block on purpose, so a block-scoped rule reports the fix itself as a defect — it cannot express "this app is the counter-example". The sentence unit makes the correction expressible; the cost is that a claim split across two sentences, or one carrying an incidental
not, slips through. A maintainer who prefers the stricter rule would need the corrected prose reworded to drop the app name entirely, and test 7 is where that decision would be recorded.Suggested upstream PR title
fix(cli): stop citing a Babel example app as the SWC StyleX setup