rollup-plugin-react-scoped-css 0.2.2 → 0.4.1

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
@@ -56,6 +56,7 @@ There are a few options available to customize how the plugin works
56
56
 
57
57
  /**
58
58
  * If you want to customize the stylesheet file pattern
59
+ * if undefined or '' is passed, all files will be evaluated
59
60
  * Default: 'scoped'
60
61
  */
61
62
  styleFileSuffix?: string
@@ -65,11 +66,23 @@ There are a few options available to customize how the plugin works
65
66
  * Default: 'v'
66
67
  */
67
68
  hashPrefix?: string
69
+
70
+ /**
71
+ * If you want to customize the preprocessors
72
+ * Default: ['scss', 'css', 'sass', 'less']
73
+ */
74
+ styleFileExtensions?: string[]
75
+
76
+ /**
77
+ * If you have jsx in other file extensions
78
+ * Default: ['jsx', 'tsx']
79
+ */
80
+ jsxFileExtensions?: string[]
68
81
  }
69
82
  ```
70
83
 
71
84
  ### With other rollup plugins
72
- Since this plugin works in two parts, you might need to expose the first part, then add any styling plugin, and then expose the second part of the plugin. This part is automatically handled with vite thanks to the enforce attribute.
85
+ Since this plugin works in two parts, you might need to expose the first part, then add any other plugin, and then expose the second part of the plugin. This part is automatically handled with vite thanks to the enforce attribute.
73
86
 
74
87
  ```
75
88
  const reactScopedPlugins = reactScopedCssPlugin()
