|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- 'use strict'
-
- const cp = require('child_process') // eslint-disable-line security/detect-child-process
- const path = require('path')
- const {promisify} = require('util')
-
- const fs = require('graceful-fs')
- const flattenDeep = require('lodash.flattendeep')
- const hasha = require('hasha')
- const releaseZalgo = require('release-zalgo')
-
- const PACKAGE_FILE = require.resolve('./package.json')
- const TEN_MEBIBYTE = 1024 * 1024 * 10
- const execFile = promisify(cp.execFile)
-
- const readFile = {
- async: promisify(fs.readFile),
- sync: fs.readFileSync
- }
-
- const tryReadFile = {
- async (file) {
- return readFile.async(file).catch(() => null)
- },
-
- sync (file) {
- try {
- return fs.readFileSync(file)
- } catch (err) {
- return null
- }
- }
- }
-
- const tryExecFile = {
- async (file, args, options) {
- return execFile(file, args, options)
- .then(({stdout}) => stdout)
- .catch(() => null)
- },
-
- sync (file, args, options) {
- try {
- return cp.execFileSync(file, args, options)
- } catch (err) {
- return null
- }
- }
- }
-
- const git = {
- tryGetRef (zalgo, dir, head) {
- const m = /^ref: (.+)$/.exec(head.toString('utf8').trim())
- if (!m) return null
-
- return zalgo.run(tryReadFile, path.join(dir, '.git', m[1]))
- },
-
- tryGetDiff (zalgo, dir) {
- return zalgo.run(tryExecFile,
- 'git',
- // Attempt to get consistent output no matter the platform. Diff both
- // staged and unstaged changes.
- ['--no-pager', 'diff', 'HEAD', '--no-color', '--no-ext-diff'],
- {
- cwd: dir,
- maxBuffer: TEN_MEBIBYTE,
- env: Object.assign({}, process.env, {
- // Force the GIT_DIR to prevent git from diffing a parent repository
- // in case the directory isn't actually a repository.
- GIT_DIR: path.join(dir, '.git')
- }),
- // Ignore stderr.
- stdio: ['ignore', 'pipe', 'ignore']
- })
- }
- }
-
- function addPackageData (zalgo, pkgPath) {
- const dir = path.dirname(pkgPath)
-
- return zalgo.all([
- dir,
- zalgo.run(readFile, pkgPath),
- zalgo.run(tryReadFile, path.join(dir, '.git', 'HEAD'))
- .then(head => {
- if (!head) return []
-
- return zalgo.all([
- zalgo.run(tryReadFile, path.join(dir, '.git', 'packed-refs')),
- git.tryGetRef(zalgo, dir, head),
- git.tryGetDiff(zalgo, dir)
- ])
- .then(results => {
- return [head].concat(results.filter(Boolean))
- })
- })
- ])
- }
-
- function computeHash (zalgo, paths, pepper, salt) {
- const inputs = []
- if (pepper) inputs.push(pepper)
-
- if (typeof salt !== 'undefined') {
- if (Buffer.isBuffer(salt) || typeof salt === 'string') {
- inputs.push(salt)
- } else if (typeof salt === 'object' && salt !== null) {
- inputs.push(JSON.stringify(salt))
- } else {
- throw new TypeError('Salt must be an Array, Buffer, Object or string')
- }
- }
-
- // TODO: Replace flattenDeep with Array#flat(Infinity) after node.js 10 is dropped
- return zalgo.all(paths.map(pkgPath => addPackageData(zalgo, pkgPath)))
- .then(furtherInputs => hasha(flattenDeep([inputs, furtherInputs]), {algorithm: 'sha256'}))
- }
-
- let ownHash = null
- let ownHashPromise = null
- function run (zalgo, paths, salt) {
- if (!ownHash) {
- return zalgo.run({
- async () {
- if (!ownHashPromise) {
- ownHashPromise = computeHash(zalgo, [PACKAGE_FILE])
- }
- return ownHashPromise
- },
- sync () {
- return computeHash(zalgo, [PACKAGE_FILE])
- }
- })
- .then(hash => {
- ownHash = Buffer.from(hash, 'hex')
- ownHashPromise = null
- return run(zalgo, paths, salt)
- })
- }
-
- if (paths === PACKAGE_FILE && typeof salt === 'undefined') {
- // Special case that allow the pepper value to be obtained. Mainly here for
- // testing purposes.
- return zalgo.returns(ownHash.toString('hex'))
- }
-
- paths = Array.isArray(paths) ? paths : [paths]
- return computeHash(zalgo, paths, ownHash, salt)
- }
-
- module.exports = (paths, salt) => {
- try {
- return run(releaseZalgo.async(), paths, salt)
- } catch (err) {
- return Promise.reject(err)
- }
- }
- module.exports.sync = (paths, salt) => {
- const result = run(releaseZalgo.sync(), paths, salt)
- return releaseZalgo.unwrapSync(result)
- }
|