Skip to content

fix(sheet): keep global CSS, imports and cascade layers in written order - #701

Open
owjs3901 wants to merge 4 commits into
mainfrom
fix/global-order
Open

owjs3901 wants to merge 4 commits into
mainfrom
fix/global-order

Conversation

@owjs3901

@owjs3901 owjs3901 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Refs #685

요약

사용자가 #701을 유지하기로 결정했습니다. 같은 파일 안에서는 일반 CSS와 같이 더 아래에 작성한 선언이 이기도록 전역 CSS 작성 순서를 보존합니다.

main의 #699(globalCss theme/responsive token, import 객체, named layer 및 layer 이름 검증)와 #698(theme 수정)는 이미 병합된 기능이며 이번 PR의 새 기능이 아닙니다. 해당 부분은 main 구현을 유지하고, main의 #743 정렬 삽입과 dead-property 검증도 그대로 보존했습니다.

동작

  • raw/object globalCss, imports, fontFaces 및 같은 객체 안의 shorthand/longhand 선언에 파일 내 작성 순서를 적용합니다.
  • custom layer는 파일별 첫 사용 순서대로 prelude에 선언하며 중첩 layer는 부모 뒤에 옵니다.
  • 동일한 raw 규칙의 반복은 유지하고, 중복 import/font face는 마지막 위치에서 한 번 출력합니다. 파일을 다시 추출하면 해당 owner의 이전 전역 규칙을 제거·교체합니다.
  • keyframe 해시는 순서 메타데이터를 제외하여 main의 기존 식별자를 유지합니다. Button 스냅샷도 main과 바이트 단위로 같습니다.
  • 컴파일러가 합성하는 Tailwind 등록은 순번 0과 기존 중복 제거를 유지합니다. 여러 파일과 재추출에서도 공유 등록을 누적·정렬하여 앞 파일의 등록을 지우거나 반복 utility의 등록을 늘리지 않습니다.

실제 public WASM과 Chromium에서 확인한 출력 및 winner:

입력 실제 출력 핵심 브라우저 결과
raw red → blue body{color:red}body{color:blue} blue
import red → blue data CSS red URL 다음 blue URL, 두 stylesheet 로드 blue
fontFaces 200% → 100% 동일 Audit/확인된 local Arial face를 해당 순서로 출력 두 face loaded, 폭 133.28125px로 기본 Arial과 동일
객체 호출 red → blue body{color:red;color:blue} blue
marginTop:'10px',m:0 body{margin-top:10px;margin:0} margin-top 0px
layer zz → aa @layer zz,aa; p margin-top 4px

새로 생기는 오류

없습니다. 이번 순서 수정은 새 빌드 오류를 추가하지 않습니다. main에 이미 있는 layer 이름 및 dead-property 오류는 그대로 유지합니다.

남는 한계

파일 간 경로 정렬, selector group/breakpoint 순서 및 imports/font/raw 등의 출력 category 분리는 기존 규칙을 유지합니다. 전체 프로젝트의 module 실행 순서나 임의 category/wrapper 사이의 완전한 interleaving을 보장한다고 주장하지 않습니다.

검증

  • 기본 Rust stack, CARGO_BUILD_JOBS=2, 6GB 전체 job 감시로 fmt/clippy 및 workspace Rust 테스트 3,985개 통과했습니다.
  • fresh binding WASM 및 root 빌드 후 Bun 5,909 pass / 0 fail, 함수·라인 커버리지 100%, lint 통과했습니다.
  • 여섯 독립 추출 회귀와 실제 Chromium winner, 두 import/face 로딩, owner 교체를 검증했습니다.
  • keyframe hash stream/식별자를 main의 독립 7필드 기준과 비교했고, 순서 변경에는 같고 property/value 변경에는 달라짐을 검증했습니다.
  • 합성 Tailwind 중복 회귀는 수정 전 RED, 수정 후 GREEN이며 fresh public WASM에서 content/leading 등록이 각각 한 번이고 main CSS 문자열과 같음을 확인했습니다.
  • 두 파일을 양쪽 순서로 추출하고 반복·파일 갱신·빈 파일 교체를 해도 두 합성 등록이 남는 회귀를 single/per-file CSS 모드 모두 검증했습니다. 실제 public WASM도 RED → GREEN이며 등록 출력은 main의 정렬된 문자열과 같습니다.
  • 모든 snapshot diff를 검토했습니다. 합성 등록의 출력 문자열·수·순서는 main과 같고 새 디버그 순서 값은 0입니다. Button snapshot SHA-256은 main과 같은 fd11618fcc97429d17730bc6c734a8a87a4383c97ddf5ebd02f9e7cf99e28505입니다.
  • LSP는 기존 환경에서 응답하지 않아 통과로 계산하지 않았습니다. 전체 hook 및 현재 head의 Linux CI 결과는 아래 최종 갱신 기록에 별도로 기재합니다.

