rollup-plugin-react-scoped-css 1.2.5 → 1.2.7
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 +49 -0
- package/dist/css/scope-css.js +294 -55
- package/package.json +29 -26
package/README.md
CHANGED
|
@@ -162,6 +162,55 @@ Scoped styles do not eliminate the need for classes. Due to the way browsers ren
|
|
|
162
162
|
|
|
163
163
|
Be careful with descendant selectors in recursive components! For a CSS rule with the selector .a .b, if the element that matches .a contains a recursive child component, then all .b in that child component will be matched by the rule.
|
|
164
164
|
|
|
165
|
+
## Testing
|
|
166
|
+
|
|
167
|
+
### Unit tests
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
npm test
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### End-to-end Vite compatibility harness
|
|
174
|
+
|
|
175
|
+
The e2e harness packs the plugin with `npm pack`, installs it into an isolated
|
|
176
|
+
project for each Vite major, then drives Chromium against both the dev server
|
|
177
|
+
and a production build served with `vite preview`.
|
|
178
|
+
|
|
179
|
+
Prerequisites: Node >= 20.19 and a Playwright browser.
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
npm run test:e2e:install # once; installs Chromium
|
|
183
|
+
npm run test:e2e # Vite 5, 6, 7, and 8; dev + build/preview
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Options (pass-through after `--`):
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
npm run test:e2e -- --vite 7 # one major (repeatable/comma-separated)
|
|
190
|
+
npm run test:e2e -- --mode dev # dev, build, or both (default)
|
|
191
|
+
npm run test:e2e -- --browser firefox # chromium (default), firefox, webkit
|
|
192
|
+
npm run test:e2e -- --headed # watch the browser
|
|
193
|
+
npm run test:e2e -- --clean --force # wipe and reinstall cached installs
|
|
194
|
+
npm run test:e2e -- --keep-temp # keep production build output
|
|
195
|
+
npm run test:e2e -- --allow-broken-sourcemap
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The harness asserts that:
|
|
199
|
+
|
|
200
|
+
- scoped JSX receives `data-<hash>` attributes and every element from a file
|
|
201
|
+
shares that file's hash;
|
|
202
|
+
- scoped selectors are applied, global styles stay global, and styles from one
|
|
203
|
+
component do not leak into another;
|
|
204
|
+
- computed styles match in both dev and production builds;
|
|
205
|
+
- emitted CSS contains the scoped selectors and JS contains the matching hash;
|
|
206
|
+
- JS sourcemaps reference the original `.tsx` files and include their content;
|
|
207
|
+
- there are no page/console/network errors and no `SOURCEMAP_BROKEN` warnings.
|
|
208
|
+
|
|
209
|
+
Vite installs are cached in `e2e/.cache/`; the cache key includes the packed
|
|
210
|
+
plugin, so plugin changes trigger a reinstall. Vite 8 uses Rolldown rather than
|
|
211
|
+
Rollup; `rollup` is installed in the harness only to satisfy this package's peer
|
|
212
|
+
dependency. No CI workflow runs the e2e harness yet — it is a local script.
|
|
213
|
+
|
|
165
214
|
## Contributing
|
|
166
215
|
Anyone is free to open a PR and contribute to this project... just be civilized!
|
|
167
216
|
|
package/dist/css/scope-css.js
CHANGED
|
@@ -1,39 +1,103 @@
|
|
|
1
|
-
import { parse, generate, } from "css-tree";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { parse, generate, List, } from "css-tree";
|
|
2
|
+
/**
|
|
3
|
+
* Keyframe selectors (`from`, `to`, `50%`) are also parsed as `Rule` nodes with
|
|
4
|
+
* a `SelectorList` prelude. They need to be left alone, otherwise animations
|
|
5
|
+
* break by becoming e.g. `0%[data-hash]`.
|
|
6
|
+
*/
|
|
7
|
+
const isKeyframesAtRule = (name) => /^(-[a-z0-9]+-)?keyframes$/i.test(name);
|
|
8
|
+
const isScopePiercingPseudoSelector = (node) => node.type === "PseudoElementSelector" &&
|
|
9
|
+
(node.name === "v-deep" || node.name === "deep");
|
|
10
|
+
/**
|
|
11
|
+
* CSS-tree parses the contents of `:deep(...)` as a `Raw` node, unlike
|
|
12
|
+
* `:is()`/`:where()` which it parses as selectors.
|
|
13
|
+
*/
|
|
14
|
+
const isDeepFunctionSelector = (node) => node.type === "PseudoClassSelector" &&
|
|
15
|
+
(node.name === "deep" || node.name === "v-deep") &&
|
|
16
|
+
!!node.children;
|
|
17
|
+
/**
|
|
18
|
+
* Pseudo selectors that refer to the document root. They cannot be scoped
|
|
19
|
+
* because the root element never receives the scope attribute, so adding the
|
|
20
|
+
* attribute would only turn working rules into dead ones.
|
|
21
|
+
*/
|
|
22
|
+
const isRootLikePseudoSelector = (node) => node.type === "PseudoClassSelector" &&
|
|
23
|
+
(node.name.toLowerCase() === "root" || node.name.toLowerCase() === "scope");
|
|
24
|
+
const isCombinator = (node) => node.type === "Combinator";
|
|
25
|
+
const isNestingSelector = (node) => node.type === "NestingSelector";
|
|
26
|
+
/**
|
|
27
|
+
* Simple selectors that can receive the scope attribute. `&` is excluded
|
|
28
|
+
* because the parent rule is already scoped, so `&[hash]` would be redundant.
|
|
29
|
+
*/
|
|
30
|
+
const isHashableSelector = (node) => node.type === "TypeSelector" ||
|
|
31
|
+
node.type === "ClassSelector" ||
|
|
32
|
+
node.type === "IdSelector" ||
|
|
33
|
+
node.type === "AttributeSelector";
|
|
34
|
+
const getDeepFunctionSelectorText = (node) => {
|
|
35
|
+
const children = node.children;
|
|
36
|
+
if (!children || children.isEmpty) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
let text = "";
|
|
40
|
+
children.forEach((child) => {
|
|
41
|
+
if (child.type === "Raw") {
|
|
42
|
+
text += child.value;
|
|
17
43
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return selectorLists;
|
|
21
|
-
};
|
|
22
|
-
const isScopePiercingPseudoSelector = (item) => {
|
|
23
|
-
return (item?.data?.type === "PseudoElementSelector" &&
|
|
24
|
-
(item?.data?.name === "v-deep" || item?.data?.name === "deep"));
|
|
44
|
+
});
|
|
45
|
+
return text || null;
|
|
25
46
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Splits a raw selector prelude on top-level `||` (column combinator) while
|
|
49
|
+
* ignoring `||` inside attribute selectors, functional pseudos, or strings.
|
|
50
|
+
*/
|
|
51
|
+
const splitOnColumnCombinator = (value) => {
|
|
52
|
+
const parts = [];
|
|
53
|
+
let start = 0;
|
|
54
|
+
let parenthesisDepth = 0;
|
|
55
|
+
let bracketDepth = 0;
|
|
56
|
+
let quote = null;
|
|
57
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
58
|
+
const char = value[index];
|
|
59
|
+
if (quote) {
|
|
60
|
+
if (char === "\\") {
|
|
61
|
+
index += 1;
|
|
62
|
+
}
|
|
63
|
+
else if (char === quote) {
|
|
64
|
+
quote = null;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (char === '"' || char === "'") {
|
|
69
|
+
quote = char;
|
|
70
|
+
}
|
|
71
|
+
else if (char === "\\") {
|
|
72
|
+
index += 1;
|
|
73
|
+
}
|
|
74
|
+
else if (char === "(") {
|
|
75
|
+
parenthesisDepth += 1;
|
|
76
|
+
}
|
|
77
|
+
else if (char === ")") {
|
|
78
|
+
parenthesisDepth -= 1;
|
|
79
|
+
}
|
|
80
|
+
else if (char === "[") {
|
|
81
|
+
bracketDepth += 1;
|
|
82
|
+
}
|
|
83
|
+
else if (char === "]") {
|
|
84
|
+
bracketDepth -= 1;
|
|
85
|
+
}
|
|
86
|
+
else if (char === "|" &&
|
|
87
|
+
value[index + 1] === "|" &&
|
|
88
|
+
parenthesisDepth === 0 &&
|
|
89
|
+
bracketDepth === 0) {
|
|
90
|
+
parts.push(value.slice(start, index));
|
|
91
|
+
index += 1;
|
|
92
|
+
start = index + 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
parts.push(value.slice(start));
|
|
96
|
+
return parts;
|
|
32
97
|
};
|
|
33
98
|
export function scopeCss(css, filename, hash, source = filename) {
|
|
34
99
|
try {
|
|
35
100
|
const ast = parse(css, { positions: true, filename: source });
|
|
36
|
-
const selectorLists = getSelectors(ast.children);
|
|
37
101
|
const attributeSelector = {
|
|
38
102
|
type: "AttributeSelector",
|
|
39
103
|
name: { type: "Identifier", name: hash },
|
|
@@ -41,40 +105,215 @@ export function scopeCss(css, filename, hash, source = filename) {
|
|
|
41
105
|
value: null,
|
|
42
106
|
flags: null,
|
|
43
107
|
};
|
|
44
|
-
selectorLists
|
|
45
|
-
|
|
46
|
-
if (selector.type
|
|
108
|
+
const scopeSelectorList = (selectorLists) => {
|
|
109
|
+
selectorLists.forEach((selector) => {
|
|
110
|
+
if (selector.type === "Selector") {
|
|
111
|
+
scopeSelector(selector);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Inserts the scope attribute after every consecutive run of simple
|
|
117
|
+
* selectors. Pseudo selectors break a run, which preserves the previous
|
|
118
|
+
* behaviour of `a:hover.b` becoming `a[hash]:hover.b[hash]`.
|
|
119
|
+
*/
|
|
120
|
+
const scopeRuns = (items, selectorChildren) => {
|
|
121
|
+
let run = [];
|
|
122
|
+
const flushRun = () => {
|
|
123
|
+
if (run.length === 0) {
|
|
47
124
|
return;
|
|
48
125
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
126
|
+
const last = run[run.length - 1];
|
|
127
|
+
if (last.next) {
|
|
128
|
+
selectorChildren.insertData(attributeSelector, last.next);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
selectorChildren.appendData(attributeSelector);
|
|
132
|
+
}
|
|
133
|
+
run = [];
|
|
134
|
+
};
|
|
135
|
+
items.forEach((item) => {
|
|
136
|
+
if (isHashableSelector(item.data)) {
|
|
137
|
+
run.push(item);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
flushRun();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
flushRun();
|
|
144
|
+
};
|
|
145
|
+
const scopeCompound = (items, selectorChildren) => {
|
|
146
|
+
if (items.length === 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (items.some((item) => isHashableSelector(item.data))) {
|
|
150
|
+
scopeRuns(items, selectorChildren);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// `&` is already scoped through its parent rule.
|
|
154
|
+
if (items.some((item) => isNestingSelector(item.data))) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// `:root` / `:scope` refer to the document root and can never match the
|
|
158
|
+
// scope attribute; leave them global.
|
|
159
|
+
if (items.some((item) => isRootLikePseudoSelector(item.data))) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (items.some((item) => item.data.type === "PseudoElementSelector")) {
|
|
163
|
+
// An attribute cannot follow a pseudo-element, so it has to be
|
|
164
|
+
// prepended: `::before` -> `[hash]::before`.
|
|
165
|
+
selectorChildren.insertData(attributeSelector, items[0]);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!items.some((item) => item.data.type === "PseudoClassSelector")) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// `:hover` -> `:hover[hash]`
|
|
172
|
+
const last = items[items.length - 1];
|
|
173
|
+
if (last.next) {
|
|
174
|
+
selectorChildren.insertData(attributeSelector, last.next);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
selectorChildren.appendData(attributeSelector);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const replaceDeepFunction = (selectorChildren, item) => {
|
|
181
|
+
const node = item.data;
|
|
182
|
+
const selectorText = getDeepFunctionSelectorText(node);
|
|
183
|
+
if (!selectorText) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
let parsed;
|
|
187
|
+
try {
|
|
188
|
+
parsed = parse(selectorText, {
|
|
189
|
+
context: "selectorList",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const selectors = parsed.children;
|
|
196
|
+
if (selectors.size === 1) {
|
|
197
|
+
// Splice the argument selector in place of `:deep(...)`.
|
|
198
|
+
const replacement = new List();
|
|
199
|
+
selectors.first.children.forEach((child) => {
|
|
200
|
+
replacement.appendData(child);
|
|
201
|
+
});
|
|
202
|
+
selectorChildren.replace(item, replacement);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// A selector list cannot be spliced into a single selector, but
|
|
206
|
+
// `:is(...)` has the same matching semantics here.
|
|
207
|
+
node.name = "is";
|
|
208
|
+
node.children = new List();
|
|
209
|
+
node.children.appendData(parsed);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const scopeSelector = (selector) => {
|
|
213
|
+
const selectorChildren = selector.children;
|
|
214
|
+
const compounds = [];
|
|
215
|
+
let compound = [];
|
|
216
|
+
for (let item = selectorChildren.head; item !== null; item = item.next) {
|
|
217
|
+
if (isCombinator(item.data)) {
|
|
218
|
+
compounds.push(compound);
|
|
219
|
+
compound = [];
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
compound.push(item);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
compounds.push(compound);
|
|
226
|
+
for (const items of compounds) {
|
|
227
|
+
const piercingIndex = items.findIndex((item) => isScopePiercingPseudoSelector(item.data) ||
|
|
228
|
+
isDeepFunctionSelector(item.data));
|
|
229
|
+
if (piercingIndex !== -1) {
|
|
230
|
+
const piercingItem = items[piercingIndex];
|
|
231
|
+
if (isDeepFunctionSelector(piercingItem.data)) {
|
|
232
|
+
// Scope everything before `:deep(...)` in the same compound,
|
|
233
|
+
// then splice in the (unscoped) argument selector.
|
|
234
|
+
scopeRuns(items.slice(0, piercingIndex), selectorChildren);
|
|
235
|
+
replaceDeepFunction(selectorChildren, piercingItem);
|
|
62
236
|
}
|
|
63
237
|
else {
|
|
64
|
-
|
|
65
|
-
|
|
238
|
+
// Replace `::deep` / `::v-deep` with the scope attribute.
|
|
239
|
+
Object.assign(piercingItem.data, attributeSelector);
|
|
66
240
|
}
|
|
67
|
-
|
|
241
|
+
// Everything after a deep selector stays global.
|
|
242
|
+
return;
|
|
68
243
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
244
|
+
scopeCompound(items, selectorChildren);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* CSS-tree parses implicit nesting as a `Raw` node inside the parent
|
|
249
|
+
* rule. Re-parse it as a rule so its selector can be scoped and write the
|
|
250
|
+
* transformed rule back.
|
|
251
|
+
*/
|
|
252
|
+
const scopeRawNestedRule = (node) => {
|
|
253
|
+
if (!node.value.includes("{")) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const rule = parse(node.value, { context: "rule" });
|
|
258
|
+
if (rule.type !== "Rule") {
|
|
259
|
+
return;
|
|
72
260
|
}
|
|
73
|
-
|
|
74
|
-
|
|
261
|
+
scopeRule(rule);
|
|
262
|
+
node.value = generate(rule);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// Leave constructs css-tree cannot parse untouched.
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const scopeRawSelectorPrelude = (prelude) => {
|
|
269
|
+
const parts = splitOnColumnCombinator(prelude.value);
|
|
270
|
+
if (parts.length === 1) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const scopedParts = parts.map((part) => {
|
|
275
|
+
const selectorList = parse(part.trim(), {
|
|
276
|
+
context: "selectorList",
|
|
277
|
+
});
|
|
278
|
+
scopeSelectorList(selectorList.children);
|
|
279
|
+
return generate(selectorList);
|
|
75
280
|
});
|
|
281
|
+
prelude.value = scopedParts.join(" || ");
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Leave constructs css-tree cannot parse untouched.
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
const scopeNodes = (children, insideRule) => {
|
|
288
|
+
children.forEach((node) => {
|
|
289
|
+
if (node.type === "Rule") {
|
|
290
|
+
scopeRule(node);
|
|
291
|
+
}
|
|
292
|
+
else if (node.type === "Atrule" &&
|
|
293
|
+
node.block &&
|
|
294
|
+
!isKeyframesAtRule(node.name)) {
|
|
295
|
+
// Recurse into conditional/group at-rules (`@media`, `@container`,
|
|
296
|
+
// `@supports`, `@layer`, `@scope`, etc.) so their rules get scoped.
|
|
297
|
+
scopeNodes(node.block.children, insideRule);
|
|
298
|
+
}
|
|
299
|
+
else if (insideRule && node.type === "Raw") {
|
|
300
|
+
scopeRawNestedRule(node);
|
|
301
|
+
}
|
|
76
302
|
});
|
|
77
|
-
}
|
|
303
|
+
};
|
|
304
|
+
const scopeRule = (rule) => {
|
|
305
|
+
if (rule.prelude?.type === "SelectorList") {
|
|
306
|
+
scopeSelectorList(rule.prelude.children);
|
|
307
|
+
}
|
|
308
|
+
else if (rule.prelude?.type === "Raw") {
|
|
309
|
+
scopeRawSelectorPrelude(rule.prelude);
|
|
310
|
+
}
|
|
311
|
+
if (rule.block) {
|
|
312
|
+
// Nested rules (`& ...`) and nested at-rules live in the rule block.
|
|
313
|
+
scopeNodes(rule.block.children, true);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
scopeNodes(ast.children, false);
|
|
78
317
|
const { css: scopedCss, map } = generate(ast, {
|
|
79
318
|
sourceMap: true,
|
|
80
319
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rollup-plugin-react-scoped-css",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "Bring scoped CSS to React with Vite & Rollup. Lightweight component-level style isolation similar to Vue and Angular.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"build:watch": "tsc --watch",
|
|
11
11
|
"lint": "eslint '*/**/*.{js,jsx,ts,tsx}'",
|
|
12
|
-
"test": "jest"
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"test:e2e": "node e2e/run.mjs",
|
|
14
|
+
"test:e2e:install": "playwright install chromium"
|
|
13
15
|
},
|
|
14
16
|
"repository": {
|
|
15
17
|
"type": "git",
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
},
|
|
43
45
|
"homepage": "https://github.com/cmseguin/rollup-plugin-react-scoped-css#readme",
|
|
44
46
|
"dependencies": {
|
|
45
|
-
"@rollup/pluginutils": "^5.
|
|
47
|
+
"@rollup/pluginutils": "^5.5.1",
|
|
46
48
|
"css-tree": "3.2.1",
|
|
47
49
|
"escodegen": "2.1.0",
|
|
48
50
|
"js-xxhash": "5.0.1"
|
|
@@ -51,33 +53,34 @@
|
|
|
51
53
|
"rollup": "^4.0.0"
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
|
-
"@babel/core": "7.29.
|
|
55
|
-
"@babel/preset-env": "7.29.
|
|
56
|
-
"@babel/preset-react": "7.
|
|
57
|
-
"@babel/preset-typescript": "7.
|
|
56
|
+
"@babel/core": "7.29.7",
|
|
57
|
+
"@babel/preset-env": "7.29.7",
|
|
58
|
+
"@babel/preset-react": "7.29.7",
|
|
59
|
+
"@babel/preset-typescript": "7.29.7",
|
|
58
60
|
"@semantic-release/commit-analyzer": "13.0.1",
|
|
59
61
|
"@semantic-release/git": "10.0.1",
|
|
60
|
-
"@semantic-release/github": "12.0.
|
|
61
|
-
"@semantic-release/npm": "13.
|
|
62
|
-
"@semantic-release/release-notes-generator": "14.1.
|
|
63
|
-
"@types/css-tree": "2.
|
|
62
|
+
"@semantic-release/github": "12.0.10",
|
|
63
|
+
"@semantic-release/npm": "13.2.0",
|
|
64
|
+
"@semantic-release/release-notes-generator": "14.1.1",
|
|
65
|
+
"@types/css-tree": "3.2.0",
|
|
64
66
|
"@types/escodegen": "0.0.10",
|
|
65
|
-
"@types/estree": "1.0.
|
|
67
|
+
"@types/estree": "1.0.9",
|
|
66
68
|
"@types/jest": "30.0.0",
|
|
67
|
-
"@types/node": "
|
|
68
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
69
|
-
"@typescript-eslint/parser": "8.
|
|
70
|
-
"acorn": "8.
|
|
71
|
-
"eslint": "9.
|
|
72
|
-
"eslint-config-prettier": "
|
|
73
|
-
"eslint-plugin-import": "2.
|
|
69
|
+
"@types/node": "26.6.5",
|
|
70
|
+
"@typescript-eslint/eslint-plugin": "8.72.0",
|
|
71
|
+
"@typescript-eslint/parser": "8.72.0",
|
|
72
|
+
"acorn": "8.19.0",
|
|
73
|
+
"eslint": "9.39.5",
|
|
74
|
+
"eslint-config-prettier": "10.1.8",
|
|
75
|
+
"eslint-plugin-import": "2.32.0",
|
|
74
76
|
"eslint-plugin-node": "11.1.0",
|
|
75
|
-
"eslint-plugin-prettier": "5.
|
|
76
|
-
"eslint-plugin-react": "7.37.
|
|
77
|
-
"jest": "30.2
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
77
|
+
"eslint-plugin-prettier": "5.5.7",
|
|
78
|
+
"eslint-plugin-react": "7.37.5",
|
|
79
|
+
"jest": "30.5.2",
|
|
80
|
+
"playwright": "1.64.1",
|
|
81
|
+
"rollup": "^4.65.0",
|
|
82
|
+
"semantic-release": "25.0.9",
|
|
83
|
+
"source-map-js": "1.2.2",
|
|
84
|
+
"typescript": "6.0.3"
|
|
82
85
|
}
|
|
83
86
|
}
|