source: trip-planner-front/node_modules/postcss/lib/lazy-result.js@ 1ad8e64

Last change on this file since 1ad8e64 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 13.2 KB
Line 
1'use strict'
2
3let { isClean, my } = require('./symbols')
4let MapGenerator = require('./map-generator')
5let stringify = require('./stringify')
6let Container = require('./container')
7let Document = require('./document')
8let warnOnce = require('./warn-once')
9let Result = require('./result')
10let parse = require('./parse')
11let Root = require('./root')
12
13const TYPE_TO_CLASS_NAME = {
14 document: 'Document',
15 root: 'Root',
16 atrule: 'AtRule',
17 rule: 'Rule',
18 decl: 'Declaration',
19 comment: 'Comment'
20}
21
22const PLUGIN_PROPS = {
23 postcssPlugin: true,
24 prepare: true,
25 Once: true,
26 Document: true,
27 Root: true,
28 Declaration: true,
29 Rule: true,
30 AtRule: true,
31 Comment: true,
32 DeclarationExit: true,
33 RuleExit: true,
34 AtRuleExit: true,
35 CommentExit: true,
36 RootExit: true,
37 DocumentExit: true,
38 OnceExit: true
39}
40
41const NOT_VISITORS = {
42 postcssPlugin: true,
43 prepare: true,
44 Once: true
45}
46
47const CHILDREN = 0
48
49function isPromise(obj) {
50 return typeof obj === 'object' && typeof obj.then === 'function'
51}
52
53function getEvents(node) {
54 let key = false
55 let type = TYPE_TO_CLASS_NAME[node.type]
56 if (node.type === 'decl') {
57 key = node.prop.toLowerCase()
58 } else if (node.type === 'atrule') {
59 key = node.name.toLowerCase()
60 }
61
62 if (key && node.append) {
63 return [
64 type,
65 type + '-' + key,
66 CHILDREN,
67 type + 'Exit',
68 type + 'Exit-' + key
69 ]
70 } else if (key) {
71 return [type, type + '-' + key, type + 'Exit', type + 'Exit-' + key]
72 } else if (node.append) {
73 return [type, CHILDREN, type + 'Exit']
74 } else {
75 return [type, type + 'Exit']
76 }
77}
78
79function toStack(node) {
80 let events
81 if (node.type === 'document') {
82 events = ['Document', CHILDREN, 'DocumentExit']
83 } else if (node.type === 'root') {
84 events = ['Root', CHILDREN, 'RootExit']
85 } else {
86 events = getEvents(node)
87 }
88
89 return {
90 node,
91 events,
92 eventIndex: 0,
93 visitors: [],
94 visitorIndex: 0,
95 iterator: 0
96 }
97}
98
99function cleanMarks(node) {
100 node[isClean] = false
101 if (node.nodes) node.nodes.forEach(i => cleanMarks(i))
102 return node
103}
104
105let postcss = {}
106
107class LazyResult {
108 constructor(processor, css, opts) {
109 this.stringified = false
110 this.processed = false
111
112 let root
113 if (
114 typeof css === 'object' &&
115 css !== null &&
116 (css.type === 'root' || css.type === 'document')
117 ) {
118 root = cleanMarks(css)
119 } else if (css instanceof LazyResult || css instanceof Result) {
120 root = cleanMarks(css.root)
121 if (css.map) {
122 if (typeof opts.map === 'undefined') opts.map = {}
123 if (!opts.map.inline) opts.map.inline = false
124 opts.map.prev = css.map
125 }
126 } else {
127 let parser = parse
128 if (opts.syntax) parser = opts.syntax.parse
129 if (opts.parser) parser = opts.parser
130 if (parser.parse) parser = parser.parse
131
132 try {
133 root = parser(css, opts)
134 } catch (error) {
135 this.processed = true
136 this.error = error
137 }
138
139 if (root && !root[my]) {
140 // istanbul ignore next
141 Container.rebuild(root)
142 }
143 }
144
145 this.result = new Result(processor, root, opts)
146 this.helpers = { ...postcss, result: this.result, postcss }
147 this.plugins = this.processor.plugins.map(plugin => {
148 if (typeof plugin === 'object' && plugin.prepare) {
149 return { ...plugin, ...plugin.prepare(this.result) }
150 } else {
151 return plugin
152 }
153 })
154 }
155
156 get [Symbol.toStringTag]() {
157 return 'LazyResult'
158 }
159
160 get processor() {
161 return this.result.processor
162 }
163
164 get opts() {
165 return this.result.opts
166 }
167
168 get css() {
169 return this.stringify().css
170 }
171
172 get content() {
173 return this.stringify().content
174 }
175
176 get map() {
177 return this.stringify().map
178 }
179
180 get root() {
181 return this.sync().root
182 }
183
184 get messages() {
185 return this.sync().messages
186 }
187
188 warnings() {
189 return this.sync().warnings()
190 }
191
192 toString() {
193 return this.css
194 }
195
196 then(onFulfilled, onRejected) {
197 if (process.env.NODE_ENV !== 'production') {
198 if (!('from' in this.opts)) {
199 warnOnce(
200 'Without `from` option PostCSS could generate wrong source map ' +
201 'and will not find Browserslist config. Set it to CSS file path ' +
202 'or to `undefined` to prevent this warning.'
203 )
204 }
205 }
206 return this.async().then(onFulfilled, onRejected)
207 }
208
209 catch(onRejected) {
210 return this.async().catch(onRejected)
211 }
212
213 finally(onFinally) {
214 return this.async().then(onFinally, onFinally)
215 }
216
217 async() {
218 if (this.error) return Promise.reject(this.error)
219 if (this.processed) return Promise.resolve(this.result)
220 if (!this.processing) {
221 this.processing = this.runAsync()
222 }
223 return this.processing
224 }
225
226 sync() {
227 if (this.error) throw this.error
228 if (this.processed) return this.result
229 this.processed = true
230
231 if (this.processing) {
232 throw this.getAsyncError()
233 }
234
235 for (let plugin of this.plugins) {
236 let promise = this.runOnRoot(plugin)
237 if (isPromise(promise)) {
238 throw this.getAsyncError()
239 }
240 }
241
242 this.prepareVisitors()
243 if (this.hasListener) {
244 let root = this.result.root
245 while (!root[isClean]) {
246 root[isClean] = true
247 this.walkSync(root)
248 }
249 if (this.listeners.OnceExit) {
250 if (root.type === 'document') {
251 for (let subRoot of root.nodes) {
252 this.visitSync(this.listeners.OnceExit, subRoot)
253 }
254 } else {
255 this.visitSync(this.listeners.OnceExit, root)
256 }
257 }
258 }
259
260 return this.result
261 }
262
263 stringify() {
264 if (this.error) throw this.error
265 if (this.stringified) return this.result
266 this.stringified = true
267
268 this.sync()
269
270 let opts = this.result.opts
271 let str = stringify
272 if (opts.syntax) str = opts.syntax.stringify
273 if (opts.stringifier) str = opts.stringifier
274 if (str.stringify) str = str.stringify
275
276 let map = new MapGenerator(str, this.result.root, this.result.opts)
277 let data = map.generate()
278 this.result.css = data[0]
279 this.result.map = data[1]
280
281 return this.result
282 }
283
284 walkSync(node) {
285 node[isClean] = true
286 let events = getEvents(node)
287 for (let event of events) {
288 if (event === CHILDREN) {
289 if (node.nodes) {
290 node.each(child => {
291 if (!child[isClean]) this.walkSync(child)
292 })
293 }
294 } else {
295 let visitors = this.listeners[event]
296 if (visitors) {
297 if (this.visitSync(visitors, node.toProxy())) return
298 }
299 }
300 }
301 }
302
303 visitSync(visitors, node) {
304 for (let [plugin, visitor] of visitors) {
305 this.result.lastPlugin = plugin
306 let promise
307 try {
308 promise = visitor(node, this.helpers)
309 } catch (e) {
310 throw this.handleError(e, node.proxyOf)
311 }
312 if (node.type !== 'root' && node.type !== 'document' && !node.parent) {
313 return true
314 }
315 if (isPromise(promise)) {
316 throw this.getAsyncError()
317 }
318 }
319 }
320
321 runOnRoot(plugin) {
322 this.result.lastPlugin = plugin
323 try {
324 if (typeof plugin === 'object' && plugin.Once) {
325 if (this.result.root.type === 'document') {
326 let roots = this.result.root.nodes.map(root =>
327 plugin.Once(root, this.helpers)
328 )
329
330 if (isPromise(roots[0])) {
331 return Promise.all(roots)
332 }
333
334 return roots
335 }
336
337 return plugin.Once(this.result.root, this.helpers)
338 } else if (typeof plugin === 'function') {
339 return plugin(this.result.root, this.result)
340 }
341 } catch (error) {
342 throw this.handleError(error)
343 }
344 }
345
346 getAsyncError() {
347 throw new Error('Use process(css).then(cb) to work with async plugins')
348 }
349
350 handleError(error, node) {
351 let plugin = this.result.lastPlugin
352 try {
353 if (node) node.addToError(error)
354 this.error = error
355 if (error.name === 'CssSyntaxError' && !error.plugin) {
356 error.plugin = plugin.postcssPlugin
357 error.setMessage()
358 } else if (plugin.postcssVersion) {
359 if (process.env.NODE_ENV !== 'production') {
360 let pluginName = plugin.postcssPlugin
361 let pluginVer = plugin.postcssVersion
362 let runtimeVer = this.result.processor.version
363 let a = pluginVer.split('.')
364 let b = runtimeVer.split('.')
365
366 if (a[0] !== b[0] || parseInt(a[1]) > parseInt(b[1])) {
367 console.error(
368 'Unknown error from PostCSS plugin. Your current PostCSS ' +
369 'version is ' +
370 runtimeVer +
371 ', but ' +
372 pluginName +
373 ' uses ' +
374 pluginVer +
375 '. Perhaps this is the source of the error below.'
376 )
377 }
378 }
379 }
380 } catch (err) {
381 // istanbul ignore next
382 if (console && console.error) console.error(err)
383 }
384 return error
385 }
386
387 async runAsync() {
388 this.plugin = 0
389 for (let i = 0; i < this.plugins.length; i++) {
390 let plugin = this.plugins[i]
391 let promise = this.runOnRoot(plugin)
392 if (isPromise(promise)) {
393 try {
394 await promise
395 } catch (error) {
396 throw this.handleError(error)
397 }
398 }
399 }
400
401 this.prepareVisitors()
402 if (this.hasListener) {
403 let root = this.result.root
404 while (!root[isClean]) {
405 root[isClean] = true
406 let stack = [toStack(root)]
407 while (stack.length > 0) {
408 let promise = this.visitTick(stack)
409 if (isPromise(promise)) {
410 try {
411 await promise
412 } catch (e) {
413 let node = stack[stack.length - 1].node
414 throw this.handleError(e, node)
415 }
416 }
417 }
418 }
419
420 if (this.listeners.OnceExit) {
421 for (let [plugin, visitor] of this.listeners.OnceExit) {
422 this.result.lastPlugin = plugin
423 try {
424 if (root.type === 'document') {
425 let roots = root.nodes.map(subRoot =>
426 visitor(subRoot, this.helpers)
427 )
428
429 await Promise.all(roots)
430 } else {
431 await visitor(root, this.helpers)
432 }
433 } catch (e) {
434 throw this.handleError(e)
435 }
436 }
437 }
438 }
439
440 this.processed = true
441 return this.stringify()
442 }
443
444 prepareVisitors() {
445 this.listeners = {}
446 let add = (plugin, type, cb) => {
447 if (!this.listeners[type]) this.listeners[type] = []
448 this.listeners[type].push([plugin, cb])
449 }
450 for (let plugin of this.plugins) {
451 if (typeof plugin === 'object') {
452 for (let event in plugin) {
453 if (!PLUGIN_PROPS[event] && /^[A-Z]/.test(event)) {
454 throw new Error(
455 `Unknown event ${event} in ${plugin.postcssPlugin}. ` +
456 `Try to update PostCSS (${this.processor.version} now).`
457 )
458 }
459 if (!NOT_VISITORS[event]) {
460 if (typeof plugin[event] === 'object') {
461 for (let filter in plugin[event]) {
462 if (filter === '*') {
463 add(plugin, event, plugin[event][filter])
464 } else {
465 add(
466 plugin,
467 event + '-' + filter.toLowerCase(),
468 plugin[event][filter]
469 )
470 }
471 }
472 } else if (typeof plugin[event] === 'function') {
473 add(plugin, event, plugin[event])
474 }
475 }
476 }
477 }
478 }
479 this.hasListener = Object.keys(this.listeners).length > 0
480 }
481
482 visitTick(stack) {
483 let visit = stack[stack.length - 1]
484 let { node, visitors } = visit
485
486 if (node.type !== 'root' && node.type !== 'document' && !node.parent) {
487 stack.pop()
488 return
489 }
490
491 if (visitors.length > 0 && visit.visitorIndex < visitors.length) {
492 let [plugin, visitor] = visitors[visit.visitorIndex]
493 visit.visitorIndex += 1
494 if (visit.visitorIndex === visitors.length) {
495 visit.visitors = []
496 visit.visitorIndex = 0
497 }
498 this.result.lastPlugin = plugin
499 try {
500 return visitor(node.toProxy(), this.helpers)
501 } catch (e) {
502 throw this.handleError(e, node)
503 }
504 }
505
506 if (visit.iterator !== 0) {
507 let iterator = visit.iterator
508 let child
509 while ((child = node.nodes[node.indexes[iterator]])) {
510 node.indexes[iterator] += 1
511 if (!child[isClean]) {
512 child[isClean] = true
513 stack.push(toStack(child))
514 return
515 }
516 }
517 visit.iterator = 0
518 delete node.indexes[iterator]
519 }
520
521 let events = visit.events
522 while (visit.eventIndex < events.length) {
523 let event = events[visit.eventIndex]
524 visit.eventIndex += 1
525 if (event === CHILDREN) {
526 if (node.nodes && node.nodes.length) {
527 node[isClean] = true
528 visit.iterator = node.getIterator()
529 }
530 return
531 } else if (this.listeners[event]) {
532 visit.visitors = this.listeners[event]
533 return
534 }
535 }
536 stack.pop()
537 }
538}
539
540LazyResult.registerPostcss = dependant => {
541 postcss = dependant
542}
543
544module.exports = LazyResult
545LazyResult.default = LazyResult
546
547Root.registerLazyResult(LazyResult)
548Document.registerLazyResult(LazyResult)
Note: See TracBrowser for help on using the repository browser.