Issue
I'm attempting to dynamically import locales in an Angular 9 (monorepo-based) app. I'm doing something like the following:
import { Injectable } from '@angular/core';
import { registerLocaleData } from '@angular/common';
@Injectable()
export class LocaleService {
...
private capitalize(str: string): string {
return str.charAt[0].toUpperCase() + str.substring(1).toLowerCase();
}
registerLocales() {
for (const lang of ['de', 'fr', 'es']) {
const basePkg = `locale${this.capitalize(lang)}`;
const extraPkg = basePkg + 'Extra';
const base = import(`@angular/common/locales/${lang}`).then(m => m[basePkg]);
const extra = import(`@angular/common/locales/extra/${lang}`).then(m => m[extraPkg]);
registerLocaleData(base, extra);
}
}
}
instead of:
import { Injectable } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de';
import localeEs from '@angular/common/locales/es';
import localeEsExtra from '@angular/common/locales/extra/es';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';
@Injectable()
export class LocaleService {
...
registerLocales() {
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeEs, localeEsExtra);
registerLocaleData(localeFr, localeFrExtra);
}
}
Within this code even executing, I'm getting a boatload of errors caused by the imports of the form:
WARNING in /home/me/somerepo/node_modules/@angular/common/locales/zu.d.ts Module build failed (from /home/me/somerepo/node_modules/@ngtools/webpack/src/index.js): Error: /home/me/somerepo/node_modules/@angular/common/locales/zu.d.ts is missing from the TypeScript compilation. Please make sure it is in your tsconfig via the 'files' or 'include' property.
Commenting out the imports and call to registerLocaleData
eliminates the error. What on earth am I doing wrong here?
Solution
The excellent article mentioned by Eliseo's comment has the answer. Typescript's import
function is not an ordinary function call. In short, what's happening here is that import tells Webpack to create chunks for everything that matches the pattern in the argument. This is a problem, because the pattern matches all of the .d.ts
files in the locales directory, while we actually only want the .js
files. The solution is to use Webpack's "magic comments". The following is enough to get everything loading properly:
const base = import(
/* webpackExclude: /\.d\.ts$/ */
`@angular/common/locales/${key}`).then(m => m[basePkg]);
const extra = import(
/* webpackExclude: /\.d\.ts$/ */
`@angular/common/locales/extra/${key}`).then(m => m[extraPkg]);
But... there are a couple of problems.
Every locale is turned into a chunk. That creates over 1,000 chunks. Ouch.
The chunks are just given numbers as names.
Magic comments to the rescue again:
const base = import(
/* webpackExclude: /\.d\.ts$/ */
/* webpackMode: "lazy-once" */
/* webpackChunkName: "i18n-base" */
`@angular/common/locales/${key}`).then(m => m[basePkg]);
const extra = import(
/* webpackExclude: /\.d\.ts$/ */
/* webpackMode: "lazy-once" */
/* webpackChunkName: "i18n-extra" */
`@angular/common/locales/extra/${key}`).then(m => m[extraPkg]);
This gets closer, creating two chunks instead of thousands, but they're big. If we know what locales we're interested in, we can do a lot better. Here's the final version:
const base = import(
/* webpackInclude: /(de|en|es|fr|it|nl|no|pl|pt-BR|pt|fi|sv|ko|ru|zh|zh-Hans|zh-Hant|ja)\.js/ */
/* webpackMode: "lazy-once" */
/* webpackChunkName: "i18n-base" */
`@angular/common/locales/${key}`).then(m => m[basePkg]);
const extra = import(
/* webpackInclude: /(de|en|es|fr|it|nl|no|pl|pt-BR|pt|fi|sv|ko|ru|zh|zh-Hans|zh-Hant|ja)\.js/ */
/* webpackMode: "lazy-once" */
/* webpackChunkName: "i18n-extra" */
`@angular/common/locales/extra/${key}`).then(m => m[extraPkg]);
This changes the logic from specifying which files to ignore to specifying which files to load. It results in chunks of around 100Kb instead of 6Mb.
Answered By - Scott Deerwester
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.