Conversation
…cking Boa's Context::default() reads std::time::Instant, which panics on wasm32-unknown-unknown without the js feature. Every .css.ts extraction crashed in the full WASM; the lite build is unaffected. Add a wasm-bindgen test and a bun regression that runs in CI. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…en-prefix rules prefer-media-shorthand suggests _motionReduce, _print and the other media shorthands for _media entries and @media keys that spell out the same query. no-typography-token-prefix reports typography=$heading, which names no preset. Both are auto-fixable and part of the recommended config. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Media shorthand queries, at-rule key parsing, query normalization, media query combination (merge, never-match, nest) and the deterministic order at-rule blocks are emitted in. StyleSelector::At now carries its enclosing at-rules and owning file. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…xtract references Nest _print, _media and @media keys with selectors in both directions (props, globalCss, template literals, Tailwind variants, StyleX conditions), apply typography conditionally, and keep responsive globalCss values in the base layer. vanilla-extract selectors, values and globalStyle now resolve references to styles, keyframes, fonts and themes, and createVar/fontFace/layer exports no longer shift onto each other. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Rules order by selector group before breakpoint level and at-rules follow every plain rule; globalCss and custom layers keep SELECTOR_ORDER instead of sorting selectors alphabetically. Adjacent rules with the same selector share one block, and a declaration written next to a conditional typography preset wins over it. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add _motionReduce, _motionSafe, _portrait, _landscape, _contrastMore, _contrastLess and _forcedColors, accept @media/@supports/@container keys, allow top-level at-rules in globalCss, and drop the never-matching _speech. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
typography takes the bare preset key, so the examples no longer write typography=$heading. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… units vanilla-extract styles add px to numbers (in .css.ts files and in style/globalStyle/keyframes calls of ordinary modules) and StyleX adds px/ms to static and dynamic numbers, instead of Devup UI's spacing scale. The unitless property list now matches vanilla-extract's, so strokeWidth, columns, zoom, orphans and similar keep bare numbers everywhere. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…gine Remove the static .css.ts fast path so development and production builds evaluate stylesheets the same way and produce the same class names and CSS. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ponents object styles vanilla-extract: name styles after the variable holding them instead of by call order, generate file-unique names for vars, containers, layers, fonts and theme variables, follow vanilla-extract's createVar/fallbackVar/createTheme/createGlobalTheme/contract/layer semantics, resolve styleVariants variants and transitive composition, and export plain values as data. Emotion and styled-components: numbers in object styles mean pixels, several style arguments or arrays merge, styled(tag, options) is a factory, and css() composes class names passed with rule objects. '@layer' records and a global '@layer' cover nested and responsive declarations, and typography presets yield to declarations written beside them. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Layered properties of any kind are written with the regular writer, one layer at a time, so a layer keeps breakpoints, selectors and at-rules. A typography atom skips the declarations its value lists as written beside it from the breakpoint they start at. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…graphy precedence Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The loop left a region tarpaulin maps past the function on Linux; the output is unchanged. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Tarpaulin's Linux report still attributed a line past the function to its closures; the output is unchanged. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The vec! expansion is the region tarpaulin maps past the function on Linux; the output is unchanged. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Reading a rule list cannot fail, so the error path tarpaulin kept reporting as uncovered past the function is gone; the output is unchanged. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Its output is asserted by test_font_faces; tarpaulin's Linux report maps the loop exit onto a line it never counts however the loop is written. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Shaped like compose so tarpaulin's Linux report maps its loop, and the coverage exclusion is gone again; the output is unchanged. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ctor The callers store the rules, so the helper ends in its value; the output is unchanged. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Tarpaulin merges the unit-test build and the library build that other targets link; the latter never ran the font-face helper, which left one region at zero. The helper is split into single-rule and list parts. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
A number on a custom property was read as the spacing scale (--columns: 4 became 16px), which broke theme tokens and var() math. The vanilla-extract API now also runs through the library build in an integration test. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…function Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
defineVars and createTheme values may be condition objects of default, @media, @supports and @container keys, optionally wrapped in types.*(). Each set of conditions becomes one block after the unconditional values, deeper conditions last so the more specific value wins. Importers resolve such variables to the same names, reading constant condition keys like [DARK] as the defining file does. A value that is not static is a build error instead of a silently missing variable. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…leX input it cannot read Math.* calls over constants fold; a style value computed from constants runs through the full engine and becomes a static class. StyleX create/defineVars/defineConsts/createTheme/keyframes/positionTry/viewTransitionClass/include report spreads, computed keys and non-object arguments as located build errors, and props()/attrs() join styles compiled elsewhere at runtime. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…values The WASM bindings export hasBuildTimeValues, and selectWasmVariant loads the full engine when a candidate file has a style value only running the module computes. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… report styles it cannot read Object, array and TypeScript enum constants, declared in the file or imported, are inlined where styles read them, a later property replacing an earlier one in style objects and among JSX props. A spread of an unknown object, a selector given a non-object, a computed key and a non-object globalCss/keyframes argument are build errors instead of silently missing; a single css()/styled argument composes as a class; a JSX literal spread keeps its non-style props; theme.colors[key] becomes a CSS variable; an index or key a spread may supply is read at runtime. The full engine computes values reached through a const or an import, spreads, shorthand properties, computed keys and conditions, standing in for the style packages and for modules it cannot load. Stylesheet evaluation no longer panics on TypeScript enums. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
hasBuildTimeValues takes the import aliases and reads imported constants through the module resolver before deciding, so a file reading only constants keeps the lite engine and one reading a computed constant selects the full engine. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… the code as written - css() and styled() compose a binding holding rules only running the module gives through the full engine; a class computed in its place is no longer read as CSS text - build errors state what each engine accepts: constants, and with the full engine values computed from them - source maps point at the code as written through rewritten imports and computed values - an imported stylesheet runs once per script it evaluates to, and each module is stripped of TypeScript once Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
A file composing a binding with styled.div(...) or styled(...)(...) may hold rules only the full engine computes, so the engine choice checks it too. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
css-utils-literal-only follows what the engines read: calls of imported or module-level functions and built-ins over constants, Math folds, arrays, conditions and computed members of constants pass, while Date, Math.random and functions only known at runtime are reported. The README documents the computed values and the engine each needs. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…and source maps Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…nts take - A runtime value an element holds is written as it is: it was optimized as CSS text, so a string passed to a function lost digits and --i became var(--i) - await, new, assignments, sequences and tagged templates reach the element instead of being dropped; a function or class is reported - globalCss() used as a value gives undefined instead of invalid code, and a runtime condition in globalCss(), keyframes() or <Global> is a build error instead of being dropped; a condition the build knows picks its branch - With the full engine, JSX spreads, selector props and the spreads, selectors and computed keys of styled objects compute what the module computes, as do css(...parts) and a template argument of css() - A namespace or object of known classes no longer runs the module or needs the full engine, and a stylesheet that loops forever fails instead of hanging Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…the engine An element spreading or selecting a binding may take styles only the full engine computes, so the engine choice checks those files too. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…values Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…impure spreads once
- A component with a non-literal as (as={Link}, as={cond ? 'a' : 'b'}) is replaced where it stands. In JSX children it replaced its parent element or a later expression and left the component, which throws at runtime, in the output; in lists it lost its key inside a fragment
- A spread whose value may change when read again (<Box {...register('email')} />, a hook) is evaluated once, with the props and children written before it in order, where it was evaluated for the spread, the className and the style
- A literal spread no longer reads a className or style it does not write
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… cover every read-once path - css(tokens.nested.x) is computed when the build knows tokens.nested but not its x: a member chain is resolved through the constants the build knows, where only the first member was checked - Tests cover reading once in children, attribute elements, generators and compiled jsx() calls, the purity of each expression kind, and the conditions globalCss() cannot switch Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… objects Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Every build loads the one extraction engine instead of scanning the project at startup to choose between the lite and full engines, so a file added while the dev server runs is extracted like the others - The static import graph and the prewarm file list are built where they were before the engine choice moved them ahead of loading WASM Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Boa is a plain dependency of the extractor, sheet and WASM crates instead of the vanilla-extract feature, so every build evaluates stylesheets and computes style values - @devup-ui/wasm builds one package: the lite build, its ./lite export and hasBuildTimeValues, which only chose between the engines, are gone Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- The docs, the css-utils-literal-only README and the changepacks no longer mention the lite engine, hasBuildTimeValues or an engine Next.js picks: every build computes style values the module computes Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…t as constants - An object, array or enum constant that code the build sees changes (a member assignment, delete, ++, push, sort, Object.assign, or handing it to a function the build does not know) is read at runtime: elements read its members as CSS variables, and css(), styled() and element spreads taking it whole are build errors naming where it is changed. Handing on a string or number it holds, or putting it in a top-level const nothing changes, keeps it constant - The module declaring an imported constant and the modules re-exporting it are checked too, and values computed at build time do not read a changed binding - Values computed at build time cannot read window, document, navigator, location, process or globalThis properties the build does not define, so the CSS never holds the build environment's answer Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Objects, arrays and enums that visible code changes are read at runtime, taking one whole is a build error naming where it changes, and values computed at build time cannot read window, document, navigator, location, process or unknown globalThis properties Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… functions change - A spread argument of css() reading a changed binding is reported like any other argument, and the check for the argument Object.assign and friends change no longer hides in a match guard coverage cannot count Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
요약
#677에서 남은 한계로 적었던 항목을 모두 해결하고, import한 상수를 정적 값으로 처리하는 최적화를 추가했습니다.
이후 라운드에서 StyleX 조건부 값, 빌드 타임 계산값(full 엔진), 상수 스타일 객체를 지원하고, 빌드가 읽을 수 없는 스타일은 조용히 버리지 않고 위치가 붙은 빌드 에러로 보고합니다.
모듈 로더 (
.css.ts가 다른 모듈을 import)setModuleResolver((specifier, importer) => ({ path, code }))extract_with_modulescreateModuleResolver로 이를 등록합니다. 해석 대상은 상대 경로, tsconfigpaths,theme.css→theme.css.ts보완입니다.import "./theme.css"를 유지해서 해당 CSS가 번들에 포함되게 합니다.export { a as b },export … from,export *,export * as nsOutput.dependencies로 반환되고, 각 플러그인이 watch 대상으로 등록합니다.addWatchFile, webpack/next/rsbuildaddDependency, Turbopack coordinator는 응답에 포함합니다..css.ts의 side-effect import(import './global.css')가 사라지던 기존 버그도 고쳤습니다.import한 상수 인라인 (제안된 최적화)
<Box color={PRIMARY} />에서PRIMARY가 다른 모듈의const문자열/숫자이면, CSS 변수 대신 정적 클래스로 처리합니다.as const객체의 멤버 접근, alias, re-export, default호환성
.attrs():.withConfig(): 제거합니다(런타임 옵션뿐입니다).styledimport가 있어도 모두 추출합니다.css(base, cond && {...}), 삼항,styled.div(...))은 속성별 삼항으로 병합합니다.cond ? cls : ''로 합칩니다. 기존에는"false"가 출력되거나 스타일이 통째로 사라졌습니다.레이어 · 타이포그래피 (cascade)
@layer안의 동적 값도 레이어 안에 둡니다.t/oN.t/b.t).검증
cargo test --workspace통과bun test전체 통과, 변경 파일 커버리지 100%bun run build,verify:dist통과createModuleResolver로 실제 end-to-end 확인:추가: 남은 동작 해결
createModuleResolver가 bare specifier를 번들러처럼node_modules에서 해석합니다.exports: 문자열, 배열, 조건(import>module>default>require>node),*패턴,null차단module→main→index순서,.cjs포함module/exports/require모듈을 평가합니다. default는__esModuleinterop를 따릅니다.void 0placeholder는 제외).require바인딩도 추적합니다..tsx/.js모듈은 각자의 source type으로 파싱합니다.value || {...},value ?? {...}를 지원합니다.styled도css()처럼 클래스를 조합합니다.감사 라운드: 찾아서 고친 누락
css(),globalCss(),keyframes(),stylex.create()는 런타임에만 알 수 있는 값을 빌드 에러로 보고합니다. 기존에는 아무도 설정하지 않는 CSS 변수를 출력하거나, 빈 선언(color:)을 남기거나, 선언을 버렸습니다.const는 resolver 없이도 인라인하고, 상수에 대한 템플릿과 산술 연산을 접습니다(${SIZE * 2}px). StyleX 파일에도 적용됩니다.globalCss/createGlobalStyle/keyframes템플릿의 보간(리터럴, 상수, 테마 읽기)을 CSS에 씁니다.${mixin},${p => cond && mixin})은 그 클래스를 조합합니다. 기존에는 mixin 자신과 다음 선언까지 조용히 사라졌습니다.dependencies를 유지하므로, import한 모듈이 바뀌면 다시 빌드합니다.css-utils-literal-only규칙이 빌드가 인라인하는 상수를 허용합니다.개선 라운드
.stylex.ts관례)에서 import한defineVars/createThemeContract/defineConsts/createTheme값을 그 모듈이 생성하는 이름으로 해석합니다. StyleX namespace/default import를 모두 인식합니다..css.ts는 그 예외를 보고합니다. 기존에는 런타임에 여전히style()을 호출하는 출력으로 폴백했습니다.file:line:column: message형식으로 보고합니다. 위치는 import alias 변환 전의 원본 위치로 매핑합니다..css.ts스타일시트 2개 모두 실패 없이 추출합니다.StyleX 조건부 값
defineVars/createTheme값에 조건 객체를 쓸 수 있습니다.default,@media,@supports,@container([DARK]같은 상수 키 포함)types.*()래퍼는 있어도 되고 없어도 됩니다.빌드 타임 계산값과 StyleX 빌드 에러
Math.*호출을 빌드 타임에 접습니다(Math.max(SIZE, 20),Math.round,Math.PI). 반올림은 JavaScript 규칙을 따르고, 다른 값으로 가려진Math는 건드리지 않습니다.css(),globalCss(),keyframes(), StyleX가 모듈 실행 시 상수로 계산하는 값(darken(0.1, PRIMARY), helper 호출)은 full 엔진(Boa)에서 계산해 정적 클래스로 만듭니다.Date와Math.random은 쓸 수 없습니다.styled는 기존처럼 런타임 CSS 변수를 씁니다.hasBuildTimeValues를 export합니다. Next.js는 후보 파일에 계산이 필요하면 full 엔진을 선택하고, lite 엔진의 에러는 full 엔진을 안내합니다.create,defineVars,defineConsts,createTheme,keyframes,positionTry,viewTransitionClass,include는 읽을 수 없는 입력(spread, 계산된 키, 객체가 아닌 인자, 없는 테마 키, 구조 분해한create())을 그대로 출력하거나 버리지 않고 위치가 붙은 빌드 에러로 보고합니다.stylex.props()/attrs()는 다른 곳에서 컴파일된 스타일(styleprop, spread, 다른 모듈의 namespace)을 런타임에 합칩니다. 같은 파일의 namespace는 정적으로 해석합니다.상수 스타일 객체와 빌드가 읽을 수 없는 스타일
css(base),{ ...base, color: 'red' },_hover: hover,space[2],Size.M,<Box {...base} _hover={hover} />props={...}같은 런타임 prop은 원래 객체를 그대로 받습니다._hover)에 준 객체가 아닌 값, 계산된 키, 객체가 아닌globalCss/keyframes인자,styled객체 형식 안의 props 함수. 알 수 없는 객체의 JSX spread는 기존처럼 런타임에 그대로 전달합니다.css(x)/styled.div(x)는 그 값을 클래스로 조합합니다(기존에는 버려졌습니다).onClick이 사라졌습니다).bg={theme.colors[key]}는 CSS 변수가 됩니다(기존에는 사라졌습니다).const나 import를 거친 값(토큰 파일의const DARK = darken(...)), spread, shorthand 속성, 계산된 키, 런타임 조건도 계산합니다.hasBuildTimeValues(filename, code, package, importAliases)는 먼저 module resolver로 상수를 인라인하므로, 정말 평가가 필요한 값이 없으면 Next.js는 lite 엔진을 유지합니다(이 저장소에서 파일당 약 0.4 ms).enum에서 panic하지 않습니다.남은 개선 항목
css()/styled()가 조합하는 바인딩이 모듈이 계산하는 규칙을 담고 있으면 그 규칙을 적용합니다.const base = makeStyles(); css(base),styled.div(base)css()결과, 스타일시트에서 import한 클래스)은 그대로 클래스로 조합합니다.styled.div(...),styled(...)(...)조합도 확인합니다.sourcesContent는 원본 코드입니다. 이전에는 치환된 코드를 기준으로 해서, 여러 줄 계산값 뒤의 줄이 어긋났습니다.css-utils-literal-only: 엔진이 읽는 범위에 맞췄습니다.Math(단Math.random제외),String등)로 상수에서 계산한 값은 허용합니다.Date,Math.random, 매개변수처럼 런타임에만 아는 함수는 보고합니다.cargo test --workspace, clippy(full·lite·wasm32) 통과추가 점검에서 찾은 버그
as가 JSX 자식 위치에서 코드를 망가뜨리던 버그 (main에도 있음):as={Link},as={cond ? 'a' : 'b'}처럼 리터럴이 아닌as를 JSX 자식으로 쓰면 결과가 틀렸습니다.<section>이 사라짐), 뒤의 다른 식({on}, 형제의title)이 대체됐습니다.<Box>가 출력에 남아 런타임에 "Cannot run on the runtime" 에러가 났습니다.items.map(... <Box key={id} as={C} />))에서는 key 없는 fragment로 감싸져 React key 경고가 났습니다.f('#336699')→f('#369'),'10px 0px'→'10px 0'),--i가var(--i)가 됐습니다.await,new, 대입, 쉼표 식, tagged template 값이 조용히 사라졌습니다. 이제 CSS 변수로 전달하고, 함수·클래스는 에러로 보고합니다.<Box {...register('email')} />처럼 다시 읽으면 값이 달라질 수 있는 spread를 spread,className,style에서 3번 평가했습니다. 훅이면 상태가 3개로 갈라집니다.await/yield도 지원합니다.jsx()호출도 같습니다.globalCss()를 값으로 쓰면 잘못된 코드가 나오던 버그 (main에도 있음):const a = globalCss(...)가const a = ;가 됐습니다. 이제void 0이 됩니다.globalCss(),keyframes(),<Global>의 런타임 조건은 조용히 사라졌습니다. 이제 빌드 에러입니다.const DARK = true; DARK ? 'white' : 'black')은 해당 분기로 접습니다.계산값 확장
styled객체의 spread·셀렉터·계산된 키,css(...parts),css()의 템플릿 인자를 full 엔진이 계산합니다.css(styles.card))는 더 이상 모듈을 실행하거나 full 엔진을 선택하게 만들지 않습니다.css(tokens.nested.x))도 상수 경로로 판별합니다.Cannot compose에러는 spread 인자 이름(...rest)을 보여 줍니다.css-utils-literal-only는 값 안에 쓴 콜백의 매개변수([1, 2].map((n) => n * 4))를 허용합니다.추가 점검 검증
cargo test --workspace가 통과하고, clippy(full·lite·wasm32)는 경고가 없습니다.as, spread 1회 평가, 계산값을 직접 확인했습니다.lite 엔진 제거
사용하는 곳이 우리 플러그인뿐이어서 엔진을 Boa가 들어간 하나로 합쳤습니다.
@devup-ui/wasm은 빌드를 하나만 만듭니다.@devup-ui/wasm/liteexport와 lite 빌드, 엔진을 고르는 데만 쓰던hasBuildTimeValues를 없앴습니다.vanilla-extractfeature를 없애고 Boa를 extractor·sheet·wasm crate의 일반 의존성으로 바꿨습니다.@devup-ui/wasm을 불러옵니다. 그래서 dev 서버 실행 중에 추가한 파일도 다른 파일과 똑같이 처리됩니다.css-utils-literal-onlyREADME, changepack에서 lite와 엔진 선택 설명을 지웠습니다.런타임에 바뀌는 값은 빌드 때 굳히지 않음
빌드 때 미리 계산한 값이 런타임에 계산한 값과 다를 수 있는 두 경우를 막았습니다.
1. 코드가 바꾸는 객체·배열·enum 상수
빌드가 볼 수 있는 코드에서 바꾸는 상수는 선언한 값으로 읽지 않습니다. 확인하는 파일은 스타일을 쓰는 파일, 상수를 선언한 모듈, 중간에 re-export하는 모듈입니다.
delete,++push·sort같은 메서드,Object.assigndarken(colors.primary)const에 담기. 예:const theme = { colors }css(obj),styled.div(obj),<Box {...obj} />처럼 객체 전체를 받으면 빌드 에러가 납니다. 에러에는 바꾸는 위치(src/tokens.ts:10:26)를 적습니다.2. 실행 환경에 따라 달라지는 코드
Boa로 계산할 때 아래 값을 읽으면 에러가 납니다. 그래서 그 값에 의존하는 계산은 빌드 때 하지 않습니다.
window,document,navigator,location,process,globalThis에서 정의되지 않은 속성typeof window === 'undefined' ? ...,process.env.NODE_ENV,globalThis.BRAND ?? 'red'css()에서 빌드 에러가 납니다.lite 제거·런타임 값 검증
5db2b827(lite 제거),8866988a(런타임 값).bfd18e60에서 커버리지가 두 줄 모자라 테스트를 보강했습니다.fmt,clippy -D warnings(host·wasm32),test --workspacemutations::tests: 바꾸는 코드, 넘기기, 읽기, 전역 이름을 가린 경우를 분류하는지 확인test_changed_constants스냅샷 21개 케이스const에 담는 경우, 순환 참조window·process·globalThis