최종 갱신

  • 2026-10-15: main a6ecc125를 실제 두 부모 merge commit e410272686a3a54d733a7ffb39ea8581ef331268으로 반영했습니다. 부모는 기존 fix(sheet): keep global CSS, imports and cascade layers in written order #701 6d068db0와 main a6ecc125입니다.
  • 독립 gate 재검토 APPROVE 후 정상 전체 pre-commit hook(lint, tarpaulin, Bun test)을 통과했습니다. hook은 기본 stack/jobs2/12GB 감시로 실행했고 실제 peak는 4.604GB, 남은 owned process는 0입니다.
  • 로컬 Windows tarpaulin은 98.26% (17,305/17,612)이며 이를 Rust 100%로 보고하지 않습니다. 현재 head의 Linux CI에서는 별도의 100% 기준을 확인합니다.
  • 현재 head e410272686a3a54d733a7ffb39ea8581ef331268의 CI 38747132150가 SUCCESS입니다. publish, benchmark, landing-next-e2e, vinext-rsc-css-e2e가 모두 통과했고 Linux Rust 커버리지 100% (12,460/12,460), Bun 5,909 pass / 0 fail 및 함수·라인 커버리지 100%, Bun plugin preload regression 8 pass / 0 fail을 확인했습니다.

owjs3901 and others added 3 commits September 30, 2026 20:54
Global rules, @import and @font-face were stored in sorted sets and custom layers were declared alphabetically, so a later rule or layer could lose to an earlier one. The extractor now numbers global rules and records the first-use order of layers; the sheet emits them in that order.

Refs #685

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
CI reformats sources to one-line calls before tarpaulin, which put the reported misses on the repeated add_css early return and a font-face struct field. Cover the repeated add_css/add_import/add_font_face calls and build the font face in one statement.

Refs #685

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 Sep 30, 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.
  • Preserve unitless legacy vendor numbers, classify vendor time values as milliseconds and emit the Opera vendor prefix correctly; reject nested responsive arrays and known dead unprefixed CSS property names with source-located build errors, while preserving supported experimental CSS, unknown properties and documented shorthands.
  • 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.
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • Emotion's CacheProvider is available from the compat entry as a no-op that renders its children, and import { CacheProvider } from '@emotion/react' is redirected there instead of keeping @emotion/react as a runtime dependency; the cache it configures holds styles Emotion inserts at runtime, which Devup UI writes at build time
  • In rule objects written against Emotion, styled-components or vanilla-extract, a property whose value is an array of literals (background: ['red', 'linear-gradient(...)']) declares each value in order as a browser fallback, as those libraries do, instead of being read as Devup UI breakpoints; numbers get px as the library gives them. Arrays holding runtime values stay as written
  • 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.
  • Global CSS keeps the order it is written in: globalCss rules, @import and @font-face now appear in source order within each file (the later of two equal rules wins as written, repeated imports and font faces count where they are written last), object globalCss declarations on the same selector keep their written order, and custom cascade layers are declared in the order files first use them (nested layers after their parent) instead of alphabetical order
  • 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.
  • More of Tailwind CSS v4 compiles: container queries (@sm:, @max-md:, @min-[400px]:, @sm/main:) and @container utilities, mask-* (all but the gradient masks), text-shadow-, inset-shadow-, font-variant-numeric utilities (tabular-nums and the like), 3D transforms (rotate-x/y/z, translate-z, scale-z, translate-3d, scale-3d, transform-3d, backface-, perspective-, perspective-origin-), contain- and outline-hidden (with its forced-colors fallback). The project's Tailwind CSS is read further: functional @Utility with --value() and --modifier(), @apply in @Utility, and theme(--name) in arbitrary values; classes of @plugin entries stay as written. A className now compiles the classes of cn()/twMerge() calls whose arguments are all static (merged as tailwind-merge does, the later class wins), of consts bound to class strings, and of cva() calls (base, variants, compound variants). The Rsbuild plugin reads the Tailwind CSS again and compiles every module again when it changes, and the Bun plugin reads it again. 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
  • Generate React JSX-runtime calls for styled components in non-JSX source modules while preserving JSX and TSX output. No new build errors.
  • 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
  • More of Tailwind CSS v4 compiles: container queries (@sm:, @max-md:, @min-[400px]:, @sm/main:) and @container utilities, mask-* (all but the gradient masks), text-shadow-, inset-shadow-, font-variant-numeric utilities (tabular-nums and the like), 3D transforms (rotate-x/y/z, translate-z, scale-z, translate-3d, scale-3d, transform-3d, backface-, perspective-, perspective-origin-), contain- and outline-hidden (with its forced-colors fallback). The project's Tailwind CSS is read further: functional @Utility with --value() and --modifier(), @apply in @Utility, and theme(--name) in arbitrary values; classes of @plugin entries stay as written. A className now compiles the classes of cn()/twMerge() calls whose arguments are all static (merged as tailwind-merge does, the later class wins), of consts bound to class strings, and of cva() calls (base, variants, compound variants). The Rsbuild plugin reads the Tailwind CSS again and compiles every module again when it changes, and the Bun plugin reads it again. 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

