-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
225 lines (206 loc) · 7.31 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
var types = require('babel-types');
var sass = require('node-sass');
var reactNativeCSS = require('react-native-css').default;
var fs = require('fs');
var polyfillMerge = `
Array.$mergeItems = function $mergeItems(merged, item) {
return Object.assign(merged, item);
};
Array.prototype.$merge = Array.prototype.$merge || function $merge() {
var list = this;
return list.reduce(Array.$mergeItems, {});
};
`;
var cache = {
isMergePolyfilled: false,
};
function importContent(filePath) {
if (!cache[filePath]) {
var data = fs.readFileSync(filePath).toString('utf-8');
cache[filePath] = {
rawData: data,
compiled: reactNativeCSS(data),
importedVariables: {}
};
}
return cache[filePath];
}
var DIRECTORY_SEPARATOR = '/';
var EXTENSION_SEPARATOR = '.';
var NATIVE_PREFIX_EXTENSION = 'native';
function isValidSourceFile(filePath, extensions) {
var extension = filePath.split(EXTENSION_SEPARATOR).pop().toLowerCase();
return extensions.filter(function (ext) {
return extension === ext;
}).length > 0;
}
/**
* Gets the specific path to native styles
* @param {string} filePath
* @param {array<string>} extensions
*/
function parsePath(filePath, nativeExtensionPrefix, extensions) {
var path = filePath.split(EXTENSION_SEPARATOR);
path.pop();
var fullPathWithoutExtension = path.join(EXTENSION_SEPARATOR);
var extension = extensions.filter(function (ext) {
return fs.existsSync([
fullPathWithoutExtension,
EXTENSION_SEPARATOR,
nativeExtensionPrefix,
EXTENSION_SEPARATOR,
ext
].join(''));
}).pop();
return [
fullPathWithoutExtension,
EXTENSION_SEPARATOR,
nativeExtensionPrefix,
EXTENSION_SEPARATOR,
extension
].join('');
}
/**
* Gets the relative path from this transformer
* @param {string} source - relative path to importer file name, e.g. (../src/component.js)
* @param {string} target - style file name
*/
function getStyleSheetFullFilePath(source, target) {
var pathFileNodes = source.split(DIRECTORY_SEPARATOR);
pathFileNodes.pop();
pathFileNodes.push(target.split(DIRECTORY_SEPARATOR).pop());
var fullFilePath = pathFileNodes.join(DIRECTORY_SEPARATOR);
return fullFilePath;
}
function isJoinExpression(path, t) {
return path.node.value.expression.callee
&& path.node.value.expression.callee.property
&& path.node.value.expression.callee.property.name
&& path.node.value.expression.callee.property.name.toLowerCase() === 'join'
&& t.isArrayExpression(path.node.value.expression.callee.object)
}
function getAllowedExtensions(pathObject) {
return ((pathObject.opts || {}).extensions || ['css', 'scss', 'sass']).map(function(ext) {
return ext.toLowerCase();
});
}
function getPrefixExtension(pathObject) {
return (pathObject.opts || {}).prefixExtension || NATIVE_PREFIX_EXTENSION;
}
module.exports = function ({ Plugin, types: t}) {
function importResolver(path, state) {
var file = state.file;
var fileOpts = file.opts;
var node = path.node;
var styles = node.specifiers && node.specifiers.length && node.specifiers[0].local && node.specifiers[0].local.name;
var extra = node.source.extra;
var specifiers = node.specifiers;
if (!styles) {
return;
}
// style file name
var targetFileName = node.source.value;
// path from root to importer the file name, e.g. (src/component.js)
isAbsolutePath = /^((\w[:](\/)+)|\/)/.test(fileOpts.filename);
prefixAbsolutePath = (isAbsolutePath ? [] : [process.cwd()]);
var sourceFileName = prefixAbsolutePath.concat(fileOpts.filename).join(DIRECTORY_SEPARATOR);
var styleSheetFullFilePath = getStyleSheetFullFilePath(sourceFileName, targetFileName);
var extensions = getAllowedExtensions(path);
var prefixExtension = getPrefixExtension(path);
if (!node || !isValidSourceFile(targetFileName, extensions) || types.isIdentifier(node)) return;
var source = parsePath(
styleSheetFullFilePath,
prefixExtension,
extensions
);
var data = importContent(source);
var { compiled: stylesAsObject } = data;
var hashImportedVariables = [sourceFileName, styleSheetFullFilePath].join('|');
if (!Object.keys(data.importedVariables).includes(hashImportedVariables)) {
data.importedVariables[hashImportedVariables] = styles;
}
var templateLiteral = [
'var',
data.importedVariables[hashImportedVariables],
'=',
JSON.stringify(stylesAsObject),
';',
(!cache.isMergePolyfilled) && polyfillMerge
].join(' ');
cache.isMergePolyfilled = true;
path.replaceWith(t.identifier(templateLiteral));
}
var pragma, expression;
var style, css;
return {
visitor: {
ImportDeclaration: importResolver,
JSXOpeningElement: {
exit(path, state) {
if (css != null) {
var classes = [];
if (t.isJSXExpressionContainer(css.node.value)) {
if (t.isArrayExpression(css.node.value.expression)) {
classes = classes.concat(css.node.value.expression.elements);
} else if (isJoinExpression(css, t)) {
classes = classes.concat(css.node.value.expression.callee.object.elements);
} else {
classes.push(css.node.value.expression);
}
}
if (style == null) {
style = css;
style.node.name.name = 'style';
} else {
if (t.isArrayExpression(style.node.value.expression)) {
classes = classes.concat(style.node.value.expression.elements);
} else {
classes.push(style.node.value.expression);
}
css.remove();
}
if (classes.length === 0) {
style.remove();
} else if (classes.length === 1) {
if (style === css) {
var classExpression = classes[0];
var hasTheJSXExpressionContainerAssigned = style.node.value && style.node.value.type === 'JSXExpressionContainer'
&& style.node.value.expression === classExpression;
if (!hasTheJSXExpressionContainerAssigned) {
style.node.value = t.JSXExpressionContainer(classExpression);
}
}
} else {
var arrayExpression = t.ArrayExpression(classes);
var memberMergeExpression = t.memberExpression(arrayExpression, t.identifier('$merge'))
var callExpressionMergeAll = t.callExpression(memberMergeExpression, []);
style.node.value = t.JSXExpressionContainer(callExpressionMergeAll);
}
css = null;
}
style = null;
}
},
JSXAttribute: function JSXAttribute(path, state) {
if (path.node.name.name === 'className') {
css = path;
} else if (path.node.name.name === 'style') {
style = path;
}
},
Program: function Program(path, state) {
// Init rule for update layout
pragma = state.opts.pragma;
if (pragma != null) {
pragma = pragma.split(".").map(function (name) {
return t.identifier(name);
}).reduce(function (object, property) {
return t.memberExpression(object, property);
});
} else {
pragma = t.identifier('styles');
}
}
}
};
}