Skip to content

Refactor CSS node types with reusable type builders#267

Open
bartveneman wants to merge 2 commits into
mainfrom
claude/package-size-reduction-97fdn8
Open

Refactor CSS node types with reusable type builders#267
bartveneman wants to merge 2 commits into
mainfrom
claude/package-size-reduction-97fdn8

Conversation

@bartveneman

Copy link
Copy Markdown
Member

Summary

This refactor introduces reusable type builders (Toggle, WithClone, and Leaf) to reduce duplication across ~40 CSS node type definitions. These are pure type-level utilities that compile away entirely, improving maintainability without runtime cost.

Key Changes

  • Added Toggle<HasKey, ValueKey, T> type: A discriminated union pattern for optional fields gated by boolean flags (e.g., has_prelude / prelude). Replaces repeated inline union patterns throughout the file.

  • Added WithClone<T> type: Wraps a node type to add a clone() method with proper return type inference (ToPlain<T>). Eliminates repetitive clone method declarations.

  • Added Leaf<Type, Name, Extra> type: Combines CSSNode, type tag, and optional extra fields for leaf nodes (nodes without children). Significantly reduces boilerplate for simple node types.

  • Refactored all node type definitions to use these builders:

    • Structural nodes: StyleSheet, Rule, Atrule, Declaration, Selector, SelectorList, Block, Comment, Raw
    • Value nodes: Identifier, Number, Dimension, String, Hash, Function, Operator, Parenthesis, Url, UnicodeRange, Value
    • Selector nodes: TypeSelector, ClassSelector, IdSelector, AttributeSelector, PseudoClassSelector, PseudoElementSelector, Combinator, UniversalSelector, NestingSelector, NthSelector, NthOfSelector, LangSelector
    • At-rule prelude nodes: AtrulePrelude, MediaQuery, MediaFeature, MediaType, ContainerQuery, SupportsQuery, SupportsDeclaration, LayerName, PreludeSelectorList, PreludeOperator, FeatureRange

Implementation Details

  • The Toggle type replaces verbose union patterns like ({ readonly has_X: true } & { readonly X: T }) | ({ readonly has_X: false } & { readonly X: null }) with a single, reusable construct.
  • BlockChild now uses Toggle for its has_next / next_sibling pattern.
  • All type definitions maintain identical semantics and exported interfaces—this is purely a refactoring for code clarity and maintainability.
  • The builders are documented with JSDoc comments explaining their purpose and usage.

https://claude.ai/code/session_01FuvCERnifYS6ggHAXMwVkD

Factor the ~44 near-identical node-type declarations (CSSNode & {
type, type_name, clone(): ToPlain<Self> }) into two small internal
generics, Leaf<> and WithClone<>, instead of hand-rolling each one.
Purely a type-level change (zero runtime/JS output difference,
zero public API change) that shrinks the largest file in the
published tarball (node-types.d.ts: 25.17kB -> 22.18kB), cutting
overall unpacked install size by ~1.5%.
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Duplicate Dependencies (found: 35, threshold: 1)

📦 Package 📋 Versions
tinybench
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • vitest@4.1.9
        • tinybench@2.9.0

  • root@
    • tinybench@6.0.2

@babel/helper-validator-identifier
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • magicast@0.5.2
        • @babel/parser@7.29.3
          • @babel/types@7.29.0
            • @babel/helper-validator-identifier@7.28.5

  • root@
    • tsdown@0.22.3
      • ...
        • @babel/parser@8.0.0
          • @babel/types@8.0.0
            • @babel/helper-validator-identifier@8.0.2

js-tokens
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ast-v8-to-istanbul@1.0.0
        • js-tokens@10.0.0

  • root@
    • tailwindcss@2.2.19
      • cosmiconfig@7.1.0
        • parse-json@5.2.0
          • @babel/code-frame@7.27.1
            • js-tokens@4.0.0

@babel/parser
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • magicast@0.5.2
        • @babel/parser@7.29.3

  • root@
    • tsdown@0.22.3
      • rolldown-plugin-dts@0.26.0
        • @babel/generator@8.0.0
          • @babel/parser@8.0.0

@babel/types
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • magicast@0.5.2
        • @babel/parser@7.29.3
          • @babel/types@7.29.0

  • root@
    • tsdown@0.22.3
      • rolldown-plugin-dts@0.26.0
        • @babel/generator@8.0.0
          • @babel/parser@8.0.0
            • @babel/types@8.0.0

@babel/helper-string-parser
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • magicast@0.5.2
        • @babel/parser@7.29.3
          • @babel/types@7.29.0
            • @babel/helper-string-parser@7.27.1

  • root@
    • tsdown@0.22.3
      • ...
        • @babel/parser@8.0.0
          • @babel/types@8.0.0
            • @babel/helper-string-parser@8.0.0

@emnapi/core
3 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • rolldown@1.0.3
          • @rolldown/binding-wasm32-wasi@1.0.3
            • @emnapi/core@1.10.0

  • root@
    • knip@6.18.0
      • oxc-resolver@11.21.3
        • @oxc-resolver/binding-wasm32-wasi@11.21.3
          • @emnapi/core@1.11.0

  • root@
    • knip@6.18.0
      • oxc-parser@0.137.0
        • @oxc-parser/binding-wasm32-wasi@0.137.0
          • @emnapi/core@1.11.1

@emnapi/wasi-threads
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • @rolldown/binding-wasm32-wasi@1.0.3
          • @emnapi/core@1.10.0
            • @emnapi/wasi-threads@1.2.1

  • root@
    • knip@6.18.0
      • oxc-parser@0.137.0
        • @oxc-parser/binding-wasm32-wasi@0.137.0
          • @emnapi/core@1.11.1
            • @emnapi/wasi-threads@1.2.2

