Skip to content

fix(extractor): preserve JavaScript semantics in StyleX dynamic functions - #758

Open
owjs3901 wants to merge 49 commits into
mainfrom
fix/stylex-dynamic-functions
Open

owjs3901 wants to merge 49 commits into
mainfrom
fix/stylex-dynamic-functions

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Refs #690 (W33/W33b: StyleX 동적 함수와 호환 계약 수정). #708, #715, #756의 변경을 포함한 스택 PR입니다. #708, #715, #756이 먼저 병합되어야 합니다. #757/W32의 오류 위치·Boa 변경은 병합하지 않았습니다.

요약

동적 스타일 함수의 기본값, 인자 평가 횟수·순서와 정적 호출의 CSS 출력을 JavaScript 의미에 맞게 수정합니다. 런타임 스칼라만 CSS 변수에 전달하며 스타일링 런타임은 추가하지 않습니다. 정확히 컴파일하지 못하는 함수/API 사용은 원래 파일·줄·열, 원인과 수정 방법을 포함한 빌드 오류입니다.

W19의 21개 mismatch 후보와 추가 edge를 실제 WASM 경로로 확인했습니다. 빈 firstThatWorks()의 생략, types.*의 정적 추가 인자, 숫자 정밀도, include() 순서와 동적 include를 함께 수정합니다. 조건식이 덮어써지거나 빈 클래스를 선택하더라도 원래 평가를 한 번씩 순서대로 보존합니다.

동작

  • 인자 생략 또는 undefined에만 기본값을 적용하며 null에는 적용하지 않습니다. 기존 동적 null과 attrs의 null 문자열 처리는 유지합니다.
  • 알려진 스칼라 호출은 정적 CSS로 특수화합니다. 동적 호출은 인자를 한 번씩 순서대로 평가하고 CSS 변수 값만 남깁니다. 사용하지 않는 인자와 초과 인자도 평가합니다.
  • 특수화한 숫자는 JS의 정확한 숫자 문자열과 속성 단위를 사용합니다. 0.123456은 .1235로 반올림하지 않습니다.
  • 알려진 스타일은 #708의 뒤의 키가 이기는 합성·null 해제를 유지합니다. #715의 attrs()는 style 속성 텍스트를 반환합니다.
  • 빈 firstThatWorks()는 속성 자체를 생략합니다. red와 빈 namespace는 두 합성 순서 모두 red를 유지하며 명시적 null만 이전 속성을 해제합니다.
  • types.*('red', 'blue')처럼 정적으로 읽을 수 있는 추가 인자는 평가 후 무시합니다. 런타임 값·부작용·spread를 추가 인자로 주면 무시하지 않고 오류를 보고합니다. defineVars/createTheme의 허용된 조건 값 객체는 유지합니다.
  • 정적 include는 원래 spread 위치에서 키별로 합성합니다. 포함된 키와 null metadata를 다시 게시하므로 재귀 include·props/attrs 합성에서도 순서가 보존됩니다. 호출하지 않은 동적 namespace를 include하는 것은 오류입니다.
  • createThemeContract()는 호환 확장이며 정적인 flat null/string placeholder만 받습니다. 중첩 namespace와 런타임 placeholder는 키 경로를 포함한 오류입니다. 중첩 계약 API를 새로 추가하지 않습니다.
  • namespace 원본·별칭·멤버의 재할당은 scoped 참조로 검사하며 metadata를 확정할 수 없으면 원래 선언에서 오류를 보고합니다. 다른 scope의 같은 이름은 영향을 주지 않습니다. shadowed undefined는 실제 매개변수 값으로 보존합니다.
  • 일반 스타일·동적 본문·pseudo·변수 조건 객체의 명시적 중복 키는 마지막 값만 반영합니다. 최종 empty/null로 덮인 이전 CSS는 제거하며 사용하지 않게 된 호출 인자도 원래 순서로 평가합니다.

타깃 버전의 근거

