domflax 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +47 -29
  2. package/dist/{chunk-EVENAJYI.js → chunk-EYQXQQQH.js} +3 -3
  3. package/dist/{chunk-3Z5ZWLXX.js → chunk-FPT4EJ6Q.js} +805 -1612
  4. package/dist/chunk-FPT4EJ6Q.js.map +1 -0
  5. package/dist/{chunk-5FWENSD2.js → chunk-JBM3MJRM.js} +149 -10
  6. package/dist/chunk-JBM3MJRM.js.map +1 -0
  7. package/dist/{chunk-H5KTGI3A.js → chunk-TTJEXWAC.js} +172 -5
  8. package/dist/chunk-TTJEXWAC.js.map +1 -0
  9. package/dist/cli.cjs +1032 -1640
  10. package/dist/cli.cjs.map +1 -1
  11. package/dist/cli.js +30 -10
  12. package/dist/cli.js.map +1 -1
  13. package/dist/index.cjs +1116 -1627
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +226 -485
  16. package/dist/index.d.ts +226 -485
  17. package/dist/index.js +16 -36
  18. package/dist/{pattern-CP9_HpVK.d.cts → pattern-DotR_dHs.d.cts} +1 -1
  19. package/dist/pattern-kit.cjs +60 -1
  20. package/dist/pattern-kit.cjs.map +1 -1
  21. package/dist/pattern-kit.d.cts +2 -2
  22. package/dist/pattern-kit.d.ts +2 -2
  23. package/dist/pattern-kit.js +1 -1
  24. package/dist/{pattern-CYgsv-jO.d.ts → pattern-urm5uuwj.d.ts} +1 -1
  25. package/dist/{resolve-ops-Ci7LgYHC.d.ts → resolve-ops-D8aQina5.d.cts} +11 -0
  26. package/dist/{resolve-ops-Ci7LgYHC.d.cts → resolve-ops-D8aQina5.d.ts} +11 -0
  27. package/dist/verify.d.cts +1 -1
  28. package/dist/verify.d.ts +1 -1
  29. package/dist/webpack-loader.cjs +1014 -1578
  30. package/dist/webpack-loader.cjs.map +1 -1
  31. package/dist/webpack-loader.d.cts +8 -2
  32. package/dist/webpack-loader.d.ts +8 -2
  33. package/dist/webpack-loader.js +7 -4
  34. package/dist/webpack-loader.js.map +1 -1
  35. package/dist/worker.cjs +983 -1601
  36. package/dist/worker.cjs.map +1 -1
  37. package/dist/worker.js +3 -3
  38. package/package.json +1 -1
  39. package/dist/chunk-3Z5ZWLXX.js.map +0 -1
  40. package/dist/chunk-5FWENSD2.js.map +0 -1
  41. package/dist/chunk-H5KTGI3A.js.map +0 -1
  42. /package/dist/{chunk-EVENAJYI.js.map → chunk-EYQXQQQH.js.map} +0 -0
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  `domflax` analyzes your JSX **and HTML** at build time and rewrites it to a smaller equivalent:
6
6
 
7
- 1. **Compress** — collapses verbose class sets into their shortest equivalents (`px-4 py-4 mt-2 mb-2` → `p-4 my-2`, `h-10 w-10` → `size-10`).
7
+ 1. **Compress** — a general engine rewrites each element's classes to the **shortest set that produces the same computed style** (`px-4 py-4 mt-2 mb-2` → `p-4 my-2`, `h-10 w-10` → `size-10`). One algorithm across **Tailwind v3, Tailwind v4, and custom CSS** — no per-utility patterns.
8
8
  2. **Flatten** — removes wrapper elements that are *provably inert* (they add no layout and paint nothing).
9
9
 
10
10
  Matching happens on **computed styles**, not raw class names — so the rules work across Tailwind, custom CSS, and (later) other providers, and a Tailwind class and an equivalent custom class compress the same way.
