Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/angular/cli/src/package-managers/package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,15 @@ export class PackageManager {
}
}

// To prevent Yarn modern from traversing up the directory tree and failing because the temporary
// directory is not part of the project's workspace, write an empty `yarn.lock` to act as a project boundary.
if (this.name === 'yarn') {
await this.host.writeFile(join(workingDirectory, 'yarn.lock'), '');
}

// Copy configuration files if the package manager requires it (e.g., bun, yarn).
if (this.descriptor.copyConfigFromProject) {
let copiedYarnConfig = false;
for (const configFile of this.descriptor.configFiles) {
try {
const configPath = join(this.cwd, configFile);
Expand All @@ -734,10 +741,20 @@ export class PackageManager {
content = sanitizeYarnRc(content);
}
await this.host.writeFile(join(workingDirectory, configFile), content);
if (this.name === 'yarn') {
copiedYarnConfig = true;
}
} catch {
// Ignore missing config files.
}
}

if (this.name === 'yarn' && !copiedYarnConfig) {
await this.host.writeFile(
join(workingDirectory, '.yarnrc.yml'),
'nodeLinker: node-modules\n',
);
}
}

const flags = [options.ignoreScripts ? this.descriptor.ignoreScriptsFlag : ''].filter(
Expand Down
47 changes: 47 additions & 0 deletions packages/angular/cli/src/package-managers/package-manager_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ describe('PackageManager', () => {
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/pnpm-workspace.yaml',
'',
);
expect(writeFileSpy).not.toHaveBeenCalledWith(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/yarn.lock',
'',
);
});

it('should copy and sanitize .yarnrc.yml when package manager is yarn and it exists', async () => {
Expand Down Expand Up @@ -244,6 +248,10 @@ describe('PackageManager', () => {
'nodeLinker: node-modules',
].join('\n');

expect(writeFileSpy).toHaveBeenCalledWith(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/yarn.lock',
'',
);
expect(writeFileSpy).toHaveBeenCalledWith(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/.yarnrc.yml',
expectedYarnRcContent,
Expand All @@ -254,6 +262,45 @@ describe('PackageManager', () => {
);
});

it('should write empty yarn.lock and default .yarnrc.yml when package manager is yarn and config does not exist', async () => {
const yarnDescriptor = SUPPORTED_PACKAGE_MANAGERS['yarn'];
const testHost = new MockHost({
'/tmp/project/node_modules': true,
});
const pm = new PackageManager(testHost, '/tmp/project', yarnDescriptor);

const createTempDirectorySpy = spyOn(testHost, 'createTempDirectory').and.resolveTo(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc',
);
const writeFileSpy = spyOn(testHost, 'writeFile').and.resolveTo();

spyOn(testHost, 'readFile').and.callFake(async (filePath) => {
if (filePath.replace(/\\/g, '/').endsWith('package.json')) {
return JSON.stringify({ packageManager: 'yarn@4.4.1' });
}
throw new Error(`ENOENT: no such file or directory, open '${filePath}'`);
});
spyOn(testHost, 'runCommand').and.resolveTo({ stdout: '4.4.1', stderr: '' });

const { workingDirectory } = await pm.acquireTempPackage('foo@1.0.0');

expect(workingDirectory).toBe('/tmp/project/node_modules/angular-cli-tmp-packages-abc');
expect(createTempDirectorySpy).toHaveBeenCalledWith('/tmp/project/node_modules');

expect(writeFileSpy).toHaveBeenCalledWith(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/yarn.lock',
'',
);
expect(writeFileSpy).toHaveBeenCalledWith(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/.yarnrc.yml',
'nodeLinker: node-modules\n',
);
expect(writeFileSpy).toHaveBeenCalledWith(
'/tmp/project/node_modules/angular-cli-tmp-packages-abc/package.json',
JSON.stringify({ packageManager: 'yarn@4.4.1' }, null, 2),
);
});

it('should copy packageManager field to temp package.json if version is resolved', async () => {
const npmDescriptor = SUPPORTED_PACKAGE_MANAGERS['npm'];
const testHost = new MockHost({
Expand Down