Skip to content

feat(extractor): alias @emotion/css css, cx, keyframes and injectGlobal - #716

Open
owjs3901 wants to merge 11 commits into
mainfrom
fix/emotion-css
Open

owjs3901 wants to merge 11 commits into
mainfrom
fix/emotion-css

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Refs #690 (D 이슈 첫 번째 PR). main에서 분기했습니다.

요약

@emotion/css를 기본 alias에 추가하고, cx는 Emotion처럼 클래스 이름을 받아 합치도록, merge는 빌드 때 클래스 문자열을 합성하도록 컴파일합니다.

import { css, cx, merge } from '@emotion/css'

const red = css({ color: 'red' })
export const a = cx('external')
export const b = cx(red, 'external', { picked: true })
export const d = (cls) => merge(`${red} external ${cls}`)
export const a = "external"
export const b = "a external picked"
export const d = (cls) => `a external ${cls}`
// .a{color:red}

동작

  • @emotion/css를 기본 alias에 추가했습니다.
    • css, keyframes: Devup UI와 같이 컴파일합니다. 숫자는 Emotion처럼 px로 읽습니다(padding: 8 → 8px).
    • injectGlobal: globalCss로 바뀝니다.
    • cx, merge: @devup-ui/react/compat로 옮겨 빌드 때 컴파일합니다(런타임에 호출하면 Cannot run on the runtime을 던집니다).
  • cx(...)는 css()가 아니라 클래스 이름을 받습니다.
    • 문자열은 항상 클래스입니다. 공백으로 나눠 한 개씩 합성합니다(cx('external') → "external").
    • 런타임 값(cls, props.className 등)은 그대로 남는 클래스입니다. falsy 값은 건너뜁니다.
    • 객체는 항상 { name: condition } 클래스 맵입니다. 조건이 참인 동안 name이 붙고, 계산된 키([cls]: on)는 키의 값이 클래스가 됩니다. true/false/null/숫자 같은 리터럴 조건은 빌드 때 접습니다. 객체 안의 숫자는 px로 바뀌지 않습니다.
    • 배열은 중첩됩니다. a && 'x', a || 'x', a ?? 'x', 삼항식도 읽습니다.
    • 인라인 css(...) 호출과 빌드가 아는 css() 클래스는 css(a, b)가 쓰는 합성 경로를 그대로 지나갑니다. 이 브랜치에서는 클래스를 이어 붙이고, #706이 병합되면 스타일 기준 합성(뒤가 이김)이 자동으로 따라옵니다.
  • merge(className): 인자를 빌드 때 읽을 수 있는 만큼 나눕니다(문자열 토큰, 공백으로 떨어진 템플릿 리터럴 조각). 알려진 css() 클래스는 cx처럼 합성하고, 나머지 클래스는 그대로 둡니다. 나눌 수 없는 런타임 문자열은 그대로 둡니다(cascade가 정합니다).
  • 타입 선언(compat/emotion): cx(...args: ClassNamesArg[]) => string, merge(className: string) => string, ClassNamesArg 타입을 @emotion/css에 선언했습니다. 소비자 프로젝트에서 @ts-expect-error로 merge('a', 'b'), merge({}), cx(() => 'a')가 거부되는 것까지 검사했습니다.

새로 생기는 오류

모두 파일:줄:열과 함께 빌드 오류로 보고됩니다.

  • cx()/merge()의 인자 spread: cx(...list), 배열 안 spread cx([...list])
  • cx() 클래스 맵의 spread, getter, setter, 메서드: cx({ ...rest }), cx({ get x() { ... } })
  • cx 또는 merge를 태그드 템플릿으로 호출: cx`a b`
  • merge()에 인자가 하나가 아닌 경우: merge('a', 'b'), merge()

남는 한계

  • 런타임 문자열은 나눌 수 없습니다. merge(props.className)는 그대로 남고, 그 안에 든 css() 클래스끼리의 우선순위는 CSS cascade가 정합니다.
  • 다른 파일에서 정의한 css() 결과는 변수로만 합쳐지는 런타임 클래스입니다(모듈 해석을 통한 합성은 A 이슈 범위).
  • 런타임 전용 API(cache, flush, hydrate, sheet, getRegisteredStyles, create-instance)는 @emotion/css에 남습니다. K #693에서 문서화합니다.

