Skip to content

Commit 8e4802d

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular-devkit/build-angular): empty output directory instead of removing
When the `deleteOutputPath` option is enabled (default is enabled), the configured `outputPath` for the project was previously removed directly. This was problematic for cases such as when the path was mounted via a Docker setup. To alleviate these issues, the contents of the output path will now be removed instead. This maintains the existing output path directory itself but ensures an empty location to place the new build output.
1 parent 8915ca9 commit 8e4802d

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

packages/angular_devkit/build_angular/src/builders/browser/specs/output-path_spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('Browser Builder output path', () => {
2121
});
2222
afterEach(async () => host.restore().toPromise());
2323

24-
it('deletes output path', async () => {
24+
it('deletes output path content', async () => {
2525
// Write a file to the output path to later verify it was deleted.
2626
await host
2727
.write(join(host.root(), 'dist/file.txt'), virtualFs.stringToFileBuffer('file'))
@@ -34,14 +34,14 @@ describe('Browser Builder output path', () => {
3434
const run = await architect.scheduleTarget(target);
3535
const output = await run.result;
3636
expect(output.success).toBe(false);
37-
expect(await host.exists(join(host.root(), 'dist')).toPromise()).toBe(false);
37+
expect(await host.exists(join(host.root(), 'dist/file.txt')).toPromise()).toBe(false);
3838
await run.stop();
3939
});
4040

41-
it('deletes output path and unlink symbolic link', async () => {
41+
it('deletes output path content and unlink symbolic link', async () => {
4242
// Write a file to the output path to later verify it was deleted.
4343
host.writeMultipleFiles({
44-
'src-link/dummy.txt': '',
44+
'src-link/a.txt': '',
4545
'dist/file.txt': virtualFs.stringToFileBuffer('file'),
4646
});
4747

@@ -63,8 +63,8 @@ describe('Browser Builder output path', () => {
6363
const output = await run.result;
6464
expect(output.success).toBe(false);
6565

66-
expect(await host.exists(join(host.root(), 'dist')).toPromise()).toBe(false);
67-
expect(await host.exists(join(host.root(), 'src-link')).toPromise()).toBe(true);
66+
expect(await host.exists(join(host.root(), 'dist/file.txt')).toPromise()).toBe(false);
67+
expect(await host.exists(join(host.root(), 'src-link/a.txt')).toPromise()).toBe(true);
6868
await run.stop();
6969
});
7070

packages/angular_devkit/build_angular/src/utils/delete-output-dir.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import * as fs from 'fs';
10-
import { resolve } from 'path';
10+
import { join, resolve } from 'path';
1111

1212
/**
1313
* Delete an output directory, but error out if it's the root of the project.
@@ -18,5 +18,19 @@ export function deleteOutputDir(root: string, outputPath: string): void {
1818
throw new Error('Output path MUST not be project root directory!');
1919
}
2020

21-
fs.rmSync(resolvedOutputPath, { force: true, recursive: true, maxRetries: 3 });
21+
// Avoid removing the actual directory to avoid errors in cases where the output
22+
// directory is mounted or symlinked. Instead the contents are removed.
23+
let entries;
24+
try {
25+
entries = fs.readdirSync(resolvedOutputPath);
26+
} catch (error) {
27+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
28+
return;
29+
}
30+
throw error;
31+
}
32+
33+
for (const entry of entries) {
34+
fs.rmSync(join(resolvedOutputPath, entry), { force: true, recursive: true, maxRetries: 3 });
35+
}
2236
}

0 commit comments

Comments
 (0)