-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
142 lines (135 loc) · 5.14 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
// Babel plugin to transform class names into cssobj localized
// imports
var util = require('util')
var converter = require('cssobj-converter')
var yaml = require('js-yaml')
var objutil = require('objutil')
var syntaxJsx = require('babel-plugin-syntax-jsx')
// constants
var templateDelimiter = '_cssobj_template_delimiter_'
// helpers
function transformObjecctToFunction (babel, code, option) {
var result = babel.transform('!' + code, {
plugins: [['./transform-plugins', option]]
})
// console.log(result.code)
// result.ast.program have: .cssobjConfig, .cssobjImports
return result
}
module.exports = function (babel) {
var t = babel.types
return {
inherits: syntaxJsx,
visitor: {
TaggedTemplateExpression (path, state) {
var root = path.hub.file
var source = root.code
var option = state.opts // babel5: state===opts
option = objutil.defaults(option, {
tag: 'CSSOBJ',
format: 'scss',
names: {
cssobj: {name: 'cssobj', path: 'cssobj'}
}
})
var node = path.node
// console.log(node)
var yamlRe = /\n\s*---\s*\n/
if (t.isIdentifier(node.tag, {name: option.tag})) {
var texts = node.quasi.quasis.map(function (v) {
return v.value.raw
})
var exps = node.quasi.expressions.map(function (v) {
return source.substring(v.start, v.end)
})
// it's cssobj template
var i = 0, cssobjConfig, cssobjConfigNode, config = '{}'
if (texts[0].search(yamlRe) === 0) {
// config parser
texts[0] = texts[0].replace(yamlRe, '\n')
var yamlArr = [], pos
for (i = 0; i < texts.length; i++) {
pos = texts[i].search(yamlRe)
if (pos > -1) {
yamlArr.push(texts[i].substr(0, pos))
texts[i] = texts[i].substr(pos).replace(yamlRe, '')
break
} else {
yamlArr.push(texts[i])
}
}
cssobjConfig = yaml.load(yamlArr.join(templateDelimiter))
if (cssobjConfig) {
cssobjConfig = util.inspect(cssobjConfig, {depth: null})
.split('\'' + templateDelimiter + '\'')
.map(function (v, i, arr) {
if (i == arr.length - 1) return v
return v + exps.shift()
})
.join('')
cssobjConfigNode = transformObjecctToFunction(babel, cssobjConfig, option)
root.path.unshiftContainer('body', cssobjConfigNode.ast.program.cssobjImports)
config = cssobjConfigNode.code.substr(1).replace(/;+$/, '')
}
// console.log(yamlArr, config, 111, texts[i])
texts = texts.slice(i)
}
// css object transform
var obj = converter(texts.join(templateDelimiter), option.format)
var objStr = util.inspect(obj, {depth: null})
.split('\'' + templateDelimiter + '\'')
.map(function (v, i, arr) {
if (i == arr.length - 1) return v
return v + exps.shift()
})
.join('')
// got css object
// console.log(objStr)
var cssobjNS = option.names['cssobj']
var cssobjName = cssobjNS.name
root.path.unshiftContainer('body', t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(cssobjName))],
t.stringLiteral(cssobjNS.path)
))
path.replaceWithSourceString(`${cssobjName} (${objStr}, ${config})`)
}
},
CallExpression (path, state) {
// get mapClass name from plugin options
var mapName = state.mapName || (state.opts && state.opts.mapName)
var callee = path.node.callee
var args = path.node.arguments
// this form: result.mapClass(JSX)
// or this form: customName(JSX)
if ((t.isMemberExpression(callee)
&& !callee.computed
&& t.isIdentifier(callee.property, {name: mapName || 'mapClass'})
||
mapName && t.isIdentifier(callee, {name: mapName}))
&& t.isJSXElement(args[0])) {
path.traverse(transformClassVisitor(), { callee: callee, mapName: mapName })
path.replaceWith(args[0])
}
}
}
}
function transformClassVisitor () {
return {
JSXAttribute (path) {
var node = path.node
if (!node.name || !node.value || ['className', 'class'].indexOf(node.name.name) < 0) return
// get the right mapClass arguments
var exp = t.isJSXExpressionContainer(node.value)
? node.value.expression
: node.value
// transform ExpressionContainer to be result.mapClass(exp)
var callee = t.isMemberExpression(this.callee)
? t.memberExpression(this.callee.object, t.identifier('mapClass'))
: this.callee
node.value = t.jSXExpressionContainer(
t.callExpression(callee, [exp])
)
}
}
}
}