diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 2f23e6d8da75..c7840d0fd443 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -101,6 +101,7 @@ ts_library( "//packages/angular_devkit/core/node", "//packages/ngtools/webpack", "@npm//@ampproject/remapping", + "@npm//@angular/common", "@npm//@angular/compiler-cli", "@npm//@angular/core", "@npm//@angular/localize", diff --git a/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html.ts b/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html.ts index 79130bfff333..d02406844a2f 100644 --- a/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html.ts +++ b/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html.ts @@ -7,6 +7,7 @@ */ import { createHash } from 'crypto'; +import { loadEsmModule } from '../load-esm'; import { htmlRewritingStream } from './html-rewriting-stream'; export type LoadOutputFileFunctionType = (file: string) => Promise; @@ -50,9 +51,14 @@ export interface FileInfo { * after processing several configurations in order to build different sets of * bundles for differential serving. */ -export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise { +export async function augmentIndexHtml( + params: AugmentIndexHtmlOptions, +): Promise<{ content: string; warnings: string[]; errors: string[] }> { const { loadOutputFile, files, entrypoints, sri, deployUrl = '', lang, baseHref, html } = params; + const warnings: string[] = []; + const errors: string[] = []; + let { crossOrigin = 'none' } = params; if (sri && crossOrigin === 'none') { crossOrigin = 'anonymous'; @@ -119,6 +125,7 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise linkTags.push(``); } + const dir = lang ? await getLanguageDirection(lang, warnings) : undefined; const { rewriter, transformedContent } = await htmlRewritingStream(html); const baseTagExists = html.includes(' { + try { + const localeData = ( + await loadEsmModule( + `@angular/common/locales/${lang}`, + ) + ).default; + + const dir = localeData[localeData.length - 2]; + + return isString(dir) ? dir : undefined; + } catch { + warnings.push( + `Locale data for '${lang}' cannot be found. 'dir' attribute will not be set for this locale.`, + ); + } +} diff --git a/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html_spec.ts b/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html_spec.ts index 3d460817cec4..e0ff4b595bbf 100644 --- a/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html_spec.ts +++ b/packages/angular_devkit/build_angular/src/utils/index-file/augment-index-html_spec.ts @@ -28,7 +28,7 @@ describe('augment-index-html', () => { tags.stripIndents`${html}`.replace(/(>\s+)/g, '>'); it('can generate index.html', async () => { - const source = augmentIndexHtml({ + const { content } = await augmentIndexHtml({ ...indexGeneratorOptions, files: [ { file: 'styles.css', extension: '.css', name: 'styles' }, @@ -39,8 +39,7 @@ describe('augment-index-html', () => { ], }); - const html = await source; - expect(html).toEqual(oneLineHtml` + expect(content).toEqual(oneLineHtml` @@ -55,14 +54,13 @@ describe('augment-index-html', () => { }); it('should replace base href value', async () => { - const source = augmentIndexHtml({ + const { content } = await augmentIndexHtml({ ...indexGeneratorOptions, html: '', baseHref: '/Apps/', }); - const html = await source; - expect(html).toEqual(oneLineHtml` + expect(content).toEqual(oneLineHtml` @@ -72,15 +70,51 @@ describe('augment-index-html', () => { `); }); - it('should add lang attribute', async () => { - const source = augmentIndexHtml({ + it('should add lang and dir LTR attribute for French (fr)', async () => { + const { content } = await augmentIndexHtml({ ...indexGeneratorOptions, lang: 'fr', }); - const html = await source; - expect(html).toEqual(oneLineHtml` - + expect(content).toEqual(oneLineHtml` + + + + + + + + `); + }); + + it('should add lang and dir RTL attribute for Pashto (ps)', async () => { + const { content } = await augmentIndexHtml({ + ...indexGeneratorOptions, + lang: 'ps', + }); + + expect(content).toEqual(oneLineHtml` + + + + + + + + `); + }); + + it(`should work when lang (locale) is not provided by '@angular/common'`, async () => { + const { content, warnings } = await augmentIndexHtml({ + ...indexGeneratorOptions, + lang: 'xx-XX', + }); + + expect(warnings).toEqual([ + `Locale data for 'xx-XX' cannot be found. 'dir' attribute will not be set for this locale.`, + ]); + expect(content).toEqual(oneLineHtml` + @@ -91,7 +125,7 @@ describe('augment-index-html', () => { }); it(`should add script and link tags even when body and head element doesn't exist`, async () => { - const source = augmentIndexHtml({ + const { content } = await augmentIndexHtml({ ...indexGeneratorOptions, html: ``, files: [ @@ -103,8 +137,7 @@ describe('augment-index-html', () => { ], }); - const html = await source; - expect(html).toEqual(oneLineHtml` + expect(content).toEqual(oneLineHtml`