@@ -27,7 +27,7 @@ It rewrites only the **static shape** of your markup. Dynamic class lists (`clas
27
27
  - **Flattening is conservative.** A wrapper is removed only when removal is *provably* render-neutral — it establishes no layout context and has no style to reproduce on its child. A `flex`/`grid` **centering** wrapper is removed only when its parent is statically `display:grid` (so `place-self:center` is provably equivalent — Chromium-verified); a flex/block/unknown parent leaves it preserved. It never drops a style it can't reproduce, and never touches a wrapper a CSS selector depends on (`.list > .item h3`).
28
28
  - domflax runs as a **purely static** source transform. It never launches a browser, so builds stay fast and deterministic.
29
29
 
30
- > **Status: v0.1.4.** Optimizes real `.jsx`/`.tsx` **and `.html`** — component-return position, inside `.map()`/expressions, and whole static-HTML sites — via Vite, Next.js (webpack), and the CLI, with Tailwind and custom-CSS providers. 22 patterns. Compression works everywhere (incl. dynamic content); inert-wrapper flatten removes nodes; centering wrappers flatten where it's *provably* render-identical (a `grid` parent). The CLI batches large sites across CPU cores with a **memory-bounded worker pool** (`--max-memory`, never OOM), and **auto-detects each HTML page's own `<link>` stylesheets**. Still **static-only never launches a browser** during a build. APIs may change before 1.0.
30
+ > **Status: v0.2.0.** Optimizes real `.jsx`/`.tsx` **and `.html`** — component-return, inside `.map()`/expressions, and whole static-HTML sites — via Vite, Next.js (webpack), and the CLI. **Compression is one general engine** that emits the shortest class set reproducing each element's computed style, uniformly across **Tailwind v3, Tailwind v4, and custom CSS** (it re-resolves the result and verifies it's identical before emitting). **Flattening** is a lean set of provably-safe structural patterns (inert wrappers + grid-parent centering). **Static-only never launches a browser** during a build; a Tailwind project it can't resolve is left untouched, never broken. The CLI batches large sites across CPU cores (`--max-memory`, never OOM) and auto-detects each HTML page's own `<link>` stylesheets; the Vite/Next plugins print a build-end optimization summary. APIs may change before 1.0.
31
31
 
32
32
  ## Install
33
33
 
@@ -67,9 +67,21 @@ module.exports = {
67
67
 
68
68
  > domflax runs as a **source transform** on your `.jsx`/`.tsx` files via the bundler — it never touches a framework's shipped `index.html`. Use `next build` (webpack); **Turbopack is not supported yet** (it doesn't accept arbitrary webpack loaders).
69
69
 
70
+ At the end of the build both plugins print a one-box summary of what domflax did:
71
+
72
+ ```
73
+ ▲ domflax
74
+ ────────────────────────────────
75
+ files optimized 42
76
+ DOM nodes removed 318
77
+ classes compressed 1,204
78
+ size saved 18.7 KB
79
+ ────────────────────────────────
80
+ ```
81
+
70
82
  ### Tailwind (auto-detected)
71
83
 
72
- When `tailwindcss` is present, `provider: 'auto'` resolves classes through the real Tailwind engine and emits the shortest equivalent Tailwind classes back. `tailwindcss` is an optional peer, loaded from your project only when used.
84
+ When `tailwindcss` is present, `provider: 'auto'` resolves classes through your project's real Tailwind engine — **Tailwind v3 and v4 are both supported** — and emits the shortest equivalent classes back. `tailwindcss` is an optional peer, loaded from your project only when used. A Tailwind version domflax can't resolve is left untouched (never broken).
73
85
 
74
86
  ### Custom CSS files
75
87
 
@@ -100,6 +112,7 @@ npx domflax ./src --out ./domflax-out
100
112
  | `--max-memory <MB>` | Cap total RAM — and thus worker parallelism. Default ≈ 70% of free RAM; low values run slower but never OOM. |
101
113
  | `--concurrency <N>` | Cap worker count (memory always wins). |
102
114
  | `--dry-run` | Preview changes, write nothing. |
115
+ | `--details` | Print per-file optimization stats (nodes / classes / bytes). |
103
116
  | `--dangerously-overwrite-source` | Allow in-place source rewrite (needs clean git). |
104
117
 
105
118
  ### HTML & static sites
@@ -117,26 +130,33 @@ npx domflax ./dist --provider custom --out ./dist-optimized
117
130
 
118
131
  ## Writing a pattern
119
132
 
120
- Patterns are how domflax knows what's safe to rewrite. Each is a **single declarative file** the definition and its tests live in one `definePattern` call, with no separate test file and no manual registration:
133
+ Compression is a general engine — there are **no per-utility compress patterns**. Patterns are for **flattening**: each is a single declarative file whose definition and tests live in one `definePattern` call, auto-discovered, with no manual registration:
121
134
 
122
135
  ```ts
123
- import { definePattern } from 'domflax/pattern-kit'
136
+ import { definePattern, not, hasDynamicClasses } from 'domflax/pattern-kit'
124
137
 
125
138
  export default definePattern({
126
- name: 'padding-shorthand',
127
- category: 'compress/padding-shorthand',
128
- safety: 1,
129
- doc: { summary: 'Equal/paired padding longhands collapse to the shortest shorthand.' },
130
- // a compress recipe rewrites only the element's own class list (declines with null otherwise)
131
- rewrite: { rewriteClasses: (computed) => foldPadding(computed) },
139
+ name: 'display-contents-wrapper',
140
+ category: 'flatten/wrapper/display-contents-wrapper',
141
+ safety: 2,
142
+ doc: { summary: 'A display:contents wrapper generates no box unwrap it into its sole child.' },
143
+ match: {
144
+ tag: 'div',
145
+ style: { display: 'contents' },
146
+ onlyChild: 'element',
147
+ paintsNothing: true,
148
+ where: [not(hasDynamicClasses)],
149
+ },
150
+ rewrite: { flattenInto: 'child' },
132
151
  test: {
133
- cases: [{ before: '<div className="px-4 py-4">{x}</div>', after: '<div className="p-4">{x}</div>' }],
134
- noMatch: ['<div className="pt-2 pr-4 pb-8 pl-4">box</div>'],
152
+ cases: [{ before: '<div className="contents"><a className="text-blue-500">L</a></div>',
153
+ after: '<a className="text-blue-500">L</a>' }],
154
+ noMatch: ['<div className="contents" ref={r}><a>L</a></div>'],
135
155
  },
136
156
  })
137
157
  ```
138
158
 
139
- Drop the file under `src/library/**` as `*.pattern.ts` and it's **auto-discovered**. The generic harness runs every pattern's `test` cases through the *real* transform, plus an automatic invariant suite (purity, opacity-barrier safety, id-preservation, fixpoint termination) so a new pattern is wired, tested, and proven sound with zero boilerplate. Flatten patterns auto-receive the opacity + selector-safety guards; compress patterns are gated only on dynamic / selector-bound classes.
159
+ Drop the file under `src/library/**` as `*.pattern.ts` and it's **auto-discovered**. The generic harness runs its `test` cases through the *real* transform, plus an automatic invariant suite (purity, opacity-barrier safety, id-preservation, fixpoint termination). Every `flatten/*` pattern auto-receives the opacity + selector-safety guards, and the conservative safety gate only commits a removal it can *prove* is render-neutral so a pattern can never produce unsafe output.
140
160
 
141
161
  ## Advanced entry points
142
162
 
@@ -153,21 +173,19 @@ Runnable examples live in [`examples/`](./examples): `vite-react-tailwind`, `vit
153
173
 
154
174
  ## Roadmap
155
175
 
156
- - [x] Monorepo + single bundled package
157
- - [x] Core engine (IR, pass manager, surgical full-module codegen)
158
- - [x] Declarative `definePattern({ …, test })` + auto-discovery; 22 flatten/compress patterns
159
- - [x] Real Tailwind engine + custom-CSS resolvers
160
- - [x] CSS selector-safety + residual-skip (don't break `div div h1`; never drop un-reproducible styles)
161
- - [x] Compression across dynamic content (refs / handlers / `{expr}` children)
162
- - [x] Optimize JSX inside `.map()` / expressions (list rows)
163
- - [x] Vite + Next.js (webpack) adapters + CLI (folders, wizard, output-safety)
164
- - [x] Standalone equivalence verifier (Playwright, opt-in)
165
- - [x] HTML frontend (`.html`/`.htm`, parse5 + surgical span edits) with per-page `<link>` CSS auto-detection
166
- - [x] Context-aware centering-wrapper flatten (provably safe under a `grid` parent, Chromium-verified)
167
- - [x] Memory-bounded parallel CLI batch (`--max-memory` / `--concurrency`, never-OOM)
168
- - [ ] Astro-static frontend; more providers
169
- - [ ] `domflax/runtime` — optimize dynamic HTML strings before `innerHTML`
170
- - [ ] `templatize` (plain-HTML cloneNode)
176
+ Done so far: monorepo + single bundled package · core IR/pass engine with surgical codegen · declarative `definePattern` + auto-discovery · the general compress **engine** (Tailwind v3 + v4 + custom CSS) · Tailwind v4 support + fail-safe · selector-safety & residual-skip · compression across dynamic content and inside `.map()` rows · Vite + Next.js adapters with a build-end summary · HTML frontend with per-page `<link>` CSS auto-detection · grid-parent centering flatten (Chromium-verified) · memory-bounded parallel CLI.
177
+
178
+ Where it's going each release adds the engine capability that unlocks its next validated pattern batch (full details in [`docs/ROADMAP.md`](./docs/ROADMAP.md)):
179
+
180
+ | Version | Theme | Patterns |
181
+ | --- | --- | --- |
182
+ | **0.3.0** | **Reach** `cn()`/template-literal static extraction, arbitrary-value + variant-aware compression, deeper static layout reasoning (margin-collapse, item sizing) | ~25 |
183
+ | **0.4.0** | **Verified tier** — opt-in render-verified flattening for static HTML (real pages, pixel-identical or rejected); unlocks animation-wrapper class-transfer, multi-child unwraps, merges | ~60 |
184
+ | **0.5.0** | **More frontends** — Astro static + Vue SFC frontends, Turbopack, Bootstrap/other providers for the compress engine | ~100 |
185
+ | **0.6.0** | **Perf & ecosystem** incremental/watch caching, `domflax/runtime`, `templatize` (cloneNode), community `domflax-pattern-*` packages | ~140 |
186
+ | **1.0.0** | **Stable** frozen API, semver guarantees, docs site, published benchmarks | **200+** |
187
+
188
+ Every pattern that ships must *uniquely fire on real code* and be proven render-neutral (statically or via the verified tier) — the count grows from new capability surface, never from padding.
171
189
 
172
190
  ## License
173
191
 
@@ -6,14 +6,14 @@ import {
6
6
  createJsxBackend,
7
7
  createJsxFrontend,
8
8
  createTailwindResolver
9
- } from "./chunk-3Z5ZWLXX.js";
9
+ } from "./chunk-FPT4EJ6Q.js";
10
10
  import {
11
11
  buildSelectorIndex,
12
12
  createSyntheticSink,
13
13
  normalizer,
14
14
  runPasses,
15
15
  syncClassesFromComputed
16
- } from "./chunk-H5KTGI3A.js";
16
+ } from "./chunk-TTJEXWAC.js";
17
17
  import {
18
18
  init_esm_shims
19
19
  } from "./chunk-U5GOONKV.js";
@@ -333,4 +333,4 @@ export {
333
333
  destinationFor,
334
334
  createTransform
335
335
  };
336
- //# sourceMappingURL=chunk-EVENAJYI.js.map
336
+ //# sourceMappingURL=chunk-EYQXQQQH.js.map