Skip to content

fix(build): scan what each Astryx package publishes, so a linked workspace builds - #7775

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:build-library-glob-cycles
Oct 21, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:build-library-glob-cycles

Conversation

@vjeux

@vjeux vjeux commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

User impact

An app that builds Astryx from source with the packages linked from a pnpm workspace (packages/core linked into its node_modules) cannot finish next build --webpack. The PostCSS plugin scans node_modules/@astryxdesign/**/*.{ts,tsx}, so it walks everything a linked package carries, not what it publishes:

  • its own node_modules, whose workspace links cycle (core → its dev dependency @astryxdesign/cli → core): in this repository's workspace the build runs out of memory at 4 GB after about eight minutes;
  • its unpublished files, such as @astryxdesign/build's test fixtures, one of them invalid on purpose.

Problem and solution fit

  • Why it fails: a workspace link exposes a package's whole directory, while a registry install holds only its files; the scan treats both alike.
  • Why it harms the task: each level of links multiplies the paths walked and the modules compiled again.
  • Why that matters: linking is how an app builds against a local Astryx checkout.

Expected behavior and authority

spec:AST-080 FR7 (current) requires a source build to produce the rules of the package's modules; this one never completes. A package's files field defines what it publishes; #7754 likewise keeps node_modules out of its Turbopack globs.

Smallest restoration

