From f0e8ea3b3224159b85e7355fbd4b7c2b5a9596f2 Mon Sep 17 00:00:00 2001 From: Manfred Steyer Date: Tue, 4 Jun 2024 23:15:13 +0300 Subject: [PATCH] feat(nf): update for Angular 18 --- libs/native-federation/README.md | 1 + libs/native-federation/docs/update18.md | 34 +++++++++++ .../migration-collection.json | 13 +++++ libs/native-federation/package.json | 5 +- libs/native-federation/project.json | 5 ++ .../src/builders/build/builder.ts | 58 ++++++++++++------- .../src/patch-angular-build.ts | 4 ++ .../src/schematics/init/schematic.ts | 42 +++++++++++++- .../src/schematics/update18/schema.json | 8 +++ .../src/schematics/update18/schematic.ts | 10 ++++ .../src/utils/patch-angular-build.ts | 30 ++++++++++ 11 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 libs/native-federation/docs/update18.md create mode 100644 libs/native-federation/migration-collection.json create mode 100644 libs/native-federation/src/patch-angular-build.ts create mode 100644 libs/native-federation/src/schematics/update18/schema.json create mode 100644 libs/native-federation/src/schematics/update18/schematic.ts create mode 100644 libs/native-federation/src/utils/patch-angular-build.ts diff --git a/libs/native-federation/README.md b/libs/native-federation/README.md index f61c785b..b6b823c2 100644 --- a/libs/native-federation/README.md +++ b/libs/native-federation/README.md @@ -24,6 +24,7 @@ We will at least provide a new version of this package per Angular major. If nec - Use version 16.2.x for Angular 16.2.x - Use version 17.x for Angular 17.x - Use version 17.1.x for Angular 17.1+ +- Use version 18.x for Angular 18+ ## Angular Integration diff --git a/libs/native-federation/docs/update18.md b/libs/native-federation/docs/update18.md new file mode 100644 index 00000000..b544c861 --- /dev/null +++ b/libs/native-federation/docs/update18.md @@ -0,0 +1,34 @@ +# Update to Native Federation for Angular 18 + +The package ``@angular-architects/native-federation`` version 18 was successfully tested with Angular 18. + +## Option 1 + +Just use ``ng update``: + +``` +ng update @angular-architects/native-federation +``` + +## Option 2 + +Use npm install: + +``` +npm i @angular-architects/native-federation@^18 +``` + +Make sure you have the following ``postinstall`` script in your ``package.json``: + +```json +"scripts": { + [...] + "postinstall": "node node_modules/@angular-architects/native-federation/src/patch-angular-build.js" +}, +``` + +Run the ``postinstall`` script once manually for initialization: + +``` +npm run postinstall +``` \ No newline at end of file diff --git a/libs/native-federation/migration-collection.json b/libs/native-federation/migration-collection.json new file mode 100644 index 00000000..3902de47 --- /dev/null +++ b/libs/native-federation/migration-collection.json @@ -0,0 +1,13 @@ +{ + "$schema": "../../node_modules/@angular-devkit/schematics/collection-schema.json", + "name": "native-federation", + "version": "0.0.1", + "schematics": { + "update18": { + "version": "18", + "factory": "./src/schematics/update18/schematic", + "schema": "./src/schematics/update18/schema.json", + "description": "migrating to v18" + } + } +} diff --git a/libs/native-federation/package.json b/libs/native-federation/package.json index db48dd57..4b6ddc7f 100644 --- a/libs/native-federation/package.json +++ b/libs/native-federation/package.json @@ -1,6 +1,6 @@ { "name": "@angular-architects/native-federation", - "version": "17.1.8", + "version": "18.0.1", "main": "src/index.js", "generators": "./collection.json", "builders": "./builders.json", @@ -15,6 +15,9 @@ "type": "git", "url": "https://github.com/angular-architects/module-federation-plugin" }, + "ng-update": { + "migrations": "./migration-collection.json" + }, "dependencies": { "@babel/core": "^7.19.0", "@softarc/native-federation": "2.0.9", diff --git a/libs/native-federation/project.json b/libs/native-federation/project.json index c31f086b..22e22aff 100644 --- a/libs/native-federation/project.json +++ b/libs/native-federation/project.json @@ -34,6 +34,11 @@ "glob": "builders.json", "output": "." }, + { + "input": "./libs/native-federation", + "glob": "migration-collection.json", + "output": "." + }, { "input": "./libs/native-federation", "glob": "executors.json", diff --git a/libs/native-federation/src/builders/build/builder.ts b/libs/native-federation/src/builders/build/builder.ts index 5fabc7c0..feb7abd0 100644 --- a/libs/native-federation/src/builders/build/builder.ts +++ b/libs/native-federation/src/builders/build/builder.ts @@ -11,10 +11,10 @@ import { createBuilder, } from '@angular-devkit/architect'; -import { buildApplication } from '@angular-devkit/build-angular'; +import { buildApplication, buildApplicationInternal } from '@angular/build/src/builders/application'; +import { serveWithVite } from '@angular/build/src/builders/dev-server/vite-server'; import { - executeDevServerBuilder, DevServerBuilderOptions, } from '@angular-devkit/build-angular'; import { normalizeOptions } from '@angular-devkit/build-angular/src/builders/dev-server/options'; @@ -53,6 +53,19 @@ import { Connect } from 'vite'; import { PluginBuild } from 'esbuild'; import { FederationInfo } from '@softarc/native-federation-runtime'; +function _buildApplication(options, context, pluginsOrExtensions) { + let extensions; + if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) { + extensions = { + codePlugins: pluginsOrExtensions, + }; + } + else { + extensions = pluginsOrExtensions; + } + return buildApplicationInternal(options, context, { write: false }, extensions); +} + export async function* runBuilder( nfOptions: NfBuilderSchema, context: BuilderContext @@ -217,20 +230,23 @@ export async function* runBuilder( // TODO: Clarify how DevServer needs to be executed. Not sure if its right. // TODO: Clarify if buildApplication is needed `executeDevServerBuilder` seems to choose the correct DevServer + + const appBuilderName = '@angular-devkit/build-angular:application'; + const builderRun = nfOptions.dev - ? executeDevServerBuilder( - options, - context, - { - indexHtml: nfOptions.skipHtmlTransform - ? {} - : { indexHtml: transformIndexHtml(nfOptions) }, - }, - { - buildPlugins: plugins, - middleware, - } - ) + ? serveWithVite( + normOuterOptions, + appBuilderName, + _buildApplication, + context, + nfOptions.skipHtmlTransform + ? {} + : { indexHtml: transformIndexHtml(nfOptions) }, + { + buildPlugins: plugins, + middleware, + } + ) : buildApplication(options, context, plugins); // builderRun.output.subscribe(async (output) => { @@ -259,13 +275,13 @@ export async function* runBuilder( updateIndexHtml(fedOptions, nfOptions); } - if (first && runServer) { - startServer(nfOptions, fedOptions.outputPath, memResults); - } + // if (first && runServer) { + // startServer(nfOptions, fedOptions.outputPath, memResults); + // } - if (!first && runServer) { - reloadBrowser(); - } + // if (!first && runServer) { + // reloadBrowser(); + // } if (!runServer) { yield output; diff --git a/libs/native-federation/src/patch-angular-build.ts b/libs/native-federation/src/patch-angular-build.ts new file mode 100644 index 00000000..880cd046 --- /dev/null +++ b/libs/native-federation/src/patch-angular-build.ts @@ -0,0 +1,4 @@ +import { patchAngularBuild } from './utils/patch-angular-build'; + +const workspaceRoot = process.cwd(); +patchAngularBuild(workspaceRoot); diff --git a/libs/native-federation/src/schematics/init/schematic.ts b/libs/native-federation/src/schematics/init/schematic.ts index 51196f19..5bb98b64 100644 --- a/libs/native-federation/src/schematics/init/schematic.ts +++ b/libs/native-federation/src/schematics/init/schematic.ts @@ -14,6 +14,8 @@ import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { strings } from '@angular-devkit/core'; import { MfSchematicSchema } from './schema'; +import { patchAngularBuildPackageJson } from '../../utils/patch-angular-build'; + import { addPackageJsonDependency, NodeDependencyType, @@ -32,6 +34,32 @@ type NormalizedOptions = { port: number; }; +export function updatePackageJson(tree: Tree): void { + const packageJson = tree.readJson('package.json'); + + const scriptCall = 'node node_modules/@angular-architects/native-federation/src/patch-angular-build.js'; + + if (!packageJson['scripts']) { + packageJson['scripts'] = {}; + } + + let postInstall = (packageJson['scripts']?.['postinstall'] || '') as string; + if (postInstall?.includes(scriptCall)) { + return; + } + + if (postInstall) { + postInstall += ' && '; + } + + postInstall += scriptCall; + + packageJson['scripts']['postinstall'] = postInstall; + + tree.overwrite('package.json', JSON.stringify(packageJson, null, 2)); + +} + export default function config(options: MfSchematicSchema): Rule { return async function (tree, context) { const workspaceFileName = getWorkspaceFileName(tree); @@ -71,6 +99,10 @@ export default function config(options: MfSchematicSchema): Rule { updateWorkspaceConfig(tree, normalized, workspace, workspaceFileName); + updatePackageJson(tree); + + patchAngularBuild(tree); + addPackageJsonDependency(tree, { name: 'es-module-shims', type: NodeDependencyType.Default, @@ -84,6 +116,12 @@ export default function config(options: MfSchematicSchema): Rule { }; } +export function patchAngularBuild(tree) { + const packageJson = JSON.parse(tree.read('node_modules/@angular/build/package.json')); + patchAngularBuildPackageJson(packageJson); + tree.overwrite('node_modules/@angular/build/package.json', JSON.stringify(packageJson, null, 2)); +} + function updateWorkspaceConfig( tree: Tree, options: NormalizedOptions, @@ -217,7 +255,7 @@ function normalizeOptions( ); const manifestPath = path - .join(projectRoot, 'src/assets/federation.manifest.json') + .join(projectRoot, 'public/federation.manifest.json') .replace(/\\/g, '/'); const main = @@ -316,7 +354,7 @@ function makeMainAsync( if (options.type === 'dynamic-host') { newMainContent = `import { initFederation } from '@angular-architects/native-federation'; -initFederation('/assets/federation.manifest.json') +initFederation('federation.manifest.json') .catch(err => console.error(err)) .then(_ => import('./bootstrap')) .catch(err => console.error(err)); diff --git a/libs/native-federation/src/schematics/update18/schema.json b/libs/native-federation/src/schematics/update18/schema.json new file mode 100644 index 00000000..1576498d --- /dev/null +++ b/libs/native-federation/src/schematics/update18/schema.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "mf", + "title": "", + "type": "object", + "properties": { + } +} diff --git a/libs/native-federation/src/schematics/update18/schematic.ts b/libs/native-federation/src/schematics/update18/schematic.ts new file mode 100644 index 00000000..7fa11cbc --- /dev/null +++ b/libs/native-federation/src/schematics/update18/schematic.ts @@ -0,0 +1,10 @@ +import { Rule, Tree } from '@angular-devkit/schematics'; + +import { patchAngularBuild, updatePackageJson } from '../init/schematic'; + +export default function update18(): Rule { + return async function (tree: Tree) { + updatePackageJson(tree); + patchAngularBuild(tree); + }; +} diff --git a/libs/native-federation/src/utils/patch-angular-build.ts b/libs/native-federation/src/utils/patch-angular-build.ts new file mode 100644 index 00000000..9b0f4725 --- /dev/null +++ b/libs/native-federation/src/utils/patch-angular-build.ts @@ -0,0 +1,30 @@ +import * as path from 'path'; +import * as fs from 'fs'; + +export function patchAngularBuildPackageJson(packageJson: unknown): void { + const exportsMap = packageJson['exports']; + + if (!exportsMap) { + console.log('No need to patch @angular/build/package.json') + return; + } + + packageJson['_exports'] = exportsMap; + delete packageJson['exports']; + + packageJson['types'] = './src/index.d.ts'; + packageJson['main'] = './src/index.js'; + packageJson['module'] = './src/index.js'; +} + +export function patchAngularBuild(workspaceRoot: string): void { + const packagePath = path.join(workspaceRoot, 'node_modules/@angular/build/package.json'); + + const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8')); + + patchAngularBuildPackageJson(packageJson); + + fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); + + console.log('@angular/build/package.json patched'); +} \ No newline at end of file