@emnapi/runtime
3 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • rolldown@1.0.3
          • @rolldown/binding-wasm32-wasi@1.0.3
            • @emnapi/runtime@1.10.0

  • root@
    • knip@6.18.0
      • oxc-resolver@11.21.3
        • @oxc-resolver/binding-wasm32-wasi@11.21.3
          • @emnapi/runtime@1.11.0

  • root@
    • knip@6.18.0
      • oxc-parser@0.137.0
        • @oxc-parser/binding-wasm32-wasi@0.137.0
          • @emnapi/runtime@1.11.1

@rolldown/binding-wasm32-wasi
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-wasm32-wasi@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-wasm32-wasi@1.1.2

obug
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • obug@2.1.1

  • root@
    • tsdown@0.22.3
      • obug@2.1.3

picomatch
2 versions
  • root@
    • tailwindcss@2.2.19
      • chokidar@3.6.0
        • anymatch@3.1.3
          • picomatch@2.3.2

  • root@
    • @vitest/coverage-v8@4.1.9
      • vitest@4.1.9
        • @vitest/mocker@4.1.9
          • vite@8.0.16
            • picomatch@4.0.4

postcss-value-parser
2 versions
  • root@
    • tailwindcss@2.2.19
      • reduce-css-calc@2.1.8
        • postcss-value-parser@3.3.1

  • root@
    • tailwindcss@2.2.19
      • autoprefixer@10.4.23
        • postcss-value-parser@4.2.0

glob-parent
2 versions
  • root@
    • tailwindcss@2.2.19
      • chokidar@3.6.0
        • glob-parent@5.1.2

  • root@
    • tailwindcss@2.2.19
      • glob-parent@6.0.2

yaml
2 versions
  • root@
    • tailwindcss@2.2.19
      • cosmiconfig@7.1.0
        • yaml@1.10.3

  • root@
    • @vitest/coverage-v8@4.1.9
      • vitest@4.1.9
        • @vitest/mocker@4.1.9
          • vite@8.0.16
            • yaml@2.9.0

is-arrayish
2 versions
  • root@
    • tailwindcss@2.2.19
      • cosmiconfig@7.1.0
        • parse-json@5.2.0
          • error-ex@1.3.4
            • is-arrayish@0.2.1

  • root@
    • tailwindcss@2.2.19
      • color@4.2.3
        • color-string@1.9.1
          • simple-swizzle@0.2.4
            • is-arrayish@0.3.4

get-tsconfig
2 versions
  • root@
    • knip@6.18.0
      • get-tsconfig@4.14.0

  • root@
    • tsdown@0.22.3
      • rolldown-plugin-dts@0.26.0
        • get-tsconfig@5.0.0-beta.5

semver
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • istanbul-lib-report@3.0.1
        • make-dir@4.0.0
          • semver@7.8.0

  • root@
    • tsdown@0.22.3
      • semver@7.8.5

@oxc-project/types
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @oxc-project/types@0.133.0

  • root@
    • knip@6.18.0
      • oxc-parser@0.137.0
        • @oxc-project/types@0.137.0

rolldown
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • vitest@4.1.9
        • @vitest/mocker@4.1.9
          • vite@8.0.16
            • rolldown@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2

@rolldown/binding-android-arm64
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-android-arm64@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-android-arm64@1.1.2

@rolldown/binding-darwin-arm64
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-darwin-arm64@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-darwin-arm64@1.1.2

@rolldown/binding-darwin-x64
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-darwin-x64@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-darwin-x64@1.1.2

@rolldown/binding-freebsd-x64
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-freebsd-x64@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-freebsd-x64@1.1.2

@rolldown/binding-linux-arm-gnueabihf
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-arm-gnueabihf@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-arm-gnueabihf@1.1.2

@rolldown/binding-linux-arm64-gnu
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-arm64-gnu@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-arm64-gnu@1.1.2

@rolldown/binding-linux-arm64-musl
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-arm64-musl@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-arm64-musl@1.1.2

@rolldown/binding-linux-ppc64-gnu
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-ppc64-gnu@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-ppc64-gnu@1.1.2

@rolldown/binding-linux-s390x-gnu
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-s390x-gnu@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-s390x-gnu@1.1.2

@rolldown/binding-linux-x64-gnu
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-x64-gnu@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-x64-gnu@1.1.2

@rolldown/binding-linux-x64-musl
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-linux-x64-musl@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-linux-x64-musl@1.1.2

@rolldown/binding-openharmony-arm64
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-openharmony-arm64@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-openharmony-arm64@1.1.2

@rolldown/binding-win32-arm64-msvc
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-win32-arm64-msvc@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-win32-arm64-msvc@1.1.2

@rolldown/binding-win32-x64-msvc
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • ...
        • vite@8.0.16
          • rolldown@1.0.3
            • @rolldown/binding-win32-x64-msvc@1.0.3

  • root@
    • tsdown@0.22.3
      • rolldown@1.1.2
        • @rolldown/binding-win32-x64-msvc@1.1.2

tinyexec
2 versions
  • root@
    • @vitest/coverage-v8@4.1.9
      • vitest@4.1.9
        • tinyexec@1.1.2

  • root@
    • tsdown@0.22.3
      • tinyexec@1.2.4

💡 To find out what depends on a specific package, run: pnpm -r why example-package

🎉 Package Size Decrease

📦 Package 📏 Base Size 📏 Source Size 📈 Size Change
@projectwallace/css-parser 41 kB 41 kB -69 B

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FuvCERnifYS6ggHAXMwVkD
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.

2 participants