diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index 506282de..21764dc7 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -2,21 +2,10 @@ name: Bug Report
description: Report a bug or regression in functionality
body:
- - type: dropdown
- id: affected-packages
- attributes:
- label: With what library do you have an issue?
- options:
- - native-federation
- - module-federation
- validations:
- required: true
-
- type: textarea
id: reproduction
attributes:
label: Reproduction of the bug/regression with instructions
- description: If on Native Federation, use our [Stackblitz template](https://stackblitz.com/github/rainerhahnekamp/native-federation-stackblitz?file=projects%2Fhost%2Fsrc%2Fapp%2Fapp.routes.ts) to reproduce the issue
placeholder: If the bug/regression does not include a reproduction via StackBlitz or GitHub repo, your issue may be closed without resolution.
validations:
required: true
@@ -32,9 +21,9 @@ body:
- type: textarea
id: version
attributes:
- label: Versions of Native/Module Federation, Angular, Node, Browser, and operating system
+ label: Versions of Module Federation, Angular, Node, Browser, and operating system
placeholder: |
- Native/Module Federation:
+ Module Federation:
Angular:
Node:
Browser:
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
index fd08ab2e..add8adb0 100644
--- a/.github/ISSUE_TEMPLATE/feature-request.yml
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -7,7 +7,6 @@ body:
attributes:
label: For which library do you have a feature request?
options:
- - native-federation
- module-federation
- other
multiple: true
diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml
index fc9afc18..98f1e8e3 100644
--- a/.github/ISSUE_TEMPLATE/question.yml
+++ b/.github/ISSUE_TEMPLATE/question.yml
@@ -7,7 +7,6 @@ body:
attributes:
label: For which library do you need help?
options:
- - native-federation
- module-federation
- other
multiple: true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8cd66877..bb4a6106 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,17 +29,7 @@ jobs:
cache: 'npm'
- run: npm ci
- # Install Playwright browsers
- - name: Install Playwright Browsers
- run: npx playwright install --with-deps chromium
- timeout-minutes: 10
- env:
- DEBIAN_FRONTEND: noninteractive
- NEEDRESTART_MODE: a
- NEEDRESTART_SUSPEND: '1'
-
- uses: nrwl/nx-set-shas@v4
- run: npx nx format:check
- #TODO: remove exclude when apps are fixed
- - run: npx nx affected -t lint test build --exclude mfe1 mfe2 playground
+ - run: npx nx affected -t lint test build
diff --git a/README.md b/README.md
index a4da0ae4..8ecf8801 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,22 @@
# @angular-architects/module-federation
-- [Readme for Module Federation](./libs/mf/README.md)
-- [Readme for Native Federation](./libs/native-federation/README.md)
-
-> [!WARNING]
-> **Native federation has moved!**
+> [!IMPORTANT]
+> **Native Federation has moved to its own organization: [github.com/native-federation](https://github.com/native-federation).**
>
-> This is our older deprecated v3 version. Starting Angular 22, we're moving to a new repository. For upgrading from v3 to v4, please have a look at our [new repository](https://github.com/native-federation/angular-adapter).
-
-## Local Development
-
-
-Playground App
-
-You can test directly the libraries by using the playground application:
-
-1. Start the `host` application:
- ```shell
- npx nx run playground:serve
- ```
-2. Start the `remote` application:
- ```shell
- npx nx run mfe1:serve --port 3001
- ```
+> This repository now only hosts the Module Federation packages. If you are looking for Native
+> Federation, or want to upgrade from v3 to v4, head to
+> [native-federation/angular-adapter](https://github.com/native-federation/angular-adapter).
+>
+> The deprecated Native Federation v3 sources for Angular 21 remain available on the
+> [`21.x.x`](https://github.com/angular-architects/module-federation-plugin/tree/21.x.x) backport
+> branch.
-By using that approach you can test your modifications on the libraries.
+- [Readme for Module Federation](./libs/mf/README.md)
+- [Migration guides](./migration-guide.md)
+- [Webpack Module Federation demo](./apps/README.md) — a host and two remotes
+ running against the sources in `libs/`
-
+## Local Development
Test Library on external repository
@@ -39,24 +29,18 @@ If you want to test the modifications directly on your application, you can foll
```
2. Then you can publish the libraries by using:
-- For Module federation:
- ```shell
- npm run publish-local:mf
- ```
-- For Native federation:
-
- ```shell
- npm run publish-local:nf
- ```
+ ```shell
+ npm run publish-local
+ ```
- This will first `build` the libraries and `publish` them to [http://localhost:4873](http://localhost:4873)
+ This will first `build` the libraries and `publish` them to [http://localhost:4873](http://localhost:4873)
3. Then just re-run the `install` on the other repo with you favorite package manager.
By default, the version from the `package.json` will be used. However, you can provide the version for a specific library by using:
```shell
-npx nx run native-federation:publish-local -- --ver=17.0.8
+npx nx run mf:publish-local -- --ver=22.0.1
```
@@ -65,20 +49,16 @@ npx nx run native-federation:publish-local -- --ver=17.0.8
Follow these steps to publish all libraries on `npm`:
-- For Module federation:
- ```shell
- npm run publish:mf
- ```
-- For Native federation:
- ```shell
- npm run publish:nf
- ```
- This will first `build` the libraries and `publish` them to `npm registry`.
+```shell
+npm run publish
+```
+
+This will first `build` the libraries and `publish` them to `npm registry`.
-By default, the version from the `package.json` will be used and the tag will be `next`. However, you can provide the version and the tag for a specific library by using:
+By default, the version from the `package.json` will be used and the tag will be `latest`. However, you can provide the version and the tag for a specific library by using:
```shell
-npx nx run native-federation:publish -- --ver=17.0.8 --tag=latest
+npx nx run mf:publish -- --ver=22.0.1 --tag=latest
```
diff --git a/apps/README.md b/apps/README.md
new file mode 100644
index 00000000..b78bd6d2
--- /dev/null
+++ b/apps/README.md
@@ -0,0 +1,99 @@
+# Webpack Module Federation demo
+
+A dynamic host and two remotes, built against the sources in `libs/`. Useful as a
+manual smoke test for `@angular-architects/module-federation` and as a worked
+example of the config the `init-webpack` schematic generates.
+
+| Project | Port | Role | Exposes |
+| ------- | ---- | ------------ | ------------------------------------ |
+| `shell` | 4200 | Dynamic host | — |
+| `mfe1` | 4201 | Remote | `./routes` → `FLIGHT_ROUTES` |
+| `mfe2` | 4202 | Remote | `./Component` → `DashboardComponent` |
+
+`libs/playground-lib` is shared by all three as a singleton, via `sharedMappings`.
+
+## Run it
+
+```shell
+npx nx serve shell
+```
+
+`shell:serve` depends on both remotes' `serve` targets, so one command starts all
+three. Then open , type a name into the login box, and
+open _Flights_ and _Dashboard_: both are separate builds served from other ports,
+and both read the name back out of the same `AuthService` instance.
+
+Each app also runs on its own — and
+ are complete Angular apps.
+
+To build instead:
+
+```shell
+npx nx run-many -t build -p shell mfe1 mfe2
+```
+
+## What each piece demonstrates
+
+**Dynamic host.** `shell/webpack.config.js` declares no `remotes`. The shell
+learns about mfe1 and mfe2 at runtime from `shell/public/mf.manifest.json`, which
+`initFederation()` in `shell/src/main.ts` fetches before Angular boots. Remotes
+can move or be added without rebuilding the shell — edit the manifest in
+`dist/apps/shell/` and reload.
+
+**Async boundary.** Every app's `main.ts` only does `import('./bootstrap')`.
+Nothing may touch a shared library before the share scope is initialized, so all
+real work sits behind a dynamic import.
+
+**Two granularities of exposure.** mfe1 exposes routes and owns everything under
+`/flights`; mfe2 exposes a single component that the shell routes to itself. See
+`shell/src/app/app.routes.ts` for both call shapes of `loadRemoteModule`.
+
+**Sharing.** `shareAll({ singleton: true, strictVersion: true, requiredVersion:
+'auto' })` shares every runtime dependency in the root `package.json`.
+`requiredVersion: 'auto'` reads the version from that same `package.json`, so all
+three builds agree. In the network tab, `remoteEntry.js` comes from `:4201` and
+`:4202`, but `@angular/core` is downloaded once — from whichever build asks
+first.
+
+**Monorepo libraries.** `libs/playground-lib` has no version of its own, so
+`shareAll` cannot see it. `sharedMappings: ['@angular-architects/playground-lib']`
+resolves it through `tsconfig.base.json` `paths` and shares it with
+`requiredVersion: false`. That is what makes `AuthService` a true singleton across
+build boundaries.
+
+## Deviations from a normal app
+
+These exist only because the demo lives inside the plugin's own repository:
+
+- The `webpack.config.js` files require `../../dist/libs/mf/webpack` instead of
+ `@angular-architects/module-federation/webpack`, because the package is never
+ installed here. Both the `build` and `serve` targets `dependsOn` `mf:build` so
+ the output exists before webpack loads the config.
+- `shell/webpack.config.js` adds two `resolve.alias` entries for
+ `@angular-architects/module-federation` and `-runtime`. Angular's webpack build
+ ignores tsconfig `paths`, so the aliases mirror the two entries the shell
+ relies on. They point at `libs/*/src`, **not** `dist/` — building `mf` also
+ rebuilds `mf-runtime`, and ng-packagr recreates `dist/libs/mf-runtime`, which
+ would yank the module out from under a dev-server already watching it. Using
+ sources also means editing a library live-reloads the demo.
+- `shell/tsconfig.app.json` sets `strict: false`. `libs/mf-runtime` is itself
+ compiled with `strict: false` and violates `strictNullChecks` and
+ `noImplicitAny`, and a strict program cannot consume those sources. Only the
+ app build is relaxed; the specs still type-check strictly. `mfe1` and `mfe2`
+ don't import the runtime, so they stay fully strict.
+
+Everything else is what `ng g @angular-architects/module-federation:init-webpack`
+generates for an Nx workspace: the `@nx/angular:webpack-browser` and
+`@nx/angular:dev-server` builders, `commonChunk: false`,
+`extractLicenses: false`, and a CORS header on each remote's dev-server.
+
+## Known cosmetic error (dev builds only)
+
+Under `nx serve`, every page load logs `SyntaxError: Cannot use 'import.meta'
+outside a module`. `withModuleFederationPlugin` sets `experiments.outputModule`,
+so webpack emits ESM for _all_ entry points, but Angular's index-html generator
+injects the `styles` entry with `defer` rather than `type="module"`. The failing
+chunk holds only webpack's runtime boilerplate — global CSS is applied through
+the separate ``, and nothing else is
+affected. Production builds don't emit a `styles` script at all, so they are
+clean.
diff --git a/apps/mfe1-e2e/cypress.json b/apps/mfe1-e2e/cypress.json
deleted file mode 100644
index ec36d251..00000000
--- a/apps/mfe1-e2e/cypress.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "fileServerFolder": ".",
- "fixturesFolder": "./src/fixtures",
- "integrationFolder": "./src/integration",
- "modifyObstructiveCode": false,
- "supportFile": "./src/support/index.ts",
- "pluginsFile": "./src/plugins/index",
- "video": true,
- "videosFolder": "../../dist/cypress/apps/mfe1-e2e/videos",
- "screenshotsFolder": "../../dist/cypress/apps/mfe1-e2e/screenshots",
- "chromeWebSecurity": false
-}
diff --git a/apps/mfe1-e2e/eslint.config.mjs b/apps/mfe1-e2e/eslint.config.mjs
deleted file mode 100644
index c95ed601..00000000
--- a/apps/mfe1-e2e/eslint.config.mjs
+++ /dev/null
@@ -1,17 +0,0 @@
-import baseConfig from '../../eslint.config.mjs';
-
-export default [
- ...baseConfig,
- {
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
- // Override or add rules here
- rules: {},
- },
- {
- files: ['src/plugins/index.js'],
- rules: {
- '@typescript-eslint/no-var-requires': 'off',
- 'no-undef': 'off',
- },
- },
-];
diff --git a/apps/mfe1-e2e/project.json b/apps/mfe1-e2e/project.json
deleted file mode 100644
index a5401053..00000000
--- a/apps/mfe1-e2e/project.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "name": "mfe1-e2e",
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "apps/mfe1-e2e/src",
- "projectType": "application",
- "tags": [],
- "implicitDependencies": ["mfe1"],
- "targets": {
- "e2e": {
- "executor": "@nx/cypress:cypress",
- "options": {
- "cypressConfig": "apps/mfe1-e2e/cypress.json",
- "devServerTarget": "mfe1:serve:development",
- "testingType": "e2e"
- },
- "configurations": {
- "production": {
- "devServerTarget": "mfe1:serve:production"
- }
- }
- },
- "lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"],
- "options": {
- "lintFilePatterns": ["apps/mfe1-e2e/**/*.{js,ts}"]
- }
- }
- }
-}
diff --git a/apps/mfe1-e2e/src/fixtures/example.json b/apps/mfe1-e2e/src/fixtures/example.json
deleted file mode 100644
index 294cbed6..00000000
--- a/apps/mfe1-e2e/src/fixtures/example.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io"
-}
diff --git a/apps/mfe1-e2e/src/integration/app.spec.ts b/apps/mfe1-e2e/src/integration/app.spec.ts
deleted file mode 100644
index fbd14627..00000000
--- a/apps/mfe1-e2e/src/integration/app.spec.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { getGreeting } from '../support/app.po';
-
-describe('mfe1', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should display welcome message', () => {
- // Custom command example, see `../support/commands.ts` file
- cy.login('my-email@something.com', 'myPassword');
-
- // Function helper example, see `../support/app.po.ts` file
- getGreeting().contains('Welcome mfe1');
- });
-});
diff --git a/apps/mfe1-e2e/src/plugins/index.js b/apps/mfe1-e2e/src/plugins/index.js
deleted file mode 100644
index 63aa33cb..00000000
--- a/apps/mfe1-e2e/src/plugins/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-const { preprocessTypescript } = require('@nx/cypress/plugins/preprocessor');
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-
- // Preprocess Typescript file using Nx helper
- on('file:preprocessor', preprocessTypescript(config));
-};
diff --git a/apps/mfe1-e2e/src/support/app.po.ts b/apps/mfe1-e2e/src/support/app.po.ts
deleted file mode 100644
index 32934246..00000000
--- a/apps/mfe1-e2e/src/support/app.po.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const getGreeting = () => cy.get('h1');
diff --git a/apps/mfe1-e2e/src/support/commands.ts b/apps/mfe1-e2e/src/support/commands.ts
deleted file mode 100644
index 310f1fa0..00000000
--- a/apps/mfe1-e2e/src/support/commands.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// ***********************************************
-// This example commands.js shows you how to
-// create various custom commands and overwrite
-// existing commands.
-//
-// For more comprehensive examples of custom
-// commands please read more here:
-// https://on.cypress.io/custom-commands
-// ***********************************************
-
-// eslint-disable-next-line @typescript-eslint/no-namespace
-declare namespace Cypress {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- interface Chainable {
- login(email: string, password: string): void;
- }
-}
-//
-// -- This is a parent command --
-Cypress.Commands.add('login', (email, password) => {
- console.log('Custom command example: Login', email, password);
-});
-//
-// -- This is a child command --
-// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
-//
-//
-// -- This is a dual command --
-// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
-//
-//
-// -- This will overwrite an existing command --
-// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
diff --git a/apps/mfe1-e2e/src/support/index.ts b/apps/mfe1-e2e/src/support/index.ts
deleted file mode 100644
index 459189af..00000000
--- a/apps/mfe1-e2e/src/support/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example support/index.ts is processed and
-// loaded automatically before your test files.
-//
-// This is a great place to put global configuration and
-// behavior that modifies Cypress.
-//
-// You can change the location of this file or turn off
-// automatically serving support files with the
-// 'supportFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/configuration
-// ***********************************************************
-
-// Import commands.ts using ES2015 syntax:
-import './commands';
diff --git a/apps/mfe1-e2e/tsconfig.json b/apps/mfe1-e2e/tsconfig.json
deleted file mode 100644
index e326af64..00000000
--- a/apps/mfe1-e2e/tsconfig.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "sourceMap": false,
- "outDir": "../../dist/out-tsc",
- "allowJs": true,
- "types": ["cypress", "node"],
- "forceConsistentCasingInFileNames": true,
- "strict": true,
- "noImplicitOverride": true,
- "noPropertyAccessFromIndexSignature": true,
- "noImplicitReturns": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src/**/*.ts", "src/**/*.js"],
- "angularCompilerOptions": {
- "enableI18nLegacyMessageIdFormat": false,
- "strictInjectionParameters": true,
- "strictInputAccessModifiers": true,
- "strictTemplates": true
- }
-}
diff --git a/apps/mfe1/eslint.config.mjs b/apps/mfe1/eslint.config.mjs
index 66525c81..6aaf44f9 100644
--- a/apps/mfe1/eslint.config.mjs
+++ b/apps/mfe1/eslint.config.mjs
@@ -1,5 +1,3 @@
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
import baseConfig from '../../eslint.config.mjs';
import nx from '@nx/eslint-plugin';
diff --git a/apps/mfe1/federation.config.js b/apps/mfe1/federation.config.js
deleted file mode 100644
index a41439e7..00000000
--- a/apps/mfe1/federation.config.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const {
- withNativeFederation,
- shareAll,
-} = require('@angular-architects/native-federation/config');
-
-module.exports = withNativeFederation({
- name: 'mfe1',
-
- exposes: {
- './cmp': 'apps/mfe1/src/app/demo/demo.component.ts',
- },
-
- shared: {
- ...shareAll({
- singleton: true,
- strictVersion: true,
- requiredVersion: 'auto',
- }),
- },
-
- sharedMappings: ['@angular-architects/playground-lib'],
-});
diff --git a/apps/mfe1/jest.config.ts b/apps/mfe1/jest.config.ts
index c9c97684..f6f7c8bb 100644
--- a/apps/mfe1/jest.config.ts
+++ b/apps/mfe1/jest.config.ts
@@ -1,4 +1,3 @@
-/* eslint-disable */
export default {
displayName: 'mfe1',
preset: '../../jest.preset.js',
diff --git a/apps/mfe1/project.json b/apps/mfe1/project.json
index 930d54f0..d857e059 100644
--- a/apps/mfe1/project.json
+++ b/apps/mfe1/project.json
@@ -4,29 +4,43 @@
"projectType": "application",
"sourceRoot": "apps/mfe1/src",
"prefix": "angular-architects",
- "tags": [],
+ "tags": ["scope:demo"],
"targets": {
"build": {
- "executor": "@angular-architects/native-federation:build",
+ "executor": "@nx/angular:webpack-browser",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
+ "dependsOn": [
+ {
+ "projects": ["mf"],
+ "target": "build"
+ }
+ ],
"options": {
+ "customWebpackConfig": {
+ "path": "apps/mfe1/webpack.config.js"
+ },
"outputPath": "dist/apps/mfe1",
"index": "apps/mfe1/src/index.html",
"main": "apps/mfe1/src/main.ts",
"polyfills": "apps/mfe1/src/polyfills.ts",
"tsConfig": "apps/mfe1/tsconfig.app.json",
- "assets": ["apps/mfe1/src/favicon.ico", "apps/mfe1/src/assets"],
+ "assets": [],
"styles": ["apps/mfe1/src/styles.css"],
- "scripts": []
+ "scripts": [],
+ "commonChunk": false,
+ "extractLicenses": false
},
"configurations": {
"production": {
+ "customWebpackConfig": {
+ "path": "apps/mfe1/webpack.prod.config.js"
+ },
"budgets": [
{
"type": "initial",
- "maximumWarning": "500kb",
- "maximumError": "1mb"
+ "maximumWarning": "1mb",
+ "maximumError": "2mb"
},
{
"type": "anyComponentStyle",
@@ -34,28 +48,26 @@
"maximumError": "4kb"
}
],
- "fileReplacements": [
- {
- "replace": "apps/mfe1/src/environments/environment.ts",
- "with": "apps/mfe1/src/environments/environment.prod.ts"
- }
- ],
"outputHashing": "all"
},
"development": {
- "buildOptimizer": false,
"optimization": false,
- "vendorChunk": true,
- "extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
}
},
"serve": {
- "executor": "@angular-devkit/build-angular:dev-server",
+ "executor": "@nx/angular:dev-server",
"defaultConfiguration": "development",
"continuous": true,
+ "options": {
+ "port": 4201,
+ "publicHost": "http://localhost:4201",
+ "headers": {
+ "Access-Control-Allow-Origin": "*"
+ }
+ },
"configurations": {
"production": {
"buildTarget": "mfe1:build:production"
@@ -63,13 +75,13 @@
"development": {
"buildTarget": "mfe1:build:development"
}
- }
- },
- "extract-i18n": {
- "executor": "@angular-devkit/build-angular:extract-i18n",
- "options": {
- "buildTarget": "mfe1:build"
- }
+ },
+ "dependsOn": [
+ {
+ "projects": ["mf"],
+ "target": "build"
+ }
+ ]
},
"lint": {
"executor": "@nx/eslint:lint",
diff --git a/apps/mfe1/src/app/app.component.css b/apps/mfe1/src/app/app.component.css
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/mfe1/src/app/app.component.html b/apps/mfe1/src/app/app.component.html
deleted file mode 100644
index e4516dc9..00000000
--- a/apps/mfe1/src/app/app.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/mfe1/src/app/app.component.spec.ts b/apps/mfe1/src/app/app.component.spec.ts
deleted file mode 100644
index 7534e3f8..00000000
--- a/apps/mfe1/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-import { NxWelcomeComponent } from './nx-welcome.component';
-
-describe('AppComponent', () => {
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [AppComponent, NxWelcomeComponent],
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app).toBeTruthy();
- });
-
- it(`should have as title 'mfe1'`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app.title).toEqual('mfe1');
- });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.nativeElement as HTMLElement;
- expect(compiled.querySelector('h1')?.textContent).toContain('Welcome mfe1');
- });
-});
diff --git a/apps/mfe1/src/app/app.component.ts b/apps/mfe1/src/app/app.component.ts
index 2ebd64a2..cedb9be8 100644
--- a/apps/mfe1/src/app/app.component.ts
+++ b/apps/mfe1/src/app/app.component.ts
@@ -1,11 +1,16 @@
import { Component } from '@angular/core';
+import { RouterLink, RouterOutlet } from '@angular/router';
@Component({
selector: 'angular-architects-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css'],
- standalone: false,
+ imports: [RouterOutlet, RouterLink],
+ template: `
+
mfe1 — standalone
+
+
+ `,
})
-export class AppComponent {
- title = 'mfe1';
-}
+export class AppComponent {}
diff --git a/apps/mfe1/src/app/app.module.ts b/apps/mfe1/src/app/app.module.ts
deleted file mode 100644
index 7f240f85..00000000
--- a/apps/mfe1/src/app/app.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { NgModule } from '@angular/core';
-import { BrowserModule } from '@angular/platform-browser';
-
-import { AppComponent } from './app.component';
-import { DemoComponent } from './demo/demo.component';
-import { NxWelcomeComponent } from './nx-welcome.component';
-
-@NgModule({
- declarations: [AppComponent, NxWelcomeComponent],
- imports: [BrowserModule, DemoComponent],
- providers: [],
- bootstrap: [AppComponent],
-})
-export class AppModule {}
diff --git a/apps/mfe1/src/app/app.routes.ts b/apps/mfe1/src/app/app.routes.ts
new file mode 100644
index 00000000..f88e1283
--- /dev/null
+++ b/apps/mfe1/src/app/app.routes.ts
@@ -0,0 +1,17 @@
+import { Routes } from '@angular/router';
+
+// The shell loads FLIGHT_ROUTES over Module Federation; running mfe1 on its own
+// mounts exactly the same routes under /flights.
+export const APP_ROUTES: Routes = [
+ {
+ path: '',
+ pathMatch: 'full',
+ loadComponent: () =>
+ import('./home/home.component').then((m) => m.HomeComponent),
+ },
+ {
+ path: 'flights',
+ loadChildren: () =>
+ import('./flights/flights.routes').then((m) => m.FLIGHT_ROUTES),
+ },
+];
diff --git a/apps/mfe1/src/app/demo/demo.component.css b/apps/mfe1/src/app/demo/demo.component.css
deleted file mode 100644
index 87002430..00000000
--- a/apps/mfe1/src/app/demo/demo.component.css
+++ /dev/null
@@ -1,3 +0,0 @@
-p {
- color: green;
-}
diff --git a/apps/mfe1/src/app/demo/demo.component.html b/apps/mfe1/src/app/demo/demo.component.html
deleted file mode 100644
index c6fb966c..00000000
--- a/apps/mfe1/src/app/demo/demo.component.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
demo works!
-
-@if (title) {
-
{{ title }}
-}
-
-
-
-
diff --git a/apps/mfe1/src/app/demo/demo.component.spec.ts b/apps/mfe1/src/app/demo/demo.component.spec.ts
deleted file mode 100644
index e5668908..00000000
--- a/apps/mfe1/src/app/demo/demo.component.spec.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { DemoComponent } from './demo.component';
-
-describe('DemoComponent', () => {
- let component: DemoComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [DemoComponent],
- }).compileComponents();
-
- fixture = TestBed.createComponent(DemoComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/apps/mfe1/src/app/demo/demo.component.ts b/apps/mfe1/src/app/demo/demo.component.ts
deleted file mode 100644
index d7271bbd..00000000
--- a/apps/mfe1/src/app/demo/demo.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import {
- AuthService,
- PlaygroundLibModule,
-} from '@angular-architects/playground-lib';
-import { CommonModule } from '@angular/common';
-import { Component, inject } from '@angular/core';
-
-@Component({
- standalone: true,
- selector: 'angular-architects-demo',
- templateUrl: './demo.component.html',
- styleUrls: ['./demo.component.css'],
- imports: [CommonModule, PlaygroundLibModule],
-})
-export class DemoComponent {
- title = 'Hallo';
-
- constructor() {
- console.log('userName', inject(AuthService).userName);
- }
-}
diff --git a/apps/mfe1/src/app/flights/flight-search.component.spec.ts b/apps/mfe1/src/app/flights/flight-search.component.spec.ts
new file mode 100644
index 00000000..c176e36a
--- /dev/null
+++ b/apps/mfe1/src/app/flights/flight-search.component.spec.ts
@@ -0,0 +1,18 @@
+import { TestBed } from '@angular/core/testing';
+import { AuthService } from '@angular-architects/playground-lib';
+
+import { FlightSearchComponent } from './flight-search.component';
+
+describe('FlightSearchComponent', () => {
+ it('reads the user name from the shared AuthService', async () => {
+ // Mirrors what the shell does before the remote is loaded: the singleton
+ // already carries a user name by the time the exposed route renders.
+ TestBed.inject(AuthService).userName = 'Jane';
+
+ const fixture = TestBed.createComponent(FlightSearchComponent);
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.userName).toBe('Jane');
+ expect(fixture.nativeElement.textContent).toContain('Jane');
+ });
+});
diff --git a/apps/mfe1/src/app/flights/flight-search.component.ts b/apps/mfe1/src/app/flights/flight-search.component.ts
new file mode 100644
index 00000000..a013d96b
--- /dev/null
+++ b/apps/mfe1/src/app/flights/flight-search.component.ts
@@ -0,0 +1,39 @@
+import { Component, inject } from '@angular/core';
+import {
+ AuthService,
+ PlaygroundLibModule,
+} from '@angular-architects/playground-lib';
+
+@Component({
+ selector: 'angular-architects-flight-search',
+ imports: [PlaygroundLibModule],
+ template: `
+
Flight Search
+
+
+ AuthService.userName as seen from mfe1:
+ {{ userName || '(empty)' }}
+
# Generate UI lib
-nx g @nx/angular:lib ui
-
-# Add a component
-nx g @nx/angular:component button --project ui
-
-
-
-
- View interactive project graph
-
-
nx graph
-
-
-
-
- Run affected commands
-
-
# see what's been affected by changes
-nx affected:graph
-
-# run tests for current changes
-nx affected:test
-
-# run e2e tests for current changes
-nx affected:e2e
-
-
-
-
- Carefully crafted with
-
-
-
-
- `,
- styles: [],
- encapsulation: ViewEncapsulation.None,
- standalone: false,
-})
-export class NxWelcomeComponent implements OnInit {
- constructor() {}
-
- ngOnInit(): void {}
-}
diff --git a/apps/mfe1/src/assets/.gitkeep b/apps/mfe1/src/assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/mfe1/src/bootstrap.ts b/apps/mfe1/src/bootstrap.ts
index d9a2e7e4..0791995c 100644
--- a/apps/mfe1/src/bootstrap.ts
+++ b/apps/mfe1/src/bootstrap.ts
@@ -1,13 +1,9 @@
-import { enableProdMode } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { bootstrapApplication } from '@angular/platform-browser';
+import { provideRouter } from '@angular/router';
-import { AppModule } from './app/app.module';
-import { environment } from './environments/environment';
+import { AppComponent } from './app/app.component';
+import { APP_ROUTES } from './app/app.routes';
-if (environment.production) {
- enableProdMode();
-}
-
-platformBrowserDynamic()
- .bootstrapModule(AppModule)
- .catch((err) => console.error(err));
+bootstrapApplication(AppComponent, {
+ providers: [provideRouter(APP_ROUTES)],
+}).catch((err) => console.error(err));
diff --git a/apps/mfe1/src/environments/environment.prod.ts b/apps/mfe1/src/environments/environment.prod.ts
deleted file mode 100644
index c9669790..00000000
--- a/apps/mfe1/src/environments/environment.prod.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const environment = {
- production: true,
-};
diff --git a/apps/mfe1/src/environments/environment.ts b/apps/mfe1/src/environments/environment.ts
deleted file mode 100644
index 66998ae9..00000000
--- a/apps/mfe1/src/environments/environment.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// This file can be replaced during build by using the `fileReplacements` array.
-// `ng build` replaces `environment.ts` with `environment.prod.ts`.
-// The list of file replacements can be found in `angular.json`.
-
-export const environment = {
- production: false,
-};
-
-/*
- * For easier debugging in development mode, you can import the following file
- * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
- *
- * This import should be commented out in production mode because it will have a negative impact
- * on performance if an error is thrown.
- */
-// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
diff --git a/apps/mfe1/src/favicon.ico b/apps/mfe1/src/favicon.ico
deleted file mode 100644
index 317ebcb2..00000000
Binary files a/apps/mfe1/src/favicon.ico and /dev/null differ
diff --git a/apps/mfe1/src/index.html b/apps/mfe1/src/index.html
index 25c192af..8ff2e44f 100644
--- a/apps/mfe1/src/index.html
+++ b/apps/mfe1/src/index.html
@@ -2,10 +2,9 @@
- Mfe1
+ mfe1 (remote)
-
diff --git a/apps/mfe1/src/main.ts b/apps/mfe1/src/main.ts
index 4d31b464..d7d2e9ae 100644
--- a/apps/mfe1/src/main.ts
+++ b/apps/mfe1/src/main.ts
@@ -1,5 +1,3 @@
-import { initFederation } from '@angular-architects/native-federation';
-
-initFederation()
- .then(() => import('./bootstrap'))
- .catch((err) => console.error(err));
+// A remote must not touch shared libraries before the share scope is
+// initialized, so all real work happens behind this dynamic import.
+import('./bootstrap').catch((err) => console.error(err));
diff --git a/apps/mfe1/src/polyfills.ts b/apps/mfe1/src/polyfills.ts
index 1685a007..aa09a9ff 100644
--- a/apps/mfe1/src/polyfills.ts
+++ b/apps/mfe1/src/polyfills.ts
@@ -1,53 +1 @@
-/**
- * This file includes polyfills needed by Angular and is loaded before the app.
- * You can add your own extra polyfills to this file.
- *
- * This file is divided into 2 sections:
- * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
- * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
- * file.
- *
- * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
- * automatically update themselves. This includes recent versions of Safari, Chrome (including
- * Opera), Edge on the desktop, and iOS and Chrome on mobile.
- *
- * Learn more in https://angular.io/guide/browser-support
- */
-
-/***************************************************************************************************
- * BROWSER POLYFILLS
- */
-
-/**
- * By default, zone.js will patch all possible macroTask and DomEvents
- * user can disable parts of macroTask/DomEvents patch by setting following flags
- * because those flags need to be set before `zone.js` being loaded, and webpack
- * will put import in the top of bundle, so user need to create a separate file
- * in this directory (for example: zone-flags.ts), and put the following flags
- * into that file, and then add the following code before importing zone.js.
- * import './zone-flags';
- *
- * The flags allowed in zone-flags.ts are listed here.
- *
- * The following flags will work for all browsers.
- *
- * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
- * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
- * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
- *
- * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
- * with the following flag, it will bypass `zone.js` patch for IE/Edge
- *
- * (window as any).__Zone_enable_cross_context_check = true;
- *
- */
-
-/***************************************************************************************************
- * Zone JS is required by default for Angular itself.
- */
-import 'zone.js'; // Included with Angular CLI.
-
-/***************************************************************************************************
- * APPLICATION IMPORTS
- */
-import 'es-module-shims';
+import 'zone.js';
diff --git a/apps/mfe1/src/styles.css b/apps/mfe1/src/styles.css
index 90d4ee00..8c780472 100644
--- a/apps/mfe1/src/styles.css
+++ b/apps/mfe1/src/styles.css
@@ -1 +1,8 @@
-/* You can add global styles to this file, and also import other style files */
+body {
+ font-family: system-ui, sans-serif;
+ margin: 1rem;
+}
+
+nav a {
+ margin-right: 1rem;
+}
diff --git a/apps/mfe1/webpack.config.js b/apps/mfe1/webpack.config.js
new file mode 100644
index 00000000..26034196
--- /dev/null
+++ b/apps/mfe1/webpack.config.js
@@ -0,0 +1,25 @@
+// In a normal app this is `require('@angular-architects/module-federation/webpack')`.
+// Inside this repo the package isn't installed, so we point at the built output —
+// `nx build mfe1` depends on `mf:build` to make sure it's there.
+const {
+ shareAll,
+ withModuleFederationPlugin,
+} = require('../../dist/libs/mf/webpack');
+
+module.exports = withModuleFederationPlugin({
+ name: 'mfe1',
+
+ exposes: {
+ './routes': './apps/mfe1/src/app/flights/flights.routes.ts',
+ },
+
+ shared: {
+ ...shareAll({
+ singleton: true,
+ strictVersion: true,
+ requiredVersion: 'auto',
+ }),
+ },
+
+ sharedMappings: ['@angular-architects/playground-lib'],
+});
diff --git a/apps/mfe1/webpack.prod.config.js b/apps/mfe1/webpack.prod.config.js
new file mode 100644
index 00000000..0c9447df
--- /dev/null
+++ b/apps/mfe1/webpack.prod.config.js
@@ -0,0 +1 @@
+module.exports = require('./webpack.config');
diff --git a/apps/mfe2-e2e/cypress.json b/apps/mfe2-e2e/cypress.json
deleted file mode 100644
index a56279f3..00000000
--- a/apps/mfe2-e2e/cypress.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "fileServerFolder": ".",
- "fixturesFolder": "./src/fixtures",
- "integrationFolder": "./src/integration",
- "modifyObstructiveCode": false,
- "supportFile": "./src/support/index.ts",
- "pluginsFile": "./src/plugins/index",
- "video": true,
- "videosFolder": "../../dist/cypress/apps/mfe2-e2e/videos",
- "screenshotsFolder": "../../dist/cypress/apps/mfe2-e2e/screenshots",
- "chromeWebSecurity": false
-}
diff --git a/apps/mfe2-e2e/eslint.config.mjs b/apps/mfe2-e2e/eslint.config.mjs
deleted file mode 100644
index c95ed601..00000000
--- a/apps/mfe2-e2e/eslint.config.mjs
+++ /dev/null
@@ -1,17 +0,0 @@
-import baseConfig from '../../eslint.config.mjs';
-
-export default [
- ...baseConfig,
- {
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
- // Override or add rules here
- rules: {},
- },
- {
- files: ['src/plugins/index.js'],
- rules: {
- '@typescript-eslint/no-var-requires': 'off',
- 'no-undef': 'off',
- },
- },
-];
diff --git a/apps/mfe2-e2e/project.json b/apps/mfe2-e2e/project.json
deleted file mode 100644
index 237a644e..00000000
--- a/apps/mfe2-e2e/project.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "name": "mfe2-e2e",
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "apps/mfe2-e2e/src",
- "projectType": "application",
- "tags": [],
- "implicitDependencies": ["mfe2"],
- "targets": {
- "e2e": {
- "executor": "@nx/cypress:cypress",
- "options": {
- "cypressConfig": "apps/mfe2-e2e/cypress.json",
- "devServerTarget": "mfe2:serve:development",
- "testingType": "e2e"
- },
- "configurations": {
- "production": {
- "devServerTarget": "mfe2:serve:production"
- }
- }
- },
- "lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"],
- "options": {
- "lintFilePatterns": ["apps/mfe2-e2e/**/*.{js,ts}"]
- }
- }
- }
-}
diff --git a/apps/mfe2-e2e/src/fixtures/example.json b/apps/mfe2-e2e/src/fixtures/example.json
deleted file mode 100644
index 294cbed6..00000000
--- a/apps/mfe2-e2e/src/fixtures/example.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io"
-}
diff --git a/apps/mfe2-e2e/src/integration/app.spec.ts b/apps/mfe2-e2e/src/integration/app.spec.ts
deleted file mode 100644
index 24b8a158..00000000
--- a/apps/mfe2-e2e/src/integration/app.spec.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { getGreeting } from '../support/app.po';
-
-describe('mfe2', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should display welcome message', () => {
- // Custom command example, see `../support/commands.ts` file
- cy.login('my-email@something.com', 'myPassword');
-
- // Function helper example, see `../support/app.po.ts` file
- getGreeting().contains('Welcome mfe2');
- });
-});
diff --git a/apps/mfe2-e2e/src/plugins/index.js b/apps/mfe2-e2e/src/plugins/index.js
deleted file mode 100644
index 63aa33cb..00000000
--- a/apps/mfe2-e2e/src/plugins/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-const { preprocessTypescript } = require('@nx/cypress/plugins/preprocessor');
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-
- // Preprocess Typescript file using Nx helper
- on('file:preprocessor', preprocessTypescript(config));
-};
diff --git a/apps/mfe2-e2e/src/support/app.po.ts b/apps/mfe2-e2e/src/support/app.po.ts
deleted file mode 100644
index 32934246..00000000
--- a/apps/mfe2-e2e/src/support/app.po.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const getGreeting = () => cy.get('h1');
diff --git a/apps/mfe2-e2e/src/support/commands.ts b/apps/mfe2-e2e/src/support/commands.ts
deleted file mode 100644
index 310f1fa0..00000000
--- a/apps/mfe2-e2e/src/support/commands.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// ***********************************************
-// This example commands.js shows you how to
-// create various custom commands and overwrite
-// existing commands.
-//
-// For more comprehensive examples of custom
-// commands please read more here:
-// https://on.cypress.io/custom-commands
-// ***********************************************
-
-// eslint-disable-next-line @typescript-eslint/no-namespace
-declare namespace Cypress {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- interface Chainable {
- login(email: string, password: string): void;
- }
-}
-//
-// -- This is a parent command --
-Cypress.Commands.add('login', (email, password) => {
- console.log('Custom command example: Login', email, password);
-});
-//
-// -- This is a child command --
-// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
-//
-//
-// -- This is a dual command --
-// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
-//
-//
-// -- This will overwrite an existing command --
-// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
diff --git a/apps/mfe2-e2e/src/support/index.ts b/apps/mfe2-e2e/src/support/index.ts
deleted file mode 100644
index 459189af..00000000
--- a/apps/mfe2-e2e/src/support/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example support/index.ts is processed and
-// loaded automatically before your test files.
-//
-// This is a great place to put global configuration and
-// behavior that modifies Cypress.
-//
-// You can change the location of this file or turn off
-// automatically serving support files with the
-// 'supportFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/configuration
-// ***********************************************************
-
-// Import commands.ts using ES2015 syntax:
-import './commands';
diff --git a/apps/mfe2-e2e/tsconfig.json b/apps/mfe2-e2e/tsconfig.json
deleted file mode 100644
index e326af64..00000000
--- a/apps/mfe2-e2e/tsconfig.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "sourceMap": false,
- "outDir": "../../dist/out-tsc",
- "allowJs": true,
- "types": ["cypress", "node"],
- "forceConsistentCasingInFileNames": true,
- "strict": true,
- "noImplicitOverride": true,
- "noPropertyAccessFromIndexSignature": true,
- "noImplicitReturns": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src/**/*.ts", "src/**/*.js"],
- "angularCompilerOptions": {
- "enableI18nLegacyMessageIdFormat": false,
- "strictInjectionParameters": true,
- "strictInputAccessModifiers": true,
- "strictTemplates": true
- }
-}
diff --git a/apps/mfe2/eslint.config.mjs b/apps/mfe2/eslint.config.mjs
index 66525c81..6aaf44f9 100644
--- a/apps/mfe2/eslint.config.mjs
+++ b/apps/mfe2/eslint.config.mjs
@@ -1,5 +1,3 @@
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
import baseConfig from '../../eslint.config.mjs';
import nx from '@nx/eslint-plugin';
diff --git a/apps/mfe2/federation.config.js b/apps/mfe2/federation.config.js
deleted file mode 100644
index 84afdfe0..00000000
--- a/apps/mfe2/federation.config.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const {
- withNativeFederation,
- shareAll,
-} = require('@angular-architects/native-federation/config');
-
-module.exports = withNativeFederation({
- name: 'mfe2',
- shared: {
- ...shareAll({
- singleton: true,
- strictVersion: true,
- requiredVersion: 'auto',
- }),
- },
-});
diff --git a/apps/mfe2/jest.config.ts b/apps/mfe2/jest.config.ts
index 21106286..2d9d75c0 100644
--- a/apps/mfe2/jest.config.ts
+++ b/apps/mfe2/jest.config.ts
@@ -1,4 +1,3 @@
-/* eslint-disable */
export default {
displayName: 'mfe2',
preset: '../../jest.preset.js',
diff --git a/apps/mfe2/project.json b/apps/mfe2/project.json
index fc9fc1ee..f49855ff 100644
--- a/apps/mfe2/project.json
+++ b/apps/mfe2/project.json
@@ -4,29 +4,43 @@
"projectType": "application",
"sourceRoot": "apps/mfe2/src",
"prefix": "angular-architects",
- "tags": [],
+ "tags": ["scope:demo"],
"targets": {
"build": {
- "executor": "@angular-architects/native-federation:build",
+ "executor": "@nx/angular:webpack-browser",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
+ "dependsOn": [
+ {
+ "projects": ["mf"],
+ "target": "build"
+ }
+ ],
"options": {
+ "customWebpackConfig": {
+ "path": "apps/mfe2/webpack.config.js"
+ },
"outputPath": "dist/apps/mfe2",
"index": "apps/mfe2/src/index.html",
"main": "apps/mfe2/src/main.ts",
"polyfills": "apps/mfe2/src/polyfills.ts",
"tsConfig": "apps/mfe2/tsconfig.app.json",
- "assets": ["apps/mfe2/src/favicon.ico", "apps/mfe2/src/assets"],
+ "assets": [],
"styles": ["apps/mfe2/src/styles.css"],
- "scripts": []
+ "scripts": [],
+ "commonChunk": false,
+ "extractLicenses": false
},
"configurations": {
"production": {
+ "customWebpackConfig": {
+ "path": "apps/mfe2/webpack.prod.config.js"
+ },
"budgets": [
{
"type": "initial",
- "maximumWarning": "500kb",
- "maximumError": "1mb"
+ "maximumWarning": "1mb",
+ "maximumError": "2mb"
},
{
"type": "anyComponentStyle",
@@ -34,29 +48,26 @@
"maximumError": "4kb"
}
],
- "fileReplacements": [
- {
- "replace": "apps/mfe2/src/environments/environment.ts",
- "with": "apps/mfe2/src/environments/environment.prod.ts"
- }
- ],
"outputHashing": "all"
},
"development": {
- "buildOptimizer": false,
"optimization": false,
- "vendorChunk": true,
- "extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
}
},
"serve": {
- "executor": "@angular-devkit/build-angular:dev-server",
+ "executor": "@nx/angular:dev-server",
"defaultConfiguration": "development",
"continuous": true,
- "options": {},
+ "options": {
+ "port": 4202,
+ "publicHost": "http://localhost:4202",
+ "headers": {
+ "Access-Control-Allow-Origin": "*"
+ }
+ },
"configurations": {
"production": {
"buildTarget": "mfe2:build:production"
@@ -64,13 +75,13 @@
"development": {
"buildTarget": "mfe2:build:development"
}
- }
- },
- "extract-i18n": {
- "executor": "@angular-devkit/build-angular:extract-i18n",
- "options": {
- "buildTarget": "mfe2:build"
- }
+ },
+ "dependsOn": [
+ {
+ "projects": ["mf"],
+ "target": "build"
+ }
+ ]
},
"lint": {
"executor": "@nx/eslint:lint",
diff --git a/apps/mfe2/src/app/app.component.css b/apps/mfe2/src/app/app.component.css
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/mfe2/src/app/app.component.html b/apps/mfe2/src/app/app.component.html
deleted file mode 100644
index e4516dc9..00000000
--- a/apps/mfe2/src/app/app.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/mfe2/src/app/app.component.spec.ts b/apps/mfe2/src/app/app.component.spec.ts
deleted file mode 100644
index 339ae4d1..00000000
--- a/apps/mfe2/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-import { NxWelcomeComponent } from './nx-welcome.component';
-
-describe('AppComponent', () => {
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [AppComponent, NxWelcomeComponent],
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app).toBeTruthy();
- });
-
- it(`should have as title 'mfe2'`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app.title).toEqual('mfe2');
- });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.nativeElement as HTMLElement;
- expect(compiled.querySelector('h1')?.textContent).toContain('Welcome mfe2');
- });
-});
diff --git a/apps/mfe2/src/app/app.component.ts b/apps/mfe2/src/app/app.component.ts
index ac9076ef..779c77b4 100644
--- a/apps/mfe2/src/app/app.component.ts
+++ b/apps/mfe2/src/app/app.component.ts
@@ -1,11 +1,12 @@
import { Component } from '@angular/core';
+import { RouterOutlet } from '@angular/router';
@Component({
selector: 'angular-architects-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css'],
- standalone: false,
+ imports: [RouterOutlet],
+ template: `
+
mfe2 — standalone
+
+ `,
})
-export class AppComponent {
- title = 'mfe2';
-}
+export class AppComponent {}
diff --git a/apps/mfe2/src/app/app.module.ts b/apps/mfe2/src/app/app.module.ts
deleted file mode 100644
index ca4cff2f..00000000
--- a/apps/mfe2/src/app/app.module.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { NgModule } from '@angular/core';
-import { BrowserModule } from '@angular/platform-browser';
-
-import { AppComponent } from './app.component';
-import { NxWelcomeComponent } from './nx-welcome.component';
-
-@NgModule({
- declarations: [AppComponent, NxWelcomeComponent],
- imports: [BrowserModule],
- providers: [],
- bootstrap: [AppComponent],
-})
-export class AppModule {}
diff --git a/apps/mfe2/src/app/app.routes.ts b/apps/mfe2/src/app/app.routes.ts
new file mode 100644
index 00000000..6ed0604a
--- /dev/null
+++ b/apps/mfe2/src/app/app.routes.ts
@@ -0,0 +1,12 @@
+import { Routes } from '@angular/router';
+
+export const APP_ROUTES: Routes = [
+ {
+ path: '',
+ pathMatch: 'full',
+ loadComponent: () =>
+ import('./dashboard/dashboard.component').then(
+ (m) => m.DashboardComponent,
+ ),
+ },
+];
diff --git a/apps/mfe2/src/app/dashboard/dashboard.component.spec.ts b/apps/mfe2/src/app/dashboard/dashboard.component.spec.ts
new file mode 100644
index 00000000..eccdfa5d
--- /dev/null
+++ b/apps/mfe2/src/app/dashboard/dashboard.component.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+import { AuthService } from '@angular-architects/playground-lib';
+
+import { DashboardComponent } from './dashboard.component';
+
+describe('DashboardComponent', () => {
+ it('reads the user name from the shared AuthService', async () => {
+ TestBed.inject(AuthService).userName = 'Jane';
+
+ const fixture = TestBed.createComponent(DashboardComponent);
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.userName).toBe('Jane');
+ expect(fixture.nativeElement.textContent).toContain('Jane');
+ });
+});
diff --git a/apps/mfe2/src/app/dashboard/dashboard.component.ts b/apps/mfe2/src/app/dashboard/dashboard.component.ts
new file mode 100644
index 00000000..9d79a0c3
--- /dev/null
+++ b/apps/mfe2/src/app/dashboard/dashboard.component.ts
@@ -0,0 +1,26 @@
+import { Component, inject } from '@angular/core';
+import {
+ AuthService,
+ PlaygroundLibModule,
+} from '@angular-architects/playground-lib';
+
+// Exposed as './Component' — see apps/mfe2/webpack.config.js.
+@Component({
+ selector: 'angular-architects-dashboard',
+ imports: [PlaygroundLibModule],
+ template: `
+
Dashboard
+
+
+ AuthService.userName as seen from mfe2:
+ {{ userName || '(empty)' }}
+
# Generate UI lib
-nx g @nx/angular:lib ui
-
-# Add a component
-nx g @nx/angular:component button --project ui
-
-
-
-
- View interactive project graph
-
-
nx graph
-
-
-
-
- Run affected commands
-
-
# see what's been affected by changes
-nx affected:graph
-
-# run tests for current changes
-nx affected:test
-
-# run e2e tests for current changes
-nx affected:e2e
-
-
-
-
- Carefully crafted with
-
-
-
-
- `,
- styles: [],
- encapsulation: ViewEncapsulation.None,
- standalone: false,
-})
-export class NxWelcomeComponent implements OnInit {
- constructor() {}
-
- ngOnInit(): void {}
-}
diff --git a/apps/mfe2/src/assets/.gitkeep b/apps/mfe2/src/assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/mfe2/src/assets/federation.manifest.json b/apps/mfe2/src/assets/federation.manifest.json
deleted file mode 100644
index 36bab1bf..00000000
--- a/apps/mfe2/src/assets/federation.manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "mfe1": "http://localhost:4200/remoteEntry.json",
- "playground": "http://localhost:4200/remoteEntry.json"
-}
diff --git a/apps/mfe2/src/bootstrap.ts b/apps/mfe2/src/bootstrap.ts
index d9a2e7e4..0791995c 100644
--- a/apps/mfe2/src/bootstrap.ts
+++ b/apps/mfe2/src/bootstrap.ts
@@ -1,13 +1,9 @@
-import { enableProdMode } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { bootstrapApplication } from '@angular/platform-browser';
+import { provideRouter } from '@angular/router';
-import { AppModule } from './app/app.module';
-import { environment } from './environments/environment';
+import { AppComponent } from './app/app.component';
+import { APP_ROUTES } from './app/app.routes';
-if (environment.production) {
- enableProdMode();
-}
-
-platformBrowserDynamic()
- .bootstrapModule(AppModule)
- .catch((err) => console.error(err));
+bootstrapApplication(AppComponent, {
+ providers: [provideRouter(APP_ROUTES)],
+}).catch((err) => console.error(err));
diff --git a/apps/mfe2/src/environments/environment.prod.ts b/apps/mfe2/src/environments/environment.prod.ts
deleted file mode 100644
index c9669790..00000000
--- a/apps/mfe2/src/environments/environment.prod.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const environment = {
- production: true,
-};
diff --git a/apps/mfe2/src/environments/environment.ts b/apps/mfe2/src/environments/environment.ts
deleted file mode 100644
index 66998ae9..00000000
--- a/apps/mfe2/src/environments/environment.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// This file can be replaced during build by using the `fileReplacements` array.
-// `ng build` replaces `environment.ts` with `environment.prod.ts`.
-// The list of file replacements can be found in `angular.json`.
-
-export const environment = {
- production: false,
-};
-
-/*
- * For easier debugging in development mode, you can import the following file
- * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
- *
- * This import should be commented out in production mode because it will have a negative impact
- * on performance if an error is thrown.
- */
-// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
diff --git a/apps/mfe2/src/favicon.ico b/apps/mfe2/src/favicon.ico
deleted file mode 100644
index 317ebcb2..00000000
Binary files a/apps/mfe2/src/favicon.ico and /dev/null differ
diff --git a/apps/mfe2/src/index.html b/apps/mfe2/src/index.html
index caa19360..48335b09 100644
--- a/apps/mfe2/src/index.html
+++ b/apps/mfe2/src/index.html
@@ -2,10 +2,9 @@
- Mfe2
+ mfe2 (remote)
-
diff --git a/apps/mfe2/src/main.ts b/apps/mfe2/src/main.ts
index 0a52b61e..d7d2e9ae 100644
--- a/apps/mfe2/src/main.ts
+++ b/apps/mfe2/src/main.ts
@@ -1,6 +1,3 @@
-import { initFederation } from '@angular-architects/native-federation';
-
-initFederation('/assets/federation.manifest.json')
- .catch((err) => console.error(err))
- .then(() => import('./bootstrap'))
- .catch((err) => console.error(err));
+// A remote must not touch shared libraries before the share scope is
+// initialized, so all real work happens behind this dynamic import.
+import('./bootstrap').catch((err) => console.error(err));
diff --git a/apps/mfe2/src/polyfills.ts b/apps/mfe2/src/polyfills.ts
index ccd8b2f4..aa09a9ff 100644
--- a/apps/mfe2/src/polyfills.ts
+++ b/apps/mfe2/src/polyfills.ts
@@ -1,54 +1 @@
-/**
- * This file includes polyfills needed by Angular and is loaded before the app.
- * You can add your own extra polyfills to this file.
- *
- * This file is divided into 2 sections:
- * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
- * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
- * file.
- *
- * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
- * automatically update themselves. This includes recent versions of Safari, Chrome (including
- * Opera), Edge on the desktop, and iOS and Chrome on mobile.
- *
- * Learn more in https://angular.io/guide/browser-support
- */
-
-/***************************************************************************************************
- * BROWSER POLYFILLS
- */
-
-/**
- * By default, zone.js will patch all possible macroTask and DomEvents
- * user can disable parts of macroTask/DomEvents patch by setting following flags
- * because those flags need to be set before `zone.js` being loaded, and webpack
- * will put import in the top of bundle, so user need to create a separate file
- * in this directory (for example: zone-flags.ts), and put the following flags
- * into that file, and then add the following code before importing zone.js.
- * import './zone-flags';
- *
- * The flags allowed in zone-flags.ts are listed here.
- *
- * The following flags will work for all browsers.
- *
- * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
- * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
- * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
- *
- * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
- * with the following flag, it will bypass `zone.js` patch for IE/Edge
- *
- * (window as any).__Zone_enable_cross_context_check = true;
- *
- */
-
-/***************************************************************************************************
- * Zone JS is required by default for Angular itself.
- */
-import 'zone.js'; // Included with Angular CLI.
-
-/***************************************************************************************************
- * APPLICATION IMPORTS
- */
-
-import 'es-module-shims';
+import 'zone.js';
diff --git a/apps/mfe2/src/styles.css b/apps/mfe2/src/styles.css
index 90d4ee00..8c780472 100644
--- a/apps/mfe2/src/styles.css
+++ b/apps/mfe2/src/styles.css
@@ -1 +1,8 @@
-/* You can add global styles to this file, and also import other style files */
+body {
+ font-family: system-ui, sans-serif;
+ margin: 1rem;
+}
+
+nav a {
+ margin-right: 1rem;
+}
diff --git a/apps/mfe2/webpack.config.js b/apps/mfe2/webpack.config.js
new file mode 100644
index 00000000..e7db8bd0
--- /dev/null
+++ b/apps/mfe2/webpack.config.js
@@ -0,0 +1,26 @@
+// See apps/mfe1/webpack.config.js for why this requires the built output
+// instead of '@angular-architects/module-federation/webpack'.
+const {
+ shareAll,
+ withModuleFederationPlugin,
+} = require('../../dist/libs/mf/webpack');
+
+module.exports = withModuleFederationPlugin({
+ name: 'mfe2',
+
+ // mfe1 exposes routes, mfe2 exposes a single component — both are valid
+ // granularities and the shell consumes them differently.
+ exposes: {
+ './Component': './apps/mfe2/src/app/dashboard/dashboard.component.ts',
+ },
+
+ shared: {
+ ...shareAll({
+ singleton: true,
+ strictVersion: true,
+ requiredVersion: 'auto',
+ }),
+ },
+
+ sharedMappings: ['@angular-architects/playground-lib'],
+});
diff --git a/apps/mfe2/webpack.prod.config.js b/apps/mfe2/webpack.prod.config.js
new file mode 100644
index 00000000..0c9447df
--- /dev/null
+++ b/apps/mfe2/webpack.prod.config.js
@@ -0,0 +1 @@
+module.exports = require('./webpack.config');
diff --git a/apps/native-federation-e2e/eslint.config.mjs b/apps/native-federation-e2e/eslint.config.mjs
deleted file mode 100644
index 53f5684f..00000000
--- a/apps/native-federation-e2e/eslint.config.mjs
+++ /dev/null
@@ -1,20 +0,0 @@
-import baseConfig from '../../eslint.base.config.mjs';
-
-export default [
- ...baseConfig,
- {
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
- // Override or add rules here
- rules: {},
- },
- {
- files: ['**/*.ts', '**/*.tsx'],
- // Override or add rules here
- rules: {},
- },
- {
- files: ['**/*.js', '**/*.jsx'],
- // Override or add rules here
- rules: {},
- },
-];
diff --git a/apps/native-federation-e2e/jest.config.ts b/apps/native-federation-e2e/jest.config.ts
deleted file mode 100644
index e521ba42..00000000
--- a/apps/native-federation-e2e/jest.config.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/* eslint-disable */
-export default {
- displayName: 'native-federation-e2e',
- preset: '../../jest.preset.js',
- transform: {
- '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
- },
- moduleFileExtensions: ['ts', 'js', 'html'],
- coverageDirectory: '../../coverage/apps/native-federation-e2e',
-};
diff --git a/apps/native-federation-e2e/project.json b/apps/native-federation-e2e/project.json
deleted file mode 100644
index 27d87e55..00000000
--- a/apps/native-federation-e2e/project.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "native-federation-e2e",
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
- "projectType": "application",
- "sourceRoot": "apps/native-federation-e2e/src",
- "tags": [],
- "implicitDependencies": ["native-federation"],
- "targets": {
- "e2e": {
- "executor": "@nx/jest:jest",
- "options": {
- "jestConfig": "apps/native-federation-e2e/jest.config.ts",
- "runInBand": true,
- "passWithNoTests": false
- },
- "dependsOn": ["native-federation:build"]
- },
- "test": {
- "command": "echo Noop"
- }
- }
-}
diff --git a/apps/native-federation-e2e/tests/native-federation.spec.ts b/apps/native-federation-e2e/tests/native-federation.spec.ts
deleted file mode 100644
index 90abda37..00000000
--- a/apps/native-federation-e2e/tests/native-federation.spec.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import {
- checkFilesExist,
- ensureNxProject,
- readJson,
- runNxCommandAsync,
- uniq,
-} from '@nx/plugin/testing';
-
-describe('native-federation e2e', () => {
- // Setting up individual workspaces per
- // test can cause e2e runs to take a long time.
- // For this reason, we recommend each suite only
- // consumes 1 workspace. The tests should each operate
- // on a unique project in the workspace, such that they
- // are not dependant on one another.
- beforeAll(() => {
- ensureNxProject(
- '@angular-architects/native-federation',
- 'dist/libs/native-federation',
- );
- });
-
- afterAll(() => {
- // `nx reset` kills the daemon, and performs
- // some work which can help clean up e2e leftovers
- runNxCommandAsync('reset');
- });
-
- it('should create native-federation', async () => {
- const project = uniq('native-federation');
- await runNxCommandAsync(
- `generate @angular-architects/native-federation:native-federation ${project}`,
- );
- const result = await runNxCommandAsync(`build ${project}`);
- expect(result.stdout).toContain('Executor ran');
- }, 120000);
-
- describe('--directory', () => {
- it('should create src in the specified directory', async () => {
- const project = uniq('native-federation');
- await runNxCommandAsync(
- `generate @angular-architects/native-federation:native-federation ${project} --directory subdir`,
- );
- expect(() =>
- checkFilesExist(`libs/subdir/${project}/src/index.ts`),
- ).not.toThrow();
- }, 120000);
- });
-
- describe('--tags', () => {
- it('should add tags to the project', async () => {
- const projectName = uniq('native-federation');
- ensureNxProject(
- '@angular-architects/native-federation',
- 'dist/libs/native-federation',
- );
- await runNxCommandAsync(
- `generate @angular-architects/native-federation:native-federation ${projectName} --tags e2etag,e2ePackage`,
- );
- const project = readJson(`libs/${projectName}/project.json`);
- expect(project.tags).toEqual(['e2etag', 'e2ePackage']);
- }, 120000);
- });
-});
diff --git a/apps/native-federation-e2e/tsconfig.json b/apps/native-federation-e2e/tsconfig.json
deleted file mode 100644
index b9c9d953..00000000
--- a/apps/native-federation-e2e/tsconfig.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "files": [],
- "include": [],
- "references": [
- {
- "path": "./tsconfig.spec.json"
- }
- ]
-}
diff --git a/apps/native-federation-e2e/tsconfig.spec.json b/apps/native-federation-e2e/tsconfig.spec.json
deleted file mode 100644
index 9b2a121d..00000000
--- a/apps/native-federation-e2e/tsconfig.spec.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "outDir": "../../dist/out-tsc",
- "module": "commonjs",
- "types": ["jest", "node"]
- },
- "include": [
- "jest.config.ts",
- "src/**/*.test.ts",
- "src/**/*.spec.ts",
- "src/**/*.d.ts"
- ]
-}
diff --git a/apps/playground-e2e/cypress.json b/apps/playground-e2e/cypress.json
deleted file mode 100644
index aac0cf39..00000000
--- a/apps/playground-e2e/cypress.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "fileServerFolder": ".",
- "fixturesFolder": "./src/fixtures",
- "integrationFolder": "./src/integration",
- "modifyObstructiveCode": false,
- "pluginsFile": "./src/plugins/index",
- "supportFile": "./src/support/index.ts",
- "video": true,
- "videosFolder": "../../dist/cypress/apps/playground-e2e/videos",
- "screenshotsFolder": "../../dist/cypress/apps/playground-e2e/screenshots",
- "chromeWebSecurity": false
-}
diff --git a/apps/playground-e2e/eslint.config.mjs b/apps/playground-e2e/eslint.config.mjs
deleted file mode 100644
index c95ed601..00000000
--- a/apps/playground-e2e/eslint.config.mjs
+++ /dev/null
@@ -1,17 +0,0 @@
-import baseConfig from '../../eslint.config.mjs';
-
-export default [
- ...baseConfig,
- {
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
- // Override or add rules here
- rules: {},
- },
- {
- files: ['src/plugins/index.js'],
- rules: {
- '@typescript-eslint/no-var-requires': 'off',
- 'no-undef': 'off',
- },
- },
-];
diff --git a/apps/playground-e2e/project.json b/apps/playground-e2e/project.json
deleted file mode 100644
index 25dec8d1..00000000
--- a/apps/playground-e2e/project.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "name": "playground-e2e",
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "apps/playground-e2e/src",
- "projectType": "application",
- "tags": [],
- "implicitDependencies": ["playground"],
- "targets": {
- "e2e": {
- "executor": "@nx/cypress:cypress",
- "options": {
- "cypressConfig": "apps/playground-e2e/cypress.json",
- "tsConfig": "apps/playground-e2e/tsconfig.e2e.json",
- "devServerTarget": "playground:serve:development"
- },
- "configurations": {
- "production": {
- "devServerTarget": "playground:serve:production"
- }
- }
- },
- "lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"],
- "options": {
- "lintFilePatterns": ["apps/playground-e2e/**/*.{js,ts}"]
- }
- }
- }
-}
diff --git a/apps/playground-e2e/src/fixtures/example.json b/apps/playground-e2e/src/fixtures/example.json
deleted file mode 100644
index 294cbed6..00000000
--- a/apps/playground-e2e/src/fixtures/example.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io"
-}
diff --git a/apps/playground-e2e/src/integration/app.spec.ts b/apps/playground-e2e/src/integration/app.spec.ts
deleted file mode 100644
index 78f282c0..00000000
--- a/apps/playground-e2e/src/integration/app.spec.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { getGreeting } from '../support/app.po';
-
-describe('playground', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should display welcome message', () => {
- // Custom command example, see `../support/commands.ts` file
- cy.login('my-email@something.com', 'myPassword');
-
- // Function helper example, see `../support/app.po.ts` file
- getGreeting().contains('Hello Native Federation!');
- });
-});
diff --git a/apps/playground-e2e/src/plugins/index.js b/apps/playground-e2e/src/plugins/index.js
deleted file mode 100644
index 63aa33cb..00000000
--- a/apps/playground-e2e/src/plugins/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-const { preprocessTypescript } = require('@nx/cypress/plugins/preprocessor');
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-
- // Preprocess Typescript file using Nx helper
- on('file:preprocessor', preprocessTypescript(config));
-};
diff --git a/apps/playground-e2e/src/support/app.po.ts b/apps/playground-e2e/src/support/app.po.ts
deleted file mode 100644
index 32934246..00000000
--- a/apps/playground-e2e/src/support/app.po.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const getGreeting = () => cy.get('h1');
diff --git a/apps/playground-e2e/src/support/commands.ts b/apps/playground-e2e/src/support/commands.ts
deleted file mode 100644
index 310f1fa0..00000000
--- a/apps/playground-e2e/src/support/commands.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// ***********************************************
-// This example commands.js shows you how to
-// create various custom commands and overwrite
-// existing commands.
-//
-// For more comprehensive examples of custom
-// commands please read more here:
-// https://on.cypress.io/custom-commands
-// ***********************************************
-
-// eslint-disable-next-line @typescript-eslint/no-namespace
-declare namespace Cypress {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- interface Chainable {
- login(email: string, password: string): void;
- }
-}
-//
-// -- This is a parent command --
-Cypress.Commands.add('login', (email, password) => {
- console.log('Custom command example: Login', email, password);
-});
-//
-// -- This is a child command --
-// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
-//
-//
-// -- This is a dual command --
-// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
-//
-//
-// -- This will overwrite an existing command --
-// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
diff --git a/apps/playground-e2e/src/support/index.ts b/apps/playground-e2e/src/support/index.ts
deleted file mode 100644
index 3d469a6b..00000000
--- a/apps/playground-e2e/src/support/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example support/index.js is processed and
-// loaded automatically before your test files.
-//
-// This is a great place to put global configuration and
-// behavior that modifies Cypress.
-//
-// You can change the location of this file or turn off
-// automatically serving support files with the
-// 'supportFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/configuration
-// ***********************************************************
-
-// Import commands.js using ES2015 syntax:
-import './commands';
diff --git a/apps/playground-e2e/tsconfig.e2e.json b/apps/playground-e2e/tsconfig.e2e.json
deleted file mode 100644
index 9dc3660a..00000000
--- a/apps/playground-e2e/tsconfig.e2e.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "sourceMap": false,
- "outDir": "../../dist/out-tsc",
- "allowJs": true,
- "types": ["cypress", "node"]
- },
- "include": ["src/**/*.ts", "src/**/*.js"]
-}
diff --git a/apps/playground-e2e/tsconfig.json b/apps/playground-e2e/tsconfig.json
deleted file mode 100644
index 08841a7f..00000000
--- a/apps/playground-e2e/tsconfig.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "files": [],
- "include": [],
- "references": [
- {
- "path": "./tsconfig.e2e.json"
- }
- ]
-}
diff --git a/apps/playground/federation.config.js b/apps/playground/federation.config.js
deleted file mode 100644
index aa17472b..00000000
--- a/apps/playground/federation.config.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const {
- withNativeFederation,
- shareAll,
-} = require('@angular-architects/native-federation/config');
-
-module.exports = withNativeFederation({
- name: 'playground-shell',
- shared: {
- ...shareAll({
- singleton: true,
- strictVersion: true,
- requiredVersion: 'auto',
- }),
- },
-
- sharedMappings: ['@angular-architects/playground-lib'],
-});
diff --git a/apps/playground/project.json b/apps/playground/project.json
deleted file mode 100644
index 3eb98d9e..00000000
--- a/apps/playground/project.json
+++ /dev/null
@@ -1,99 +0,0 @@
-{
- "name": "playground",
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
- "projectType": "application",
- "sourceRoot": "apps/playground/src",
- "prefix": "angular-architects",
- "tags": [],
- "targets": {
- "demo": {
- "executor": "@angular-architects/native-federation:build",
- "options": {},
- "configurations": {}
- },
- "build": {
- "executor": "@angular-architects/native-federation:build",
- "defaultConfiguration": "production",
- "options": {
- "outputPath": "dist/apps/playground",
- "index": "apps/playground/src/index.html",
- "main": "apps/playground/src/main.ts",
- "polyfills": "apps/playground/src/polyfills.ts",
- "tsConfig": "apps/playground/tsconfig.app.json",
- "assets": [
- "apps/playground/src/favicon.ico",
- "apps/playground/src/assets"
- ],
- "styles": ["apps/playground/src/styles.css"],
- "scripts": []
- },
- "configurations": {
- "production": {
- "budgets": [
- {
- "type": "initial",
- "maximumWarning": "500kb",
- "maximumError": "1mb"
- },
- {
- "type": "anyComponentStyle",
- "maximumWarning": "2kb",
- "maximumError": "4kb"
- }
- ],
- "fileReplacements": [
- {
- "replace": "apps/playground/src/environments/environment.ts",
- "with": "apps/playground/src/environments/environment.prod.ts"
- }
- ],
- "outputHashing": "all"
- },
- "development": {
- "buildOptimizer": false,
- "optimization": false,
- "vendorChunk": true,
- "extractLicenses": false,
- "sourceMap": true,
- "namedChunks": true
- }
- }
- },
- "serve": {
- "executor": "@angular-devkit/build-angular:dev-server",
- "defaultConfiguration": "development",
- "continuous": true,
- "configurations": {
- "production": {
- "buildTarget": "playground:build:production"
- },
- "development": {
- "buildTarget": "playground:build:development"
- }
- }
- },
- "extract-i18n": {
- "executor": "@angular-devkit/build-angular:extract-i18n",
- "options": {
- "buildTarget": "playground:build"
- }
- },
- "lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"],
- "options": {
- "lintFilePatterns": [
- "apps/playground/src/**/*.ts",
- "apps/playground/src/**/*.html"
- ]
- }
- },
- "test": {
- "executor": "@nx/jest:jest",
- "outputs": ["{workspaceRoot}/coverage/apps/playground"],
- "options": {
- "jestConfig": "apps/playground/jest.config.ts"
- }
- }
- }
-}
diff --git a/apps/playground/src/app/app.component.css b/apps/playground/src/app/app.component.css
deleted file mode 100644
index f222adff..00000000
--- a/apps/playground/src/app/app.component.css
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Remove template code below
- */
-:host {
- display: block;
- font-family: sans-serif;
- min-width: 300px;
- max-width: 600px;
- margin: 50px auto;
-}
-
-.gutter-left {
- margin-left: 9px;
-}
-
-.col-span-2 {
- grid-column: span 2;
-}
-
-.flex {
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-header {
- background-color: #143055;
- color: white;
- padding: 5px;
- border-radius: 3px;
-}
-
-main {
- padding: 0 36px;
-}
-
-p {
- text-align: center;
-}
-
-h1 {
- text-align: center;
- margin-left: 18px;
- font-size: 24px;
-}
-
-h2 {
- text-align: center;
- font-size: 20px;
- margin: 40px 0 10px 0;
-}
-
-.resources {
- text-align: center;
- list-style: none;
- padding: 0;
- display: grid;
- grid-gap: 9px;
- grid-template-columns: 1fr 1fr;
-}
-
-.resource {
- color: #0094ba;
- height: 36px;
- background-color: rgba(0, 0, 0, 0);
- border: 1px solid rgba(0, 0, 0, 0.12);
- border-radius: 4px;
- padding: 3px 9px;
- text-decoration: none;
-}
-
-.resource:hover {
- background-color: rgba(68, 138, 255, 0.04);
-}
-
-pre {
- padding: 9px;
- border-radius: 4px;
- background-color: black;
- color: #eee;
-}
-
-details {
- border-radius: 4px;
- color: #333;
- background-color: rgba(0, 0, 0, 0);
- border: 1px solid rgba(0, 0, 0, 0.12);
- padding: 3px 9px;
- margin-bottom: 9px;
-}
-
-summary {
- cursor: pointer;
- outline: none;
- height: 36px;
- line-height: 36px;
-}
-
-.github-star-container {
- margin-top: 12px;
- line-height: 20px;
-}
-
-.github-star-container a {
- display: flex;
- align-items: center;
- text-decoration: none;
- color: #333;
-}
-
-.github-star-badge {
- color: #24292e;
- display: flex;
- align-items: center;
- font-size: 12px;
- padding: 3px 10px;
- border: 1px solid rgba(27, 31, 35, 0.2);
- border-radius: 3px;
- background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
- margin-left: 4px;
- font-weight: 600;
-}
-
-.github-star-badge:hover {
- background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
- border-color: rgba(27, 31, 35, 0.35);
- background-position: -0.5em;
-}
-.github-star-badge .material-icons {
- height: 16px;
- width: 16px;
- margin-right: 4px;
-}
diff --git a/apps/playground/src/app/app.component.html b/apps/playground/src/app/app.component.html
deleted file mode 100644
index 2d307792..00000000
--- a/apps/playground/src/app/app.component.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
Hello Native Federation!
-
-
-
-
-
-@if (Cmp) {
-
-
-
-}
diff --git a/apps/playground/src/app/app.component.spec.ts b/apps/playground/src/app/app.component.spec.ts
deleted file mode 100644
index 85cd18f0..00000000
--- a/apps/playground/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-
-describe('AppComponent', () => {
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [AppComponent],
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app).toBeTruthy();
- });
-
- it(`should have as title 'playground'`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app.title).toEqual('playground');
- });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.nativeElement;
- expect(compiled.querySelector('h1').textContent).toContain(
- 'Hello Native Federation!',
- );
- });
-});
diff --git a/apps/playground/src/app/app.component.ts b/apps/playground/src/app/app.component.ts
deleted file mode 100644
index 6f8df8a6..00000000
--- a/apps/playground/src/app/app.component.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { loadRemoteModule } from '@angular-architects/native-federation';
-import { AuthService } from '@angular-architects/playground-lib';
-import { Component, inject, Type } from '@angular/core';
-
-@Component({
- selector: 'angular-architects-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css'],
- standalone: false,
-})
-export class AppComponent {
- title = 'playground';
- Cmp: Type;
-
- constructor() {
- inject(AuthService).userName = 'Jane Doe';
- }
-
- async load() {
- // const m = await importShim('http://localhost:3001/cmp.js');
-
- const m = await loadRemoteModule({
- remoteEntry: 'http://localhost:3001/remoteEntry.json',
- // remoteName: 'mfe1',
- exposedModule: './cmp',
- });
-
- this.Cmp = m.DemoComponent;
- }
-}
diff --git a/apps/playground/src/app/app.module.ts b/apps/playground/src/app/app.module.ts
deleted file mode 100644
index 7bcb9e9a..00000000
--- a/apps/playground/src/app/app.module.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { NgModule } from '@angular/core';
-import { BrowserModule } from '@angular/platform-browser';
-
-import { AppComponent } from './app.component';
-
-@NgModule({
- declarations: [AppComponent],
- imports: [BrowserModule],
- providers: [],
- bootstrap: [AppComponent],
-})
-export class AppModule {}
diff --git a/apps/playground/src/assets/.gitkeep b/apps/playground/src/assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/playground/src/bootstrap.ts b/apps/playground/src/bootstrap.ts
deleted file mode 100644
index d9a2e7e4..00000000
--- a/apps/playground/src/bootstrap.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { enableProdMode } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-
-import { AppModule } from './app/app.module';
-import { environment } from './environments/environment';
-
-if (environment.production) {
- enableProdMode();
-}
-
-platformBrowserDynamic()
- .bootstrapModule(AppModule)
- .catch((err) => console.error(err));
diff --git a/apps/playground/src/environments/environment.prod.ts b/apps/playground/src/environments/environment.prod.ts
deleted file mode 100644
index c9669790..00000000
--- a/apps/playground/src/environments/environment.prod.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const environment = {
- production: true,
-};
diff --git a/apps/playground/src/environments/environment.ts b/apps/playground/src/environments/environment.ts
deleted file mode 100644
index 66998ae9..00000000
--- a/apps/playground/src/environments/environment.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// This file can be replaced during build by using the `fileReplacements` array.
-// `ng build` replaces `environment.ts` with `environment.prod.ts`.
-// The list of file replacements can be found in `angular.json`.
-
-export const environment = {
- production: false,
-};
-
-/*
- * For easier debugging in development mode, you can import the following file
- * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
- *
- * This import should be commented out in production mode because it will have a negative impact
- * on performance if an error is thrown.
- */
-// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
diff --git a/apps/playground/src/favicon.ico b/apps/playground/src/favicon.ico
deleted file mode 100644
index 317ebcb2..00000000
Binary files a/apps/playground/src/favicon.ico and /dev/null differ
diff --git a/apps/playground/src/main.ts b/apps/playground/src/main.ts
deleted file mode 100644
index 1e485abf..00000000
--- a/apps/playground/src/main.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { initFederation } from '@angular-architects/native-federation';
-
-initFederation({
- //'mfe1': 'http://localhost:3001/remoteEntry.json'
-})
- .then(() => import('./bootstrap'))
- .catch((e) => console.error('err', e));
diff --git a/apps/playground/src/polyfills.ts b/apps/playground/src/polyfills.ts
deleted file mode 100644
index afab4cd4..00000000
--- a/apps/playground/src/polyfills.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * This file includes polyfills needed by Angular and is loaded before the app.
- * You can add your own extra polyfills to this file.
- *
- * This file is divided into 2 sections:
- * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
- * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
- * file.
- *
- * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
- * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
- * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
- *
- * Learn more in https://angular.io/guide/browser-support
- */
-
-/***************************************************************************************************
- * BROWSER POLYFILLS
- */
-
-/**
- * By default, zone.js will patch all possible macroTask and DomEvents
- * user can disable parts of macroTask/DomEvents patch by setting following flags
- * because those flags need to be set before `zone.js` being loaded, and webpack
- * will put import in the top of bundle, so user need to create a separate file
- * in this directory (for example: zone-flags.ts), and put the following flags
- * into that file, and then add the following code before importing zone.js.
- * import './zone-flags';
- *
- * The flags allowed in zone-flags.ts are listed here.
- *
- * The following flags will work for all browsers.
- *
- * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
- * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
- * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
- *
- * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
- * with the following flag, it will bypass `zone.js` patch for IE/Edge
- *
- * (window as any).__Zone_enable_cross_context_check = true;
- *
- */
-
-/***************************************************************************************************
- * Zone JS is required by default for Angular itself.
- */
-import 'zone.js'; // Included with Angular CLI.
-
-/***************************************************************************************************
- * APPLICATION IMPORTS
- */
-
-import 'es-module-shims';
diff --git a/apps/playground/src/styles.css b/apps/playground/src/styles.css
deleted file mode 100644
index 90d4ee00..00000000
--- a/apps/playground/src/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-/* You can add global styles to this file, and also import other style files */
diff --git a/apps/playground/src/test-setup.ts b/apps/playground/src/test-setup.ts
deleted file mode 100644
index 78d7020a..00000000
--- a/apps/playground/src/test-setup.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
-setupZoneTestEnv();
-
-import { getTestBed } from '@angular/core/testing';
-import {
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting,
-} from '@angular/platform-browser-dynamic/testing';
-
-getTestBed().resetTestEnvironment();
-getTestBed().initTestEnvironment(
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting(),
- { teardown: { destroyAfterEach: false } },
-);
diff --git a/apps/playground/tsconfig.app.json b/apps/playground/tsconfig.app.json
deleted file mode 100644
index feaf147e..00000000
--- a/apps/playground/tsconfig.app.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "outDir": "../../dist/out-tsc",
- "types": []
- },
- "files": ["src/main.ts", "src/polyfills.ts"],
- "include": ["src/**/*.d.ts"],
- "exclude": ["jest.config.ts"]
-}
diff --git a/apps/playground/tsconfig.json b/apps/playground/tsconfig.json
deleted file mode 100644
index 7dcba407..00000000
--- a/apps/playground/tsconfig.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "files": [],
- "include": [],
- "references": [
- {
- "path": "./tsconfig.app.json"
- },
- {
- "path": "./tsconfig.spec.json"
- },
- {
- "path": "./tsconfig.editor.json"
- }
- ],
- "compilerOptions": {
- "target": "es2020"
- }
-}
diff --git a/apps/playground/eslint.config.mjs b/apps/shell/eslint.config.mjs
similarity index 90%
rename from apps/playground/eslint.config.mjs
rename to apps/shell/eslint.config.mjs
index 66525c81..6aaf44f9 100644
--- a/apps/playground/eslint.config.mjs
+++ b/apps/shell/eslint.config.mjs
@@ -1,5 +1,3 @@
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
import baseConfig from '../../eslint.config.mjs';
import nx from '@nx/eslint-plugin';
diff --git a/apps/playground/jest.config.ts b/apps/shell/jest.config.ts
similarity index 72%
rename from apps/playground/jest.config.ts
rename to apps/shell/jest.config.ts
index 1ce534fd..5f73a87c 100644
--- a/apps/playground/jest.config.ts
+++ b/apps/shell/jest.config.ts
@@ -1,17 +1,11 @@
-/* eslint-disable */
export default {
- displayName: 'playground',
+ displayName: 'shell',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['/src/test-setup.ts'],
globals: {},
- coverageDirectory: '../../coverage/apps/playground',
- snapshotSerializers: [
- 'jest-preset-angular/build/serializers/no-ng-attributes',
- 'jest-preset-angular/build/serializers/ng-snapshot',
- 'jest-preset-angular/build/serializers/html-comment',
- ],
+ coverageDirectory: '../../coverage/apps/shell',
transform: {
- '^.+.(ts|mjs|js|html)$': [
+ '^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '/tsconfig.spec.json',
@@ -19,5 +13,10 @@ export default {
},
],
},
- transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'],
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
+ snapshotSerializers: [
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
+ 'jest-preset-angular/build/serializers/ng-snapshot',
+ 'jest-preset-angular/build/serializers/html-comment',
+ ],
};
diff --git a/apps/shell/project.json b/apps/shell/project.json
new file mode 100644
index 00000000..4a4dd548
--- /dev/null
+++ b/apps/shell/project.json
@@ -0,0 +1,107 @@
+{
+ "name": "shell",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "application",
+ "sourceRoot": "apps/shell/src",
+ "prefix": "angular-architects",
+ "tags": ["scope:demo"],
+ "targets": {
+ "build": {
+ "executor": "@nx/angular:webpack-browser",
+ "outputs": ["{options.outputPath}"],
+ "defaultConfiguration": "production",
+ "dependsOn": [
+ {
+ "projects": ["mf"],
+ "target": "build"
+ }
+ ],
+ "options": {
+ "customWebpackConfig": {
+ "path": "apps/shell/webpack.config.js"
+ },
+ "outputPath": "dist/apps/shell",
+ "index": "apps/shell/src/index.html",
+ "main": "apps/shell/src/main.ts",
+ "polyfills": "apps/shell/src/polyfills.ts",
+ "tsConfig": "apps/shell/tsconfig.app.json",
+ "assets": [
+ {
+ "glob": "**/*",
+ "input": "apps/shell/public",
+ "output": "."
+ }
+ ],
+ "styles": ["apps/shell/src/styles.css"],
+ "scripts": [],
+ "commonChunk": false,
+ "extractLicenses": false
+ },
+ "configurations": {
+ "production": {
+ "customWebpackConfig": {
+ "path": "apps/shell/webpack.prod.config.js"
+ },
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "1mb",
+ "maximumError": "2mb"
+ },
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "2kb",
+ "maximumError": "4kb"
+ }
+ ],
+ "outputHashing": "all"
+ },
+ "development": {
+ "optimization": false,
+ "sourceMap": true,
+ "namedChunks": true
+ }
+ }
+ },
+ "serve": {
+ "executor": "@nx/angular:dev-server",
+ "defaultConfiguration": "development",
+ "continuous": true,
+ "dependsOn": [
+ {
+ "projects": ["mf"],
+ "target": "build"
+ },
+ {
+ "projects": ["mfe1", "mfe2"],
+ "target": "serve"
+ }
+ ],
+ "options": {
+ "port": 4200,
+ "publicHost": "http://localhost:4200"
+ },
+ "configurations": {
+ "production": {
+ "buildTarget": "shell:build:production"
+ },
+ "development": {
+ "buildTarget": "shell:build:development"
+ }
+ }
+ },
+ "lint": {
+ "executor": "@nx/eslint:lint",
+ "options": {
+ "lintFilePatterns": ["apps/shell/**/*.ts", "apps/shell/**/*.html"]
+ }
+ },
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/apps/shell"],
+ "options": {
+ "jestConfig": "apps/shell/jest.config.ts"
+ }
+ }
+ }
+}
diff --git a/apps/shell/public/mf.manifest.json b/apps/shell/public/mf.manifest.json
new file mode 100644
index 00000000..cd613acc
--- /dev/null
+++ b/apps/shell/public/mf.manifest.json
@@ -0,0 +1,4 @@
+{
+ "mfe1": "http://localhost:4201/remoteEntry.js",
+ "mfe2": "http://localhost:4202/remoteEntry.js"
+}
diff --git a/apps/shell/src/app/app.component.spec.ts b/apps/shell/src/app/app.component.spec.ts
new file mode 100644
index 00000000..fac48f8a
--- /dev/null
+++ b/apps/shell/src/app/app.component.spec.ts
@@ -0,0 +1,27 @@
+import { TestBed } from '@angular/core/testing';
+import { provideRouter } from '@angular/router';
+import { AuthService } from '@angular-architects/playground-lib';
+
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(() =>
+ TestBed.configureTestingModule({ providers: [provideRouter([])] }),
+ );
+
+ it('writes the login input straight into the shared AuthService', async () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ const input: HTMLInputElement =
+ fixture.nativeElement.querySelector('input');
+ input.value = 'Jane';
+ input.dispatchEvent(new Event('input'));
+ await fixture.whenStable();
+
+ // Remotes resolve the same singleton out of the share scope at runtime,
+ // which is what makes the name visible inside mfe1 and mfe2.
+ expect(TestBed.inject(AuthService).userName).toBe('Jane');
+ });
+});
diff --git a/apps/shell/src/app/app.component.ts b/apps/shell/src/app/app.component.ts
new file mode 100644
index 00000000..31aed671
--- /dev/null
+++ b/apps/shell/src/app/app.component.ts
@@ -0,0 +1,48 @@
+import { Component, inject } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
+import { AuthService } from '@angular-architects/playground-lib';
+
+@Component({
+ selector: 'angular-architects-root',
+ imports: [RouterOutlet, RouterLink, RouterLinkActive, FormsModule],
+ template: `
+
+ Type a name above, then open Flights or Dashboard. Both
+ are separate builds served from other ports, and both read the name back
+ out of the same AuthService instance.
+
+
+ Watch the network tab: remoteEntry.js comes from
+ :4201/:4202, but Angular itself is downloaded
+ once, by whichever build gets there first.
+
+ `,
+})
+export class HomeComponent {}
diff --git a/apps/shell/src/app/not-found.component.ts b/apps/shell/src/app/not-found.component.ts
new file mode 100644
index 00000000..c317729a
--- /dev/null
+++ b/apps/shell/src/app/not-found.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'angular-architects-not-found',
+ template: `