domflax 0.1.0 → 0.1.1
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.
- package/README.md +159 -0
- package/dist/{chunk-4HHISSMR.js → chunk-DNHOGPYV.js} +2675 -1503
- package/dist/chunk-DNHOGPYV.js.map +1 -0
- package/dist/{chunk-ZJ2S36GY.js → chunk-DOQEBGWB.js} +33 -20
- package/dist/chunk-DOQEBGWB.js.map +1 -0
- package/dist/{chunk-77SLHRN6.js → chunk-DWLB7FRR.js} +341 -176
- package/dist/chunk-DWLB7FRR.js.map +1 -0
- package/dist/cli.cjs +2169 -760
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +183 -91
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3021 -1699
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +477 -54
- package/dist/index.d.ts +477 -54
- package/dist/index.js +49 -3
- package/dist/pattern-CV607P87.d.ts +547 -0
- package/dist/pattern-F5xBtIE-.d.cts +547 -0
- package/dist/pattern-kit.cjs +60 -39
- package/dist/pattern-kit.cjs.map +1 -1
- package/dist/pattern-kit.d.cts +3 -18
- package/dist/pattern-kit.d.ts +3 -18
- package/dist/pattern-kit.js +3 -1
- package/dist/pattern-kit.js.map +1 -1
- package/dist/{types-BQ7l6dVe.d.ts → resolve-ops-DIwEelH-.d.cts} +26 -251
- package/dist/{types-BQ7l6dVe.d.cts → resolve-ops-DIwEelH-.d.ts} +26 -251
- package/dist/verify.d.cts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/webpack-loader.cjs +2975 -1699
- package/dist/webpack-loader.cjs.map +1 -1
- package/dist/webpack-loader.d.cts +2 -2
- package/dist/webpack-loader.d.ts +2 -2
- package/dist/webpack-loader.js +3 -3
- package/package.json +3 -6
- package/dist/chunk-4HHISSMR.js.map +0 -1
- package/dist/chunk-77SLHRN6.js.map +0 -1
- package/dist/chunk-ZJ2S36GY.js.map +0 -1
- package/dist/pattern-CX6iBzTD.d.ts +0 -237
- package/dist/pattern-P4FIKAUB.d.cts +0 -237
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# domflax
|
|
2
|
+
|
|
3
|
+
> Compile-time DOM flattener and semantic CSS compressor — fewer DOM nodes, smaller class sets, **identical rendered UI**.
|
|
4
|
+
|
|
5
|
+
`domflax` analyzes your JSX at build time and rewrites it to a smaller equivalent:
|
|
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`).
|
|
8
|
+
2. **Flatten** — removes wrapper elements that are *provably inert* (they add no layout and paint nothing).
|
|
9
|
+
|
|
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.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
// before
|
|
14
|
+
<div className="contents">
|
|
15
|
+
<div className="px-4 py-4 mt-2 mb-2" onClick={save}>{title}</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
// after — the inert wrapper is gone, classes are minimized, behavior is identical
|
|
19
|
+
<div className="p-4 my-2" onClick={save}>{title}</div>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
It rewrites only the **static shape** of your markup. Dynamic class lists (`className={cn(...)}`), components, and `dangerouslySetInnerHTML` are opaque and preserved; `async`/data-fetching code is untouched.
|
|
23
|
+
|
|
24
|
+
**Safety model — conservative by default, no browser involved.**
|
|
25
|
+
|
|
26
|
+
- **Compression is always safe.** It only re-serializes an element's *own* class list, so a `ref`, an event handler, a `{dynamic}` child, or `dangerouslySetInnerHTML` never blocks it — only a *dynamic* className (or a class a CSS selector depends on) is left alone.
|
|
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. It never drops a style it can't reproduce, and never touches a wrapper a CSS selector depends on (`.list > .item h3`).
|
|
28
|
+
- domflax runs as a **purely static** source transform. It never launches a browser, so builds stay fast and deterministic.
|
|
29
|
+
|
|
30
|
+
> **Status: v0.1.1.** Works end-to-end on real `.jsx`/`.tsx` — in component-return position **and inside `.map()` / expressions (list rows)** — via Vite, Next.js (webpack), and the CLI, with Tailwind and custom-CSS providers. 22 patterns. Wrappers that establish a layout context (e.g. `flex`/`grid` centering) are **conservatively preserved** — proving those render-identical needs context a static pass can't see; recovering them safely is on the Roadmap. APIs may change before 1.0.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -D domflax
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
One install, one package. `pattern-kit` and `verify` are subpaths of `domflax` — there are no separate packages to add.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### Vite
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// vite.config.ts
|
|
46
|
+
import { defineConfig } from 'vite'
|
|
47
|
+
import domflax from 'domflax'
|
|
48
|
+
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
plugins: [domflax.vite({ provider: 'auto' })],
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Next.js (webpack)
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// next.config.js
|
|
58
|
+
const domflax = require('domflax')
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
webpack(config) {
|
|
62
|
+
domflax.webpack({ provider: 'tailwind' }).apply(config)
|
|
63
|
+
return config
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
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
|
+
|
|
70
|
+
### Tailwind (auto-detected)
|
|
71
|
+
|
|
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.
|
|
73
|
+
|
|
74
|
+
### Custom CSS files
|
|
75
|
+
|
|
76
|
+
No Tailwind? Point domflax at your stylesheets; it parses them (PostCSS) for forward (class → style) and reverse (style → class) resolution, and reads their selectors for safety.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
domflax.vite({ provider: 'custom', cssFiles: ['./src/styles/main.css'] })
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## CLI
|
|
83
|
+
|
|
84
|
+
domflax also runs standalone — point it at a folder or files. Run it with no arguments for an interactive wizard.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npx domflax # interactive wizard (arrow keys, multiselect)
|
|
88
|
+
npx domflax ./src --dry-run # preview diffs, write nothing
|
|
89
|
+
npx domflax ./src --out ./domflax-out
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Source is never overwritten by default.** Output goes to `--out` (or `./domflax-out`), or in place only inside disposable build dirs (`dist/`, `build/`). Rewriting source in place requires the explicit `--dangerously-overwrite-source` flag *and* a clean git tree. The wizard never runs in CI / non-TTY.
|
|
93
|
+
|
|
94
|
+
| Flag | Description |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `<path>` | Folder (auto-scanned) or glob of files. |
|
|
97
|
+
| `--out <dir>` | Write optimized output here (mirrors input structure). |
|
|
98
|
+
| `--provider <name>` | `auto` (default), `tailwind`, or `custom`. |
|
|
99
|
+
| `--css <files...>` | Stylesheets when `--provider custom`. |
|
|
100
|
+
| `--dry-run` | Preview changes, write nothing. |
|
|
101
|
+
| `--dangerously-overwrite-source` | Allow in-place source rewrite (needs clean git). |
|
|
102
|
+
|
|
103
|
+
## Writing a pattern
|
|
104
|
+
|
|
105
|
+
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:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { definePattern } from 'domflax/pattern-kit'
|
|
109
|
+
|
|
110
|
+
export default definePattern({
|
|
111
|
+
name: 'padding-shorthand',
|
|
112
|
+
category: 'compress/padding-shorthand',
|
|
113
|
+
safety: 1,
|
|
114
|
+
doc: { summary: 'Equal/paired padding longhands collapse to the shortest shorthand.' },
|
|
115
|
+
// a compress recipe rewrites only the element's own class list (declines with null otherwise)
|
|
116
|
+
rewrite: { rewriteClasses: (computed) => foldPadding(computed) },
|
|
117
|
+
test: {
|
|
118
|
+
cases: [{ before: '<div className="px-4 py-4">{x}</div>', after: '<div className="p-4">{x}</div>' }],
|
|
119
|
+
noMatch: ['<div className="pt-2 pr-4 pb-8 pl-4">box</div>'],
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
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.
|
|
125
|
+
|
|
126
|
+
## Advanced entry points
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { definePattern } from 'domflax/pattern-kit' // author custom patterns
|
|
130
|
+
import { verifyEquivalence } from 'domflax/verify' // optional, standalone equivalence checker
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The transform itself is static and never launches a browser. `domflax/verify` is a **separate, opt-in tool** that renders before/after in headless Chromium (via Playwright, an optional peer) and diffs pixels + box geometry + computed styles — handy for vetting patterns, *not* part of your build.
|
|
134
|
+
|
|
135
|
+
## Examples
|
|
136
|
+
|
|
137
|
+
Runnable examples live in [`examples/`](./examples): `vite-react-tailwind`, `vite-custom-css` (custom provider + selector-safety), and `next-tailwind`.
|
|
138
|
+
|
|
139
|
+
## Roadmap
|
|
140
|
+
|
|
141
|
+
- [x] Monorepo + single bundled package
|
|
142
|
+
- [x] Core engine (IR, pass manager, surgical full-module codegen)
|
|
143
|
+
- [x] Declarative `definePattern({ …, test })` + auto-discovery; 22 flatten/compress patterns
|
|
144
|
+
- [x] Real Tailwind engine + custom-CSS resolvers
|
|
145
|
+
- [x] CSS selector-safety + residual-skip (don't break `div div h1`; never drop un-reproducible styles)
|
|
146
|
+
- [x] Compression across dynamic content (refs / handlers / `{expr}` children)
|
|
147
|
+
- [x] Optimize JSX inside `.map()` / expressions (list rows)
|
|
148
|
+
- [x] Vite + Next.js (webpack) adapters + CLI (folders, wizard, output-safety)
|
|
149
|
+
- [x] Standalone equivalence verifier (Playwright, opt-in)
|
|
150
|
+
- [ ] Context-aware (or opt-in-verified) flatten for `flex`/`grid` centering wrappers
|
|
151
|
+
- [ ] HTML frontend (plain `.html` / Astro static)
|
|
152
|
+
- [ ] `domflax/runtime` — optimize dynamic HTML strings before `innerHTML`
|
|
153
|
+
- [ ] More providers; `templatize` (plain-HTML cloneNode)
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
See [LICENSE](./LICENSE) (Domflax Software License 1.0). The `domflax/runtime`, `domflax/cli`, and pattern-library components are additionally available under the MIT License per the Runtime Exception.
|
|
158
|
+
|
|
159
|
+
© Krishnesh Mishra
|