The library scan lists node_modules/@astryxdesign/* once per real directory and reads each package's files: a directory brings the .ts/.tsx files below it, a file itself,, and a glob or ! entry fails the build, naming the package and entry, rather than approximating npm pack (no Astryx package lists one). Without files, the whole package minus nested node_modules.

Evidence

  • Reproduction before the change: apps/example-nextjs-source added to this workspace (Astryx linked from packages/*), with fix(build): withAstryx keeps symlink resolution so pnpm's default layout builds #7773 applied, runs out of memory as above.
  • Result after the change: the same build completes and prerenders, and all 226 rendered class names have a rule. The workspace scan is the 1,317 files a registry install holds.
  • Representative unchanged path: on registry installs of the documented example the scanned list is identical: 1,317 files isolated, 1,317 hoisted, 1,562 hoisted with Lab.
  • Regression test or other mutation-sensitive proof: postcss.workspace-links.test.mjs builds a workspace whose links cycle. On main two cases compile 60 files each, fixtures included; here only the published module, or a files-less package's source. Two more expect a glob and a ! entry to be refused.

Scope

  • One defect is restored.
  • Separable contradictory or unsettled tagalongs were removed or split.
  • No new public API, visual representation, default, or interaction model is hidden under the bug-fix label.
  • Public text and artifacts contain no internal Meta context.

Testing

@vercel

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 21, 2026 2:16am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 21, 2026, 2:41 AM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

No new or modified components detected.

Bundle Size Summary

No component packages changed.

Accessibility Audit

Status: No accessibility violations detected.

Visual Regression

40 of 486 shot(s) changed.

A change here is a question, not a failure: check whether the after is the
picture you intended. Record that review on the PR. Baseline maintenance is an
explicit dispatch of CI; this report never rewrites the baseline or adds a release gate.

component story theme mode pixels
Selector Indicator Space Bottom Sheet Narrow End RTL neutral light 754,077
Selector Indicator Space Bottom Sheet Wide Start neutral light 740,188
Chat Full AI Chat probe light 230,823
Chat Full AI Chat probe dark 229,805
RichTextEditor Markdown parity: side by side probe dark 94,866
RichTextEditor Markdown parity: side by side probe light 89,762
Collapsible Dividers probe dark 66,445
Collapsible Dividers probe light 66,445
Chat Default neutral dark 36,304
Chat Default neutral light 35,767
Chat Empty State probe light 23,531
Chat Empty State probe dark 22,622
ChatAutoScroll Scroll Behavior Comparison neutral dark 15,378
ChatAutoScroll Scroll Behavior Comparison neutral light 14,107
Chat With Attachments probe light 13,406
Chat With Attachments probe dark 11,232
ProgressBar With Target Mark probe dark 7,895
ProgressBar With Target Mark probe light 7,895
Markdown Default neutral dark 7,573
Markdown Default neutral light 7,566

and 20 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@astracat-bot astracat-bot 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.

This looks ready to merge: scanning each package's published files keeps a workspace-linked source build out of its link cycles, and registry installs still compile the same files into identical CSS. Two optional follow-ups:

  • A files glob such as src/* only reaches files directly in src, while npm also packs nested files such as src/Box/Box.tsx; no Astryx package uses a glob entry today, so a fix and a test for the glob and ! branches can come later.
  • The comment above libraryGlobs names the cycle as core -> a11y-spec -> core, but @astryxdesign/a11y-spec has no Astryx dependency; in this workspace the cycle runs through core's dev dependency @astryxdesign/cli.

[Automated review]

Comment thread packages/build/src/index.js Outdated
Comment on lines +83 to +85
} else if (isGlob(pattern) || LIBRARY_SOURCE.test(pattern)) {
// A file or glob entry, as written; only its .ts and .tsx matches count.
include.push(`${base}/${pattern}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A glob entry is globbed as written, so src/* reaches only files directly in src, while npm pack of a package with files: ["src/*"] also packs src/Box/Box.tsx. No Astryx package uses a glob entry today, so this is latent; globbing the contents of matched directories (or treating a trailing /* as /**) plus a test for the glob and ! branches would close it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in e82bd86. A glob entry now brings its matches and everything below a directory it matches, so src/* includes src/Box/Box.tsx as npm pack does. A ! entry drops its matches with their contents. A new case in postcss.workspace-links.test.mjs covers both and fails on the previous head. The registry file lists are unchanged (1,317 / 1,317 / 1,562).

Comment thread packages/build/src/index.js Outdated
Comment on lines +47 to +48
* `node_modules`, whose links can lead back to the package (core -> a11y-spec
* -> core); neither is scanned.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@astryxdesign/a11y-spec declares no Astryx dependency, so it does not link back to core. In this workspace the cycle runs through core's dev dependency @astryxdesign/cli, whose node_modules/@astryxdesign/core links to packages/core.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right, thanks: the cycle runs core → @astryxdesign/cli → core. Fixed in e82bd86 in the comment, the commit message and the description, and the test workspace now models that chain.

@astracat-bot astracat-bot 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.

This looks ready to merge: reading each package's published files keeps a workspace-linked source build out of its link cycles, the new test fails on main and passes here, and registry installs still compile the same files into byte-identical CSS. One optional follow-up:

  • files reading still differs from npm pack in shapes no Astryx package uses yet: a ! entry listed before the directory it carves from, a slash-less ! entry such as !*.stories.tsx, and a glob such as src/B* that matches a directory (details on packages/build/src/index.js lines 77-85).

[Automated review]

Comment thread packages/build/src/index.js Outdated
Comment on lines +77 to +85
if (negated) {
// An excluded directory takes everything below it along.
ignore.push(`${base}/${pattern}`, `${base}/${pattern}/**`);
} else if (isDirectory) {
include.push(`${base}/${pattern}/**/*.{ts,tsx}`);
} else if (isGlob(pattern)) {
// The glob's own matches, and everything below a directory it matches:
// `src/*` packs `src/Box/Box.tsx` too.
include.push(`${base}/${pattern}`, `${base}/${pattern}/**/*.{ts,tsx}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These branches read files differently from npm pack (and pnpm pack, which agrees) in three shapes no Astryx package uses yet:

files npm and pnpm pack This plugin compiles
["!src/internal", "src"] src/a.ts, src/internal/secret.ts src/a.ts
["src", "!*.stories.tsx"] src/a.ts also src/top.stories.tsx, src/Box/Box.stories.tsx
["src/B*"] src/Bar.ts also src/Box/Box.tsx

