@pathscale/rsbuild-plugin-ui-css-purge 0.9.2 → 0.9.3
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 +80 -70
- package/dist/generate-manifest.d.ts +4 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1425 -73
- package/dist/postbuild-purge.d.ts +51 -39
- package/dist/postbuild-purge.js +433 -319
- package/dist/scan-consumer.d.ts +38 -11
- package/package.json +31 -32
- package/src/generate-manifest.ts +428 -183
package/README.md
CHANGED
|
@@ -1,131 +1,141 @@
|
|
|
1
|
-
# @pathscale/
|
|
1
|
+
# @pathscale/rsbuild-plugin-ui-css-purge
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Database-first CSS purge for `@pathscale/ui` consumers. It scans consumer JSX with SWC, cross-references a versioned component purge database, and removes only selectors whose ownership and unused state are known.
|
|
4
4
|
|
|
5
|
-
Runs as a postbuild step under Bun.
|
|
5
|
+
Runs as a postbuild step under Bun.
|
|
6
6
|
|
|
7
|
-
## How
|
|
7
|
+
## How It Works
|
|
8
8
|
|
|
9
|
-
The purge operates in two phases across two repositories
|
|
9
|
+
The purge operates in two phases across two repositories.
|
|
10
10
|
|
|
11
|
-
**
|
|
11
|
+
**Lib side (`@pathscale/ui`):** Components ship `*.classes.ts` files that describe component-owned classes by slot and prop. The manifest generator also scans colocated component CSS for selectors, data/aria attributes, CSS variable references, keyframes, and component dependencies. It writes a versioned `purge-manifest.json` database:
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```ts
|
|
14
|
+
interface PurgeDatabaseV2 {
|
|
15
|
+
version: 2;
|
|
16
|
+
components: Record<string, ComponentPurgeRecord>;
|
|
17
|
+
shared: {
|
|
18
|
+
selectors: { selector: string; components: string[] }[];
|
|
19
|
+
cssVars: Record<string, { declaredBy: string[]; referencedBy: string[] }>;
|
|
20
|
+
keyframes: Record<string, { declaredBy: string[]; referencedBy: string[] }>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
```
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|-------|-------------|--------|
|
|
17
|
-
| L1 | Removes entire CSS rules whose class selectors aren't in the safelist | [purgecss](https://purgecss.com/) |
|
|
18
|
-
| L2 | Within kept rules, strips `[data-*]` / `[aria-*]` attribute selectors not in the attr safelist | [postcss](https://postcss.org/) AST walk |
|
|
19
|
-
| L3 | Iteratively removes CSS custom properties that are declared but never referenced | postcss AST walk |
|
|
25
|
+
**Consumer side:** After `rsbuild build`, the CLI scans source files for canonical `@pathscale/ui` usage, including aliases, deep imports, namespace imports, compound JSX such as `Modal.Root`, re-exports, spreads, and dynamic props. It builds usage facts and applies the database conservatively.
|
|
20
26
|
|
|
21
|
-
##
|
|
27
|
+
## Purge Rules
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
| Rule | Behavior |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| Unused known component | Selectors owned only by that component can be removed. |
|
|
32
|
+
| Used component, unused prop variant | Selectors requiring that known unused class can be removed. |
|
|
33
|
+
| Runtime data/aria state | Kept when the owning used component selector is kept. |
|
|
34
|
+
| Unknown ownership | Kept by default. |
|
|
35
|
+
| Theme, reset, app selectors | Kept unless they are fully known unused component selectors. |
|
|
36
|
+
| CSS vars and keyframes | Removed only after selector purge proves they are unreferenced. |
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
440.7 KB raw CSS (before)
|
|
27
|
-
42.3 KB after L1 (class purge)
|
|
28
|
-
42.3 KB after L2 (attr purge)
|
|
29
|
-
27.7 KB after L3 (var cleanup)
|
|
30
|
-
4.4 KB brotli compressed
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
93.7% reduction in raw CSS size.
|
|
38
|
+
The selector walk uses PostCSS. Lightning CSS is used for final minification.
|
|
34
39
|
|
|
35
40
|
## Installation
|
|
36
41
|
|
|
37
42
|
```bash
|
|
38
|
-
bun add -d @pathscale/
|
|
43
|
+
bun add -d @pathscale/rsbuild-plugin-ui-css-purge
|
|
39
44
|
```
|
|
40
45
|
|
|
41
46
|
## Usage
|
|
42
47
|
|
|
43
|
-
### Consumer
|
|
48
|
+
### Consumer Project
|
|
44
49
|
|
|
45
|
-
Add
|
|
50
|
+
Add the postbuild purge after `rsbuild build`:
|
|
46
51
|
|
|
47
52
|
```json
|
|
48
53
|
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "rsbuild build && bunx rsbuild-plugin-ui-css-purge --dist dist --src src --manifest node_modules/@pathscale/ui/dist/purge-manifest.json"
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
```
|
|
54
59
|
|
|
55
60
|
Options:
|
|
56
61
|
|
|
57
62
|
| Flag | Default | Description |
|
|
58
|
-
|
|
59
|
-
| `--manifest` |
|
|
60
|
-
| `--dist` | `./dist` | Directory containing built CSS files |
|
|
61
|
-
| `--src` | `./src` | Consumer source directory to scan for JSX usage |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| `--manifest` | required | Path to `purge-manifest.json`. |
|
|
65
|
+
| `--dist` | `./dist` | Directory containing built CSS files. |
|
|
66
|
+
| `--src` | `./src` | Consumer source directory to scan for JSX usage. |
|
|
62
67
|
|
|
63
|
-
### Lib-
|
|
68
|
+
### Lib-Side Manifest Generation
|
|
64
69
|
|
|
65
|
-
Run from `@pathscale/ui` as a
|
|
70
|
+
Run from `@pathscale/ui` as a build step:
|
|
66
71
|
|
|
67
72
|
```json
|
|
68
73
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
"scripts": {
|
|
75
|
+
"prebuild": "bunx generate-manifest src/components --out dist/purge-manifest.json"
|
|
76
|
+
}
|
|
72
77
|
}
|
|
73
78
|
```
|
|
74
79
|
|
|
75
|
-
This scans all `*.
|
|
80
|
+
This scans all `*.classes.ts` files and colocated component CSS files.
|
|
76
81
|
|
|
77
|
-
## The
|
|
82
|
+
## The `*.classes.ts` Convention
|
|
78
83
|
|
|
79
|
-
Every component
|
|
84
|
+
Every component should export a `CLASSES` object that names all component-owned classes. Tailwind utilities are filtered out so the manifest tracks ownership of durable component selectors, not generic utility classes.
|
|
80
85
|
|
|
81
86
|
```ts
|
|
82
|
-
//
|
|
87
|
+
// button.classes.ts
|
|
83
88
|
export const CLASSES = {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
base: "button inline-flex items-center",
|
|
90
|
+
variant: {
|
|
91
|
+
primary: "button--primary",
|
|
92
|
+
secondary: "button--secondary",
|
|
93
|
+
},
|
|
94
|
+
size: {
|
|
95
|
+
sm: "button--sm",
|
|
96
|
+
md: "button--md",
|
|
97
|
+
},
|
|
98
|
+
flag: {
|
|
99
|
+
disabled: "button--disabled",
|
|
100
|
+
},
|
|
101
|
+
attrs: {
|
|
102
|
+
open: { "data-open": "true" },
|
|
103
|
+
},
|
|
98
104
|
} as const;
|
|
99
105
|
```
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
| Slot | Shape | Purpose |
|
|
104
|
-
|------|-------|---------|
|
|
105
|
-
| `base` | `string \| string[]` | Always rendered when the component mounts |
|
|
106
|
-
| `variant`, `size`, `color` | `{ enumValue: classString }` | Enum prop value maps to classes |
|
|
107
|
-
| `flag` | `{ propName: classString }` | Boolean prop name maps to classes |
|
|
108
|
-
| `attrs` | `{ propName: { attr: value } }` | L2 attribute selectors tied to props |
|
|
107
|
+
Compound components use a nested shape:
|
|
109
108
|
|
|
110
|
-
|
|
109
|
+
```ts
|
|
110
|
+
export const CLASSES = {
|
|
111
|
+
Root: { base: "modal" },
|
|
112
|
+
Panel: { base: "modal__panel" },
|
|
113
|
+
} as const;
|
|
114
|
+
```
|
|
111
115
|
|
|
112
116
|
## Programmatic API
|
|
113
117
|
|
|
114
|
-
The scanner and safelist builder are also exported for custom integrations:
|
|
115
|
-
|
|
116
118
|
```ts
|
|
117
|
-
import {
|
|
119
|
+
import {
|
|
120
|
+
buildSafelists,
|
|
121
|
+
purgeCssWithDatabase,
|
|
122
|
+
scanConsumerSource,
|
|
123
|
+
} from "@pathscale/rsbuild-plugin-ui-css-purge";
|
|
118
124
|
|
|
119
125
|
const usages = await scanConsumerSource("/path/to/consumer/src");
|
|
120
126
|
const manifest = JSON.parse(await Bun.file("purge-manifest.json").text());
|
|
121
|
-
const
|
|
127
|
+
const safelists = buildSafelists(usages, manifest);
|
|
128
|
+
const result = purgeCssWithDatabase(css, manifest, safelists);
|
|
129
|
+
|
|
130
|
+
console.log(result.report);
|
|
122
131
|
```
|
|
123
132
|
|
|
124
133
|
## Development
|
|
125
134
|
|
|
126
135
|
```bash
|
|
127
136
|
bun install
|
|
128
|
-
bun run
|
|
137
|
+
bun run test
|
|
138
|
+
bun run build
|
|
129
139
|
bun run lint
|
|
130
140
|
bun run format
|
|
131
141
|
```
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lib-side database generator.
|
|
2
|
+
* Lib-side purge database generator.
|
|
3
3
|
*
|
|
4
|
-
* Reads all `*.classes.ts` files from @pathscale/ui's component tree
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Reads all `*.classes.ts` files from @pathscale/ui's component tree and
|
|
5
|
+
* produces a versioned `purge-manifest.json` database consumed by the postbuild
|
|
6
|
+
* purge script.
|
|
7
7
|
*
|
|
8
8
|
* Usage: bun run src/generate-manifest.ts <path-to-ui-src/components>
|
|
9
9
|
* Output: purge-manifest.json in cwd (or pass --out <path>)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
1
|
+
export { cleanUnusedVars, cleanUnusedVarsWithReport, normalizePurgeDatabase, purgeCssWithDatabase, } from "./postbuild-purge";
|
|
2
|
+
export { extractUIImports, extractJSXUsages, buildSafelists, scanConsumerSource, } from "./scan-consumer";
|
|
3
|
+
export type { ComponentManifest, ComponentPurgeRecord, PropUsage, PurgeDatabaseV2, PurgeManifest, Safelists, } from "./scan-consumer";
|