Skip to content

feat(plugins): deterministic class and file numbering - #743

Merged
owjs3901 merged 13 commits into
mainfrom
feat/deterministic-numbering
Oct 14, 2026
Merged

owjs3901 merged 13 commits into
mainfrom
feat/deterministic-numbering

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Refs #694 (G: 처리 순서에 따라 class 이름과 파일 번호가 바뀌는 문제, part 1). 이 PR은 #703, #704, #726을 포함하는 통합 브랜치입니다. #703, #704, #726이 먼저 병합되어야 하며, 병합 후 이 PR의 diff는 G 작업만 남습니다.

요약

class 이름과 파일 번호를 "처음 본 순서"로 매기던 것을 경로 순서로 바꿨습니다. 같은 소스는 어떤 순서로, 몇 개의 워커가 동시에 처리해도 같은 이름을 얻습니다.

  • 정적 빌드: 모든 플러그인(vite, next, webpack, rsbuild, bun)이 첫 변환 전에 추출 가능한 파일(프로젝트 소스와 include 패키지)을 훑어 경로 순으로 번호를 확정합니다(D9 사전 패스). 엔진의 새 seedFileMap(files)가 정렬·중복 제거 후 번호를 매깁니다.
  • dev: 이미 번호를 가진 파일은 그대로 두고 새 파일만 기존 번호 뒤에 이어 붙입니다(dev와 prod 이름은 다를 수 있음).
  • 같은 스타일 집합을 어떤 해시 순서로 넣어도 같은 class 이름이 나옵니다.

동작 (이슈 항목별)

  • INF-12 / PLG-14: 파일 번호는 사전 패스가 경로 순으로 확정합니다. vite는 src/app만, webpack은 src만, rsbuild는 아예 사전 패스가 없었고 include 패키지는 도착 순서였습니다. 모두 같은 collectNumberedFiles(plugin-utils)로 include 패키지의 소스까지 포함합니다. 각 플러그인이 추출에 넘기는 이름(vite 절대 POSIX, webpack/next cwd 상대 POSIX, rsbuild/bun 절대)으로 번호를 매깁니다. 파일 목록 정렬도 localeCompare(로케일 의존) 대신 코드 포인트 순서입니다. vanilla-extract ID에 들어가는 파일 번호도 이 번호를 씁니다.
  • INF-11: StyleSheet::update_styles가 FxHashSet을 해시 순서로 돌며 class 이름을 등록하던 것을, 안정된 키(ExtractStyleValue의 Ord)로 정렬한 뒤 등록합니다.
  • INF-14: exportClassMap/exportFileMap/exportCanonicalMap은 키를 정렬한 canonical JSON입니다.
  • INF-13: global_styles와 constant_exports는 평가기가 실행한 순서(= 소스 순서)로 내보냅니다. 이 순서는 CSS 캐스케이드 의미를 가지므로 정렬하지 않고, 입력이 같으면 같은 순서임을 유지합니다. 코드 변경 없음.
  • INF-15: 전체 vanilla-extract Boa 평가에 비결정 가드를 적용합니다. Math.random(), Date.now(), new Date(), Date()는 빌드 오류가 됩니다. new Date(0)처럼 고정된 날짜는 그대로 동작합니다.
  • PLG-15: 새 resetBuildState()가 이전 빌드가 엔진에 남긴 prefix, atom hoist, 라우트, 버킷, 이름·번호, 스타일, 모듈 해석기를 지웁니다. beginBuild(plugin-utils)가 이미 실행 중인 다른 빌드가 없을 때만 호출하므로 같은 프로세스의 동시 빌드를 지우지 않습니다. prefix는 값이 없어도 매 설정마다 setPrefix(prefix ?? null)로 지정합니다.
  • PLG-16: 변환마다 따로 async 쓰기를 하던 것을 createStateWriter가 파일별로 직렬화합니다. 새 스냅샷이 들어오면 아직 시작하지 않은 오래된 쓰기는 버리므로 오래된 상태가 마지막에 쓰이지 않습니다(webpack/next 로더, rsbuild, vite). 쓰기가 없을 때는 즉시 시작합니다. 원자적 임시 파일 교체(writeFileAtomically)도 제공하지만, 플러그인 테스트가 fs/promises.writeFile 호출을 검증하므로 플러그인은 기존 writeFile을 주입해 씁니다(직렬화/순서 보장만 적용).
  • PLG-17: vite generateBundle의 서버 importedCss 정리는 W17(fix/vite-rsc-shared-css)이 같은 코드를 고치고 있어 이 PR에서는 건드리지 않았습니다. 순서 무관 처리는 그쪽에서 다룹니다.
  • Next.js 코디네이터 항목(NXT-*)은 건드리지 않았습니다. next webpack 모드(로더)와 터보팩 설정에는 같은 사전 패스·writer를 적용했습니다.