npm applies a ! entry only to the entries before it, matches a slash-less one at any depth, and reads only a trailing /* as the whole directory. Mirroring those rules with a test case each, or narrowing the comment at line 44 and the test name to the shapes handled, would keep the "the way npm packs them" claim true.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed in 6e3a0b6. Unsupported files shapes now fail loudly instead of approximating npm pack. A plain entry works as before: a directory brings the files below it and a file brings itself. An entry with glob characters or a leading ! fails the build with @astryxdesign/build reads plain files entries only; <package> lists "<entry>". No Astryx package lists one today (checked in the workspace, in the current canary and in 0.7.1). The refusal is tested, and registry installs still scan identical file lists.

…space builds

The PostCSS plugin scanned node_modules/@astryxdesign/**/*.{ts,tsx}, so a
package linked from a workspace contributed everything in its directory: its
own node_modules, whose links can cycle back to it (here, core's dev
dependency @astryxdesign/cli links core again) until the build runs out of
memory, and unpublished files such as @astryxdesign/build's intentionally
invalid test fixtures.

Scan each installed package's published files instead: each `files` entry
is a directory, which brings the files below it, or a file, and a package
without `files` is scanned whole minus nested node_modules. An entry with
glob characters or a leading `!` fails the build naming the package and the
entry, rather than approximating npm pack. For a registry install the
scanned files are the same as before.

@astracat-bot astracat-bot 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.

This looks ready to merge: reading each installed package's plain files entries keeps a workspace-linked source build out of its link cycles, the new test fails on main and passes here, and registry installs still scan the same files into byte-identical CSS. Refusing glob and ! entries is safe today, since every stable @astryxdesign/* release lists only plain entries. As an optional follow-up, a repository check that published manifests keep plain files entries would surface a future glob in this repository's CI rather than in consumers' source builds.

[Automated review]

@cixzhang
cixzhang merged commit a19fceb into meta:main Oct 21, 2026
54 checks passed
@vjeux
vjeux deleted the build-library-glob-cycles branch October 21, 2026 14:31
cixzhang added a commit that referenced this pull request Oct 21, 2026
)

<!-- template: maintenance.md -->

## Maintainer impact

`main` is red: `test-node` fails in `packages/build/src/postcss.test.mjs` ("compiles Astryx source without the app's Babel config"). Every pull request based on current `main`, and any release that gates on a green `main`, is blocked.

## Intended invariant

The test keeps proving what #7774 added: the PostCSS plugin's library pass compiles Astryx source without the app's Babel config, while the app's own files still get it. It should run against an Astryx package installed the way a real one is.

## Change

#7775 and #7774 each passed on their own but conflict once both are on `main`:

- #7775 makes the plugin scan what each installed `@astryxdesign/*` package publishes, read from that package's `package.json`; a directory without one is not treated as an Astryx package.
- #7774 added this test with a fixture that installs `node_modules/@astryxdesign/core/src/Box/Box.tsx` but no `package.json`.

So, on `main`, the fixture's Astryx module is compiled as app code (`@layer product`) and the assertion on `@layer astryx-base` fails. The fix gives the fixture's package a minimal manifest (`{"name": "@astryxdesign/core"}`, no `files`, so the whole package is scanned). Test-only; no plugin change.

## Evidence

- Failure before the change: on current `main`, `pnpm exec vitest run --project node packages/build/src/postcss.test.mjs` fails with `expected '@layer product {…' to match /@layer astryx-base \{[^]*color:\s*red/`, the same failure as `main`'s `test-node` job.
- Success after the change: the same file passes, and so does the whole `packages/build` suite.
- Product/runtime behavior verified unchanged: only the test fixture changes.

## Scope

- [x] No intended public API, product behavior, visual direction, or policy change.
- [x] Product changes discovered during the work were removed or split.
- [x] Public text and artifacts contain no internal Meta context.

## Testing

- `pnpm exec vitest run --project node packages/build`: 7 files, 85 tests pass (the target test fails without this change).
- Prettier and `pnpm check:changesets`: pass. No Changeset: test-only.

This branch was successfully deployed

1 active deployment
Preview — 6e3a0b65 Deployed Oct 21, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants