Skip to content

fix(extractor): filter every transient prop from styled rest spreads - #774

Open
owjs3901 wants to merge 19 commits into
mainfrom
fix/transient-props
Open

owjs3901 wants to merge 19 commits into
mainfrom
fix/transient-props

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Refs #689, #682 (W50: transient props). #711 위에 쌓은 PR이며, #711이 먼저 병합된 뒤 이 PR을 병합해야 합니다. 기준 커밋은 9e4d28abcf769705644818aba5d6bc943b2c6ff3입니다. CI의 publish.yml 실행을 위해 base는 main입니다.

요약

styled-components 5.1의 transient props 규칙대로, 이름이 $로 시작하는 모든 props를 렌더링 대상에 전달하지 않습니다. 소스에서 읽거나 열거하지 않은 이름, 호출자가 런타임 spread로 넘긴 이름, attrs가 만든 이름에도 적용됩니다. 태그와 컴포넌트, as로 바뀐 대상 모두 같은 규칙을 따릅니다.

생성된 styled 컴포넌트의 rest spread에만 짧은 인라인 필터를 넣습니다. 추가 import나 스타일링 런타임은 없고, rest spread가 없는 경로에는 필터가 생기지 않습니다. 스타일 함수와 attrs는 필터 전 props를 계속 읽으므로 $tone 같은 값은 기존 CSS 변수에 정상 반영됩니다. #711의 read props, theme, 빌드 시 shouldForwardProp, 기본 태그 정책은 변경하지 않았습니다.

migration 문서에 transient props 지원을 명시하고, coordinator가 승인한 같은 페이지의 두 오래된 .withConfig() 설명도 고칩니다. shouldForwardProp는 빌드 시 평가하고, displayName/componentId 등 다른 설정 메타데이터는 무시한다는 #711 동작으로 맞춥니다.

동작

실제 WASM 프로브 입력:

import { styled } from '@devup-ui/react'
export const Card = styled.div.withConfig({ shouldForwardProp: () => true })({ p: 2 })

변경 전 실제 생성 코드:

export const Card = ({ style, className, as: DevupAs = "div", forwardedAs, ...rest }) => <DevupAs {...rest} as={forwardedAs} className={["a", className].filter(Boolean).join(" ")} style={style} />;

변경 후 실제 생성 코드:

export const Card = ({ style, className, as: DevupAs = "div", forwardedAs, ...rest }) => <DevupAs {...Object.fromEntries(Object.entries(rest).filter(([k]) => k[0] !== "$"))} as={forwardedAs} className={["a", className].filter(Boolean).join(" ")} style={style} />;

기존 CSS import와 .a{padding:8px}는 그대로입니다. 이 프로브의 생성 코드 크기는 236 → 302 bytes(+66), Bun minify 후 252 → 314 bytes(+62)입니다. rest spread를 전달하는 컴포넌트마다 이 정도 코드와 props 복사/필터 비용이 추가되며, 사용자가 승인한 비용입니다. 스타일 계산을 런타임으로 옮기지는 않습니다.

React 서버 렌더링에서 변경 전에는 $, $id, $other, $forced, $fromAttrs가 대상에 도달해 Invalid attribute/unknown-prop 경고가 발생했고, 컴포넌트 대상은 $ 키를 실제 받았습니다. React가 잘못된 속성을 최종 HTML에서 자체 제거하는 것만으로는 지원을 증명할 수 없으므로, 경고가 전혀 없다는 것과 컴포넌트가 받은 키도 함께 검사합니다. 일반 id, title, data-*는 유지합니다.

새로 생기는 오류

없습니다. #711의 기존 빌드 오류 및 지원 범위를 바꾸지 않습니다.

남는 한계

  • 런타임에만 도착하는 $ 없는 임의의 이름(invented 등)에 대한 전체 유효 속성 검증은 이 작업에 포함하지 않습니다. #711처럼 기존 rest 전달 및 빌드 시 확인 가능한 forwarding 정책을 유지합니다.
  • 이 기준 브랜치의 public styled TypeScript 타입은 .attrs()와 .withConfig()를 선언하지 않습니다. 이 PR에서 타입을 확장하지 않고, 런타임 렌더링 회귀 테스트는 suppression/cast 없는 .test.jsx로 작성했습니다. 별도 #738의 정확한 head 0dcb7174172cb3382bc4a65c5c5f4c5bfeb39e2c는 StyledTemplate.attrs/withConfig를 이미 선언하고 public/compat export도 이를 사용합니다.
  • 기존 Vite/Webpack plugin 테스트가 describe 범위에서 console.error = mock()으로 바꾼 뒤 복구하지 않는 테스트 격리 문제가 발견되었습니다(packages/vite-plugin/src/__tests__/plugin.test.ts:227, packages/webpack-plugin/src/__tests__/plugin.test.ts:174). 이 PR에서는 기존 테스트를 바꾸지 않고 새 SSR 관찰을 한 foreground Bun child에 격리합니다. 경고 문자열을 필터링하거나 assertion을 완화하지 않습니다.

