UNPKG

4.16 kBJavaScriptView Raw
1'use strict'
2
3const util = require('util')
4const fs = require('graceful-fs')
5// bind need only for mock unit tests
6const readFile = util.promisify(fs.readFile.bind(fs))
7const tryToRead = function (path, log) {
8 const maxRetries = 3
9 let promise = readFile(path)
10 for (let retryCount = 1; retryCount <= maxRetries; retryCount++) {
11 promise = promise.catch((err) => {
12 log.warn(err)
13 log.warn('retrying ' + retryCount)
14 return readFile(path)
15 })
16 }
17 return promise.catch((err) => {
18 log.warn(err)
19 return Promise.reject(err)
20 })
21}
22
23const mm = require('minimatch')
24const { isBinaryFile } = require('isbinaryfile')
25const _ = require('lodash')
26const CryptoUtils = require('./utils/crypto-utils')
27
28const log = require('./logger').create('preprocess')
29
30function executeProcessor (process, file, content) {
31 let done = null
32 const donePromise = new Promise((resolve, reject) => {
33 done = function (error, content) {
34 // normalize B-C
35 if (arguments.length === 1 && typeof error === 'string') {
36 content = error
37 error = null
38 }
39 if (error) {
40 reject(error)
41 } else {
42 resolve(content)
43 }
44 }
45 })
46
47 return (process(content, file, done) || Promise.resolve()).then((content) => {
48 if (content) {
49 // async process correctly returned content
50 return content
51 }
52 // process called done() (Either old sync api or an async function that did not return content)
53 return donePromise
54 })
55}
56
57async function runProcessors (preprocessors, file, content) {
58 try {
59 for (const process of preprocessors) {
60 content = await executeProcessor(process, file, content)
61 }
62 } catch (error) {
63 file.contentPath = null
64 file.content = null
65 throw error
66 }
67
68 file.contentPath = null
69 file.content = content
70 file.sha = CryptoUtils.sha1(content)
71}
72
73function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, injector) {
74 const emitter = injector.get('emitter')
75 const instances = new Map()
76
77 function instantiatePreprocessor (name) {
78 if (instances.has(name)) {
79 return instances.get(name)
80 }
81
82 let p
83 try {
84 p = injector.get('preprocessor:' + name)
85 if (!p) {
86 log.error(`Failed to instantiate preprocessor ${name}`)
87 emitter.emit('load_error', 'preprocessor', name)
88 }
89 } catch (e) {
90 if (e.message.includes(`No provider for "preprocessor:${name}"`)) {
91 log.error(`Can not load "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
92 } else {
93 log.error(`Can not load "${name}"!\n ` + e.stack)
94 }
95 emitter.emit('load_error', 'preprocessor', name)
96 }
97
98 instances.set(name, p)
99 return p
100 }
101 _.union.apply(_, Object.values(config)).forEach(instantiatePreprocessor)
102
103 return async function preprocess (file) {
104 const buffer = await tryToRead(file.originalPath, log)
105 let isBinary = file.isBinary
106 if (isBinary == null) {
107 // Pattern did not specify, probe for it.
108 isBinary = await isBinaryFile(buffer, buffer.length)
109 }
110
111 const preprocessorNames = Object.keys(config).reduce((ppNames, pattern) => {
112 if (mm(file.originalPath, pattern, { dot: true })) {
113 ppNames = _.union(ppNames, config[pattern])
114 }
115 return ppNames
116 }, [])
117
118 // Apply preprocessor priority.
119 const preprocessors = preprocessorNames
120 .map((name) => [name, preprocessorPriority[name] || 0])
121 .sort((a, b) => b[1] - a[1])
122 .map((duo) => duo[0])
123 .reduce((preProcs, name) => {
124 const p = instantiatePreprocessor(name)
125
126 if (!isBinary || (p && p.handleBinaryFiles)) {
127 preProcs.push(p)
128 } else {
129 log.warn(`Ignored preprocessing ${file.originalPath} because ${name} has handleBinaryFiles=false.`)
130 }
131 return preProcs
132 }, [])
133
134 await runProcessors(preprocessors, file, isBinary ? buffer : buffer.toString())
135 }
136}
137
138createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'injector']
139exports.createPriorityPreprocessor = createPriorityPreprocessor