UNPKG

6.04 kBJavaScriptView Raw
1import safe from './react/safe.js'
2import wrap from './react/wrap.js'
3import toCamelCase from 'to-camel-case'
4import toSlugCase from 'to-slug-case'
5
6const safeScope = value =>
7 typeof value === 'string' && !isCode(value) ? JSON.stringify(value) : value
8
9export const checkParentStem = (node, styleKey) => {
10 if (styleKey !== 'hover' || styleKey !== 'disabled' || !node.parent)
11 return false
12
13 const matchingParentStem = node.parent.scopes.some(
14 scope => scope.value === styleKey
15 )
16
17 return matchingParentStem && (node.parent.is || node.parent.name)
18}
19
20const INTERPOLATION = /\${(.+)}/
21export const isInterpolation = str => INTERPOLATION.test(str)
22export const deinterpolate = str => {
23 const match = str.match(INTERPOLATION)
24 return match ? match[1] : str
25}
26
27export const getObjectAsString = obj =>
28 wrap(
29 Object.keys(obj)
30 .map(k => {
31 const v =
32 typeof obj[k] === 'object' && hasKeys(obj[k])
33 ? getObjectAsString(obj[k])
34 : obj[k]
35 return `${JSON.stringify(k)}: ${v}`
36 })
37 .join(',')
38 )
39
40export const getPropertiesAsObject = list => {
41 const obj = {}
42
43 list.forEach(prop => {
44 obj[prop.name] = safeScope(prop.value)
45 })
46
47 return getObjectAsString(obj)
48}
49
50export const getProp = (node, key) => {
51 const finder =
52 typeof key === 'string' ? p => p.name === key : p => key.test(p.name)
53
54 return node.properties && node.properties.find(finder)
55}
56
57export const getScope = node => node.value.split('when ')[1]
58
59const maybeSafe = node =>
60 node.tags.code
61 ? node.value
62 : typeof node.value === 'string' ? safe(node.value) : node.value
63
64const getScopedProps = (propNode, blockNode) => {
65 const scopes = blockNode.scopes
66 .filter(scope => !scope.isSystem)
67 .map(scope => {
68 const prop = scope.properties.find(prop => prop.name === propNode.name)
69 return prop && { prop, when: scope.value }
70 })
71 .filter(Boolean)
72 .reverse()
73
74 if (isEmpty(scopes)) return false
75
76 return scopes
77}
78
79export const getScopedCondition = (propNode, blockNode) => {
80 let conditional = maybeSafe(propNode)
81
82 if (!getScopedProps(propNode, blockNode)) return false
83
84 getScopedProps(propNode, blockNode).forEach(scope => {
85 conditional = `${scope.when} ? ${maybeSafe(scope.prop)} : ` + conditional
86 })
87
88 return conditional
89}
90
91export const getScopedImageCondition = (scopes, scopedNames, defaultName) => {
92 let conditional = defaultName
93
94 scopes.forEach((scope, index) => {
95 conditional = `${scope.when} ? ${scopedNames[index]} : ` + conditional
96 })
97
98 return conditional
99}
100
101export const getScopedRequireCondition = (scopes, paths, defaultName) => {
102 let conditional = `requireImage('${defaultName}')`
103
104 scopes.forEach((scope, index) => {
105 conditional =
106 `${scope.when} ? requireImage('${paths[index]}') : ` + conditional
107 })
108
109 return conditional
110}
111
112const styleStems = ['hover', 'focus', 'placeholder', 'disabled', 'print']
113export const getStyleType = node =>
114 styleStems.find(tag => isTag(node, tag)) || 'base'
115export const hasKeys = obj => Object.keys(obj).length > 0
116export const hasKeysInChildren = obj =>
117 Object.keys(obj).some(k => hasKeys(obj[k]))
118
119export const hasProp = (node, key, match) => {
120 const prop = getProp(node, key)
121 if (!prop) return false
122 return typeof match === 'function' ? match(prop.value) : true
123}
124
125export const hasDefaultProp = (node, parent) =>
126 parent.properties.some(prop => prop.nameRaw === node.nameRaw)
127
128export const CODE_EXPLICIT = /^{.+}$/
129export const isCodeExplicit = str => CODE_EXPLICIT.test(str)
130export const isCode = node =>
131 typeof node === 'string'
132 ? /props|item|index/.test(node) || isCodeExplicit(node)
133 : isTag(node, 'code')
134export const isStyle = node => isTag(node, 'style')
135export const isTag = (node, tag) => node.tags[tag]
136
137export const getActionableParent = node => {
138 if (!node.parent) return false
139 if (node.parent.action) return node.parent
140 return getActionableParent(node.parent)
141}
142
143export const getAllowedStyleKeys = node => {
144 if (node.isCapture) {
145 return ['base', 'focus', 'hover', 'disabled', 'placeholder', 'print']
146 } else if (node.action || getActionableParent(node)) {
147 return ['base', 'focus', 'hover', 'disabled', 'print']
148 }
149 return ['base', 'focus', 'print']
150}
151
152export const isList = node =>
153 node && node.type === 'Block' && node.name === 'List'
154
155export const isEmpty = list => list.length === 0
156
157export const isValidImgSrc = (node, parent) =>
158 node.name === 'source' && parent.name === 'Image' && parent.isBasic
159
160export const pushImageToState = (state, scopedNames, paths) =>
161 scopedNames.forEach(name => {
162 const path = paths[scopedNames.findIndex(item => item === name)]
163 if (!state.images.includes(path)) {
164 state.images.push({
165 name,
166 file: path,
167 })
168 }
169 })
170
171export const getScopes = (node, parent) => {
172 const scopedProps = getScopedProps(node, parent)
173 if (!scopedProps) return false
174 const paths = scopedProps.map(scope => scope.prop.value)
175 const scopedNames = paths.map(path => toCamelCase(path))
176
177 return { scopedProps, paths, scopedNames }
178}
179
180export const isSvg = node => /^Svg/.test(node.name) && node.isBasic
181
182export const getScopeDescription = scope => {
183 const dictionary = {}
184 const re = /(?:^|\W)props.(\w+)(?!\w)/g
185
186 let match = re.exec(scope)
187 while (match) {
188 dictionary[match[1]] = toSlugCase(match[1])
189 match = re.exec(scope)
190 }
191
192 for (let key in dictionary) {
193 scope = scope.replace(new RegExp(key, 'g'), dictionary[key])
194 }
195
196 return toCamelCase(
197 scope
198 .replace(/\|\|/g, '-or-')
199 .replace(/!/g, 'not-')
200 .replace(/&&/g, '-and-')
201 .replace(/props\./g, '')
202 .replace(/\s/g, '')
203 )
204}
205
206export const makeOnClickTracker = (node, state) => {
207 const block = node.testId
208 ? `"${state.name}.${node.testId}"`
209 : `props["${state.testIdKey}"] || "${state.name}"`
210
211 const track = `context.track({ block: ${block}, action: "click" })`
212
213 state.isTracking = true
214
215 return `event => { ${track}; (${node.value})(event); }`
216}