@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
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • 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
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • Emotion's CacheProvider is available from the compat entry as a no-op that renders its children, and import { CacheProvider } from '@emotion/react' is redirected there instead of keeping @emotion/react as a runtime dependency; the cache it configures holds styles Emotion inserts at runtime, which Devup UI writes at build time
  • 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
  • More of Tailwind CSS v4 compiles: container queries (@sm:, @max-md:, @min-[400px]:, @sm/main:) and @container utilities, mask-* (all but the gradient masks), text-shadow-, inset-shadow-, font-variant-numeric utilities (tabular-nums and the like), 3D transforms (rotate-x/y/z, translate-z, scale-z, translate-3d, scale-3d, transform-3d, backface-, perspective-, perspective-origin-), contain- and outline-hidden (with its forced-colors fallback). The project's Tailwind CSS is read further: functional @Utility with --value() and --modifier(), @apply in @Utility, and theme(--name) in arbitrary values; classes of @plugin entries stay as written. A className now compiles the classes of cn()/twMerge() calls whose arguments are all static (merged as tailwind-merge does, the later class wins), of consts bound to class strings, and of cva() calls (base, variants, compound variants). The Rsbuild plugin reads the Tailwind CSS again and compiles every module again when it changes, and the Bun plugin reads it again. 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

@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 Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
...s/extractor/src/extract_style/extract_keyframes.rs 100.00% <100.00%> (ø)
...xtractor/src/extract_style/extract_static_style.rs 100.00% <ø> (ø)
...extractor/src/extract_style/extract_style_value.rs 100.00% <100.00%> (ø)
libs/extractor/src/extract_style/mod.rs 100.00% <ø> (ø)
.../extractor/extract_global_style_from_expression.rs 100.00% <100.00%> (ø)
...tor/src/extractor/extract_style_from_expression.rs 100.00% <ø> (ø)
...tractor/src/extractor/extract_style_from_stylex.rs 100.00% <100.00%> (ø)
libs/extractor/src/lib.rs 100.00% <ø> (ø)
libs/extractor/src/prop_modify_utils.rs 100.00% <100.00%> (ø)
libs/extractor/src/style_collector.rs 100.00% <100.00%> (ø)
... and 2 more
🚀 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.

@owjs3901 owjs3901 left a comment

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.

이 pr은 문제가 있다고 생각한다. 왜냐하면 기본적으로 global css의 경우 file 형태로 이루어 지며 완전히 기존의 속성을 제거하고 교체하는 방식이므로 order가 필요없을 터이다.

Keep main keyframe identity and synthetic Tailwind registration deduplication.

Refs #685

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