'use strict' const fs = require('fs') const path = require('path') const isEqual = require('lodash.isequal') const md5Hex = require('md5-hex') const currentEnv = require('./currentEnv') const hashDependencies = require('./hashDependencies') const hashSources = require('./hashSources') function ensureMissingBabelrcFile (file, cache) { if (cache && cache.fileExistence && cache.fileExistence.has(file)) { return cache.fileExistence.get(file) } const promise = new Promise((resolve, reject) => { fs.access(file, err => { if (err) { if (err.code !== 'ENOENT') { reject(err) } else { resolve(true) } } else { resolve(false) } }) }) if (cache && cache.fileExistence) { cache.fileExistence.set(file, promise) } return promise } class Verifier { constructor (babelrcDir, envNames, dependencies, sources) { Object.assign(this, { babelrcDir, envNames, dependencies, sources }) } selectByEnv (arr, envName, mapFn) { const selectDefault = !this.envNames.has(envName) return arr .filter(item => selectDefault ? item.default : item.envs.has(envName)) .map(mapFn || (item => item)) } cacheKeysForCurrentEnv () { const envName = currentEnv() const getHash = item => item.hash const dependencyHashes = this.selectByEnv(this.dependencies, envName, getHash) const sourceHashes = this.selectByEnv(this.sources, envName, getHash) return { dependencies: md5Hex(dependencyHashes), sources: md5Hex(sourceHashes) } } verifyCurrentEnv (fixedHashes, cache) { const envName = currentEnv() const sourcesToHash = this.selectByEnv(this.sources, envName) const expectedSourceHashes = sourcesToHash.map(item => item.hash) const pendingSourceHashes = hashSources(sourcesToHash, fixedHashes && fixedHashes.sources, cache) let checkedBabelrcFile = true if (this.babelrcDir) { const babelrcFile = path.join(this.babelrcDir, '.babelrc') if (!sourcesToHash.some(item => item.source === babelrcFile)) { checkedBabelrcFile = ensureMissingBabelrcFile(babelrcFile, cache) } } const dependenciesToHash = this.selectByEnv(this.dependencies, envName) const expectedDependencyHashes = dependenciesToHash.map(item => item.hash) const pendingDependencyHashes = hashDependencies(dependenciesToHash, cache) return Promise.all([ pendingSourceHashes, checkedBabelrcFile ]) .then(result => { const sourceHashes = result[0] const babelrcFileIsSame = result[1] if (!babelrcFileIsSame || !isEqual(sourceHashes, expectedSourceHashes)) { return { sourcesChanged: true } } return pendingDependencyHashes .then(dependencyHashes => { const dependenciesChanged = !isEqual(dependencyHashes, expectedDependencyHashes) let verifier = this if (dependenciesChanged) { const dependencies = this.dependencies.map((item, index) => { const hash = dependencyHashes[index] return Object.assign({}, item, { hash }) }) verifier = new Verifier(this.babelrcDir, this.envNames, dependencies, this.sources) } return { sourcesChanged: false, dependenciesChanged, cacheKeys: { dependencies: md5Hex(dependencyHashes), sources: md5Hex(sourceHashes) }, verifier } }) }) .catch(err => { if (err.name === 'NoSourceFileError') { return { missingSource: true } } if (err.name === 'BadDependencyError') { return { badDependency: true } } throw err }) } toBuffer () { return Buffer.from(JSON.stringify({ babelrcDir: this.babelrcDir, envNames: this.envNames, dependencies: this.dependencies, sources: this.sources }, (key, value) => { return key === 'envNames' || key === 'envs' ? Array.from(value) : value }, 2)) } static fromBuffer (buffer) { const json = JSON.parse(buffer.toString('utf8'), (key, value) => { return key === 'envNames' || key === 'envs' ? new Set(value) : value }) return new this(json.babelrcDir, json.envNames, json.dependencies, json.sources) } static hashAndCreate (babelrcDir, envNames, dependencies, sources, fixedSourceHashes, cache) { return Promise.all([ hashDependencies(dependencies, cache), hashSources(sources, fixedSourceHashes, cache) ]) .then(results => { const dependencyHashes = results[0] const sourceHashes = results[1] dependencies.forEach((item, index) => { item.hash = dependencyHashes[index] }) sources.forEach((item, index) => { item.hash = sourceHashes[index] }) return new this(babelrcDir, envNames, dependencies, sources) }) } } module.exports = Verifier