검증

  • 회귀 우선: 변경 전 Rust 생성 코드 실행 12/12 실패(런타임 $ 키가 실제 전달됨), React 서버 렌더링 5/5 실패(실제 경고 또는 컴포넌트 전달 키).
  • 변경 후 Rust 집중 테스트 15/15 통과: 태그/컴포넌트, forwarding 정책 없음/일반 predicate/항상 true, attrs, as, 빈 withholding 목록, 일반 JSX 무변경.
  • cargo fmt --all, cargo +1.99 clippy --workspace --all-targets -- -D warnings, cargo test --workspace: 통과(총 2,458 unit/doc tests, 0 실패).
  • fresh WASM 및 root build, bun test 5,503 통과/0 실패/100% coverage. 실제 SSR fixture 직접 실행도 exit 0이며 모든 시나리오의 warnings가 빈 배열, 태그의 id/title/data-*와 CSS 변수 값이 유지되고 컴포넌트/as 대상에 $ 키가 없음을 확인했습니다. 최종 hook/CI 결과는 아래 진행 상태로 갱신합니다.
  • bun lint: 기존 경고 2개 외 새 오류/경고 없음.
  • 두 commit의 실제 lint/tarpaulin/Bun hook은 통과했습니다. Windows LLVM tarpaulin은 97.74%(14,547/14,883), Bun은 100%를 보고했으므로 로컬 Rust 100%라고 주장하지 않습니다. Linux CI의 별도 100% coverage gate를 반드시 통과해야 ready로 전환합니다.
  • CARGO_BUILD_JOBS=2와 빌드/테스트 descendant tree 6 GiB watchdog을 사용했습니다. Git Bash에서는 PID 계보만으로 자식 메모리를 덜 집계하는 것을 확인해, 최종 전체트리 commit hook에는 Windows kernel Job의 12 GiB cap을 함께 적용했습니다. 실제 전체 Job 피크는 3,296,841,728 bytes로 제한 아래였습니다.
  • LSP는 Rust daemon timeout, JS 서버 미설치, MDX 미설정으로 사용 불가였으며, 실제 compiler/clippy/lint/test 결과로 검증했습니다.
  • #711의 기존 테스트 입력과 의미 assertion은 그대로입니다. 12개 문자열 expectation의 유일한 차이는 필터 wrapper이며 자동 byte audit로 확인했습니다: test_styled_extension_composes_base의 Ext/ObjExt/Twice/FromRuntime/FromChanging/Ordered(6), test_styled_with_component_and_as의 Aside(1), test_styled_prop_forwarding의 tone / $c+tone+theme / x+y+theme / $c / level+theme spread(5).
  • 아래 58개 snapshot의 101개 필터 삽입 외 모든 canonical repository byte가 기준 커밋과 동일함을 자동 audit로 확인했습니다. snapshot 본문의 CSS/style 결과, attrs, 타깃, class/style 및 기존 withholding expression은 동일합니다.
