UNPKG

20.4 kBJavaScriptView Raw
1"use strict";
2
3exports.__esModule = true;
4exports.log = log;
5exports.setStateOptions = exports.createReactComponentImportDeclaration = exports.booleanOption = exports.processCss = exports.combinePlugins = exports.addSourceMaps = exports.makeSourceMapGenerator = exports.makeStyledJsxTag = exports.cssToBabelType = exports.templateLiteralFromPreprocessedCss = exports.computeClassNames = exports.getJSXStyleInfo = exports.validateExternalExpressions = exports.findStyles = exports.isStyledJsx = exports.isGlobalEl = exports.getScope = exports.addClassName = exports.hashString = void 0;
6
7var _path = _interopRequireDefault(require("path"));
8
9var _helperModuleImports = require("@babel/helper-module-imports");
10
11var t = _interopRequireWildcard(require("@babel/types"));
12
13var _stringHash = _interopRequireDefault(require("string-hash"));
14
15var _sourceMap = require("source-map");
16
17var _convertSourceMap = _interopRequireDefault(require("convert-source-map"));
18
19var _styleTransform = _interopRequireDefault(require("./lib/style-transform"));
20
21var _constants = require("./_constants");
22
23function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
24
25function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
26
27function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
28
29function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
30
31var concat = function concat(a, b) {
32 return t.binaryExpression('+', a, b);
33};
34
35var and = function and(a, b) {
36 return t.logicalExpression('&&', a, b);
37};
38
39var or = function or(a, b) {
40 return t.logicalExpression('||', a, b);
41};
42
43var joinSpreads = function joinSpreads(spreads) {
44 return spreads.reduce(function (acc, curr) {
45 return or(acc, curr);
46 });
47};
48
49var hashString = function hashString(str) {
50 return String((0, _stringHash["default"])(str));
51};
52
53exports.hashString = hashString;
54
55var addClassName = function addClassName(path, jsxId) {
56 var jsxIdWithSpace = concat(jsxId, t.stringLiteral(' '));
57 var attributes = path.get('attributes');
58 var spreads = [];
59 var className = null; // Find className and collect spreads
60
61 for (var i = attributes.length - 1, attr; attr = attributes[i]; i--) {
62 var node = attr.node;
63
64 if (t.isJSXSpreadAttribute(attr)) {
65 if (t.isObjectExpression(node.argument)) {
66 var properties = node.argument.properties;
67 var index = properties.findIndex(function (property) {
68 return property.key.name === 'className';
69 });
70
71 if (~index) {
72 className = attr.get('argument').get("properties." + index); // Remove jsx spread attribute if there is only className property
73
74 if (properties.length === 1) {
75 attr.remove();
76 }
77
78 break;
79 }
80 }
81
82 if (t.isMemberExpression(node.argument) || t.isIdentifier(node.argument)) {
83 var name = node.argument.name;
84 var spreadObj = t.isMemberExpression(node.argument) ? node.argument : t.identifier(name);
85 var attrNameDotClassName = t.memberExpression(spreadObj, t.identifier('className'));
86 spreads.push( // `${name} && ${name}.className != null && ${name}.className`
87 and(spreadObj, and(t.binaryExpression('!=', attrNameDotClassName, t.nullLiteral()), attrNameDotClassName)));
88 }
89
90 continue;
91 }
92
93 if (t.isJSXAttribute(attr) && node.name.name === 'className') {
94 className = attributes[i]; // found className break the loop
95
96 break;
97 }
98 }
99
100 if (className) {
101 var newClassName = className.node.value.expression || className.node.value;
102 newClassName = t.isStringLiteral(newClassName) || t.isTemplateLiteral(newClassName) ? newClassName : or(newClassName, t.stringLiteral(''));
103 className.remove();
104 className = t.jSXExpressionContainer(spreads.length === 0 ? concat(jsxIdWithSpace, newClassName) : concat(jsxIdWithSpace, or(joinSpreads(spreads), newClassName)));
105 } else {
106 className = t.jSXExpressionContainer(spreads.length === 0 ? jsxId : concat(jsxIdWithSpace, or(joinSpreads(spreads), t.stringLiteral(''))));
107 }
108
109 path.node.attributes.push(t.jSXAttribute(t.jSXIdentifier('className'), className));
110};
111
112exports.addClassName = addClassName;
113
114var getScope = function getScope(path) {
115 return (path.findParent(function (path) {
116 return path.isFunctionDeclaration() || path.isArrowFunctionExpression() || path.isClassMethod();
117 }) || path).scope;
118};
119
120exports.getScope = getScope;
121
122var isGlobalEl = function isGlobalEl(el) {
123 return el && el.attributes.some(function (_ref) {
124 var name = _ref.name;
125 return name && name.name === _constants.GLOBAL_ATTRIBUTE;
126 });
127};
128
129exports.isGlobalEl = isGlobalEl;
130
131var isStyledJsx = function isStyledJsx(_ref2) {
132 var el = _ref2.node;
133 return t.isJSXElement(el) && el.openingElement.name.name === 'style' && el.openingElement.attributes.some(function (attr) {
134 return attr.name.name === _constants.STYLE_ATTRIBUTE;
135 });
136};
137
138exports.isStyledJsx = isStyledJsx;
139
140var findStyles = function findStyles(path) {
141 if (isStyledJsx(path)) {
142 var node = path.node;
143 return isGlobalEl(node.openingElement) ? [path] : [];
144 }
145
146 return path.get('children').filter(isStyledJsx);
147};
148
149exports.findStyles = findStyles;
150var validateExternalExpressionsVisitor = {
151 Identifier: function Identifier(path) {
152 if (t.isMemberExpression(path.parentPath)) {
153 return;
154 }
155
156 var name = path.node.name;
157
158 if (!path.scope.hasBinding(name)) {
159 throw path.buildCodeFrameError(path.getSource());
160 }
161 },
162 MemberExpression: function MemberExpression(path) {
163 var node = path.node;
164
165 if (!t.isIdentifier(node.object)) {
166 return;
167 }
168
169 if (!path.scope.hasBinding(node.object.name)) {
170 throw path.buildCodeFrameError(path.getSource());
171 }
172 },
173 ThisExpression: function ThisExpression(path) {
174 throw new Error(path.parentPath.getSource());
175 }
176};
177
178var validateExternalExpressions = function validateExternalExpressions(path) {
179 try {
180 path.traverse(validateExternalExpressionsVisitor);
181 } catch (error) {
182 throw path.buildCodeFrameError("\n Found an `undefined` or invalid value in your styles: `" + error.message + "`.\n\n If you are trying to use dynamic styles in external files this is unfortunately not possible yet.\n Please put the dynamic parts alongside the component. E.g.\n\n <button>\n <style jsx>{externalStylesReference}</style>\n <style jsx>{`\n button { background-color: ${" + error.message + "} }\n `}</style>\n </button>\n ");
183 }
184};
185
186exports.validateExternalExpressions = validateExternalExpressions;
187
188var getJSXStyleInfo = function getJSXStyleInfo(expr, scope) {
189 var node = expr.node;
190 var location = node.loc; // Assume string literal
191
192 if (t.isStringLiteral(node)) {
193 return {
194 hash: hashString(node.value),
195 css: node.value,
196 expressions: [],
197 dynamic: false,
198 location: location
199 };
200 } // Simple template literal without expressions
201
202
203 if (node.expressions.length === 0) {
204 return {
205 hash: hashString(node.quasis[0].value.raw),
206 css: node.quasis[0].value.raw,
207 expressions: [],
208 dynamic: false,
209 location: location
210 };
211 } // Special treatment for template literals that contain expressions:
212 //
213 // Expressions are replaced with a placeholder
214 // so that the CSS compiler can parse and
215 // transform the css source string
216 // without having to know about js literal expressions.
217 // Later expressions are restored.
218 //
219 // e.g.
220 // p { color: ${myConstant}; }
221 // becomes
222 // p { color: %%styled-jsx-placeholder-${id}%%; }
223
224
225 var quasis = node.quasis,
226 expressions = node.expressions;
227 var hash = hashString(expr.getSource().slice(1, -1));
228 var dynamic = Boolean(scope);
229
230 if (dynamic) {
231 try {
232 var val = expr.evaluate();
233
234 if (val.confident) {
235 dynamic = false;
236 } else if (val.deopt) {
237 var computedObject = val.deopt.get('object').resolve().evaluate();
238 dynamic = !computedObject.confident;
239 }
240 } catch (_) {}
241 }
242
243 var css = quasis.reduce(function (css, quasi, index) {
244 return "" + css + quasi.value.raw + (quasis.length === index + 1 ? '' : "%%styled-jsx-placeholder-" + index + "%%");
245 }, '');
246 return {
247 hash: hash,
248 css: css,
249 expressions: expressions,
250 dynamic: dynamic,
251 location: location
252 };
253};
254
255exports.getJSXStyleInfo = getJSXStyleInfo;
256
257var computeClassNames = function computeClassNames(styles, externalJsxId) {
258 if (styles.length === 0) {
259 return {
260 className: externalJsxId
261 };
262 }
263
264 var hashes = styles.reduce(function (acc, styles) {
265 if (styles.dynamic === false) {
266 acc["static"].push(styles.hash);
267 } else {
268 acc.dynamic.push(styles);
269 }
270
271 return acc;
272 }, {
273 "static": [],
274 dynamic: []
275 });
276 var staticClassName = "jsx-" + hashString(hashes["static"].join(',')); // Static and optionally external classes. E.g.
277 // '[jsx-externalClasses] jsx-staticClasses'
278
279 if (hashes.dynamic.length === 0) {
280 return {
281 staticClassName: staticClassName,
282 className: externalJsxId ? concat(t.stringLiteral(staticClassName + ' '), externalJsxId) : t.stringLiteral(staticClassName)
283 };
284 } // _JSXStyle.dynamic([ ['1234', [props.foo, bar, fn(props)]], ... ])
285
286
287 var dynamic = t.callExpression( // Callee: _JSXStyle.dynamic
288 t.memberExpression(t.identifier(_constants.STYLE_COMPONENT), t.identifier('dynamic')), // Arguments
289 [t.arrayExpression(hashes.dynamic.map(function (styles) {
290 return t.arrayExpression([t.stringLiteral(hashString(styles.hash + staticClassName)), t.arrayExpression(styles.expressions)]);
291 }))]); // Dynamic and optionally external classes. E.g.
292 // '[jsx-externalClasses] ' + _JSXStyle.dynamic([ ['1234', [props.foo, bar, fn(props)]], ... ])
293
294 if (hashes["static"].length === 0) {
295 return {
296 staticClassName: staticClassName,
297 className: externalJsxId ? concat(concat(externalJsxId, t.stringLiteral(' ')), dynamic) : dynamic
298 };
299 } // Static, dynamic and optionally external classes. E.g.
300 // '[jsx-externalClasses] jsx-staticClasses ' + _JSXStyle.dynamic([ ['5678', [props.foo, bar, fn(props)]], ... ])
301
302
303 return {
304 staticClassName: staticClassName,
305 className: externalJsxId ? concat(concat(externalJsxId, t.stringLiteral(" " + staticClassName + " ")), dynamic) : concat(t.stringLiteral(staticClassName + " "), dynamic)
306 };
307};
308
309exports.computeClassNames = computeClassNames;
310
311var templateLiteralFromPreprocessedCss = function templateLiteralFromPreprocessedCss(css, expressions) {
312 var quasis = [];
313 var finalExpressions = [];
314 var parts = css.split(/(?:%%styled-jsx-placeholder-(\d+)%%)/g);
315
316 if (parts.length === 1) {
317 return t.stringLiteral(css);
318 }
319
320 parts.forEach(function (part, index) {
321 if (index % 2 > 0) {
322 // This is necessary because, after preprocessing, declarations might have been alterate.
323 // eg. properties are auto prefixed and therefore expressions need to match.
324 finalExpressions.push(expressions[part]);
325 } else {
326 quasis.push(part);
327 }
328 });
329 return t.templateLiteral(quasis.map(function (quasi, index) {
330 return t.templateElement({
331 raw: quasi,
332 cooked: quasi
333 }, quasis.length === index + 1);
334 }), finalExpressions);
335};
336
337exports.templateLiteralFromPreprocessedCss = templateLiteralFromPreprocessedCss;
338
339var cssToBabelType = function cssToBabelType(css) {
340 if (typeof css === 'string') {
341 return t.stringLiteral(css);
342 }
343
344 if (Array.isArray(css)) {
345 return t.arrayExpression(css);
346 }
347
348 return t.cloneDeep(css);
349};
350
351exports.cssToBabelType = cssToBabelType;
352
353var makeStyledJsxTag = function makeStyledJsxTag(id, transformedCss, expressions) {
354 if (expressions === void 0) {
355 expressions = [];
356 }
357
358 var css = cssToBabelType(transformedCss);
359 var attributes = [t.jSXAttribute(t.jSXIdentifier(_constants.STYLE_COMPONENT_ID), t.jSXExpressionContainer(typeof id === 'string' ? t.stringLiteral(id) : id))];
360
361 if (expressions.length > 0) {
362 attributes.push(t.jSXAttribute(t.jSXIdentifier(_constants.STYLE_COMPONENT_DYNAMIC), t.jSXExpressionContainer(t.arrayExpression(expressions))));
363 }
364
365 return t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier(_constants.STYLE_COMPONENT), attributes), t.jSXClosingElement(t.jSXIdentifier(_constants.STYLE_COMPONENT)), [t.jSXExpressionContainer(css)]);
366};
367
368exports.makeStyledJsxTag = makeStyledJsxTag;
369
370var makeSourceMapGenerator = function makeSourceMapGenerator(file) {
371 var filename = file.sourceFileName;
372 var generator = new _sourceMap.SourceMapGenerator({
373 file: filename,
374 sourceRoot: file.sourceRoot
375 });
376 generator.setSourceContent(filename, file.code);
377 return generator;
378};
379
380exports.makeSourceMapGenerator = makeSourceMapGenerator;
381
382var addSourceMaps = function addSourceMaps(code, generator, filename) {
383 var sourceMaps = [_convertSourceMap["default"].fromObject(generator).toComment({
384 multiline: true
385 }), "/*@ sourceURL=" + filename.replace(/\\/g, '\\\\') + " */"];
386
387 if (Array.isArray(code)) {
388 return code.concat(sourceMaps);
389 }
390
391 return [code].concat(sourceMaps).join('\n');
392};
393
394exports.addSourceMaps = addSourceMaps;
395var combinedPluginsCache = {
396 plugins: null,
397 combined: null
398};
399
400var combinePlugins = function combinePlugins(plugins) {
401 if (!plugins) {
402 return function (css) {
403 return css;
404 };
405 }
406
407 var pluginsToString = JSON.stringify(plugins);
408
409 if (combinedPluginsCache.plugins === pluginsToString) {
410 return combinedPluginsCache.combined;
411 }
412
413 if (!Array.isArray(plugins) || plugins.some(function (p) {
414 return !Array.isArray(p) && typeof p !== 'string';
415 })) {
416 throw new Error('`plugins` must be an array of plugins names (string) or an array `[plugin-name, {options}]`');
417 }
418
419 combinedPluginsCache.plugins = pluginsToString;
420 combinedPluginsCache.combined = plugins.map(function (plugin, i) {
421 var options = {};
422
423 if (Array.isArray(plugin)) {
424 options = plugin[1] || {};
425 plugin = plugin[0];
426
427 if (Object.prototype.hasOwnProperty.call(options, 'babel')) {
428 throw new Error("\n Error while trying to register the styled-jsx plugin: " + plugin + "\n The option name `babel` is reserved.\n ");
429 }
430 }
431
432 log('Loading plugin from path: ' + plugin);
433
434 var p = require(plugin);
435
436 if (p["default"]) {
437 p = p["default"];
438 }
439
440 var type = typeof p;
441
442 if (type !== 'function') {
443 throw new Error("Expected plugin " + plugins[i] + " to be a function but instead got " + type);
444 }
445
446 return {
447 plugin: p,
448 options: options
449 };
450 }).reduce(function (previous, _ref3) {
451 var plugin = _ref3.plugin,
452 options = _ref3.options;
453 return function (css, babelOptions) {
454 return plugin(previous ? previous(css, babelOptions) : css, _extends({}, options, {
455 babel: babelOptions
456 }));
457 };
458 }, null);
459 return combinedPluginsCache.combined;
460};
461
462exports.combinePlugins = combinePlugins;
463
464var getPrefix = function getPrefix(isDynamic, id) {
465 return isDynamic ? '.__jsx-style-dynamic-selector' : "." + id;
466};
467
468var processCss = function processCss(stylesInfo, options) {
469 var hash = stylesInfo.hash,
470 css = stylesInfo.css,
471 expressions = stylesInfo.expressions,
472 dynamic = stylesInfo.dynamic,
473 location = stylesInfo.location,
474 file = stylesInfo.file,
475 isGlobal = stylesInfo.isGlobal,
476 plugins = stylesInfo.plugins,
477 vendorPrefixes = stylesInfo.vendorPrefixes,
478 sourceMaps = stylesInfo.sourceMaps;
479 var fileInfo = {
480 code: file.code,
481 sourceRoot: file.opts.sourceRoot,
482 filename: file.opts.filename || file.filename
483 };
484 fileInfo.sourceFileName = file.opts.sourceFileName || file.sourceFileName || // According to https://babeljs.io/docs/en/options#source-map-options
485 // filenameRelative = path.relative(file.opts.cwd, file.opts.filename)
486 // sourceFileName = path.basename(filenameRelative)
487 // or simply
488 // sourceFileName = path.basename(file.opts.filename)
489 fileInfo.filename && _path["default"].basename(fileInfo.filename);
490 var staticClassName = stylesInfo.staticClassName || "jsx-" + hashString(hash);
491 var splitRules = options.splitRules;
492 var useSourceMaps = Boolean(sourceMaps) && !splitRules;
493 var pluginsOptions = {
494 location: {
495 start: _extends({}, location.start),
496 end: _extends({}, location.end)
497 },
498 vendorPrefixes: vendorPrefixes,
499 sourceMaps: useSourceMaps,
500 isGlobal: isGlobal,
501 filename: fileInfo.filename
502 };
503 var transformedCss;
504
505 if (useSourceMaps) {
506 var generator = makeSourceMapGenerator(fileInfo);
507 var filename = fileInfo.sourceFileName;
508 transformedCss = addSourceMaps((0, _styleTransform["default"])(isGlobal ? '' : getPrefix(dynamic, staticClassName), plugins(css, pluginsOptions), {
509 generator: generator,
510 offset: location.start,
511 filename: filename,
512 splitRules: splitRules,
513 vendorPrefixes: vendorPrefixes
514 }), generator, filename);
515 } else {
516 transformedCss = (0, _styleTransform["default"])(isGlobal ? '' : getPrefix(dynamic, staticClassName), plugins(css, pluginsOptions), {
517 splitRules: splitRules,
518 vendorPrefixes: vendorPrefixes
519 });
520 }
521
522 if (expressions.length > 0) {
523 if (typeof transformedCss === 'string') {
524 transformedCss = templateLiteralFromPreprocessedCss(transformedCss, expressions);
525 } else {
526 transformedCss = transformedCss.map(function (transformedCss) {
527 return templateLiteralFromPreprocessedCss(transformedCss, expressions);
528 });
529 }
530 } else if (Array.isArray(transformedCss)) {
531 transformedCss = transformedCss.map(function (transformedCss) {
532 return t.stringLiteral(transformedCss);
533 });
534 }
535
536 return {
537 hash: dynamic ? hashString(hash + staticClassName) : hashString(hash),
538 css: transformedCss,
539 expressions: dynamic && expressions
540 };
541};
542
543exports.processCss = processCss;
544
545var booleanOption = function booleanOption(opts) {
546 var ret;
547 opts.some(function (opt) {
548 if (typeof opt === 'boolean') {
549 ret = opt;
550 return true;
551 }
552
553 return false;
554 });
555 return ret;
556};
557
558exports.booleanOption = booleanOption;
559
560var createReactComponentImportDeclaration = function createReactComponentImportDeclaration(state) {
561 (0, _helperModuleImports.addDefault)(state.file.path, typeof state.opts.styleModule === 'string' ? state.opts.styleModule : 'styled-jsx/style', {
562 nameHint: _constants.STYLE_COMPONENT
563 });
564};
565
566exports.createReactComponentImportDeclaration = createReactComponentImportDeclaration;
567
568var setStateOptions = function setStateOptions(state) {
569 var vendorPrefixes = booleanOption([state.opts.vendorPrefixes, state.file.opts.vendorPrefixes]);
570 state.opts.vendorPrefixes = typeof vendorPrefixes === 'boolean' ? vendorPrefixes : true;
571 var sourceMaps = booleanOption([state.opts.sourceMaps, state.file.opts.sourceMaps]);
572 state.opts.sourceMaps = Boolean(sourceMaps);
573
574 if (!state.plugins) {
575 state.plugins = combinePlugins(state.opts.plugins, {
576 sourceMaps: state.opts.sourceMaps,
577 vendorPrefixes: state.opts.vendorPrefixes
578 });
579 }
580};
581
582exports.setStateOptions = setStateOptions;
583
584function log(message) {
585 console.log('[styled-jsx] ' + message);
586}
\No newline at end of file