@@ -80,6 +93,4 @@ export default {
80
93
  ```
81
94
 
82
95
  ## Contributing
83
- Anyone is free to open a PR and contribute to this project... I, by no means, am the best, and I am sure someone smarter than me can bring a lot to this project.
84
-
85
- The only rules is: Don't be a dick
96
+ Anyone is free to open a PR and contribute to this project... just be civilized!
package/dist/ast.utils.js CHANGED
@@ -17,23 +17,13 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from) {
17
17
  };
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.addHashAttributesToJsxTagsAst = void 0;
20
- function isNode(obj) {
21
- var _a, _b, _c;
22
- return typeof obj === 'object' &&
23
- typeof ((_a = obj) === null || _a === void 0 ? void 0 : _a.end) === 'number' &&
24
- typeof ((_b = obj) === null || _b === void 0 ? void 0 : _b.start) === 'number' &&
25
- typeof ((_c = obj) === null || _c === void 0 ? void 0 : _c.type) === 'string';
26
- }
27
- function isReactCreateElementCallExpression(node) {
28
- var _a, _b, _c, _d;
29
- return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
30
- ((_b = (_a = node === null || node === void 0 ? void 0 : node.callee) === null || _a === void 0 ? void 0 : _a.object) === null || _b === void 0 ? void 0 : _b.name) === 'React' &&
31
- ((_d = (_c = node === null || node === void 0 ? void 0 : node.callee) === null || _c === void 0 ? void 0 : _c.property) === null || _d === void 0 ? void 0 : _d.name) === 'createElement';
32
- }
33
- function isLiteralCallExpression(node) {
34
- var _a;
35
- return ((_a = node === null || node === void 0 ? void 0 : node.arguments) === null || _a === void 0 ? void 0 : _a[0].type) === 'Literal';
36
- }
20
+ /**
21
+ * Traverses the AST tree and calls callback for each node.
22
+ * -
23
+ * @param ast The ast tree
24
+ * @param callback Callback function to be called for each node
25
+ * @returns
26
+ */
37
27
  function traverse(ast, callback) {
38
28
  for (var _i = 0, _a = Object.keys(ast); _i < _a.length; _i++) {
39
29
  var key = _a[_i];
@@ -52,6 +42,42 @@ function traverse(ast, callback) {
52
42
  var result = callback(ast);
53
43
  return typeof result !== 'undefined' ? result : ast;
54
44
  }
45
+ function isNode(obj) {
46
+ var _a, _b, _c;
47
+ return typeof obj === 'object' &&
48
+ typeof ((_a = obj) === null || _a === void 0 ? void 0 : _a.end) === 'number' &&
49
+ typeof ((_b = obj) === null || _b === void 0 ? void 0 : _b.start) === 'number' &&
50
+ typeof ((_c = obj) === null || _c === void 0 ? void 0 : _c.type) === 'string';
51
+ }
52
+ function isElementWithOldJsxTransform(node) {
53
+ var _a, _b, _c, _d;
54
+ return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
55
+ ((_b = (_a = node === null || node === void 0 ? void 0 : node.callee) === null || _a === void 0 ? void 0 : _a.object) === null || _b === void 0 ? void 0 : _b.name) === 'React' &&
56
+ ((_d = (_c = node === null || node === void 0 ? void 0 : node.callee) === null || _c === void 0 ? void 0 : _c.property) === null || _d === void 0 ? void 0 : _d.name) === 'createElement';
57
+ }
58
+ function isElementWithNewJsxTransform(node) {
59
+ var _a;
60
+ return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
61
+ ['_jsxDEV', '_jsx', '_jsxs'].includes((_a = node === null || node === void 0 ? void 0 : node.callee) === null || _a === void 0 ? void 0 : _a.name);
62
+ }
63
+ function isFragmentWithOldJsxTransform(node) {
64
+ var _a, _b, _c, _d, _e, _f;
65
+ return ((_c = (_b = (_a = node === null || node === void 0 ? void 0 : node.arguments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.object) === null || _c === void 0 ? void 0 : _c.name) === "React" &&
66
+ ((_f = (_e = (_d = node === null || node === void 0 ? void 0 : node.arguments) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.property) === null || _f === void 0 ? void 0 : _f.name) === "Fragment";
67
+ }
68
+ function isFragmentWithNewJsxTransform(node) {
69
+ var _a, _b, _c, _d;
70
+ return ((_b = (_a = node.arguments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.type) === "Identifier" &&
71
+ ((_d = (_c = node.arguments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.name) === "_Fragment";
72
+ }
73
+ function isFragment(node) {
74
+ return isFragmentWithOldJsxTransform(node) ||
75
+ isFragmentWithNewJsxTransform(node);
76
+ }
77
+ function isReactElement(node) {
78
+ return isElementWithOldJsxTransform(node) ||
79
+ isElementWithNewJsxTransform(node);
80
+ }
55
81
  function createAttributeNode(attr) {
56
82
  return {
57
83
  type: 'Property',
@@ -73,7 +99,7 @@ function createAttributeNode(attr) {
73
99
  function addHashAttributesToJsxTagsAst(program, attr) {
74
100
  return traverse(program, function (node) {
75
101
  var _a;
76
- if (isReactCreateElementCallExpression(node) && isLiteralCallExpression(node)) {
102
+ if (isReactElement(node) && !isFragment(node)) {
77
103
  var arg_1 = (_a = node === null || node === void 0 ? void 0 : node.arguments) === null || _a === void 0 ? void 0 : _a[1];
78
104
  return __assign(__assign({}, node), { arguments: node === null || node === void 0 ? void 0 : node.arguments.map(function (v, i) {
79
105
  if (i === 1) {
package/dist/index.d.ts CHANGED
@@ -1,10 +1,37 @@
1
1
  import { FilterPattern } from '@rollup/pluginutils';
2
2
  import type { Plugin } from 'rollup';
3
3
  export interface ReactScopedCssPluginOptions {
4
+ /**
5
+ * Which files should be included and parsed by the plugin
6
+ * Default: undefined
7
+ */
4
8
  include?: FilterPattern;
9
+ /**
10
+ * Which files should be exluded and that should not be parsed by the plugin
11
+ * Default: undefined
12
+ */
5
13
  exclude?: FilterPattern;
14
+ /**
15
+ * If you want to customize the stylesheet file pattern
16
+ * if undefined or '' is passed, all files will be evaluated
17
+ * Default: 'scoped'
18
+ */
6
19
  styleFileSuffix?: string;
20
+ /**
21
+ * If you want to customize the attribute prefix that is added to the jsx elements
22
+ * Default: 'v'
23
+ */
7
24
  hashPrefix?: string;
25
+ /**
26
+ * If you want to customize the stylesheet extensions
27
+ * Default: ['scss', 'css', 'sass', 'less']
28
+ */
29
+ styleFileExtensions?: string[];
30
+ /**
31
+ * If you have jsx in other file extensions
32
+ * Default: ['jsx', 'tsx']
33
+ */
34
+ jsxFileExtensions?: string[];
8
35
  }
9
36
  export interface VitePartialPlugin extends Plugin {
10
37
  enforce?: "pre" | "post";
package/dist/index.js CHANGED
@@ -50,32 +50,33 @@ var generateHash = function (input, seed) {
50
50
  var hashNum = js_xxhash_1.xxHash32(Buffer.from(input, 'utf8'), seed);
51
51
  return hashNum.toString(16);
52
52
  };
53
- var addAttributesToCss = function (src, fileName, hash, prefix) {
53
+ var addAttributesToCss = function (src, fileName, hash) {
54
54
  var code = component_compiler_utils_1.compileStyle({
55
55
  source: src,
56
56
  filename: fileName,
57
- id: "data-" + prefix + "-" + hash,
57
+ id: hash,
58
58
  scoped: true,
59
59
  trim: true,
60
60
  }).code;
61
61
  return code;
62
62
  };
63
- var addAttributesToJsx = function (src, hash, prefix) {
64
- return src.replace(/<[^/][^>]*>/g, function (tag) { return tag.replace(/([^=<])(\/?>)/, function (m) {
65
- var sm = [];
66
- for (var _i = 1; _i < arguments.length; _i++) {
67
- sm[_i - 1] = arguments[_i];
68
- }
69
- return sm[0] + " data-" + prefix + "-" + hash + sm[1];
70
- }); });
71
- };
72
63
  function reactScopedCssPlugin(optionsIn) {
73
64
  if (optionsIn === void 0) { optionsIn = {}; }
74
- var options = __assign({ styleFileSuffix: 'scoped', hashPrefix: 'v' }, optionsIn);
65
+ var options = __assign({ hashPrefix: 'v', styleFileSuffix: 'scoped', styleFileExtensions: ['scss', 'css', 'sass', 'less'], jsxFileExtensions: ['jsx', 'tsx'] }, optionsIn);
66
+ if (!options.styleFileExtensions || !options.styleFileExtensions.length) {
67
+ throw new Error('You need to provide at least one style file extension');
68
+ }
69
+ if (!options.jsxFileExtensions || !options.jsxFileExtensions.length) {
70
+ throw new Error('You need to provide at least one jsx file extension');
71
+ }
75
72
  var filter = pluginutils_1.createFilter(options.include, options.exclude);
76
- var scopedCssRegex = new RegExp("." + options.styleFileSuffix + ".(scss|css|sass)$");
77
- var scopedCssInFileRegex = new RegExp("." + options.styleFileSuffix + ".(scss|css|sass)(\"|')");
78
- var jsxRegex = /\.(tsx|jsx)$/;
73
+ var scopedCssRegex = options.styleFileSuffix
74
+ ? new RegExp("." + options.styleFileSuffix + ".(" + options.styleFileExtensions.join('|') + ")$")
75
+ : new RegExp(".(" + options.styleFileExtensions.join('|') + ")$");
76
+ var scopedCssInFileRegex = options.styleFileSuffix
77
+ ? new RegExp("." + options.styleFileSuffix + ".(" + options.styleFileExtensions.join('|') + ")(\"|')")
78
+ : new RegExp(".(" + options.styleFileExtensions.join('|') + ")(\"|')");
79
+ var jsxRegex = new RegExp(".(" + options.jsxFileExtensions.join('|') + ")$");
79
80
  return [
80
81
  {
81
82
  name: 'rollup-plugin-react-scoped-css:pre',
@@ -105,7 +106,7 @@ function reactScopedCssPlugin(optionsIn) {
105
106
  }
106
107
  if (scopedCssRegex.test(getFilenameFromPath(id))) {
107
108
  var importerHash = getHashFromPath(id);
108
- return addAttributesToCss(code, getFilenameFromPath(id), importerHash, options.hashPrefix);
109
+ return addAttributesToCss(code, getFilenameFromPath(id), "data-" + options.hashPrefix + "-" + importerHash);
109
110
  }
110
111
  }
111
112
  }
@@ -0,0 +1,15 @@
1
+ import { JsxParser, ParserImplementations } from "./jsx-parser.model";
2
+ export declare class BaseParser implements Pick<JsxParser, 'isNode' & 'traverse'> {
3
+ isNode(obj: any): boolean;
4
+ /**
5
+ * Traverses the AST tree and calls callback for each node.
6
+ * -
7
+ * @param ast The ast tree
8
+ * @param callback Callback function to be called for each node
9
+ * @returns
10
+ */
11
+ traverse(ast: any, callback: Function): any;
12
+ findImplementation(ast: any): ParserImplementations.classic;
13
+ extendNodeWithAttributes(node: any, attr: string): any;
14
+ private createAttributeNode;
15
+ }
@@ -0,0 +1,109 @@
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 __spreadArray = (this && this.__spreadArray) || function (to, from) {
14
+ for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
15
+ to[j] = from[i];
16
+ return to;
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.BaseParser = void 0;
20
+ var jsx_parser_model_1 = require("./jsx-parser.model");
21
+ var BaseParser = /** @class */ (function () {
22
+ function BaseParser() {
23
+ }
24
+ BaseParser.prototype.isNode = function (obj) {
25
+ var _a, _b, _c;
26
+ return typeof obj === 'object' &&
27
+ typeof ((_a = obj) === null || _a === void 0 ? void 0 : _a.end) === 'number' &&
28
+ typeof ((_b = obj) === null || _b === void 0 ? void 0 : _b.start) === 'number' &&
29
+ typeof ((_c = obj) === null || _c === void 0 ? void 0 : _c.type) === 'string';
30
+ };
31
+ /**
32
+ * Traverses the AST tree and calls callback for each node.
33
+ * -
34
+ * @param ast The ast tree
35
+ * @param callback Callback function to be called for each node
36
+ * @returns
37
+ */
38
+ BaseParser.prototype.traverse = function (ast, callback) {
39
+ for (var _i = 0, _a = Object.keys(ast); _i < _a.length; _i++) {
40
+ var key = _a[_i];
41
+ if (['end', 'start', 'type'].includes(key)) {
42
+ continue;
43
+ }
44
+ if (Array.isArray(ast[key])) {
45
+ for (var nodeIndex in ast[key]) {
46
+ ast[key][nodeIndex] = this.traverse(ast[key][nodeIndex], callback);
47
+ }
48
+ }
49
+ else if (this.isNode(ast[key])) {
50
+ ast[key] = this.traverse(ast[key], callback);
51
+ }
52
+ }
53
+ var result = callback(ast);
54
+ return typeof result !== 'undefined' ? result : ast;
55
+ };
56
+ BaseParser.prototype.findImplementation = function (ast) {
57
+ var implementation = jsx_parser_model_1.ParserImplementations.classic;
58
+ this.traverse(ast, function (node) {
59
+ if (node.type !== 'ImportDeclaration') {
60
+ return;
61
+ }
62
+ if (node.source.value === 'react/jsx-runtime') {
63
+ implementation = jsx_parser_model_1.ParserImplementations.new;
64
+ }
65
+ });
66
+ return implementation;
67
+ };
68
+ BaseParser.prototype.extendNodeWithAttributes = function (node, attr) {
69
+ var _this = this;
70
+ var _a;
71
+ var arg = (_a = node === null || node === void 0 ? void 0 : node.arguments) === null || _a === void 0 ? void 0 : _a[1];
72
+ return __assign(__assign({}, node), { arguments: node === null || node === void 0 ? void 0 : node.arguments.map(function (v, i) {
73
+ if (i === 1) {
74
+ if (v.type === 'ObjectExpression') {
75
+ return __assign(__assign({}, v), { properties: __spreadArray(__spreadArray([], arg.properties), [
76
+ _this.createAttributeNode(attr)
77
+ ]) });
78
+ }
79
+ else {
80
+ return {
81
+ type: 'ObjectExpression',
82
+ properties: [_this.createAttributeNode(attr)]
83
+ };
84
+ }
85
+ }
86
+ return v;
87
+ }) });
88
+ };
89
+ BaseParser.prototype.createAttributeNode = function (attr) {
90
+ return {
91
+ type: 'Property',
92
+ method: false,
93
+ shorthand: false,
94
+ computed: false,
95
+ key: {
96
+ type: 'Identifier',
97
+ name: "\"" + attr + "\""
98
+ },
99
+ value: {
100
+ type: 'Literal',
101
+ value: true,
102
+ raw: "true"
103
+ },
104
+ kind: 'init'
105
+ };
106
+ };
107
+ return BaseParser;
108
+ }());
109
+ exports.BaseParser = BaseParser;
@@ -0,0 +1,6 @@
1
+ import { BaseParser } from "./base-parser";
2
+ import { JsxParser } from "./jsx-parser.model";
3
+ export declare class ClassicJsxParser extends BaseParser implements JsxParser {
4
+ isNodeReactFragment(node: any): boolean;
5
+ isNodeReactElement(node: any): boolean;
6
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.ClassicJsxParser = void 0;
19
+ var base_parser_1 = require("./base-parser");
20
+ var ClassicJsxParser = /** @class */ (function (_super) {
21
+ __extends(ClassicJsxParser, _super);
22
+ function ClassicJsxParser() {
23
+ return _super !== null && _super.apply(this, arguments) || this;
24
+ }
25
+ ClassicJsxParser.prototype.isNodeReactFragment = function (node) {
26
+ var _a, _b, _c, _d, _e, _f;
27
+ return ((_c = (_b = (_a = node === null || node === void 0 ? void 0 : node.arguments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.object) === null || _c === void 0 ? void 0 : _c.name) === "React" &&
28
+ ((_f = (_e = (_d = node === null || node === void 0 ? void 0 : node.arguments) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.property) === null || _f === void 0 ? void 0 : _f.name) === "Fragment";
29
+ };
30
+ ClassicJsxParser.prototype.isNodeReactElement = function (node) {
31
+ var _a, _b, _c, _d;
32
+ return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
33
+ ((_b = (_a = node === null || node === void 0 ? void 0 : node.callee) === null || _a === void 0 ? void 0 : _a.object) === null || _b === void 0 ? void 0 : _b.name) === 'React' &&
34
+ ((_d = (_c = node === null || node === void 0 ? void 0 : node.callee) === null || _c === void 0 ? void 0 : _c.property) === null || _d === void 0 ? void 0 : _d.name) === 'createElement';
35
+ };
36
+ return ClassicJsxParser;
37
+ }(base_parser_1.BaseParser));
38
+ exports.ClassicJsxParser = ClassicJsxParser;
@@ -0,0 +1,6 @@
1
+ export interface JsxParser {
2
+ isNode: (node: any) => boolean;
3
+ isNodeReactElement: (node: any) => boolean;
4
+ isNodeReactFragment: (node: any) => boolean;
5
+ traverse: (ast: any, callback: (ast: any) => void) => void;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ export interface JsxParser {
2
+ isNodeReactElement: (node: any) => boolean;
3
+ isNodeReactFragment: (node: any) => boolean;
4
+ isNode: (node: any) => boolean;
5
+ traverse: (ast: any, callback: (ast: any) => void) => void;
6
+ findImplementation: (ast: any) => ParserImplementations;
7
+ extendNodeWithAttributes: (node: any, attr: string) => any;
8
+ }
9
+ export declare enum ParserImplementations {
10
+ new = "new",
11
+ classic = "classic"
12
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParserImplementations = void 0;
4
+ var ParserImplementations;
5
+ (function (ParserImplementations) {
6
+ ParserImplementations["new"] = "new";
7
+ ParserImplementations["classic"] = "classic";
8
+ })(ParserImplementations = exports.ParserImplementations || (exports.ParserImplementations = {}));
@@ -0,0 +1,6 @@
1
+ import { BaseParser } from "./base-parser";
2
+ import { JsxParser } from "./jsx-parser.model";
3
+ export declare class NewJsxParser extends BaseParser implements JsxParser {
4
+ isNodeReactFragment(node: any): boolean;
5
+ isNodeReactElement(node: any): boolean;
6
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.NewJsxParser = void 0;
19
+ var base_parser_1 = require("./base-parser");
20
+ var NewJsxParser = /** @class */ (function (_super) {
21
+ __extends(NewJsxParser, _super);
22
+ function NewJsxParser() {
23
+ return _super !== null && _super.apply(this, arguments) || this;
24
+ }
25
+ NewJsxParser.prototype.isNodeReactFragment = function (node) {
26
+ var _a, _b, _c, _d;
27
+ return ((_b = (_a = node.arguments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.type) === "Identifier" &&
28
+ ((_d = (_c = node.arguments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.name) === "_Fragment";
29
+ };
30
+ NewJsxParser.prototype.isNodeReactElement = function (node) {
31
+ var _a;
32
+ return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
33
+ ['_jsxDEV', '_jsx', '_jsxs'].includes((_a = node === null || node === void 0 ? void 0 : node.callee) === null || _a === void 0 ? void 0 : _a.name);
34
+ };
35
+ return NewJsxParser;
36
+ }(base_parser_1.BaseParser));
37
+ exports.NewJsxParser = NewJsxParser;
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "rollup-plugin-react-scoped-css",
3
- "version": "0.2.2",
3
+ "version": "0.4.1",
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
+ "build:watch": "tsc --watch",
9
10
  "prepare": "npm run build && npm test",
10
11
  "test": "jest"
11
12
  },
@@ -14,6 +15,18 @@
14
15
  "url": "git+https://github.com/cmseguin/rollup-plugin-react-scoped-css.git"
15
16
  },
16
17
  "author": "cmseguin",
18
+ "contributors": [
19
+ "cmseguin (https://github.com/cmseguin)",
20
+ "helianthuswhite (https://github.com/helianthuswhite)"
21
+ ],
22
+ "keywords": [
23
+ "rollup",
24
+ "react",
25
+ "css",
26
+ "scoped",
27
+ "vite",
28
+ "vitejs"
29
+ ],
17
30
  "license": "ISC",
18
31
  "bugs": {
19
32
  "url": "https://github.com/cmseguin/rollup-plugin-react-scoped-css/issues"
@@ -32,6 +32,6 @@ function App() {
32
32
  target: "_blank",
33
33
  rel: "noopener noreferrer",
34
34
  "data-v-123456": true
35
- }, "Vite Docs"))), React.createElement(Sub, null)));
35
+ }, "Vite Docs"))), React.createElement(Sub, { "data-v-123456": true })));
36
36
  }
37
37
  export default App;
package/src/ast.utils.ts CHANGED
@@ -1,3 +1,28 @@
1
+ /**
2
+ * Traverses the AST tree and calls callback for each node.
3
+ * -
4
+ * @param ast The ast tree
5
+ * @param callback Callback function to be called for each node
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; }
11
+
12
+ if (Array.isArray(ast[key])) {
13
+ for (let nodeIndex in ast[key]) {
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
24
+ }
25
+
1
26
  function isNode(obj: unknown) {
2
27
  return typeof obj === 'object' &&
3
28
  typeof (obj as any)?.end === 'number' &&
@@ -5,32 +30,35 @@ function isNode(obj: unknown) {
5
30
  typeof (obj as any)?.type === 'string'
6
31
  }
7
32
 
8
- function isReactCreateElementCallExpression(node: any) {
33
+ function isElementWithOldJsxTransform(node: any) {
9
34
  return node?.type === 'CallExpression' &&
10
35
  node?.callee?.object?.name === 'React' &&
11
36
  node?.callee?.property?.name === 'createElement'
12
37
  }
13
38
 
14
- function isLiteralCallExpression(node: any) {
15
- return node?.arguments?.[0].type === 'Literal'
39
+ function isElementWithNewJsxTransform(node: any) {
40
+ return node?.type === 'CallExpression' &&
41
+ ['_jsxDEV', '_jsx', '_jsxs'].includes(node?.callee?.name)
16
42
  }
17
43
 
18
- function traverse(ast: any, callback: Function) {
19
- for (let key of Object.keys(ast)) {
20
- if (['end', 'start', 'type'].includes(key)) { continue; }
44
+ function isFragmentWithOldJsxTransform(node: any) {
45
+ return node?.arguments?.[0]?.object?.name === "React" &&
46
+ node?.arguments?.[0]?.property?.name === "Fragment"
47
+ }
21
48
 
22
- if (Array.isArray(ast[key])) {
23
- for (let nodeIndex in ast[key]) {
24
- ast[key][nodeIndex] = traverse(ast[key][nodeIndex], callback)
25
- }
26
- } else if(isNode(ast[key])) {
27
- ast[key] = traverse(ast[key], callback)
28
- }
29
- }
49
+ function isFragmentWithNewJsxTransform(node: any) {
50
+ return node.arguments?.[0]?.type === "Identifier" &&
51
+ node.arguments?.[0]?.name === "_Fragment"
52
+ }
30
53
 
31
- const result = callback(ast);
54
+ function isFragment(node: any) {
55
+ return isFragmentWithOldJsxTransform(node) ||
56
+ isFragmentWithNewJsxTransform(node)
57
+ }
32
58
 
33
- return typeof result !== 'undefined' ? result : ast
59
+ function isReactElement(node: any) {
60
+ return isElementWithOldJsxTransform(node) ||
61
+ isElementWithNewJsxTransform(node)
34
62
  }
35
63
 
36
64
  function createAttributeNode(attr: string) {
@@ -54,7 +82,7 @@ function createAttributeNode(attr: string) {
54
82
 
55
83
  export function addHashAttributesToJsxTagsAst(program: any, attr: string) {
56
84
  return traverse(program, (node: any) => {
57
- if (isReactCreateElementCallExpression(node) && isLiteralCallExpression(node)) {
85
+ if (isReactElement(node) && !isFragment(node)) {
58
86
  const arg = node?.arguments?.[1]
59
87
  return {
60
88
  ...node,
package/src/index.ts CHANGED
@@ -23,11 +23,11 @@ const generateHash = (input: string, seed: number = 0) => {
23
23
  return hashNum.toString(16);
24
24
  }
25
25
 
26
- const addAttributesToCss = (src: string, fileName: string, hash: string, prefix: string) => {
26
+ const addAttributesToCss = (src: string, fileName: string, hash: string) => {
27
27
  const { code } = compileStyle({
28
28
  source: src,
29
29
  filename: fileName,
30
- id: `data-${prefix}-${hash}`,
30
+ id: hash,
31
31
  scoped: true,
32
32
  trim: true,
33
33
  })
@@ -35,32 +35,74 @@ const addAttributesToCss = (src: string, fileName: string, hash: string, prefix:
35
35
  return code
36
36
  }
37
37
 
38
- const addAttributesToJsx = (src: string, hash: string, prefix: string) => {
39
- return src.replace(/<[^/][^>]*>/g, (tag) => tag.replace(/([^=<])(\/?>)/, (m, ...sm) => `${sm[0]} data-${prefix}-${hash}${sm[1]}`))
40
- }
41
-
42
38
  export interface ReactScopedCssPluginOptions {
39
+ /**
40
+ * Which files should be included and parsed by the plugin
41
+ * Default: undefined
42
+ */
43
43
  include?: FilterPattern
44
+
45
+ /**
46
+ * Which files should be exluded and that should not be parsed by the plugin
47
+ * Default: undefined
48
+ */
44
49
  exclude?: FilterPattern
50
+
51
+ /**
52
+ * If you want to customize the stylesheet file pattern
53
+ * if undefined or '' is passed, all files will be evaluated
54
+ * Default: 'scoped'
55
+ */
45
56
  styleFileSuffix?: string
57
+
58
+ /**
59
+ * If you want to customize the attribute prefix that is added to the jsx elements
60
+ * Default: 'v'
61
+ */
46
62
  hashPrefix?: string
63
+
64
+ /**
65
+ * If you want to customize the stylesheet extensions
66
+ * Default: ['scss', 'css', 'sass', 'less']
67
+ */
68
+ styleFileExtensions?: string[]
69
+
70
+ /**
71
+ * If you have jsx in other file extensions
72
+ * Default: ['jsx', 'tsx']
73
+ */
74
+ jsxFileExtensions?: string[]
47
75
  }
48
76
 
49
77
  export interface VitePartialPlugin extends Plugin {
50
78
  enforce?: "pre" | "post"
51
79
  }
52
80
 
53
- export default function reactScopedCssPlugin(optionsIn:ReactScopedCssPluginOptions = {}): VitePartialPlugin[] {
54
- const options = {
55
- styleFileSuffix: 'scoped',
81
+ export default function reactScopedCssPlugin(optionsIn: ReactScopedCssPluginOptions = {}): VitePartialPlugin[] {
82
+ const options: Partial<ReactScopedCssPluginOptions> = {
56
83
  hashPrefix: 'v',
84
+ styleFileSuffix: 'scoped',
85
+ styleFileExtensions: ['scss', 'css', 'sass', 'less'],
86
+ jsxFileExtensions: ['jsx', 'tsx'],
57
87
  ...optionsIn
58
88
  }
59
89
 
90
+ if (!options.styleFileExtensions || !options.styleFileExtensions.length) {
91
+ throw new Error('You need to provide at least one style file extension')
92
+ }
93
+
94
+ if (!options.jsxFileExtensions || !options.jsxFileExtensions.length) {
95
+ throw new Error('You need to provide at least one jsx file extension')
96
+ }
97
+
60
98
  const filter = createFilter( options.include, options.exclude );
61
- const scopedCssRegex = new RegExp(`\.${options.styleFileSuffix}\.(scss|css|sass)$`)
62
- const scopedCssInFileRegex = new RegExp(`\.${options.styleFileSuffix}\.(scss|css|sass)(\"|\')`)
63
- const jsxRegex = /\.(tsx|jsx)$/
99
+ const scopedCssRegex = options.styleFileSuffix
100
+ ? new RegExp(`\.${options.styleFileSuffix}\.(${options.styleFileExtensions.join('|')})$`)
101
+ : new RegExp(`\.(${options.styleFileExtensions.join('|')})$`)
102
+ const scopedCssInFileRegex = options.styleFileSuffix
103
+ ? new RegExp(`\.${options.styleFileSuffix}\.(${options.styleFileExtensions.join('|')})(\"|\')`)
104
+ : new RegExp(`\.(${options.styleFileExtensions.join('|')})(\"|\')`)
105
+ const jsxRegex = new RegExp(`\.(${options.jsxFileExtensions.join('|')})$`)
64
106
 
65
107
  return [
66
108
  {
@@ -94,7 +136,7 @@ export default function reactScopedCssPlugin(optionsIn:ReactScopedCssPluginOptio
94
136
 
95
137
  if (scopedCssRegex.test(getFilenameFromPath(id))) {
96
138
  const importerHash = getHashFromPath(id);
97
- return addAttributesToCss(code, getFilenameFromPath(id), importerHash, options.hashPrefix)
139
+ return addAttributesToCss(code, getFilenameFromPath(id), `data-${options.hashPrefix}-${importerHash}`)
98
140
  }
99
141
  }
100
142
  }