Skip to content

fix(binding): make compiler state portable across targets - #794

Draft
owjs3901 wants to merge 1 commit into
mainfrom
fix/portable-binding-state
Draft

owjs3901 wants to merge 1 commit into
mainfrom
fix/portable-binding-state

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Refs #694 (결정론), #686 (WASM/네이티브 바인딩)

W74 네이티브 바인딩의 Rust 전용 선행 PR입니다. Draft로 유지하며, #752가 구형 디스크 상태와 allocator-only 체크포인트를 복원 전에 폐기하는 state-format admission guard를 포함한 뒤에만 병합할 수 있습니다. 네이티브 바인딩과 플러그인 engine 옵션 연결은 이 PR에 포함하지 않습니다.

요약

  • sheet의 hash set 저장 구조와 추출 경로는 유지하고, 내보내는 property 배열만 전체 직렬화 내용에 따른 결정적인 순서로 정렬합니다.
  • keyframes 중복 제거 키를 플랫폼 크기에 의존하는 hash 대신 손실 없는 콘텐츠로 바꿉니다. 실제 이름은 계속 짧은 카운터입니다.
  • debug keyframes도 production과 같은 짧은 카운터를 사용합니다. 기존 debug 전용 k-<hash> 이름의 스냅샷 변경은 사용자가 승인한 동작 변경입니다.
  • exported state generation을 갱신해 #752의 디스크 상태 admission guard가 구형 키와 새 키를 섞지 않도록 합니다. 이 PR은 플러그인과 plugin-utils를 수정하지 않습니다.

동작

StyleSheetProperty와 selector의 기존 CSS 정렬 비교는 직렬화되는 모든 필드를 구별하지 않습니다. 따라서 그 비교로 BTreeSet을 만들거나 단순 정렬해 서로 다른 레코드를 잃는 방식은 사용하지 않습니다. 원시 export는 모든 레코드를 보존하면서 hash 함수와 pointer width에 독립적인 순서를 갖고, CSS 방출 순서는 그대로 유지됩니다.

keyframes 콘텐츠 키는 step 및 선언 순서와 값 경계를 보존하며, 실제 CSS 이름으로 콘텐츠나 hash를 내보내지 않습니다. production의 code, source map, CSS, CSS 파일명, dependencies는 수정 전의 실제 WASM 기준 결과와 바이트 단위로 비교합니다.

새로 생기는 오류

새 application build error는 없습니다. 구형 디스크 캐시의 안전한 폐기는 오류가 아니라 #752 admission 경계의 cold-start이며, 그 가드는 이 PR의 필수 병합 선행 조건입니다.

남는 한계

  • 이 Rust PR만으로 구형 디스크 캐시 혼입 방지를 완료했다고 주장하지 않습니다. 현재 main의 Next 세 곳과 Webpack 한 곳은 generation 필드를 검사하지 않으며, fix(plugin-core): align bundler resolution, roots and WASM diagnostics #755/#752의 변경된 아키텍처에 맞는 가드가 필요합니다.
  • 네이티브 package, 8개 플랫폼 matrix, 원시 네 export 전체의 WASM/native 동일성 gate, npm 게시, 플러그인 engine 연결은 후속 PR입니다.
  • debug keyframes 이름은 의도적으로 변경됩니다. production 카운터 이름과 CSS 내용의 바이트 동일성은 별도로 검증합니다.

검증

현재 증거: main 기반에서 canonical sheet export 1건, 콘텐츠 keyframes 키 1건, debug 글로벌/로컬 카운터 2건의 회귀 assertion RED를 실제 확인했습니다. 첫 keyframes 테스트의 enum 비교 컴파일 오류는 테스트 자체를 수정한 뒤 assertion RED로 다시 확인했으며, 컴파일 실패를 RED 증거로 계산하지 않았습니다.

