'use strict' const fs = require('graceful-fs') const mm = require('minimatch') const isBinaryFile = require('isbinaryfile') const _ = require('lodash') const CryptoUtils = require('./utils/crypto-utils') const log = require('./logger').create('preprocess') function createNextProcessor (preprocessors, file, done) { return function nextPreprocessor (error, content) { // normalize B-C if (arguments.length === 1 && typeof error === 'string') { content = error error = null } if (error) { file.content = null file.contentPath = null return done(error) } if (!preprocessors.length) { file.contentPath = null file.content = content file.sha = CryptoUtils.sha1(content) return done() } preprocessors.shift()(content, file, nextPreprocessor) } } function createPreprocessor (config, basePath, injector) { const emitter = injector.get('emitter') const alreadyDisplayedErrors = {} const instances = {} let patterns = Object.keys(config) function instantiatePreprocessor (name) { if (alreadyDisplayedErrors[name]) { return } let p try { p = injector.get('preprocessor:' + name) } catch (e) { if (e.message.includes(`No provider for "preprocessor:${name}"`)) { log.error(`Can not load "${name}", it is not registered!\n Perhaps you are missing some plugin?`) } else { log.error(`Can not load "${name}"!\n ` + e.stack) } alreadyDisplayedErrors[name] = true emitter.emit('load_error', 'preprocessor', name) } return p } let allPreprocessors = [] patterns.forEach((pattern) => { allPreprocessors = _.union(allPreprocessors, config[pattern]) }) allPreprocessors.forEach(instantiatePreprocessor) return function preprocess (file, done) { patterns = Object.keys(config) let retryCount = 0 let maxRetries = 3 function readFileCallback (err, buffer) { if (err) { log.warn(err) if (retryCount < maxRetries) { retryCount++ log.warn('retrying ' + retryCount) fs.readFile(file.originalPath, readFileCallback) return } else { throw err } } isBinaryFile(buffer, buffer.length, function (err, isBinary) { if (err) { throw err } let preprocessorNames = [] patterns.forEach((pattern) => { if (mm(file.originalPath, pattern, {dot: true})) { preprocessorNames = _.union(preprocessorNames, config[pattern]) } }) let preprocessors = [] const nextPreprocessor = createNextProcessor(preprocessors, file, done) preprocessorNames.forEach((name) => { const p = instances[name] || instantiatePreprocessor(name) if (p == null) { if (!alreadyDisplayedErrors[name]) { alreadyDisplayedErrors[name] = true log.error(`Failed to instantiate preprocessor ${name}`) emitter.emit('load_error', 'preprocessor', name) } return } instances[name] = p if (!isBinary || p.handleBinaryFiles) { preprocessors.push(p) } else { log.warn(`Ignored preprocessing ${file.originalPath} because ${name} has handleBinaryFiles=false.`) } }) nextPreprocessor(null, isBinary ? buffer : buffer.toString()) }) } return fs.readFile(file.originalPath, readFileCallback) } } createPreprocessor.$inject = ['config.preprocessors', 'config.basePath', 'injector'] exports.createPreprocessor = createPreprocessor