@pika/pack
Version:
package building, reimagined.
287 lines (286 loc) • 12.1 kB
JavaScript
import * as child from './child.js';
import { fixCmdWinSlashes } from './fix-cmd-win-slashes.js';
// export const IGNORE_MANIFEST_KEYS: Set<string> = new Set(['readme', 'notice', 'licenseText']);
// // We treat these configs as internal, thus not expose them to process.env.
// // This helps us avoid some gyp issues when building native modules.
// // See https://github.com/yarnpkg/yarn/issues/2286.
// const IGNORE_CONFIG_KEYS = ['lastUpdateCheck'];
// async function getPnpParameters(config: Config): Promise<Array<string>> {
// if (await fs.exists(`${config.lockfileFolder}/${constants.PNP_FILENAME}`)) {
// return ['-r', `${config.lockfileFolder}/${constants.PNP_FILENAME}`];
// } else {
// return [];
// }
// }
// let wrappersFolder = null;
// export async function getWrappersFolder(config: Config): Promise<string> {
// if (wrappersFolder) {
// return wrappersFolder;
// }
// wrappersFolder = await fs.makeTempDir();
// await makePortableProxyScript(process.execPath, wrappersFolder, {
// proxyBasename: 'node',
// prependArguments: [...(await getPnpParameters(config))],
// });
// await makePortableProxyScript(process.execPath, wrappersFolder, {
// proxyBasename: 'pika',
// prependArguments: [process.argv[1]],
// });
// return wrappersFolder;
// }
// const INVALID_CHAR_REGEX = /\W/g;
export async function makeEnv() {
// stage: string,
// cwd: string,
// config: Config,
const env = {
NODE: process.execPath,
INIT_CWD: process.cwd(),
// This lets `process.env.NODE` to override our `process.execPath`.
// This is a bit confusing but it is how `npm` was designed so we
// try to be compatible with that.
...process.env,
};
return env;
}
// // Merge in the `env` object specified in .pikarc
// const customEnv = config.getOption('env');
// if (customEnv && typeof customEnv === 'object') {
// Object.assign(env, customEnv);
// }
// env.npm_lifecycle_event = stage;
// env.npm_node_execpath = env.NODE;
// env.npm_execpath = env.npm_execpath || (process.mainModule && process.mainModule.filename);
// // Set the env to production for npm compat if production mode.
// // https://github.com/npm/npm/blob/30d75e738b9cb7a6a3f9b50e971adcbe63458ed3/lib/utils/lifecycle.js#L336
// if (config.production) {
// env.NODE_ENV = 'production';
// }
// // Note: npm_config_argv environment variable contains output of nopt - command-line
// // parser used by npm. Since we use other parser, we just roughly emulate it's output. (See: #684)
// env.npm_config_argv = JSON.stringify({
// remain: [],
// cooked: config.commandName === 'run' ? [config.commandName, stage] : [config.commandName],
// original: process.argv.slice(2),
// });
// const manifest = await config.maybeReadManifest(cwd);
// if (manifest) {
// if (manifest.scripts && Object.prototype.hasOwnProperty.call(manifest.scripts, stage)) {
// env.npm_lifecycle_script = manifest.scripts[stage];
// }
// // add npm_package_*
// const queue = [['', manifest]];
// while (queue.length) {
// const [key, val] = queue.pop();
// if (typeof val === 'object') {
// for (const subKey in val) {
// const fullKey = [key, subKey].filter(Boolean).join('_');
// if (fullKey && fullKey[0] !== '_' && !IGNORE_MANIFEST_KEYS.has(fullKey)) {
// queue.push([fullKey, val[subKey]]);
// }
// }
// } else {
// let cleanVal = String(val);
// if (cleanVal.indexOf('\n') >= 0) {
// cleanVal = JSON.stringify(cleanVal);
// }
// //replacing invalid chars with underscore
// const cleanKey = key.replace(INVALID_CHAR_REGEX, '_');
// env[`npm_package_${cleanKey}`] = cleanVal;
// }
// }
// }
// // add npm_config_* and npm_package_config_* from pika config
// const keys: Set<string> = new Set([
// ...Object.keys(config.registries.pika.config),
// ...Object.keys(config.registries.npm.config),
// ]);
// const cleaned = Array.from(keys)
// .filter(key => !key.match(/:_/) && IGNORE_CONFIG_KEYS.indexOf(key) === -1)
// .map(key => {
// let val = config.getOption(key);
// if (!val) {
// val = '';
// } else if (typeof val === 'number') {
// val = '' + val;
// } else if (typeof val !== 'string') {
// val = JSON.stringify(val);
// }
// if (val.indexOf('\n') >= 0) {
// val = JSON.stringify(val);
// }
// return [key, val];
// });
// // add npm_config_*
// for (const [key, val] of cleaned) {
// const cleanKey = key.replace(/^_+/, '');
// const envKey = `npm_config_${cleanKey}`.replace(INVALID_CHAR_REGEX, '_');
// env[envKey] = val;
// }
// // add npm_package_config_*
// if (manifest && manifest.name) {
// const packageConfigPrefix = `${manifest.name}:`;
// for (const [key, val] of cleaned) {
// if (key.indexOf(packageConfigPrefix) !== 0) {
// continue;
// }
// const cleanKey = key.replace(/^_+/, '').replace(packageConfigPrefix, '');
// const envKey = `npm_package_config_${cleanKey}`.replace(INVALID_CHAR_REGEX, '_');
// env[envKey] = val;
// }
// }
// // split up the path
// const envPath = env[constants.ENV_PATH_KEY];
// const pathParts = envPath ? envPath.split(path.delimiter) : [];
// // Include the directory that contains node so that we can guarantee that the scripts
// // will always run with the exact same Node release than the one use to run Pika
// const execBin = path.dirname(process.execPath);
// if (pathParts.indexOf(execBin) === -1) {
// pathParts.unshift(execBin);
// }
// // Include node-gyp version that was bundled with the current Node.js version,
// // if available.
// pathParts.unshift(path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'node-gyp-bin'));
// pathParts.unshift(
// path.join(path.dirname(process.execPath), '..', 'lib', 'node_modules', 'npm', 'bin', 'node-gyp-bin'),
// );
// // Include node-gyp version from homebrew managed npm, if available.
// pathParts.unshift(
// path.join(path.dirname(process.execPath), '..', 'libexec', 'lib', 'node_modules', 'npm', 'bin', 'node-gyp-bin'),
// );
// // Add global bin folder if it is not present already, as some packages depend
// // on a globally-installed version of node-gyp.
// const globalBin = await getGlobalBinFolder(config, {});
// if (pathParts.indexOf(globalBin) === -1) {
// pathParts.unshift(globalBin);
// }
// // Add node_modules .bin folders to the PATH
// for (const registry of Object.keys(registries)) {
// const binFolder = path.join(config.registries[registry].folder, '.bin');
// if (config.workspacesEnabled && config.workspaceRootFolder) {
// pathParts.unshift(path.join(config.workspaceRootFolder, binFolder));
// }
// pathParts.unshift(path.join(config.linkFolder, binFolder));
// pathParts.unshift(path.join(cwd, binFolder));
// if (config.modulesFolder) {
// pathParts.unshift(path.join(config.modulesFolder, '.bin'));
// }
// }
// if (await fs.exists(`${config.lockfileFolder}/${constants.PNP_FILENAME}`)) {
// // TODO: Fix. import()? Do we even like that it does this?
// throw new Error("pnp temporarily not supported");
// const pnpApi = {}; //dynamicRequire(`${config.lockfileFolder}/${constants.PNP_FILENAME}`);
// const packageLocator = pnpApi.findPackageLocator(`${config.cwd}/`);
// const packageInformation = pnpApi.getPackageInformation(packageLocator);
// for (const [name, reference] of packageInformation.packageDependencies.entries()) {
// const dependencyInformation = pnpApi.getPackageInformation({name, reference});
// if (!dependencyInformation || !dependencyInformation.packageLocation) {
// continue;
// }
// pathParts.unshift(`${dependencyInformation.packageLocation}/.bin`);
// }
// }
// pathParts.unshift(await getWrappersFolder(config));
// // join path back together
// env[constants.ENV_PATH_KEY] = pathParts.join(path.delimiter);
// return env;
// }
export async function executeLifecycleScript({
// config,
cwd, cmd, args, isInteractive, onProgress, customShell, }) {
const env = await makeEnv();
// await checkForGypIfNeeded(config, cmd, env[constants.ENV_PATH_KEY].split(path.delimiter));
if (process.platform === 'win32' && (!customShell || customShell === 'cmd')) {
// handle windows run scripts starting with a relative path
cmd = fixCmdWinSlashes(cmd);
}
// By default (non-interactive), pipe everything to the terminal and run child process detached
// as long as it's not Windows (since windows does not have /dev/tty)
let stdio = ['ignore', 'pipe', 'pipe'];
let detached = process.platform !== 'win32';
if (isInteractive) {
stdio = 'inherit';
detached = false;
}
const shell = customShell || true;
const stdout = await child.spawn(cmd, args, { cwd, env, stdio, detached, shell }, onProgress);
return { cwd, command: cmd, stdout };
}
export default executeLifecycleScript;
// let checkGypPromise: Promise<void> = null;
// // /**
// // * Special case: Some packages depend on node-gyp, but don't specify this in
// // * their package.json dependencies. They assume that node-gyp is available
// // * globally. We need to detect this case and show an error message.
// // */
// function checkForGypIfNeeded(config: Config, cmd: string, paths: Array<string>): Promise<void> {
// if (cmd.substr(0, cmd.indexOf(' ')) !== 'node-gyp') {
// return Promise.resolve();
// }
// // Ensure this only runs once, rather than multiple times in parallel.
// if (!checkGypPromise) {
// checkGypPromise = _checkForGyp(config, paths);
// }
// return checkGypPromise;
// }
// async function _checkForGyp(config: Config, paths: Array<string>): Promise<void> {
// const {reporter} = config;
// // Check every directory in the PATH
// const allChecks = await Promise.all(paths.map(dir => fs.exists(path.join(dir, 'node-gyp'))));
// if (allChecks.some(Boolean)) {
// // node-gyp is available somewhere
// return;
// }
// reporter.info(reporter.lang('packageRequiresNodeGyp'));
// }
// export async function execFromDistributions(config: Config, cwd: string, dist: string, step: string): Promise<void> {
// const pkg = await config.maybeReadManifest(cwd);
// if (!pkg || !pkg.distributions || !pkg.distributions[dist] || typeof pkg.distributions[dist][step] !== 'string') {
// return false;
// }
// const cmd: ?string = pkg.distributions[dist][step];
// await execCommand({stage: 'build', config, cmd, cwd, isInteractive: true});
// return true;
// }
// export async function execFromManifest(config: Config, commandName: string, cwd: string): Promise<void> {
// const pkg = await config.maybeReadManifest(cwd);
// if (!pkg || !pkg.scripts) {
// return;
// }
// const cmd: ?string = pkg.scripts[commandName];
// if (cmd) {
// await execCommand({stage: commandName, config, cmd, cwd, isInteractive: true});
// }
// }
// export async function execCommand({
// stage,
// config,
// cmd,
// cwd,
// isInteractive,
// customShell,
// }: {
// stage: string;
// config: Config;
// cmd: string;
// cwd: string;
// isInteractive: boolean;
// customShell?: string;
// }): Promise<void> {
// const {reporter} = config;
// try {
// reporter.command(cmd);
// await executeLifecycleScript({config, cwd, cmd, isInteractive, customShell});
// return Promise.resolve();
// } catch (err) {
// if (err instanceof ProcessTermError) {
// throw new MessageError(
// err.EXIT_SIGNAL
// ? reporter.lang('commandFailedWithSignal', err.EXIT_SIGNAL)
// : reporter.lang('commandFailedWithCode', err.EXIT_CODE),
// );
// } else {
// throw err;
// }
// }
// }