You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					201 lines
				
				8.1 KiB
			
		
		
			
		
	
	
					201 lines
				
				8.1 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Object.defineProperty(exports, "__esModule", {
							 | 
						||
| 
								 | 
							
								  value: true
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								exports.default = void 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _helperModuleImports = require("@babel/helper-module-imports");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _detectors = require("../utils/detectors");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _options = require("../utils/options");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Most of this code was taken from @satya164's babel-plugin-css-prop
							 | 
						||
| 
								 | 
							
								// @see https://github.com/satya164/babel-plugin-css-prop
							 | 
						||
| 
								 | 
							
								const TAG_NAME_REGEXP = /^[a-z][a-z\d]*(\-[a-z][a-z\d]*)?$/;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const getName = (node, t) => {
							 | 
						||
| 
								 | 
							
								  if (typeof node.name === 'string') return node.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (t.isJSXMemberExpression(node)) {
							 | 
						||
| 
								 | 
							
								    return `${getName(node.object, t)}.${node.property.name}`;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  throw path.buildCodeFrameError(`Cannot infer name from node with type "${node.type}". Please submit an issue at github.com/styled-components/babel-plugin-styled-components with your code so we can take a look at your use case!`);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const getNameExpression = (node, t) => {
							 | 
						||
| 
								 | 
							
								  if (typeof node.name === 'string') return t.identifier(node.name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (t.isJSXMemberExpression(node)) {
							 | 
						||
| 
								 | 
							
								    return t.memberExpression(getNameExpression(node.object, t), t.identifier(node.property.name));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  throw path.buildCodeFrameError(`Cannot infer name expression from node with type "${node.type}". Please submit an issue at github.com/styled-components/babel-plugin-styled-components with your code so we can take a look at your use case!`);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const getLocalIdentifier = path => {
							 | 
						||
| 
								 | 
							
								  const identifier = path.scope.generateUidIdentifier('css'); // make it transient
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  identifier.name = identifier.name.replace('_', '$_');
							 | 
						||
| 
								 | 
							
								  return identifier;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _default = t => (path, state) => {
							 | 
						||
| 
								 | 
							
								  if (!(0, _options.useCssProp)(state)) return;
							 | 
						||
| 
								 | 
							
								  if (path.node.name.name !== 'css') return;
							 | 
						||
| 
								 | 
							
								  const program = state.file.path; // state.customImportName is passed through from styled-components/macro if it's used
							 | 
						||
| 
								 | 
							
								  // since the macro also inserts the import
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let importName = state.customImportName || (0, _detectors.importLocalName)('default', state);
							 | 
						||
| 
								 | 
							
								  const {
							 | 
						||
| 
								 | 
							
								    bindings
							 | 
						||
| 
								 | 
							
								  } = program.scope; // Insert import if it doesn't exist yet
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!importName || !bindings[importName.name] || !bindings[importName]) {
							 | 
						||
| 
								 | 
							
								    (0, _helperModuleImports.addDefault)(path, 'styled-components', {
							 | 
						||
| 
								 | 
							
								      nameHint: 'styled'
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    importName = t.identifier((0, _detectors.importLocalName)('default', state, {
							 | 
						||
| 
								 | 
							
								      bypassCache: true
							 | 
						||
| 
								 | 
							
								    }));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!t.isIdentifier(importName)) importName = t.identifier(importName);
							 | 
						||
| 
								 | 
							
								  const elem = path.parentPath;
							 | 
						||
| 
								 | 
							
								  const name = getName(elem.node.name, t);
							 | 
						||
| 
								 | 
							
								  const nameExpression = getNameExpression(elem.node.name, t);
							 | 
						||
| 
								 | 
							
								  const id = path.scope.generateUidIdentifier('Styled' + name.replace(/^([a-z])/, (match, p1) => p1.toUpperCase()));
							 | 
						||
| 
								 | 
							
								  let styled;
							 | 
						||
| 
								 | 
							
								  let injector;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (TAG_NAME_REGEXP.test(name)) {
							 | 
						||
| 
								 | 
							
								    styled = t.callExpression(importName, [t.stringLiteral(name)]);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    styled = t.callExpression(importName, [nameExpression]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (bindings[name] && !t.isImportDeclaration(bindings[name].path.parent)) {
							 | 
						||
| 
								 | 
							
								      injector = nodeToInsert => (t.isVariableDeclaration(bindings[name].path.parent) ? bindings[name].path.parentPath : bindings[name].path).insertAfter(nodeToInsert);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let css;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (t.isStringLiteral(path.node.value)) {
							 | 
						||
| 
								 | 
							
								    css = t.templateLiteral([t.templateElement({
							 | 
						||
| 
								 | 
							
								      raw: path.node.value.value,
							 | 
						||
| 
								 | 
							
								      cooked: path.node.value.value
							 | 
						||
| 
								 | 
							
								    }, true)], []);
							 | 
						||
| 
								 | 
							
								  } else if (t.isJSXExpressionContainer(path.node.value)) {
							 | 
						||
| 
								 | 
							
								    if (t.isTemplateLiteral(path.node.value.expression)) {
							 | 
						||
| 
								 | 
							
								      css = path.node.value.expression;
							 | 
						||
| 
								 | 
							
								    } else if (t.isTaggedTemplateExpression(path.node.value.expression) && path.node.value.expression.tag.name === 'css') {
							 | 
						||
| 
								 | 
							
								      css = path.node.value.expression.quasi;
							 | 
						||
| 
								 | 
							
								    } else if (t.isObjectExpression(path.node.value.expression)) {
							 | 
						||
| 
								 | 
							
								      css = path.node.value.expression;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      css = t.templateLiteral([t.templateElement({
							 | 
						||
| 
								 | 
							
								        raw: '',
							 | 
						||
| 
								 | 
							
								        cooked: ''
							 | 
						||
| 
								 | 
							
								      }, false), t.templateElement({
							 | 
						||
| 
								 | 
							
								        raw: '',
							 | 
						||
| 
								 | 
							
								        cooked: ''
							 | 
						||
| 
								 | 
							
								      }, true)], [path.node.value.expression]);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!css) return;
							 | 
						||
| 
								 | 
							
								  elem.node.attributes = elem.node.attributes.filter(attr => attr !== path.node);
							 | 
						||
| 
								 | 
							
								  elem.node.name = t.jSXIdentifier(id.name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (elem.parentPath.node.closingElement) {
							 | 
						||
| 
								 | 
							
								    elem.parentPath.node.closingElement.name = t.jSXIdentifier(id.name);
							 | 
						||
| 
								 | 
							
								  } // object syntax
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (t.isObjectExpression(css)) {
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * for objects as CSS props, we have to recurse through the object and replace any
							 | 
						||
| 
								 | 
							
								     * object key/value scope references with generated props similar to how the template
							 | 
						||
| 
								 | 
							
								     * literal transform above creates dynamic interpolations
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    const p = t.identifier('p');
							 | 
						||
| 
								 | 
							
								    let replaceObjectWithPropFunction = false;
							 | 
						||
| 
								 | 
							
								    css.properties = css.properties.reduce(function propertiesReducer(acc, property) {
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * handle potential object key interpolations
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      if (t.isMemberExpression(property.key) || t.isCallExpression(property.key) || // checking for css={{[something]: something}}
							 | 
						||
| 
								 | 
							
								      t.isIdentifier(property.key) && path.scope.hasBinding(property.key.name) && ( // but not a object reference shorthand like css={{ color }}
							 | 
						||
| 
								 | 
							
								      t.isIdentifier(property.value) ? property.key.name !== property.value.name : true)) {
							 | 
						||
| 
								 | 
							
								        replaceObjectWithPropFunction = true;
							 | 
						||
| 
								 | 
							
								        const identifier = getLocalIdentifier(path);
							 | 
						||
| 
								 | 
							
								        elem.node.attributes.push(t.jSXAttribute(t.jSXIdentifier(identifier.name), t.jSXExpressionContainer(property.key)));
							 | 
						||
| 
								 | 
							
								        property.key = t.memberExpression(p, identifier);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (t.isObjectExpression(property.value)) {
							 | 
						||
| 
								 | 
							
								        // recurse for objects within objects (e.g. {'::before': { content: x }})
							 | 
						||
| 
								 | 
							
								        property.value.properties = property.value.properties.reduce(propertiesReducer, []);
							 | 
						||
| 
								 | 
							
								        acc.push(property);
							 | 
						||
| 
								 | 
							
								      } else if (t.isSpreadElement(property)) {
							 | 
						||
| 
								 | 
							
								        // handle spread variables and such
							 | 
						||
| 
								 | 
							
								        if (t.isObjectExpression(property.argument)) {
							 | 
						||
| 
								 | 
							
								          property.argument.properties = property.argument.properties.reduce(propertiesReducer, []);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          replaceObjectWithPropFunction = true;
							 | 
						||
| 
								 | 
							
								          const identifier = getLocalIdentifier(path);
							 | 
						||
| 
								 | 
							
								          elem.node.attributes.push(t.jSXAttribute(t.jSXIdentifier(identifier.name), t.jSXExpressionContainer(property.argument)));
							 | 
						||
| 
								 | 
							
								          property.argument = t.memberExpression(p, identifier);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        acc.push(property);
							 | 
						||
| 
								 | 
							
								      } else if ( // if a non-primitive value we have to interpolate it
							 | 
						||
| 
								 | 
							
								      [t.isBigIntLiteral, t.isBooleanLiteral, t.isNullLiteral, t.isNumericLiteral, t.isStringLiteral].filter(Boolean) // older versions of babel might not have bigint support baked in
							 | 
						||
| 
								 | 
							
								      .every(x => !x(property.value))) {
							 | 
						||
| 
								 | 
							
								        replaceObjectWithPropFunction = true;
							 | 
						||
| 
								 | 
							
								        const identifier = getLocalIdentifier(path);
							 | 
						||
| 
								 | 
							
								        elem.node.attributes.push(t.jSXAttribute(t.jSXIdentifier(identifier.name), t.jSXExpressionContainer(property.value)));
							 | 
						||
| 
								 | 
							
								        acc.push(t.objectProperty(property.key, t.memberExpression(p, identifier)));
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        // some sort of primitive which is safe to pass through as-is
							 | 
						||
| 
								 | 
							
								        acc.push(property);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return acc;
							 | 
						||
| 
								 | 
							
								    }, []);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (replaceObjectWithPropFunction) {
							 | 
						||
| 
								 | 
							
								      css = t.arrowFunctionExpression([p], css);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    // tagged template literal
							 | 
						||
| 
								 | 
							
								    css.expressions = css.expressions.reduce((acc, ex) => {
							 | 
						||
| 
								 | 
							
								      if (Object.keys(bindings).some(key => bindings[key].referencePaths.find(p => p.node === ex)) || t.isFunctionExpression(ex) || t.isArrowFunctionExpression(ex)) {
							 | 
						||
| 
								 | 
							
								        acc.push(ex);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        const identifier = getLocalIdentifier(path);
							 | 
						||
| 
								 | 
							
								        const p = t.identifier('p');
							 | 
						||
| 
								 | 
							
								        elem.node.attributes.push(t.jSXAttribute(t.jSXIdentifier(identifier.name), t.jSXExpressionContainer(ex)));
							 | 
						||
| 
								 | 
							
								        acc.push(t.arrowFunctionExpression([p], t.memberExpression(p, identifier)));
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return acc;
							 | 
						||
| 
								 | 
							
								    }, []);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!injector) {
							 | 
						||
| 
								 | 
							
								    let parent = elem;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (!t.isProgram(parent)) {
							 | 
						||
| 
								 | 
							
								      parent = parent.parentPath;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    injector = nodeToInsert => parent.pushContainer('body', nodeToInsert);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  injector(t.variableDeclaration('var', [t.variableDeclarator(id, t.isObjectExpression(css) || t.isArrowFunctionExpression(css) ? t.callExpression(styled, [css]) : t.taggedTemplateExpression(styled, css))]));
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.default = _default;
							 |