Skip to content

Commit 47bf88e

Browse files
committed
refactor(@angular-devkit/build-angular): use module resolution to search for i18n locale data
Previously, the search for a locale's data when using i18n was performed by a series of file system accesses that searched through the `@angular/common` package. The search is now conducted via Node.js module resolution which has several advantages. The internal structure of the package is no longer assumed and allows the `@angular/common` package to change its internal implementation without affecting the locale data search. File extensions of the locale data files are also not hard-coded as only the name of the locale data is needed to perform a search. There are also less direct file system access calls and the search can leverage whatever internal caching Node.js performs during module resolution.
1 parent c6e1131 commit 47bf88e

File tree

1 file changed

+20
-35
lines changed

1 file changed

+20
-35
lines changed

‎packages/angular_devkit/build_angular/src/utils/i18n-options.ts

+20-35
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,20 @@
88

99
import { BuilderContext } from '@angular-devkit/architect';
1010
import { json } from '@angular-devkit/core';
11-
import * as fs from 'fs';
12-
import * as os from 'os';
13-
import * as path from 'path';
11+
import fs from 'fs';
12+
import module from 'module';
13+
import os from 'os';
14+
import path from 'path';
1415
import { Schema as BrowserBuilderSchema } from '../builders/browser/schema';
1516
import { Schema as ServerBuilderSchema } from '../builders/server/schema';
1617
import { readTsconfig } from '../utils/read-tsconfig';
1718
import { createTranslationLoader } from './load-translations';
1819

20+
/**
21+
* The base module location used to search for locale specific data.
22+
*/
23+
const LOCALE_DATA_BASE_MODULE = '@angular/common/locales/global';
24+
1925
export interface I18nOptions {
2026
inlineLocales: Set<string>;
2127
sourceLocale: string;
@@ -171,12 +177,10 @@ export async function configureI18nBuild<T extends BrowserBuilderSchema | Server
171177
}
172178

173179
const projectRoot = path.join(context.workspaceRoot, (metadata.root as string) || '');
174-
const localeDataBasePath = findLocaleDataBasePath(projectRoot);
175-
if (!localeDataBasePath) {
176-
throw new Error(
177-
`Unable to find locale data within '@angular/common'. Please ensure '@angular/common' is installed.`,
178-
);
179-
}
180+
// The trailing slash is required to signal that the path is a directory and not a file.
181+
const projectRequire = module.createRequire(projectRoot + '/');
182+
const localeResolver = (locale: string) =>
183+
projectRequire.resolve(path.join(LOCALE_DATA_BASE_MODULE, locale));
180184

181185
// Load locale data and translations (if present)
182186
let loader;
@@ -186,11 +190,11 @@ export async function configureI18nBuild<T extends BrowserBuilderSchema | Server
186190
continue;
187191
}
188192

189-
let localeDataPath = findLocaleDataPath(locale, localeDataBasePath);
193+
let localeDataPath = findLocaleDataPath(locale, localeResolver);
190194
if (!localeDataPath) {
191195
const [first] = locale.split('-');
192196
if (first) {
193-
localeDataPath = findLocaleDataPath(first.toLowerCase(), localeDataBasePath);
197+
localeDataPath = findLocaleDataPath(first.toLowerCase(), localeResolver);
194198
if (localeDataPath) {
195199
context.logger.warn(
196200
`Locale data for '${locale}' cannot be found. Using locale data for '${first}'.`,
@@ -275,37 +279,18 @@ export async function configureI18nBuild<T extends BrowserBuilderSchema | Server
275279
return { buildOptions, i18n };
276280
}
277281

278-
function findLocaleDataBasePath(projectRoot: string): string | null {
279-
try {
280-
const commonPath = path.dirname(
281-
require.resolve('@angular/common/package.json', { paths: [projectRoot] }),
282-
);
283-
const localesPath = path.join(commonPath, 'locales/global');
284-
285-
if (!fs.existsSync(localesPath)) {
286-
return null;
287-
}
288-
289-
return localesPath;
290-
} catch {
291-
return null;
292-
}
293-
}
294-
295-
function findLocaleDataPath(locale: string, basePath: string): string | null {
282+
function findLocaleDataPath(locale: string, resolver: (locale: string) => string): string | null {
296283
// Remove private use subtags
297284
const scrubbedLocale = locale.replace(/-x(-[a-zA-Z0-9]{1,8})+$/, '');
298285

299-
const localeDataPath = path.join(basePath, scrubbedLocale + '.js');
300-
301-
if (!fs.existsSync(localeDataPath)) {
286+
try {
287+
return resolver(scrubbedLocale);
288+
} catch {
302289
if (scrubbedLocale === 'en-US') {
303290
// fallback to known existing en-US locale data as of 9.0
304-
return findLocaleDataPath('en-US-POSIX', basePath);
291+
return findLocaleDataPath('en-US-POSIX', resolver);
305292
}
306293

307294
return null;
308295
}
309-
310-
return localeDataPath;
311296
}

0 commit comments

Comments
 (0)