Skip to content

Commit 2f83307

Browse files
committed
refactor(core): separate fileReplacements from compensating missing exports
1 parent 705e78d commit 2f83307

File tree

8 files changed

+61
-40
lines changed

8 files changed

+61
-40
lines changed

libs/native-federation-core/README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,8 @@ const tsConfig = 'tsconfig.json';
9191
const outputPath = `dist/${projectName}`;
9292

9393
/*
94-
* Step 1: Initialize Native Federation
94+
* Step 1: Initialize Native Federation
9595
*/
96-
9796
await federationBuilder.init({
9897
options: {
9998
workspaceRoot: path.join(__dirname, '..'),
@@ -324,6 +323,38 @@ The script with the type `esms-options` configures the polyfill. This library wa
324323
325324
To make the polyfill to load your EcmaScript modules (bundles) in shim mode, assign the type `module-shim`.
326325
326+
## React and Other CommonJS Libs
327+
328+
Native Federation uses Web Standards like EcmaScript Modules. Most libs and frameworks support them meanwhile. Unfortunately, React still uses CommonJS (und UMD). We do our best to convert these libs to EcmaScript Modules. In the case of React there are some challenges due to the dynamic way the React bundles use the ``exports`` object.
329+
330+
As the community is moving to EcmaScrpt Modules, we expect that these issues will vanish over time. In between, we provide some solutions for dealing with CommonJS-based libraries using ``exports`` in a dynamic way.
331+
332+
One of them is ``fileReplacemnts``:
333+
334+
```javascript
335+
import { reactReplacements } from '@softarc/native-federation-esbuild/src/lib/react-replacements';
336+
import { createEsBuildAdapter } from '@softarc/native-federation-esbuild';
337+
338+
[...]
339+
340+
createEsBuildAdapter({
341+
plugins: [],
342+
fileReplacements: reactReplacements.prod
343+
})
344+
```
345+
346+
Please note that the adapter comes with ``fileReplacements`` settings for React for both, ``dev`` mode and ``prod`` mode. For similar libraries you can add your own replacements. Also, using the ``compensateExports`` property, you can activate some additional logic for such libraries to make sure the exports are not lost
347+
348+
```javascript
349+
createEsBuildAdapter({
350+
plugins: [],
351+
fileReplacements: reactReplacements.prod,
352+
compensateExports: [new RegExp('/my-lib/')]
353+
})
354+
```
355+
356+
The default value for ``compensateExports`` is ``[new RegExp('/react/')]``.
357+
327358
## More: Blog Articles
328359
329360
Find out more about our work including Micro Frontends and Module Federation but also about alternatives to these approaches in our [blog](https://www.angulararchitects.io/en/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/).
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "@softarc/native-federation",
3-
"version": "1.1.0-beta.0",
3+
"version": "1.1.0",
44
"type": "commonjs",
55
"dependencies": {
66
"json5": "^2.2.0",
77
"npmlog": "^6.0.2",
8-
"@softarc/native-federation-runtime": "1.1.0-beta.0"
8+
"@softarc/native-federation-runtime": "1.1.0"
99
}
1010
}

libs/native-federation-esbuild/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@softarc/native-federation-esbuild",
3-
"version": "1.1.0-beta.0",
3+
"version": "1.1.0",
44
"type": "commonjs",
55
"dependencies": {
66
"@rollup/plugin-commonjs": "^22.0.2",
@@ -11,6 +11,5 @@
1111
"esbuild": "^0.15.5",
1212
"npmlog": "^6.0.2",
1313
"acorn": "^8.8.1"
14-
1514
}
1615
}

libs/native-federation-esbuild/src/lib/adapter.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@ export const esBuildAdapter: BuildAdapter = createEsBuildAdapter({
2121
});
2222

2323
export type ReplacementConfig = {
24-
file: string,
25-
tryCompensateMissingExports: boolean
24+
file: string
2625
};
2726

2827
export interface EsBuildAdapterConfig {
2928
plugins: esbuild.Plugin[];
3029
fileReplacements?: Record<string, string | ReplacementConfig>
3130
skipRollup?: boolean,
31+
compensateExports?: RegExp[]
3232
}
3333

3434
export function createEsBuildAdapter(config: EsBuildAdapterConfig) {
35+
36+
if (!config.compensateExports) {
37+
config.compensateExports = [new RegExp('/react/')];
38+
}
39+
3540
return async (options: BuildAdapterOptions) => {
3641
const { entryPoint, external, outfile, watch } = options;
3742

@@ -43,7 +48,7 @@ export function createEsBuildAdapter(config: EsBuildAdapterConfig) {
4348
await prepareNodePackage(entryPoint, external, tmpFolder, config);
4449
}
4550

46-
const r = await esbuild.build({
51+
await esbuild.build({
4752
entryPoints: [isPkg ? tmpFolder : entryPoint],
4853
external,
4954
outfile,
@@ -66,27 +71,18 @@ export function createEsBuildAdapter(config: EsBuildAdapterConfig) {
6671
plugins: [...config.plugins],
6772
});
6873

69-
postProcess(config, entryPoint, outfile);
70-
};
71-
}
72-
73-
function postProcess(config: EsBuildAdapterConfig, entryPoint: string, outfile: string) {
74-
if (config.fileReplacements) {
75-
const replacements = normalize(config.fileReplacements);
76-
77-
const normalizedPath = entryPoint.replace(/\\/g, '/');
78-
const key = Object.keys(replacements).find(key => normalizedPath.endsWith(key))
79-
80-
if (key && replacements[key] && replacements[key].tryCompensateMissingExports) {
81-
const file = replacements[key].file;
82-
compensateExports(file, outfile);
74+
const normEntryPoint = entryPoint.replace(/\\/g, '/');
75+
if (isPkg && config?.compensateExports?.find(regExp => regExp.exec(normEntryPoint))) {
76+
logger.verbose('compensate exports for ' + tmpFolder);
77+
compensateExports(tmpFolder, outfile);
8378
}
84-
}
79+
80+
};
8581
}
8682

87-
function compensateExports(entryPoint: string, outfile: string): void {
83+
function compensateExports(entryPoint: string, outfile?: string): void {
8884
const inExports = collectExports(entryPoint);
89-
const outExports = collectExports(outfile);
85+
const outExports = outfile ? collectExports(outfile) : inExports;
9086

9187
if (!outExports.hasDefaultExport || outExports.hasFurtherExports) {
9288
return;
@@ -99,7 +95,8 @@ function compensateExports(entryPoint: string, outfile: string): void {
9995
exports += `export { ${exp}$softarc as ${exp} };\n`;
10096
}
10197

102-
fs.appendFileSync(outfile, exports, 'utf-8');
98+
const target = outfile ?? entryPoint;
99+
fs.appendFileSync(target, exports, 'utf-8');
103100
}
104101

105102
async function prepareNodePackage(
@@ -135,6 +132,7 @@ async function prepareNodePackage(
135132
sourcemap: true,
136133
exports: 'named',
137134
});
135+
138136
}
139137

140138
function inferePkgName(entryPoint: string) {
@@ -149,7 +147,6 @@ function normalize(config: Record<string, string | ReplacementConfig>): Record<s
149147
if (typeof config[key] === 'string') {
150148
result[key] = {
151149
file: config[key] as string,
152-
tryCompensateMissingExports: false
153150
}
154151
}
155152
else {

libs/native-federation-esbuild/src/lib/react-replacements.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,23 @@ export const reactReplacements: Record<
77
dev: {
88
'node_modules/react/index.js': {
99
file: 'node_modules/react/cjs/react.development.js',
10-
tryCompensateMissingExports: true,
1110
},
1211
'node_modules/react/jsx-dev-runtime.js': {
1312
file: 'node_modules/react/cjs/react-jsx-dev-runtime.development.js',
14-
tryCompensateMissingExports: true,
1513
},
1614
'node_modules/react/jsx-runtime.js': {
1715
file: 'node_modules/react/cjs/react-jsx-runtime.development.js',
18-
tryCompensateMissingExports: true,
1916
},
2017
},
2118
prod: {
2219
'node_modules/react/index.js': {
2320
file: 'node_modules/react/cjs/react.production.min.js',
24-
tryCompensateMissingExports: true,
2521
},
2622
'node_modules/react/jsx-dev-runtime.js': {
2723
file: 'node_modules/react/cjs/react-jsx-dev-runtime.production.min.js',
28-
tryCompensateMissingExports: true,
2924
},
3025
'node_modules/react/jsx-runtime.js': {
3126
file: 'node_modules/react/cjs/react-jsx-runtime.production.min.js',
32-
tryCompensateMissingExports: true,
3327
},
3428
},
3529
};

libs/native-federation-runtime/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@softarc/native-federation-runtime",
3-
"version": "1.1.0-beta.0",
3+
"version": "1.1.0",
44
"peerDependencies": {},
55
"dependencies": {
66
"tslib": "^2.3.0"

libs/native-federation/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
"rollup-plugin-node-externals": "^4.1.1",
2424
"esbuild": "^0.15.5",
2525
"@babel/core": "^7.19.0",
26-
"@softarc/native-federation": "1.1.0-beta.0",
27-
"@softarc/native-federation-runtime": "1.1.0-beta.0",
26+
"@softarc/native-federation": "1.1.0",
27+
"@softarc/native-federation-runtime": "1.1.0",
2828
"@rollup/plugin-json": "^4.1.0",
2929
"cross-spawn": "^7.0.3",
3030
"rollup-plugin-terser": "^7.0.2",

update-local.bat

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
call npm unpublish @softarc/native-federation@1.1.0-beta.0 --registry http://localhost:4873
2-
call npm unpublish @softarc/native-federation-runtime@1.1.0-beta.0 --registry http://localhost:4873
3-
call npm unpublish @softarc/native-federation-esbuild@1.1.0-beta.0 --registry http://localhost:4873
1+
call npm unpublish @softarc/native-federation@1.1.0 --registry http://localhost:4873
2+
call npm unpublish @softarc/native-federation-runtime@1.1.0 --registry http://localhost:4873
3+
call npm unpublish @softarc/native-federation-esbuild@1.1.0 --registry http://localhost:4873
44
call npm unpublish @angular-architects/native-federation@1.1.0-beta.0 --registry http://localhost:4873
55

66
call nx build native-federation

0 commit comments

Comments
 (0)