Skip to content

fix(extractor): finish static composition and styled capture limits - #754

Open
owjs3901 wants to merge 44 commits into
mainfrom
fix/composition-rest
Open

owjs3901 wants to merge 44 commits into
mainfrom
fix/composition-rest

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Refs #688, #690, #695, #685 (정적으로 읽을 수 있는 합성의 남은 한계).

이 브랜치는 main 기반이며 #736과 #745의 커밋을 모두 포함합니다. 두 PR과 그 선행 의존성(#740은 이들을 통해 전이적으로 포함)이 먼저 병합되어야 하고, 이 PR은 #736과 #745 뒤에 병합해야 합니다. 자동 병합은 사용하지 않습니다.

요약

  • namespace로 가져온 styled 컴포넌트를 CSS 텍스트 선택자, 계산된 규칙 키, JSX와 컴파일된 JSX(jsx(UI['Child'], ...))의 css prop에서 named import와 같은 경로로 읽습니다.
  • 인자나 mixin으로 쓰는 인라인 태그드 css, 정확히 읽을 수 있는 배열 spread를 클래스 문자열이 아니라 스타일 단위로 합성합니다. 알려진 mixin은 중첩 선택자, @media, @layer 안에서도, 조건으로 고른 경우에도 D1로 합성합니다.
  • props 함수가 규칙 객체 전체를 고르는 경우(?:, &&, ||, ??)를 속성별 조건으로 컴파일합니다. ||는 falsy, ??는 nullish 기준을 그대로 따릅니다.
  • 함수 안의 읽기 전용 스타일 객체/배열(희소 배열 포함)은 정적인 구조를 펼치고 런타임 잎만 CSS 변수로 남깁니다. 생성 코드의 임시 이름은 사용자 바인딩(rest, style, className 등)과 충돌하지 않습니다.
  • 읽기 전용 const에 묶인 styled 콜백(화살표/function)을 함수 실행 없이 인라인 콜백과 같이 읽습니다.
  • CSS 텍스트의 이름 있는 @layer 블록을 객체 '@layer'와 같은 레이어 메타데이터로 읽습니다 (Refs [전수조사/E] CSS 생성 정확성: 순서, layer, theme, 유효하지 않은 CSS #685).
  • 스타일링 런타임과 새 의존성은 없습니다. changepack은 모두 Patch입니다.

동작

합성은 D1입니다. 속성 + 선택자 + 브레이크포인트 + 레이어가 같으면 뒤의 선언이 이기고, 다른 선택자/반응형 단계/레이어의 선언은 남습니다. props의 className이나 외부 클래스처럼 런타임만 아는 합성은 기존대로 CSS 캐스케이드를 따릅니다.

Namespace 컴포넌트

${UI.Child}, [UI.Child], <UI.Child css={...}>, 컴파일된 jsx(UI['Child'], { css })가 같은 정의에 도달합니다. 이름 문자열이 아니라 실제 import 바인딩을 읽으므로 같은 이름의 매개변수는 import 정의로 바뀌지 않습니다. type import, 없는 export, 동적 멤버는 값 정의가 되지 않습니다.

import { jsx } from '@emotion/react'
import * as UI from './ui'
export const element = jsx(UI['Child'], { css: { color: 'blue' } })

공개 WASM API로 컴파일한 뒤 SSR한 결과는 <span>이고 색은 blue 하나만 남으며, 기반의 padding은 유지됩니다(게이트 2). namespace css prop의 실제 SSR 출력:

<span title="child" class="padding-0-4px--255 color-0-blue--255 c-1-Child"></span>

알려진 mixin과 모듈 계산

다른 모듈의 알려진 정의도 합성합니다. probe-imported-known.log의 실제 출력(B 정의의 원문 줄 그대로, 4줄 모두):

export const B = __devupForwardRef((__devupRefProps, __devupRef) => (({ style, className, as: DevupAs = "div", forwardedAs, ...rest }) => <DevupAs {...(({ "theme": __devupOmit0, ...__devupDom }) => __devupDom)(rest)} as={forwardedAs} className={["c-1-B c-0-Rules color-0-blue--255 padding-0-8px--255", className].filter(Boolean).join(" ")} style={style} />)({
	...__devupRefProps,
	ref: __devupRef
}));

같은 로그의 CSS 중 이 파일의 원자 규칙(테마 @layer t 앞부분은 생략):

.color-0-blue--255{color:blue}.padding-0-8px--255{padding:8px}

중첩 레이어 안의 mixin과 조건 mixin:

const mixin = css({ color: 'red', padding: '4px' })
export const Layered = styled.div`@layer base { ${mixin}; color: blue; }`
export const Conditional = styled.div`${p => p.on && mixin}; color: blue;`

Layered는 색이 blue 하나이고 padding은 레이어가 붙은 클래스로 남습니다. Conditional은 on이 true/false 모두 색이 blue이며 padding은 on일 때만 붙습니다(게이트 3).

정확한 계산(scaled(2) 등)은 기존 Boa의 닫힌 계산 필터로만 읽습니다. 모듈 전체나 알 수 없는 효과는 실행하지 않습니다.

전체 규칙 조건

export const Or = styled.div(p => (p.on && { color: 'red' }) || { color: 'blue' })
export const Nullish = styled.div(p => (p.on ? { color: 'red' } : null) ?? { color: 'blue' })

두 컴포넌트 모두 on이면 red, 아니면 blue입니다(게이트 5; 이 게이트는 색 단언만 기록하며 HTML 원문은 남기지 않았습니다).

선택되지 않은 분기와 분기에 없는 속성은 앞의 스타일을 지우지 않습니다. 아래는 위 Or/Nullish의 출력이 아니라, 수동 QA의 별도 Toggle 픽스처(&& 조건으로 규칙 전체를 고르는 경우)의 실제 SSR 출력으로, 꺼진 분기에서 앞의 blue와 padding이 유지됨을 보여줍니다:

<!-- on=false --><div class="c-0-Toggle padding-0-4px--255 color-0-blue--255 "></div>
<!-- on=true  --><div class="c-0-Toggle padding-0-4px--255 color-0-red--255 color-0-green-_a__c_hover-255"></div>

원래 함수가 읽는 props는 변환 전에 기록하여 기본 DOM 전달 정책에서 on 같은 스타일 입력이 요소로 새지 않게 합니다.

함수 안의 구조, 이름 위생, 평가 시점

export function Rest(props) { const rest = { color: props.color }; const C = styled('div', rest); return <C /> }
export function Sparse(props) { const rules = [, { color: props.color }]; return <div css={rules} /> }

rest/style/className으로 이름 붙인 지역 값도 생성 코드의 구조 분해 이름과 충돌하지 않고 purple이 렌더링됩니다(게이트 1). 희소 배열의 빈 칸은 건너뜁니다(게이트 6). 값은 원래 데이터 경로에서 한 번만 읽습니다:

<div class="color-0--_a__c_hover-255 color-0---255" style="--color-0-_a__c_hover:green;--color-0-:purple"></div>
<div class="line-height-0---255 padding-0---255" style="--line-height-0-:2;--padding-0-:3px"></div>

평가 시점은 원래 JS와 같습니다. 나중에 호출되는 콜백 안에서 뒤에 선언된 const를 읽는 것은 정상이고, 즉시 호출(IIFE)로 TDZ에 걸리는 코드는 위치가 있는 빌드 오류입니다(게이트 7).

// 정상: on이면 red, 아니면 blue
const rules = { color: p => p.on ? tone : 'blue' }; const tone = 'red'
// 빌드 오류: probe-gate-iife.tsx:<줄>:<열>
const rules = { color: (() => tone)() }; const tone = 'red'

변수에 바인딩된 styled 콜백

const rules = p => ({ ... }); styled.div(rules)처럼 모듈/지역의 읽기 전용 const에 묶인 화살표·function 콜백을 인라인 콜백과 같은 경로로 읽습니다. 함수를 실행하거나 모르는 구조를 추측하지 않고 선언만 정적으로 따라갑니다. 캡처한 이름은 어휘 위생을 지켜 원래 공개 export와 live binding을 그대로 둡니다. 숫자 단위(Emotion px, native 간격 배율)는 사용하는 곳마다 라이브러리 정보로 정하며, 콜백 초기화 식을 전역으로 고쳐 쓰지 않고 매개변수와 데이터로 쓰는 값도 바꾸지 않습니다.

import styled from '@emotion/styled'; const rules=p=>({padding:3,lineHeight:2}); export const Choice=styled.div(rules);

callback-units-green의 실제 SSR 출력(같은 규칙의 native styled는 p:3이 12px):

<div class="c-1-Choice padding-0-3px--255 line-height-0-2--255"></div>

리뷰에서 고친 네 가지

기능 리뷰가 찾은 문제를 고쳤고, 각각 회귀 테스트가 있습니다.

  1. 지역 함수 캡처 위생. 함수 안 지역 값의 이름이 생성 코드 이름이나 사용자가 가린(shadow) 이름과 겹쳐도 원래 바인딩을 읽습니다. 회귀: libs/extractor/src/local_shape_hygiene_w27_tests.rs, bound_callback_alias_tests.rs.
  2. 바인딩 콜백의 export와 live binding 보존. export된 캡처 값은 지역 선언과 live export 지정자로 바뀌어 공개 이름과 이후 갱신이 그대로입니다. 회귀: libs/extractor/src/bound_callback_export_tests.rs.
  3. 왼쪽 ||/?? 피연산자의 Emotion px. 규칙을 담는 양쪽 피연산자가 모두 px가 됩니다. &&의 왼쪽은 조건으로 남고, 매개변수/기본값/데이터 인자는 바꾸지 않습니다. 회귀: libs/extractor/src/import_alias_visit.rs, bound_callback_numeric_tests.rs, numeric_context_w27_tests.rs. 추가 전체 추출 회귀 24건은 생성된 클래스 선택을 실제로 평가하여 Emotion/styled-components의 3px/4px, native의 12px/16px와 단위 없는 값/0/커스텀 변수를 비교합니다.
  4. 바인딩되거나 가려진 undefined의 전체 규칙. 실제 전역 undefined, void 0, null은 빈 규칙이지만, 같은 이름의 지역/가린 바인딩이 빌드가 알 수 없는 규칙 구조이면 원래 콜백 위치의 빌드 오류입니다. 스칼라 값은 CSS 변수로 남고, 도달할 수 없는 분기는 건너뜁니다. 회귀: libs/extractor/src/extractor/w27_props_rule_choices_regressions.rs.

CSS 텍스트의 레이어 (Refs #685)

export const composed = css(css`@layer base { color: red; margin: 1px; } @layer other { color: green; }`, css`@layer base { color: blue; }`)
export const nested = css`@layer base { @layer reset { &:hover { color: red; } @media print { margin: 1px; } } }`
export const Layered = styled.div`@layer base { width: ${(p) => p.width}; &:hover { color: red; } }`

probe-layers.log의 실제 출력:

export const composed = "color-0-green-_at_layer_s_other-255 margin-0-1px-_at_layer_s_base-255 color-0-blue-_at_layer_s_base-255";
export const nested = "margin-0-1px-_at_media_s_print_at_layer_s_base_d_reset-255 color-0-red-_a__c_hover_at_layer_s_base_d_reset-255";

같은 로그의 CSS 중 테마 @layer t 블록 뒤의 컴포넌트 레이어 부분 전체(원문은 한 줄이며, 가독성을 위해 최상위 @layer 블록 경계에서만 줄을 나눴고 문자는 바꾸지 않았습니다):

@layer base{.color-0-blue-_at_layer_s_base-255{color:blue}.color-0-red-_at_layer_s_base-255{color:red}.margin-0-1px-_at_layer_s_base-255{margin:1px}.width-0--_at_layer_s_base-255{width:var(--width-0-_at_layer_s_base)}.color-0-red-_a__c_hover_at_layer_s_base-255:hover{color:red}}
@layer base.reset{.color-0-red-_a__c_hover_at_layer_s_base_d_reset-255:hover{color:red}@media print{.margin-0-1px-_at_media_s_print_at_layer_s_base_d_reset-255{margin:1px}}}
@layer components{.color-0-blue-_at_layer_s_components-255{color:blue}}
@layer other{.color-0-green-_at_layer_s_other-255{color:green}}

composed의 클래스 목록에서 base의 red는 뒤의 blue로 대체되고(시트에는 .color-0-red-_at_layer_s_base-255 규칙이 남아 있지만 위 composed 클래스 목록에는 붙지 않습니다) other의 green은 남습니다. 중첩은 base.reset이 되며, 동적 CSS 변수에도 레이어가 유지됩니다. 레이어 이름은 텍스트/객체가 같은 CSS 식별자 검증과 정규화를 쓰고, escaped dot과 중첩 dot을 구분합니다. 레이어 순서는 globalCss에 선언합니다. 레이어 이름이 데이터로만 쓰이는 객체(data['@layer'].inherit)는 스타일 규칙으로 오인하지 않으며 콜백은 한 번만 호출됩니다(게이트 4).

새로 생기는 오류

모두 파일:줄:열, 문제가 된 코드, 필요한 조건을 담은 빌드 오류입니다.

  • 컴포넌트 스타일의 잘못된 레이어 이름/CSS-wide 키워드:
    probe-layer-invalid-name.tsx:2:42: `css()` cannot use `@layer inherit` at build time: name the layer, for example `@layer components { ... }`: component layer names must be dot-separated CSS identifiers, not CSS-wide reserved keywords; declare layer order with `globalCss`
    
  • 컴포넌트 스타일의 익명 레이어:
    probe-layer-anonymous.tsx:2:28: `css()` cannot use `@layer {` at build time: name the layer, for example `@layer components { ... }`: component layer names must be dot-separated CSS identifiers, not CSS-wide reserved keywords; declare layer order with `globalCss`
    
  • 컴포넌트 스타일의 레이어 순서 문장:
    probe-layer-order.tsx:2:28: `css()` cannot use `@layer base, other;` at build time: layer order statements belong in `globalCss`, not component styles; declare the order with `globalCss` and use named `@layer` blocks here
    
    여러 파일의 atom을 합친 출력에서 익명 레이어의 첫 등장 순서를 보장할 수 없으므로 내부 이름을 임의로 만들지 않습니다. 기존 globalCss 텍스트의 순서 선언과 익명 레이어 처리는 그대로입니다.
  • 빌드가 내용을 알 수 없는 mixin을 중첩 선택자/@media/@layer 안에 넣으면, 그 mixin이 감싸는 선택자/media/레이어 문맥을 함께 가져야 한다는 위치 있는 오류를 냅니다. 알려진 mixin은 위처럼 합성됩니다.
  • 즉시 실행되는 초기화에서 TDZ 변수를 읽는 지역 스타일은 위치 있는 오류입니다.
  • 바인딩된 styled 콜백이 안전하지 않거나, 변경 가능하거나, 스타일 밖으로 새거나, TDZ에 걸리면 원래 소스 위치의 오류를 냅니다. 바인딩 콜백 안의 잘못된 레이어도 인라인과 같은 오류입니다(callback-final):
    callback-layer-bound-arrow-invalid.tsx:1:71: `styled()` cannot use `@layer inherit` at build time: name the layer, for example `@layer components { ... }`: component layer names must be dot-separated CSS identifiers, not CSS-wide reserved keywords; declare layer order with `globalCss`
    
  • 지역이나 가린 바인딩 undefined를 빌드가 알 수 없는 전체 규칙 구조로 쓰면 원래 콜백 위치의 오류입니다(전역 undefined/void 0/null은 그대로 허용).
  • 새로 인식한 namespace styled 컴포넌트의 css prop이 합성할 수 없는 조건으로 기반 스타일을 덮으면 named import와 같은 기존 overlap 오류를 냅니다.
  • 알 수 없는 spread/전체 규칙 구조, 변경되거나 스타일 밖으로 전달된 지역 구조, 독립 CSS API의 런타임 값은 기존의 위치 있는 오류를 유지합니다. 실제 출력:
    probe-unknown-spread.tsx:2:40: Cannot compose `...pieces` at build time: each style must be a rule object, a class, or a condition choosing between them
    

남는 한계

  • styled(Link), 런타임 컴포넌트 팩터리, 빌드가 읽을 수 없는 모듈/외부 클래스, 런타임에만 선택되는 전체 규칙 키는 정확한 정의를 만들 수 없습니다. 외부 클래스와 props의 className 합성은 CSS 캐스케이드를 따르고, 알 수 없는 규칙 구조는 빌드 오류입니다.
  • 모듈 스코프의 효과/변경 가능한 값/임의 런타임 입력에 의존하는 정의는 다른 파일에서 재평가하지 않습니다. 자신의 모듈에서 CSS 변수로 값을 넣는 기존 경로를 유지합니다.
  • 내보내는 styled 컴포넌트의 마커 정책은 #736과 같습니다. 정의 모듈은 다른 파일의 선택 여부를 모르므로 export마다 마커를 유지합니다.
  • 이 베이스의 Emotion 규칙 객체 배열은 #717이 병합되기 전까지 반응형으로 읽힙니다. fix(extractor): read property arrays in aliased library rules as fallbacks #717(fix/emotion-fallback-arrays, main 기반, EMO-15)이 Emotion/styled-components/vanilla-extract의 property-array를 브라우저 fallback으로 고칩니다. 이 PR은 그 작업을 중복하지 않으며, 두 스택이 병합된 뒤 결합 동작을 확인합니다. 브레이크포인트 보존 테스트는 의도에 맞게 native @devup-ui/react를 사용합니다.
  • 커리하지 않은 형태 styled(tag, styles)의 공개 계약은 기존대로 객체만 받습니다. 이 PR은 타입이나 API를 넓히지 않습니다.

검증

최종 Linux CI 37201011483 성공 (HEAD ab459f82c29227fbec348c0ea8f2374db7dafec0). 네 job 모두 success: benchmark 111432518222, publish 111432518305, landing-next-e2e 111432518313, vinext-rsc-css-e2e 111432518330. Codecov/patch도 success입니다.

  • Linux tarpaulin --fail-under 100: 100.00% (13283/13283). 첫 실행의 미커버 16줄은 모두 닫혔습니다. 변경하지 않은 파이프라인 파일 composition.rs도 117/117로 추가 확인했습니다.
  • Linux Rust 테스트 2882개 통과(extractor unit 2166개, integration 3개 포함).
  • Linux Bun 테스트 5499개 pass, 0 fail, 표시 집계 100%. 로컬 Bun 5491개와 개수가 다른데, 로컬은 이 브랜치 소스 기준이고 CI는 main과 병합한 커밋을 실행하기 때문입니다. bun-plugin preload 회귀 6 pass, 0 fail.
  • publish의 landing E2E: 기본 130 passed, singleCss 130 passed.
  • Vite plugin 파일은 Linux에서 함수 100%, 줄 99.76%(로컬 99.73%)입니다. 이 PR이 바꾸지 않았고 별도 작업(W20) 범위이므로 모든 Bun 파일이 100%라고 주장하지 않습니다.
변경한 Rust 파일 53개의 Linux 커버리지 (경로는 libs/extractor/src/ 기준)

git diff --name-only 743e59a5 HEAD -- libs/extractor/src의 .rs 53개입니다. 35개는 publish 로그의 || 파일: covered/total 행이 있고 모두 covered = total입니다. 나머지 18개는 테스트 전용 모듈이라 tarpaulin이 파일별 행을 내지 않으므로 N/A로 적습니다(100%로 추정하지 않으며 제외 설정은 추가하지 않았습니다).

파일 Linux 커버리지
bound_callback_alias_tests.rs N/A (행 없음)
bound_callback_captures.rs 11/11
bound_callback_export_tests.rs 38/38
bound_callback_numeric_tests.rs 2/2
bound_style_callbacks.rs 91/91
bound_style_callbacks_tests.rs 2/2
capture_public_exports_w27.rs 19/19
composition_coverage_tests.rs N/A (행 없음)
composition_runtime_coverage_tests.rs N/A (행 없음)
css_composition_rest_tests.rs N/A (행 없음)
css_utils.rs 351/351
css_utils/component_layer_coverage_tests.rs N/A (행 없음)
css_utils/component_layer_objects.rs 40/40
css_utils/component_layer_policy_tests.rs N/A (행 없음)
css_utils/component_layer_tests.rs N/A (행 없음)
css_utils/component_layers.rs 52/52
css_utils/layer_blocks.rs 33/33
css_utils/layer_names.rs 84/84
extractor/extract_style_from_expression.rs 359/359
extractor/extract_style_from_styled.rs 441/441
extractor/styled_choice_test_support.rs 36/36
extractor/styled_mixin_context.rs 49/49
extractor/styled_rule_choices.rs 59/59
extractor/styled_rule_parts.rs 23/23
extractor/styled_template_composition.rs 64/64
extractor/styled_template_parts.rs 52/52
extractor/w27_coverage_tests.rs N/A (행 없음)
extractor/w27_props_rule_choices_regressions.rs 3/3
extractor/w27_props_rule_choices_tests.rs 30/30
extractor/w27_styled_choice_coverage.rs 20/20
extractor/w27_styled_composition_coverage.rs N/A (행 없음)
extractor/w27_styled_context_coverage.rs N/A (행 없음)
extractor/w27_styled_known_mixins_regressions.rs 3/3
extractor/w27_styled_known_mixins_tests.rs 17/17
extractor/w27_styled_linux_coverage.rs N/A (행 없음)
extractor/w27_styled_statement_coverage.rs N/A (행 없음)
extractor/w27_styled_template_coverage.rs N/A (행 없음)
extractor/w27_styled_template_parts_tests.rs N/A (행 없음)
import_alias_visit.rs 349/349
imported_constants.rs 1149/1149
imported_styled.rs 259/259
imported_styled_coverage_tests.rs N/A (행 없음)
lib.rs 206/206
local_capture_coverage_tests.rs N/A (행 없음)
local_capture_reads_w27.rs 70/70
local_exact_constants_w27_tests.rs 2/2
local_rule_shapes_w27.rs 77/77
local_shape_captures_w27.rs 13/13
local_shape_composition_w27_tests.rs N/A (행 없음)
local_shape_hygiene_w27_tests.rs 23/23
numeric_context_w27_tests.rs N/A (행 없음)
style_values.rs 118/118
visit.rs 1828/1828

아래는 로컬 결과와 이력입니다.

  • 공개 WASM API + React SSR 드라이버 6개를 새로 실행해 모두 통과: 리뷰 수정 회귀 11/11(위 네 가지와 스칼라/도달 불가 대조군, 수정 전 실패 확인), 생성 코드 export 3/3(생성 TSX를 파서로 읽어 Choice, color, Choice, getColor 이름 유지 확인), 콜백 숫자 단위 4/4(Emotion 3px, native 12px, lineHeight 2), 기존 게이트 7/7, 바인딩 콜백 7/7, 수동 QA 16건. 위 코드/CSS/오류 발췌는 이 실행들의 실제 출력입니다. live binding은 Rust 테스트로 따로 확인했습니다.
  • 현재 cargo test --workspace 2882개(extractor unit 2166개와 integration 3개 포함) 0 fail, INSTA_FORCE_PASS 없이 실행. cargo check --workspace도 통과했습니다.
  • WASM/루트 빌드를 새로 실행해 통과했습니다.
  • 최종 Rust 1.99 workspace/all-targets clippy -D warnings exit 0, cargo fmt 검사 통과, lint 오류 0(베이스부터 있던 경고 2개만), Bun 테스트 5491개 0 fail.
  • Bun 커버리지 집계는 100%로 표시되지만 모든 파일이 100%는 아닙니다. Vite plugin 파일의 99.73%는 이 PR이 바꾸지 않았고 별도 작업(W20) 범위입니다.
  • 로컬 Rust 커버리지 참고값: 인계 때 Windows에서 CI의 넓은 rustfmt 설정을 재현한 측정은 99.35% (13201/13287), 네 번째 정상 커밋 훅의 기본 포맷 측정은 97.54% (18404/18869)입니다. 포맷이 달라 분모를 비교하지 않으며, 합격 근거는 위 Linux CI 100.00%입니다.
  • 기능 리뷰는 APPROVE(HIGH)이고 대기 중인 리뷰는 없습니다.
  • 추가 숫자 전체 추출 회귀 24건은 통과했습니다. 인덱스 자체를 별도 디렉터리에 내보내 G1 회귀 3건과 G2 extractor unit 2119건/integration 3건을 검증했습니다.
  • 정상 전체 훅(bun lint, LLVM tarpaulin, bun test)을 각각 통과한 논리 커밋 4개: c0a88f2d 숫자 논리 피연산자, aaae7acf 합성·캡처 파이프라인과 직접 회귀, a3ea3a20 Patch changepack, ab459f82 미커버 16줄 회귀 보강. 훅을 생략하지 않았습니다.
  • 첫 Linux CI 37195494754: Rust 테스트 2835개는 모두 통과했지만 tarpaulin이 **99.88% (13271/13287, 미커버 16줄)**여서 --fail-under 100에 실패했습니다. 뒤의 Bun 테스트는 실행되지 않았으므로 Linux Bun 결과로 주장하지 않습니다.
  • 같은 실행의 benchmark, landing-next-e2e, vinext-rsc-css-e2e는 모두 성공했습니다. 실제 미커버 분기 16줄을 겨냥한 회귀 47건을 보강해 로컬에서 모두 통과했습니다. 방문자의 spread 선검사 뒤 도달할 수 없던 중복 arm만 제거했고, 함수형 최상위 opaque class callback 경로는 실제로 도달하므로 그대로 두고 회귀로 검증했습니다. 커버리지 제외, 스타일링 런타임, 새 의존성, 커리하지 않은 API 확장은 없습니다.
  • 보강 뒤 Rust 1.99 clippy, 포맷, WASM/루트 빌드, Bun 5491건 및 여섯 공개 WASM/SSR 드라이버를 다시 실행해 통과했습니다. 네 번째 논리 커밋 ab459f82는 정상 전체 훅을 통과해 푸시했고, 후속 Linux CI 37201011483이 위와 같이 성공했습니다.

2026-10-08 main 11790bef를 병합하여 업데이트했습니다. 충돌 없이 갱신된 부모 PR의 변경과 main의 변경을 모두 보존했습니다.

owjs3901 and others added 27 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>
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 #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>
Refs #688

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

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

Refs #688

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ins and inline css() by styles

Refs #688, #690

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ClassNames cx calls, read style functions of props

Refs #690, #695

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

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

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688, #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>
…ines

Refs #688, #689, #690, #695, #685

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688, #689, #690, #695, #685

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688, #689, #690, #695, #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 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

  • 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
  • Compose namespace-imported styled components, inline tagged CSS and exact CSS spreads with later styles winning, and compile conditional props rule objects and read-only function-local style shapes without a styling runtime. Local function captures keep lexical hygiene, so user names like rest, style or className never collide with generated code. Styled callbacks bound to read-only const arrow or function declarations compile like inline callbacks while keeping the original exports and live bindings. Emotion numbers on the left operand of OR and nullish whole-rule choices become px like the right operand. Preserve Emotion numeric units and captured values, read exact imported computations, and retain named CSS layer metadata through composition. Unknowable shapes and spreads keep located build errors, including an unknown whole-rule shape held by a local or shadowing binding named undefined; namespace css props report the same non-inlineable overlap error as named imports, and anonymous layers, invalid layer names, and component layer-order statements report located errors directing users to named layers and globalCss.
  • 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
  • Numbers in rules written against Emotion, styled-components or vanilla-extract now read as px lengths when a constant stands in for the literal, as they do for a literal written in place: css({ padding: gap }) with const gap = 8, styled.div({ margin: SIZE }), <Global styles={{ body: { margin: GAP } }} /> and the same with imported constants and constant style objects give 8px, while unitless properties keep bare numbers, 0 stays 0 and Devup shorthands such as p keep the spacing scale. css${mixin}; color: red; outside the css prop composes the mixin by styles, so a later declaration replaces the mixin's, and a css... template is a known style a later css() composes. Inline css() calls inside css() arguments (css(css({ ... }), { ... }), css(cond && css({ ... }), base)) are composed with the other parts, the later winning, instead of being compiled to a class of their own. 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.
  • A styled component another module defines now composes at build time exactly as one the file defines itself: styled(ImportedBase)(...), styled(ImportedBase).attrs(...), ImportedBase.withComponent(...) and the Emotion css prop on ImportedBase render the base's tag with the base's styles under their own (the later declaration wins per property, selector, breakpoint and layer), merge attrs in order and filter props by what the styles read and shouldForwardProp. The build reads the other module through the same module resolution imported constants use (relative paths, tsconfig paths, package exports, include libraries) and reports it as a dependency, so a change to it rebuilds the importing file. A definition the build cannot read exactly (a base only the runtime gives, an attrs or style expression reading a binding of the defining module, a module with a build error, an unresolvable module, an import cycle) stays a runtime component that wraps, as before. Every exported styled component now carries a marker class computed from its file and its export name alone, so ${ImportedChild} { ... } in CSS text and [ImportedChild] as a rule key select it from another file; the marker replaces the registry-ordered one, which two files could not compute alike. No new build errors: the css prop on an imported styled component now reports the same override error a local one does when its styles overlap the component's
  • A style object declared with const inside a function and passed to css(), styled(), globalCss(), keyframes(), a style prop or a spread (also through the Emotion, styled-components and vanilla-extract aliases) now compiles when every entry is written out and the object is only read as styles, instead of leaving the object where the class belongs; the build errors, at the binding, when the object is also read elsewhere (where code may change it), and at the call when it holds values only the runtime gives. Emotion's ClassNames cx keeps a function call's result as a class that stays as written, as @emotion/css cx does. css() and styled() compose an array of rule objects under a condition. A style function of the props (styled.div((props) => ({ ... })), and as a later argument) compiles: each value reading the props becomes a CSS variable the component sets (a number is a px length unless the property is unitless), theme reads (including theme.a[2]) become var(--a-2), and a condition chooses per value. New build errors: styled(...).attrs(...) chained after the styles (chain .attrs before them), a style function of the props that does not give one rule object at once with every entry written out, and a style object declared in a function that is also read elsewhere
  • 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
  • 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

@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
  • 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/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

  • 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

  • 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

  • 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
  • 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

@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
  • 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

  • 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
  • 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 Δ
libs/css/src/lib.rs 100.00% <100.00%> (ø)
libs/extractor/src/bound_callback_captures.rs 100.00% <100.00%> (ø)
libs/extractor/src/bound_callback_export_tests.rs 100.00% <100.00%> (ø)
libs/extractor/src/bound_callback_numeric_tests.rs 100.00% <100.00%> (ø)
libs/extractor/src/bound_style_callbacks.rs 100.00% <100.00%> (ø)
libs/extractor/src/bound_style_callbacks_tests.rs 100.00% <100.00%> (ø)
libs/extractor/src/build_time_values.rs 100.00% <100.00%> (ø)
libs/extractor/src/capture_public_exports_w27.rs 100.00% <100.00%> (ø)
libs/extractor/src/composition.rs 100.00% <100.00%> (ø)
libs/extractor/src/css_prop.rs 100.00% <100.00%> (ø)
... and 42 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.

No conflicted files; preserve composition changes and main ESLint checks.
owjs3901 and others added 16 commits October 8, 2026 15:12
No conflicted files; combine updated parent and PR changes.
No conflicted files; combine updated parent and PR changes.
Conflicted file: libs/extractor/src/lib.rs. Retain both composition helper and regression tests from PR 706 and Tailwind per-class regression tests from main 11790be; take rewritten main Tailwind implementation with Rust 1.99 assertions. bun.lock regenerated by bun install; no snapshots were hand-merged.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Merge updated parent PR 706 (30247ba), containing main 11790be. Conflicted file: libs/extractor/src/prop_modify_utils.rs. Preserve PR 707's last-written className/style spread semantics and no spread_props parameter with capacity 2, together with main's per-class Tailwind compiler. No snapshots were hand-merged.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
No conflicted files; retain PR changes and merge updated parent #707, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #710, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #711, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #722, which contains main 11790be.
Merge updated parent PR 727 (12328b9), containing main 11790be. Conflicted file: packages/bun-plugin/src/plugin.ts. Preserve PR 728's readJsxImportSource alias discovery and main PR 703's compiledPackages scanner, Bun.build stylesheet deferral, runtime writes and debug policy; keep main PR 704 plugin CSS behavior. WASM/root builds and real Bun stylesheet regression passed; no snapshots were hand-merged.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
No conflicted files; retain PR changes and merge updated parent #728, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #729, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #730, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #730, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #740, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #745, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #736, which contains main 11790be.
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