새로 생기는 오류

  • 스타일시트(vanilla-extract) 평가에서 Math.random(), Date.now(), new Date(), Date()를 읽으면 JS execution error: ... gives a different value each build: a stylesheet cannot read it로 보고됩니다. 빌드마다 값이 달라지는 코드라 같은 소스가 다른 CSS를 내는 것을 막습니다.

남는 한계

  • 번호는 버킷 단위(single-importer collapse로 합쳐진 파일은 대표 파일 하나)이고, @devup-ui·StyleX·별칭 패키지를 언급하는 파일만 받습니다. 그래도 이전보다 스타일을 내지 않는 파일이 슬롯을 차지할 수 있어, 접두사가 한 글자(26개)를 넘는 시점이 앞당겨질 수 있습니다.
  • 사전 패스는 src(vite는 src/app/lib 후보, next는 src/app/pages)와 include 패키지를 봅니다. 그 밖의 경로에서 도착한 파일은 정렬된 번호 뒤에 도착 순서로 번호를 받습니다.
  • 마커 클래스(feat(extractor): compose styled components other modules define #736)와 component_marker 의미는 그대로이며 파일 번호를 쓰므로 이 PR의 번호 확정의 이점을 그대로 받습니다.

측정: 사전 패스 비용

apps/landing(소스 138개, 그중 87개가 @devup-ui 사용)에서 collectNumberedFiles + seedFileMap: 7회 중앙값 5.1 ms(스캔 4.9 ms, 번호 확정 0.19 ms). 파일당 약 0.04 ms이므로 1,000개 기준 약 40 ms입니다(이슈 예상 0.2~1초보다 작음, Windows 따뜻한 캐시 기준).

검증

  • cargo fmt, cargo +1.99 clippy --workspace --all-targets -- -D warnings, cargo test --workspace 통과 (1.99용 lint 수정 포함)
  • Rust: 파일 번호 시드(순서 무관), 해시 순서가 다른 스타일 집합이 같은 class 이름을 내는지, canonical JSON, resetBuildState, 비결정 가드
  • bun: 순서를 섞고(정방향, 역방향, 임의) 동시에(Promise.all) 처리해도 5개 파일에 대해 코드, 파일 맵, 파일별 CSS, 기본 CSS가 모두 같음을 vite/webpack·next/rsbuild·bun의 이름 방식별로 실제 WASM으로 확인(next-plugin/src/__tests__/deterministic-numbering.test.ts), writer 순서 보장, 사전 패스와 resetBuildState 호출 각 플러그인 테스트, vite 사전 패스 테스트
  • WASM 재빌드, 루트 bun run build, bun test 5510 통과, 커버리지 100%
  • bun lint: 기존 경고 2개 외 없음
  • 병합한 PR: fix(bun-plugin): emit the stylesheet in Bun.build and compile alias and StyleX imports #703(bun-plugin), fix(webpack-plugin,rsbuild-plugin): keep every extracted style in the built stylesheets #704(css-snapshot), fix(next-plugin): isolate coordinator port file and explain unreachable coordinators #726(coordinator-port-isolation). next-plugin/src/loader.ts 충돌은 두 쪽을 모두 유지(의존성 보고 + 연결 오류 처리)했고, #726의 린트 오류 한 건과 커버되지 않던 연결 오류 경로 테스트를 추가했습니다.

W30 통합 업데이트: 이 브랜치는 main의 a935315c까지 포함합니다 (#747 vinext/RSC CSS 보존 및 #700 버전 업데이트).

W30 최종 CI

  • 최종 head: 4f1f953cfb1cf29184f3d66d8152f1857fb89986, main의 a935315c까지 포함; MERGEABLE/CLEAN 확인
  • CI run 37172517071: success, publish / landing-next-e2e / vinext-rsc-css-e2e / benchmark 모두 통과
  • Ubuntu Rust 커버리지 100% (9843/9843), Bun 5527 통과 / 0 실패, 함수·라인 커버리지 100%
  • 로컬 vinext RSC 재현: per-file / singleCss 각각 29 통과; main의 RSC 전달/복원 구현은 byte-for-byte 유지
  • 추가 빌드 오류/의존성 없음; PR 병합/승인은 수행하지 않음

2026-10-08 업데이트: main 11790bef를 merge commit으로 병합했습니다; 충돌 파일: 없음; 충돌 없이 자동 병합했고, 이미 포함된 squash 원본은 조상 확인 후 ours 전략으로 중복 적용하지 않았습니다.

  • 2026-10-14 업데이트: main bf3c92b6(fix(extractor): preserve conditional-spread precedence in styled objects #776, test: restore global state and scoped spies #778)부터 최신 a2b74d0f까지 두 부모 merge commit 1b61bf8e로 병합했습니다. 충돌 파일 packages/{bun-plugin,rsbuild-plugin,vite-plugin,webpack-plugin}/src/plugin.ts, packages/next-plugin/src/loader.ts, packages/plugin-utils/src/index.ts, packages/webpack-plugin/src/__tests__/plugin.test.ts에서 main의 Tailwind 설정·의존성·HMR·코디네이터 오류·scoped spy 복원과 이 PR의 결정론적 source scan·seed·stateWriter·reset lifecycle 및 모든 회귀 검증을 함께 보존했습니다. Tailwind CSS 설정 파일은 감시 대상으로 유지하고 JS/TS source 번호를 소비하지 않는 기존 계약을 실제 프로브로 확인했으며, 공통 복구 7960b48f·b71ca4ea(fix: repair main tests, docs lint and Tailwind test isolation #780)도 원본 그대로 포함했습니다. 새 WASM/루트 빌드·Rust 전체·Bun 5,903개(함수/줄 100%)·실제 WASM 순서/Tailwind 프로브 6개와 빌드된 Next 로더 오류 2개·전체 훅이 통과했습니다. 기존 snapshot 변경 없음, 새 오류·추가 한계 없음.

owjs3901 and others added 7 commits October 1, 2026 01:50
…nd StyleX imports

The generated stylesheet always resolved to an empty module, so Bun.build dropped every style, and the pre-parse gate only accepted import declarations of @devup-ui/react, so files using the alias packages, StyleX, subpaths or re-exports ran the runtime stubs. DevupUI() now serves the complete sheet to the CSS loader after defer(), and the gate reads the real imports with Bun.Transpiler.scanImports.

Refs #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… built stylesheets

A stylesheet module is built on its first import, and the shared base is read from disk past the plugin, so styles extracted later were missing from webpack and Rsbuild output; Rsbuild also served a module-wide string kept only when it grew. The files the entries reach are now extracted before the build, the base on disk is kept current, and a compilation whose stylesheets changed is run once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet.

Refs #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…into feat/deterministic-numbering

# Conflicts:
#	packages/next-plugin/src/loader.ts
…ing order

Refs #694

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #694

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… forwarding alone

Refs #694

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • Rewriting styled-components, Emotion and vanilla-extract imports keeps type-only imports type-only: import { type DefaultTheme, ThemeProvider } now leaves import type { DefaultTheme } on the package instead of a value import that made it a runtime dependency, and an import type declaration is left as written. spin.getName() on a keyframes() binding becomes the generated keyframes name instead of a call on a string that threw at runtime
  • The styled-components and Emotion compat ThemeProvider now gives its theme through React context: useTheme, withTheme and the new ThemeConsumer return the real theme object (an object merged over the outer theme, or what a theme function makes of it) instead of an empty Proxy of var() references, withTheme forwards refs, lets a caller's theme win and sets a displayName. Styles keep reading the CSS variables the provider declares. styled-components v6.4 createTheme is available from the compat entry, and ThemeConsumer and createTheme imports are redirected there instead of staying a runtime dependency on styled-components
  • Respect source-order property replacement across conditional spreads in styled rule objects, and document that styled callbacks cannot return runtime class names. No new build errors; existing located Cannot compose errors remain unchanged.
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Typography documents the emitted default breakpoints, and source-backed integration tests keep the public breakpoint lists and range bounds synchronized with the production defaults. No runtime changes or new build errors.
  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.
  • The extractor guide shows reproducible native-element output, generated CSS-variable bindings, and actual atomic CSS rather than obsolete class names. Documentation only: no new build errors.
  • Selectors: a nested rule without & in a styled or css template is scoped to the component instead of becoming a global rule (.child {} and > p {} select descendants, :hover {} and ::before {} the component), and a rule nested in a selector list applies under each of its selectors; content keeps the quotes of its strings (content: 'x', content: ""); params go on the pseudo-class that takes them, so _groupNthChild gives :is(...):nth-child(2n) .a also when nested; a bare selectors key is a pseudo-class name, an attribute of the element or a descendant selector ('div p', '> p'), under a parent too, and keeps the case of class names; ::part(), ::slotted(), ::highlight(), ::picker() and the view-transition pseudo-elements get two colons. New build errors: a _name prop or selector key that names nothing (_notASelector, selectors={{ div: ... }}), and a string under a selector that is not CSS text (_hover="external-class")
  • stylex.attrs() gives the dynamic CSS variables it sets as style attribute text ("--a:" + value), as StyleX does for spreading onto non-React targets, instead of a style object; stylex.props() keeps the object React takes. The attrs() return type declares style as a string
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled
  • Order standalone nonresponsive hover-capability guards within their selector group so active, focus and disabled styles can override hover. Keep responsive Tailwind hover and Devup selector prop ordering unchanged. No new build errors.
  • Tailwind classes in a className follow Tailwind CSS v4: colors are the v4 default palette (oklch) and the colors and shadows of devup.json theme are usable as Tailwind theme values (bg-primary, text-text, shadow-card, as var(--token)); the opacity modifier (bg-red-500/50, /[71.37%], /(--alpha)) mixes with color-mix as v4 does; a trailing or leading ! makes every declaration important; blur, brightness, contrast, grayscale, hue-rotate, invert, saturate, sepia and their backdrop-* forms compose through Tailwind's custom properties instead of overwriting filter and backdrop-filter; gradients use bg-linear-, bg-conic- and bg-radial-* with from-, via- and to-* composing through the gradient variables (bg-gradient-to-* is not defined by v4 and stays as written); animate-* brings the @Keyframes it names and transition, duration, delay and ease follow v4; space-* and divide-* apply to the children like v4 (:where(& > :not(:last-child))); @Property rules are emitted only for the custom properties a class uses. Anything the build cannot reproduce exactly (drop-shadow-*) stays as written in the className. No new build errors
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors
  • A styled-components or Emotion style function reading the theme by literal keys, such as p => p.theme.space[2] or ({ theme }) => theme.colors['brand'], now reads the CSS variable ThemeProvider declares (var(--space-2)). A theme read the build cannot turn into a CSS variable, such as p => p.theme, p => p.theme.brand() or theme.space[i], is a build error naming the file, line, column and code, where it used to read a theme object that does not exist at runtime and give undefined or throw
  • While @vanilla-extract/css is aliased, importing @vanilla-extract/recipes or @vanilla-extract/sprinkles is a build error naming the file, line and column: those packages generate CSS Devup UI does not compile, so their rules used to be silently missing from the stylesheet. The error says to write the styles with css(), or to disable the alias and build with the vanilla-extract plugin. Type-only imports are allowed
  • Skip source-map generation in the codeExtractWithoutSourceMap WASM export while preserving extracted code, CSS and dependencies. Add real WASM and Next loader regressions for map-enabled and map-disabled extraction. No new build errors.

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • Bun plugin works with Bun.build: DevupUI() (from @devup-ui/bun-plugin/register) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (debug option). Files importing the packages Devup UI takes the place of (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css), @stylexjs/stylex, or a subpath or re-export of these or @devup-ui/react, are compiled instead of skipped
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.json

Patch

  • The @devup-ui/react README and the css-utils-literal-only rule docs link their mention of build errors to the new Build Errors reference (https://devup-ui.com/docs/build-errors), which lists the build errors grouped by API; the docs sidebar lists the page after Supported Syntax & Limitations, whose Build errors section links to it. Documentation only: no new build errors
  • ESLint rules now check exactly what the build reads. The array, typography, media and styleOrder rules and css-utils-literal-only follow the import aliases the build compiles by default (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css) and the rules of styled (styled.div({...}), styled("div", {...}), styled(Base)({...}, {...}); not .attrs()), createGlobalStyle({...}) and the styles of Emotion Global, and check and fix a module-level const object that only styles read. Vanilla-extract stylesheets (.css.ts/.css.js) are no longer read as Devup UI. New rule no-runtime-read reports css/keyframes/styled/Box read as a value ("X is read at runtime, where it does not exist"), and css-utils-literal-only reports globals that do not exist where a .css.ts is evaluated (window, document, process, ...). No new build errors.
  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • Isolate the coordinator port file per owning process when another live next dev/build holds it, record the owner pid, and report port, pid and recovery when the coordinator is missing or unreachable
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors
  • Skip source-map generation in the codeExtractWithoutSourceMap WASM export while preserving extracted code, CSS and dependencies. Add real WASM and Next loader regressions for map-enabled and map-disabled extraction. No new build errors.

@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/react@1.0.44 → 1.0.45 - packages/react/package.json

Patch

  • The @devup-ui/react README and the css-utils-literal-only rule docs link their mention of build errors to the new Build Errors reference (https://devup-ui.com/docs/build-errors), which lists the build errors grouped by API; the docs sidebar lists the page after Supported Syntax & Limitations, whose Build errors section links to it. Documentation only: no new build errors
  • The styled-components and Emotion compat ThemeProvider now gives its theme through React context: useTheme, withTheme and the new ThemeConsumer return the real theme object (an object merged over the outer theme, or what a theme function makes of it) instead of an empty Proxy of var() references, withTheme forwards refs, lets a caller's theme win and sets a displayName. Styles keep reading the CSS variables the provider declares. styled-components v6.4 createTheme is available from the compat entry, and ThemeConsumer and createTheme imports are redirected there instead of staying a runtime dependency on styled-components
  • The README no longer claims every CSS-in-JS pattern compiles: it describes what the build compiles and that anything it cannot know is a CSS variable or a located build error, and links the new Supported Syntax & Limitations page, which lists what compiles, the style override rules (where a later part wins and where the CSS cascade decides), Baseline 2024 browser support, that a component taking the css prop must pass className and style on, the runtime-only APIs and how each is handled, and the known limitations
  • stylex.attrs() gives the dynamic CSS variables it sets as style attribute text ("--a:" + value), as StyleX does for spreading onto non-React targets, instead of a style object; stylex.props() keeps the object React takes. The attrs() return type declares style as a string

@devup-ui/reset-css@1.0.31 → 1.0.32 - packages/reset-css/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@codecov

codecov Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
bindings/devup-ui-wasm/src/lib.rs 100.00% <100.00%> (ø)
libs/css/src/file_map.rs 100.00% <100.00%> (ø)
libs/extractor/src/lib.rs 100.00% <ø> (ø)
libs/extractor/src/module_loader.rs 100.00% <ø> (ø)
libs/extractor/src/tailwind_children.rs 100.00% <ø> (ø)
libs/extractor/src/tailwind_color.rs 100.00% <ø> (ø)
libs/extractor/src/tailwind_effects.rs 100.00% <ø> (ø)
libs/extractor/src/tailwind_misc.rs 100.00% <ø> (ø)
libs/extractor/src/tailwind_motion.rs 100.00% <ø> (ø)
libs/extractor/src/vanilla_extract.rs 100.00% <ø> (ø)
... and 11 more

... and 11 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Refs #694, #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901 added a commit that referenced this pull request Oct 6, 2026
Resolve packages/next-plugin/src/loader.ts using the #743 integration callback: preserve connection diagnostics/cache eviction and main dependency transport/full WASM. Apply the known coordinator-port test lint repair and add its missing connection-error coverage as a distinct EOF regression so the existing #743 test merges independently; retain main published versions, without numbering/state-writer features.

Refs #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Merge main history while preserving this PR implementation. No manual conflict resolutions.
No conflicted files. The exact original #703 head b6984e6 is already an ancestor; retain its implementation with the ours strategy.
No conflicted files. The exact original #704 head 71a5984 is already an ancestor; retain its implementation with the ours strategy.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
owjs3901 added a commit that referenced this pull request Oct 8, 2026
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Refs #694; includes exact main repairs 7960b48 and b71ca4e

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901 added a commit that referenced this pull request Oct 14, 2026
Refs #694; merge updated PR #743 with main a2b74d0

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@owjs3901
owjs3901 merged commit 76b1a15 into main Oct 14, 2026
5 checks passed
@owjs3901
owjs3901 deleted the feat/deterministic-numbering branch October 14, 2026 11:59
owjs3901 added a commit that referenced this pull request Oct 14, 2026
Refs #688 #707 #777 #716 #743

Preserve inherited captures and actual TS targets; cover dynamic bases and assertion guards

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
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.

1 participant