로컬 benchmark가 고정하는 @stylexjs/babel-plugin@0.19.1의 태그 커밋 1fcb9a51cbecad2c82255e0f4b45cc072fa1a95a와 공개 배포 bundle, 실제 StyleQ 0.2.1 merger를 확인했습니다. 이 근거는 소스 추적이며 upstream compiler 실행이나 특정 class hash를 주장하지 않습니다.

  • firstThatWorks helper: 인자가 없으면 빈 배열입니다.
  • raw object flatten: 빈 배열은 속성 tuple을 만들지 않으며 null tombstone이 아닙니다.
  • StyleQ merger: 실제 존재하는 키만 정의하므로 빈 namespace는 앞/뒤 모두 기존 color를 유지합니다.
  • helper argument evaluation, types implementations: 모든 추가 인자를 정적으로 평가한 뒤 unary helper가 무시합니다. runtime 추가 인자는 deopt됩니다.
  • include, createThemeContract는 해당 버전의 공식 export가 아니라 Devup 호환 확장입니다. standalone helper는 upstream에서도 compiler-required runtime placeholder이므로 이 PR은 무런타임 정책에 따라 빌드 오류로 바꿉니다.

새로 생기는 오류

모두 파일:줄:열, 문제가 된 코드, 원인과 수정 방법을 포함합니다.

  • async/generator/일반 함수 표현식, rest·구조분해 매개변수, statement/non-object 본문, 정적으로 확인하지 못하는 default/body 값, method/getter/setter namespace·본문
  • 배열·조건식·동적 키·optional call·인자 spread 등 직접 호출로 정확히 내리지 못하는 동적 함수 형태, 호출하지 않은 동적 namespace와 함수값 alias
  • 키가 겹치는 동적 합성, 동적 함수와 런타임 클래스의 혼합, 동적 호출 옆의 spread, 명시적 JSX 스타일과 동적 StyleX spread의 혼합
  • 소비되는 enclosing API 밖의 firstThatWorks/include/types.* 호출 또는 helper 함수값 escape
  • 잘못된 include 인자 수·형태와 호출하지 않은 dynamic include
  • types wrapper의 누락/spread primary 인자 또는 런타임·부작용을 포함한 추가 인자. 빈 firstThatWorks와 정적 types 추가 인자는 오류가 아닙니다.
  • @mediaOops 같은 잘못된 at-rule 토큰 또는 빈 query
  • 계약의 중첩 object·숫자·boolean·런타임 placeholder: flat 키로 바꾸고 null/string을 사용한 뒤 실제 값은 createTheme으로 설정하도록 안내
  • 재할당되는 namespace 원본·별칭 또는 변경되는 멤버: 바뀌지 않는 바인딩·멤버를 사용하도록 안내. helper 객체를 return/assignment/일반 함수 인자로 escape하는 형태도 원래 read 위치에서 오류

남는 한계

정확히 내릴 수 없는 런타임 구조·합성은 위 오류로 제한하며 스타일링 런타임이나 임의 evaluator를 추가하지 않습니다. 추가 인자의 정적 판정은 현재 상수 처리와 literal/plain object/array 형태를 사용하며 미해결 값은 추측하여 버리지 않습니다. 기존 빈 클래스 결과의 className: ''/class: '' 모양은 유지합니다. flat contract 확장은 중첩 계약을 지원하지 않습니다.

W24/#738 타입 정합성

공개 타입은 이 브랜치에서 수정하지 않았습니다. packages/react/src/utils/stylex.ts와 compat declaration의 다음 정합성은 #738/W24가 소유합니다.

  • create의 현재 입력 generic은 정적 StyleProperties만 받고 동적 함수와 null namespace를 표현하지 못합니다.
  • createThemeContract의 StyleValue는 number/undefined도 허용하지만 이 빌드는 flat null/string placeholder만 받습니다. TypeScript의 일반 string은 런타임 표현식의 빌드 가능성을 증명하지 못합니다.
  • defineVars/createTheme override와 types.* primary 타입은 빌드가 지원하는 조건 값 객체를 표현하지 못합니다.
  • types.*는 타입상 unary라 upstream처럼 정적인 추가 인자를 받는 호출이 타입 검사를 통과하지 못합니다.
  • props.style의 Record<string,string>은 동적 number/null 값을 충분히 표현하지 못합니다. attrs.style의 문자열 계약은 유지합니다.

