1 | 'use strict'
|
2 |
|
3 | const util = require('util')
|
4 | const fs = require('graceful-fs')
|
5 |
|
6 | const readFile = util.promisify(fs.readFile.bind(fs))
|
7 | const 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 |
|
23 | const mm = require('minimatch')
|
24 | const { isBinaryFile } = require('isbinaryfile')
|
25 | const _ = require('lodash')
|
26 | const CryptoUtils = require('./utils/crypto-utils')
|
27 |
|
28 | const log = require('./logger').create('preprocess')
|
29 |
|
30 | function executeProcessor (process, file, content) {
|
31 | let done = null
|
32 | const donePromise = new Promise((resolve, reject) => {
|
33 | done = function (error, content) {
|
34 |
|
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 |
|
50 | return content
|
51 | }
|
52 |
|
53 | return donePromise
|
54 | })
|
55 | }
|
56 |
|
57 | async function runProcessors (preprocessors, file, content) {
|
58 | try {
|
59 | for (let 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 |
|
73 | function 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 |
|
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 |
|
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 |
|
138 | createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'injector']
|
139 | exports.createPriorityPreprocessor = createPriorityPreprocessor
|