검증

  • cargo fmt, cargo +1.99 clippy --workspace --all-targets -- -D warnings, cargo test --workspace 통과. 새 테스트: test_emotion_cx_takes_class_names, test_emotion_merge_splits_what_it_can, test_emotion_cx_and_merge_report_what_they_cannot_compile, test_emotion_class_calls_follow_the_package_namespace_and_aliases, test_emotion_css_maps_onto_devup_equivalents.
  • 넓은 rustfmt 설정(CI와 같은 설정)으로 cargo tarpaulin을 돌려 새 코드(class_arguments.rs, util_type.rs, style_values.rs) 줄 커버리지 100%를 확인했습니다.
  • WASM 재빌드, 루트 bun run build, bun test 5476 통과, 커버리지 100%
  • bun lint: 기존 경고 2개 외 없음
  • 프로브 emotion-css-cx.tsx: a = "external", b = "a external picked", .a{color:red}만 생성되고 .b{picked:true} 규칙은 없습니다.
  • 소비자 프로젝트 타입 검사(@devup-ui/react/compat/emotion) 통과
  • Rust 1.99 clippy assert_is_empty 수정은 fix(extractor): let a later css() part replace an earlier one's declarations #706 등과 같은 내용입니다.

W28: 네임스페이스 Import 보강

요약

import * as E from '@emotion/css'의 E.css, E.cx, E.merge, E.keyframes, E.injectGlobal을 기존 named import와 같은 추출 경로로 컴파일합니다. 네임스페이스만 쓰는 파일이 런타임 Emotion 호출과 빈 CSS를 그대로 내보내던 문제를 수정했습니다.

동작

  • 점 표기, 문자열 대괄호 표기, 상수 문자열 키, const { css, cx: classNames } = E 구조분해, const N = E / const c = N.css 별칭을 심볼 기준으로 추적합니다. 같은 이름의 지역 변수·매개변수는 변경하지 않습니다.
  • css·keyframes·injectGlobal의 숫자는 px이며 단위 없는 속성은 숫자를 유지합니다. 상수 인라인·Boa 정적 계산 뒤에도 같은 단위를 유지합니다.
  • cx 클래스 맵의 숫자는 참/거짓 조건으로만 쓰고 CSS 선언으로 읽지 않습니다. merge와 클래스 합성은 이 PR의 named import와 같은 규칙을 사용합니다.
  • 런타임 전용 cache, flush, hydrate, sheet, getRegisteredStyles를 읽는 경우 해당 참조와 @emotion/css import를 유지합니다. 이 멤버들의 중첩 구조분해·기본값·optional 조회도 유지합니다. 스타일 API만 사용하면 Emotion import는 사라집니다.
  • alias를 끄면 해당 Emotion 코드는 기존처럼 처리하지 않습니다. 원본 소스 맵과 오류의 파일·줄·열을 유지합니다.

새로 생기는 오류

모두 파일:줄:열: ... cannot use ... at build time: ... 형식입니다.

  • 루트 @emotion/css에는 default export가 없으므로 value default import를 거부합니다. default export가 있는 @emotion/css/create-instance와 혼동하지 않습니다.
  • 정적으로 알 수 없는 네임스페이스 키, 존재하지 않는 export, 네임스페이스 객체 또는 스타일 함수의 외부 전달·변경·재수출
  • optional 스타일 조회·호출, let·var 스타일 별칭, 네임스페이스 rest 구조분해, 스타일 API의 기본값·중첩 구조분해, 스타일 API 바인딩 자체의 export
  • 별칭이 초기화되기 전에 실행될 수 있는 조회: 기존 코드의 TDZ 오류를 클래스 문자열로 바꾸어 숨기지 않도록, 초기화 순서를 증명할 수 없으면 빌드 오류로 보고합니다.
  • require·TS import-equals·동적 import 등 정적 ESM import가 아닌 경로에서 스타일 API를 읽는 경우: 정적 import * as ns from '@emotion/css' 사용을 안내합니다. await 없이 namespace를 Promise·callback으로 넘기는 동적 import도 빌드가 추적할 수 없어 오류입니다. 인식되는 런타임 로더의 런타임 전용 API 조회는 보존합니다.
  • 기존 css·cx·merge·keyframes·injectGlobal이 읽을 수 없는 입력은 named import와 같은 빌드 오류를 보고합니다.

