From a05ee3099f86403aca94167587e460028fecb6d9 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 20 Feb 2020 14:51:53 +0100 Subject: [PATCH] fix(@angular-devkit/build-angular): baseHref with protocol and localize option `posix.join` will dedupe double forward slashes resulting in incorrect protocol. Closes: #17029 (cherry picked from commit 4e65705205c55e476d6cb8ab1a83716b4e89885f) --- .../build_angular/src/browser/index.ts | 13 ++++---- .../build_angular/src/utils/index.ts | 1 + .../build_angular/src/utils/url.ts | 17 +++++++++++ .../build_angular/src/utils/url_spec.ts | 30 +++++++++++++++++++ .../e2e/tests/i18n/ivy-localize-basehref.ts | 7 +++++ 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 packages/angular_devkit/build_angular/src/utils/url.ts create mode 100644 packages/angular_devkit/build_angular/src/utils/url_spec.ts diff --git a/packages/angular_devkit/build_angular/src/browser/index.ts b/packages/angular_devkit/build_angular/src/browser/index.ts index 7ef41ed0adf7..6f63242a20fc 100644 --- a/packages/angular_devkit/build_angular/src/browser/index.ts +++ b/packages/angular_devkit/build_angular/src/browser/index.ts @@ -49,6 +49,7 @@ import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps, + urlJoin, } from '../utils'; import { BundleActionExecutor } from '../utils/action-executor'; import { findCachePath } from '../utils/cache-path'; @@ -695,11 +696,9 @@ export function buildWebpackBrowser( for (const [locale, outputPath] of outputPaths.entries()) { let localeBaseHref; if (i18n.locales[locale] && i18n.locales[locale].baseHref !== '') { - localeBaseHref = path.posix.join( + localeBaseHref = urlJoin( options.baseHref || '', - i18n.locales[locale].baseHref === undefined - ? `/${locale}/` - : i18n.locales[locale].baseHref, + i18n.locales[locale].baseHref ?? `/${locale}/`, ); } @@ -726,11 +725,9 @@ export function buildWebpackBrowser( for (const [locale, outputPath] of outputPaths.entries()) { let localeBaseHref; if (i18n.locales[locale] && i18n.locales[locale].baseHref !== '') { - localeBaseHref = path.posix.join( + localeBaseHref = urlJoin( options.baseHref || '', - i18n.locales[locale].baseHref === undefined - ? `/${locale}/` - : i18n.locales[locale].baseHref, + i18n.locales[locale].baseHref ?? `/${locale}/`, ); } diff --git a/packages/angular_devkit/build_angular/src/utils/index.ts b/packages/angular_devkit/build_angular/src/utils/index.ts index 8cd7670f0a9b..8382db78b736 100644 --- a/packages/angular_devkit/build_angular/src/utils/index.ts +++ b/packages/angular_devkit/build_angular/src/utils/index.ts @@ -15,3 +15,4 @@ export * from './normalize-asset-patterns'; export * from './normalize-source-maps'; export * from './normalize-optimization'; export * from './normalize-builder-schema'; +export * from './url'; diff --git a/packages/angular_devkit/build_angular/src/utils/url.ts b/packages/angular_devkit/build_angular/src/utils/url.ts new file mode 100644 index 000000000000..39b047b407bb --- /dev/null +++ b/packages/angular_devkit/build_angular/src/utils/url.ts @@ -0,0 +1,17 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + +export function urlJoin(...parts: string[]): string { + const [p, ...rest] = parts; + + // Remove trailing slash from first part + // Join all parts with `/` + // Dedupe double slashes from path names + return p.replace(/\/$/, '') + ('/' + rest.join('/')).replace(/\/\/+/g, '/'); +} diff --git a/packages/angular_devkit/build_angular/src/utils/url_spec.ts b/packages/angular_devkit/build_angular/src/utils/url_spec.ts new file mode 100644 index 000000000000..ca0d7935a741 --- /dev/null +++ b/packages/angular_devkit/build_angular/src/utils/url_spec.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { urlJoin } from './url'; + +describe('urlJoin', () => { + it('should work with absolute url with trailing slash', () => { + expect(urlJoin('http://foo.com/', '/one/')).toBe('http://foo.com/one/'); + }); + + it('should work with absolute url without trailing slash', () => { + expect(urlJoin('http://foo.com', '/one')).toBe('http://foo.com/one'); + }); + + it('should work with absolute url without slashes', () => { + expect(urlJoin('http://foo.com', 'one', 'two')).toBe('http://foo.com/one/two'); + }); + + it('should work with relative url without slashes', () => { + expect(urlJoin('one', 'two', 'three')).toBe('one/two/three'); + }); + + it('should keep trailing slash if empty path is provided', () => { + expect(urlJoin('one/', '')).toBe('one/'); + }); +}); diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts b/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts index 51ad75da4699..6ec11b72e9a4 100644 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts +++ b/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts @@ -100,4 +100,11 @@ export default async function() { server.close(); } } + + // Test absolute base href. + await ng('build', '--base-href', 'http://www.domain.com/'); + for (const { lang, outputPath } of langTranslations) { + // Verify the HTML base HREF attribute is present + await expectFileToMatch(`${outputPath}/index.html`, `href="http://www.domain.com${baseHrefs[lang] || '/'}"`); + } }