@fullhuman/purgecss-from-jsx 8.0.0 → 9.0.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.
- package/README.md +6 -4
- package/lib/purgecss-from-jsx.esm.js +36 -8
- package/lib/purgecss-from-jsx.js +36 -8
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npm install @fullhuman/purgecss-from-jsx --save-dev
|
|
|
15
15
|
### With PurgeCSS
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
|
-
import PurgeCSS from "purgecss";
|
|
18
|
+
import { PurgeCSS } from "purgecss";
|
|
19
19
|
import purgeFromJsx from "@fullhuman/purgecss-from-jsx";
|
|
20
20
|
|
|
21
21
|
const result = await new PurgeCSS().purge({
|
|
@@ -85,10 +85,12 @@ A function that takes JSX content as a string and returns an array of selectors.
|
|
|
85
85
|
|
|
86
86
|
The extractor parses JSX and extracts:
|
|
87
87
|
|
|
88
|
-
1. **Component names** - JSX element names (e.g., `div`, `Header`, `MyComponent`)
|
|
89
|
-
2. **className values** - String values from `className` attributes, split by
|
|
88
|
+
1. **Component names** - JSX element names (e.g., `div`, `Header`, `MyComponent`), including elements inside the value of another attribute (e.g., `icon={<Icon />}`)
|
|
89
|
+
2. **className values** - String values from `className` attributes, split by whitespace
|
|
90
90
|
3. **id values** - String values from `id` attributes
|
|
91
91
|
|
|
92
|
+
When `className` or `id` is an expression, every string in it is extracted, as well as the static parts of template literals and the keys of object literals. For example, `className={active ? "on" : "off"}` gives `on` and `off`, ``className={`card ${size}`}`` gives `card`, and `className={clsx({ active: isActive })}` gives `active`.
|
|
93
|
+
|
|
92
94
|
### Example
|
|
93
95
|
|
|
94
96
|
```jsx
|
|
@@ -103,7 +105,7 @@ Extracted selectors: `["Card", "card", "card-primary", "featured-card", "CardHea
|
|
|
103
105
|
## Limitations
|
|
104
106
|
|
|
105
107
|
- Only extracts static string values from `className` and `id`
|
|
106
|
-
-
|
|
108
|
+
- Values computed at runtime, like `className={styles.container}` or the `size` of ``className={`btn-${size}`}``, are not extracted
|
|
107
109
|
- For dynamic class names, consider using PurgeCSS's [safelist](https://purgecss.com/safelisting.html) option
|
|
108
110
|
|
|
109
111
|
## License
|
|
@@ -31,7 +31,7 @@ function purgeFromJsx(options) {
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
JSXAttribute(acornNode, state, callback) {
|
|
34
|
-
var _a;
|
|
34
|
+
var _a, _b;
|
|
35
35
|
const node = acornNode;
|
|
36
36
|
if (!node.value) {
|
|
37
37
|
return;
|
|
@@ -43,15 +43,19 @@ function purgeFromJsx(options) {
|
|
|
43
43
|
case "id":
|
|
44
44
|
case "className":
|
|
45
45
|
{
|
|
46
|
-
// Get
|
|
47
|
-
|
|
46
|
+
// Get all the strings in node.value, e.g. both strings of
|
|
47
|
+
// className={active ? "on" : "off"}
|
|
48
|
+
const valueState = { strings: [] };
|
|
48
49
|
callback(node.value, valueState);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
(_a = state.selectors) === null || _a === void 0 ? void 0 : _a.push(...valueState.text.split(" "));
|
|
50
|
+
for (const text of (_a = valueState.strings) !== null && _a !== void 0 ? _a : []) {
|
|
51
|
+
(_b = state.selectors) === null || _b === void 0 ? void 0 : _b.push(...text.split(/\s+/).filter(Boolean));
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
break;
|
|
55
|
+
default:
|
|
56
|
+
// the value can contain JSX elements, e.g. icon={<Icon />}
|
|
57
|
+
callback(node.value, { selectors: state.selectors });
|
|
58
|
+
break;
|
|
55
59
|
}
|
|
56
60
|
},
|
|
57
61
|
JSXIdentifier(acornNode, state) {
|
|
@@ -62,12 +66,36 @@ function purgeFromJsx(options) {
|
|
|
62
66
|
const node = acornNode;
|
|
63
67
|
state.text = node.namespace.name + ":" + node.name.name;
|
|
64
68
|
},
|
|
65
|
-
// Only
|
|
69
|
+
// Only static strings are collected, not values computed at runtime
|
|
66
70
|
Literal(acornNode, state) {
|
|
71
|
+
var _a;
|
|
67
72
|
const node = acornNode;
|
|
68
73
|
if (typeof node.value === "string") {
|
|
69
|
-
state.
|
|
74
|
+
(_a = state.strings) === null || _a === void 0 ? void 0 : _a.push(node.value);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
// static parts of a template literal, e.g. `btn btn-${size}`
|
|
78
|
+
TemplateElement(acornNode, state) {
|
|
79
|
+
var _a;
|
|
80
|
+
const node = acornNode;
|
|
81
|
+
if (node.value.cooked) {
|
|
82
|
+
(_a = state.strings) === null || _a === void 0 ? void 0 : _a.push(node.value.cooked);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
// keys of the objects in a className or id expression, e.g.
|
|
86
|
+
// clsx({ active: isActive, "is-open": isOpen })
|
|
87
|
+
Property(acornNode, state, callback) {
|
|
88
|
+
const node = acornNode;
|
|
89
|
+
if (state.strings &&
|
|
90
|
+
!node.computed &&
|
|
91
|
+
node.key.type === "Identifier") {
|
|
92
|
+
state.strings.push(node.key.name);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// a string key is a Literal, a computed key any expression
|
|
96
|
+
callback(node.key, state);
|
|
70
97
|
}
|
|
98
|
+
callback(node.value, state);
|
|
71
99
|
},
|
|
72
100
|
}, { ...walk.base });
|
|
73
101
|
return state.selectors || [];
|
package/lib/purgecss-from-jsx.js
CHANGED
|
@@ -53,7 +53,7 @@ function purgeFromJsx(options) {
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
JSXAttribute(acornNode, state, callback) {
|
|
56
|
-
var _a;
|
|
56
|
+
var _a, _b;
|
|
57
57
|
const node = acornNode;
|
|
58
58
|
if (!node.value) {
|
|
59
59
|
return;
|
|
@@ -65,15 +65,19 @@ function purgeFromJsx(options) {
|
|
|
65
65
|
case "id":
|
|
66
66
|
case "className":
|
|
67
67
|
{
|
|
68
|
-
// Get
|
|
69
|
-
|
|
68
|
+
// Get all the strings in node.value, e.g. both strings of
|
|
69
|
+
// className={active ? "on" : "off"}
|
|
70
|
+
const valueState = { strings: [] };
|
|
70
71
|
callback(node.value, valueState);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
(_a = state.selectors) === null || _a === void 0 ? void 0 : _a.push(...valueState.text.split(" "));
|
|
72
|
+
for (const text of (_a = valueState.strings) !== null && _a !== void 0 ? _a : []) {
|
|
73
|
+
(_b = state.selectors) === null || _b === void 0 ? void 0 : _b.push(...text.split(/\s+/).filter(Boolean));
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
break;
|
|
77
|
+
default:
|
|
78
|
+
// the value can contain JSX elements, e.g. icon={<Icon />}
|
|
79
|
+
callback(node.value, { selectors: state.selectors });
|
|
80
|
+
break;
|
|
77
81
|
}
|
|
78
82
|
},
|
|
79
83
|
JSXIdentifier(acornNode, state) {
|
|
@@ -84,12 +88,36 @@ function purgeFromJsx(options) {
|
|
|
84
88
|
const node = acornNode;
|
|
85
89
|
state.text = node.namespace.name + ":" + node.name.name;
|
|
86
90
|
},
|
|
87
|
-
// Only
|
|
91
|
+
// Only static strings are collected, not values computed at runtime
|
|
88
92
|
Literal(acornNode, state) {
|
|
93
|
+
var _a;
|
|
89
94
|
const node = acornNode;
|
|
90
95
|
if (typeof node.value === "string") {
|
|
91
|
-
state.
|
|
96
|
+
(_a = state.strings) === null || _a === void 0 ? void 0 : _a.push(node.value);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
// static parts of a template literal, e.g. `btn btn-${size}`
|
|
100
|
+
TemplateElement(acornNode, state) {
|
|
101
|
+
var _a;
|
|
102
|
+
const node = acornNode;
|
|
103
|
+
if (node.value.cooked) {
|
|
104
|
+
(_a = state.strings) === null || _a === void 0 ? void 0 : _a.push(node.value.cooked);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
// keys of the objects in a className or id expression, e.g.
|
|
108
|
+
// clsx({ active: isActive, "is-open": isOpen })
|
|
109
|
+
Property(acornNode, state, callback) {
|
|
110
|
+
const node = acornNode;
|
|
111
|
+
if (state.strings &&
|
|
112
|
+
!node.computed &&
|
|
113
|
+
node.key.type === "Identifier") {
|
|
114
|
+
state.strings.push(node.key.name);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// a string key is a Literal, a computed key any expression
|
|
118
|
+
callback(node.key, state);
|
|
92
119
|
}
|
|
120
|
+
callback(node.value, state);
|
|
93
121
|
},
|
|
94
122
|
}, { ...walk__namespace.base });
|
|
95
123
|
return state.selectors || [];
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fullhuman/purgecss-from-jsx",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "JSX extractor for PurgeCSS",
|
|
5
5
|
"author": "Ffloriel",
|
|
6
6
|
"homepage": "https://github.com/FullHuman/purgecss#readme",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.12.0"
|
|
10
|
+
},
|
|
8
11
|
"main": "lib/purgecss-from-jsx.js",
|
|
9
12
|
"module": "lib/purgecss-from-jsx.esm.js",
|
|
10
13
|
"types": "lib/purgecss-from-jsx.d.ts",
|
|
@@ -19,22 +22,21 @@
|
|
|
19
22
|
"type": "git",
|
|
20
23
|
"url": "git+https://github.com/FullHuman/purgecss.git"
|
|
21
24
|
},
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "ts-node build.ts",
|
|
24
|
-
"test": "jest"
|
|
25
|
-
},
|
|
26
25
|
"bugs": {
|
|
27
26
|
"url": "https://github.com/FullHuman/purgecss/issues"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"acorn": "^8.
|
|
31
|
-
"acorn-jsx": "^5.3.
|
|
29
|
+
"acorn": "^8.19.0",
|
|
30
|
+
"acorn-jsx": "^5.3.2",
|
|
32
31
|
"acorn-jsx-walk": "^2.0.0",
|
|
33
|
-
"acorn-walk": "^8.
|
|
32
|
+
"acorn-walk": "^8.3.5"
|
|
34
33
|
},
|
|
35
34
|
"publishConfig": {
|
|
36
35
|
"access": "public",
|
|
37
36
|
"registry": "https://registry.npmjs.org/"
|
|
38
37
|
},
|
|
39
|
-
"
|
|
40
|
-
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "ts-node build.ts",
|
|
40
|
+
"test": "jest"
|
|
41
|
+
}
|
|
42
|
+
}
|