Skip to content

Commit a0a6b42

Browse files
committed
fix(@angular/build): ensure asset followSymlinks defaults to false
When `followSymlinks` is omitted from an asset configuration entry, its value is `undefined`. Because `tinyglobby` treats `followSymbolicLinks: undefined` as `true`, symlinked subdirectories within asset folders were traversed even though the schema documents the default as `false`. Coerce `entry.followSymlinks` to a boolean (`entry.followSymlinks ?? false`) so the runtime behavior matches the documented schema default and the existing Webpack builder behavior. Closes angular#34164
1 parent 099e52f commit a0a6b42

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

‎packages/angular/build/src/builders/application/tests/options/assets_spec.ts‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import fs from 'node:fs/promises';
910
import { buildApplication } from '../../index';
1011
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
1112

@@ -408,6 +409,44 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
408409

409410
harness.expectFile('dist/browser/test.svg').toNotExist();
410411
});
412+
413+
it('does not follow symlink directories by default', async () => {
414+
await harness.writeFile('target/test.txt', 'symlink-content');
415+
await fs.symlink(
416+
harness.resolvePath('target'),
417+
harness.resolvePath('src/symlink-dir'),
418+
'junction',
419+
);
420+
421+
harness.useTarget('build', {
422+
...BASE_OPTIONS,
423+
assets: [{ glob: '**/*', input: 'src' }],
424+
});
425+
426+
const { result } = await harness.executeOnce();
427+
428+
expect(result?.success).toBe(true);
429+
harness.expectFile('dist/browser/symlink-dir/test.txt').toNotExist();
430+
});
431+
432+
it('follows symlink directories when followSymlinks is true', async () => {
433+
await harness.writeFile('target/test.txt', 'symlink-content');
434+
await fs.symlink(
435+
harness.resolvePath('target'),
436+
harness.resolvePath('src/symlink-dir'),
437+
'junction',
438+
);
439+
440+
harness.useTarget('build', {
441+
...BASE_OPTIONS,
442+
assets: [{ glob: '**/*', input: 'src', followSymlinks: true }],
443+
});
444+
445+
const { result } = await harness.executeOnce();
446+
447+
expect(result?.success).toBe(true);
448+
harness.expectFile('dist/browser/symlink-dir/test.txt').content.toBe('symlink-content');
449+
});
411450
});
412451
});
413452
});

‎packages/angular/build/src/utils/resolve-assets.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export async function resolveAssets(
3939
cwd,
4040
dot: true,
4141
ignore: entry.ignore ? [...DEFAULT_ASSET_IGNORE, ...entry.ignore] : DEFAULT_ASSET_IGNORE,
42-
followSymbolicLinks: entry.followSymlinks,
42+
followSymbolicLinks: entry.followSymlinks ?? false,
4343
});
4444

4545
for (const file of files) {

‎packages/angular_devkit/build_angular/src/utils/copy-assets.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export async function copyAssets(
3333
cwd,
3434
dot: true,
3535
ignore: entry.ignore ? defaultIgnore.concat(entry.ignore) : defaultIgnore,
36-
followSymbolicLinks: entry.followSymlinks,
36+
followSymbolicLinks: entry.followSymlinks ?? false,
3737
});
3838

3939
const directoryExists = new Set<string>();

0 commit comments

Comments
 (0)