Skip to content

Commit 0bd9300

Browse files
committed
fix(@angular/cli): use dedicated cache directory for temporary package installs
Relocates temporary package installations to a dedicated cache directory. This ensures that the project's `.npmrc` is correctly respected, as the installer does relies on the current working directory (CWD) rather than flags like `--prefix`.
1 parent 1944008 commit 0bd9300

File tree

4 files changed

+82
-66
lines changed

4 files changed

+82
-66
lines changed

packages/angular/cli/src/commands/add/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
import { assertIsError } from '../../utilities/error';
3434
import { isTTY } from '../../utilities/tty';
3535
import { VERSION } from '../../utilities/version';
36+
import { getCacheConfig } from '../cache/utilities';
3637

3738
class CommandError extends Error {}
3839

@@ -299,6 +300,7 @@ export default class AddCommandModule
299300
task: AddCommandTaskWrapper,
300301
): Promise<void> {
301302
context.packageManager = await createPackageManager({
303+
cacheDirectory: getCacheConfig(this.context.workspace).path,
302304
cwd: this.context.root,
303305
logger: this.context.logger,
304306
dryRun: context.dryRun,

packages/angular/cli/src/commands/update/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
getProjectDependencies,
3131
readPackageJson,
3232
} from '../../utilities/package-tree';
33+
import { getCacheConfig } from '../cache/utilities';
3334
import {
3435
checkCLIVersion,
3536
coerceVersionNumber,
@@ -173,6 +174,7 @@ export default class UpdateCommandModule extends CommandModule<UpdateCommandArgs
173174
// Instantiate the package manager
174175
const packageManager = await createPackageManager({
175176
cwd: this.context.root,
177+
cacheDirectory: getCacheConfig(this.context.workspace).path,
176178
logger,
177179
configuredPackageManager: this.context.packageManager.name,
178180
});

packages/angular/cli/src/package-managers/factory.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import { major } from 'semver';
1010
import { discover } from './discovery';
11-
import { Host, NodeJS_HOST } from './host';
11+
import { Host, createNodeJsHost } from './host';
1212
import { Logger } from './logger';
1313
import { PackageManager } from './package-manager';
1414
import { PackageManagerName, SUPPORTED_PACKAGE_MANAGERS } from './package-manager-descriptor';
@@ -107,12 +107,13 @@ async function determinePackageManager(
107107
*/
108108
export async function createPackageManager(options: {
109109
cwd: string;
110+
cacheDirectory: string;
110111
configuredPackageManager?: PackageManagerName;
111112
logger?: Logger;
112113
dryRun?: boolean;
113114
}): Promise<PackageManager> {
114-
const { cwd, configuredPackageManager, logger, dryRun } = options;
115-
const host = NodeJS_HOST;
115+
const { cwd, cacheDirectory, configuredPackageManager, logger, dryRun } = options;
116+
const host = createNodeJsHost(cacheDirectory);
116117

117118
const { name, source } = await determinePackageManager(
118119
host,

packages/angular/cli/src/package-managers/host.ts

Lines changed: 74 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
import { type SpawnOptions, spawn } from 'node:child_process';
1717
import { Stats } from 'node:fs';
18-
import { mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
19-
import { platform, tmpdir } from 'node:os';
18+
import { mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
19+
import { platform } from 'node:os';
2020
import { join } from 'node:path';
2121
import { PackageManagerError } from './error';
2222

@@ -88,67 +88,78 @@ export interface Host {
8888

8989
/**
9090
* A concrete implementation of the `Host` interface that uses the Node.js APIs.
91+
* @param cacheDirectory The directory to use for caching.
92+
* @returns A host that uses the Node.js APIs.
9193
*/
92-
export const NodeJS_HOST: Host = {
93-
stat,
94-
readdir,
95-
readFile: (path: string) => readFile(path, { encoding: 'utf8' }),
96-
writeFile,
97-
createTempDirectory: () => mkdtemp(join(tmpdir(), 'angular-cli-')),
98-
deleteDirectory: (path: string) => rm(path, { recursive: true, force: true }),
99-
runCommand: async (
100-
command: string,
101-
args: readonly string[],
102-
options: {
103-
timeout?: number;
104-
stdio?: 'pipe' | 'ignore';
105-
cwd?: string;
106-
env?: Record<string, string>;
107-
} = {},
108-
): Promise<{ stdout: string; stderr: string }> => {
109-
const signal = options.timeout ? AbortSignal.timeout(options.timeout) : undefined;
110-
const isWin32 = platform() === 'win32';
111-
112-
return new Promise((resolve, reject) => {
113-
const spawnOptions = {
114-
shell: isWin32,
115-
stdio: options.stdio ?? 'pipe',
116-
signal,
117-
cwd: options.cwd,
118-
env: {
119-
...process.env,
120-
...options.env,
121-
},
122-
} satisfies SpawnOptions;
123-
const childProcess = isWin32
124-
? spawn(`${command} ${args.join(' ')}`, spawnOptions)
125-
: spawn(command, args, spawnOptions);
126-
127-
let stdout = '';
128-
childProcess.stdout?.on('data', (data) => (stdout += data.toString()));
129-
130-
let stderr = '';
131-
childProcess.stderr?.on('data', (data) => (stderr += data.toString()));
132-
133-
childProcess.on('close', (code) => {
134-
if (code === 0) {
135-
resolve({ stdout, stderr });
136-
} else {
137-
const message = `Process exited with code ${code}.`;
138-
reject(new PackageManagerError(message, stdout, stderr, code));
139-
}
140-
});
141-
142-
childProcess.on('error', (err) => {
143-
if (err.name === 'AbortError') {
144-
const message = `Process timed out.`;
94+
export function createNodeJsHost(cacheDirectory: string): Host {
95+
return {
96+
stat,
97+
readdir,
98+
readFile: (path: string) => readFile(path, { encoding: 'utf8' }),
99+
writeFile,
100+
createTempDirectory: async () => {
101+
await mkdir(cacheDirectory, { recursive: true });
102+
103+
console.log();
104+
const x = await mkdtemp(join(cacheDirectory, 'package-manager-temp-'));
105+
console.log({ x });
106+
return x;
107+
},
108+
deleteDirectory: (path: string) => rm(path, { recursive: true, force: true }),
109+
runCommand: async (
110+
command: string,
111+
args: readonly string[],
112+
options: {
113+
timeout?: number;
114+
stdio?: 'pipe' | 'ignore';
115+
cwd?: string;
116+
env?: Record<string, string>;
117+
} = {},
118+
): Promise<{ stdout: string; stderr: string }> => {
119+
const signal = options.timeout ? AbortSignal.timeout(options.timeout) : undefined;
120+
const isWin32 = platform() === 'win32';
121+
122+
return new Promise((resolve, reject) => {
123+
const spawnOptions = {
124+
shell: isWin32,
125+
stdio: options.stdio ?? 'pipe',
126+
signal,
127+
cwd: options.cwd,
128+
env: {
129+
...process.env,
130+
...options.env,
131+
},
132+
} satisfies SpawnOptions;
133+
const childProcess = isWin32
134+
? spawn(`${command} ${args.join(' ')}`, spawnOptions)
135+
: spawn(command, args, spawnOptions);
136+
137+
let stdout = '';
138+
childProcess.stdout?.on('data', (data) => (stdout += data.toString()));
139+
140+
let stderr = '';
141+
childProcess.stderr?.on('data', (data) => (stderr += data.toString()));
142+
143+
childProcess.on('close', (code) => {
144+
if (code === 0) {
145+
resolve({ stdout, stderr });
146+
} else {
147+
const message = `Process exited with code ${code}.`;
148+
reject(new PackageManagerError(message, stdout, stderr, code));
149+
}
150+
});
151+
152+
childProcess.on('error', (err) => {
153+
if (err.name === 'AbortError') {
154+
const message = `Process timed out.`;
155+
reject(new PackageManagerError(message, stdout, stderr, null));
156+
157+
return;
158+
}
159+
const message = `Process failed with error: ${err.message}`;
145160
reject(new PackageManagerError(message, stdout, stderr, null));
146-
147-
return;
148-
}
149-
const message = `Process failed with error: ${err.message}`;
150-
reject(new PackageManagerError(message, stdout, stderr, null));
161+
});
151162
});
152-
});
153-
},
154-
};
163+
},
164+
};
165+
}

0 commit comments

Comments
 (0)