@mpxjs/core 2.9.36 → 2.9.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,116 @@
1
+ import cssauron from './cssauron'
2
+
3
+ const language = cssauron({
4
+ tag: function (node) {
5
+ return node.nodeType
6
+ },
7
+ class: function (node) {
8
+ return node.data?.class
9
+ },
10
+ id: function (node) {
11
+ return node.data?.id
12
+ },
13
+ children: function (node) {
14
+ return node.children
15
+ },
16
+ parent: function (node) {
17
+ return node.parent
18
+ },
19
+ contents: function (node) {
20
+ return node.contents || ''
21
+ },
22
+ attr: function (node, attr) {
23
+ if (node.properties) {
24
+ const attrs = node.properties.attributes
25
+ if (attrs && attrs[attr]) {
26
+ return attrs[attr]
27
+ }
28
+ return node.properties[attr]
29
+ }
30
+ }
31
+ })
32
+
33
+ export default function cssSelect (sel, options) {
34
+ options = options || {}
35
+ const selector = language(sel, options.moduleId)
36
+ function match (vtree) {
37
+ const node = mapTree(vtree, null, options) || {}
38
+ const matched = []
39
+
40
+ // Traverse each node in the tree and see if it matches our selector
41
+ traverse(node, function (node) {
42
+ let result = selector(node)
43
+ if (result) {
44
+ if (!Array.isArray(result)) {
45
+ result = [result]
46
+ }
47
+ matched.push.apply(matched, result)
48
+ }
49
+ })
50
+
51
+ const results = mapResult(matched)
52
+ if (results.length === 0) {
53
+ return null
54
+ }
55
+ return results
56
+ }
57
+ match.matches = function (vtree) {
58
+ const node = mapTree(vtree, null, options)
59
+ return !!selector(node)
60
+ }
61
+ return match
62
+ }
63
+
64
+ function traverse (vtree, fn) {
65
+ fn(vtree)
66
+ if (vtree.children) {
67
+ vtree.children.forEach(function (vtree) {
68
+ traverse(vtree, fn)
69
+ })
70
+ }
71
+ }
72
+
73
+ function mapResult (result) {
74
+ return result
75
+ .filter(function (node) {
76
+ return !!node.vtree
77
+ })
78
+ .map(function (node) {
79
+ return node.vtree
80
+ })
81
+ }
82
+
83
+ function getNormalizeCaseFn (caseSensitive) {
84
+ return caseSensitive
85
+ ? function noop (str) {
86
+ return str
87
+ }
88
+ : function toLowerCase (str) {
89
+ return str.toLowerCase()
90
+ }
91
+ }
92
+
93
+ // Map a virtual-dom node tree into a data structure that cssauron can use to
94
+ // traverse.
95
+ function mapTree (vtree, parent, options) {
96
+ const normalizeTagCase = getNormalizeCaseFn(options.caseSensitiveTag)
97
+
98
+ if (vtree.nt != null) {
99
+ const node = {}
100
+ node.parent = parent
101
+ node.vtree = vtree
102
+ node.nodeType = normalizeTagCase(vtree.nt)
103
+ if (vtree.d) {
104
+ node.data = vtree.d
105
+ }
106
+
107
+ if (vtree.c) {
108
+ node.children = vtree.c
109
+ .map(function (child) {
110
+ return mapTree(child, node, options)
111
+ })
112
+ .filter(Boolean)
113
+ }
114
+ return node
115
+ }
116
+ }
@@ -0,0 +1,19 @@
1
+ export default function through (onData) {
2
+ let dataCb = null
3
+
4
+ return {
5
+ on: function (name, callback) {
6
+ if (name === 'data') {
7
+ dataCb = callback
8
+ }
9
+ },
10
+ end: function (data) {
11
+ onData(data)
12
+ },
13
+ queue: function (data) {
14
+ if (dataCb) {
15
+ dataCb(data)
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,371 @@
1
+ import through from './through'
2
+
3
+ const PSEUDOSTART = 'pseudo-start'
4
+ const ATTR_START = 'attr-start'
5
+ const ANY_CHILD = 'any-child'
6
+ const ATTR_COMP = 'attr-comp'
7
+ const ATTR_END = 'attr-end'
8
+ const PSEUDOPSEUDO = '::'
9
+ const PSEUDOCLASS = ':'
10
+ const READY = '(ready)' // 重置标志位
11
+ const OPERATION = 'op'
12
+ const CLASS = 'class'
13
+ const COMMA = 'comma'
14
+ const ATTR = 'attr'
15
+ const SUBJECT = '!'
16
+ const TAG = 'tag'
17
+ const STAR = '*'
18
+ const ID = 'id'
19
+
20
+ export default function tokenize () {
21
+ let escaped = false
22
+ let gathered = []
23
+ let state = READY
24
+ let data = []
25
+ let idx = 0
26
+ let stream
27
+ let length
28
+ let quote
29
+ let depth
30
+ let lhs
31
+ let rhs
32
+ let cmp
33
+ let c
34
+
35
+ return (stream = through(ondata, onend))
36
+
37
+ function ondata (chunk) {
38
+ data = data.concat(chunk.split(''))
39
+ length = data.length
40
+
41
+ while (idx < length && (c = data[idx++])) {
42
+ switch (state) {
43
+ case READY:
44
+ state_ready()
45
+ break
46
+ case ANY_CHILD:
47
+ state_any_child()
48
+ break
49
+ case OPERATION:
50
+ state_op()
51
+ break
52
+ case ATTR_START:
53
+ state_attr_start()
54
+ break
55
+ case ATTR_COMP:
56
+ state_attr_compare()
57
+ break
58
+ case ATTR_END:
59
+ state_attr_end()
60
+ break
61
+ case PSEUDOCLASS:
62
+ case PSEUDOPSEUDO:
63
+ state_pseudo()
64
+ break
65
+ case PSEUDOSTART:
66
+ state_pseudostart()
67
+ break
68
+ case ID:
69
+ case TAG:
70
+ case CLASS:
71
+ state_gather()
72
+ break
73
+ }
74
+ }
75
+
76
+ data = data.slice(idx)
77
+
78
+ if (gathered.length) {
79
+ stream.queue(token())
80
+ }
81
+ }
82
+
83
+ function onend (chunk) {
84
+ // if (arguments.length) {
85
+ // ondata(chunk)
86
+ // }
87
+
88
+ // if (gathered.length) {
89
+ // stream.queue(token())
90
+ // }
91
+ }
92
+
93
+ function state_ready () {
94
+ switch (true) {
95
+ case c === '#':
96
+ state = ID
97
+ break
98
+ case c === '.':
99
+ state = CLASS
100
+ break
101
+ case c === ':':
102
+ state = PSEUDOCLASS
103
+ break
104
+ case c === '[':
105
+ state = ATTR_START
106
+ break
107
+ case c === '!':
108
+ subject()
109
+ break
110
+ case c === '*':
111
+ star()
112
+ break
113
+ case c === ',':
114
+ comma()
115
+ break
116
+ case /[>+~]/.test(c):
117
+ state = OPERATION
118
+ break
119
+ case /\s/.test(c):
120
+ state = ANY_CHILD
121
+ break
122
+ case /[\w\d\-_]/.test(c):
123
+ state = TAG
124
+ --idx
125
+ break
126
+ }
127
+ }
128
+
129
+ function subject () {
130
+ state = SUBJECT
131
+ gathered = ['!']
132
+ stream.queue(token())
133
+ state = READY
134
+ }
135
+
136
+ function star () {
137
+ state = STAR
138
+ gathered = ['*']
139
+ stream.queue(token())
140
+ state = READY
141
+ }
142
+
143
+ function comma () {
144
+ state = COMMA
145
+ gathered = [',']
146
+ stream.queue(token())
147
+ state = READY
148
+ }
149
+
150
+ function state_op () {
151
+ if (/[>+~]/.test(c)) {
152
+ return gathered.push(c)
153
+ }
154
+
155
+ // chomp down the following whitespace.
156
+ if (/\s/.test(c)) {
157
+ return
158
+ }
159
+
160
+ stream.queue(token())
161
+ state = READY
162
+ --idx // 指针左移,归档,开始匹配下一个 token
163
+ }
164
+
165
+ function state_any_child () {
166
+ if (/\s/.test(c)) {
167
+ return
168
+ }
169
+
170
+ if (/[>+~]/.test(c)) {
171
+ --idx
172
+ state = OPERATION
173
+ return state
174
+ // return --idx, (state = OPERATION)
175
+ }
176
+
177
+ // 生成 any_child 节点,并重置状态
178
+ stream.queue(token())
179
+ state = READY
180
+ --idx
181
+ }
182
+
183
+ function state_pseudo () {
184
+ rhs = state
185
+ state_gather(true)
186
+
187
+ if (state !== READY) {
188
+ return
189
+ }
190
+
191
+ if (c === '(') {
192
+ lhs = gathered.join('')
193
+ state = PSEUDOSTART
194
+ gathered.length = 0
195
+ depth = 1
196
+ ++idx
197
+
198
+ return
199
+ }
200
+
201
+ state = PSEUDOCLASS
202
+ stream.queue(token())
203
+ state = READY
204
+ }
205
+
206
+ function state_pseudostart () {
207
+ if (gathered.length === 0 && !quote) {
208
+ quote = /['"]/.test(c) ? c : null
209
+
210
+ if (quote) {
211
+ return
212
+ }
213
+ }
214
+
215
+ if (quote) {
216
+ if (!escaped && c === quote) {
217
+ quote = null
218
+
219
+ return
220
+ }
221
+
222
+ if (c === '\\') {
223
+ escaped ? gathered.push(c) : (escaped = true)
224
+
225
+ return
226
+ }
227
+
228
+ escaped = false
229
+ gathered.push(c)
230
+
231
+ return
232
+ }
233
+
234
+ gathered.push(c)
235
+
236
+ if (c === '(') {
237
+ ++depth
238
+ } else if (c === ')') {
239
+ --depth
240
+ }
241
+
242
+ if (!depth) {
243
+ gathered.pop()
244
+ stream.queue({
245
+ type: rhs,
246
+ data: lhs + '(' + gathered.join('') + ')'
247
+ })
248
+
249
+ state = READY
250
+ lhs = rhs = cmp = null
251
+ gathered.length = 0
252
+ }
253
+ }
254
+
255
+ function state_attr_start () {
256
+ // 在收集字符的阶段,还会有 state 标志位的判断,因此会影响到下面的逻辑执行
257
+ state_gather(true)
258
+
259
+ if (state !== READY) {
260
+ return
261
+ }
262
+
263
+ if (c === ']') {
264
+ state = ATTR
265
+ stream.queue(token())
266
+ state = READY
267
+
268
+ return
269
+ }
270
+
271
+ lhs = gathered.join('')
272
+ gathered.length = 0
273
+ state = ATTR_COMP
274
+ }
275
+
276
+ // 属性选择器:https://www.w3school.com.cn/css/css_attribute_selectors.asp
277
+ function state_attr_compare () {
278
+ if (/[=~|$^*]/.test(c)) {
279
+ gathered.push(c)
280
+ }
281
+
282
+ // 操作符&=
283
+ if (gathered.length === 2 || c === '=') {
284
+ cmp = gathered.join('')
285
+ gathered.length = 0
286
+ state = ATTR_END
287
+ quote = null
288
+ }
289
+ }
290
+
291
+ function state_attr_end () {
292
+ if (!gathered.length && !quote) {
293
+ quote = /['"]/.test(c) ? c : null
294
+
295
+ if (quote) {
296
+ return
297
+ }
298
+ }
299
+
300
+ if (quote) {
301
+ if (!escaped && c === quote) {
302
+ quote = null
303
+
304
+ return
305
+ }
306
+
307
+ if (c === '\\') {
308
+ if (escaped) {
309
+ gathered.push(c)
310
+ }
311
+
312
+ escaped = !escaped
313
+
314
+ return
315
+ }
316
+
317
+ escaped = false
318
+ gathered.push(c)
319
+
320
+ return
321
+ }
322
+
323
+ state_gather(true)
324
+
325
+ if (state !== READY) {
326
+ return
327
+ }
328
+
329
+ stream.queue({
330
+ type: ATTR,
331
+ data: {
332
+ lhs: lhs,
333
+ rhs: gathered.join(''),
334
+ cmp: cmp
335
+ }
336
+ })
337
+
338
+ state = READY
339
+ lhs = rhs = cmp = null
340
+ gathered.length = 0
341
+ }
342
+
343
+ function state_gather (quietly) {
344
+ // 如果是非单词字符,例如 空格。会更新 state 的状态
345
+ if (/[^\d\w\-_]/.test(c) && !escaped) {
346
+ if (c === '\\') {
347
+ escaped = true
348
+ } else {
349
+ !quietly && stream.queue(token())
350
+ state = READY
351
+ --idx
352
+ }
353
+
354
+ return
355
+ }
356
+
357
+ escaped = false
358
+ gathered.push(c)
359
+ }
360
+
361
+ function token () {
362
+ const data = gathered.join('')
363
+
364
+ gathered.length = 0
365
+
366
+ return {
367
+ type: state,
368
+ data: data
369
+ }
370
+ }
371
+ }