Skip to content

Commit 0fc7d57

Browse files
committed
fix(@angular/build): ensure locale base href retains leading slash (#32040)
When baseHref is provided, getLocaleBaseHref was incorrectly adding a leading slash from the joined path. This commit removes calls `stripLeadingSlash` to ensure the leading slash is removed when appropriate. (cherry picked from commit 38b16ea)
1 parent 54f1009 commit 0fc7d57

File tree

2 files changed

+117
-4
lines changed

2 files changed

+117
-4
lines changed

packages/angular/build/src/builders/application/options.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
loadPostcssConfiguration,
2626
} from '../../utils/postcss-configuration';
2727
import { getProjectRootPaths, normalizeDirectoryPath } from '../../utils/project-metadata';
28-
import { addTrailingSlash, joinUrlParts } from '../../utils/url';
28+
import { addTrailingSlash, joinUrlParts, stripLeadingSlash } from '../../utils/url';
2929
import {
3030
Schema as ApplicationBuilderOptions,
3131
ExperimentalPlatform,
@@ -681,9 +681,16 @@ export function getLocaleBaseHref(
681681

682682
const baseHrefSuffix = localeData.baseHref ?? localeData.subPath + '/';
683683

684-
return baseHrefSuffix !== ''
685-
? addTrailingSlash(joinUrlParts(baseHref, baseHrefSuffix))
686-
: undefined;
684+
let joinedBaseHref: string | undefined;
685+
if (baseHrefSuffix !== '') {
686+
joinedBaseHref = addTrailingSlash(joinUrlParts(baseHref, baseHrefSuffix));
687+
688+
if (baseHref && baseHref[0] !== '/') {
689+
joinedBaseHref = stripLeadingSlash(joinedBaseHref);
690+
}
691+
}
692+
693+
return joinedBaseHref;
687694
}
688695

689696
/**
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { NormalizedApplicationBuildOptions, getLocaleBaseHref } from './options';
10+
11+
describe('getLocaleBaseHref', () => {
12+
const baseI18nOptions: NormalizedApplicationBuildOptions['i18nOptions'] = {
13+
inlineLocales: new Set(),
14+
sourceLocale: 'en-US',
15+
locales: {},
16+
flatOutput: false,
17+
shouldInline: false,
18+
hasDefinedSourceLocale: false,
19+
};
20+
21+
it('should return undefined if flatOutput is true', () => {
22+
const result = getLocaleBaseHref(undefined, { ...baseI18nOptions, flatOutput: true }, 'fr');
23+
expect(result).toBeUndefined();
24+
});
25+
26+
it('should return undefined if locale is not found', () => {
27+
const result = getLocaleBaseHref(undefined, baseI18nOptions, 'fr');
28+
expect(result).toBeUndefined();
29+
});
30+
31+
it('should return baseHref from locale data if present', () => {
32+
const i18nOptions = {
33+
...baseI18nOptions,
34+
locales: {
35+
fr: {
36+
files: [],
37+
translation: {},
38+
subPath: 'fr',
39+
baseHref: '/fr/',
40+
},
41+
},
42+
};
43+
const result = getLocaleBaseHref(undefined, i18nOptions, 'fr');
44+
expect(result).toBe('/fr/');
45+
});
46+
47+
it('should join baseHref and locale subPath if baseHref is provided', () => {
48+
const i18nOptions = {
49+
...baseI18nOptions,
50+
locales: {
51+
fr: {
52+
files: [],
53+
translation: {},
54+
subPath: 'fr',
55+
},
56+
},
57+
};
58+
const result = getLocaleBaseHref('/app/', i18nOptions, 'fr');
59+
expect(result).toBe('/app/fr/');
60+
});
61+
62+
it('should handle missing baseHref (undefined) correctly', () => {
63+
const i18nOptions = {
64+
...baseI18nOptions,
65+
locales: {
66+
fr: {
67+
files: [],
68+
translation: {},
69+
subPath: 'fr',
70+
},
71+
},
72+
};
73+
const result = getLocaleBaseHref(undefined, i18nOptions, 'fr');
74+
expect(result).toBe('/fr/');
75+
});
76+
77+
it('should handle empty baseHref correctly', () => {
78+
const i18nOptions = {
79+
...baseI18nOptions,
80+
locales: {
81+
fr: {
82+
files: [],
83+
translation: {},
84+
subPath: 'fr',
85+
},
86+
},
87+
};
88+
const result = getLocaleBaseHref('', i18nOptions, 'fr');
89+
expect(result).toBe('/fr/');
90+
});
91+
92+
it('should strip leading slash if baseHref does not start with slash', () => {
93+
const i18nOptions = {
94+
...baseI18nOptions,
95+
locales: {
96+
fr: {
97+
files: [],
98+
translation: {},
99+
subPath: 'fr',
100+
},
101+
},
102+
};
103+
const result = getLocaleBaseHref('app/', i18nOptions, 'fr');
104+
expect(result).toBe('app/fr/');
105+
});
106+
});

0 commit comments

Comments
 (0)