diff --git a/docs/generated/packages/angular.json b/docs/generated/packages/angular.json index 190be2c94160a..c0ccd36bf9634 100644 --- a/docs/generated/packages/angular.json +++ b/docs/generated/packages/angular.json @@ -2963,6 +2963,116 @@ "aliases": [], "hidden": false, "path": "/packages/angular/src/builders/webpack-server/schema.json" + }, + { + "name": "module-federation-dev-server", + "implementation": "/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema", + "title": "Schema for Module Federation Dev Server", + "description": "The module-federation-dev-server executor is reserved exclusively for use with host Module Federation applications. It allows the user to specify which remote applications should be served with the host.", + "type": "object", + "presets": [ + { + "name": "Using a Different Port", + "keys": ["browserTarget", "port"] + } + ], + "properties": { + "browserTarget": { + "type": "string", + "description": "A browser builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.", + "pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$" + }, + "port": { + "type": "number", + "description": "Port to listen on.", + "default": 4200 + }, + "host": { + "type": "string", + "description": "Host to listen on.", + "default": "localhost" + }, + "proxyConfig": { + "type": "string", + "description": "Proxy configuration file. For more information, see https://angular.io/guide/build#proxying-to-a-backend-server." + }, + "ssl": { + "type": "boolean", + "description": "Serve using HTTPS.", + "default": false + }, + "sslKey": { + "type": "string", + "description": "SSL key to use for serving HTTPS." + }, + "sslCert": { + "type": "string", + "description": "SSL certificate to use for serving HTTPS." + }, + "headers": { + "type": "object", + "description": "Custom HTTP headers to be added to all responses.", + "propertyNames": { "pattern": "^[-_A-Za-z0-9]+$" }, + "additionalProperties": { "type": "string" } + }, + "open": { + "type": "boolean", + "description": "Opens the url in default browser.", + "default": false, + "alias": "o" + }, + "verbose": { + "type": "boolean", + "description": "Adds more details to output logging." + }, + "liveReload": { + "type": "boolean", + "description": "Whether to reload the page on change, using live-reload.", + "default": true + }, + "publicHost": { + "type": "string", + "description": "The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies." + }, + "allowedHosts": { + "type": "array", + "description": "List of hosts that are allowed to access the dev server.", + "default": [], + "items": { "type": "string" } + }, + "servePath": { + "type": "string", + "description": "The pathname where the app will be served." + }, + "disableHostCheck": { + "type": "boolean", + "description": "Don't verify connected clients are part of allowed hosts.", + "default": false + }, + "hmr": { + "type": "boolean", + "description": "Enable hot module replacement.", + "default": false + }, + "watch": { + "type": "boolean", + "description": "Rebuild on change.", + "default": true + }, + "poll": { + "type": "number", + "description": "Enable and define the file watching poll time period in milliseconds." + } + }, + "additionalProperties": false, + "required": ["browserTarget"] + }, + "description": "The module-federation-dev-server executor is reserved exclusively for use with host Module Federation applications. It allows the user to specify which remote applications should be served with the host.", + "aliases": [], + "hidden": false, + "path": "/packages/angular/src/builders/module-federation-dev-server/schema.json" } ] } diff --git a/docs/packages.json b/docs/packages.json index 7f2192b1004e3..aeafe801a991d 100644 --- a/docs/packages.json +++ b/docs/packages.json @@ -13,7 +13,8 @@ "ng-packagr-lite", "package", "webpack-browser", - "webpack-server" + "webpack-server", + "module-federation-dev-server" ], "generators": [ "add-linting", diff --git a/packages/angular/executors.json b/packages/angular/executors.json index 91ffc6b6ee111..9b15b53d85555 100644 --- a/packages/angular/executors.json +++ b/packages/angular/executors.json @@ -24,6 +24,11 @@ "implementation": "./src/builders/webpack-server/webpack-server.impl", "schema": "./src/builders/webpack-server/schema.json", "description": "The `webpack-server` executor is very similar to the standard `dev-server` builder provided by the Angular Devkit. It is usually used in tandem with `@nrwl/angular:webpack-browser` when your Angular application uses a custom webpack configuration." + }, + "module-federation-dev-server": { + "implementation": "./src/builders/module-federation-dev-server/module-federation-dev-server.impl", + "schema": "./src/builders/module-federation-dev-server/schema.json", + "description": "The module-federation-dev-server executor is reserved exclusively for use with host Module Federation applications. It allows the user to specify which remote applications should be served with the host." } }, "builders": { @@ -51,6 +56,11 @@ "implementation": "./src/builders/webpack-server/webpack-server.impl", "schema": "./src/builders/webpack-server/schema.json", "description": "The `webpack-server` executor is very similar to the standard `dev-server` builder provided by the Angular Devkit. It is usually used in tandem with `@nrwl/angular:webpack-browser` when your Angular application uses a custom webpack configuration." + }, + "module-federation-dev-server": { + "implementation": "./src/builders/module-federation-dev-server/module-federation-dev-server.impl", + "schema": "./src/builders/module-federation-dev-server/schema.json", + "description": "The module-federation-dev-server executor is reserved exclusively for use with host Module Federation applications. It allows the user to specify which remote applications should be served with the host." } } } diff --git a/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts b/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts new file mode 100644 index 0000000000000..21e54d4ecda36 --- /dev/null +++ b/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts @@ -0,0 +1,50 @@ +import type { Schema } from './schema'; +import { Workspaces } from '@nrwl/devkit'; +import { scheduleTarget } from 'nx/src/adapter/ngcli-adapter'; +import { BuilderContext, createBuilder } from '@angular-devkit/architect'; +import { JsonObject } from '@angular-devkit/core'; +import { join } from 'path'; +import { webpackServer } from '../webpack-server/webpack-server.impl'; + +export function moduleFederationDevServer( + schema: Schema, + context: BuilderContext +) { + const workspaces = new Workspaces(context.workspaceRoot); + const workspaceConfig = workspaces.readWorkspaceConfiguration(); + + const p = workspaceConfig.projects[context.target.project]; + + const mfeConfigPath = join(context.workspaceRoot, p.root, 'mfe.config.js'); + + let mfeConfig: { remotes: string[] }; + try { + mfeConfig = require(mfeConfigPath); + } catch { + throw new Error( + `Could not load ${mfeConfigPath}. Was this project generated with "@nrwl/angular:host"?` + ); + } + + const { ...options } = schema; + const unparsedRemotes = mfeConfig.remotes.length > 0 ? mfeConfig.remotes : []; + const remotes = unparsedRemotes.map((a) => (Array.isArray(a) ? a[0] : a)); + + for (const remote of remotes) { + scheduleTarget( + context.workspaceRoot, + { + project: remote, + target: 'serve', + configuration: context.target.configuration, + runOptions: {}, + executor: context.builder.builderName, + }, + options.verbose + ); + } + + return webpackServer(options, context); +} + +export default createBuilder(moduleFederationDevServer); diff --git a/packages/angular/src/builders/module-federation-dev-server/schema.d.ts b/packages/angular/src/builders/module-federation-dev-server/schema.d.ts new file mode 100644 index 0000000000000..5ebdefd1fb25b --- /dev/null +++ b/packages/angular/src/builders/module-federation-dev-server/schema.d.ts @@ -0,0 +1,20 @@ +export interface Schema { + browserTarget: string; + port: number; + host: string; + proxyConfig?: string; + ssl: boolean; + sslKey?: string; + sslCert?: string; + headers?: Record; + open: boolean; + verbose?: boolean; + liveReload: boolean; + publicHost?: string; + allowedHosts?: string[]; + servePath?: string; + disableHostCheck?: boolean; + hmr?: boolean; + watch?: boolean; + poll?: number; +} diff --git a/packages/angular/src/builders/module-federation-dev-server/schema.json b/packages/angular/src/builders/module-federation-dev-server/schema.json new file mode 100644 index 0000000000000..eebe7b706ae30 --- /dev/null +++ b/packages/angular/src/builders/module-federation-dev-server/schema.json @@ -0,0 +1,108 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "title": "Schema for Module Federation Dev Server", + "description": "The module-federation-dev-server executor is reserved exclusively for use with host Module Federation applications. It allows the user to specify which remote applications should be served with the host.", + "type": "object", + "presets": [ + { + "name": "Using a Different Port", + "keys": ["browserTarget", "port"] + } + ], + "properties": { + "browserTarget": { + "type": "string", + "description": "A browser builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.", + "pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$" + }, + "port": { + "type": "number", + "description": "Port to listen on.", + "default": 4200 + }, + "host": { + "type": "string", + "description": "Host to listen on.", + "default": "localhost" + }, + "proxyConfig": { + "type": "string", + "description": "Proxy configuration file. For more information, see https://angular.io/guide/build#proxying-to-a-backend-server." + }, + "ssl": { + "type": "boolean", + "description": "Serve using HTTPS.", + "default": false + }, + "sslKey": { + "type": "string", + "description": "SSL key to use for serving HTTPS." + }, + "sslCert": { + "type": "string", + "description": "SSL certificate to use for serving HTTPS." + }, + "headers": { + "type": "object", + "description": "Custom HTTP headers to be added to all responses.", + "propertyNames": { + "pattern": "^[-_A-Za-z0-9]+$" + }, + "additionalProperties": { + "type": "string" + } + }, + "open": { + "type": "boolean", + "description": "Opens the url in default browser.", + "default": false, + "alias": "o" + }, + "verbose": { + "type": "boolean", + "description": "Adds more details to output logging." + }, + "liveReload": { + "type": "boolean", + "description": "Whether to reload the page on change, using live-reload.", + "default": true + }, + "publicHost": { + "type": "string", + "description": "The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies." + }, + "allowedHosts": { + "type": "array", + "description": "List of hosts that are allowed to access the dev server.", + "default": [], + "items": { + "type": "string" + } + }, + "servePath": { + "type": "string", + "description": "The pathname where the app will be served." + }, + "disableHostCheck": { + "type": "boolean", + "description": "Don't verify connected clients are part of allowed hosts.", + "default": false + }, + "hmr": { + "type": "boolean", + "description": "Enable hot module replacement.", + "default": false + }, + "watch": { + "type": "boolean", + "description": "Rebuild on change.", + "default": true + }, + "poll": { + "type": "number", + "description": "Enable and define the file watching poll time period in milliseconds." + } + }, + "additionalProperties": false, + "required": ["browserTarget"] +}