검증

수정 전 재현

f0969a040ed4f926965468cd27fc92387bb84fa8에서 설치·WASM·루트 빌드 후 원래 두 control 및 matrix mismatch 21개를 재현했습니다. matrix는 전체를 출력하지 않고 mismatch 코드만 검색해 사용했습니다.

// 기본값 수정 전: --a를 할당하지 않아 red가 사라짐
const styles = { tone: 'b' };
export const Default = <div {...{ className: 'b' }} />;
// .b{color:var(--a)}

// async 수정 전: Promise 함수가 동기 함수처럼 컴파일됨
export const App = <div {...{ className: 'b', style: { '--a': 'red' } }} />;

height를 두 속성에 재사용하면 getter가 두 번 실행되었고, nested dynamic 호출은 문자열 namespace를 함수처럼 호출했습니다. empty-fallback QA는 수정 전 forward의 빈 class 때문에 실제 실패했습니다. 추가 재현에서는 정적 opacity가 .1235로 반올림되고 include 두 순서가 모두 red를 남겼으며 dynamic include는 할당 없는 var 참조만 남겼습니다.

수정 후 실제 WASM 출력

export const Default = <div {...{ className: 'c' }} />;
// 적용되는 CSS: .c{color:red}

style: { ...((sx0) => ({ '--a': sx0 === void 0 ? 'red' : sx0 }))(color) }
// undefined -> { '--a': 'red' }; null -> { '--a': null }
// attrs(undefined).style == '--a:red'; attrs(null).style == '--a:null'

원래 async control은 stylex-async-control.tsx:2:38에서 async arrow라는 원인과 synchronous expression-bodied arrow라는 수정 방법을 보고합니다. 실행 QA는 next()가 한 번 호출되고 두 height 값이 18px임을 확인했습니다. empty-helper 두 순서 모두 같은 red class, explicit null은 빈 class; include 후 own blue는 blue만, own blue 후 include red는 red만 남습니다. 알려진/기본 opacity는 같은 정적 class에 .123456 CSS를 만들고 runtime assignment는 숫자 0.123456을 유지합니다.

독립 GPT 검토의 다섯 결함은 26개 red-before 회귀로 재현했으며 후속 caller 확인에서도 15개 red-before 회귀를 추가했습니다. 실제 WASM 실행은 재할당 alias와 types return escape가 located error가 되고, shadowed undefined가 class를 유지하며, 중복 empty/null 본문은 class를 만들지 않으면서 사용하지 않는 인자를 한 번 실행함을 확인했습니다. 직접 s.base='external' 변경은 이전 red class를 고정하던 출력 대신 선언 위치 오류를 내고, default:'red',default:null 변수는 이전 red CSS를 더 이상 남기지 않습니다.

W19 matrix 21개는 첫 W33b 재검증에서 13개 located error와 8개 성공 출력으로 확인되었습니다. 외부 helper, async/rest, include 추가 인자, runtime types 추가 인자, 계약 runtime 값과 잘못된 at-rule은 오류이며 default/empty-helper/null/runtime-class/누락 props·attrs는 성공입니다.

최종 게이트

