Skip to content

Commit 4475340

Browse files
committed
feat(nf): add shareAngularLocales helper
1 parent fecb7e3 commit 4475340

File tree

12 files changed

+166
-47
lines changed

12 files changed

+166
-47
lines changed
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "@softarc/native-federation",
3-
"version": "2.0.25",
3+
"version": "2.0.26",
44
"type": "commonjs",
55
"license": "MIT",
66
"dependencies": {
77
"json5": "^2.2.0",
88
"npmlog": "^6.0.2",
9-
"@softarc/native-federation-runtime": "2.0.25"
9+
"@softarc/native-federation-runtime": "2.0.26"
1010
}
1111
}

libs/native-federation-core/src/build.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
export { DEFAULT_SKIP_LIST } from './lib/core/default-skip-list';
22

3-
export { NormalizedFederationConfig } from './lib/config/federation-config';
3+
export {
4+
NormalizedFederationConfig,
5+
SharedConfig,
6+
} from './lib/config/federation-config';
47
export { FederationOptions } from './lib/core/federation-options';
58
export { setBuildAdapter } from './lib/core/build-adapter';
69
export { writeImportMap } from './lib/core/write-import-map';

libs/native-federation-esbuild/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@softarc/native-federation-esbuild",
3-
"version": "2.0.25",
3+
"version": "2.0.26",
44
"type": "commonjs",
55
"dependencies": {
66
"@rollup/plugin-commonjs": "^22.0.2",
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"name": "@softarc/native-federation-node",
3-
"version": "2.0.25"
3+
"version": "2.0.26"
44
}

libs/native-federation-runtime/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@softarc/native-federation-runtime",
3-
"version": "2.0.25",
3+
"version": "2.0.26",
44
"dependencies": {
55
"tslib": "^2.3.0"
66
},

libs/native-federation/README.md

+32
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,38 @@ ng serve shell -o
334334

335335
Now, by clicking at the 2nd menu item, you can load the remote directly into the host.
336336

