diff --git a/bin/main.test.js b/bin/main.test.js index aee63c868..332e42196 100644 --- a/bin/main.test.js +++ b/bin/main.test.js @@ -160,6 +160,7 @@ beforeEach(() => { CHROMATIC_PROJECT_TOKEN: undefined, }; execa.mockReset(); + execa.mockReturnValue({ stdout: '1.2.3' }); }); afterEach(() => { process.env = processEnv; diff --git a/bin/tasks/build.js b/bin/tasks/build.js index 37d6073a8..026d98447 100644 --- a/bin/tasks/build.js +++ b/bin/tasks/build.js @@ -3,7 +3,6 @@ import fs from 'fs-extra'; import path from 'path'; import semver from 'semver'; import tmp from 'tmp-promise'; -import yarnOrNpm, { spawn } from 'yarn-or-npm'; import { createTask, transitionTo } from '../lib/tasks'; import buildFailed from '../ui/messages/errors/buildFailed'; @@ -21,23 +20,36 @@ export const setSourceDir = async (ctx) => { } }; -export const setSpawnParams = (ctx) => { +export const setSpawnParams = async (ctx) => { const webpackStatsSupported = semver.gte(semver.coerce(ctx.storybook.version), '6.2.0'); if (ctx.git.changedFiles && !webpackStatsSupported) { ctx.log.warn('Storybook version 6.2.0 or later is required to use the --only-changed flag'); } - const client = yarnOrNpm(); - const { stdout } = spawn.sync(['--version']); + + // Run either: + // node path/to/npm-cli.js run build-storybook + // node path/to/yarn.js run build-storybook + // npm run build-storybook + // Based on https://github.com/mysticatea/npm-run-all/blob/52eaf86242ba408dedd015f53ca7ca368f25a026/lib/run-task.js#L156-L174 + const npmExecPath = process.env.npm_execpath; + const npmExecFile = npmExecPath && path.basename(npmExecPath); + const isJsPath = npmExecFile && /\.m?js$/.test(npmExecFile); + const isYarn = npmExecFile && npmExecFile.includes('yarn'); + const isNpx = npmExecFile && npmExecFile.includes('npx'); + + const client = isYarn ? 'yarn' : 'npm'; + const { stdout } = await execa(client, ['--version']); const clientVersion = stdout && stdout.toString().trim(); + ctx.spawnParams = { client, clientVersion, platform: process.platform, - command: client, - clientArgs: ['run', '--silent'], + command: (!isNpx && (isJsPath ? process.execPath : npmExecPath)) || 'npm', + clientArgs: !isNpx && isJsPath ? [npmExecPath, 'run'] : ['run', '--silent'], scriptArgs: [ ctx.options.buildScriptName, - client === 'yarn' ? '' : '--', + isYarn ? '' : '--', '--output-dir', ctx.sourceDir, ctx.git.changedFiles && webpackStatsSupported && '--webpack-stats-json', diff --git a/bin/tasks/build.test.js b/bin/tasks/build.test.js index 58be568b5..32219f235 100644 --- a/bin/tasks/build.test.js +++ b/bin/tasks/build.test.js @@ -1,15 +1,8 @@ import execa from 'execa'; -import yarnOrNpm, { spawn } from 'yarn-or-npm'; import { buildStorybook, setSourceDir, setSpawnParams } from './build'; jest.mock('execa'); -jest.mock('yarn-or-npm'); - -beforeEach(() => { - yarnOrNpm.mockReturnValue('npm'); - spawn.sync.mockReturnValue({ stdout: '1.2.3' }); -}); describe('setSourceDir', () => { it('sets a random temp directory path on the context', async () => { @@ -38,6 +31,13 @@ describe('setSourceDir', () => { }); describe('setSpawnParams', () => { + const npmExecPath = process.env.npm_execpath; + + beforeEach(() => { + process.env.npm_execpath = npmExecPath; + execa.mockReturnValue({ stdout: '1.2.3' }); + }); + it('sets the spawn params on the context', async () => { process.env.npm_execpath = 'npm'; const ctx = { @@ -69,7 +69,7 @@ describe('setSpawnParams', () => { }); it('supports yarn', async () => { - yarnOrNpm.mockReturnValue('yarn'); + process.env.npm_execpath = '/path/to/yarn.js'; const ctx = { sourceDir: './source-dir/', options: { buildScriptName: 'build:storybook' }, @@ -81,8 +81,8 @@ describe('setSpawnParams', () => { client: 'yarn', clientVersion: '1.2.3', platform: expect.stringMatching(/darwin|linux|win32/), - command: 'yarn', - clientArgs: ['run', '--silent'], + command: expect.stringMatching(/node/), + clientArgs: ['/path/to/yarn.js', 'run'], scriptArgs: ['build:storybook', '--output-dir', './source-dir/'], spawnOptions: { preferLocal: true,