현재 head는 0c9d6f47d5aa64d5325f75b49904dc83909e5880이며 소스 계약 수정 84049381, Patch changepack 설명 b3ed249d, Linux coverage 회귀 0c9d6f47을 각각 전체 hook 검증으로 커밋했습니다. 전체 파일을 stage하고 unstaged/untracked 저장소 변경이 없음을 확인하여 hook이 검사한 디스크 트리와 커밋 트리를 일치시켰습니다. stash를 새로 만들거나 기존 공유 stash를 적용·삭제하지 않았습니다.

  • cargo fmt --all 및 cargo +1.99 clippy --workspace --all-targets --message-format short -- -D warnings 통과
  • StyleX 집중 회귀 427 통과 / 0 실패; 전체 workspace extractor 2276, CSS 530, WASM 34, sheet 148, integration 3 및 doctest 통과 / 0 실패
  • 네 기존 snapshot 변경을 모두 검토했습니다. 세 개는 잘못된 uncalled dynamic 성공 출력 대신 located error이며 include 하나는 서로 겹치지 않는 토큰 순서 변경입니다. 최종 전체 실행에서 새 .snap.new 없음
  • WASM 재빌드와 루트 빌드 통과; Bun 5504 통과 / 0 실패 / 함수·라인 커버리지 100%
  • bun lint 통과, 기반부터 존재한 forwardRef/use-prefix 경고 2개 외 추가 경고 없음
  • qa-runtime/qa-edges/qa-review의 재빌드된 emitted JavaScript 실제 실행 및 원래 control/최종 matrix 21개 프로브 통과. qa-review는 null default·빈 computed/optional member를 포함한 9개 control 실행
  • LSP daemon timeout 및 TypeScript/Biome LSP 미설치로 clean LSP 결과는 주장하지 않습니다. compiler/clippy/test 검증 결과를 사용합니다
  • Windows hook tarpaulin 97.61% (19148/19616)는 참고 수치이며 최종 Linux CI --fail-under 100을 권위로 사용합니다

이전 4d32efa2의 Linux CI 37240953252는 benchmark·Next/vinext e2e가 통과했으나 Rust 커버리지 99.91% (13559/13571)로 실패했습니다. 실제 edge 회귀와 불가능한 분기의 metadata 모델 정리로 처리했고 coverage 제외나 warning suppression은 사용하지 않습니다. b3ed249d의 37269061703은 benchmark·Next/vinext e2e와 테스트가 통과했으며 publish가 Rust 커버리지 99.96% (13740/13745, 5줄) 때문에 실패했습니다. 0c9d6f47은 중복 키 검사의 지배된 분기를 재배치하고 null default·빈 computed namespace·optional member의 실제 동작 및 runtime 조건 키의 정확한 오류 위치를 검증하는 10개 회귀를 추가하여 아래의 최종 Linux 100% 판정을 받았습니다.

최종 Linux CI: 제품 게이트 통과, Codecov 외부 장애

최종 37273538541, head 0c9d6f47d5aa64d5325f75b49904dc83909e5880의 실제 결론은 **failure이며 유일한 실패 단계는 Upload to codecov.io**입니다. 녹색 CI라고 주장하지 않습니다.

  • Linux Rust 커버리지 100.00% (13745/13745), uncovered source 줄 없음
  • Linux Bun 5512 통과 / 0 실패 / 함수·라인 커버리지 100%, 별도 bun-plugin preload race 회귀 6 통과 / 0 실패
  • WASM/패키지 빌드, package entry/tarball 검증, lint, 전체 테스트, landing 빌드와 두 CSS 모드 e2e 통과
  • benchmark와 Turbopack profiling, Next landing e2e 두 CSS 모드, vinext RSC e2e 두 CSS 모드의 세 job 모두 success
  • Codecov CLI 다운로드에서 curl: (35) ... sslv3 alert handshake failure가 발생하고, 이어 codecov.SHA256SUM.sig가 없어 GPG signature 검증 실패

외부 장애 규칙에 따라 workflow를 변경하거나 반복 재실행하지 않았습니다. Codecov 복구 후 coordinator가 실패한 publish job을 재실행할 수 있습니다. Codecov 뒤의 clean-publish/changepack 단계는 그 장애로 skip되었으며 통과했다고 주장하지 않습니다. 사용자 검토·병합은 수행하지 않았고 #708, #715, #756 이후에 병합해야 합니다.

owjs3901 and others added 30 commits October 1, 2026 21:16
…rations