337+
## Advanced Use Cases
338+
339+
### SSR and Hydration
340+
341+
We support Angular's SSR and (Incremental) Hydration. Please find [more information here](https://www.angulararchitects.io/blog/ssr-and-hydration-with-native-federation-for-angular/).
342+
343+
### Native Federation and Module Federation
344+
345+
We support combining Native Federation with Module Federation. Please find [more information here](https://www.angulararchitects.io/blog/combining-native-federation-and-module-federation/).
346+
347+
### Angular I18N
348+
349+
Since version 19.0.13, Native Federation for Angular supports Angular I18N. Here are some things to keep in mind:
350+
351+
- Make sure, you also add I18N support to your shell (e.g., `ng add @angular/localize --project shell`)
352+
- Configure I18N in your `angular.json`. Don’t use command line parameters (as the Native Federation Builder does not forward them to the ApplicationBuilder by design)
353+
- In production, make sure your `federation.manifest.json` points to the right language versions of your remotes
354+
355+
### Angular Localization
356+
357+
To just build the localization files you need for federation, you can use the `shareAngularLocales` introduced with version 19.0.14:
358+
359+
```js
360+
module.exports = withNativeFederation({
361+
[...],
362+
shared: {
363+
...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
364+
...shareAngularLocales(['en', 'de', 'fr']),
365+
}
366+
});
367+
```
368+
337369
## FAQ
338370

339371
### When to use this package?

libs/native-federation/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-architects/native-federation",
3-
"version": "19.0.13",
3+
"version": "19.0.15",
44
"main": "src/index.js",
55
"generators": "./collection.json",
66
"builders": "./builders.json",
@@ -20,8 +20,8 @@
2020
},
2121
"dependencies": {
2222
"@babel/core": "^7.19.0",
23-
"@softarc/native-federation": "2.0.25",
24-
"@softarc/native-federation-runtime": "2.0.25",
23+
"@softarc/native-federation": "2.0.26",
24+
"@softarc/native-federation-runtime": "2.0.26",
2525
"@types/browser-sync": "^2.29.0",
2626
"@chialab/esbuild-plugin-commonjs": "^0.18.0",
2727
"browser-sync": "^3.0.2",

libs/native-federation/src/builders/build/builder.ts

+53-7
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ import { createSharedMappingsPlugin } from '../../utils/shared-mappings-plugin';
4444
// import { NextHandleFunction } from 'vite';
4545
import { PluginBuild } from 'esbuild';
4646
import { FederationInfo } from '@softarc/native-federation-runtime';
47-
import { getI18nConfig, translateFederationArtefacts } from '../../utils/i18n';
48-
import { createOrUpdateGithubRelease } from 'nx/src/command-line/release/utils/github';
47+
import {
48+
getI18nConfig,
49+
I18nConfig,
50+
translateFederationArtefacts,
51+
} from '../../utils/i18n';
4952

5053
function _buildApplication(options, context, pluginsOrExtensions) {
5154
let extensions;
@@ -147,12 +150,20 @@ export async function* runBuilder(
147150

148151
const i18n = await getI18nConfig(context);
149152

153+
const localeFilter = getLocaleFilter(options, runServer);
154+
150155
const browserOutputPath = path.join(
151156
outputOptions.base,
152157
outputOptions.browser,
153-
i18n?.sourceLocale || ''
158+
options.localize ? i18n?.sourceLocale || '' : ''
154159
);
155160

161+
const differentDevServerOutputPath =
162+
Array.isArray(localeFilter) && localeFilter.length === 1;
163+
const devServerOutputPath = !differentDevServerOutputPath
164+
? browserOutputPath
165+
: path.join(outputOptions.base, outputOptions.browser, options.localize[0]);
166+
156167
const fedOptions: FederationOptions = {
157168
workspaceRoot: context.workspaceRoot,
158169
outputPath: browserOutputPath,
@@ -192,7 +203,7 @@ export async function* runBuilder(
192203

193204
const fileName = path.join(
194205
fedOptions.workspaceRoot,
195-
fedOptions.outputPath,
206+
devServerOutputPath,
196207
url
197208
);
198209

@@ -246,8 +257,13 @@ export async function* runBuilder(
246257
}
247258

248259
const hasLocales = i18n?.locales && Object.keys(i18n.locales).length > 0;
249-
if (!runServer && hasLocales) {
250-
translateFederationArtefacts(i18n, outputOptions.base, federationResult);
260+
if (hasLocales && localeFilter) {
261+
translateFederationArtefacts(
262+
i18n,
263+
localeFilter,
264+
outputOptions.base,
265+
federationResult
266+
);
251267
}
252268

253269
options.deleteOutputPath = false;
@@ -306,7 +322,21 @@ export async function* runBuilder(
306322
if (!first && (nfOptions.dev || watch)) {
307323
setTimeout(async () => {
308324
try {
309-
await buildForFederation(config, fedOptions, externals);
325+
federationResult = await buildForFederation(
326+
config,
327+
fedOptions,
328+
externals
329+
);
330+
331+
if (hasLocales && localeFilter) {
332+
translateFederationArtefacts(
333+
i18n,
334+
localeFilter,
335+
outputOptions.base,
336+
federationResult
337+
);
338+
}
339+
310340
logger.info('Done!');
311341
} catch {
312342
logger.error('Not successful!');
@@ -327,6 +357,22 @@ export async function* runBuilder(
327357
// eslint-disable-next-line @typescript-eslint/no-explicit-any
328358
export default createBuilder(runBuilder) as any;
329359

360+
function getLocaleFilter(
361+
options: ApplicationBuilderOptions,
362+
runServer: boolean
363+
) {
364+
let localize = options.localize || false;
365+
366+
if (runServer && Array.isArray(localize) && localize.length > 1) {
367+
localize = false;
368+
}
369+
370+
if (runServer && localize === true) {
371+
localize = false;
372+
}
373+
return localize;
374+
}
375+
330376
function removeBaseHref(req: any, baseHref?: string) {
331377
let url = req.url;
332378

libs/native-federation/src/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ export {
55
shareAll,
66
DEFAULT_SKIP_LIST,
77
} from '@softarc/native-federation/build';
8+
9+
export { shareAngularLocales } from './utils/angular-locales';

libs/native-federation/src/utils/angular-esbuild-adapter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ function setNgServerMode(): void {
432432
if (!content.includes(lineToAdd)) {
433433
content = lineToAdd + '\n' + content;
434434
fs.writeFileSync(fileToPatch, content);
435-
}
435+
}
436436
}
437437
} catch (e) {
438438
console.error(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { share, SharedConfig } from '@softarc/native-federation/build';
2+
3+
export function shareAngularLocales(
4+
keys,
5+
config: SharedConfig = {
6+
singleton: true,
7+
strictVersion: true,
8+
requiredVersion: 'auto',
9+
}
10+
) {
11+
return keys.reduce((acc, key) => {
12+
acc[`@angular/common/locales/${key}`] = {
13+
...config,
14+
packageInfo: config.packageInfo || {
15+
...config.packageInfo,
16+
entryPoint:
17+
config.packageInfo?.entryPoint ||
18+
`node_modules/@angular/common/locales/${key}.mjs`,
19+
},
20+
};
21+
return share(acc);
22+
}, {});
23+
}

libs/native-federation/src/utils/i18n.ts

+43-30
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,29 @@ export async function getI18nConfig(
2727

2828
export async function translateFederationArtefacts(
2929
i18n: I18nConfig,
30+
localize: boolean | string[],
3031
outputPath: string,
3132
federationResult: FederationInfo
3233
) {
34+
const neededLocales = Array.isArray(localize)
35+
? localize
36+
: Object.keys(i18n.locales);
37+
38+
const locales = Object.keys(i18n.locales).filter((locale) =>
39+
neededLocales.includes(locale)
40+
);
41+
42+
if (locales.length === 0) {
43+
return;
44+
}
3345

3446
logger.info('Writing Translations');
3547

36-
const translationFiles =
37-
'"' +
38-
Object.values(i18n.locales || {})
39-
.map((value) => JSON.stringify(value))
40-
.join(' ')
41-
.replace(/"/g, '') +
42-
'"';
48+
const translationFiles = locales
49+
.map((loc) => i18n.locales[loc])
50+
.map((value) => JSON.stringify(value))
51+
.join(' ');
4352

44-
const locales = Object.keys(i18n.locales);
4553
const targetLocales = locales.join(' ');
4654

4755
const sourceLocale = i18n.sourceLocale;
@@ -57,11 +65,7 @@ export async function translateFederationArtefacts(
5765
// to improve performance
5866
const sourcePattern = '{' + federationFiles.join(',') + '}';
5967

60-
const sourceLocalePath = path.join(
61-
outputPath,
62-
'browser',
63-
sourceLocale,
64-
);
68+
const sourceLocalePath = path.join(outputPath, 'browser', sourceLocale);
6569

6670
const cmd = `node node_modules/.bin/localize-translate -r ${sourceLocalePath} -s "${sourcePattern}" -t ${translationFiles} -o ${translationOutPath} --target-locales ${targetLocales} -l ${sourceLocale}`;
6771

@@ -74,26 +78,35 @@ export async function translateFederationArtefacts(
7478
}
7579

7680
function execCommand(cmd: string, defaultSuccessInfo: string) {
77-
try {
78-
const output = execSync(cmd);
79-
logger.info(output.toString() || defaultSuccessInfo);
80-
} catch (error) {
81-
logger.error(error.message);
82-
}
81+
try {
82+
const output = execSync(cmd);
83+
logger.info(output.toString() || defaultSuccessInfo);
84+
} catch (error) {
85+
logger.error(error.message);
86+
}
8387
}
8488

85-
function copyRemoteEntry(locales: string[], outputPath: string, sourceLocalePath: string) {
86-
const remoteEntry = path.join(sourceLocalePath, 'remoteEntry.json');
87-
88-
for (const locale of locales) {
89-
const localePath = path.join(outputPath, 'browser', locale, 'remoteEntry.json');
90-
fs.copyFileSync(remoteEntry, localePath);
91-
}
89+
function copyRemoteEntry(
90+
locales: string[],
91+
outputPath: string,
92+
sourceLocalePath: string
93+
) {
94+
const remoteEntry = path.join(sourceLocalePath, 'remoteEntry.json');
95+
96+
for (const locale of locales) {
97+
const localePath = path.join(
98+
outputPath,
99+
'browser',
100+
locale,
101+
'remoteEntry.json'
102+
);
103+
fs.copyFileSync(remoteEntry, localePath);
104+
}
92105
}
93106

94107
function ensureDistFolders(locales: string[], outputPath: string) {
95-
for (const locale of locales) {
96-
const localePath = path.join(outputPath, 'browser', locale);
97-
fs.mkdirSync(localePath, { recursive: true })
98-
}
108+
for (const locale of locales) {
109+
const localePath = path.join(outputPath, 'browser', locale);
110+
fs.mkdirSync(localePath, { recursive: true });
111+
}
99112
}

0 commit comments

Comments
 (0)