Skip to content

Commit 1c9cf59

Browse files
alan-agius4clydin
authored andcommitted
fix(@angular/cli): handle missing which binary in path
This change updates the `hasGlobalCliInstall` logic so that a pending promise is not created. Closes #23997 (cherry picked from commit 4fa5b52)
1 parent ad69281 commit 1c9cf59

File tree

1 file changed

+21
-29
lines changed

1 file changed

+21
-29
lines changed

packages/angular/cli/src/utilities/completion.ts

+21-29
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Appended \`source <(ng completion script)\` to \`${rcFile}\`. Restart your termi
8181
`.trim(),
8282
);
8383

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

270270
/**
271-
* Returns whether the user has a global CLI install or `undefined` if this can't be determined.
271+
* Returns whether the user has a global CLI install.
272272
* Execution from `npx` is *not* considered a global CLI install.
273273
*
274274
* This does *not* mean the current execution is from a global CLI install, only that a global
275275
* install exists on the system.
276276
*/
277-
export async function hasGlobalCliInstall(): Promise<boolean | undefined> {
277+
export function hasGlobalCliInstall(): Promise<boolean> {
278278
// List all binaries with the `ng` name on the user's `$PATH`.
279-
const proc = execFile('which', ['-a', 'ng']);
280-
let stdout = '';
281-
proc.stdout?.addListener('data', (content) => {
282-
stdout += content;
283-
});
284-
const exitCode = await new Promise<number | null>((resolve) => {
285-
proc.addListener('exit', (exitCode) => {
286-
resolve(exitCode);
287-
});
288-
});
279+
return new Promise<boolean>((resolve) => {
280+
execFile('which', ['-a', 'ng'], (error, stdout) => {
281+
if (error) {
282+
// No instances of `ng` on the user's `$PATH`
283+
284+
// `which` returns exit code 2 if an invalid option is specified and `-a` doesn't appear to be
285+
// supported on all systems. Other exit codes mean unknown errors occurred. Can't tell whether
286+
// CLI is globally installed, so treat this as inconclusive.
287+
288+
// `which` was killed by a signal and did not exit gracefully. Maybe it hung or something else
289+
// went very wrong, so treat this as inconclusive.
290+
resolve(false);
291+
292+
return;
293+
}
289294

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

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

0 commit comments

Comments
 (0)