남는 한계

  • 런타임 클래스 문자열의 합성 한계는 위 PR 설명과 같습니다. 네임스페이스 지원은 런타임 스타일 함수를 추가하지 않습니다.
  • @emotion/react·@vanilla-extract/css 네임스페이스와 @emotion/css/create-instance는 이 작업 범위가 아닙니다.
  • 기존 compat/emotion.d.ts는 런타임 전용 API의 타입을 선언하지 않습니다. 런타임 전용 멤버 보존과 별개인 기존 타입 선언 누락으로 코디네이터에 보고합니다.
  • 별칭 선언보다 먼저 만들어진 함수 표현식·arrow는 호출이 나중이어도 초기화 순서를 보수적으로 판정해 거부할 수 있습니다. 별칭을 먼저 선언하면 됩니다. Hoisted 함수 선언은 알려진 호출 위치가 모두 초기화 뒤인 경우 컴파일합니다.

검증

  • cargo fmt --all, cargo +1.99 clippy --workspace --all-targets -- -D warnings, cargo test --workspace 통과: extractor 테스트 1581개 통과
  • WASM·루트 패키지 재빌드 통과, bun test 5476개 통과, 커버리지 100%, bun lint는 기존 경고 2개 외 없음
  • 최종 WASM 공개 API 프로브 20개 및 namespace-only 소스 맵 프로브 통과. 정적 멤버·구조분해·문자열 키·별칭·shadowing·런타임 멤버 보존·빌드 오류·alias 비활성화 및 wrapper·템플릿 리터럴이 있는 오류 표현의 원문 복원을 확인했습니다.
  • Linux CI run 37163339327 전체 통과: Rust 100.00% coverage, 10529/10529 lines covered, Bun 5484개/0 fail/100%, publish·benchmark·landing-next-e2e·vinext-rsc-css-e2e 및 codecov/patch SUCCESS. 최초 CI의 미실행 6줄을 실제 테스트와 구조 정리로 보강했으며 제외나 suppression은 추가하지 않았습니다.
  • 최종 head: 9b2a04a346d0d9f13748a9bf358512206db5e5bc. 새 커밋은 37f692ab(구현·회귀 테스트), 112af7b0(Patch note), 9b2a04a3(커버리지 보강)이며 기존 PR branch에 fast-forward로 푸시했습니다.
  • 최종 원문 복원·커버리지 수정의 Oracle 재검토 APPROVE. 재검토에서도 24개 공개 WASM assertions가 독립적으로 통과했습니다.
  • LSP daemon은 timeout이라 LSP clean 결과는 주장하지 않습니다. Rust 컴파일러·workspace clippy·테스트가 정적 검증 근거입니다.

아래는 빈 theme를 등록한 실제 WASM 프로브의 출력입니다. 클래스 합성은 named import와 같으며, 런타임 전용 멤버를 읽을 때만 Emotion import가 남습니다.

