| '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 |