css(a, b) composing classes whose styles the build knows, bound to css() in the file or exported by another module, merges their atoms per property, selector, breakpoint and layer, conditions included, instead of joining classes whose winner the stylesheet order picked. vanilla-extract style([...]) passes each composed style as its own argument, keeping a style composed again later.

Refs #688

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

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

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

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

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

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

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

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

Refs #688

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…JSX spreads win

Refs #688

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…led order and JSX element className

Refs #688

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

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

Refs #688

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…e parts

Refs #688

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…uate shouldForwardProp at build time

Refs #689

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

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

Refs #689

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

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

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

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

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

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

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

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

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… and conditional styles

Refs #690

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…h Emotion

Refs #690

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ed parameters

Refs #690

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ead through namespaces

Refs #695

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…akes from the visitor

Refs #695

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…eclared after their use

Refs #695, #686

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

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

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

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

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

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

Refs #695

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

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

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901 and others added 15 commits October 4, 2026 06:36
Refs #695

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690; prerequisite PR #715

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690; prerequisite PR #708 with #715 and #756 preserved

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 4, 2026 •

Copy link
Copy Markdown
Contributor

Changepacks

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

Patch

  • A project module re-exporting Devup UI (export { Box, css } from '@devup-ui/react', export * from '@devup-ui/react', export { styled as s }, export * as Devup, a default export of an import, chains of barrels and the compat entry) now works: a file importing Box or css from it compiles exactly as if it imported from the package, following the barrel through the resolver (relative paths, tsconfig paths, package exports), and hasDevupUI sees such a file. Members read from a namespace or default import of the package (Devup['css'], const { css } = Devup, Devup.css as a value) compile where the build can follow them, Devup.x for a name the package compiles being read like the named import. New build errors, located at the use: what a barrel re-exports but the build cannot read (an unresolvable export * or module, a namespace of modules re-exporting the package) is an error where its name is used instead of a runtime crash, and a namespace member read by a key the build cannot know (Devup[key]), written, or destructured in a form it cannot follow (a rest, a computed key, a default value, a nested pattern, an exported declaration, or members mixed with ones that stay at runtime) is an error that names the members to read. A namespace passed on whole (Object.keys(Devup), spyOn(Devup, 'css'), export default Devup) is left as it is
  • css(a, b) composing classes whose styles the build knows (a const bound to css() in the file, or a css() result another module exports with a rule object every value of which is known) merges their styles: a later part's declaration replaces an earlier one's for the same property, selector, breakpoint and layer, also under conditions (css(base, cond && danger)) and for ||/?? parts, where the classes used to be joined and the stylesheet order picked the winner. vanilla-extract style([a, b]) passes each composed style as its own argument, so the later one wins, and a style composed again later (style([a, b, a])) is kept. Classes the build does not know (strings, props, CSS Modules) are kept as they are
  • styled(Base) extending a styled component the file binds to a const renders Base's tag directly with Base's styles composed under its own, so the extension's declarations replace Base's for the same property, selector, breakpoint and layer instead of losing to them by stylesheet order; Base's attrs apply before the extension's. attrs merge as styled-components merges them: className is joined with the caller's, style is merged and other props replace earlier ones. On an element, a className or style spread after the explicit prop now wins, as React merges props in the order they are written
  • stylex.props() and stylex.attrs() merge namespaces this file creates key by key as StyleX does: a later namespace's key replaces an earlier one's, so props(red, blue) is blue and props(blue, red) is red, and a key set to null removes the earlier value; conditions (cond && styles.x, cond ? a : b) choose per key. Arguments the build cannot read key by key (namespaces from other modules, props, dynamic namespaces, include()) keep the previous joined behavior
  • Emotion's now compiles at build time with no runtime: the element becomes what its child function ({ css, cx, theme }) => ... renders, each css and cx call becomes the classes it composes (later styles replace earlier ones; css reads strings as CSS text and its tagged templates split around mixins; cx keeps unknown classes, reads { name: condition } objects as conditional classes and composes nested css and cx calls), and theme.a.b in their values becomes var(--a-b). Numbers in css rules are px lengths, as Emotion reads them, and top-level constants are inlined. New build errors, each at file:line:col: a without exactly one child function taking { css, cx, theme } that gives what it renders at once (attributes, other children, rest or default parameters, async or generator functions, several statements); reading the theme other than as theme.a.b in a css value; using css or cx other than by calling them; a cx object with a spread, getter or method; parts css and cx cannot compose (runtime calls, spreads, a condition choosing between styles and classes). @devup-ui/react/compat exports ClassNames with the ClassNamesContent and ClassNamesArg types, and the @emotion/react declarations include them
  • Emotion and styled-components component selectors now compile at build time: a styled component the file defines that another style selects (${Child} { ... } in CSS text, [Child] or [&:hover ${Child}] as a rule key) gets a short marker class, which only such components get, and the selector reads it as .marker; a component extending it, or an element inlining it for its css prop, keeps the marker. Selectors written without & now nest under the component as stylis nests them (ul { ... } is & ul, a selector list nests each selector, :hover applies to the component itself) instead of becoming global rules, and rule-object keys starting with ., #, [, *, >, + or ~ are read as nested selectors instead of properties
  • Emotion's css prop compiles at build time while @emotion/react is aliased: on tags and Devup UI components always, and on every element once the file imports @emotion/react or @emotion/styled or names @emotion/react in a @jsxImportSource pragma; jsx, jsxs and jsxDEV from @emotion/react/jsx-runtime (or jsx-dev-runtime) and jsx from @emotion/react compile it too, so libraries built with Emotion's JSX runtime do as well. The prop becomes the element's className, with CSS variables in its style for values only the runtime gives: arrays and conditions compose with a later part replacing what an earlier one sets, css() classes the file knows compose by their styles, strings and templates are CSS text, css`` mixins split the text around them, a function of the theme reads theme.a.b as var(--a-b), unitless numbers are px as in Emotion (constants included), and a className holding known css() classes overrides the prop as Emotion's registered classes do. A styled component the file defines renders its tag in the element's place when the prop overrides its styles and it renders a tag with no attrs or props read and the element has no spread, as or forwardedAs. Emotion's JSX runtime imports become react/jsx-runtime, the pragma names react, and jsx comes from @devup-ui/react/compat (React's createElement); @devup-ui/react/compat/css-prop types the prop on React.Attributes. New build errors, each with file:line:column and the code: a css prop part the build cannot read (a call, an element), a style object declared inside a function or with let, a binding only running the module gives or code changes, a theme function that does more than return rules or reads the theme other than as theme.a.b in a value, an interpolation CSS text cannot place or a mixin inside a nested rule, and a css prop overriding a styled component's styles where its tag cannot be rendered in place. Composing css() with a part that reads a known binding (such as a keyframes name) now reads its value
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • hasDevupUI takes the import aliases and is true for a file extraction changes: one importing a redirected package (@emotion/styled, ...), naming @emotion/react in a @jsxImportSource pragma, or in a project whose tsconfig jsxImportSource is @emotion/react, so the Bun plugin no longer skips files whose only Devup UI is Emotion's css prop or an aliased import. Aliases of Devup UI now compile like the original: export const B = Box, const c = css; export { c as cc }, export const D = Devup and export const C = Devup.css in a barrel (the barrel module exports them as re-exports of the package, and what imports them follows them), export { Box } after an import, export default Box, const D = Devup namespace aliases, and const inner = css declared inside a function. A css() or keyframes() const that a function reads before its declaration compiles. New build error: a css()/keyframes() const read before its declaration runs (outside a function) is located and says to move the declaration above its first read
  • Preserve JavaScript semantics for exact imported enum and Math values including negative zero, lexical constants, binding-aware CommonJS require, React createElement calls, type-only imports, and source-ordered props evaluation with native spread snapshots and CSS-variable fallbacks without a styling runtime. Migrate UI-kit style defaults by pure reordering and test reset CSS through compiled output. Report located build errors for runtime styleOrder values, unknowable whole-object style overrides, opaque selector literals, genuinely unmovable lazy logical suspension, Devup UI namespaces passed whole to runtime code, and immediate style reads in the temporal dead zone of lexical bindings; deferred initialized reads keep folding and pre-assignment var reads remain dynamic.
  • Resolve compiled styling APIs and components by their lexical binding so shadowed local names, including minified library identifiers, remain ordinary JavaScript and JSX. No new build errors; existing diagnostics now apply only to the compiled binding they describe.
  • Styled components the build generates are wrapped in React's forwardRef, so a ref passed to them reaches the element they render on React 18 as well as React 19; the file imports forwardRef from react when it defines one
  • Styled components no longer pass every prop to the tag they render: $ props, theme and the props their style functions or attrs read are kept away from a tag unless it takes them as attributes, and shouldForwardProp (Emotion options or styled-components withConfig) is evaluated at build time. A shouldForwardProp the build cannot evaluate is a build error naming the file, line and code, with the forms it accepts. An element using a styled component the file defines drops props the component neither reads nor passes on; spread props are passed as written
  • Styled components render what their as prop names, defaulting to the tag or component they were defined with, and pass forwardedAs on as as, as styled-components and Emotion do; as used to reach the rendered tag as an attribute. Component.withComponent(target) on a styled component the file binds to a const builds a component rendering the same styles and attrs as target (a tag name or a component JSX can name), where it used to call a method the generated function does not have and throw
  • 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
  • Preserve exact JavaScript defaults, numeric arguments, source-order evaluation and final duplicate assignments in StyleX dynamic functions, static composition and variable conditions, with no styling runtime. Empty firstThatWorks omits its property, statically readable types helper extra arguments are evaluated and ignored, ordered static includes retain keyed precedence and null resets, shadowed undefined remains a real binding, and attrs returns serialized style text. Report located build errors with repairs for unsupported function forms, nested or overlapping dynamic calls, incompatible JSX/spread compositions, uncalled dynamic includes or function aliases, unconsumed helper calls and value escapes, runtime helper extra arguments, malformed at-rule conditions, non-flat or runtime theme-contract placeholders, and written namespace bindings, aliases or members whose metadata cannot be known exactly.

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

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • hasDevupUI takes the import aliases and is true for a file extraction changes: one importing a redirected package (@emotion/styled, ...), naming @emotion/react in a @jsxImportSource pragma, or in a project whose tsconfig jsxImportSource is @emotion/react, so the Bun plugin no longer skips files whose only Devup UI is Emotion's css prop or an aliased import. Aliases of Devup UI now compile like the original: export const B = Box, const c = css; export { c as cc }, export const D = Devup and export const C = Devup.css in a barrel (the barrel module exports them as re-exports of the package, and what imports them follows them), export { Box } after an import, export default Box, const D = Devup namespace aliases, and const inner = css declared inside a function. A css() or keyframes() const that a function reads before its declaration compiles. New build error: a css()/keyframes() const read before its declaration runs (outside a function) is located and says to move the declaration above its first read

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

Patch

  • Preserve JavaScript semantics for exact imported enum and Math values including negative zero, lexical constants, binding-aware CommonJS require, React createElement calls, type-only imports, and source-ordered props evaluation with native spread snapshots and CSS-variable fallbacks without a styling runtime. Migrate UI-kit style defaults by pure reordering and test reset CSS through compiled output. Report located build errors for runtime styleOrder values, unknowable whole-object style overrides, opaque selector literals, genuinely unmovable lazy logical suspension, Devup UI namespaces passed whole to runtime code, and immediate style reads in the temporal dead zone of lexical bindings; deferred initialized reads keep folding and pre-assignment var reads remain dynamic.

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

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

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

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

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

Patch

  • Emotion's now compiles at build time with no runtime: the element becomes what its child function ({ css, cx, theme }) => ... renders, each css and cx call becomes the classes it composes (later styles replace earlier ones; css reads strings as CSS text and its tagged templates split around mixins; cx keeps unknown classes, reads { name: condition } objects as conditional classes and composes nested css and cx calls), and theme.a.b in their values becomes var(--a-b). Numbers in css rules are px lengths, as Emotion reads them, and top-level constants are inlined. New build errors, each at file:line:col: a without exactly one child function taking { css, cx, theme } that gives what it renders at once (attributes, other children, rest or default parameters, async or generator functions, several statements); reading the theme other than as theme.a.b in a css value; using css or cx other than by calling them; a cx object with a spread, getter or method; parts css and cx cannot compose (runtime calls, spreads, a condition choosing between styles and classes). @devup-ui/react/compat exports ClassNames with the ClassNamesContent and ClassNamesArg types, and the @emotion/react declarations include them
  • Emotion's css prop compiles at build time while @emotion/react is aliased: on tags and Devup UI components always, and on every element once the file imports @emotion/react or @emotion/styled or names @emotion/react in a @jsxImportSource pragma; jsx, jsxs and jsxDEV from @emotion/react/jsx-runtime (or jsx-dev-runtime) and jsx from @emotion/react compile it too, so libraries built with Emotion's JSX runtime do as well. The prop becomes the element's className, with CSS variables in its style for values only the runtime gives: arrays and conditions compose with a later part replacing what an earlier one sets, css() classes the file knows compose by their styles, strings and templates are CSS text, css`` mixins split the text around them, a function of the theme reads theme.a.b as var(--a-b), unitless numbers are px as in Emotion (constants included), and a className holding known css() classes overrides the prop as Emotion's registered classes do. A styled component the file defines renders its tag in the element's place when the prop overrides its styles and it renders a tag with no attrs or props read and the element has no spread, as or forwardedAs. Emotion's JSX runtime imports become react/jsx-runtime, the pragma names react, and jsx comes from @devup-ui/react/compat (React's createElement); @devup-ui/react/compat/css-prop types the prop on React.Attributes. New build errors, each with file:line:column and the code: a css prop part the build cannot read (a call, an element), a style object declared inside a function or with let, a binding only running the module gives or code changes, a theme function that does more than return rules or reads the theme other than as theme.a.b in a value, an interpolation CSS text cannot place or a mixin inside a nested rule, and a css prop overriding a styled component's styles where its tag cannot be rendered in place. Composing css() with a part that reads a known binding (such as a keyframes name) now reads its value
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • 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

  • Preserve JavaScript semantics for exact imported enum and Math values including negative zero, lexical constants, binding-aware CommonJS require, React createElement calls, type-only imports, and source-ordered props evaluation with native spread snapshots and CSS-variable fallbacks without a styling runtime. Migrate UI-kit style defaults by pure reordering and test reset CSS through compiled output. Report located build errors for runtime styleOrder values, unknowable whole-object style overrides, opaque selector literals, genuinely unmovable lazy logical suspension, Devup UI namespaces passed whole to runtime code, and immediate style reads in the temporal dead zone of lexical bindings; deferred initialized reads keep folding and pre-assignment var reads remain dynamic.

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

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

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

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

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

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@codecov

codecov Bot commented Oct 4, 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% <ø> (ø)
libs/css/src/lib.rs 100.00% <100.00%> (ø)
libs/css/src/theme_tokens.rs 100.00% <ø> (ø)
libs/extractor/src/barrel.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/aliases.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/gate.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/namespace_aliases.rs 100.00% <100.00%> (ø)
libs/extractor/src/build_time_values.rs 100.00% <100.00%> (ø)
libs/extractor/src/build_time_values/exact_math.rs 100.00% <100.00%> (ø)
libs/extractor/src/composition.rs 100.00% <100.00%> (ø)
... and 49 more

... and 23 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.

owjs3901 and others added 4 commits October 5, 2026 07:31
Refs #690

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

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

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

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

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

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

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