namespace.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
const red = "a b c";
const { css: localCss, cx: classNames, merge: joinClasses } = {
	"css": void 0,
	"cx": void 0,
	"merge": void 0
};
const blue = "d e";
export const direct = "a b c d e external picked";
export const destructured = "a b c on";
export const merged = "a b c external";
export const animation = "f";
;

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */@layer b;@layer b{body{margin:8px}}@keyframes f{from{opacity:0;width:10px}}.d{color:blue}.a{color:red}.c{line-height:1.5}.e{margin:2px}.b{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */@layer b;@layer b{body{margin:8px}}@keyframes f{from{opacity:0;width:10px}}.d{color:blue}.a{color:red}.c{line-height:1.5}.e{margin:2px}.b{padding:8px}

brackets.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
const red = "a b";
const { ["css"]: localCss, cx, merge, keyframes, injectGlobal } = {
	"css": void 0,
	"cx": void 0,
	"merge": void 0,
	"keyframes": void 0,
	"injectGlobal": void 0
};
const blue = "c";
export const direct = "a b c selected";
export const merged = "a b external";
export const animation = "d";
;

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */body{margin:0}@keyframes d{from{width:10px}}.c{color:blue}.b{opacity:.5}.a{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */body{margin:0}@keyframes d{from{width:10px}}.c{color:blue}.b{opacity:.5}.a{padding:8px}

runtime.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
import * as E from "@emotion/css";
export const style = "a";
export const cache = E.cache;
export const sheet = E.sheet;
E.flush();
E.hydrate(["server"]);
E.getRegisteredStyles([], "external");

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{padding:8px}

shadow.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
export const style = "a";
export function nested(E: {
	css: (rules: {
		padding: number;
	}) => string;
}) {
	return E.css({ padding: 8 });
}
export function scoped() {
	const { css: make } = { "css": void 0 };
	return "b";
}

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.b{margin:2px}.a{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.b{margin:2px}.a{padding:8px}

runtime-patterns.tsx

=== CODE ===
import * as E from "@emotion/css";
const { cache: { key }, flush = () => {} } = {
	"cache": E["cache"],
	"flush": E["flush"]
};
export const optionalCache = E?.cache;
export const runtime = {
	key,
	flush
};

=== CSS (this file) ===

=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */

undefined.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
const c = void 0;
const undefined = 1;
export const style = "a";
export const value = undefined;

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{padding:8px}

alias-wrappers.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
const c = void 0;
const other = void 0;
export const style = "a";
export const style2 = "b";

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.b{margin:2px}.a{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.b{margin:2px}.a{padding:8px}

string-named.tsx

=== CODE ===
import "@devup-ui/react/devup-ui.css";
export const style = "a";

=== CSS (this file) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{padding:8px}
=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{padding:8px}

error.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/error.tsx:3:35: `@emotion/css()` cannot use `E[key]` at build time: namespace member keys must be constant strings

default.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/default.tsx:1:8: `@emotion/css()` cannot use `E` at build time: the root module has no default export; use named or namespace imports

import-equals.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/import-equals.tsx:2:22: `@emotion/css()` cannot use `E.css` at build time: styling APIs read through require(), import-equals or dynamic import cannot be compiled; use `import * as ns from '@emotion/css'`

tdz.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/tdz.tsx:3:16: `@emotion/css()` cannot use `c` at build time: an alias must be initialized before any read that can run first; declare it above the earliest code that can reach this read

comment.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/comment.tsx:3:22: `cx()` cannot use `E.cx`a b`` at build time: call it with class names, as in `cx('a', 'b')`

dynamic-styling.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/dynamic-styling.tsx:2:22: `@emotion/css()` cannot use `E.css` at build time: styling APIs read through require(), import-equals or dynamic import cannot be compiled; use `import * as ns from '@emotion/css'`

dynamic-runtime.tsx

=== CODE ===
const E = await import('@emotion/css')
E.flush()
export const cache = E?.cache

=== CSS (this file) ===

=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */

module-loader.cjs

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/module-loader.cjs:2:15: `@emotion/css()` cannot use `E.css` at build time: styling APIs read through require(), import-equals or dynamic import cannot be compiled; use `import * as ns from '@emotion/css'`

alias-off.tsx

=== CODE ===
import * as E from '@emotion/css'
export const style = E.css({ padding: 8 })

=== CSS (this file) ===

=== CSS (whole sheet) ===
/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */

wrapped-error.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/wrapped-error.tsx:2:23: `cx()` cannot use `(E.cx)`a b`` at build time: call it with class names, as in `cx('a', 'b')`

cast-error.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/cast-error.tsx:2:23: `cx()` cannot use `(E.cx as typeof E.cx)`a b`` at build time: call it with class names, as in `cx('a', 'b')`

literal-error.tsx

=== ERROR ===
C:/Users/devfive3901/AppData/Local/Temp/opencode/workers/w28-emotion-css-namespace/literal-error.tsx:2:23: `cx()` cannot use `E.cx`a __emotion_user`` at build time: call it with class names, as in `cx('a', 'b')`

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

owjs3901 and others added 2 commits October 2, 2026 12:49
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>
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Changepacks

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

Patch

  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • 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.
  • 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

@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

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

@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
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access

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

Patch

  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access

@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

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

Patch

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

@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

@codecov

codecov Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
libs/extractor/src/class_arguments.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/acquire.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/bindings.rs 100.00% <100.00%> (ø)
...bs/extractor/src/emotion_namespace/declarations.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/driver.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/order.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/output.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/reads.rs 100.00% <100.00%> (ø)
libs/extractor/src/emotion_namespace/restore.rs 100.00% <100.00%> (ø)
... and 12 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

owjs3901 and others added 4 commits October 3, 2026 16:18
…composition

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>
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
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