Repository navigation
Conversation
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Contributor
Changepacks@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.jsonPatch
@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.jsonPatch
@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.jsonPatch
@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.jsonPatch
@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.jsonPatch
@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.jsonPatch
@devup-ui/react@1.0.44 → 1.0.45 - packages/react/package.jsonPatch
@devup-ui/reset-css@1.0.31 → 1.0.32 - packages/reset-css/package.jsonPatch
@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.jsonPatch
@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.jsonPatch
@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.jsonPatch
|
Codecov Report✅ All modified and coverable lines are covered by tests.
... and 44 files with indirect coverage changes 🚀 New features to boost your workflow:
|
This was referenced Oct 5, 2026
Merge updated parent PR 750 (74afb76), containing main 11790be. Conflicts: libs/extractor/src/prop_modify_utils.rs and conditional Tailwind snapshot. Share main's per-class Tailwind compiler between normal generation and PR 756 class capture, retaining semantic IDs and single evaluation. Regenerate the conditional snapshot to combine main's extra line-height declaration with the captured controller; retain all assertions and update five exact template-code expectations for PR 756 single-coercion IIFEs. Full workspace tests, fresh WASM/root builds and real WASM probe pass; no snapshots were hand-merged. Refs #686 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901
added a commit
that referenced
this pull request
Oct 8, 2026
owjs3901
added a commit
that referenced
this pull request
Oct 8, 2026
Merge #756 at f2fc451, retaining the style-order metadata policy and forwarding/callback provenance from faff4a0. Keep the current scope-aware style-order-range rule and clarify that nested style metadata is checked while ordinary props and data are ignored. Coordinator-approved integration reconciliation: the incoming test_tailwind_keeps_other_classes cases 07, 08, 09, 14 and 15 retain the current once-only __devupValue0 controller captures; case 10 folds the truthy literal a without reading the right side of OR. See the source-fed failure evidence D/W38g-756-test-conflict.md and successful executed controls W38g-756-tailwind-qa3.log. Existing HEAD assertions and goldens are unchanged. Reclassify exactly the two incoming ESLint fixtures containing nested zero and a computed styleOrder zero from valid to invalid, retaining their source and ordinary props/data controls. #766 reads metadata at those positions; #756 alone predated it. Pin the 2+1 original diagnostic locations, as recorded in W38g-756-eslint-focused.log. Rule production behavior is unchanged. Fresh gates: Rust1.99 clippy, default workspace4119/0, rebuilt WASM/root, Bun6472/0 aggregate100%, lint0errors/2baselinewarnings, original48probes with only the incoming1.0.82-to1.0.83 banner delta, and named literal/wrapper QA. The incoming guarded-hover/active cascade finding is reported separately, not fixed by this merge, per coordinator attribution instructions. Refs #684 #685 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901
added a commit
that referenced
this pull request
Oct 8, 2026
Merge #757 at 78ef660 after the genuine #756 integration975f3371. Recursive merge bases74afb760 and6d0c9c83 produce an identical tree3c14e7e427686d8802d1e0722afc33515d3eed28 with no conflicts or source changes. Preserve both exact base histories and normal hook gates. Keep the approved six incoming Tailwind expectation and two incoming ESLint fixture reconciliations from the prior merge, current JSX capture/constant-fold semantics, located metadata errors, and existing HEAD assertions/goldens. F195 and the remaining coverage units are separate subsequent changes. Refs #684 #685 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901
added a commit
that referenced
this pull request
Oct 8, 2026
Refs #690, #693 Merge exact f2fc451 retaining the current native/readback/provenance and incoming namespace/JS-semantics changes, without resetting merge state. Resolve five combined source/snapshot seams, use direct private tracking fields and current evaluate_located test API, and bind the incoming reachable-file helper to existing resolveSourceFile. Coordinator-approved merge-test alignments: lexical local require is not a module request; genuine unbound require keeps its original helper.ts:1:25 semantic site and unchanged cause/Fix/no-publication, while reference-free exploration falls back to entry.tsx:2:23 ingress; eight native consumer cases evaluate independent runtime sentinels, exact once-only getters/calls and wrong-slot/restoration instead of generated identifier spellings; only positive g uses valid styleOrder={undefined}, with the complete original invalid JSX retained as the located a.tsx:9:34 negative. All other fixtures and oracles remain unchanged; incoming capture/spread snapshot preserves the existing CJS_DYNAMIC alias. Full workspace4719, Rust1.99clippy, fresh WASM/root, Bun5535, preservation/API/carrier/graph and50both-mode browser positive/wrong-class/restoration gates passed. The prior advisory B Oracle result was lost to tool failure; coordinator authorized full-diff self-review and objective gates, not a claimed clean Oracle review. No contextual carrier/plugin delivery or out-of-scope Devup-only composition fix is included. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This was referenced Oct 11, 2026
Refs #682 #695 #688 #689 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ue branches A generated declaration nothing reads, holding a value made only to be named, goes. One that only names a parameter or an import nothing writes or rebinds goes too, its reads reading that name instead, so `<__devupRecord10 />` after `const __devupRecord10 = Dyn` is `<Dyn />`. A generated arrow left only returning a value is written as it. A branch packet `test ? [true, value] : [false]` whose value is never `undefined` holds that value itself: which branch ran is whether it is defined, or whether it is truthy where it always is when given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed values
A branch capture whose one branch gives a single value never
`undefined` and whose other gives none is now built as that value,
`test ? value : void 0`, where it is made, instead of being repacked
after the fact from the shape of its packet.
The build ran code for a style value even where the module handed the
binding it reads to unknown code, as long as the build could not read
that binding itself: `const made = make(); watch(made);` let
`css({ p: f() })` bake `made.p` in although `watch` may change it. What
such code reads is now checked where it runs: a value it hands on that
holds an object there computes nothing, leaving the error the build
gives for any value known only at runtime.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…onJS exports in order
- A constant string or array has its length, and a string its UTF-16
units, so `PRIMARY.length` is the number it is.
- A style prop of a component the module computes from constants with
its own code, such as `color={darken(0.4, PRIMARY)}`, is computed like
the arguments of `css()`. An element prop reads a hole or `undefined`
as it is, so a value JSON would turn into `null` is left to runtime.
- A conditional whose branches each give the styles one whole value is
captured as that one value, so `a === b ? `${a}` : `${a}`` no longer
repacks a flag no class reads.
- A string a template literal holds as a value is written as its text,
except in a tagged template, whose tag reads the parts as written.
- `CommonJS` exports are read as the top-level statements leave them in
order: `module.exports = { A: 'a' }; module.exports = { A: 'b' }`
exports `b`. Once `module.exports` holds another object, `exports.x`
writes to the one no importer reads, which the build baked in before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ches only for its effects Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build wrote checks whose answer it had just written, fallbacks never
reached and values wrapped to be unwrapped again. Each is now written as
the code it means, running the same steps in the same order:
- The styled renderer no longer checks the record it wrote itself:
`[2] !== void 0` and `[1] === void 0` fold where the build knows the
slot, and a composing component reads the same classes as its own.
- A pass after the IIFEs unwrap simplifies what remains: `(0, f)()` to
`f()`, string-only `[...].filter(Boolean).join(" ")` to its text,
`"a" || x` and `x ?? void 0` to their left side, conditions read only
for whether they hold (`c ? true : false`, `c ? d : false`, a value
guarded by its own guard), conditionals of a known or repeated test,
templates held in templates, objects spread in place in objects and
JSX, `?.` on a generated name holding an object, and leading values a
sequence reads to drop.
- A generated constant declared as another is read as that one, found
by scope, so components repeating the same names lose them too.
- A conditional whose branches are the same code keeps its condition
only for its effects; the restore `branch_capture` did for it is gone.
Each rule keeps what reading changes: a member or name called through a
sequence keeps its `this` and indirect `eval`, a function keeps the name
it would infer, `typeof` is only dropped for declared names, and `key`
stays spread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… array
`["a b", className].filter(Boolean).join(" ")`, which every styled
component renders with, is written as `className ? `a b ${className}` :
"a b"`: the same test `filter` runs and the same text `join` writes,
without building an array and calling two methods on each render.
The name is read once more to test it, so only a declared or generated
name takes this form; a member, two names or texts a template would read
apart stay as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…op filter
- `const { ..., ...rest } = { ...props, ref }` is written as
`const { ..., ...rest } = props; rest.ref = ref;`: the rest is a new
object either way and takes `ref` where it did, so the props are no
longer copied twice on each render.
- `Object.fromEntries(Object.entries(rest).filter(([k]) => ...))`, which
each styled component writes, is written once per module as a function
when more than one component drops the same props. Only a predicate
reading nothing but its key moves, and none does where the module
declares its own `Object`.
- A generated constant declared as a constant or an import of the module
is read as that name, as one declared as another generated name was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Read code the build wrote as text as code, so every peephole rule sees it - Render forwardRef components as one arrow: destructure props in the parameter and pass `ref` and constant attrs as JSX attributes - Copy `.attrs` objects once (`rest.k = v`) instead of building `__devupProps` - Inline a generated constant into the declaration that reads it next - Collapse `c ? x : x` to `(c, x)`, and drop reads of parameters and functions from sequences - Write a DOM `className` of a defined-or-undefined branch as `b && "a"` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thout the composition protocol A styled component reading runtime values carried the composition protocol (`__devupCss0`, the record tuple and the hook calls) even when nothing could extend it at runtime. Only a component the module exports, or one given `css` or read as a value, can receive it; every other one renders directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generated constants no code changes are recorded as they are built, so a member read of one becomes its literal, `void 0` when nothing writes it, or the member of the object it was copied from. An object read only to be spread is built where it is spread, an object pattern no longer reads properties a later key overrides, `filter(Boolean)` loses parts written falsy, objects lose spreads of nothing, and templates splice in strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… value cannot change A generated constant read once now moves to where it is read when evaluating its value there does nothing and gives the same value: a literal, a binding nothing reassigns that an earlier statement initialized, a member of an object rest whose key no code changes, or tests of those. Where a moved value read a style variable, the styled renderer loses its body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…needs Inside a function, a generated constant no code reads goes when computing it does nothing, one only written into goes with each write left as the value it assigns, and one bound to a short literal is written in place of each read. A statement whose evaluation does nothing goes too, and `a && b` whose `b` does nothing is kept as `a`. The top level keeps its constants, which a script may read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI formats the sources to one line per call before measuring, so an
`else { return }` no input reaches gets a line of its own. Checks that
another already made are folded into helpers shared with a path that
fails them (the generated call and arrow `unwrap_iife` reads, the return
it unwraps, a sequence `split` and `omit_ahead` take apart, a captured
class), let chains replace the rest, and the paths inputs do reach gain
tests: values `defined` tells apart, omissions a key, default or rest
rules out, a body not ending in a return, an empty object nothing reads,
a name written through a pattern, handed values of a namespace or of a
change, a CommonJS module whose exports the build cannot read, and props
with nothing withheld.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The attrs of an imported styled component are now written straight onto the element as `role="note"` rather than through an object literal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…className prop
A styled component now writes its classes as
`className ? `... ${className}` : "..."`, so the hoist check reads the
classes written when no className is given.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A condition with two equal branches is written as `(test, value)`, and
the code generator drops the parentheses, which JSX needs around a comma
operator: `stroke={theme === "light", "#6159D4"}` does not parse. Inside
a JSX expression the sequence is written as `[test, value][1]`, which
evaluates its parts in the same order and gives the same value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…le does not hold
A literal key into a literal table, such as `[1, 0.5][-10]`, `{ a: 1 }["wrong"]`
or a hole in `[1, , null][1]`, always reads `undefined`. The extractor emitted a
runtime lookup for it, with CSS for every branch and an empty `class=""`.
Now such a selection reaches the member extractor as written, as on main:
- an array: any numeric literal key
- an object: a string, a template with no expressions, or a non-negative
integer, when the table holds the key or it is not a name every object
inherits
The table may evaluate nothing: only literal values and plain properties, with
holes at its top level. This assumes the built-in prototypes are untouched, as
main did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rops
A spread the build cannot read in full reaches the element as it is. When it
names styles, those styles never became classes. They landed on the DOM as
props, with no error.
The build now refuses two such spreads:
- `<Box {...base} />` where code changes `base`. This restores the error main
gave.
- An object that names styles next to a key only the running code knows. For
example, `{ p: 1, [k]: 1 }` or `{ ...rest, p: 1 }`. The build cannot tell
which of its styles that key replaces. This covers JSX and `jsx()` calls.
An object with no style keys still passes through as it is.
Contract tests that pinned the old pass-through now expect the build error.
Fixtures that used such an object for something else now give those keys at
runtime.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as props
`<Box {...(on ? { p: 1 } : rest)} />` compiled with no styles. When `on` held,
`p` reached the DOM as a prop. Main refused it with "cannot use `rest`", and
the build does so again.
A spread's sides are read as the build reads rules:
- a side whose styles the build reads, next to one it cannot read, is refused
at the side it cannot read
- an object among them that names styles next to keys only the running code
knows is refused as before
`on && { … }` compiled with no styles too, though it spreads what
`on ? { … } : null` does: a falsy left side is a primitive. It is now written
as that conditional and compiles to a class.
Contract tests that pinned raw values for a literal next to runtime props now
expect the build error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d styles behind guards
`jsx(Box, { ...(on ? { p: 1 } : null) })` and `{ ...(on && { p: 1 }) }`
compiled with no class, and `p` reached the element as a prop. Main
compiled them. They are now read as `<Box {...(on ? { p: 1 } : null)} />`
is: each side's styles become a conditional class, and the props a side
names next to its styles still reach the element.
`<Box {...(rest || knownImport)} />` with an imported `{ bg: 'red' }`
passed `bg` on as a prop, as only conditional spreads read imported
objects. A logical spread now reads them when a side names styles, so
- `rest || knownImport` is refused at `rest`
- `cond && knownImport` compiles to a class
A logical spread of imported objects naming no styles is left as written.
A side `(on, { p: 1 })` spreads its last expression and is read so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ay does not hold
`<Flex opacity={[1, 0.5][+10]} />` always reads `undefined`, yet it
emitted both opacity classes and a runtime lookup table. `+10` is the
number `10` as `-10` is `-10`, so the literal index is now kept for the
member extractor, which selects no style: `<div className="a" />`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A style value written longer than the one value it always holds emitted a
class for every value it was written with, and a runtime lookup among them:
- `opacity={[1, 0.5]['10']}` emitted both classes, though it reads `undefined`
- `opacity={[1, 0.5]['1']}` emitted `1` too, though it reads `0.5`
- `[1, 0.5][0 + 1]`, `[1, 0.5][(0, 1)]`, `[1, 0.5][true ? 0 : 1]` kept the table
- `!0 ? 1 : 0.5` kept both classes and the test
- `[1, 0.5, 0.2][c ? 0 : 1]` emitted `0.2`, which no key reads
- `({ a: 1, a: 0.5 }).a`, `({ ...{ a: 1 }, a: 0.5 }).a` and `(0, 0.5)` were
left to the runtime as a CSS variable
Style values are now written as the value they hold before the build reads
them: a literal test picks its side, numbers add up, a literal key into a
literal table reads what it holds there (`void 0` where it holds nothing),
and a conditional key into a table of literals chooses between what each
key reads. Only literals are left out, whose evaluation the code cannot
observe, so the code runs the same steps; a `styleOrder` is still reported
as written.
This holds for JSX props, `jsx()` props, the `css` prop, `css()`, `css`
templates and `styled`. `styled` reads `void 0` as `undefined`, no style.
A `jsx()` conditional spread whose props are captured for a call after it
is now covered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Style values are folded before the extractor reads them, so its own literal tests now see only `undefined`, which a binding may name and the fold leaves as written. The extractor reads a literal test as the fold does, and its nullish test so reads `void 0` as `undefined`. Covered: - `undefined` on the left of `&&`, `||`, `??` and as a test, alone and captured around calls - a styled lookup whose key no fold reads - tables whose left-out entries are nested arrays and objects - a string on the left of a captured logical Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`<Box {...(rest || { bg: 'red' })} />` and `{...(rest ?? knownImport)}` are
build errors now: the build would otherwise hand the fallback's styles to
the element as props. no-unknown-style-spread still reported them, and its
agreement tests still expected the build to pass them through.
The rule now repeats no build error, as for the mixed conditionals: a
spread whose runtime side sits next to a side the build reads styles from
is not reported. A side the build reads is an object literal naming keys,
an import or a const holding one, of a style-capable type. Empty literal,
DOM-only and computed fallbacks are passed through, and still report.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions
`jsx(Box, { ...styles })`, `jsx(Box, { bg: theme.bg })` and the
`createElement` forms never inlined the imported constants their props
read, so the build passed the object through at runtime or set a CSS
variable, where `<Box {...styles} />` compiles to a static class. The
props of a call building a component of the package are now read as its
JSX attributes are, spreads included, so both forms compile alike: a
spread mixing an import with a runtime value is the same build error.
`styled.div`opacity: ${undefined};`` set a CSS variable holding nothing,
and `${false}` wrote `opacity: false`. styled-components and Emotion
interpolate `null`, `false` and `undefined` as no text, so the build now
writes them as none, and drops a declaration left without a value, as
browsers do. An `undefined` a binding names is still read as written.
The signed-infinity alias test pinned the old dynamic call output; the
call now reads `NaN`, as the JSX element already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stable clippy now reports `map(f).unwrap_or_default()`, which it asks to write as `map_or_default(f)`, and a clone of a value read for the last time. CI installs stable, so the lint step failed on code this branch did not change. The rewrites are clippy's own and change no behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No code installs the hook, and the optional dependency is enabled only by `--all-features`. Current cargo reports it as unused, which the lint step denies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI installs stable, which became rustc 1.100.0 on 2026-11-13. Its coverage instrumentation reports the head line of a match arm with a block body as uncovered even when its body runs, so cargo-tarpaulin 0.37.6 measured 97.56% across files this branch never touched, and the 100% threshold failed. The new clippy lints failed the lint step the same way. Pin the toolchain to 1.99.1, the last release CI passed on, and revert the rewrites made for the 1.100 clippy, which 1.99.1 does not ask for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Return CI to the latest stable Rust, as the workflow promises, and keep the rewrites the 1.100 clippy asks for: `map(f).unwrap_or_default()` as `map_or_default(f)`, and no clone of a value read for the last time. They compile on 1.99.1 as well. Only the coverage run stays on 1.99.1. cargo-tarpaulin 0.37.6, its latest release, misreads the coverage rustc 1.100 records: it reports the first line of match arms, impls and macros whose bodies run as uncovered, 662 lines across files no test misses. Neither tarpaulin's ptrace engine (90.5%) nor cargo-llvm-cov reproduces the measure on 1.100 either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vite 8.3.8, vinext 1.4.5, next-intl 4.15.1, prettier 3.10.1, eslint-plugin-mdx 3.9.5, @babel/core 8.1.2, StyleX 0.19.2 and errno 0.3.15. Every other Rust crate is already at its latest release, and the React 18 example keeps React 18, which it exists to test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
JSX·styled 변환에서 불필요한 arrow와 중간 값을 없애고, 생성 코드를 그 코드가 뜻하는 가장 짧은 형태로 씁니다. JavaScript의 평가 순서와 단일 평가, 예외 시점은 그대로 지킵니다.
합성 프로토콜·const를 필요한 곳에만 (이번 갱신)
합성 프로토콜을 필요한 곳에만 둡니다 (
5020ecc)cssprop·값으로 쓰일 때만__devupCss0/__devupForwarded0합성 프로토콜이 필요합니다.css없는 JSX 태그로만 렌더되는 컴포넌트는 이제 이 프로토콜 없이 렌더합니다.빌드가 아는 객체의 멤버를 값으로 씁니다 (
7d0dce8)void 0, 복사해 온 객체가 있으면 그 객체의 멤버[void 0, "", a].filter(Boolean).join(" ")[a].filter(Boolean).join(" "){ ...void 0, ...null, a }{ a }`${""}x${"y"}``xy`다음 경우는 모두 그대로 둡니다. 동작이 바뀔 수 있기 때문입니다.
delete·메서드 호출·값 전달을 받는 경우__proto__·getter·Object.prototype상속 멤버인 경우undefined가 매개변수로 가려진 경우값이 바뀔 수 없는 한 번 읽는 const는 읽는 자리로 옮깁니다 (
5e19491)선언 순서와 읽는 순서가 달라서 남아 있던 const입니다. 그 사이에 어떤 코드가 실행되더라도 값이 같은 경우에만 옮깁니다.
let/const, 또는 컴포넌트를 만드는 문장보다 앞선 최상위 선언이어야 합니다. 그래서 TDZ 시점도 바뀌지 않습니다.rest.bg)의 멤버||·?:·===·!다음 경우는 그대로 둡니다.
vardelete·메서드 호출·값 전달이 있는 경우생성 코드 전체는 187,580 → 186,677 바이트이고, 남은 생성 const는 123 → 107개입니다.
아무도 필요로 하지 않는 const와 문장을 지웁니다 (
76c15eb)true,0,null,void 0) const는 읽는 자리마다 값을 씁니다. 그래서__devupRecord13 ? a : b같은 조건이 접힙니다.rest;,"a";)을 지웁니다.a && b;에서b가 아무 일도 하지 않으면a;로 줄입니다.남은 const 104개를 하나씩 확인했고, 모두 필요한 것입니다.
__devupRecord4 && __devupRecord4[3],(__devupRecord6) => {...}const __devupBranch0 = a === b ? \${a}px` : void 0`const DevupAs0 = ["div", "section"][b] || "div"func(),obj.color, 선언되지 않은 전역condReferenceError순서가 바뀜. 사이에 있는${className}이toString을 부를 수 있음생성 코드 전체는 186,677 → 186,246 바이트이고, 생성 const는 107 → 104개입니다.
새 규칙은 각각 단위 테스트로 적용되는 경우와 그대로 두는 경우를 모두 확인합니다. 스냅샷 전체 생성 코드는 211,356 → 186,246 바이트입니다.
로컬 메모리가 부족해서 pre-commit 훅(tarpaulin 포함) 대신 CI로 검증합니다. 로컬에서는 extractor 전체 테스트, clippy
-D warnings, fmt가 통과했습니다. llvm-cov 기준으로 이번에 추가한 코드는 커버리지 100%입니다.한 번만 읽히는 const 제거 (이전 갱신)
rest.ref = refref와.attrs상수는 JSX 속성const __devupX = v; const p = __devupX;const p = v;(익명 함수는 이름이 바뀌므로 제외)b ? <div /> : <div />b, <div />className={__devupBranch0 ? "a" : ""}(DOM 요소)className={__devupBranch0 && "a"}(cond, "a"),a === b)let·const·import·class는 TDZ 순서 때문에 유지같은 키를 여러 번 넣은
.attrs는 처음 들어간 자리에 마지막 값을 써서 props 순서를 지킵니다. 스냅샷 전체 생성 코드는 220,938 → 211,356 바이트입니다.생성 코드 단순화
IIFE를 푼 뒤 도는 peephole 단계(
libs/extractor/src/peephole.rs,peephole_omit.rs)를 추가하고, styled 렌더러가 스스로 쓴 값을 다시 검사하던 코드를 없앴습니다.{...props, ref}를 복사한 뒤 구조 분해props를 구조 분해하고rest.ref = ref["a b", className].filter(Boolean).join(" ")className ? `a b ${className}` : "a b"__devupOmitN)[2] !== void 0,[1] === void 0처럼 빌드가 쓴 레코드를 다시 검사parent ? ["e"] : ["e"],c ? true : false,c ? d : false["e"], 조건만 읽는 형태const __devupRecord2 = __devupRecord1,const __devupRecord8 = rest(0, f)(),"a" || x,x ?? void 0,`${`${x}`}`f(),"a",x,`${x}`<Dyn {...{...r, a: v}} />,style={{...{"--b": a}, ...s}}__devupSpreadN?.x(객체 리터럴을 담은 이름).x각 규칙은 읽는 동작이 바뀌는 경우를 그대로 둡니다.
(0, a.b)(),(0, eval)():this바인딩과 간접eval을 지키려고 남깁니다.x = (0, function(){}): 함수가x라는 이름을 얻지 않도록 남깁니다.typeof, 멤버나 이름 두 개를 읽는 className 결합: 두 번 읽으면 getter나 ReferenceError가 달라져서 남깁니다.key, getter로 정의된ref, 패턴이ref를 직접 꺼내는 구조 분해: 그대로 둡니다.k말고 다른 이름을 읽는 필터,Object를 직접 선언한 모듈: 필터를 공유하지 않습니다.let·var·매개변수를 가리키는 별칭: 그대로 둡니다.알려진 가장자리 경우가 두 가지 있습니다. 누군가
Object.prototype에refsetter를 심어 두었다면rest.ref = …대입이 다르게 동작합니다. 또 대상const가 TDZ일 때 별칭을 선언하면, 원래 그 자리에서 나던 에러가 이름을 처음 읽는 시점으로 옮겨집니다.이전 갱신 이후 포함된 다른 변경
length, 모듈이 상수로 계산할 수 있는 컴포넌트 style prop(color={darken(0.4, PRIMARY)})을 빌드 시점에 계산합니다.module.exports를 다시 할당한 경우 포함).let슬롯과 쉼표 대입 대신 인자와const를 씁니다. 아무도 읽지 않는 generated 선언을 지우고, 한 값만 갖는 분기 패킷을 풉니다.as가 이름 사이에서 고르는 요소는 앞쪽 props를 먼저 평가한 뒤 정적 요소로 나눕니다.{...{ animation: … }})의 style 결과를 정적 클래스로 읽습니다.이전 본문의 정적 스타일 선택(
{lg: …, md: …}[size] || ""), 평가 순서·단일 평가 보존, styled 렌더 중간 함수 병합 내용은 그대로 유지됩니다.검증
검증 커밋:
c5a43bc3731461ca7358de6f7df3a7e0b77428a8검증 소스 트리:
7850779312ec01198651f1b461c2ead98c385e30cargo test --workspace)peephole.rs·peephole_omit.rs라인 커버리지 100%-D warnings, 모든 target·feature)·fmt·ESLint스냅샷은 바뀐 diff를 패턴별로 묶어 하나씩 확인했습니다. 런타임 실행 테스트(
ref·children·theme·$prop 전달, getter,this,eval, 함수 이름 추론)도 모두 통과합니다.CI
publish를 포함한 모든 검증이 통과합니다. Rust 커버리지는 100.00%(27,171/27,171줄)입니다.CI는 tarpaulin을 돌리기 전에 소스를
max_width = 100000으로 다시 포맷합니다. 그래서 CI가 보고하는 줄 번호는 체크인된 소스와 다릅니다. 같은 설정으로 포맷한 사본에서 확인해 보니, 보고된 줄은 모두 실제로 실행되지 않은 분기였습니다. 다음과 같이 정리했습니다(ccc0ea5).else { return }: 앞에서 이미 확인한 조건이라 실행될 수 없는 분기입니다. 헬퍼 함수와 let 체인으로 합쳤습니다. 헬퍼의 실패 경로는 다른 곳에서 실제로 실행됩니다.unwrap_iife,styled_inline_simplify,peephole_known,peephole_omit,build_time_values,visit.rsdefined(),omission의 거부 조건,handed, CommonJSmodule.exports,without_props이 과정에서 드러난 문제도 함께 고쳤습니다.
JSX 안의 시퀀스 (
fdc7ae9)(test, value)로 접힙니다. 이 식이 JSX 속성 안에서 괄호 없이 출력되어 파싱되지 않았고, 그래서 Landing 빌드가 실패했습니다.[test, value][1]로 씁니다.출력 형태 변경에 맞춘 JS 테스트 (
b16528a,fcd1181)attrs값이 JSX 속성role="note"로 바뀐 것에 맞췄습니다.className ? `… ${className}` : "…"형태로 바뀐 것에 맞췄습니다.🤖 Generated with Claude Code