Skip to content

Commit

Permalink
fix(@angular/cli): handle missing which binary in path
Browse files Browse the repository at this point in the history
This change updates the `hasGlobalCliInstall` logic so that a pending promise is not created.

Closes #23997

(cherry picked from commit 4fa5b52)
  • Loading branch information
alan-agius4 authored and clydin committed Oct 7, 2022
1 parent ad69281 commit 1c9cf59
Showing 1 changed file with 21 additions and 29 deletions.
50 changes: 21 additions & 29 deletions packages/angular/cli/src/utilities/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Appended \`source <(ng completion script)\` to \`${rcFile}\`. Restart your termi
`.trim(),
);

if ((await hasGlobalCliInstall()) === false) {
if (!(await hasGlobalCliInstall())) {
logger.warn(
'Setup completed successfully, but there does not seem to be a global install of the' +
' Angular CLI. For autocompletion to work, the CLI will need to be on your `$PATH`, which' +
Expand Down Expand Up @@ -268,27 +268,30 @@ function getShellRunCommandCandidates(shell: string, home: string): string[] | u
}

/**
* Returns whether the user has a global CLI install or `undefined` if this can't be determined.
* Returns whether the user has a global CLI install.
* Execution from `npx` is *not* considered a global CLI install.
*
* This does *not* mean the current execution is from a global CLI install, only that a global
* install exists on the system.
*/
export async function hasGlobalCliInstall(): Promise<boolean | undefined> {
export function hasGlobalCliInstall(): Promise<boolean> {
// List all binaries with the `ng` name on the user's `$PATH`.
const proc = execFile('which', ['-a', 'ng']);
let stdout = '';
proc.stdout?.addListener('data', (content) => {
stdout += content;
});
const exitCode = await new Promise<number | null>((resolve) => {
proc.addListener('exit', (exitCode) => {
resolve(exitCode);
});
});
return new Promise<boolean>((resolve) => {
execFile('which', ['-a', 'ng'], (error, stdout) => {
if (error) {
// No instances of `ng` on the user's `$PATH`

// `which` returns exit code 2 if an invalid option is specified and `-a` doesn't appear to be
// supported on all systems. Other exit codes mean unknown errors occurred. Can't tell whether
// CLI is globally installed, so treat this as inconclusive.

// `which` was killed by a signal and did not exit gracefully. Maybe it hung or something else
// went very wrong, so treat this as inconclusive.
resolve(false);

return;
}

switch (exitCode) {
case 0:
// Successfully listed all `ng` binaries on the `$PATH`. Look for at least one line which is a
// global install. We can't easily identify global installs, but local installs are typically
// placed in `node_modules/.bin` by NPM / Yarn. `npx` also currently caches files at
Expand All @@ -303,18 +306,7 @@ export async function hasGlobalCliInstall(): Promise<boolean | undefined> {
return !localInstall;
});

return hasGlobalInstall;
case 1:
// No instances of `ng` on the user's `$PATH`.
return false;
case null:
// `which` was killed by a signal and did not exit gracefully. Maybe it hung or something else
// went very wrong, so treat this as inconclusive.
return undefined;
default:
// `which` returns exit code 2 if an invalid option is specified and `-a` doesn't appear to be
// supported on all systems. Other exit codes mean unknown errors occurred. Can't tell whether
// CLI is globally installed, so treat this as inconclusive.
return undefined;
}
return resolve(hasGlobalInstall);
});
});
}

0 comments on commit 1c9cf59

Please sign in to comment.