jazelle/utils/install-deps.js (106 lines of code) (raw):

// @flow const {dirname, relative} = require('path'); const {getHash} = require('./checksum-cache.js'); const {merge} = require('./lockfile.js'); const { read, move, exec, spawn, write, exists, remove, } = require('./node-helpers.js'); const {node, yarn} = require('./binary-paths.js'); const {setupSymlinks} = require('./setup-symlinks.js'); const {executeHook} = require('./execute-hook.js'); /*:: import type {Metadata} from './get-local-dependencies.js'; import type {Hooks} from './get-manifest.js'; export type InstallDepsArgs = { root: string, cwd: string, deps?: Array<Metadata>, ignore?: Array<Metadata>, hooks?: Hooks, } export type InstallDeps = (InstallDepsArgs) => Promise<void> */ const installDeps /*: InstallDeps */ = async ({ root, cwd, deps = [], ignore = [], hooks: {preinstall, postinstall} = {}, }) => { const modulesDir = `${root}/node_modules`; const sandbox = relative(root, cwd); const bin = `${root}/third_party/jazelle/temp/${sandbox}`; await spawn('mkdir', ['-p', bin], {cwd: root}); // we may already have things installed. If so, check whether we need to reinstall let needsInstall = true; const hash = await getHash(`${cwd}/yarn.lock`); if (await exists(`${modulesDir}/.jazelle-source`)) { const data = await read(`${modulesDir}/.jazelle-source`, 'utf8'); const prev = JSON.parse(data); if (await exists(prev.dir)) await remove(prev.dir); await move(modulesDir, prev.dir); if (await exists(`${bin}/node_modules/.jazelle-source`)) { const data = await read(`${bin}/node_modules/.jazelle-source`, 'utf8'); const curr = JSON.parse(data); if (hash === curr.hash) needsInstall = false; } } await generateLockfile({root, bin, deps, ignore}); // jazelle hook await executeHook(preinstall, root); await executeNpmHooks(deps, 'preinstall'); // install external deps if (needsInstall) await generateNodeModules(bin); if (await exists(modulesDir)) { await remove(modulesDir); } await move(`${bin}/node_modules`, modulesDir); await setupSymlinks({root, deps}); await executeNpmHooks(deps, 'postinstall'); await executeHook(postinstall, root); // record the source of this node_modules so we are able to recycle on future installs if needed const sourceFile = `${modulesDir}/.jazelle-source`; const data = { dir: `${bin}/node_modules`, hash, upstreams: deps.map(d => d.dir), }; await write(sourceFile, JSON.stringify(data, null, 2), 'utf8'); }; const executeNpmHooks = async (deps, type) => { const nodePath = dirname(node); for (const dep of deps) { const options = { env: { ...process.env, PATH: `${nodePath}:${String(process.env.PATH)}`, }, cwd: dep.dir, }; const stdio = [process.stdout, process.stderr]; if (dep.meta.scripts && dep.meta.scripts[type]) { await exec(dep.meta.scripts[type], options, stdio); } } }; const generateLockfile = async ({root, bin, deps, ignore}) => { const tmp = `${root}/third_party/jazelle/temp/yarn-utilities-tmp`; await spawn('rm', ['-f', `${bin}/yarn.lock`], {cwd: root}); await spawn('rm', ['-f', `${bin}/package.json`], {cwd: root}); await merge({ roots: deps.map(dep => dep.dir), out: bin, ignore: ignore.map(dep => dep.meta.name), tmp, }); }; const generateNodeModules = async bin => { const nodePath = dirname(node); const args = [ yarn, 'install', '--production=false', '--pure-lockfile', '--non-interactive', '--color=always', ]; const options = { env: { ...process.env, PATH: `${nodePath}:${String(process.env.PATH)}`, }, cwd: bin, stdio: 'inherit', }; await spawn(node, args, options); }; module.exports = {installDeps};