rollup-plugin-react-scoped-css 0.4.0 → 0.5.2
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/.github/workflows/pull-request.yml +19 -0
- package/.github/workflows/release.yml +30 -0
- package/.releaserc +13 -0
- package/README.md +25 -2
- package/dist/ast-iterator.d.ts +1 -0
- package/dist/ast-iterator.js +114 -0
- package/dist/ast-transformer.d.ts +1 -0
- package/dist/ast-transformer.js +50 -0
- package/dist/ast.utils.d.ts +4 -0
- package/dist/ast.utils.js +49 -108
- package/dist/index.js +12 -12
- package/dist/parsers/classic-parser.d.ts +7 -0
- package/dist/parsers/classic-parser.js +95 -0
- package/dist/parsers/jsx-parser.model.d.ts +9 -0
- package/dist/parsers/jsx-parser.model.js +8 -0
- package/dist/parsers/new-parser.d.ts +7 -0
- package/dist/parsers/new-parser.js +94 -0
- package/package.json +9 -3
- package/src/__mocks__/{compiled-jsx-with-attr.js → classic-compiled-jsx-with-attr.js} +0 -0
- package/src/__mocks__/{compiled-jsx.js → classic-compiled-jsx.js} +0 -0
- package/src/__mocks__/new-compiled-jsx-with-attr.js +101 -0
- package/src/__mocks__/new-compiled-jsx.js +64 -0
- package/src/__mocks__/sample-code-file.js +4 -0
- package/src/ast-iterator.spec.ts +19 -0
- package/src/ast-iterator.ts +27 -0
- package/src/ast-transformer.spec.ts +20 -0
- package/src/ast-transformer.ts +24 -0
- package/src/ast.utils.spec.ts +36 -12
- package/src/ast.utils.ts +36 -95
- package/src/index.ts +0 -1
- package/src/parsers/classic-parser.ts +58 -0
- package/src/parsers/jsx-parser.model.ts +10 -0
- package/src/parsers/new-parser.ts +58 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
14
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
15
|
+
if (!m) return o;
|
|
16
|
+
var i = m.call(o), r, ar = [], e;
|
|
17
|
+
try {
|
|
18
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
19
|
+
}
|
|
20
|
+
catch (error) { e = { error: error }; }
|
|
21
|
+
finally {
|
|
22
|
+
try {
|
|
23
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
24
|
+
}
|
|
25
|
+
finally { if (e) throw e.error; }
|
|
26
|
+
}
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
30
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
31
|
+
if (ar || !(i in from)) {
|
|
32
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
33
|
+
ar[i] = from[i];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.NewJsxParser = void 0;
|
|
40
|
+
var NewJsxParser = /** @class */ (function () {
|
|
41
|
+
function NewJsxParser() {
|
|
42
|
+
}
|
|
43
|
+
NewJsxParser.prototype.isNodeReactFragment = function (node) {
|
|
44
|
+
var _a, _b, _c, _d;
|
|
45
|
+
return ((_b = (_a = node.arguments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.type) === "Identifier" &&
|
|
46
|
+
['_Fragment', 'Fragment'].includes((_d = (_c = node.arguments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.name);
|
|
47
|
+
};
|
|
48
|
+
NewJsxParser.prototype.isNodeReactElement = function (node) {
|
|
49
|
+
var _a;
|
|
50
|
+
return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
|
|
51
|
+
['_jsxDEV', '_jsx', '_jsxs', 'jsxDev', 'jsx', 'jsxs'].includes((_a = node === null || node === void 0 ? void 0 : node.callee) === null || _a === void 0 ? void 0 : _a.name);
|
|
52
|
+
};
|
|
53
|
+
NewJsxParser.prototype.extendNodeWithAttributes = function (node, attr) {
|
|
54
|
+
var _this = this;
|
|
55
|
+
var _a;
|
|
56
|
+
var arg = (_a = node === null || node === void 0 ? void 0 : node.arguments) === null || _a === void 0 ? void 0 : _a[1];
|
|
57
|
+
return __assign(__assign({}, node), { arguments: node === null || node === void 0 ? void 0 : node.arguments.map(function (v, i) {
|
|
58
|
+
if (i === 1) {
|
|
59
|
+
if (v.type === 'ObjectExpression') {
|
|
60
|
+
return __assign(__assign({}, v), { properties: __spreadArray(__spreadArray([], __read(arg.properties), false), [
|
|
61
|
+
_this.createAttributeNode(attr)
|
|
62
|
+
], false) });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return {
|
|
66
|
+
type: 'ObjectExpression',
|
|
67
|
+
properties: [_this.createAttributeNode(attr)]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return v;
|
|
72
|
+
}) });
|
|
73
|
+
};
|
|
74
|
+
NewJsxParser.prototype.createAttributeNode = function (attr) {
|
|
75
|
+
return {
|
|
76
|
+
type: 'Property',
|
|
77
|
+
method: false,
|
|
78
|
+
shorthand: false,
|
|
79
|
+
computed: false,
|
|
80
|
+
key: {
|
|
81
|
+
type: 'Identifier',
|
|
82
|
+
name: "\"".concat(attr, "\"")
|
|
83
|
+
},
|
|
84
|
+
value: {
|
|
85
|
+
type: 'Literal',
|
|
86
|
+
value: true,
|
|
87
|
+
raw: "true"
|
|
88
|
+
},
|
|
89
|
+
kind: 'init'
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
return NewJsxParser;
|
|
93
|
+
}());
|
|
94
|
+
exports.NewJsxParser = NewJsxParser;
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rollup-plugin-react-scoped-css",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "A rollup plugin designed to allow scoped css to be run in react (Compatible with vite and rollup)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"build:watch": "tsc --watch",
|
|
10
|
-
"prepare": "npm run build && npm test",
|
|
11
10
|
"test": "jest"
|
|
12
11
|
},
|
|
13
12
|
"repository": {
|
|
@@ -42,9 +41,16 @@
|
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@babel/preset-env": "^7.15.0",
|
|
44
43
|
"@babel/preset-typescript": "^7.15.0",
|
|
44
|
+
"@semantic-release/commit-analyzer": "^9.0.2",
|
|
45
|
+
"@semantic-release/git": "^10.0.1",
|
|
46
|
+
"@semantic-release/github": "^8.0.2",
|
|
47
|
+
"@semantic-release/npm": "^8.0.3",
|
|
48
|
+
"@semantic-release/release-notes-generator": "^10.0.3",
|
|
45
49
|
"@types/escodegen": "0.0.7",
|
|
46
50
|
"@types/estree": "0.0.50",
|
|
47
51
|
"@types/jest": "^27.0.1",
|
|
48
|
-
"
|
|
52
|
+
"acorn": "^8.6.0",
|
|
53
|
+
"jest": "^27.0.6",
|
|
54
|
+
"semantic-release": "^18.0.1"
|
|
49
55
|
}
|
|
50
56
|
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
j as jsxRuntime,
|
|
3
|
+
r as react,
|
|
4
|
+
R as ReactDOM,
|
|
5
|
+
a as React
|
|
6
|
+
} from "./vendor.1137a3d7.js";
|
|
7
|
+
var index = "body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n";
|
|
8
|
+
var App$1 = ".sub {\n color: green;\n}";
|
|
9
|
+
var App_scoped = ".App {\n text-align: center;\n}\n\n.sub {\n color: blue !important;\n}\n\n.App-logo {\n height: 40vmin;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .App-logo {\n animation: App-logo-spin infinite 20s linear;\n }\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\nbutton {\n font-size: calc(10px + 2vmin);\n}\n";
|
|
10
|
+
var logo = "/assets/logo.ecc203fb.svg";
|
|
11
|
+
var Sub_scoped = ".sub {\n color: red;\n}\n.sub__test {\n margin: 0;\n}\n.sub h2 {\n font-weight: 300;\n font-size: 3rem;\n}\n\nh2 {\n font-size: 10rem;\n}\n\n.drawer {\n width: 400px;\n}\n.drawer__list-item {\n padding: 0;\n}\n.drawer__list-item-icon {\n margin-right: 1rem;\n}\n.drawer__link {\n display: flex;\n width: 300px;\n padding: 1rem;\n align-items: center;\n text-decoration: none;\n border: 0;\n outline: 0;\n background: transparent;\n font-family: \"Quicksand\", \"Roboto\", sans-serif;\n font-size: 1rem;\n font-weight: 400;\n}\n.drawer__link:hover {\n background-color: #fafafa;\n}";
|
|
12
|
+
const jsx = jsxRuntime.exports.jsx;
|
|
13
|
+
const jsxs = jsxRuntime.exports.jsxs;
|
|
14
|
+
const Fragment = jsxRuntime.exports.Fragment;
|
|
15
|
+
function Sub() {
|
|
16
|
+
return jsx("div", {
|
|
17
|
+
className: "sub",
|
|
18
|
+
children: jsx("h2", {
|
|
19
|
+
children: "Sub",
|
|
20
|
+
"data-v-123456": true
|
|
21
|
+
}),
|
|
22
|
+
"data-v-123456": true
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function App() {
|
|
26
|
+
const [count, setCount] = react.exports.useState(0);
|
|
27
|
+
return jsx(Fragment, {
|
|
28
|
+
children: jsxs("div", {
|
|
29
|
+
className: "App",
|
|
30
|
+
children: [
|
|
31
|
+
jsxs("header", {
|
|
32
|
+
className: "App-header",
|
|
33
|
+
children: [
|
|
34
|
+
jsx("img", {
|
|
35
|
+
src: logo,
|
|
36
|
+
className: "App-logo",
|
|
37
|
+
alt: "logo",
|
|
38
|
+
"data-v-123456": true
|
|
39
|
+
}),
|
|
40
|
+
jsx("p", {
|
|
41
|
+
children: "Hello Vite + React!",
|
|
42
|
+
"data-v-123456": true
|
|
43
|
+
}),
|
|
44
|
+
jsx("p", {
|
|
45
|
+
children: jsxs("button", {
|
|
46
|
+
type: "button",
|
|
47
|
+
onClick: () => setCount(count2 => count2 + 1),
|
|
48
|
+
children: [
|
|
49
|
+
"count is: ",
|
|
50
|
+
count
|
|
51
|
+
],
|
|
52
|
+
"data-v-123456": true
|
|
53
|
+
}),
|
|
54
|
+
"data-v-123456": true
|
|
55
|
+
}),
|
|
56
|
+
jsxs("p", {
|
|
57
|
+
children: [
|
|
58
|
+
"Edit ",
|
|
59
|
+
jsx("code", {
|
|
60
|
+
children: "App.jsx",
|
|
61
|
+
"data-v-123456": true
|
|
62
|
+
}),
|
|
63
|
+
" and save to test HMR updates."
|
|
64
|
+
],
|
|
65
|
+
"data-v-123456": true
|
|
66
|
+
}),
|
|
67
|
+
jsxs("p", {
|
|
68
|
+
children: [
|
|
69
|
+
jsx("a", {
|
|
70
|
+
className: "App-link",
|
|
71
|
+
href: "https://reactjs.org",
|
|
72
|
+
target: "_blank",
|
|
73
|
+
rel: "noopener noreferrer",
|
|
74
|
+
children: "Learn React",
|
|
75
|
+
"data-v-123456": true
|
|
76
|
+
}),
|
|
77
|
+
" | ",
|
|
78
|
+
jsx("a", {
|
|
79
|
+
className: "App-link",
|
|
80
|
+
href: "https://vitejs.dev/guide/features.html",
|
|
81
|
+
target: "_blank",
|
|
82
|
+
rel: "noopener noreferrer",
|
|
83
|
+
children: "Vite Docs",
|
|
84
|
+
"data-v-123456": true
|
|
85
|
+
})
|
|
86
|
+
],
|
|
87
|
+
"data-v-123456": true
|
|
88
|
+
})
|
|
89
|
+
],
|
|
90
|
+
"data-v-123456": true
|
|
91
|
+
}),
|
|
92
|
+
jsx(Fragment, { children: jsx(Sub, { "data-v-123456": true }) })
|
|
93
|
+
],
|
|
94
|
+
"data-v-123456": true
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
ReactDOM.render(jsx(React.StrictMode, {
|
|
99
|
+
children: jsx(App, { "data-v-123456": true }),
|
|
100
|
+
"data-v-123456": true
|
|
101
|
+
}), document.getElementById("root"));
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { j as jsxRuntime, r as react, R as ReactDOM, a as React } from "./vendor.1137a3d7.js";
|
|
2
|
+
var index = "body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n";
|
|
3
|
+
var App$1 = ".sub {\n color: green;\n}";
|
|
4
|
+
var App_scoped = ".App {\n text-align: center;\n}\n\n.sub {\n color: blue !important;\n}\n\n.App-logo {\n height: 40vmin;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .App-logo {\n animation: App-logo-spin infinite 20s linear;\n }\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\nbutton {\n font-size: calc(10px + 2vmin);\n}\n";
|
|
5
|
+
var logo = "/assets/logo.ecc203fb.svg";
|
|
6
|
+
var Sub_scoped = '.sub {\n color: red;\n}\n.sub__test {\n margin: 0;\n}\n.sub h2 {\n font-weight: 300;\n font-size: 3rem;\n}\n\nh2 {\n font-size: 10rem;\n}\n\n.drawer {\n width: 400px;\n}\n.drawer__list-item {\n padding: 0;\n}\n.drawer__list-item-icon {\n margin-right: 1rem;\n}\n.drawer__link {\n display: flex;\n width: 300px;\n padding: 1rem;\n align-items: center;\n text-decoration: none;\n border: 0;\n outline: 0;\n background: transparent;\n font-family: "Quicksand", "Roboto", sans-serif;\n font-size: 1rem;\n font-weight: 400;\n}\n.drawer__link:hover {\n background-color: #fafafa;\n}';
|
|
7
|
+
const jsx = jsxRuntime.exports.jsx;
|
|
8
|
+
const jsxs = jsxRuntime.exports.jsxs;
|
|
9
|
+
const Fragment = jsxRuntime.exports.Fragment;
|
|
10
|
+
function Sub() {
|
|
11
|
+
return /* @__PURE__ */ jsx("div", {
|
|
12
|
+
className: "sub",
|
|
13
|
+
children: /* @__PURE__ */ jsx("h2", {
|
|
14
|
+
children: "Sub"
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function App() {
|
|
19
|
+
const [count, setCount] = react.exports.useState(0);
|
|
20
|
+
return /* @__PURE__ */ jsx(Fragment, {
|
|
21
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
22
|
+
className: "App",
|
|
23
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
24
|
+
className: "App-header",
|
|
25
|
+
children: [/* @__PURE__ */ jsx("img", {
|
|
26
|
+
src: logo,
|
|
27
|
+
className: "App-logo",
|
|
28
|
+
alt: "logo"
|
|
29
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
30
|
+
children: "Hello Vite + React!"
|
|
31
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
32
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
33
|
+
type: "button",
|
|
34
|
+
onClick: () => setCount((count2) => count2 + 1),
|
|
35
|
+
children: ["count is: ", count]
|
|
36
|
+
})
|
|
37
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
38
|
+
children: ["Edit ", /* @__PURE__ */ jsx("code", {
|
|
39
|
+
children: "App.jsx"
|
|
40
|
+
}), " and save to test HMR updates."]
|
|
41
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
42
|
+
children: [/* @__PURE__ */ jsx("a", {
|
|
43
|
+
className: "App-link",
|
|
44
|
+
href: "https://reactjs.org",
|
|
45
|
+
target: "_blank",
|
|
46
|
+
rel: "noopener noreferrer",
|
|
47
|
+
children: "Learn React"
|
|
48
|
+
}), " | ", /* @__PURE__ */ jsx("a", {
|
|
49
|
+
className: "App-link",
|
|
50
|
+
href: "https://vitejs.dev/guide/features.html",
|
|
51
|
+
target: "_blank",
|
|
52
|
+
rel: "noopener noreferrer",
|
|
53
|
+
children: "Vite Docs"
|
|
54
|
+
})]
|
|
55
|
+
})]
|
|
56
|
+
}), /* @__PURE__ */ jsx(Fragment, {
|
|
57
|
+
children: /* @__PURE__ */ jsx(Sub, {})
|
|
58
|
+
})]
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
ReactDOM.render(/* @__PURE__ */ jsx(React.StrictMode, {
|
|
63
|
+
children: /* @__PURE__ */ jsx(App, {})
|
|
64
|
+
}), document.getElementById("root"));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { parse } from "acorn";
|
|
4
|
+
import { astIterator } from "./ast-iterator";
|
|
5
|
+
|
|
6
|
+
describe('astIterator', () => {
|
|
7
|
+
it('should iterate through all nodes', async () => {
|
|
8
|
+
let iterationCount = 0;
|
|
9
|
+
const file = await readFile(resolve(__dirname, './__mocks__/sample-code-file.js'), { encoding: 'utf-8' })
|
|
10
|
+
const ast = parse(file, { sourceType: 'module', ecmaVersion: 2022 });
|
|
11
|
+
|
|
12
|
+
for (const node of astIterator(ast)) {
|
|
13
|
+
iterationCount++;
|
|
14
|
+
expect(node).toBeTruthy();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
expect(iterationCount).toBe(28);
|
|
18
|
+
});
|
|
19
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
const isNode = (obj: any) => {
|
|
3
|
+
return typeof obj === 'object' &&
|
|
4
|
+
typeof (obj as any)?.end === 'number' &&
|
|
5
|
+
typeof (obj as any)?.start === 'number' &&
|
|
6
|
+
typeof (obj as any)?.type === 'string'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const astIterator = function* (ast: any): IterableIterator<any> {
|
|
10
|
+
if (!ast) { return }
|
|
11
|
+
|
|
12
|
+
for (let key of Object.keys(ast)) {
|
|
13
|
+
// Skip useless keys
|
|
14
|
+
if (['end', 'start', 'type'].includes(key)) { continue; }
|
|
15
|
+
|
|
16
|
+
// If key value is an array, iterate over it
|
|
17
|
+
if (Array.isArray(ast[key])) {
|
|
18
|
+
for (let nodeIndex in ast[key]) {
|
|
19
|
+
yield *astIterator(ast[key][nodeIndex])
|
|
20
|
+
}
|
|
21
|
+
} else if(isNode(ast[key])) {
|
|
22
|
+
yield *astIterator(ast[key])
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
yield ast
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { parse } from "acorn";
|
|
4
|
+
import { astTransformer } from "./ast-transformer";
|
|
5
|
+
|
|
6
|
+
describe('astTransformer', () => {
|
|
7
|
+
it('should iterate through all nodes', async () => {
|
|
8
|
+
let iterationCount = 0;
|
|
9
|
+
const file = await readFile(resolve(__dirname, './__mocks__/sample-code-file.js'), { encoding: 'utf-8' })
|
|
10
|
+
const ast = parse(file, { sourceType: 'module', ecmaVersion: 2022 });
|
|
11
|
+
|
|
12
|
+
const newAst = astTransformer(ast, (node) => {
|
|
13
|
+
iterationCount++;
|
|
14
|
+
expect(node).toBeTruthy();
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(newAst).toBeDefined();
|
|
18
|
+
expect(iterationCount).toBe(28);
|
|
19
|
+
});
|
|
20
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const isNode = (obj: any) => {
|
|
2
|
+
return typeof obj === 'object' &&
|
|
3
|
+
typeof (obj as any)?.end === 'number' &&
|
|
4
|
+
typeof (obj as any)?.start === 'number' &&
|
|
5
|
+
typeof (obj as any)?.type === 'string'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const astTransformer = (ast: any, callback: Function) => {
|
|
9
|
+
for (let key of Object.keys(ast)) {
|
|
10
|
+
if (['end', 'start', 'type'].includes(key)) { continue; }
|
|
11
|
+
|
|
12
|
+
if (Array.isArray(ast[key])) {
|
|
13
|
+
for (let nodeIndex in ast[key]) {
|
|
14
|
+
ast[key][nodeIndex] = astTransformer(ast[key][nodeIndex], callback)
|
|
15
|
+
}
|
|
16
|
+
} else if(isNode(ast[key])) {
|
|
17
|
+
ast[key] = astTransformer(ast[key], callback)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = callback(ast);
|
|
22
|
+
|
|
23
|
+
return typeof result !== 'undefined' ? result : ast
|
|
24
|
+
}
|
package/src/ast.utils.spec.ts
CHANGED
|
@@ -5,18 +5,42 @@ import { generate } from 'escodegen'
|
|
|
5
5
|
import { addHashAttributesToJsxTagsAst } from './ast.utils'
|
|
6
6
|
|
|
7
7
|
describe('AST Utils', () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const ast = parse(compiled, { ecmaVersion: 2021, sourceType: "module" })
|
|
12
|
-
const modifiedAst = addHashAttributesToJsxTagsAst(ast, 'data-v-123456')
|
|
13
|
-
const modifiedCompiled = generate(modifiedAst, { format: {
|
|
14
|
-
indent: {
|
|
15
|
-
style: ' ',
|
|
16
|
-
},
|
|
17
|
-
quotes: 'double',
|
|
18
|
-
} })
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
global.implementation = null
|
|
10
|
+
})
|
|
19
11
|
|
|
20
|
-
|
|
12
|
+
describe('classic implementation', () => {
|
|
13
|
+
it('Should add the attr to the right elements', async () => {
|
|
14
|
+
const compiled = await readFile(resolve(__dirname, './__mocks__/classic-compiled-jsx.js'), { encoding: 'utf-8' })
|
|
15
|
+
const compiledWithAttr = await readFile(resolve(__dirname, './__mocks__/classic-compiled-jsx-with-attr.js'), { encoding: 'utf-8' })
|
|
16
|
+
const ast = parse(compiled, { ecmaVersion: 2021, sourceType: "module" })
|
|
17
|
+
const modifiedAst = addHashAttributesToJsxTagsAst(ast, 'data-v-123456')
|
|
18
|
+
const modifiedCompiled = generate(modifiedAst, { format: {
|
|
19
|
+
indent: {
|
|
20
|
+
style: ' ',
|
|
21
|
+
},
|
|
22
|
+
quotes: 'double',
|
|
23
|
+
} })
|
|
24
|
+
|
|
25
|
+
expect(modifiedCompiled).toEqual(compiledWithAttr)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('new implementation', () => {
|
|
30
|
+
it('Should add the attr to the right elements', async () => {
|
|
31
|
+
const compiled = await readFile(resolve(__dirname, './__mocks__/new-compiled-jsx.js'), { encoding: 'utf-8' })
|
|
32
|
+
const compiledWithAttr = await readFile(resolve(__dirname, './__mocks__/new-compiled-jsx-with-attr.js'), { encoding: 'utf-8' })
|
|
33
|
+
const ast = parse(compiled, { ecmaVersion: 2021, sourceType: "module" })
|
|
34
|
+
const modifiedAst = addHashAttributesToJsxTagsAst(ast, 'data-v-123456')
|
|
35
|
+
const modifiedCompiled = generate(modifiedAst, { format: {
|
|
36
|
+
indent: {
|
|
37
|
+
style: ' ',
|
|
38
|
+
},
|
|
39
|
+
quotes: 'double',
|
|
40
|
+
} })
|
|
41
|
+
|
|
42
|
+
expect(modifiedCompiled).toEqual(compiledWithAttr)
|
|
43
|
+
})
|
|
21
44
|
})
|
|
45
|
+
|
|
22
46
|
})
|
package/src/ast.utils.ts
CHANGED
|
@@ -1,111 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* @returns
|
|
7
|
-
*/
|
|
8
|
-
function traverse(ast: any, callback: Function) {
|
|
9
|
-
for (let key of Object.keys(ast)) {
|
|
10
|
-
if (['end', 'start', 'type'].includes(key)) { continue; }
|
|
1
|
+
import { astIterator } from "./ast-iterator"
|
|
2
|
+
import { astTransformer } from "./ast-transformer"
|
|
3
|
+
import { ClassicJsxParser } from "./parsers/classic-parser"
|
|
4
|
+
import { JsxParser, ParserImplementations } from "./parsers/jsx-parser.model"
|
|
5
|
+
import { NewJsxParser } from "./parsers/new-parser"
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ast[key][nodeIndex] = traverse(ast[key][nodeIndex], callback)
|
|
15
|
-
}
|
|
16
|
-
} else if(isNode(ast[key])) {
|
|
17
|
-
ast[key] = traverse(ast[key], callback)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const result = callback(ast);
|
|
22
|
-
|
|
23
|
-
return typeof result !== 'undefined' ? result : ast
|
|
7
|
+
declare global {
|
|
8
|
+
var implementation: null | ParserImplementations
|
|
24
9
|
}
|
|
25
10
|
|
|
26
|
-
|
|
27
|
-
return typeof obj === 'object' &&
|
|
28
|
-
typeof (obj as any)?.end === 'number' &&
|
|
29
|
-
typeof (obj as any)?.start === 'number' &&
|
|
30
|
-
typeof (obj as any)?.type === 'string'
|
|
31
|
-
}
|
|
11
|
+
global.implementation = null
|
|
32
12
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
node?.callee?.object?.name === 'React' &&
|
|
36
|
-
node?.callee?.property?.name === 'createElement'
|
|
37
|
-
}
|
|
13
|
+
const classicParser = new ClassicJsxParser()
|
|
14
|
+
const newParser = new NewJsxParser()
|
|
38
15
|
|
|
39
|
-
|
|
40
|
-
return node?.type === 'CallExpression' &&
|
|
41
|
-
['_jsxDEV', '_jsx'].includes(node?.callee?.name)
|
|
42
|
-
}
|
|
16
|
+
let parser: JsxParser = classicParser;
|
|
43
17
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
18
|
+
const findImplementation = (program: any) => {
|
|
19
|
+
let implementation: ParserImplementations = ParserImplementations.classic;
|
|
20
|
+
for (let node of astIterator(program)) {
|
|
21
|
+
if (node.type !== 'ImportDeclaration') {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
25
|
+
if (
|
|
26
|
+
node.specifiers.some((n: any) =>
|
|
27
|
+
['_jsxDEV', 'jsxRuntime'].includes(n.local.name))
|
|
28
|
+
|| ['react/jsx-dev-runtime', 'react/jsx-runtime'].includes(node.source.value)
|
|
29
|
+
) {
|
|
30
|
+
return ParserImplementations.new
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return implementation
|
|
52
34
|
}
|
|
53
35
|
|
|
54
|
-
function
|
|
55
|
-
return isFragmentWithOldJsxTransform(node) ||
|
|
56
|
-
isFragmentWithNewJsxTransform(node)
|
|
57
|
-
}
|
|
36
|
+
export function addHashAttributesToJsxTagsAst(program: any, attr: string) {
|
|
58
37
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
38
|
+
// Once in the program, we can determine which parser to use
|
|
39
|
+
if (global.implementation === null) {
|
|
40
|
+
global.implementation = findImplementation(program)
|
|
63
41
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
method: false,
|
|
68
|
-
shorthand: false,
|
|
69
|
-
computed: false,
|
|
70
|
-
key: {
|
|
71
|
-
type: 'Identifier',
|
|
72
|
-
name: `"${attr}"`
|
|
73
|
-
},
|
|
74
|
-
value: {
|
|
75
|
-
type: 'Literal',
|
|
76
|
-
value: true,
|
|
77
|
-
raw: "true"
|
|
78
|
-
},
|
|
79
|
-
kind: 'init'
|
|
42
|
+
if (global.implementation === ParserImplementations.new) {
|
|
43
|
+
parser = newParser
|
|
44
|
+
}
|
|
80
45
|
}
|
|
81
|
-
}
|
|
82
46
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const arg = node?.arguments?.[1]
|
|
87
|
-
return {
|
|
88
|
-
...node,
|
|
89
|
-
arguments: node?.arguments.map((v: any, i: number) => {
|
|
90
|
-
if (i === 1) {
|
|
91
|
-
if (v.type === 'ObjectExpression') {
|
|
92
|
-
return {
|
|
93
|
-
...v,
|
|
94
|
-
properties: [
|
|
95
|
-
...arg.properties,
|
|
96
|
-
createAttributeNode(attr)
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
return {
|
|
101
|
-
type: 'ObjectExpression',
|
|
102
|
-
properties: [createAttributeNode(attr)]
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return v
|
|
107
|
-
})
|
|
108
|
-
}
|
|
47
|
+
return astTransformer(program, (node: any) => {
|
|
48
|
+
if (parser.isNodeReactElement(node) && !parser.isNodeReactFragment(node)) {
|
|
49
|
+
return parser.extendNodeWithAttributes(node, attr)
|
|
109
50
|
}
|
|
110
51
|
})
|
|
111
52
|
}
|
package/src/index.ts
CHANGED