UNPKG

26.3 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.loader = loader;
7exports.render = render;
8
9var _crypto = require('crypto');
10
11var _invariant = require('invariant');
12
13var _invariant2 = _interopRequireDefault(_invariant);
14
15var _loaderUtils = require('loader-utils');
16
17var LoaderUtils = _interopRequireWildcard(_loaderUtils);
18
19var _babelTypes = require('babel-types');
20
21var types = _interopRequireWildcard(_babelTypes);
22
23var _babelGenerator = require('babel-generator');
24
25var _babelGenerator2 = _interopRequireDefault(_babelGenerator);
26
27var _babelTraverse = require('babel-traverse');
28
29var _babelTraverse2 = _interopRequireDefault(_babelTraverse);
30
31var _api = require('babel-plugin-ast-literal/api');
32
33var _babylon = require('babylon');
34
35var _postcss = require('postcss');
36
37var postcss = _interopRequireWildcard(_postcss);
38
39var _postcssSelectorParser = require('postcss-selector-parser');
40
41var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser);
42
43var _HTMLTagList = require('./HTMLTagList');
44
45var _HTMLTagList2 = _interopRequireDefault(_HTMLTagList);
46
47var _CSSPseudoClassList = require('./CSSPseudoClassList');
48
49var _CSSPseudoClassList2 = _interopRequireDefault(_CSSPseudoClassList);
50
51var _ComponentRef = require('./ComponentRef');
52
53var ComponentRef = _interopRequireWildcard(_ComponentRef);
54
55function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
56
57function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
58
59var __liftToAST = require('babel-plugin-ast-literal/lib/liftToAST').default; /**
60 * @copyright 2016-present, React CSS Components team
61 *
62 */
63
64var LOADER = require.resolve('../webpack');
65
66var COMPONENT_RE = /^[A-Z][a-zA-Z_0-9]*$/;
67
68var PROP_VARIANT_NAME = ':prop';
69
70function hash(value) {
71 var hasher = (0, _crypto.createHash)('md5');
72 hasher.update(value);
73 return hasher.digest('hex');
74}
75
76function parseSelector(selector) {
77 var parser = (0, _postcssSelectorParser2.default)();
78 return parser.process(selector).res;
79}
80
81function isPropReference(path) {
82 if (!types.isIdentifier(path.node)) {
83 return false;
84 }
85 if (path.node.__seen) {
86 return false;
87 }
88 if (path.scope.parent !== undefined) {
89 return false;
90 }
91 if (types.isMemberExpression(path.parentPath.node)) {
92 while (types.isMemberExpression(path.parentPath.node)) {
93 if (path.node === path.parentPath.node.property) {
94 return false;
95 }
96 path = path.parentPath;
97 }
98 }
99 return true;
100}
101
102function parsePropVariantExpression(expression) {
103 var node = (0, _babylon.parse)(expression);
104 (0, _babelTraverse2.default)(node, {
105 enter: function enter(path) {
106 if (isPropReference(path)) {
107 var nextNode = function (_param) {
108 _param = __liftToAST(_param);
109 return {
110 "type": "MemberExpression",
111 "object": {
112 "type": "Identifier",
113 "name": "props"
114 },
115 "property": _param,
116 "computed": false,
117 "extra": {
118 "parenthesized": true,
119 "parenStart": 0
120 }
121 };
122 }(path.node);
123 nextNode.object.__seen = true;
124 path.replaceWith(nextNode);
125 }
126 }
127 });
128 node = node.program.body[0].expression;
129 return node;
130}
131
132function findComponentNames(node) {
133 var componentNames = [];
134 var selector = parseSelector(node.selector);
135 selector.eachTag(function (selector) {
136 if (COMPONENT_RE.exec(selector.value)) {
137 componentNames.push(selector.value);
138 }
139 });
140 return componentNames;
141}
142
143function findVariants(node) {
144 var variantNames = [];
145 var selector = parseSelector(node.selector);
146 selector.eachPseudo(function (selector) {
147 var expression = null;
148 var variantName = selector.value.slice(1);
149
150 if (selector.value === PROP_VARIANT_NAME) {
151 expression = node.selector.slice(selector.source.start.column + PROP_VARIANT_NAME.length, selector.source.end.column - 1);
152 variantName = variantName + '__' + hash(expression).slice(0, 6);
153 expression = parsePropVariantExpression(expression);
154 }
155
156 var idx = selector.parent.nodes.indexOf(selector);
157 var prev = selector.parent.nodes[idx - 1];
158 if (prev && prev.type === 'tag' && COMPONENT_RE.exec(prev.value)) {
159 variantNames.push({
160 componentName: prev.value,
161 variantName: variantName,
162 expression: expression
163 });
164 }
165 });
166 return variantNames;
167}
168
169function isPrimaryComponent(node) {
170 var selector = parseSelector(node.selector);
171 return selector.nodes.length === 1 && selector.nodes[0].type === 'selector' && selector.nodes[0].nodes.length === 1 && selector.nodes[0].nodes[0].type === 'tag' && COMPONENT_RE.exec(selector.nodes[0].nodes[0].value);
172}
173
174function renderToCSS(source) {
175 var root = postcss.parse(source);
176 root.walkRules(function (node) {
177 removeBaseDeclaration(node);
178 localizeComponentRule(node);
179 });
180 return root.toString();
181}
182
183function removeBaseDeclaration(node) {
184 node.walkDecls(function (node) {
185 if (node.prop === 'base') {
186 node.remove();
187 }
188 });
189}
190
191function localizeComponentRule(node) {
192 var componentNames = findComponentNames(node);
193 if (componentNames.length > 0) {
194 (function () {
195 var toClassify = [];
196 var toPseudoClassify = [];
197 var selector = parseSelector(node.selector);
198 selector.eachTag(function (selector) {
199 if (componentNames.indexOf(selector.value) > -1) {
200 toClassify.push(selector);
201 }
202 });
203 selector.eachPseudo(function (selector) {
204 var idx = selector.parent.nodes.indexOf(selector);
205 var prev = selector.parent.nodes[idx - 1];
206 if (prev && prev.type === 'tag' && componentNames.indexOf(prev.value) > -1) {
207 var _componentName = prev.value;
208 var _variantName = selector.value.slice(1);
209 if (_CSSPseudoClassList2.default[_variantName]) {
210 selector.parent.parent.append((0, _postcssSelectorParser.className)({ value: _componentName + '__' + _variantName }));
211 } else {
212 toPseudoClassify.push(selector);
213 }
214 }
215 });
216 toPseudoClassify.forEach(function (selector) {
217 var parent = selector.parent;
218 var idx = parent.nodes.indexOf(selector);
219 var prev = parent.nodes[idx - 1];
220 var componentName = prev.value;
221 var variantName = selector.value.slice(1);
222 if (selector.value === PROP_VARIANT_NAME) {
223 var _expression = node.selector.slice(selector.source.start.column + PROP_VARIANT_NAME.length, selector.source.end.column - 1);
224 variantName = variantName + '__' + hash(_expression).slice(0, 6);
225 }
226 var nextSelector = (0, _postcssSelectorParser.className)({ value: componentName + '__' + variantName });
227 prev.removeSelf();
228 selector.replaceWith(nextSelector);
229 });
230 toClassify.forEach(function (selector) {
231 var nextSelector = (0, _postcssSelectorParser.className)({ value: selector.value });
232 selector.replaceWith(nextSelector);
233 });
234 var nextNode = node.clone();
235 nextNode.selector = selector.toString();
236 node.replaceWith(nextNode);
237 })();
238 }
239}
240
241function renderToJS(source, config) {
242 var root = postcss.parse(source);
243
244 var imports = function (_param2) {
245 _param2 = __liftToAST(_param2);
246 return [{
247 "type": "VariableDeclaration",
248 "declarations": [{
249 "type": "VariableDeclarator",
250 "id": {
251 "type": "Identifier",
252 "name": "React"
253 },
254 "init": {
255 "type": "CallExpression",
256 "callee": {
257 "type": "Identifier",
258 "name": "require"
259 },
260 "arguments": [{
261 "type": "StringLiteral",
262 "extra": {
263 "rawValue": "react",
264 "raw": "\"react\""
265 },
266 "value": "react"
267 }]
268 }
269 }],
270 "kind": "var"
271 }, {
272 "type": "VariableDeclaration",
273 "declarations": [{
274 "type": "VariableDeclarator",
275 "id": {
276 "type": "Identifier",
277 "name": "styles"
278 },
279 "init": {
280 "type": "CallExpression",
281 "callee": {
282 "type": "Identifier",
283 "name": "require"
284 },
285 "arguments": [_param2]
286 }
287 }],
288 "kind": "var"
289 }];
290 }(config.loadCSS);
291 var statements = [];
292 var components = {};
293
294 statements.push(function () {
295 return {
296 "type": "FunctionDeclaration",
297 "id": {
298 "type": "Identifier",
299 "name": "reconcileProps"
300 },
301 "generator": false,
302 "expression": false,
303 "async": false,
304 "params": [{
305 "type": "Identifier",
306 "name": "props"
307 }, {
308 "type": "Identifier",
309 "name": "className"
310 }],
311 "body": {
312 "type": "BlockStatement",
313 "body": [{
314 "type": "VariableDeclaration",
315 "declarations": [{
316 "type": "VariableDeclarator",
317 "id": {
318 "type": "Identifier",
319 "name": "nextProps"
320 },
321 "init": {
322 "type": "ObjectExpression",
323 "properties": []
324 }
325 }],
326 "kind": "var"
327 }, {
328 "type": "ForInStatement",
329 "left": {
330 "type": "VariableDeclaration",
331 "declarations": [{
332 "type": "VariableDeclarator",
333 "id": {
334 "type": "Identifier",
335 "name": "k"
336 },
337 "init": null
338 }],
339 "kind": "var"
340 },
341 "right": {
342 "type": "Identifier",
343 "name": "props"
344 },
345 "body": {
346 "type": "BlockStatement",
347 "body": [{
348 "type": "IfStatement",
349 "test": {
350 "type": "BinaryExpression",
351 "left": {
352 "type": "Identifier",
353 "name": "k"
354 },
355 "operator": "===",
356 "right": {
357 "type": "StringLiteral",
358 "extra": {
359 "rawValue": "variant",
360 "raw": "'variant'"
361 },
362 "value": "variant"
363 }
364 },
365 "consequent": {
366 "type": "BlockStatement",
367 "body": [{
368 "type": "ContinueStatement",
369 "label": null
370 }],
371 "directives": []
372 },
373 "alternate": null
374 }, {
375 "type": "IfStatement",
376 "test": {
377 "type": "CallExpression",
378 "callee": {
379 "type": "MemberExpression",
380 "object": {
381 "type": "Identifier",
382 "name": "props"
383 },
384 "property": {
385 "type": "Identifier",
386 "name": "hasOwnProperty"
387 },
388 "computed": false
389 },
390 "arguments": [{
391 "type": "Identifier",
392 "name": "k"
393 }]
394 },
395 "consequent": {
396 "type": "BlockStatement",
397 "body": [{
398 "type": "ExpressionStatement",
399 "expression": {
400 "type": "AssignmentExpression",
401 "operator": "=",
402 "left": {
403 "type": "MemberExpression",
404 "object": {
405 "type": "Identifier",
406 "name": "nextProps"
407 },
408 "property": {
409 "type": "Identifier",
410 "name": "k"
411 },
412 "computed": true
413 },
414 "right": {
415 "type": "MemberExpression",
416 "object": {
417 "type": "Identifier",
418 "name": "props"
419 },
420 "property": {
421 "type": "Identifier",
422 "name": "k"
423 },
424 "computed": true
425 }
426 }
427 }],
428 "directives": []
429 },
430 "alternate": null
431 }],
432 "directives": []
433 }
434 }, {
435 "type": "ExpressionStatement",
436 "expression": {
437 "type": "AssignmentExpression",
438 "operator": "=",
439 "left": {
440 "type": "MemberExpression",
441 "object": {
442 "type": "Identifier",
443 "name": "nextProps"
444 },
445 "property": {
446 "type": "Identifier",
447 "name": "className"
448 },
449 "computed": false
450 },
451 "right": {
452 "type": "Identifier",
453 "name": "className"
454 }
455 }
456 }, {
457 "type": "ReturnStatement",
458 "argument": {
459 "type": "Identifier",
460 "name": "nextProps"
461 }
462 }],
463 "directives": []
464 }
465 };
466 }());
467
468 function registerComponent(componentName) {
469 if (components[componentName] === undefined) {
470 components[componentName] = {
471 base: (0, _babelTypes.stringLiteral)('div'),
472 variants: {}
473 };
474 }
475 }
476
477 function registerComponentVariants(_ref) {
478 var componentName = _ref.componentName;
479 var variantName = _ref.variantName;
480 var expression = _ref.expression;
481
482 (0, _invariant2.default)(components[componentName], 'Trying to configure base for an unknown component %s', componentName);
483 components[componentName].variants[variantName] = { expression: expression };
484 }
485
486 function configureComponentBase(componentName, base) {
487 (0, _invariant2.default)(components[componentName], 'Trying to configure base for an unknown component %s', componentName);
488
489 if (_HTMLTagList2.default[base]) {
490 base = (0, _babelTypes.stringLiteral)(base);
491 } else {
492 var ref = ComponentRef.parse(base);
493 (0, _invariant2.default)(ref != null, 'Found invalid component ref: %s', base);
494 base = (0, _babelTypes.identifier)(componentName + '__Base');
495 imports.push(function (_param3, _param4, _param5) {
496 _param3 = __liftToAST(_param3);
497 _param4 = __liftToAST(_param4);
498 _param5 = __liftToAST(_param5);
499 return {
500 "type": "VariableDeclaration",
501 "declarations": [{
502 "type": "VariableDeclarator",
503 "id": _param3,
504 "init": {
505 "type": "MemberExpression",
506 "object": {
507 "type": "CallExpression",
508 "callee": {
509 "type": "Identifier",
510 "name": "require"
511 },
512 "arguments": [_param4]
513 },
514 "property": _param5,
515 "computed": false
516 }
517 }],
518 "kind": "var"
519 };
520 }(base, ref.source, (0, _babelTypes.identifier)(ref.name)));
521 }
522
523 components[componentName].base = base;
524 }
525
526 // walk CSS AST and register all component configurations
527 root.walkRules(function (node) {
528 var componentNames = findComponentNames(node);
529 if (componentNames.length === 0) {
530 return;
531 }
532
533 componentNames.forEach(function (componentName) {
534 registerComponent(componentName);
535 });
536
537 if (isPrimaryComponent(node)) {
538 (function () {
539 var componentName = componentNames[0];
540 node.walkDecls(function (decl) {
541 if (decl.prop === 'base') {
542 configureComponentBase(componentName, decl.value);
543 }
544 });
545 })();
546 }
547
548 var variants = findVariants(node);
549 for (var i = 0; i < variants.length; i++) {
550 var variant = variants[i];
551 registerComponentVariants(variant);
552 }
553 });
554
555 // generate JS code from component configurations
556 for (var _componentName2 in components) {
557 var component = components[_componentName2];
558 if (components.hasOwnProperty(_componentName2)) {
559 var _className = function (_param6) {
560 _param6 = __liftToAST(_param6);
561 return {
562 "type": "MemberExpression",
563 "object": {
564 "type": "Identifier",
565 "name": "styles"
566 },
567 "property": _param6,
568 "computed": false,
569 "extra": {
570 "parenthesized": true,
571 "parenStart": 0
572 }
573 };
574 }((0, _babelTypes.identifier)(_componentName2));
575 for (var _variantName2 in component.variants) {
576 var variant = component.variants[_variantName2];
577 if (variant.expression) {
578 _className = function (_param7, _param8, _param9) {
579 _param7 = __liftToAST(_param7);
580 _param8 = __liftToAST(_param8);
581 _param9 = __liftToAST(_param9);
582 return {
583 "type": "BinaryExpression",
584 "left": _param7,
585 "operator": "+",
586 "right": {
587 "type": "ConditionalExpression",
588 "test": _param8,
589 "consequent": {
590 "type": "BinaryExpression",
591 "left": {
592 "type": "StringLiteral",
593 "extra": {
594 "rawValue": " ",
595 "raw": "' '"
596 },
597 "value": " "
598 },
599 "operator": "+",
600 "right": {
601 "type": "MemberExpression",
602 "object": {
603 "type": "Identifier",
604 "name": "styles"
605 },
606 "property": _param9,
607 "computed": false
608 }
609 },
610 "alternate": {
611 "type": "StringLiteral",
612 "extra": {
613 "rawValue": "",
614 "raw": "''"
615 },
616 "value": ""
617 },
618 "extra": {
619 "parenthesized": true,
620 "parenStart": 24
621 }
622 },
623 "extra": {
624 "parenthesized": true,
625 "parenStart": 0
626 }
627 };
628 }(_className, variant.expression, (0, _babelTypes.identifier)(_componentName2 + '__' + _variantName2));
629 } else {
630 _className = function (_param10, _param11, _param12) {
631 _param10 = __liftToAST(_param10);
632 _param11 = __liftToAST(_param11);
633 _param12 = __liftToAST(_param12);
634 return {
635 "type": "BinaryExpression",
636 "left": _param10,
637 "operator": "+",
638 "right": {
639 "type": "ConditionalExpression",
640 "test": {
641 "type": "MemberExpression",
642 "object": {
643 "type": "Identifier",
644 "name": "variant"
645 },
646 "property": _param11,
647 "computed": false
648 },
649 "consequent": {
650 "type": "BinaryExpression",
651 "left": {
652 "type": "StringLiteral",
653 "extra": {
654 "rawValue": " ",
655 "raw": "' '"
656 },
657 "value": " "
658 },
659 "operator": "+",
660 "right": {
661 "type": "MemberExpression",
662 "object": {
663 "type": "Identifier",
664 "name": "styles"
665 },
666 "property": _param12,
667 "computed": false
668 }
669 },
670 "alternate": {
671 "type": "StringLiteral",
672 "extra": {
673 "rawValue": "",
674 "raw": "''"
675 },
676 "value": ""
677 },
678 "extra": {
679 "parenthesized": true,
680 "parenStart": 25
681 }
682 },
683 "extra": {
684 "parenthesized": true,
685 "parenStart": 0
686 }
687 };
688 }(_className, (0, _babelTypes.identifier)(_variantName2), (0, _babelTypes.identifier)(_componentName2 + '__' + _variantName2));
689 }
690 }
691 statements.push(function (_param13, _param14, _param15, _param16) {
692 _param13 = __liftToAST(_param13);
693 _param14 = __liftToAST(_param14);
694 _param15 = __liftToAST(_param15);
695 _param16 = __liftToAST(_param16);
696 return {
697 "type": "ExpressionStatement",
698 "expression": {
699 "type": "AssignmentExpression",
700 "operator": "=",
701 "left": {
702 "type": "MemberExpression",
703 "object": {
704 "type": "MemberExpression",
705 "object": {
706 "type": "Identifier",
707 "name": "module"
708 },
709 "property": {
710 "type": "Identifier",
711 "name": "exports"
712 },
713 "computed": false
714 },
715 "property": _param13,
716 "computed": false
717 },
718 "right": {
719 "type": "FunctionExpression",
720 "id": _param14,
721 "generator": false,
722 "expression": false,
723 "async": false,
724 "params": [{
725 "type": "Identifier",
726 "name": "props"
727 }],
728 "body": {
729 "type": "BlockStatement",
730 "body": [{
731 "type": "VariableDeclaration",
732 "declarations": [{
733 "type": "VariableDeclarator",
734 "id": {
735 "type": "Identifier",
736 "name": "variant"
737 },
738 "init": {
739 "type": "LogicalExpression",
740 "left": {
741 "type": "MemberExpression",
742 "object": {
743 "type": "Identifier",
744 "name": "props"
745 },
746 "property": {
747 "type": "Identifier",
748 "name": "variant"
749 },
750 "computed": false
751 },
752 "operator": "||",
753 "right": {
754 "type": "ObjectExpression",
755 "properties": []
756 }
757 }
758 }],
759 "kind": "var"
760 }, {
761 "type": "VariableDeclaration",
762 "declarations": [{
763 "type": "VariableDeclarator",
764 "id": {
765 "type": "Identifier",
766 "name": "className"
767 },
768 "init": _param15
769 }],
770 "kind": "var"
771 }, {
772 "type": "ReturnStatement",
773 "argument": {
774 "type": "CallExpression",
775 "callee": {
776 "type": "MemberExpression",
777 "object": {
778 "type": "Identifier",
779 "name": "React"
780 },
781 "property": {
782 "type": "Identifier",
783 "name": "createElement"
784 },
785 "computed": false
786 },
787 "arguments": [_param16, {
788 "type": "CallExpression",
789 "callee": {
790 "type": "Identifier",
791 "name": "reconcileProps"
792 },
793 "arguments": [{
794 "type": "Identifier",
795 "name": "props"
796 }, {
797 "type": "Identifier",
798 "name": "className"
799 }]
800 }]
801 }
802 }],
803 "directives": []
804 }
805 }
806 }
807 };
808 }((0, _babelTypes.identifier)(_componentName2), (0, _babelTypes.identifier)(_componentName2), _className, component.base));
809 }
810 }
811
812 return (0, _babelGenerator2.default)((0, _babelTypes.program)(imports.concat(statements))).code;
813}
814
815/**
816 * Webpack loader for React CSS component modules.
817 */
818function loader(source) {
819 this.cacheable();
820 var query = LoaderUtils.parseQuery(this.query);
821 if (query.css) {
822 var result = renderToCSS(source);
823 return result;
824 } else {
825 var _loadCSS = query.loadCSS ? query.loadCSS : ['style-loader', 'css-loader?modules'];
826 var _result = renderToJS(source, {
827 loadCSS: '!!' + _loadCSS.join('!') + '!' + LOADER + '?css!' + this.resource
828 });
829 return _result;
830 }
831}
832
833/**
834 * Render React CSS component module into JS and CSS sources.
835 */
836function render(source, config) {
837 var js = renderToJS(source, { loadCSS: config.loadCSS });
838 var css = renderToCSS(source);
839 return { js: js, css: css };
840}