Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): use corepack when enabled to sync lockfile/run npm script (…
…#775) * feat(core): use corepack when enabled to sync lockfile/run npm script
- Loading branch information
1 parent
d092fc6
commit 3f5624c
Showing
11 changed files
with
231 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
packages/core/src/corepack/exec-package-manager.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import chalk from 'chalk'; | ||
import npmlog from 'npmlog'; | ||
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; | ||
|
||
import { execPackageManager, execPackageManagerSync } from './exec-package-manager'; | ||
import { exec, execSync, getChildProcessCount } from '../child-process'; | ||
import { Package } from '../package'; | ||
|
||
vi.mock('../child-process', async () => ({ | ||
...(await vi.importActual<any>('../child-process')), | ||
exec: vi.fn(), | ||
execSync: vi.fn(), | ||
getChildProcessCount: (await vi.importActual<any>('../child-process')).getChildProcessCount, | ||
})); | ||
|
||
const execActual = (await vi.importActual<any>('../child-process')).exec; | ||
const execSyncActual = (await vi.importActual<any>('../child-process')).execSync; | ||
|
||
describe('.execPackageManagerSync()', () => { | ||
beforeEach(() => { | ||
process.env = {}; | ||
}); | ||
|
||
describe('mock child processes', () => { | ||
it('calls execSync without corepack when disabled', () => { | ||
execPackageManagerSync('echo', ['execPackageManagerSync']); | ||
|
||
expect(execSync).toHaveBeenCalledWith('echo', ['execPackageManagerSync'], undefined, false); | ||
}); | ||
|
||
it('calls execSync with corepack when enabled', () => { | ||
Object.assign({}, process.env); | ||
process.env.COREPACK_ROOT = 'pnpm'; | ||
|
||
execPackageManagerSync('echo', ['execPackageManagerSync']); | ||
|
||
expect(execSync).toHaveBeenCalledWith('corepack', ['echo', 'execPackageManagerSync'], undefined, false); | ||
}); | ||
}); | ||
|
||
describe('import actual child processes', () => { | ||
beforeEach(() => { | ||
(execSync as Mock).mockImplementationOnce(execSyncActual); | ||
}); | ||
|
||
it('should execute a command in a child process and return the result', () => { | ||
expect(execPackageManagerSync('echo', ['execPackageManagerSync'])).toContain(`execPackageManagerSync`); | ||
}); | ||
|
||
it('should execute a command in dry-run and log the command', () => { | ||
const logSpy = vi.spyOn(npmlog, 'info'); | ||
execPackageManagerSync('echo', ['execPackageManagerSync'], undefined, true); | ||
expect(logSpy).toHaveBeenCalledWith(chalk.bold.magenta('[dry-run] >'), 'echo execPackageManagerSync'); | ||
}); | ||
|
||
it('does not error when stdout is ignored', () => { | ||
expect(() => execPackageManagerSync('echo', ['ignored'], { stdio: 'ignore' })).not.toThrow(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('.execPackageManager()', () => { | ||
beforeEach(() => { | ||
process.env = {}; | ||
}); | ||
|
||
describe('mock child processes', () => { | ||
it('calls exec without corepack when disabled', () => { | ||
execPackageManager('echo', ['execPackageManager']); | ||
|
||
expect(exec).toHaveBeenCalledWith('echo', ['execPackageManager'], undefined, false); | ||
}); | ||
|
||
it('calls exec with corepack when enabled', () => { | ||
Object.assign({}, process.env); | ||
process.env.COREPACK_ROOT = 'pnpm'; | ||
|
||
execPackageManager('echo', ['execPackageManager']); | ||
|
||
expect(exec).toHaveBeenCalledWith('corepack', ['echo', 'execPackageManager'], undefined, false); | ||
}); | ||
}); | ||
|
||
describe('import actual child processes', () => { | ||
beforeEach(() => { | ||
(exec as Mock).mockImplementationOnce(execActual); | ||
}); | ||
|
||
it('returns an execa Promise', async () => { | ||
const { stderr, stdout } = (await execPackageManager('echo', ['foo'])) as any; | ||
|
||
expect(stderr).toBe(''); | ||
expect(stdout).toContain(`foo`); | ||
}); | ||
|
||
it('should execute a command in dry-run and log the command', () => { | ||
const logSpy = vi.spyOn(npmlog, 'info'); | ||
execPackageManager('echo', ['exec'], undefined, true); | ||
expect(logSpy).toHaveBeenCalledWith(chalk.bold.magenta('[dry-run] >'), 'echo exec'); | ||
}); | ||
|
||
it('rejects on undefined command', async () => { | ||
const result = execPackageManager('nowImTheModelOfAModernMajorGeneral', undefined as any); | ||
|
||
await expect(result).rejects.toThrow(/\bnowImTheModelOfAModernMajorGeneral\b/); | ||
expect(getChildProcessCount()).toBe(0); | ||
}); | ||
|
||
it('decorates opts.pkg on error if caught', async () => { | ||
const result = execPackageManager('theVeneratedVirginianVeteranWhoseMenAreAll', ['liningUpToPutMeUpOnAPedestal'], { | ||
pkg: { name: 'hamilton' } as Package, | ||
}); | ||
|
||
await expect(result).rejects.toThrow( | ||
expect.objectContaining({ | ||
pkg: { name: 'hamilton' }, | ||
}) | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { Options as ExecaOptions, SyncOptions as ExacaSyncOptions } from 'execa'; | ||
|
||
import { exec, execSync } from '../child-process'; | ||
import { isCorepackEnabled } from './is-corepack-enabled'; | ||
import type { Package } from '../package'; | ||
|
||
function createCommandAndArgs(npmClient: string, args: string[]) { | ||
let command = npmClient; | ||
const commandArgs = args === undefined ? [] : [...args]; | ||
|
||
if (isCorepackEnabled()) { | ||
commandArgs.unshift(command); | ||
command = 'corepack'; | ||
} | ||
|
||
return { command, commandArgs }; | ||
} | ||
|
||
// prettier-ignore | ||
export function execPackageManager(npmClient: string, args: string[], opts?: ExecaOptions & { pkg?: Package }, dryRun = false): Promise<any> { | ||
const { command, commandArgs } = createCommandAndArgs(npmClient, args); | ||
return exec(command, commandArgs, opts, dryRun); | ||
} | ||
|
||
export function execPackageManagerSync(npmClient: string, args: string[], opts?: ExacaSyncOptions, dryRun = false): string { | ||
const { command, commandArgs } = createCommandAndArgs(npmClient, args); | ||
return execSync(command, commandArgs, opts, dryRun); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './exec-package-manager'; | ||
export * from './is-corepack-enabled'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export function isCorepackEnabled() { | ||
// https://github.com/nodejs/corepack#environment-variables | ||
// The COREPACK_ROOT environment variable is specifically set by Corepack to indicate that it is running. | ||
return process.env['COREPACK_ROOT'] !== undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.