UNPKG

9.4 kBJavaScriptView Raw
1"use strict";
2
3exports.__esModule = true;
4exports.isCreateElementCall = exports.default = void 0;
5
6var _generator = _interopRequireDefault(require("@babel/generator"));
7
8var t = _interopRequireWildcard(require("@babel/types"));
9
10var _picocolors = _interopRequireDefault(require("picocolors"));
11
12var _Symbols = require("../utils/Symbols");
13
14var _addPragma = _interopRequireDefault(require("../utils/addPragma"));
15
16var _buildTaggedTemplate = _interopRequireDefault(require("../utils/buildTaggedTemplate"));
17
18var _createStyleNode = _interopRequireDefault(require("../utils/createStyleNode"));
19
20var _getNameFromPath = _interopRequireDefault(require("../utils/getNameFromPath"));
21
22var _isCssTag = _interopRequireDefault(require("../utils/isCssTag"));
23
24var _isStylesheetTag = _interopRequireDefault(require("../utils/isStylesheetTag"));
25
26var _trimEnd = _interopRequireDefault(require("../utils/trimEnd"));
27
28var _wrapInClass = _interopRequireDefault(require("../utils/wrapInClass"));
29
30function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
31
32function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && 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; }
33
34function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35
36const HAS_JSX = Symbol('Astroturf has jsx');
37const HAS_CREATE_ELEMENT = Symbol('Astroturf has createElement call');
38const IS_JSX = Symbol('Is a JSX call expression');
39
40// XXX: if the single class cssProp is disabled fallback to the stylesheet tag
41// this enables pre v1 tag meaning for folks that want to do an incremental migration
42const isCssPropTag = (tagPath, options) => options.cssTagName === false ? (0, _isStylesheetTag.default)(tagPath, options) : (0, _isCssTag.default)(tagPath, options);
43
44const isJsxCallExpression = p => {
45 const result = p.isCallExpression() && ( // @ts-ignore
46 p.get('callee').referencesImport('react/jsx-runtime') || // @ts-ignore
47 p.get('callee').referencesImport('react/jsx-dev-runtime'));
48 return result;
49};
50
51const isCreateElementCall = p => p.isCallExpression() && p.get('callee.property').node && p.get('callee.property').node.name === 'createElement';
52
53exports.isCreateElementCall = isCreateElementCall;
54
55function buildCssProp(valuePath, name, options, isJsx = false) {
56 const {
57 file,
58 defaultedOptions: pluginOptions
59 } = options;
60 const cssState = file.get(_Symbols.STYLES);
61 const nodeMap = file.get(_Symbols.COMPONENTS);
62
63 if (!pluginOptions.enableCssProp) {
64 if (!pluginOptions.noWarnings) // eslint-disable-next-line no-console
65 console.warn(_picocolors.default.yellow(`It looks like you are trying to use the css prop with${_picocolors.default.bold('astroturf')} but have not enabled it. add ${_picocolors.default.bold('enableCssProp: true')} to the loader or plugin options to compile the css prop.`));
66 return null;
67 }
68
69 const displayName = `CssProp${++cssState.id}_${name}`;
70 let vars = null;
71 let variants = null;
72 const baseStyle = (0, _createStyleNode.default)(valuePath, displayName, {
73 file,
74 pluginOptions
75 });
76 const style = { ...baseStyle,
77 type: 'class',
78 interpolations: [],
79 imports: '',
80 value: ''
81 };
82 let importId;
83
84 if (valuePath.isStringLiteral()) {
85 style.value = (0, _wrapInClass.default)(valuePath.node.value);
86 importId = options.styleImports.add(style);
87 } else {
88 const exprPath = valuePath.isJSXExpressionContainer() ? valuePath.get('expression') : valuePath;
89
90 if (exprPath.isTemplateLiteral() || exprPath.isTaggedTemplateExpression() && isCssPropTag(exprPath.get('tag'), pluginOptions)) {
91 importId = options.styleImports.add(style);
92 const template = (0, _buildTaggedTemplate.default)({
93 style,
94 nodeMap,
95 importId,
96 pluginOptions,
97 location: 'PROP',
98 quasiPath: exprPath.isTemplateLiteral() ? exprPath : exprPath.get('quasi')
99 });
100 vars = template.vars.elements.length ? template.vars : null;
101 variants = template.variants.elements.length ? template.variants : null;
102 style.interpolations = template.interpolations;
103 style.value = template.css;
104 }
105 }
106
107 if (!importId) {
108 return null;
109 }
110
111 let runtimeNode = t.arrayExpression((0, _trimEnd.default)([importId, vars, variants]).map(n => n != null ? n : t.nullLiteral()));
112 style.importIdentifier = importId.name; // FIXME?
113 // @ts-ignore
114
115 nodeMap.set(runtimeNode.expression, style);
116
117 if (isJsx) {
118 runtimeNode = t.jsxExpressionContainer(runtimeNode);
119 }
120
121 cssState.styles.set(style.absoluteFilePath, style);
122 if (pluginOptions.generateInterpolations) style.code = (0, _generator.default)(runtimeNode).code;
123 return runtimeNode;
124}
125
126const getObjectKey = keyPath => {
127 if (keyPath.isStringLiteral()) return keyPath.node.value;
128 if (keyPath.isIdentifier()) return keyPath.node.name;
129 return keyPath.node.name;
130};
131
132const cssPropertyVisitors = {
133 ObjectProperty(path, state) {
134 const {
135 typeName
136 } = state;
137 if (getObjectKey(path.get('key')) !== 'css') return;
138 const valuePath = path.get('value');
139 const compiledNode = buildCssProp(valuePath, typeName, state);
140
141 if (compiledNode) {
142 valuePath.replaceWith(compiledNode);
143 state.processed = true;
144 }
145 },
146
147 CallExpression(path) {
148 // prevent the inner traversal from finding nested css props, on `children:` keys
149 // but mark the path so we can skip the check when the outer traversal finds it
150 if (isCreateElementCall(path) || isJsxCallExpression(path)) {
151 path[IS_JSX] = true;
152 path.skip();
153 }
154 }
155
156};
157var _default = {
158 Program: {
159 exit(path, state) {
160 const hasJsx = !!state.file.get(HAS_JSX);
161 const hasCreateElement = !!state.file.get(HAS_CREATE_ELEMENT);
162 if (!hasJsx && !hasCreateElement) return;
163 const {
164 jsx
165 } = state[_Symbols.JSX_IDENTS];
166 const pragmaDisabled = !state.defaultedOptions.jsxPragma; // For createElement calls we still need to add an import
167 // but we don't need to do pragma bits
168 // if the pragma is disabled and there is no createElement call we are done
169
170 if (pragmaDisabled && !hasCreateElement) return;
171 const changes = (0, _addPragma.default)(path, jsx, hasJsx && !pragmaDisabled);
172 state.file.get(_Symbols.STYLES).changeset.unshift(...changes);
173 }
174
175 },
176
177 CallExpression(path, state) {
178 const {
179 file
180 } = state;
181 const pluginOptions = state.defaultedOptions;
182 const isAutomaticRuntime = isJsxCallExpression(path);
183 if (!path[IS_JSX] && !isCreateElementCall(path) && !isAutomaticRuntime) return;
184 const typeName = (0, _getNameFromPath.default)(path.get('arguments')[0]);
185 const propsPath = path.get('arguments')[1];
186 const innerState = { ...state,
187 pluginOptions,
188 file,
189 processed: false,
190 typeName
191 }; // We aren't checking very hard that this is a React createElement call
192
193 if (!propsPath) {
194 return;
195 }
196
197 propsPath.traverse(cssPropertyVisitors, innerState);
198
199 if (innerState.processed) {
200 const {
201 jsx
202 } = state[_Symbols.JSX_IDENTS];
203 const {
204 changeset
205 } = file.get(_Symbols.STYLES);
206 const callee = path.get('callee');
207
208 if (isAutomaticRuntime) {
209 const end = path.get('arguments')[0].node.start;
210 const calleeName = callee.node.name;
211 changeset.push({
212 type: 'create-element',
213 code: `${jsx.name}.jsx2(${calleeName}, `,
214 start: callee.node.start,
215 end
216 });
217 path.unshiftContainer('arguments', t.identifier(calleeName));
218 callee.replaceWith(t.memberExpression(t.identifier(jsx.name), t.identifier('jsx2')));
219 } else {
220 changeset.push({
221 type: 'create-element',
222 code: jsx.name,
223 start: callee.node.start,
224 end: callee.node.end
225 });
226 callee.replaceWith(t.identifier(jsx.name));
227 }
228
229 file.set(HAS_CREATE_ELEMENT, true);
230 }
231 },
232
233 JSXAttribute(path, state) {
234 const {
235 file
236 } = state;
237 if (path.node.name.name !== 'css') return;
238 const valuePath = path.get('value');
239 const parentPath = path.findParent(p => p.isJSXOpeningElement());
240 const compiledNode = buildCssProp(valuePath, parentPath && (0, _getNameFromPath.default)(parentPath.get('name')), state, true);
241
242 if (compiledNode) {
243 valuePath.replaceWith(compiledNode);
244 file.set(HAS_JSX, true);
245 }
246 }
247
248};
249exports.default = _default;
\No newline at end of file