fresh WASM 빌드와 root package 빌드가 통과했습니다. 128개 요소와 keyframes/global CSS를 포함하는 fixture의 수정 전/후를 새 프로세스 3회씩 비교하여 production code, source map, CSS, CSS 파일명, dependencies, updatedBaseStyle, getCss 문자열이 모두 동일함을 확인했습니다.

기본 stack에서 Rust workspace 5,817개 테스트, workspace all-target/all-feature clippy -D warnings, cargo fmt --all, bun lint가 통과했습니다. 기존 React 권고 lint warning 20개는 수정하지 않았으며 새 warning은 없습니다. Bun 1.4.3 + 작업 전용 최신 Node 26.12.0에서 전체 Bun 6,631 pass / 0 fail, 함수·라인 커버리지 100%를 확인했습니다. Node는 공식 latest 실행파일의 SHA-256을 검증했고 공유 설치는 변경하지 않았습니다.

의도된 snapshot 변경은 Button의 whole / partial / icon+loading spinner 3건으로, animation-name의 k-1813759225198633340가 a로 바뀐 한 필드씩입니다. Rust .snap.new는 없습니다. 초기 구형 공유 Node 실행의 Windows PID 종료 확인 1건은 최신 도구 전체 재실행에서 통과했으며 해당 플러그인 테스트를 수정하지 않았습니다.

공유 머신의 3회 warm median 범위는 추출 수정 전 3.3325–3.7078ms / 수정 후 3.3259–4.1819ms, sheet export 수정 전 0.0350–0.0382ms / 수정 후 0.0448–0.0641ms입니다. hash-set 저장 구조를 유지한 export 경계 정렬 비용을 측정한 것이며, 잡음이 있는 이 결과로 성능 향상을 주장하지 않습니다.

실제 production probe 출력:

import "@devup-ui/react/devup-ui.css";
const fade = "a";
export const view = <div className="b c" />;

같은 probe의 CSS 핵심 부분 발췌:

@keyframes a{from{opacity:0}to{opacity:1}}.b{animation:a 1s linear}.c{padding:8px}

Head: e06b139b82a26a8f70525691611f5b25674eafcc. Husky hook을 우회하지 않은 한 번의 커밋 실행으로 생성됐고 작업 트리는 깨끗합니다. PowerShell 5.1의 native stderr 승격으로 바깥 wrapper는 exit 1을 표시하고 상세 hook 로그를 잃었으므로, 로컬 tarpaulin 수치는 추정해 기재하지 않습니다.

CI 39405079981는 success입니다. Linux tarpaulin은 --fail-under 100을 적용해 100.00%, 15,581/15,581 lines covered, Bun은 **6,631 pass / 0 fail, 함수·라인 100%**를 확인했습니다. wasm-size, benchmark, publish, react-18, landing-next-e2e, vinext-rsc-css-e2e, release-compat, webpack-extends-theme 8개 validation job이 모두 통과했습니다. PR에서 권한 있는 release와 deploy-pages가 skip되는 것은 의도된 동작이며 게시·배포는 수행하지 않았습니다.

최종 리뷰는 implementation blocker 없이 APPROVE입니다. CI가 초록이어도 이 PR은 #752의 구형 디스크 상태 및 allocator-only 체크포인트 admission guard가 들어오기 전까지 Draft이며 병합하면 안 됩니다.

로컬은 stable Rust 1.99.1, Bun 1.4.3, 기본 stack, CARGO_BUILD_JOBS=2와 6 GiB process-tree watchdog을 사용합니다. Rust LSP daemon은 timeout이고 TypeScript LSP는 이전 설치 거절 상태이므로 LSP-clean이라고 주장하지 않습니다.

Refs #694

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

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

codecov Bot commented Oct 20, 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%> (ø)
...s/extractor/src/extract_style/extract_keyframes.rs 100.00% <100.00%> (ø)
libs/extractor/src/lib.rs 100.00% <ø> (ø)
libs/sheet/src/lib.rs 100.00% <100.00%> (ø)
🚀 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.

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