112 lines
3.3 KiB
JavaScript
112 lines
3.3 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
const util = require('util')
|
||
|
const fs = require('graceful-fs')
|
||
|
// bind need only for mock unit tests
|
||
|
const readFile = util.promisify(fs.readFile.bind(fs))
|
||
|
const tryToRead = function (path, log) {
|
||
|
const maxRetries = 3
|
||
|
let promise = readFile(path)
|
||
|
for (let retryCount = 1; retryCount <= maxRetries; retryCount++) {
|
||
|
promise = promise.catch((err) => {
|
||
|
log.warn(err)
|
||
|
log.warn('retrying ' + retryCount)
|
||
|
return readFile(path)
|
||
|
})
|
||
|
}
|
||
|
return promise.catch((err) => {
|
||
|
log.warn(err)
|
||
|
return Promise.reject(err)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
const mm = require('minimatch')
|
||
|
const { isBinaryFile } = require('isbinaryfile')
|
||
|
const _ = require('lodash')
|
||
|
const CryptoUtils = require('./utils/crypto-utils')
|
||
|
|
||
|
const log = require('./logger').create('preprocess')
|
||
|
|
||
|
function executeProcessor (process, file, content) {
|
||
|
let done = null
|
||
|
const donePromise = new Promise((resolve, reject) => {
|
||
|
done = function (error, content) {
|
||
|
// normalize B-C
|
||
|
if (arguments.length === 1 && typeof error === 'string') {
|
||
|
content = error
|
||
|
error = null
|
||
|
}
|
||
|
if (error) {
|
||
|
reject(error)
|
||
|
} else {
|
||
|
resolve(content)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
return (process(content, file, done) || Promise.resolve()).then((content) => {
|
||
|
if (content) {
|
||
|
// async process correctly returned content
|
||
|
return content
|
||
|
}
|
||
|
// process called done() (Either old sync api or an async function that did not return content)
|
||
|
return donePromise
|
||
|
})
|
||
|
}
|
||
|
|
||
|
async function runProcessors (preprocessors, file, content) {
|
||
|
try {
|
||
|
for (const process of preprocessors) {
|
||
|
content = await executeProcessor(process, file, content)
|
||
|
}
|
||
|
} catch (error) {
|
||
|
file.contentPath = null
|
||
|
file.content = null
|
||
|
throw error
|
||
|
}
|
||
|
|
||
|
file.contentPath = null
|
||
|
file.content = content
|
||
|
file.sha = CryptoUtils.sha1(content)
|
||
|
}
|
||
|
|
||
|
function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, instantiatePlugin) {
|
||
|
_.union.apply(_, Object.values(config)).forEach((name) => instantiatePlugin('preprocessor', name))
|
||
|
return async function preprocess (file) {
|
||
|
const buffer = await tryToRead(file.originalPath, log)
|
||
|
let isBinary = file.isBinary
|
||
|
if (isBinary == null) {
|
||
|
// Pattern did not specify, probe for it.
|
||
|
isBinary = await isBinaryFile(buffer, buffer.length)
|
||
|
}
|
||
|
|
||
|
const preprocessorNames = Object.keys(config).reduce((ppNames, pattern) => {
|
||
|
if (mm(file.originalPath, pattern, { dot: true })) {
|
||
|
ppNames = _.union(ppNames, config[pattern])
|
||
|
}
|
||
|
return ppNames
|
||
|
}, [])
|
||
|
|
||
|
// Apply preprocessor priority.
|
||
|
const preprocessors = preprocessorNames
|
||
|
.map((name) => [name, preprocessorPriority[name] || 0])
|
||
|
.sort((a, b) => b[1] - a[1])
|
||
|
.map((duo) => duo[0])
|
||
|
.reduce((preProcs, name) => {
|
||
|
const p = instantiatePlugin('preprocessor', name)
|
||
|
|
||
|
if (!isBinary || (p && p.handleBinaryFiles)) {
|
||
|
preProcs.push(p)
|
||
|
} else {
|
||
|
log.warn(`Ignored preprocessing ${file.originalPath} because ${name} has handleBinaryFiles=false.`)
|
||
|
}
|
||
|
return preProcs
|
||
|
}, [])
|
||
|
|
||
|
await runProcessors(preprocessors, file, isBinary ? buffer : buffer.toString())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'instantiatePlugin']
|
||
|
exports.createPriorityPreprocessor = createPriorityPreprocessor
|