rollup-plugin-react-scoped-css 1.2.5 → 1.2.6

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 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
 
@@ -1,22 +1,24 @@
1
1
  import { parse, generate, } 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);
2
8
  const getSelectors = (items) => {
3
- const selectorLists = items.reduce((acc, node) => {
4
- if (node.type === "Rule" && node?.prelude?.type === "SelectorList") {
5
- acc.push(node.prelude.children);
9
+ const selectorLists = [];
10
+ items.forEach((node) => {
11
+ if (node.type === "Rule" && node.prelude?.type === "SelectorList") {
12
+ selectorLists.push(node.prelude.children);
6
13
  }
7
- else if (node.type === "Atrule" && node.name === "media") {
8
- node.block?.children.forEach((rule) => {
9
- if (rule.type === "Atrule" && rule.name === "media") {
10
- const selectors = getSelectors(rule.block?.children);
11
- acc.push(...selectors);
12
- }
13
- if (rule.type === "Rule" && rule.prelude.type === "SelectorList") {
14
- acc.push(rule.prelude.children);
15
- }
16
- });
14
+ else if (node.type === "Atrule" &&
15
+ node.block &&
16
+ !isKeyframesAtRule(node.name)) {
17
+ // Recurse into conditional/group at-rules (`@media`, `@container`,
18
+ // `@supports`, `@layer`, `@scope`, etc.) so their rules get scoped too.
19
+ selectorLists.push(...getSelectors(node.block.children));
17
20
  }
18
- return acc;
19
- }, []);
21
+ });
20
22
  return selectorLists;
21
23
  };
22
24
  const isScopePiercingPseudoSelector = (item) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rollup-plugin-react-scoped-css",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
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.3.0",
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.0",
55
- "@babel/preset-env": "7.29.0",
56
- "@babel/preset-react": "7.28.5",
57
- "@babel/preset-typescript": "7.28.5",
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.6",
61
- "@semantic-release/npm": "13.1.5",
62
- "@semantic-release/release-notes-generator": "14.1.0",
63
- "@types/css-tree": "2.3.11",
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.8",
67
+ "@types/estree": "1.0.9",
66
68
  "@types/jest": "30.0.0",
67
- "@types/node": "25.3.5",
68
- "@typescript-eslint/eslint-plugin": "8.56.1",
69
- "@typescript-eslint/parser": "8.56.1",
70
- "acorn": "8.16.0",
71
- "eslint": "9.12.0",
72
- "eslint-config-prettier": "9.1.0",
73
- "eslint-plugin-import": "2.31.0",
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.2.1",
76
- "eslint-plugin-react": "7.37.1",
77
- "jest": "30.2.0",
78
- "rollup": "^4.59.0",
79
- "semantic-release": "25.0.3",
80
- "source-map-js": "1.2.1",
81
- "typescript": "5.9.3"
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
  }