'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