변경된 snapshot 58개 (libs/extractor/src/snapshots/)
extractor__tests__compiled_bindings_never_dangle.snap
extractor__tests__compose_rules_the_module_computes.snap
extractor__tests__compute_what_elements_and_styled_objects_take.snap
extractor__tests__conditional_composition.snap
extractor__tests__constant_objects_arrays_and_enums.snap
extractor__tests__create_global_style_collapses_to_a_null_component.snap
extractor__tests__emotion_and_styled_components_object_styles-2.snap
extractor__tests__emotion_and_styled_components_object_styles.snap
extractor__tests__emotion_global_component_and_import_surface.snap
extractor__tests__import_alias_custom_named_export.snap
extractor__tests__import_alias_emotion_styled.snap
extractor__tests__import_alias_multiple_aliases.snap
extractor__tests__import_alias_renamed_import.snap
extractor__tests__import_alias_styled_components.snap
extractor__tests__inline_imported_constants.snap
extractor__tests__layer_record_places_every_declaration.snap
extractor__tests__namespace_import_of_a_default_only_package_redirects.snap
extractor__tests__package_imported_whole.snap
extractor__tests__style_results_read_as_values.snap
extractor__tests__styled-10.snap
extractor__tests__styled-11.snap
extractor__tests__styled-2.snap
extractor__tests__styled-3.snap
extractor__tests__styled-4.snap
extractor__tests__styled-5.snap
extractor__tests__styled-6.snap
extractor__tests__styled-7.snap
extractor__tests__styled-8.snap
extractor__tests__styled-9.snap
extractor__tests__styled.snap
extractor__tests__styled_accepts_both_call_forms.snap
extractor__tests__styled_components_attrs_and_every_styled_import.snap
extractor__tests__styled_components_import_surface_fully_redirects.snap
extractor__tests__styled_components_of_any_base.snap
extractor__tests__styled_components_theme_resolves_to_css_variables.snap
extractor__tests__styled_object_form_theme_interpolation.snap
extractor__tests__styled_typescript_wrappers.snap
extractor__tests__styled_with_spread.snap
extractor__tests__styled_with_variable-2.snap
extractor__tests__styled_with_variable-3.snap
extractor__tests__styled_with_variable-4.snap
extractor__tests__styled_with_variable-5.snap
extractor__tests__styled_with_variable.snap
extractor__tests__styled_with_variable_like_emotion-2.snap
extractor__tests__styled_with_variable_like_emotion-3.snap
extractor__tests__styled_with_variable_like_emotion-4.snap
extractor__tests__styled_with_variable_like_emotion-5.snap
extractor__tests__styled_with_variable_like_emotion-6.snap
extractor__tests__styled_with_variable_like_emotion-7.snap
extractor__tests__styled_with_variable_like_emotion.snap
extractor__tests__styled_with_variable_like_emotion_props-2.snap
extractor__tests__styled_with_variable_like_emotion_props-3.snap
extractor__tests__styled_with_variable_like_emotion_props-4.snap
extractor__tests__styled_with_variable_like_emotion_props.snap
extractor__tests__styles_the_build_cannot_read.snap
extractor__tests__template_interpolations.snap
extractor__tests__wrong_styled_with_variable_like_emotion_props-2.snap
extractor__tests__wrong_styled_with_variable_like_emotion_props.snap

진행 상태

CI 38246397965는 정확한 head b77cc5d17c1a221babe73f3b0c4573dae8a1bc25에서 success입니다. coordinator의 green 통보 뒤 publish job 114801213376 로그를 직접 확인했습니다.

  • Linux Rust: 100.00% coverage, 10543/10543 lines covered. 새 styled_transient.rs도 15/15입니다.
  • Bun: 5503 pass, 0 fail, functions/lines coverage 모두 100%.
  • 네 job 모두 success: publish(lint, coverage, Bun, 패키지 entry/tarball 검증, landing e2e), benchmark, landing-next-e2e, vinext-rsc-css-e2e. Bun preload race 회귀도 8/8 통과했습니다.

이 결과 확인 후 ready로 전환합니다. #711 위의 stack 및 #711 먼저 병합 조건은 그대로 유지하며, 이 PR을 병합/승인/닫지 않습니다.

owjs3901 and others added 19 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>
No conflicted files; preserve composition changes and main ESLint checks.
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.
Refs #689, #682

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

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

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

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

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
  • 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.
  • 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
  • Styled components now remove every runtime prop whose name starts with $ before forwarding a rest spread to a tag or component, including props supplied by callers or attrs. Style and attrs functions can still read transient props, and all other build-time forwarding policies remain unchanged. The styled-components migration guide documents transient prop support. No new build errors

@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

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

Patch

  • Styled components now remove every runtime prop whose name starts with $ before forwarding a rest spread to a tag or component, including props supplied by callers or attrs. Style and attrs functions can still read transient props, and all other build-time forwarding policies remain unchanged. The styled-components migration guide documents transient prop support. No new build errors

@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 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
libs/extractor/src/composition.rs 100.00% <100.00%> (ø)
...tractor/src/extractor/extract_style_from_styled.rs 100.00% <100.00%> (ø)
libs/extractor/src/extractor/styled_transient.rs 100.00% <100.00%> (ø)
libs/extractor/src/imported_constants.rs 100.00% <100.00%> (ø)
libs/extractor/src/lib.rs 100.00% <ø> (ø)
libs/extractor/src/prop_modify_utils.rs 100.00% <100.00%> (ø)
libs/extractor/src/prop_valid.rs 100.00% <100.00%> (ø)
libs/extractor/src/style_values.rs 100.00% <100.00%> (ø)
libs/extractor/src/styled_reads.rs 100.00% <100.00%> (ø)
libs/extractor/src/utils.rs 100.00% <100.00%> (ø)
... and 2 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
owjs3901 marked this pull request as ready for review October 12, 2026 12:23
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