From 6faeafe998d67029c4e81c966a7cc232e527602e Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Thu, 6 Aug 2026 09:13:34 +0200 Subject: [PATCH 1/2] chore(mf)!: Remove Native Federation Native Federation has moved to its own organization at github.com/native-federation. This repository now ships only the Module Federation packages. The deprecated v3 sources for Angular 21 stay available on the 21.x.x backport branch, so nothing needs preserving on main. Removed the five NF libs (native-federation, -core, -esbuild, -node, -runtime) and apps/native-federation-e2e. Also removed the mfe1, mfe2 and playground demo apps with their e2e projects. Despite the names, all three were Native Federation demos: NF build executor, initFederation() from @angular-architects/native-federation, and withNativeFederation() configs. Transforming them to Module Federation would have rewritten every federation-relevant line while carrying forward NgModule-era boilerplate and a repair bill CI had already given up on -- they sat behind `--exclude mfe1 mfe2 playground` with a "remove exclude when apps are fixed" TODO, and their Cypress suites still used the Nx-12-era cypress.json format against Cypress 15. Module Federation demos will be rebuilt separately, generated through the plugin's own init-webpack schematic so they double as end-to-end coverage of it. libs/playground-lib is kept for that: it is the sharedMappings demo target, and Module Federation has the same feature. The ng-add/init schematic no longer offers the native-federation-esbuild stack, which also drops its hardcoded @angular-architects/native-federation@^19.0.0 install task. Config fallout: 2 dependencies and 21 devDependencies whose only consumers were NF libs (167 packages out of node_modules), the NF targetDefault and the @nx/vite plugin from nx.json, and six @softarc/native-federation paths from tsconfig.base.json. serve.js went with the demo apps -- it was hardcoded to dist/apps/playground -- as did update-local-nf.sh and error.png, the latter referenced only by the NF readme. esbuild's ">=0.27.1 <0.27.4 || >0.27.4" range moved from devDependencies into overrides, where it can actually constrain the transitive copy @angular/build pulls in. CI drops the Playwright install step, which existed for native-federation-runtime's vitest browser mode, and no longer needs the app exclusion list: `nx affected -t lint test build` now covers everything that remains. The three @softarc/native-federation-* entries in DEFAULT_SKIP_LIST are kept deliberately. They are inert strings, and dropping them would silently change share behaviour for anyone running both plugin families during migration. BREAKING CHANGE: `ng add @angular-architects/module-federation` no longer offers the Native Federation stack. Use github.com/native-federation/angular-adapter instead. --- .github/ISSUE_TEMPLATE/bug-report.yml | 15 +- .github/ISSUE_TEMPLATE/feature-request.yml | 1 - .github/ISSUE_TEMPLATE/question.yml | 1 - .github/workflows/ci.yml | 12 +- README.md | 70 +- apps/mfe1-e2e/cypress.json | 12 - apps/mfe1-e2e/eslint.config.mjs | 17 - apps/mfe1-e2e/project.json | 30 - apps/mfe1-e2e/src/fixtures/example.json | 4 - apps/mfe1-e2e/src/integration/app.spec.ts | 13 - apps/mfe1-e2e/src/plugins/index.js | 22 - apps/mfe1-e2e/src/support/app.po.ts | 1 - apps/mfe1-e2e/src/support/commands.ts | 33 - apps/mfe1-e2e/src/support/index.ts | 17 - apps/mfe1-e2e/tsconfig.json | 22 - apps/mfe1/eslint.config.mjs | 32 - apps/mfe1/federation.config.js | 22 - apps/mfe1/jest.config.ts | 23 - apps/mfe1/project.json | 88 - apps/mfe1/src/app/app.component.css | 0 apps/mfe1/src/app/app.component.html | 1 - apps/mfe1/src/app/app.component.spec.ts | 30 - apps/mfe1/src/app/app.component.ts | 11 - apps/mfe1/src/app/app.module.ts | 14 - apps/mfe1/src/app/demo/demo.component.css | 3 - apps/mfe1/src/app/demo/demo.component.html | 9 - apps/mfe1/src/app/demo/demo.component.spec.ts | 22 - apps/mfe1/src/app/demo/demo.component.ts | 21 - apps/mfe1/src/app/nx-welcome.component.ts | 930 ------ apps/mfe1/src/assets/.gitkeep | 0 apps/mfe1/src/bootstrap.ts | 13 - .../mfe1/src/environments/environment.prod.ts | 3 - apps/mfe1/src/environments/environment.ts | 16 - apps/mfe1/src/favicon.ico | Bin 15086 -> 0 bytes apps/mfe1/src/index.html | 13 - apps/mfe1/src/main.ts | 5 - apps/mfe1/src/polyfills.ts | 53 - apps/mfe1/src/styles.css | 1 - apps/mfe1/src/test-setup.ts | 2 - apps/mfe1/tsconfig.app.json | 10 - apps/mfe1/tsconfig.editor.json | 7 - apps/mfe1/tsconfig.json | 31 - apps/mfe1/tsconfig.spec.json | 10 - apps/mfe2-e2e/cypress.json | 12 - apps/mfe2-e2e/eslint.config.mjs | 17 - apps/mfe2-e2e/project.json | 30 - apps/mfe2-e2e/src/fixtures/example.json | 4 - apps/mfe2-e2e/src/integration/app.spec.ts | 13 - apps/mfe2-e2e/src/plugins/index.js | 22 - apps/mfe2-e2e/src/support/app.po.ts | 1 - apps/mfe2-e2e/src/support/commands.ts | 33 - apps/mfe2-e2e/src/support/index.ts | 17 - apps/mfe2-e2e/tsconfig.json | 22 - apps/mfe2/eslint.config.mjs | 32 - apps/mfe2/federation.config.js | 15 - apps/mfe2/jest.config.ts | 23 - apps/mfe2/project.json | 89 - apps/mfe2/src/app/app.component.css | 0 apps/mfe2/src/app/app.component.html | 1 - apps/mfe2/src/app/app.component.spec.ts | 30 - apps/mfe2/src/app/app.component.ts | 11 - apps/mfe2/src/app/app.module.ts | 13 - apps/mfe2/src/app/nx-welcome.component.ts | 930 ------ apps/mfe2/src/assets/.gitkeep | 0 apps/mfe2/src/assets/federation.manifest.json | 4 - apps/mfe2/src/bootstrap.ts | 13 - .../mfe2/src/environments/environment.prod.ts | 3 - apps/mfe2/src/environments/environment.ts | 16 - apps/mfe2/src/favicon.ico | Bin 15086 -> 0 bytes apps/mfe2/src/index.html | 13 - apps/mfe2/src/main.ts | 6 - apps/mfe2/src/polyfills.ts | 54 - apps/mfe2/src/styles.css | 1 - apps/mfe2/src/test-setup.ts | 2 - apps/mfe2/tsconfig.app.json | 10 - apps/mfe2/tsconfig.editor.json | 7 - apps/mfe2/tsconfig.json | 31 - apps/mfe2/tsconfig.spec.json | 10 - apps/native-federation-e2e/eslint.config.mjs | 20 - apps/native-federation-e2e/jest.config.ts | 10 - apps/native-federation-e2e/project.json | 22 - .../tests/native-federation.spec.ts | 64 - apps/native-federation-e2e/tsconfig.json | 10 - apps/native-federation-e2e/tsconfig.spec.json | 14 - apps/playground-e2e/cypress.json | 12 - apps/playground-e2e/eslint.config.mjs | 17 - apps/playground-e2e/project.json | 30 - apps/playground-e2e/src/fixtures/example.json | 4 - .../src/integration/app.spec.ts | 13 - apps/playground-e2e/src/plugins/index.js | 22 - apps/playground-e2e/src/support/app.po.ts | 1 - apps/playground-e2e/src/support/commands.ts | 33 - apps/playground-e2e/src/support/index.ts | 17 - apps/playground-e2e/tsconfig.e2e.json | 10 - apps/playground-e2e/tsconfig.json | 10 - apps/playground/eslint.config.mjs | 32 - apps/playground/federation.config.js | 17 - apps/playground/jest.config.ts | 23 - apps/playground/project.json | 99 - apps/playground/src/app/app.component.css | 133 - apps/playground/src/app/app.component.html | 11 - apps/playground/src/app/app.component.spec.ts | 31 - apps/playground/src/app/app.component.ts | 30 - apps/playground/src/app/app.module.ts | 12 - apps/playground/src/assets/.gitkeep | 0 apps/playground/src/bootstrap.ts | 13 - .../src/environments/environment.prod.ts | 3 - .../src/environments/environment.ts | 16 - apps/playground/src/favicon.ico | Bin 15086 -> 0 bytes apps/playground/src/index.html | 13 - apps/playground/src/main.ts | 7 - apps/playground/src/polyfills.ts | 54 - apps/playground/src/styles.css | 1 - apps/playground/src/test-setup.ts | 15 - apps/playground/tsconfig.app.json | 10 - apps/playground/tsconfig.editor.json | 8 - apps/playground/tsconfig.json | 19 - apps/playground/tsconfig.spec.json | 10 - error.png | Bin 115482 -> 0 bytes libs/mf/README.md | 12 +- .../src/schematics/init-webpack/schematic.ts | 2 +- libs/mf/src/schematics/init/schema.d.ts | 5 +- libs/mf/src/schematics/init/schema.json | 7 +- libs/mf/src/schematics/init/schematic.ts | 29 +- libs/native-federation-core/LICENSE | 8 - libs/native-federation-core/README.md | 509 --- libs/native-federation-core/build.ts | 1 - libs/native-federation-core/eslint.config.mjs | 33 - libs/native-federation-core/jest.config.ts | 11 - libs/native-federation-core/package.json | 13 - libs/native-federation-core/project.json | 36 - libs/native-federation-core/src/build.ts | 37 - libs/native-federation-core/src/config.ts | 2 - libs/native-federation-core/src/index.ts | 1 - .../src/lib/config/configuration-context.ts | 18 - .../src/lib/config/federation-config.ts | 59 - .../src/lib/config/share-utils.ts | 648 ---- .../src/lib/config/with-native-federation.ts | 142 - .../src/lib/core/build-adapter.d.ts | 31 - .../src/lib/core/build-adapter.ts | 52 - .../src/lib/core/build-for-federation.ts | 292 -- .../lib/core/bundle-exposed-and-mappings.ts | 180 -- .../src/lib/core/bundle-shared.ts | 272 -- .../src/lib/core/default-external-list.ts | 33 - .../src/lib/core/default-server-deps-list.ts | 7 - .../src/lib/core/default-skip-list.ts | 63 - .../src/lib/core/federation-builder.ts | 56 - .../src/lib/core/federation-options.ts | 15 - .../src/lib/core/get-externals.ts | 8 - .../src/lib/core/load-federation-config.ts | 39 - .../src/lib/core/remove-unused-deps.ts | 152 - .../src/lib/core/write-federation-info.ts | 16 - .../src/lib/core/write-import-map.ts | 24 - .../src/lib/utils/build-result-map.ts | 35 - .../src/lib/utils/build-utils.ts | 6 - .../src/lib/utils/bundle-caching.ts | 117 - .../src/lib/utils/config-utils.ts | 14 - .../src/lib/utils/errors.ts | 7 - .../src/lib/utils/get-external-imports.ts | 101 - .../src/lib/utils/hash-file.ts | 9 - .../src/lib/utils/logger.d.ts | 10 - .../src/lib/utils/logger.ts | 38 - .../src/lib/utils/mapped-paths.ts | 83 - .../src/lib/utils/normalize.ts | 22 - .../src/lib/utils/package-info.ts | 395 --- .../src/lib/utils/rebuild-queue.ts | 60 - .../src/lib/utils/resolve-glob.ts | 41 - .../src/lib/utils/resolve-wildcard-keys.ts | 166 - .../src/lib/utils/rewrite-chunk-imports.ts | 99 - libs/native-federation-core/stack.png | Bin 11702 -> 0 bytes libs/native-federation-core/tsconfig.json | 22 - libs/native-federation-core/tsconfig.lib.json | 10 - .../native-federation-core/tsconfig.spec.json | 14 - libs/native-federation-esbuild/LICENSE | 8 - libs/native-federation-esbuild/README.md | 3 - .../eslint.config.mjs | 30 - libs/native-federation-esbuild/jest.config.ts | 11 - libs/native-federation-esbuild/package.json | 15 - libs/native-federation-esbuild/project.json | 36 - libs/native-federation-esbuild/src/index.ts | 1 - .../src/lib/adapter.ts | 209 -- .../src/lib/collect-exports.ts | 82 - .../src/lib/react-replacements.ts | 35 - libs/native-federation-esbuild/tsconfig.json | 22 - .../tsconfig.lib.json | 10 - .../tsconfig.spec.json | 14 - libs/native-federation-node/LICENSE | 8 - libs/native-federation-node/README.md | 11 - .../build/create-data-url.js | 16 - libs/native-federation-node/eslint.config.mjs | 34 - libs/native-federation-node/jest.config.ts | 11 - libs/native-federation-node/package.json | 9 - libs/native-federation-node/project.json | 46 - libs/native-federation-node/src/index.ts | 1 - .../src/lib/node/init-node-federation.ts | 94 - .../src/lib/utils/fstart-args-parser.ts | 74 - .../src/lib/utils/fstart.mjs | 439 --- .../src/lib/utils/fstart.ts | 15 - .../src/lib/utils/import-map-loader.js | 298 -- .../src/lib/utils/loader-as-data-url.js | 2 - libs/native-federation-node/tsconfig.json | 18 - libs/native-federation-node/tsconfig.lib.json | 18 - .../native-federation-node/tsconfig.spec.json | 14 - libs/native-federation-runtime/LICENSE | 8 - libs/native-federation-runtime/README.md | 7 - .../eslint.config.mjs | 50 - .../native-federation-runtime/ng-package.json | 7 - .../package-lock.json | 41 - libs/native-federation-runtime/package.json | 26 - libs/native-federation-runtime/project.json | 59 - .../public/mockServiceWorker.js | 349 -- libs/native-federation-runtime/src/index.ts | 6 - .../src/lib/__test-helpers__/dom-helpers.ts | 62 - .../__test-helpers__/federation-fixtures.ts | 109 - .../lib/__test-helpers__/module-fixtures.ts | 72 - .../src/lib/__test-helpers__/msw-handlers.ts | 164 - .../src/lib/get-shared.ts | 75 - .../lib/init-federation.integration.spec.ts | 763 ----- .../src/lib/init-federation.ts | 432 --- .../load-remote-module.integration.spec.ts | 758 ----- .../src/lib/load-remote-module.ts | 270 -- .../lib/model/build-notifications-options.ts | 13 - .../src/lib/model/externals.spec.ts | 72 - .../src/lib/model/externals.ts | 18 - .../src/lib/model/federation-info.ts | 35 - .../src/lib/model/global-cache.ts | 23 - .../src/lib/model/import-map.spec.ts | 61 - .../src/lib/model/import-map.ts | 15 - .../src/lib/model/remotes.spec.ts | 99 - .../src/lib/model/remotes.ts | 30 - .../src/lib/test-setup.ts | 9 - .../src/lib/utils/add-import-map.spec.ts | 32 - .../src/lib/utils/add-import-map.ts | 11 - .../src/lib/utils/path-utils.spec.ts | 42 - .../src/lib/utils/path-utils.ts | 27 - .../src/lib/utils/trusted-types.spec.ts | 95 - .../src/lib/utils/trusted-types.ts | 30 - .../src/lib/watch-federation-build.spec.ts | 73 - .../src/lib/watch-federation-build.ts | 25 - .../src/test-setup.ts | 2 - libs/native-federation-runtime/tsconfig.json | 30 - .../tsconfig.lib.json | 17 - .../tsconfig.lib.prod.json | 9 - .../tsconfig.spec.json | 10 - .../native-federation-runtime/vite.config.mts | 14 - .../vitest.config.mts | 55 - libs/native-federation/LICENSE | 8 - libs/native-federation/README.md | 494 --- .../build/create-data-url.js | 16 - libs/native-federation/builders.json | 10 - libs/native-federation/collection.json | 27 - libs/native-federation/config.ts | 1 - libs/native-federation/demo-repo.png | Bin 228617 -> 0 bytes libs/native-federation/docs/migrate.md | 173 - libs/native-federation/docs/share-faq.md | 148 - libs/native-federation/docs/update18.md | 19 - libs/native-federation/eslint.config.mjs | 30 - libs/native-federation/example.png | Bin 45183 -> 0 bytes libs/native-federation/executors.json | 10 - libs/native-federation/generators.json | 12 - libs/native-federation/jest.config.ts | 11 - libs/native-federation/migrate-appbuilder.md | 142 - .../migration-collection.json | 13 - libs/native-federation/package.json | 34 - libs/native-federation/post-build.js | 10 - libs/native-federation/project.json | 80 - .../src/builders/build/builder.ts | 616 ---- .../build/federation-build-notifier.ts | 259 -- .../src/builders/build/schema.d.ts | 23 - .../src/builders/build/schema.json | 95 - .../src/builders/serve/builder.ts.bak | 57 - .../src/builders/serve/schema.json.bak | 107 - libs/native-federation/src/config.ts | 9 - .../src/executors/build/executor.spec.ts | 11 - .../src/executors/build/executor.ts | 12 - .../src/executors/build/schema.d.ts | 1 - .../src/executors/build/schema.json | 9 - .../files/src/index.ts__template__ | 1 - .../native-federation/generator.spec.ts | 20 - .../generators/native-federation/generator.ts | 76 - .../generators/native-federation/schema.d.ts | 5 - .../generators/native-federation/schema.json | 29 - libs/native-federation/src/index.ts | 1 - .../src/patch-angular-build.ts | 8 - .../src/plugin/dev-externals-mixin.ts | 34 - .../src/plugin/externals-skip-list.ts | 5 - libs/native-federation/src/plugin/index.ts | 123 - .../src/schematics/appbuilder/schema.d.ts | 3 - .../src/schematics/appbuilder/schema.json | 17 - .../src/schematics/appbuilder/schematic.ts | 148 - .../init/files/federation.config.js__tmpl__ | 33 - .../src/schematics/init/schema.d.ts | 6 - .../src/schematics/init/schema.json | 34 - .../src/schematics/init/schematic.ts | 638 ---- .../src/schematics/remove/schema.d.ts | 3 - .../src/schematics/remove/schema.json | 17 - .../src/schematics/remove/schematic.ts | 179 -- .../src/schematics/update18/schema.json | 7 - .../src/schematics/update18/schematic.ts | 10 - .../src/tools/fstart-as-data-url.ts | 2 - .../src/utils/angular-esbuild-adapter.ts | 522 --- .../src/utils/angular-locales.ts | 27 - .../src/utils/coverage-instrumentation.ts | 83 - .../utils/create-awaitable-compiler-plugin.ts | 40 - .../src/utils/create-compiler-options.ts | 69 - .../src/utils/event-sorce.ts | 18 - libs/native-federation/src/utils/i18n.spec.ts | 232 -- libs/native-federation/src/utils/i18n.ts | 298 -- .../native-federation/src/utils/mem-resuts.ts | 70 - .../src/utils/patch-angular-build.ts | 47 - .../src/utils/rebuild-events.ts | 9 - .../src/utils/shared-mappings-plugin.ts | 35 - .../src/utils/updateIndexHtml.ts | 73 - libs/native-federation/tsconfig.json | 25 - libs/native-federation/tsconfig.lib.json | 11 - libs/native-federation/tsconfig.spec.json | 15 - nx.json | 19 - package-lock.json | 2851 ----------------- package.json | 42 +- serve.js | 31 - tsconfig.base.json | 18 - update-local-nf.sh | 7 - 322 files changed, 41 insertions(+), 22079 deletions(-) delete mode 100644 apps/mfe1-e2e/cypress.json delete mode 100644 apps/mfe1-e2e/eslint.config.mjs delete mode 100644 apps/mfe1-e2e/project.json delete mode 100644 apps/mfe1-e2e/src/fixtures/example.json delete mode 100644 apps/mfe1-e2e/src/integration/app.spec.ts delete mode 100644 apps/mfe1-e2e/src/plugins/index.js delete mode 100644 apps/mfe1-e2e/src/support/app.po.ts delete mode 100644 apps/mfe1-e2e/src/support/commands.ts delete mode 100644 apps/mfe1-e2e/src/support/index.ts delete mode 100644 apps/mfe1-e2e/tsconfig.json delete mode 100644 apps/mfe1/eslint.config.mjs delete mode 100644 apps/mfe1/federation.config.js delete mode 100644 apps/mfe1/jest.config.ts delete mode 100644 apps/mfe1/project.json delete mode 100644 apps/mfe1/src/app/app.component.css delete mode 100644 apps/mfe1/src/app/app.component.html delete mode 100644 apps/mfe1/src/app/app.component.spec.ts delete mode 100644 apps/mfe1/src/app/app.component.ts delete mode 100644 apps/mfe1/src/app/app.module.ts delete mode 100644 apps/mfe1/src/app/demo/demo.component.css delete mode 100644 apps/mfe1/src/app/demo/demo.component.html delete mode 100644 apps/mfe1/src/app/demo/demo.component.spec.ts delete mode 100644 apps/mfe1/src/app/demo/demo.component.ts delete mode 100644 apps/mfe1/src/app/nx-welcome.component.ts delete mode 100644 apps/mfe1/src/assets/.gitkeep delete mode 100644 apps/mfe1/src/bootstrap.ts delete mode 100644 apps/mfe1/src/environments/environment.prod.ts delete mode 100644 apps/mfe1/src/environments/environment.ts delete mode 100644 apps/mfe1/src/favicon.ico delete mode 100644 apps/mfe1/src/index.html delete mode 100644 apps/mfe1/src/main.ts delete mode 100644 apps/mfe1/src/polyfills.ts delete mode 100644 apps/mfe1/src/styles.css delete mode 100644 apps/mfe1/src/test-setup.ts delete mode 100644 apps/mfe1/tsconfig.app.json delete mode 100644 apps/mfe1/tsconfig.editor.json delete mode 100644 apps/mfe1/tsconfig.json delete mode 100644 apps/mfe1/tsconfig.spec.json delete mode 100644 apps/mfe2-e2e/cypress.json delete mode 100644 apps/mfe2-e2e/eslint.config.mjs delete mode 100644 apps/mfe2-e2e/project.json delete mode 100644 apps/mfe2-e2e/src/fixtures/example.json delete mode 100644 apps/mfe2-e2e/src/integration/app.spec.ts delete mode 100644 apps/mfe2-e2e/src/plugins/index.js delete mode 100644 apps/mfe2-e2e/src/support/app.po.ts delete mode 100644 apps/mfe2-e2e/src/support/commands.ts delete mode 100644 apps/mfe2-e2e/src/support/index.ts delete mode 100644 apps/mfe2-e2e/tsconfig.json delete mode 100644 apps/mfe2/eslint.config.mjs delete mode 100644 apps/mfe2/federation.config.js delete mode 100644 apps/mfe2/jest.config.ts delete mode 100644 apps/mfe2/project.json delete mode 100644 apps/mfe2/src/app/app.component.css delete mode 100644 apps/mfe2/src/app/app.component.html delete mode 100644 apps/mfe2/src/app/app.component.spec.ts delete mode 100644 apps/mfe2/src/app/app.component.ts delete mode 100644 apps/mfe2/src/app/app.module.ts delete mode 100644 apps/mfe2/src/app/nx-welcome.component.ts delete mode 100644 apps/mfe2/src/assets/.gitkeep delete mode 100644 apps/mfe2/src/assets/federation.manifest.json delete mode 100644 apps/mfe2/src/bootstrap.ts delete mode 100644 apps/mfe2/src/environments/environment.prod.ts delete mode 100644 apps/mfe2/src/environments/environment.ts delete mode 100644 apps/mfe2/src/favicon.ico delete mode 100644 apps/mfe2/src/index.html delete mode 100644 apps/mfe2/src/main.ts delete mode 100644 apps/mfe2/src/polyfills.ts delete mode 100644 apps/mfe2/src/styles.css delete mode 100644 apps/mfe2/src/test-setup.ts delete mode 100644 apps/mfe2/tsconfig.app.json delete mode 100644 apps/mfe2/tsconfig.editor.json delete mode 100644 apps/mfe2/tsconfig.json delete mode 100644 apps/mfe2/tsconfig.spec.json delete mode 100644 apps/native-federation-e2e/eslint.config.mjs delete mode 100644 apps/native-federation-e2e/jest.config.ts delete mode 100644 apps/native-federation-e2e/project.json delete mode 100644 apps/native-federation-e2e/tests/native-federation.spec.ts delete mode 100644 apps/native-federation-e2e/tsconfig.json delete mode 100644 apps/native-federation-e2e/tsconfig.spec.json delete mode 100644 apps/playground-e2e/cypress.json delete mode 100644 apps/playground-e2e/eslint.config.mjs delete mode 100644 apps/playground-e2e/project.json delete mode 100644 apps/playground-e2e/src/fixtures/example.json delete mode 100644 apps/playground-e2e/src/integration/app.spec.ts delete mode 100644 apps/playground-e2e/src/plugins/index.js delete mode 100644 apps/playground-e2e/src/support/app.po.ts delete mode 100644 apps/playground-e2e/src/support/commands.ts delete mode 100644 apps/playground-e2e/src/support/index.ts delete mode 100644 apps/playground-e2e/tsconfig.e2e.json delete mode 100644 apps/playground-e2e/tsconfig.json delete mode 100644 apps/playground/eslint.config.mjs delete mode 100644 apps/playground/federation.config.js delete mode 100644 apps/playground/jest.config.ts delete mode 100644 apps/playground/project.json delete mode 100644 apps/playground/src/app/app.component.css delete mode 100644 apps/playground/src/app/app.component.html delete mode 100644 apps/playground/src/app/app.component.spec.ts delete mode 100644 apps/playground/src/app/app.component.ts delete mode 100644 apps/playground/src/app/app.module.ts delete mode 100644 apps/playground/src/assets/.gitkeep delete mode 100644 apps/playground/src/bootstrap.ts delete mode 100644 apps/playground/src/environments/environment.prod.ts delete mode 100644 apps/playground/src/environments/environment.ts delete mode 100644 apps/playground/src/favicon.ico delete mode 100644 apps/playground/src/index.html delete mode 100644 apps/playground/src/main.ts delete mode 100644 apps/playground/src/polyfills.ts delete mode 100644 apps/playground/src/styles.css delete mode 100644 apps/playground/src/test-setup.ts delete mode 100644 apps/playground/tsconfig.app.json delete mode 100644 apps/playground/tsconfig.editor.json delete mode 100644 apps/playground/tsconfig.json delete mode 100644 apps/playground/tsconfig.spec.json delete mode 100644 error.png delete mode 100644 libs/native-federation-core/LICENSE delete mode 100644 libs/native-federation-core/README.md delete mode 100644 libs/native-federation-core/build.ts delete mode 100644 libs/native-federation-core/eslint.config.mjs delete mode 100644 libs/native-federation-core/jest.config.ts delete mode 100644 libs/native-federation-core/package.json delete mode 100644 libs/native-federation-core/project.json delete mode 100644 libs/native-federation-core/src/build.ts delete mode 100644 libs/native-federation-core/src/config.ts delete mode 100644 libs/native-federation-core/src/index.ts delete mode 100644 libs/native-federation-core/src/lib/config/configuration-context.ts delete mode 100644 libs/native-federation-core/src/lib/config/federation-config.ts delete mode 100644 libs/native-federation-core/src/lib/config/share-utils.ts delete mode 100644 libs/native-federation-core/src/lib/config/with-native-federation.ts delete mode 100644 libs/native-federation-core/src/lib/core/build-adapter.d.ts delete mode 100644 libs/native-federation-core/src/lib/core/build-adapter.ts delete mode 100644 libs/native-federation-core/src/lib/core/build-for-federation.ts delete mode 100644 libs/native-federation-core/src/lib/core/bundle-exposed-and-mappings.ts delete mode 100644 libs/native-federation-core/src/lib/core/bundle-shared.ts delete mode 100644 libs/native-federation-core/src/lib/core/default-external-list.ts delete mode 100644 libs/native-federation-core/src/lib/core/default-server-deps-list.ts delete mode 100644 libs/native-federation-core/src/lib/core/default-skip-list.ts delete mode 100644 libs/native-federation-core/src/lib/core/federation-builder.ts delete mode 100644 libs/native-federation-core/src/lib/core/federation-options.ts delete mode 100644 libs/native-federation-core/src/lib/core/get-externals.ts delete mode 100644 libs/native-federation-core/src/lib/core/load-federation-config.ts delete mode 100644 libs/native-federation-core/src/lib/core/remove-unused-deps.ts delete mode 100644 libs/native-federation-core/src/lib/core/write-federation-info.ts delete mode 100644 libs/native-federation-core/src/lib/core/write-import-map.ts delete mode 100644 libs/native-federation-core/src/lib/utils/build-result-map.ts delete mode 100644 libs/native-federation-core/src/lib/utils/build-utils.ts delete mode 100644 libs/native-federation-core/src/lib/utils/bundle-caching.ts delete mode 100644 libs/native-federation-core/src/lib/utils/config-utils.ts delete mode 100644 libs/native-federation-core/src/lib/utils/errors.ts delete mode 100644 libs/native-federation-core/src/lib/utils/get-external-imports.ts delete mode 100644 libs/native-federation-core/src/lib/utils/hash-file.ts delete mode 100644 libs/native-federation-core/src/lib/utils/logger.d.ts delete mode 100644 libs/native-federation-core/src/lib/utils/logger.ts delete mode 100644 libs/native-federation-core/src/lib/utils/mapped-paths.ts delete mode 100644 libs/native-federation-core/src/lib/utils/normalize.ts delete mode 100644 libs/native-federation-core/src/lib/utils/package-info.ts delete mode 100644 libs/native-federation-core/src/lib/utils/rebuild-queue.ts delete mode 100644 libs/native-federation-core/src/lib/utils/resolve-glob.ts delete mode 100644 libs/native-federation-core/src/lib/utils/resolve-wildcard-keys.ts delete mode 100644 libs/native-federation-core/src/lib/utils/rewrite-chunk-imports.ts delete mode 100644 libs/native-federation-core/stack.png delete mode 100644 libs/native-federation-core/tsconfig.json delete mode 100644 libs/native-federation-core/tsconfig.lib.json delete mode 100644 libs/native-federation-core/tsconfig.spec.json delete mode 100644 libs/native-federation-esbuild/LICENSE delete mode 100644 libs/native-federation-esbuild/README.md delete mode 100644 libs/native-federation-esbuild/eslint.config.mjs delete mode 100644 libs/native-federation-esbuild/jest.config.ts delete mode 100644 libs/native-federation-esbuild/package.json delete mode 100644 libs/native-federation-esbuild/project.json delete mode 100644 libs/native-federation-esbuild/src/index.ts delete mode 100644 libs/native-federation-esbuild/src/lib/adapter.ts delete mode 100644 libs/native-federation-esbuild/src/lib/collect-exports.ts delete mode 100644 libs/native-federation-esbuild/src/lib/react-replacements.ts delete mode 100644 libs/native-federation-esbuild/tsconfig.json delete mode 100644 libs/native-federation-esbuild/tsconfig.lib.json delete mode 100644 libs/native-federation-esbuild/tsconfig.spec.json delete mode 100644 libs/native-federation-node/LICENSE delete mode 100644 libs/native-federation-node/README.md delete mode 100644 libs/native-federation-node/build/create-data-url.js delete mode 100644 libs/native-federation-node/eslint.config.mjs delete mode 100644 libs/native-federation-node/jest.config.ts delete mode 100644 libs/native-federation-node/package.json delete mode 100644 libs/native-federation-node/project.json delete mode 100644 libs/native-federation-node/src/index.ts delete mode 100644 libs/native-federation-node/src/lib/node/init-node-federation.ts delete mode 100644 libs/native-federation-node/src/lib/utils/fstart-args-parser.ts delete mode 100644 libs/native-federation-node/src/lib/utils/fstart.mjs delete mode 100644 libs/native-federation-node/src/lib/utils/fstart.ts delete mode 100644 libs/native-federation-node/src/lib/utils/import-map-loader.js delete mode 100644 libs/native-federation-node/src/lib/utils/loader-as-data-url.js delete mode 100644 libs/native-federation-node/tsconfig.json delete mode 100644 libs/native-federation-node/tsconfig.lib.json delete mode 100644 libs/native-federation-node/tsconfig.spec.json delete mode 100644 libs/native-federation-runtime/LICENSE delete mode 100644 libs/native-federation-runtime/README.md delete mode 100644 libs/native-federation-runtime/eslint.config.mjs delete mode 100644 libs/native-federation-runtime/ng-package.json delete mode 100644 libs/native-federation-runtime/package-lock.json delete mode 100644 libs/native-federation-runtime/package.json delete mode 100644 libs/native-federation-runtime/project.json delete mode 100644 libs/native-federation-runtime/public/mockServiceWorker.js delete mode 100644 libs/native-federation-runtime/src/index.ts delete mode 100644 libs/native-federation-runtime/src/lib/__test-helpers__/dom-helpers.ts delete mode 100644 libs/native-federation-runtime/src/lib/__test-helpers__/federation-fixtures.ts delete mode 100644 libs/native-federation-runtime/src/lib/__test-helpers__/module-fixtures.ts delete mode 100644 libs/native-federation-runtime/src/lib/__test-helpers__/msw-handlers.ts delete mode 100644 libs/native-federation-runtime/src/lib/get-shared.ts delete mode 100644 libs/native-federation-runtime/src/lib/init-federation.integration.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/init-federation.ts delete mode 100644 libs/native-federation-runtime/src/lib/load-remote-module.integration.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/load-remote-module.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/build-notifications-options.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/externals.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/externals.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/federation-info.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/global-cache.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/import-map.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/import-map.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/remotes.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/model/remotes.ts delete mode 100644 libs/native-federation-runtime/src/lib/test-setup.ts delete mode 100644 libs/native-federation-runtime/src/lib/utils/add-import-map.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/utils/add-import-map.ts delete mode 100644 libs/native-federation-runtime/src/lib/utils/path-utils.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/utils/path-utils.ts delete mode 100644 libs/native-federation-runtime/src/lib/utils/trusted-types.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/utils/trusted-types.ts delete mode 100644 libs/native-federation-runtime/src/lib/watch-federation-build.spec.ts delete mode 100644 libs/native-federation-runtime/src/lib/watch-federation-build.ts delete mode 100644 libs/native-federation-runtime/src/test-setup.ts delete mode 100644 libs/native-federation-runtime/tsconfig.json delete mode 100644 libs/native-federation-runtime/tsconfig.lib.json delete mode 100644 libs/native-federation-runtime/tsconfig.lib.prod.json delete mode 100644 libs/native-federation-runtime/tsconfig.spec.json delete mode 100644 libs/native-federation-runtime/vite.config.mts delete mode 100644 libs/native-federation-runtime/vitest.config.mts delete mode 100644 libs/native-federation/LICENSE delete mode 100644 libs/native-federation/README.md delete mode 100644 libs/native-federation/build/create-data-url.js delete mode 100644 libs/native-federation/builders.json delete mode 100644 libs/native-federation/collection.json delete mode 100644 libs/native-federation/config.ts delete mode 100644 libs/native-federation/demo-repo.png delete mode 100644 libs/native-federation/docs/migrate.md delete mode 100644 libs/native-federation/docs/share-faq.md delete mode 100644 libs/native-federation/docs/update18.md delete mode 100644 libs/native-federation/eslint.config.mjs delete mode 100644 libs/native-federation/example.png delete mode 100644 libs/native-federation/executors.json delete mode 100644 libs/native-federation/generators.json delete mode 100644 libs/native-federation/jest.config.ts delete mode 100644 libs/native-federation/migrate-appbuilder.md delete mode 100644 libs/native-federation/migration-collection.json delete mode 100644 libs/native-federation/package.json delete mode 100644 libs/native-federation/post-build.js delete mode 100644 libs/native-federation/project.json delete mode 100644 libs/native-federation/src/builders/build/builder.ts delete mode 100644 libs/native-federation/src/builders/build/federation-build-notifier.ts delete mode 100644 libs/native-federation/src/builders/build/schema.d.ts delete mode 100644 libs/native-federation/src/builders/build/schema.json delete mode 100644 libs/native-federation/src/builders/serve/builder.ts.bak delete mode 100644 libs/native-federation/src/builders/serve/schema.json.bak delete mode 100644 libs/native-federation/src/config.ts delete mode 100644 libs/native-federation/src/executors/build/executor.spec.ts delete mode 100644 libs/native-federation/src/executors/build/executor.ts delete mode 100644 libs/native-federation/src/executors/build/schema.d.ts delete mode 100644 libs/native-federation/src/executors/build/schema.json delete mode 100644 libs/native-federation/src/generators/native-federation/files/src/index.ts__template__ delete mode 100644 libs/native-federation/src/generators/native-federation/generator.spec.ts delete mode 100644 libs/native-federation/src/generators/native-federation/generator.ts delete mode 100644 libs/native-federation/src/generators/native-federation/schema.d.ts delete mode 100644 libs/native-federation/src/generators/native-federation/schema.json delete mode 100644 libs/native-federation/src/index.ts delete mode 100644 libs/native-federation/src/patch-angular-build.ts delete mode 100644 libs/native-federation/src/plugin/dev-externals-mixin.ts delete mode 100644 libs/native-federation/src/plugin/externals-skip-list.ts delete mode 100644 libs/native-federation/src/plugin/index.ts delete mode 100644 libs/native-federation/src/schematics/appbuilder/schema.d.ts delete mode 100644 libs/native-federation/src/schematics/appbuilder/schema.json delete mode 100644 libs/native-federation/src/schematics/appbuilder/schematic.ts delete mode 100644 libs/native-federation/src/schematics/init/files/federation.config.js__tmpl__ delete mode 100644 libs/native-federation/src/schematics/init/schema.d.ts delete mode 100644 libs/native-federation/src/schematics/init/schema.json delete mode 100644 libs/native-federation/src/schematics/init/schematic.ts delete mode 100644 libs/native-federation/src/schematics/remove/schema.d.ts delete mode 100644 libs/native-federation/src/schematics/remove/schema.json delete mode 100644 libs/native-federation/src/schematics/remove/schematic.ts delete mode 100644 libs/native-federation/src/schematics/update18/schema.json delete mode 100644 libs/native-federation/src/schematics/update18/schematic.ts delete mode 100644 libs/native-federation/src/tools/fstart-as-data-url.ts delete mode 100644 libs/native-federation/src/utils/angular-esbuild-adapter.ts delete mode 100644 libs/native-federation/src/utils/angular-locales.ts delete mode 100644 libs/native-federation/src/utils/coverage-instrumentation.ts delete mode 100644 libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts delete mode 100644 libs/native-federation/src/utils/create-compiler-options.ts delete mode 100644 libs/native-federation/src/utils/event-sorce.ts delete mode 100644 libs/native-federation/src/utils/i18n.spec.ts delete mode 100644 libs/native-federation/src/utils/i18n.ts delete mode 100644 libs/native-federation/src/utils/mem-resuts.ts delete mode 100644 libs/native-federation/src/utils/patch-angular-build.ts delete mode 100644 libs/native-federation/src/utils/rebuild-events.ts delete mode 100644 libs/native-federation/src/utils/shared-mappings-plugin.ts delete mode 100644 libs/native-federation/src/utils/updateIndexHtml.ts delete mode 100644 libs/native-federation/tsconfig.json delete mode 100644 libs/native-federation/tsconfig.lib.json delete mode 100644 libs/native-federation/tsconfig.spec.json delete mode 100644 serve.js delete mode 100644 update-local-nf.sh 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..6e6a03fc 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,20 @@ # @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) -
+## Local Development
Test Library on external repository @@ -39,24 +27,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 +47,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/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 deleted file mode 100644 index 66525c81..00000000 --- a/apps/mfe1/eslint.config.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import baseConfig from '../../eslint.config.mjs'; -import nx from '@nx/eslint-plugin'; - -export default [ - ...baseConfig, - ...nx.configs['flat/angular'], - { - files: ['**/*.ts'], - rules: { - '@angular-eslint/directive-selector': [ - 'error', - { - type: 'attribute', - prefix: 'angularArchitects', - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'error', - { - type: 'element', - prefix: 'angular-architects', - style: 'kebab-case', - }, - ], - '@angular-eslint/prefer-standalone': 'off', - }, - }, - ...nx.configs['flat/angular-template'], -]; 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 deleted file mode 100644 index c9c97684..00000000 --- a/apps/mfe1/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'mfe1', - preset: '../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../coverage/apps/mfe1', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - 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/mfe1/project.json b/apps/mfe1/project.json deleted file mode 100644 index 930d54f0..00000000 --- a/apps/mfe1/project.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "name": "mfe1", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/mfe1/src", - "prefix": "angular-architects", - "tags": [], - "targets": { - "build": { - "executor": "@angular-architects/native-federation:build", - "outputs": ["{options.outputPath}"], - "defaultConfiguration": "production", - "options": { - "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"], - "styles": ["apps/mfe1/src/styles.css"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "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", - "defaultConfiguration": "development", - "continuous": true, - "configurations": { - "production": { - "buildTarget": "mfe1:build:production" - }, - "development": { - "buildTarget": "mfe1:build:development" - } - } - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "mfe1:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "options": { - "lintFilePatterns": ["apps/mfe1/**/*.ts", "apps/mfe1/**/*.html"] - } - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/apps/mfe1"], - "options": { - "jestConfig": "apps/mfe1/jest.config.ts" - } - } - } -} 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 deleted file mode 100644 index 2ebd64a2..00000000 --- a/apps/mfe1/src/app/app.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'angular-architects-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'], - standalone: false, -}) -export class AppComponent { - title = 'mfe1'; -} 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/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/nx-welcome.component.ts b/apps/mfe1/src/app/nx-welcome.component.ts deleted file mode 100644 index 72f9a9bb..00000000 --- a/apps/mfe1/src/app/nx-welcome.component.ts +++ /dev/null @@ -1,930 +0,0 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; - -/* eslint-disable */ - -@Component({ - selector: 'angular-architects-nx-welcome', - template: ` - - -
-
- -
-

- Hello there, - Welcome mfe1 👋 -

-
- - -
-
-

- - - - You're up and running -

- What's next? -
-
- - - -
-
- - - - - -
-

Next steps

-

Here are some things you can do with Nx:

-
- - - - - Add UI library - -
# 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 deleted file mode 100644 index d9a2e7e4..00000000 --- a/apps/mfe1/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/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 317ebcb2336e0833a22dddf0ab287849f26fda57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 - - - - Mfe1 - - - - - - - - diff --git a/apps/mfe1/src/main.ts b/apps/mfe1/src/main.ts deleted file mode 100644 index 4d31b464..00000000 --- a/apps/mfe1/src/main.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { initFederation } from '@angular-architects/native-federation'; - -initFederation() - .then(() => import('./bootstrap')) - .catch((err) => console.error(err)); diff --git a/apps/mfe1/src/polyfills.ts b/apps/mfe1/src/polyfills.ts deleted file mode 100644 index 1685a007..00000000 --- a/apps/mfe1/src/polyfills.ts +++ /dev/null @@ -1,53 +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 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'; diff --git a/apps/mfe1/src/styles.css b/apps/mfe1/src/styles.css deleted file mode 100644 index 90d4ee00..00000000 --- a/apps/mfe1/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/mfe1/src/test-setup.ts b/apps/mfe1/src/test-setup.ts deleted file mode 100644 index 4555f138..00000000 --- a/apps/mfe1/src/test-setup.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; -setupZoneTestEnv(); diff --git a/apps/mfe1/tsconfig.app.json b/apps/mfe1/tsconfig.app.json deleted file mode 100644 index 7d7a8070..00000000 --- a/apps/mfe1/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": ["**/*.test.ts", "**/*.spec.ts", "jest.config.ts"] -} diff --git a/apps/mfe1/tsconfig.editor.json b/apps/mfe1/tsconfig.editor.json deleted file mode 100644 index 20c4afdb..00000000 --- a/apps/mfe1/tsconfig.editor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.ts"], - "compilerOptions": { - "types": ["jest", "node"] - } -} diff --git a/apps/mfe1/tsconfig.json b/apps/mfe1/tsconfig.json deleted file mode 100644 index c756d846..00000000 --- a/apps/mfe1/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], - "compilerOptions": { - "target": "es2020", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/mfe1/tsconfig.spec.json b/apps/mfe1/tsconfig.spec.json deleted file mode 100644 index c5db0277..00000000 --- a/apps/mfe1/tsconfig.spec.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "files": ["src/test-setup.ts"], - "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] -} 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 deleted file mode 100644 index 66525c81..00000000 --- a/apps/mfe2/eslint.config.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import baseConfig from '../../eslint.config.mjs'; -import nx from '@nx/eslint-plugin'; - -export default [ - ...baseConfig, - ...nx.configs['flat/angular'], - { - files: ['**/*.ts'], - rules: { - '@angular-eslint/directive-selector': [ - 'error', - { - type: 'attribute', - prefix: 'angularArchitects', - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'error', - { - type: 'element', - prefix: 'angular-architects', - style: 'kebab-case', - }, - ], - '@angular-eslint/prefer-standalone': 'off', - }, - }, - ...nx.configs['flat/angular-template'], -]; 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 deleted file mode 100644 index 21106286..00000000 --- a/apps/mfe2/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'mfe2', - preset: '../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../coverage/apps/mfe2', - transform: { - '^.+\\.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - 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/mfe2/project.json b/apps/mfe2/project.json deleted file mode 100644 index fc9fc1ee..00000000 --- a/apps/mfe2/project.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "name": "mfe2", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/mfe2/src", - "prefix": "angular-architects", - "tags": [], - "targets": { - "build": { - "executor": "@angular-architects/native-federation:build", - "outputs": ["{options.outputPath}"], - "defaultConfiguration": "production", - "options": { - "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"], - "styles": ["apps/mfe2/src/styles.css"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "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", - "defaultConfiguration": "development", - "continuous": true, - "options": {}, - "configurations": { - "production": { - "buildTarget": "mfe2:build:production" - }, - "development": { - "buildTarget": "mfe2:build:development" - } - } - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "mfe2:build" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "options": { - "lintFilePatterns": ["apps/mfe2/**/*.ts", "apps/mfe2/**/*.html"] - } - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/apps/mfe2"], - "options": { - "jestConfig": "apps/mfe2/jest.config.ts" - } - } - } -} 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 deleted file mode 100644 index ac9076ef..00000000 --- a/apps/mfe2/src/app/app.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'angular-architects-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'], - standalone: false, -}) -export class AppComponent { - title = 'mfe2'; -} 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/nx-welcome.component.ts b/apps/mfe2/src/app/nx-welcome.component.ts deleted file mode 100644 index e76ebd96..00000000 --- a/apps/mfe2/src/app/nx-welcome.component.ts +++ /dev/null @@ -1,930 +0,0 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; - -/* eslint-disable */ - -@Component({ - selector: 'angular-architects-nx-welcome', - template: ` - - -
-
- -
-

- Hello there, - Welcome mfe2 👋 -

-
- - -
-
-

- - - - You're up and running -

- What's next? -
-
- - - -
-
- - - - - -
-

Next steps

-

Here are some things you can do with Nx:

-
- - - - - Add UI library - -
# 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 deleted file mode 100644 index d9a2e7e4..00000000 --- a/apps/mfe2/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/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 317ebcb2336e0833a22dddf0ab287849f26fda57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 - - - - Mfe2 - - - - - - - - diff --git a/apps/mfe2/src/main.ts b/apps/mfe2/src/main.ts deleted file mode 100644 index 0a52b61e..00000000 --- a/apps/mfe2/src/main.ts +++ /dev/null @@ -1,6 +0,0 @@ -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)); diff --git a/apps/mfe2/src/polyfills.ts b/apps/mfe2/src/polyfills.ts deleted file mode 100644 index ccd8b2f4..00000000 --- a/apps/mfe2/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 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'; diff --git a/apps/mfe2/src/styles.css b/apps/mfe2/src/styles.css deleted file mode 100644 index 90d4ee00..00000000 --- a/apps/mfe2/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/mfe2/src/test-setup.ts b/apps/mfe2/src/test-setup.ts deleted file mode 100644 index 4555f138..00000000 --- a/apps/mfe2/src/test-setup.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; -setupZoneTestEnv(); diff --git a/apps/mfe2/tsconfig.app.json b/apps/mfe2/tsconfig.app.json deleted file mode 100644 index 7d7a8070..00000000 --- a/apps/mfe2/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": ["**/*.test.ts", "**/*.spec.ts", "jest.config.ts"] -} diff --git a/apps/mfe2/tsconfig.editor.json b/apps/mfe2/tsconfig.editor.json deleted file mode 100644 index 20c4afdb..00000000 --- a/apps/mfe2/tsconfig.editor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.ts"], - "compilerOptions": { - "types": ["jest", "node"] - } -} diff --git a/apps/mfe2/tsconfig.json b/apps/mfe2/tsconfig.json deleted file mode 100644 index c756d846..00000000 --- a/apps/mfe2/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], - "compilerOptions": { - "target": "es2020", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/apps/mfe2/tsconfig.spec.json b/apps/mfe2/tsconfig.spec.json deleted file mode 100644 index c5db0277..00000000 --- a/apps/mfe2/tsconfig.spec.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "files": ["src/test-setup.ts"], - "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] -} 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/eslint.config.mjs b/apps/playground/eslint.config.mjs deleted file mode 100644 index 66525c81..00000000 --- a/apps/playground/eslint.config.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import baseConfig from '../../eslint.config.mjs'; -import nx from '@nx/eslint-plugin'; - -export default [ - ...baseConfig, - ...nx.configs['flat/angular'], - { - files: ['**/*.ts'], - rules: { - '@angular-eslint/directive-selector': [ - 'error', - { - type: 'attribute', - prefix: 'angularArchitects', - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'error', - { - type: 'element', - prefix: 'angular-architects', - style: 'kebab-case', - }, - ], - '@angular-eslint/prefer-standalone': 'off', - }, - }, - ...nx.configs['flat/angular-template'], -]; 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/jest.config.ts b/apps/playground/jest.config.ts deleted file mode 100644 index 1ce534fd..00000000 --- a/apps/playground/jest.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'playground', - 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', - ], - transform: { - '^.+.(ts|mjs|js|html)$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.(html|svg)$', - }, - ], - }, - transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'], -}; 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 317ebcb2336e0833a22dddf0ab287849f26fda57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI332;U^%p|z7g|#(P)qFEA@4f!_@qOK2 z_lJl}!lhL!VT_U|uN7%8B2iKH??xhDa;*`g{yjTFWHvXn;2s{4R7kH|pKGdy(7z!K zgftM+Ku7~24TLlh(!g)gz|foI94G^t2^IO$uvX$3(OR0<_5L2sB)lMAMy|+`xodJ{ z_Uh_1m)~h?a;2W{dmhM;u!YGo=)OdmId_B<%^V^{ovI@y`7^g1_V9G}*f# zNzAtvou}I!W1#{M^@ROc(BZ! z+F!!_aR&Px3_reO(EW+TwlW~tv*2zr?iP7(d~a~yA|@*a89IUke+c472NXM0wiX{- zl`UrZC^1XYyf%1u)-Y)jj9;MZ!SLfd2Hl?o|80Su%Z?To_=^g_Jt0oa#CT*tjx>BI z16wec&AOWNK<#i0Qd=1O$fymLRoUR*%;h@*@v7}wApDl^w*h}!sYq%kw+DKDY)@&A z@9$ULEB3qkR#85`lb8#WZw=@})#kQig9oqy^I$dj&k4jU&^2(M3q{n1AKeGUKPFbr z1^<)aH;VsG@J|B&l>UtU#Ejv3GIqERzYgL@UOAWtW<{p#zy`WyJgpCy8$c_e%wYJL zyGHRRx38)HyjU3y{-4z6)pzb>&Q1pR)B&u01F-|&Gx4EZWK$nkUkOI|(D4UHOXg_- zw{OBf!oWQUn)Pe(=f=nt=zkmdjpO^o8ZZ9o_|4tW1ni+Un9iCW47*-ut$KQOww!;u z`0q)$s6IZO!~9$e_P9X!hqLxu`fpcL|2f^I5d4*a@Dq28;@2271v_N+5HqYZ>x;&O z05*7JT)mUe&%S0@UD)@&8SmQrMtsDfZT;fkdA!r(S=}Oz>iP)w=W508=Rc#nNn7ym z1;42c|8($ALY8#a({%1#IXbWn9-Y|0eDY$_L&j{63?{?AH{);EzcqfydD$@-B`Y3<%IIj7S7rK_N}je^=dEk%JQ4c z!tBdTPE3Tse;oYF>cnrapWq*o)m47X1`~6@(!Y29#>-#8zm&LXrXa(3=7Z)ElaQqj z-#0JJy3Fi(C#Rx(`=VXtJ63E2_bZGCz+QRa{W0e2(m3sI?LOcUBx)~^YCqZ{XEPX)C>G>U4tfqeH8L(3|pQR*zbL1 zT9e~4Tb5p9_G}$y4t`i*4t_Mr9QYvL9C&Ah*}t`q*}S+VYh0M6GxTTSXI)hMpMpIq zD1ImYqJLzbj0}~EpE-aH#VCH_udYEW#`P2zYmi&xSPs_{n6tBj=MY|-XrA;SGA_>y zGtU$?HXm$gYj*!N)_nQ59%lQdXtQZS3*#PC-{iB_sm+ytD*7j`D*k(P&IH2GHT}Eh z5697eQECVIGQAUe#eU2I!yI&%0CP#>%6MWV z@zS!p@+Y1i1b^QuuEF*13CuB zu69dve5k7&Wgb+^s|UB08Dr3u`h@yM0NTj4h7MnHo-4@xmyr7(*4$rpPwsCDZ@2be zRz9V^GnV;;?^Lk%ynzq&K(Aix`mWmW`^152Hoy$CTYVehpD-S1-W^#k#{0^L`V6CN+E z!w+xte;2vu4AmVNEFUOBmrBL>6MK@!O2*N|2=d|Y;oN&A&qv=qKn73lDD zI(+oJAdgv>Yr}8(&@ZuAZE%XUXmX(U!N+Z_sjL<1vjy1R+1IeHt`79fnYdOL{$ci7 z%3f0A*;Zt@ED&Gjm|OFTYBDe%bbo*xXAQsFz+Q`fVBH!N2)kaxN8P$c>sp~QXnv>b zwq=W3&Mtmih7xkR$YA)1Yi?avHNR6C99!u6fh=cL|KQ&PwF!n@ud^n(HNIImHD!h87!i*t?G|p0o+eelJ?B@A64_9%SBhNaJ64EvKgD&%LjLCYnNfc; znj?%*p@*?dq#NqcQFmmX($wms@CSAr9#>hUR^=I+=0B)vvGX%T&#h$kmX*s=^M2E!@N9#m?LhMvz}YB+kd zG~mbP|D(;{s_#;hsKK9lbVK&Lo734x7SIFJ9V_}2$@q?zm^7?*XH94w5Qae{7zOMUF z^?%F%)c1Y)Q?Iy?I>knw*8gYW#ok|2gdS=YYZLiD=CW|Nj;n^x!=S#iJ#`~Ld79+xXpVmUK^B(xO_vO!btA9y7w3L3-0j-y4 z?M-V{%z;JI`bk7yFDcP}OcCd*{Q9S5$iGA7*E1@tfkyjAi!;wP^O71cZ^Ep)qrQ)N z#wqw0_HS;T7x3y|`P==i3hEwK%|>fZ)c&@kgKO1~5<5xBSk?iZV?KI6&i72H6S9A* z=U(*e)EqEs?Oc04)V-~K5AUmh|62H4*`UAtItO$O(q5?6jj+K^oD!04r=6#dsxp?~}{`?&sXn#q2 zGuY~7>O2=!u@@Kfu7q=W*4egu@qPMRM>(eyYyaIE<|j%d=iWNdGsx%c!902v#ngNg z@#U-O_4xN$s_9?(`{>{>7~-6FgWpBpqXb`Ydc3OFL#&I}Irse9F_8R@4zSS*Y*o*B zXL?6*Aw!AfkNCgcr#*yj&p3ZDe2y>v$>FUdKIy_2N~}6AbHc7gA3`6$g@1o|dE>vz z4pl(j9;kyMsjaw}lO?(?Xg%4k!5%^t#@5n=WVc&JRa+XT$~#@rldvN3S1rEpU$;XgxVny7mki3 z-Hh|jUCHrUXuLr!)`w>wgO0N%KTB-1di>cj(x3Bav`7v z3G7EIbU$z>`Nad7Rk_&OT-W{;qg)-GXV-aJT#(ozdmnA~Rq3GQ_3mby(>q6Ocb-RgTUhTN)))x>m&eD;$J5Bg zo&DhY36Yg=J=$Z>t}RJ>o|@hAcwWzN#r(WJ52^g$lh^!63@hh+dR$&_dEGu&^CR*< z!oFqSqO@>xZ*nC2oiOd0eS*F^IL~W-rsrO`J`ej{=ou_q^_(<$&-3f^J z&L^MSYWIe{&pYq&9eGaArA~*kA - - - - Playground - - - - - - - - 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.editor.json b/apps/playground/tsconfig.editor.json deleted file mode 100644 index 1bf3c0a7..00000000 --- a/apps/playground/tsconfig.editor.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.ts"], - "compilerOptions": { - "types": ["jest", "node"] - }, - "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/tsconfig.spec.json b/apps/playground/tsconfig.spec.json deleted file mode 100644 index b6347c6f..00000000 --- a/apps/playground/tsconfig.spec.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "files": ["src/test-setup.ts"], - "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"] -} diff --git a/error.png b/error.png deleted file mode 100644 index b0586aa4b41e945bdfbe52d3203ff0fcccd9acab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115482 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 z zd_XljrzA$xrK>Y1lk|gA`z#oq+F*BcjmO~6LYuI1PGNIo)X zRl3(>l}=+q!Y1yS54+ysx?9@DUUsSnk$*0ttQRx=!4sP_B}uQ<9!Ouke<3Ar-z?kM z)4I4y+xc)oa^K8#qK!y97BEfq1a>bIxY=z+}&& z`k!P|E!B4vQ^oFlaM>1Ux!M|?W$jR^u8b29XBwV{X>A*?3w(l@b@}^wc-;Eh8Pgh2 zBox^x2L-1qUsmOzq16j#_DDnZWGl~()&_KQD62ca52qf{N35s?>Idw%L{I2t>64Ev zZ`0D4az^(`Q0Ghv5fh7;$1}eVIxFW%y)#&&-QN!jR>|3QW@Y(d_xXlZ-c?gs+AQu+ zZ|yw2VHf#GN_)wn!WX_WA#7DNsM|}v_RS$WoY}&%p96OHVNgGv#Qc$YBO%{kIVqXW z8e@2U$p9sGzfT}teLf7R?c3s~5X>WNc*{ny8II&PmkCE8W#P^racfUs;v!OZX=5_Q zKE{-qF0Vts$|*vJaM7uZs323tv2Z%&X$dzC0;}fzm<^iF1UW9$492UG^x!s*dwZg+1WEkH^+xPf_0(V}Ve*P8g4BbbdV!?G(l)u=MY9OCSPjR!qHbgFJ9o@Ah=i2t%20hDn6ic+ z7ba2*7`|uJqrrUP1HxawzRS0V0rT8~FrkU#kFi;5CLHhO-) zPufy&fI^Ysyq;$=?Gm09$f0V+IWaoSh=Oq89SIivl$f!JPnMcmnCSUE&(KqUs%LtD z%v4r%<)?nmm#Jw~TF@moWP&LkE04^-78xN};JJBECtXGF?ud-sVg4X>%|pSqk32!B zSdKF1>1Dc8^JtM=)8$d}G9bljLya~?je^5hS@#07KDCw*X_o?q@^W*nK)=9V6!wi+ zy;w$WPkLh=|F}6sE`KeLf8Vk1m3;5^%gLQ5jazLgGptx27IrVTwc4K(5Sc(GX?0*6<{@FVyck&8mE^`^%T&8`lO`1x^#C z%)=pmGj-{%wolS?GQ`;--@_@)ow8K+Mr(7+^Gxr&81mh`ml`n?9Q#)zCGE3e@167D z5du&2KBp~&(&43g&hBy$CMnS zZRmR5{Bi-yYwzndy^lHdxa9zcaesHH52AEZ%wPv$=fvZ&*GYG1v#lvyWYC)_MV^;b1tY`76FCON<9G!n_=lB}Ub`sukwqq7Pgl65{eA z^|-_RrPjs|p?fSE6Sa{ya-1rLc=XlHXvy~; zs2j|oS|dn{-@jhR%BQUqd1%|dYG?J!?yoC2w9-gdX_XR?R_Sm{8y-zkq=5WVNa$oi z<}Z!x^2n-`wAQy&wGF9!e&_iUWezgEwBq3})CY5|TgcUK@Lr~=`7f$qN80Pzb4t!Z zN8bx(r7gsqBH?4VHl+VGB;?Q)iu50<^Sjcp)d=8L+@Pv5nqdLIwgz82vV0sV24VNn zUH18&^}g==K-=IsS!BCJ-%y$9@}_(jqpaqe_NO|uy;<+u3qD$5AKLa>aOPXh|+5@36a+ z%QuBj0N8Xj=r3jWM6*PX@M(-`bbhIy8q46*U!-AT`$_2v6f+3%Grh5Jc*et$t~RC(|D|jv=f>35WKL`+Q$8E-zBI z8=EnBB?8Q)`Dt7lP@SK;%dto*W)CFkO?lRB>Ro4975@c} zoTi?e>dU_6XS4ZV`K5Xq*^H57@IOb5JrLA3$w|FG?sD3lgP1&wWkWIv4#Q?;R>X!p z*kZoyb$7m>T}S8U

`^_iELuDhH8M;}9Se(i5mmPWLbSXUvF0zey zz=8m&ty$VF&1$PZ8E(9DZ_J{$?${+wlKudM+InZX39X(UfbKyJ`0z11@~VR22oV(n z(xEn458mBwU!147U?jgM6V?^-#Yj3Wwm&aAug>mEY$LqeIXl&7-``IsR!y_@$|=5F zeYAp>JSZnx{Eqb3ieOu5{^2GuQiJ^(%gt?K5VLmbSu4}?+YAe6qjs^J4yUeJ2Hdu6 z$NXod_|>_EZbZ+^ZAOIe{-&CIW(%ZT33uIk#v_GA-rit0Dg#$;Y7v2TJgTxg@)X(I zwMJvweN9#Rxns-(JFS8TP*@9HGDoW-eRSE9>tT;WxwZ6ElXh&8-rP11)56 ztlvh?R9Ie-x{AM5Uh2Uua#)c5!|Dg?OlW?r*^IPOfLxT{5D>@7vsI%IkNS%J4S0(W zhNxuwKl=eCqNcbY_fbrfAQ$Pob`zS{(;+J=Q6b+f57*5fyTj&NS~3Uj#Z3j?28xk`XuCAO(_-SWTDuR#`sy4n}*zU&%M)Vhvn0^Y7 zROhJu6R=0?;tE2BI=j6jIo}zP4^wzfy^3c#+wT}b6$dkVgg55YSLcW1*%mRY-wKpBr6gFLzh`Z@4;%J> z{uE;@$7zurYRYRcLj7R!SX?zLKL*Nzs7Q6ze6+o0oW;F&bw>^>HHOUJpaC||%OlC6 z3ap!)v1#tFKHIev&#$yMhKhwFgjEa@^C`6ewE}2za7nH@Rn7i4r2G*~nZaJ6ve}8y z;*=~ZeiA!{a-+7E?ND0Kj|LRE6-eujx#NAyuLyzm6Q8Oh?C#Wg2Sn`QU@&_yy5Zv2 z)o|1jjIelg=q-VITdo^z0(Y3b(X?A$Ou~f8G$#fB?6Trjc2SLb!r896+ffpflKe2# zkAujEcb!aG+59!#p?JqlVRKK>iQIFTaenywFU__7N{Mn$U7n{|*%76d8?Mo`+0-!m z|J-R?zJTp+F$1r3_P<{9?JaBdY=8HHiG$5oM!q7XPt>^eu|c)nyx%d)QWDH4eSO3A zAyK%kY++(jwI5=dys4c!4)@GthzMgG`? zX~QmOkS0)!08Nb={U{Rzlu@TH#&IqNQdOBsac@0RwYy*RSjmKw=vR5>{$T&4NXiXB z=Z273rbG`lom!_33C-hyJ&W3;OtIW^_8%@k5P1H0YH+rJdF;5HRAC{4HtV=zx$|^2 z7i4@yB1JbPPM3#$RBRN%SmKA*uJ+t-8ce1y*MB`!alr@>MQ9ZeWg_8Jl(_#U3h()5-{g~&%k%BemySy8@3#K% z7upz>pll#h9aDZU$j#NCz1-Y;vf9xHF1fhmlKS>I;>eN4ZOR7_W=8!_Yq{nok%yGM z$ztP76OEfS915=eA<|r*{|0{U;q%Lt3|ZV24u8Z)>?e>At|yS-fClvb%FpfP!$W6@ znhcGe6W>ST;JUQ1gkQI)fKJ!fcIoS_J?I>m6ZZ`P6|Tsf#WL+J2yWCK10y%)jJjfG zz{VKEwQv-0{_AB(@>{|GcD{819`W=^4tR0t(f@P*`aj?1|FD5xnHLoFV-aJqdUA5G zbtfwbE$Z$0$QiQNB1i7J_H&tE5vB)={Jl5-cBDK5>ECdK5M}7~#;#7MS`dcdgsoMu zU1nu-CWaKqv1%Yuf9S1pB>!x9B!xc(5 z29;-E1}FK%p2YzSW+3T&OVHuP5|?`~%>_Y)Pyh1L)_QLNj6X+zq<>HloFF0c3aGe| z_K;YRNIWzu4)0?_e|~Y`M$-Vmy!!<3sVd(8VU@q78*>Nf*+OGtJh2dOfTJ0nL{y`E zjW9SP)Bv#1^(@mG)V6|p@FfNu1O-XDVh^6_m!oSuEMYHsD}KGA8_c z8rs;&1#canW~KVWKSJ&02`jNvTHT;-&MChuk9r9@rP{~-va^)i^J0e&Z~s1rqFKif z<(r{Q&vg5)Zhw4qexD!*cyYh?KYmcAX4Q|D*4oUOes`pae;?bZPn zJT4Iza5rX@9E+7C%nG>GdGH{f0JPLtMll+PPJOF1NpYq?wx>+!?u>g?gAdi`xo@%l zC{~~JVH+yu5YyU}=gO|>r}6u6iKx2|VbM<7k8CeeN7R~h z7ON;)w*2<5ee;3@62|m`cY8q59{aYQd-3ODgR*kf9G4)-xssjORhgVz6rMauo*@xT7vyNMK%6)Yz-MXKF9_~!HVQdjxskn*s@g%jU%^%?#rp&upHfSsx zg^`_du<;L;u)U57$(a3+RiAJ)5kslM`C-#PnRxy1F#03>>fVAhX7<$=>0BM?S80)Y zl=n&>yq;HW5CxMBDZkBH{;r2w?7kVwRr5C$c5T2;2(&grmjHL^Z7B7)GeUXw@Ocv+nGRpJM`;T+)SGdyEQQkqdB>%0y?8`9a z&@I`|(ZQUG9dV;LblwNQdNmti?zHO<<*XIVWNCCe_zVqOthAl!8_vuc9=62sA#HR8 zVe2A$g1KDL<%z|;_|(|Qg|(n)OV5O{8`0F|xrnTZm^2P)2kmUJKjCLfaNQkaSR@n< zaE?b7!)%2>avcyShZKk^l671>u1#|NUaeij;6M<+=%S_p&21Plt9N;z|GFtVIh^v3oTnm-Gro^^t zE=c6|pqo?#X$5_FK7i_xM6aYnY&n%~cUHctlt^VZeydo)2%};y`mVle)KOl~QC6DT z4^#Q_0$@?S47FPEk|MSHJgcWq*63=x_N#yU#{SjVm2gc*}|?(8YA1?+I_sr zf}pI~LrYpWuLPG&Yk&G$5(n2Sa#dU$tI8ha(fi2k!TwxKppYZfujcmyF@|v0I*)5l z_BSCGkOdQHthX>nkFQ)su$4&K2aAuF-wz>Y?NEg}FD`M9h1~&Hg+Y2`nxm6^w6jZ@ zGk486y%Q;N&Or|&mSeiRcNw>0+!r1jB<6Gj%F9vKy)qxy zFtSkEao2ML9wh@_jS}lVm`0XrGJgbGQhs8Z1P2|VHNkwjr<7qF5UG|n8=I$2MPwug zM+o0zELY*Etl*YM#WJ(|WbroweH;e!elde}U_X5Bhq_|qPd?%!M}LpoS|)oR9|+hq zUSNuCH|BW7bxH8N_^DoIfzF6=X?Hz4tEzl#{-cvz5>rb6{@wSo^w7M@(U}(e?}9aW z%=jO(Iuv;n9z{HZ;O%{}PELL=@voT#K^h?lBlS_v0Uq_#M0GeV`3-*+8qee6XtFp~ zb{{r@#(QwvnHNW9Nf{tX7&KqHZc&7;YpQ<2T%UMYOT6>3A4`4eCsctcuiC)8jTT2i zjYpx)D6l8eshvjLZ_ayV6qo(u8I5kN(UF9=ExwU=@m7W7LZTRQ0d?U?0G17#ddUNEk_};RGKVxaJG|6ZF0KMkGJbnJ4@{3 z?>c0(q@obmMLn3Y0HX7?qaHiZLEigxZYe{|7mo8gV<~>TFDhPZm6wiYOHmyE{OQPU z+xQq@!~Cq3GbkpjtW}3S*t4$XhH{K|%WqOc{U$w{6+CS;dV)EZ>3CBjS;iIF5X^_C z-X(hNpc@cpX>Ij;e{k{Ob3xE>){CU$4&_^sUI3}PnBiPxK(lu*QjaTR4N(%d5oh+% z%)>N0#;DTjdkcxRokOI zw3%Tjf_DGure3pm%?ahEK0Stxs%Q$qotF7bb{k<==kaE;rvHQ6TW{9zf=*LkekV&i zs5Yn79h2(nFynPe`{ve-jwUlpfDP1b6&Cu6l<$u()P(|ytr6mbn9e-p%Co%`mtv3k zGK<@c*{6({(D+PAdfqjZDwb2`?rZinm{E!wlz8>U&xylbW?QB2(M*z7@Dy|U zNzD4nMF!Y0pNO(U8=CEb_A)edEM47Z4926f^L%JycM$MtoM zZICAsI8}dmLqhr4L^ts9p*;jfxEpZTgq9>ZPXAeg&+h9%R}7qxRVgtlEY3P1+&BDl z9&>&AS$plNLW&>GNBh#c2z!Y2LA`T8n4;Iy#y-*rriSNqB^t9+Kg78wPRxT8^W$ii zpbq{Xm&QioOrF;Kw3^;lX(lEOiR+2H&*jS|mG=lxyuJtK%P3EV+dh$#q&c6HOO_u8 zsJLBh(-&J@JCnbr6I=JMFIi${J^>^QqK{48(78GLPnX@yr_5}avKgs8wK=GcOUDsC zdTf)6`M*v7^=}$XC{fiSPuix}Asy-{_?QOXY=SWis{9jBJ%L~WP-*5KUsSE`mhVsW z1(h=YeC%3tQ1w7>k;U@UpWt$*=@+Bs*R2*#o{ljv3|XX#DZm4{UUn_hbvvFT0SKX2 z9sX1by8wy-_&BY86Hamc?w7x|-8GI50=!@1?*-0Ae=qO=E^r5UED}jQ827Y8oU^BI z2V~{ZCqJbB8rr=%H?*$Pq7q_K98RDKgY(&)<5Lm^J3-BjF281G!y{)Yu z4Y-`$Xc6Co9D_PSd|ji*0g3@Y9|gWzy8Fjq+fbzV$#Q{Ot3jQ?m6bYzUS;KR_}xbU z6R1}Y3OwXdC9IG&x8z37Z;CN|D*90vEFlhtcP~!byw!8ptiSvhU3{|5NFV`$zyu%1 z@^X)@glRkngTq70d%VEYDO{wgE5b)U#AFO7l_efLhM?<qpsB+ zh8%>0FNGd=L`huXcko$F_P&Hwcul_?Bx?o)$5}LJ;p5*u0>r3KDMT5`)$5wb?0)B2 zp!2nP`3~tXU+59;0>FBpbMSjP0G-&;*Ct5#yuOX>v7@hi#)N4lOdsO>$65iF)>1RN ziDgu*%@gXz@)uP7DDOo28xX!rtwiD8b^+N@C3raq#KsOuzxJQL(1Y}!uKWTa&oZ#1 zzbVlsb4rDA1D+`@788e0Yz_lNs*aYkChbm~eVD9wB^|cDBI1iyx6MBx9Y_+yQh=QW3x})b$!IIzg;KqQG5NM(Y zrF>uIBc}s|VV7IqsV9L%*=qWZPT)B}xILu53Ri~N+*icSq$obh?oJ)!*9E=RX4o4! z5RYqI_ix>@s(*8>^=S6oXGf`y`8IybRgU3eTTMM4m<|1Ie$mgNy>#j-HK5IZ zmPr*QVl4Ekoycy0vN@lSyfKk}+J^voAj#`4m^&@I>V>{V9=+tf*afkZ6YMgZ+r&EB zPZ&OLn=MGbFtFm6F|<^b5)9o85C{d*l&^&C4M+9sM&qF64_>8 zge+2vSSB7GZX(Z&9dow@vT8_4eH`D@dY)r0A8N}11b-*lpF(V$$vw1R)eDt- zj4xtS?|nCmaknF_*4u9vbIGo=lsD$Elc26x_RTc&2u)^@r55Og$<4`=)o{p<{V=A8 zc^;XczOCdi_?V(*T183ke*67TUA3`(9=CaTVu+YDR_oc7B3Ubz@wP|C2M_aX>lLxT z3e?7=@ojXIvHhJ%F`0rMhH*Rk=x9Rm*X3{(t^uHCBIOj2PGWb+nISHk85+)((X&|$ zt!Bv9b~aE~dG(SLXiRI$7^$RMlw0NXNuLQ*iWyx=ziI9Q~VwdMcD7yWPNP2MyTL1Hcx3uHnQ8aMNqW z5Q6CO1BO>>BUI#cA8c8upX#wwi-{;REY#_}a2I$SOJ+Bfe~jnT^D>!|uJe(#NWCvD z|GZf3I49}kuXc65^~<8ADU!?2)7W`CzKhVxyYG;& z@tX^}=8b6#!e_MkT4LUEE4c0h4_e2uA~rWk)8h(t9nKB~dp|6{yv{;h!yvCZz$iO( z$!cUA7GBQUO3U3INe5p`Jr8(`2&vAz2#E8uZ z`tWKzko)T`XS}Ax-h8xcHazu}-<=8@`e)Yjqr#lcoFS^+47M}P8OQkVK~Cu3TICcoFSIP2ruheKUW-Cr@mr0iSEKUPIR5~0qxyB{3dQznq) zcoD7ET(P0QFElE~!_`HfE8VLmUi6K|%^*L?{F!e)#Z+1a_qrs;@V>kQm5Jqo_Nx`@ zEsFId>Q^R2-r}gNu2R>345$Wf*&JF8iK?e!tWtY9EBLD=3~ZVMEwrCuAm8#)v5Q{` zYx{4fbzUE-oEHSyTsj=pc%rACn>PD>&F`|o*j4##N-NnT$GMFhs2(Cd{VGs$(eugb z3?xV)J9CX##k)TCO}))OYd;UwjsH|6AoztZ#%M5d1EgS9@Y|5ELb~mBcEd1FDQ%&p z)3)|0Jrn2w+KayF_c;geOl=@c+Mf_N+WT&nh@Ir_s<&?#V(yIbc-j?+0GwtxJ%fcG z;((2RNLny>TU0fkprohTsd`d-Qknq7musEQ7y&855*lnLZ$Aw^!ZEYK zZVP!e5M(`-!|_ast(z`U`or>a)#9u-e_Y_LC{|Dq^ki+S?Igmlr?Rx1#qbmGKzuN^ zLB80TR;9&^{*<*pt7e9g(<~m{8trfwRQWk{fks^3x=ImSMs+JOKXQGzb5h{z^rxJ&;EdIk2r) zD|8np|H&nFF#y`xKgUG4B(<+h=8Lx*BYrc5TU%_v>EWT*&?JEAmiIYf15p_59~E8p z`+CKW2*&fkAXu3^bK z>z#AqM(S#7JF%0r1b`a+_5TNY`yW~V$MtZlBJfnUgtC${C~|P_=g;j!u>~*~ED;j@2E;{cN*hz} z#k?F75L+p#t>V{tU}mO&xZ(41d%8p13#jmc0v|8+Lf1BZAJYM$;lL*>>$G>BK1kBK z2B4Y(-|cR~tNqCWNK`UT6M}CpAdDt@x*yC<7wXkWzktUdf z>lg*fG1oUrhsBA83ImaxgvzubcT{YqA?A2*i9nQ8J(h=c6#}*DM{Au_ z^wmVhpK|v@P|N71=H?yBhpgAgJrwK z1q(%)N)-?Rl^Q^b6hTBvD5CTpsz~o(s3J=52%!lo1PDF!8bErJ-g}oWy@YZ{aL%>X zK6|fo_C5F9`|0w5Z+Vk3-cg?MAOGj~;QFwBL9F7RP1^few4Q%bx#dhhP^l${OS`Z# z{kOLm{FU$?a|g!#dwb2>USCFGTiE)ZC>u1VSU`lNN1iY6@L(^fHTGf7V_V!n7Ku zv-IxLW<+l!Bb@t-9U?NyJY~ zzsFsdY*$lEaFOCyjreZUL5>Vk&ne8gS@CKgl8H30)&AbkotbL0v7*&ZoZrHKVikoj z-ca_jwOR8vy$r@c)5IogFQLE9Soid;e6|1*ldXU)Gi?%$e7ZH6K zUY8>szNc4jQLhyR1;?}}m)T5Y&9n0zf4kM>l#_ozW2&XKj|?$AvY2$_i(Hw(8Z-Bn zq8#m9)?d5(Cs!3m%tqO>Z$~&V6{+%q*F|gyj)m)ss}F9W>Z41mNsh>K1;8t=Gkp4m zcG^Uv-8)d$ndDN0Am!UVKN5+`%d~X%V+*fKMfF1pKP}W$LFsy{WY|1hDJ_Lcejv|jTXu)F` zkj;*kqf6WDOsJZYVAhhoa~&ENByTL#U*Xuob4cdQ7Gu<`J>yVukWV%}1$YCJ7E~YB zJ@+VVcUF~6IMQGKsoVaxmg>*JQdq)}vzBaK^-<0kh>BE-xHdi`^4R2a#qo*{OmKVg zMA*0$?NF`|pXkaLPt)zEIgmSBSEU<5`CLZh2?Cq0Zo5&?X%s{&5N~qTrl@QSvUk#! zYb-2DDL&Xl*Nc#4tEr(a*}oPVSAW04RP;$(q*)WCkMw_;`8~n3)!^qtFGl$pY29sG zH1`40=kWFL)o4ZtagE!ayPY6gqtCc96EbuDnman8=NVqaj=rr#$EZyi=jv{%>U}Hb zAi4Z#AE{NB@xgL4N_9>=e5-(Iu^-ovOiGm0e9&v{3yM0-A?mJmR4Ob)#Wdg<(p1Rx z%+^4S)X2>&1->Y@{B(3!N=uEhlg2SMN^+1pPft180YjC~Gnl{R*PmNGhs^CMs(~5E zA{Ze*Mk|-YEK(Sf10qwde|TlZwl?BUsoUZo4QJL#9?|=8s{q6mQ*WIr;dw*U()=Vn zE89)Q@dQls)fR6AQ$D?lt8Py*-=9_OIQ{m$~Ph_&iiM?991&V%n@@DkH6v9U6ZK% zp9iM062r!+P}}=;#ce_%F!Ye6R2Ji{qB|E=pcZpx{lmNqj~sy`@WuQ`s*QZF%SsS| z(QveJ*&65r3tHtEf~&ht-4i=%U3MSSlJ2y3o(?l@hrR6CTAilj+-fNP)MYTLDWhPHm))ZOx+#q=CMt{IcFU;OhA{tUyMIGO#tc zpIfa|g!w@_50;euPxP8!R5~g_p$R=2Wj5H_oT6 zVfKBSQe|U>yj`X_#9v9%5Komlsni|jla`7!Uyj(J(#d258~GIRs>}A>537@BKTcbl z$cpmlWCcf`OP~4ECEZD!r=Ozgq>b{umyG;CR##N|Dk4`#opY9cP=2|{h=^qCWnB+3 z+3a(Ac!NO)BV`(i!h{<+Jz?DXYg1CS2NI$hvWmQesqE5DhT2Fmck>ga;fn9Qy0?bEB80VG zc8qz{yL|kqEoyp(Z+nwp=g#7QB%cb%lN z)uD>aFDRyZ(lM%IVz(nr)r(as)?{sB#2zJI0Rn&B~F9Q>8-|I01|ciJ~WUijVjR^?i5V4V>~L7V^-PrEP0^* za`I6)GoO$)UJ(UImvapiKveGZ6V$(4+$OguYQ)bfX1B#+2o*Y(D=E->#hzLqZk+nH zhk;L#j{DRaY=2=|-|#+dFb^q%>;3dR=}Z*fRr;|S@m5}^tw3u2Yx5h>2T$2W0&QQs zhYf>XAc%KUKh)=Q(GzO+!P9xYk7u~wDO?&Xj0HAg{m9MN`Bf~B-V zzSiQ>Y|H#Tr1B4nU8}i}aFnd(PobFD$$n@{j$#t4(iE5zM|Vv+*NY*wO!O%#m{ru9 z{erc~lnlDrgP`$$_6M2!XT}a=@^&AVeGgnhxbem{NR+)L`=7u;h>&iqB~Lcm*X zi_QPFFHBkzY0?{h>K=cMLv=Xo{stO1Y8FhS{?D%9I_AwjD+X&} z&Gg!TzI**1ezsL3s@a}o0=CcT@1%n{YO@q963oKR-hw26l+I zp4J%<#3x1a-+`wYGwnp&OCxLTEF+G-&-zk6(6aXg!PuF!OwpOu`lz~`X;BIO3YH&O zJ>>xxW`@HKj#oI*Go?MfTO+by{%AR&kW>NpTKqO3fF6$(F>{ zB6}X1RxvoyGuu)T912t{_zuL-8bh~^QHAB^JLqq=OoVJPGExSn+naZ^qE7-RVM&=t zk#5A;^;Z_L7;N@&4kx=&hq0rrECDXdT z$Z8rIX!bGU*&DH}QoQ+oWWQF7n{xpBfb15tk7FQcNv&y@>)t-_VG9bd zRoGBi8-UBuwHoODRX2{v(^Kc_P3jBPhgF?hzY2QPm?~z8fNR-vI%%y$RpiN!vk(BZ z*ktzLaXey-bhmq{AhP%Tl<3X&jC`bk+fC%=5oLVIBj za#EHWd1+4_ugm8$Ix?Uw#EZ&4S=B39?g!eo_~{g3#a!8sS{pAvsu}2kZ;%k$t;%dG z6KC0+eD68vaTe%rH)turg!eyI&pr9WHCRM7W>5OhHX3JqGP(Bd(^k=!vS)q`f^pX9 zr~F-o3X0U}ugRL=1K46-rvj`5`vS&ucRUrc#wvgogrJQo7}u&%r}Glm|hw=30L&{2Ucde|uKJDBDCY;h@;ba(NGSYFZ$xK)mcl@2;(p+Z-cWlb+9= z>XaR10-WX!kj3`x#GOajTbB4=wPV*RwfQM2=T3d13gp`Ha)b5Qq`nON$zW;kzPDy* zr`hn)p(Nk_W?#7hRkW7jN3KV^2amMsX|~q#+n$?g>}8ff&}D7Rsw)Z+#V;+U8j5~G zyW13Us}>qyl`xJw=oLqpd9JbE<~~*FJ^?;_4r%@7<347GNS%_a2}4{Tz@Z(DxAPVP z^DCsTtkFe z59~fWw_yr%=e*#|kQnInWHjiY$9W}y;xwtNHz)eF8C)lJPRfs7_)(E`a$Hi~=6Un` z@!Pt10v~t0PwwV%r!tSQ5bSo#>8LJ8oKh&GY3R&s>rM&-ibb~LcsTjj&*?+~v+e*6 z!=loX_YDcBVh@FbVso02_6zTs({gxVgW$MdMf9j;`c;39m;W)XaiVOnw!&CO%g%UJ zrxm=cOfzTblZ#CUziz<>;DE#t#wYwhD;#XP*dJPi$#yPRy)HVq-#@6X1~afH7cYbp zGD(jvYHJ6UKX^Uf%`dy4Dx*1zpXpYuEn=Xy?D4HC8hF1m_b2G6=hneFO0_hTWi2$@ z+S^v?dU3mhB+-vG>p7jO^KL{_Zfm6f)ZqjUZAn*lLk7O%PTB`LmtJs3fz@t!+w-9P z!02_VFmOK0Vr6J!$d&efRkP!!u(a!laOo`W2A_pQg1TIu9eXQ@U49?o2{TB1TZcC* z|DKz=hok`n$3uTW@-D~_A6v_UUMB=5fb%Q*Jh+V|W~1T4OTQc07%ofe<7DOybG&!K zn}1yYGFS6^BjNmw$GFpM`TeK_Wt{YVHh!s+v17?O6xyS8W1_#Y!r-EwR%Oi}>5rFx z|EjBhed0`O=N^31cM;3uUYcjMJKn*3LHjEBS*kP}=Yyg2 z&Tpla_(O*`_1kMCwi4D;OB|6^dhv z97F#yT?Z>5yU^^t>m@xF7luT2^)BuR)|~&aJm9nauSg%@f#-jE1pk}Fkv!bJ8}0_@ zUoj;X74Tn4d2mjr|BM#-=NJBu4)4D-tP_NCW@cK56r1O3=Wn+)kzgS?#yxD@c+sr)IVq z8@TN8CXzo*sGkVA9IV<29y?7vELf;$U#N|5TJYLFa0@6wJBe)F^9+n9z`{3&7g^6{ zdke<#R>ts#Okn4Y^|ejt@TT*0gVXh#pqCcJmV(;lp4&tjzwyS(kL=)2wH|=x&Tndp4Xux}M-SQX8F5cNh z!$3#p11}Ox9pH4r0fhsqYZboEqU?@m4X$`zG_RD{_~sth$(Pri1%Sy0bj_u^@dT3< zp`@DHW&I0YkO%wZSLsep#hO%~yMSl6SUcw zTqy$TQ&8#PuY1wT^}Efoz(r9PIf3x*J`E+YU{j3**}q6W$@dlqFL`|C zHmr?JMZq>q;&y|z0jJuC>>5I{wJ181X)wbJQ%}*M&6E+fCqW`s29X>|M55%%j{eA@ zQt8|Y9xn4a{4si&p(Ea-MAuRG{T?qFo38**AMX@tiAKxFCwq}gWy4vQj5= zEzw+kWD_|C7PD5#)B7)65jJ&Sn$k+JC1v}&I!lW0ZCGO?BR(l<5NcZQL(0O7)MfPd ztT`bI+>{GtJ$@e>3?ChBbaB$~rA0Gqy257Wa^gBYtYLqc##_ajt6S;%kXKKe#i~5& zXGlALbJk6^|BmSaM#NU#+0Fq3%A%)l_y^xz&J*sEAhFURuY?!F5p#l&Mw+cxA)Sj7 zY+1K%Y|4cgXO?5RZ#ZcHhxxOr|E|NFPopKIV@UW9`d=}hUQ5;^P4WqOEtnz=GXvlR zsriA0&7sFx<=eyf0+wni)2SF64eNa-Z{}}dyti_8Lt?7OiieSxxG-+OUGQ|j$!hL- zda=X2 ze^4mxXK=Ubvb%T24mzaq>00hJH!o34rGf%4j<2KK{BT>2mUgD#V&FFP-qpR1NTmbu zC%b8vj{?SudK>nh1ksa)>u$(K?9-O>^T+M@Ha*bstr&`ZPux5DXA*6l5YI|c(5{5} z)$)dj`-_p43cZa6ZknVdDzE5y4Kg-TMV#k9Dqo+ekV+GC^$>}g71Py%S@A$Hm(A)k ziYUxJ%~rUiOxe`{5D+FMAsy5792xZx_j{?q_X8@h)#PEQn%HHY`#J~Th{i1xu>e)y? zqhsQWy?W)Zba!^|J?bKV@M(i3vX9TQfKKYQ3%XG42X%DF(9qpXd%G$wl}~31=Qo6U zi^TvN2!Yvd*LXN@?u^R%O<9-1@2cD)J}V-a59P}^pJQEj-S3Ci`W63--XuGMDY&Gk~fqn&aO*krRPLa$o62AFS zx(d_GTu8|hvwp=CS~vbxhyfZIW|dN~QK2DYO@|)(*}!5}*~Dr%@vZNKNLSDOs^KHu zjC(26!P^n)H0U9Fr7j)_S3`Erp@EhPU5MO^6F0zQ~ z1$OM{JXs_J$-+XTED(33%XI@q`BSz*b(=e{aT#6gh52+4u_d8$U(npWJ}!-0qx`pd z9;S)(Q4vgk@HE0g%FV8`O@e&sK=~O3KjkG@#mdJ73yL1aeRsP!D+^u&AB9dMQ*Dan zjPO_NyU2Y>-L1!~I2-Cg4KGM)Q#J~#U>*vo)y~V8b@m`q6YM;-WRr*p7IC7(IK4R(kNriS{*FZ0uh`Knv9Vj5- zWn0rTWdcSE$e{HgEvmzLY>?w)+l>zLY1Qrg=_+uf`j$`pXNjBFZdv@o(Fkr3a`iiv zJq090+h^pr9d{f2ktiza>^&s6c1VtPgd~9ghrP*~DDF&u;oWDUi3P9FS%osbG2x+o z^81~i$G2VR<+{kUu7004w`dpN%93D}sSP=3VZ96;n*Tl&{mRUr?}oVlQZI2xs6``0 zzN0a;Q8>m9G0T^Iq3juDAc2mn?#!EPKXbYMu-H-yHj8#k)qI@oUNuY5iZ;uHE~&LR zN|-aMN}I^42FX|5?zYBFyskF>ePVrbVS=uLmDf>4^S#p=wE%tWn)0Bg@t7X&%@kWy zrhI5n9TNVz7HHq!f#)dVSqs=al7pTRxQ1L1a{dTVV7Zn5B^09nOO2wnu(X7<;619S zQtxmgrvtV|0e|jK2DRjk#Xyeha}6gc~X3;*B|l`(X4>1SaCqH(x(8^cw>L;Zl+S|;51;ihEe#@ z-sZD^s?M(IL~|d%eA1P{e2rEAi+;wcd2md^zBzO1fdN34t5eJ>XN+a?+#kS!-53o~aygo* zugqwa%yjRTknPbf{xDS4Ek?|81_jzGNa!A;=O=l_b2dK;_Dt1%e2(Mqc=othGW0l` zWUeao#852QY>Zw>&q5_9T5)JTVkkP2D2~y^@L}7Rp0%i>{z>wrw;i|~k0=%c(?B1h z^bqybVQ+?1ZfFk`?*+xStkKv3Cy*+Dn=Rz9Q|0p9wju!y(o?nAQZ(HEBsXYr)dMh7 zQC=Cs*$qjW%rA?WL>COlJNM8M%`vx19pd2{ifZ#xY)&=jh|PKJ(A|czxkimO*(x*f z+c#uO-NK6cCC}TkxAf)QTZOSe!*AsL&WBh=+{?moCh|Lv9q~d>MEqV7K4DXtAs+5A z$RyKPU3}1nD!hp24lR7n1KNZrS++xZ1n(P%8;|0+!(0P=)-lk~j%5urQ`F72Vz@)S zgTASR#xQfDN?6|n$jE^6xTNLFQal_qH%(ewtt43%OhQ*+eKus{8k&iJ4Edqt3N&BK zmTBstKImm(4ad1yW}{0xD%r9_ViJ-Dde|DssW*mWNU6rU5kT6?{FK56fMwN{KUHOW z=C;HJRH@IGIS@^Q3`t~M>F54g5Ku6SBvlNMdG6fM@! zq1Jr`P{oCX0XStibW!@=A0q2h90;Yonc+0@_WXLmi~qi&f+NE*ps2 zn}Lz9wO%XDDDGvHC^~U5f-uI0f!g-Qh~2oJZnUbD+>iJG_uBvp-Qi^a1tRt)qRQyh z{i-8-LGE>a`N-!5?d@w1mzMO13G?Oou1`+vpifexA73&P{v8W{|6lS6P+!DW@DBBa z-`s4IVbl+#AZkO~78whFi&=DYiQt3Ljw*6}%7?!*Wc11JIb%+Bu_ZJ{Lii*xhbK3W0a^lmu64O?eAxW)Kr;BR1D9)b!+8QCR z{K`1Tlaq>yM!=2gwB2>B4r}3gz$eD>`9rJuYJ~so;C7F%p^hSv*}6dC1+?fg$>5mf zdf^80_;F#1pMk2CmLRJ#*-0sGfL+oKE5=t;$bLR5YCQfM3<2__De};LCd19UCNA;c zPD#7Yb3Zy7UeJG-xo6VQ7J@!I(7 zi8qJ8eo-)p1~y*@D0{Z?$qt!4uAwJCuIyt1(kI7H&XI%)4;EgGd+t@p3t%&h(?>%DN>iEtTxby*UH5hM`KEHyvEPBEapL#!SqdbboPTfGVp!(pt@j@! zo|8=Ct8Vq31Pp4lxSZXcjp8FAT>s1SNAVl#`Z!F8q>PGWi})v;vHLtne>0ZW8lM&3 z$qILQRBbjSqS;XO+mesVLiyj}#-6th{QEzaX!7?1{|49EX#ImsyE!eUNYdeau;cLa zblE>Dr4Ig9P+q6do*BA-4kK`IdSvyvGi@==aI$-|$lR(kP+!z}K3w;Z`G{b?Jik(2J&Z@u83u?$Ci|F@7_ zUCimPhA4rQM9<3V5Qnk$<6%CV*BoP69Fqg-A42> ztls%4%KLWzv^UWDsiMRr5Wf^Y2F)n#)Xf_Dp(yw7Xk`igA`toOn_X|*hnGo|XTIDe zss!WZF@5gjcN)wV3IEdh_fbqvd!MNh;(FL==6@<40U2V4S@1*T+eS&72l}8w3^7E` z7KP;&X$nY>UUlQDzp%dqjo@_l6&k+1dpX(a#Fg$L?y;p_|4k9;*Fn{wAF0*Gi|-tQ zHy%KJ?LjVW57v%zG^Yu2Ehv&>GoKgr0Ql6y z{RZjL0-h*lHCT;G@^UPOOK6aj#wmP3zFI3+5F&=9x*lD3hiaM1#(y-+$g)*{?){jB z>Wum8E91tCB6%@0#r$JsBOt9_2VNi|kclrL%LW#fm` z1y0%&wP(aGhxeK%1-G;@#=d{kD9I5f^a80z1Dd-Qzz3C|t z8c^=+O!)eyHOLCE+Z0I*^x+79PT=4c#><P(RG6=_GRzl)lbzU z2`wap`H+&>^BuRJP1VV)TzXMn?4$mW5t9&~;r5jSt`|~0L4LBGMwDiMS@VcNjA?PBu`Y|R^qk)Oq4o+5muHz}f$Y`q*|!AaRWu1l{%=c8pI<&* zGH?``Way!UtaP~7$_8haBsC@Is+w`Zt_87D-KrDMKw#YA zdWp^a12z>3+2~c0KW7y`j05$NLQ}RMsGL#2Bc!CU#RW^IN>A!i{}QCuX*@aLwm6i+ z*uun3lb#Om%$z!%Rk_fvUf0FMf9g^2g-|B>9}nR6N$ywnOrONMWtcLGt=zfc>cmd`aG(fgGQA>!B^ z)LKb43D>(z-n^Wk;-Ob4AL`u zNZ)&{hmJS<%WNc!w#yng){nt^Ik44EywJ~Skx#f(eiBGkH3Rqk)%l86#v=LA)u!-R zUADETZ6729A7Arwg0{8X?fJ9F!nu_Wl27P2>TTxvNm&a~W4s1m!(0gZBm5CWAiZ=( zP#+S*&qh-&Z)JpABAi#G;|caE0@EgSpI!2->%c>OvpL9p0#Zj_?c!GLB>G1R?hE=o zqPKqh$dEE<6K-hr_y55Fy&sq%86ixw8I}TdyQ`BBK$NG@B~Ow$tO__%P~~#6zN2sD z_n?uAXe5;K&x*;)+gx3U0HQ@CkAb9VFpAC8TUGuMP#erK-s?|K z8La;(kn5rsMvX9+XnT+1l`E0kHP&NVdC%CQ6{0>XxPK`3L~)_U4|S)(hpB_&clyVr zrV(NFOGj#aBr*=crne6beZhgrixd73%-1%8@%g$%Gr#8nk0|YIeT{ERB8N5DL+jZA z`9Rp0;6DH(#`OONMr1aP06N2rY%94+?9Z&-o76<~u`uM<`(Hgx&uw>!7ohnxn=MZu z}yAjBdEC<&fLu>r2ay@3)wiqx=0RMV>?=ZS~VLB_)gc5EIn!7VQme_(ZgWG{`)s8?;!) z9!a)k6h-eQ-!=`Q}!jJ6|=sNg=s7v!TX*`Cgm zY+etXcMTayCqIX$H9AH5@(R6?zeT2M9oH1TNn2ROEe>%W*atu!0fNj^cK=pExv{Ow z1B+I1E9RZJHAH!I9w%d{-W%6(w>2<5S20k8h-6koO1D@@+n5GcA!49ZZ+W~WL~=uh zmVyiVtCyUGM_cy$trao8a!IzT(l-F;Inj(;D%Qt9HiO$8&(@;pg<4ECCwzT1QRF&F z(&y0dBg#^ba%CGDv+cS1D+t^zm32UK_=8aT;XAgx|a(I%?O(M99;A#TdelJN-IsUMxi=Ve-2c z4zPV0HcP8n{Uh#439$&GjT5wH;i{kSi& zH2o>7#LH*47Wf3TeY6;-T3H<`D1Py=M`FQzAMXCC1FYK%N~wpDu=eqFQcHoaL{Y>! zA?jG*D;hh0<2LnxGS$s!1wxfy;m_${W4Cv~T?S4LQl+y+xX>{mNLtwY{Uzt|y+PjT!J1>R^JJH(&sKe0nra^{Q$G9!@Y_s35Jqr#bu zY*>WUwtN_p=3TiQd(_C5BN~rX7Cjti?T0J1fng$@DV)s*Svzc<`6RQ4dF}PNHY{LG z%|s50{qPmL#rE`)KUh%V<E09&v~Wn0cYWL|PN_xXMAYgncuKGba*q6=9dbt%#pyLp}Q?dnXM{TF?ZzU z<$6*MI-~4~=Do{?%GA+xjT87JLF(z>8Oz3U(jx9CszjHs`UPi4dL819_V1?tz3>;U zke7*1%W%?b*6Uq0TVO;L&CXtI7T(}Nm6sqxZF9(AyLTx3YDu>EqU+n2kbfnjaDDm* z5d{^bUfhw)ka}H~wYBxdX4de^HU_ES- zPBx8_a>jA}+*+;uf5GYZw zlCcP=nkVxTC7KcU5hVGl)<)R(TrA|i`Y+6uEPvJbePmlS*}wb%Aa-__T287pUpXRg z_OeH&@ZxRb_~jRBalAeokj6SaN}O<=pTs~SHJyZR@=$wY!dDYh8RV{#j{|g~(%Mp+ zUK!4zMMYREHhu+$X5lz*3VX8x*E&nq?F5V=i6`u*Y_eH(o@@uJN6@W8H_%H4s5lv+*cb!txIL0{R*H%(c1p<4$Fw1<6gT2(yHU6S{`j@kmI~)|Aj)%PPS3hdN zwt=SO>f8V8Mnc?Tn1ypJN42Os6O*p)U?W36-LlJvY2nR$ek5%ih9?wVvMU3iAZ7b?hCr>iz4na`py)&hx6xBK=GrW%NjS!@ZHrgYQ%G$o{lyy#0nEG!Ck5|Y$(M$+o$rsk; zy|6!yhL4@%s`opXg(oVSHMksRCi==}jS-0MHq@s`G+ABAlYt(9QW9?LL4Or78M z0<_v?7yZ~>>A`%Q>2ug)3ZEm>YRBkL#)?2Die9|vBqGf3#wIZu3iM@e6%EO36$ND* zf#{~MvK^m&K;(0nvbA3Qoxyw_Evp+o73ca|BM_X{!TIBHn9SJpFXfcG+r_oMzEU*O zValtwSvAV5EZc&7I71F0FTBv%zj!t*g z21X!M{~o88zdQNcEH`G~%-cwimMi3@oTPh??#pVY$Rg(Y&5G|biL2&JzM>*A%f)mL z>3S?HRv&m(|DcX7`wI&Z{x71cnefNE`Zv4lO1z?k9jj*?Rr4oAo8N-UEa=02e-Aj(QSiL-{1J5cizLp3$;xMxH_1y|_lCRSm?pj;Glv@`CoRbdTt zII6I&mTNIC(CpjnZyTh;lyX0SVX(iJR&vP@yExs@?U>WHxVs?Px055S&f1x@6S&Mc zhlFitlN8Dxmd9clAX_D+aGM8^-wc2|Ex<7JU)R6_dgGLv%gmER2@Bdg0Ql)V(_(#R zNUdCo(sya&hW2vRu|)SNd7ZcQ%;(CUb5-4j(Diia9$nVimi%V+2`B;9PDhsoGL&Rq zgyz|Q0ie-wtO5jc5qjGlCwkoG4&X`#C9ZBLJz5tiR&V-^72Urpw`CW&Gx4nd+?;mh z?8*6~r#ea-f%h=#d81S)K-g`JOI>Bp88h3iK}2E$KZJUmeKBOchpQT9%ukYV+RK=rPWRR8>p)(#9094W=l4VJO7s9z>uedQ=OXUg`bYFv*R2Zx zL=7|To7=0@yLn3BTz(*b6<18EdSpS&Z*a`~?M+$G7mdK~CcV*C`{t~Pz*u7> zDD}}Dxb(E5J-STrtf*aacc*yrAGWlPq6|{hxhd`EliS&({W92ZzF)@387w<{lvB0c zFRBG+m>e~(YS+0fuD$3sX|8I+l#+G~3L_o79-^l89Cdl(0p5>+b!tu zfF(qk@uk+wymJn%(Tsy|uDhgET+X`n-z}b}DtjCM2T9OqR%&+kNEf3>hfA-oGcm_ZvHY%2uY#5iAZeW?#qjk3(00# zpowYa=(#7lRj~ZSgxbAGC)_9g=(};%ID#&@N3-hNK;04=iB*hhBE=gSAb^Op{9n<^LV z8OXr!F#NKob{SJc2b)Msi^GxVMRII2tZ9F~lL#XC%vi5KDJ<|&RN034FIH-?bq=9e z#EuIrqSC*oYNaPdR9Iw5aIE3StQG){r>W)BbFDkSdyARd;!N3w~ACr~MN0L(+ z5zLH8ATBE1L5aUsUMM>Fu`)7SuGOB*)s=_F9n051ARFQfGqKokAMK|VxBgd&Z}j!{ z)Bq$;66x%f_t7ZD9;9pW`iBRcE2Y{ zg;`wZ-DI`!KbPr}uMejyU6E=3k@3?9iAU%2w?bF>5{kORAaI*nPyC)dSGYxH5V*4j z1+dNBU+DlL4K>u0668N_JrMv1tPMWfid0<5g^kM5g|$dFJTrz=-_`smix2eX7Tb39 zed(hYQe-WZ&D2eGSy;g>-=;-N9t`2+V-YCu+e7z24%>fHie-r(L*~F0F%|Omp))-^=nw-ak z%Z2*&2cx=#@IENTDdYKd;JbK%frZ+8UDZHw`DQMc*-R${uJP&p;eF61?`V4e8&g-( z^65$C16VIT&wmvvQ&13{Z3hsRx2~nvR|AHn$}5n*+TFJw&{*@056F<&=fFcY7B0|e ze+V;7+4x(*fK(QoB@$`w5#`Gc{=ss6!!rM5$Ohyv3G+eoorMHRZu zEk5+@j*}14mKs$VULWkpMK^x@nnOj|uLyIoo@0VN9Wl;!-pn0X2@H(#vHGwH&C$pi z1!#GqM{cQdIng)Ijn`8d#KrI_=j(3K*fHT4kwvkYn@TbF61fdhUfZ^+OR|37MIJ~5 zefh~ZM~Ziwtu;Z1rQs!LQeRI5z?SMm{wb%q7zYRqcQ%LpX^iKN8ZtZAknFTlG2QuQ z*~TYt8g8zzHFbd;)*ST;V!JhM6|J zhLa(hTktm_OuMuDf2?=JzloNB?`?w$3Q`}gw8UxDr;y&@%ICV(C`Edb^e5`@iC0IZcP&wfa#;y>`N8xlvjuo+7T-y=AnHHLhI&;@5X)$fi{`&;a@n};FX52zZRzEXpGffXUSt= zdADs8#``>9I$&e)KCmkhJz^>2xZ`;~+c=y-{ufU;utQbJ26^E9><;rUXq=e6xtu|45TXKkYACWC|GwC~Z_G ziuG^9Uup#Yk4yt@oV+)?Q0ua{iLwB=HKpOU{waKIv+cI_L5|rekAC*mwNuOW6Lf{5 z5_lPJ1~K7(Mhw4jtWxPUE?YOr);-fa|KS`-zwfbg7T*B`ZnqT$@b7flp(pj;uyEqM zNLsT-}X&%5{6+gS7l=&T5 zP@rlKyqB+a`6~CD%)0YeIKk0#f?C5Ib~R&-RSlFPhg2|V9Dny?#=!n!w1+wVd$lR~ zwrtg=pGBpi3e#PK^n3Fbg!Sq5U;2F73|XIum4r-%fO0=Jn4LDk(INdN&X8B0{$a$g zt#Ml&`1Og#UusBUJ&tNy`3Wj5J-m_Y9-mvAZ*f14`pgQi3-8>jiV<95z3vD#Jjt-H zF5fJ((9Lnh%>Ze#x}K@klKw=MCFN%-7&~Nv%ZVM&_XKUnZ9Tx*WixizI}eIV6UBkM z=*?+ciIxRz);f{)!AAm|M3`o9^YBfBqO+bFL3Ers3jaCx-rro~k4c%qV2vN*{@!+eWXOV2AD*$z5yjkq7Cf zispE{6g|B|af}W7<^r~O8o=jQuNb|}EqHo9<}K#`y#|zD>sRje_Cu&uA-;2v%u_}! zO^g0}fhW?Q0#STZTSEaXUmm{$t$Fg?P3DAZqwQW>@pex|qrsfj1ZN~|e1Cy~EDJ-l z<@Qg%B^&@ZUr9LgE~=_pA8-2&z#bwvU*-(Q-y>G~W+44wChCJ{V=;WP5s_!?I8!9(iqMSDea|danQicV?0bCdMXWI_*UyT_VIJYZYC~@fP@cv~< z@Z)1V0s5X&pKs`fTaNRFI6F+Y5yLW!^RgSf`ARQ2OIXjKJk{bskbe3r7*{v;0e15a zw5yfi`PjT&tFAEi>1T~-b>B1fU%&GeSHVnwb@>)S9)D@Fxbz;i(m#p1s*k=&S#mYw zd9fs!_2%+}VaK+RM>oArwJm%mCBOytntSNwmlYY7Bbqcy1S~a96t3=_X(_w7>J)? z*XX3aSZ|Kn?oCeW5p=L|Ks)8D%2hxW7MtM(FE&X~#ZbLfc$|Xz_o~KM2u>HjI8O6P z==yi(JG>8=8V4HU2^RdY1lM)u=WyW`S7Dx?&?mx1Z!&b&bJD^9^p`u-YFhX*0dPRl zH{G5`v)>+LR=UVwc(+t(y=_joK{-Ax%Lv%J-bVd@V(+4=_it;yrJ`k!D!zSFl|8}% zx_L4J4m3Z_Vs6T6BvAd`QimzfQk6@MAg0{Ixi#AMw{A)f?89_1Cour!n}ZXa2-(?Y zh?x-e45|AH%aDsF79K(!liI7dW|TSXqIK_-^1o?J5a}ZO&Vy(weilM?o0A*=y-;cu z{W~L{W>A(pM1$4Vy6rQ$l}AC{eYN)B%Nby;1eOGQi*yd{H6dNIp$no0{=sT?dK}G! zPjfhU8|P6xvAt2l0#W2ya}y{F#?LD-O-bwhlhw(YiKJ|!(R_`%9?cm-MC^}t^RkX$ zHaJ;Pt{Ln&W1eNcdQ+_RepxxOjf8%&xsX2 zT0Po0DY?k)85h=dijF{HD>Pa}URN|xPEV-a+^wj9oqy^v?cbSuPkgoACwoga@#!&q zzZSH|C|HMw`eleW4mgUIZ?DSFRQumFJETp*5xsEJez#9Lwd+&flKVtC||soDdvWy7a;Y3oDa&0y>z7 z_V#pg`RRQ82Gd~^D4`v=bb7Gd>)hM2fpPC%+Q6J}Ga)+6UNxyfV4epvtqE?2>vd_l z)tcG^t{|M)-mI%1mh23n6{$!dt{#U9ue^9BZ9zL0+O()t8C&q0=lgpf7mGoDYb9)17yb$n`NQD=6{fj2i;FfWW1`C zNwW2eRrEK;*k&;Vl(t`rC+Q`D+IYr;g>0Wl)a`R(T*$s?X>WtIj5=PJJwa*hF& zXGm+4n)dlY4clAaLW;&e_Jb8M=^DB6%;ka`W?Npg3j&iK&q}r5C@TCXspXcC`aded zcyxc$HZMD`2_qY1-mdaZ(-gLI;}g~(WlbZl^;0HfD&y6z++@sW^O%iF9nPN0CFd2D zMVta%YZoIfu7k&68y}S=FR&;c7T+P0)kXMKXnuqp6^Zsi(!g4ACTNQ}>zP95tNes6 zZY~v`@vi?Ddv6^T<+|{H>o!0oEJ9MHTe?A1P#B~;W$5lMk(QF~P`Y#IZWuzkLApBz z7~;KWaL4mJd++C*-#O>~t#_@LKV0Hk!d!P;_jP^m@Avanp9!ZYLoEy8q!Q)~Z%I0| zxeUQq^h?d@cA1~EP2t{$78}y$+zf5eGK-8EMmV>0pMt(mztFS`AX{_K8Hvhmv8`7c zd?-IfT}O*j#Gwy6EI716imPU1z-76^-}~weSjCm7oI&7K-mOzUi->?Lf4=TFF|Qz; zcPnjuyGc}1jEETE%ZengpD{q8o3DVf89#-n%;&C|FE}G|*S0{J5!$m24(dg_$qxUd zG0WqFs#MIM$S~h<%Vmb*d)ZF)s-B~>A9_L^su-S#KJd)>YL;u;e70EHO;6^-IM%{6 zAmS&0zj84e5QjQ6oL|ztuE;#3%)jZk?nOm5O%Ngcu9Z}dVhwX$8L=|{hY^d5_y5F*<>+r}W6Mu{6+3&X(J@U7#iYU?)4|xysac?O4BN_mgLnByAOSsyb zARd8Ll3xb1@@QwJPWbuZrxBSs28h|W9zv-9cLBjOOILEO`@}z%b!DxZn8aZg;BWiy zLOQ->1(S-uy#rKab~1)Y7hdmf@c9>YobO40uH$(9$8;RtK49*IG@HZ#9hT8U;iVcW=qessF)M zCAi_DVG!|K)s2IbK@tPdP2waO?%Sh8T)31e8j*J*i)ZlgG*T{8BOGJx&KJ^&2Z@vW z7JY(TeBt+7-`{Q+*>ZSBf$vo^2>2}0@Rb9iW(SxR@o>B<4YXuH&Z{-Skxtk3BO%i* zbxQdR^m2HW4_YZIbSg|xiU8ln4iFbq5*pQevM{T(mS3W%sz{B_eCDHVg1}y0t2ZVv z+OOZnKBk3L*uQY@ap(EMkT|WGLJs>-=TG9XUfg@+)yru5nx0Xj;ak^Os+3hS&<#UJ zmwAx-_(&=>O;IKpNJc);WJxOzJ@-fBl>AM}8louEUc;x$9#$F-k|QYWu|SJ&IotRQ zS${gxBb(~ADni;J%{i9|1WFb~%ogF}DJ2qBC%g&93caaY@=EwPCoKr`&>bM5_R4Rr z2yJnkoMBw99gDVUU9lzrHj>_iOM6d-bBU@}1Db}R^+XR?YCnlNZxtZQBa6uQ`j$rn zO4SguX+{hZ*m{)!53Dv6V!m;8z$Hgb);9m%$w)Hc+-dErod@>ZHA9^Tp|d_8&r{|@ z;Yq7#(%vfUtD8_MkkL1wyXZz^6j@7ahDct?_093|^|>+-@8Y_bSM?Dxpx+-5U)+^NlZQ>q-T&*^~)D+4|<Ex{RO4a~&-QwU4eTF27tc%IMx4Kl1%oG-P!zz8u#txMxiXIyaLHup zzn4Y6M`QalN$5hOik;{&y*l$fHGjdIa}@cHSEBsPHH@HozVFdIRKceu?(-zEyZBE- z16Af4!m0?$^l!Ceb-q;h2kcn{xme}_nRrZ(BGLN;D!N7FTC@y+iX)&F?r>vXdJO3r zjm&O9FCScrjLuhWZ8*jw9J14(7C#5Yp!}RlW6^_$ zPn!oM-sFEz*LAEXXIAN|QvrDP*8`3oJ$5vJo~>6gYWji&D+*DKn=3>U;V>^i{i=WW zfbaf;9Yuf3hsQmwD*pF0m4%S!M&!nsFB<&8>+b!bEuhHIo`^uy3yS;CLb z0K|DwMECGi+m_T%9hb2MmRCQt{5$VnEXJyw_lRc?u>MHEvjR_gRm z;D1`XHj zCb^BeN&2E{-Sf}w9QtPYu73dt7AD`Nw8r=+w>;X{T5Krvrpd!u=UEM< z?_=(X@4OedoBr@Ydjz&Jq#exPuP&B~4rukSup|iT4Xj88K0=kPE2QdL;YIC5o7F&p z1+IJydsj-E&SB1INT-kX1P>RuvY_N!&c@?j>URd)rla*dctDhj3@vOOQqpTRBXXPcz2|k z$+`1o1OUS;3@c^DmSLht9|0c^yf2M|~ZWBn? z{4c(s6;qc=B7YP*u;QJ8Un;ETNPe+r%PU3Gc)TAcF5bTc)l{a=kYo#z118)~=jgcf zmmMwFgTre_f5MSsiu7(OxpyL-Xh2rOGwV^2kg-|p=Aylg!vJi?| z{xne?p=0+zC`*_JS#oWw4i2`r@N|>ld~HM9=dZ{I;zn;)SUsf9F9eaxu`t0s`G!wK zOC5v_?o-f{NXr=eMq_lpRe-$fCj<0Vpk6UK8=(-JXE9yWxbw(?BsKyETU3!>YJgTFd} zDn_Z^+*Dbht>G{;{qE1{oJ8lL--Gj;M%73UXjB&9Kh>x_zx<~(DreWz9DWN(oLt2F zkjFFmiyT11{W!i3S`MS;Sal`fRxJVbv?w|?J%rdJg^0=i-$g4A>OY89l|=RpLeoa!_T{-&g^t!dISacs+f3Pfy+jflP!YGq z_vy{%;M-#}_kV8P#eTK>Y;iun-{_tOC|Bu!pGu5x;uw88vwx2k@!PBK{AMx=L~TA! zI9Hv0*u3t2&2O7(xM8>e!uMGM+jD@Sl~&k>^Q=Blgm4Fuw0Ut7S(k zXn+_SGaF_lqPDAI-=|(0^&$2HUMIj2Dh;Di(|m{Z8*F z4=zygi{ar@|6d$HX>I?*0Tf_|yxKm-U)hw9Zypgt;J)hrLOwbX-`o7mTO$6B`1&Ogu=$S9d#iG>4O{$YR9l%itl^?7kOwTP(`d_~G5g_1`@&_(Mz{rF0 zr0H>FCByu{qcZ0zn_65j?)d?!Fy+rCumYO5fVy|k?Y-(vT3*+^BybG(BCZt^f8Pct zo?M+{C%fWJ#XUo3SrL~aN4xF~K@pC%vF68ImvI+@{^TF=0j8l3Zo=>4$}=k>%3!qTb&1XN4f+)Fg+*#^-HQnSLu9H8)k8+-LGOOp+=ITJ~h z52_gD^|;a%%kb80+DZ!U#^QE`A4yr}X1y`!fB@S=2XVpdHwJ$hHJq`p`jBEjusxjJ z1NvUu`f2fnw^!wDl{q#V#aN3T@2Bjs;ZKvFyt3_tC`(cN>-a&>n#&GA zs~YNSOhMA!ch>g5v5KV*xcC{fh9eHdM@P#zfJ?eh+gt}woS-f0RdtSflK~0{3!6;b zv;76)MqGNeA1hBAAEU>*L@RsI8Ra~4%+v;jtXJdObhJtFCE^qoNR75IBNZy>OEcXX zhW#(0#s4g+%Ef;mn1y7npZWLRV_?{VN(*mYTi8R)IDG9H!wBO2p{_hc!b`(zY&X(uHgRJG38fSm1^R%n@Cty8Cxan_3yB%M*0D6 z3IJ9$@!<+q4lHC#z^B=Ks7 zB;@K}ay#FeTP=&c-!Nmg|1yh`;KC5Ilh{}WAu4Bbygu#9Z)7YkrrrRrD)@dy$WKxj zx*j4|ea#=KK9gk1{)-vES&2W-_+7{N$1{GX{@wuc=7@tSDr&N>-aSH+YTf_Yi4_4Fx<-3nHO?DxO(1fVD zzKv%s#T`}nW`lwkQIRCp`_Hj$60bI))c$~%Na>g7K!nf0cMynfWqs=x2u}u5Y=!Wl z|4Uw?jy&bWuzX6k%IXl5C2dydD4orT4ASnoHP8TH+f3YgEr33hfn}a|gCxFBaT$FI zIJ9ZqQD;x36rKq&S>apw3Yfl2eLTK`kX=B4J<792CQ~1KC?l;_zL_$FYD~t$J;~(8iM^X#S$^y&mf6O0C=b^J>9-Vu3SnA@uqW2);0?y1H@Y!1w zM#)_EL!z2GbId@?8uO;V{Sydy}5vC{T^XjkbCiroTFT-^c~rg&hQ?lrS5(OYp@+P zQu(*t`fJOPOx)~eft$qHdgy=@Qvmwe!X12pXyIPG9*h_jGINGH5O&Hs9Vr-Lyqszq z(1JQ%4HWyaW$U&?$OzF+EcHDY@Jz+h)=syR6OeLrZMwbGw-Vn8Tw*Husm`Uv`+OPe zPOV#8__YlB*d*K>ngDmJ+aQ{5(ZDVB;itl_AnUda{|Tw>xqS6}Zfaq9fPX>vB_$3R zh(SlcLKPd&&&7!C|7tk8y2gm#Nc?xjhcp<2c*-miO69n_al=|i5lncY*`PZj9-`0z zj5Fcgdz~-E=m4_#pU`{E9L;k76QMB|c-w;&i?L{@m0uB&v6 z!g>-8)XxZ)Xg#rq5X2zY8`o?~?e9;ws2>4tgIb{6tFC#p(r&+=!2I&V*X+2-tKCJ6 z=A+jF9@?}t!5-9LTFSQ8A#8NH-Q;FO1$Zq_Y3et6ZJ?ROpPFt$Mek(=L+JkF9sPu& z5J2I{Q2M{7@MHtes>nLn`NtvB{r<$3m712;G~_)0@dkkP&42y^5ZDXas$hv~wUI$D zifS+S_BPwjaGqHq&1XCx9<)$akovr+DU^1-v!c5{p?}fyHvRFv*yp;dEpCp>dPt&F zk!3AMDeL`AU&&PMjEwKs2Bz+&myb&^jWCPUmPVBc~PxU=3Hh&tPG%=g`-FT_ylI2a3%iQ& z^c(?EPoQk^{Hlw5l&lqH6QzfrFH4*u7<2VVHxY-}=u?dbnfMVTWS(MhqH;6+$lGdy z9?rRwzu#lKbxxwOk62G$RoY6kVYVXtR~7pO(7#r3Fx|V6uad4^X(i@QJyeMn2*2;Y z`-Utx=W5h-n7kvmmmt4)pyM)B;9THrXa4^X7Z4Tu z%Mcew``>wtiegd3P$>w1Q)hmsiXYvwt;L;V+XT>1$m^tD*$k);6`yg6a(VX&J^?Vz z>r9Vl*AE7*sxKSj>0P9enX7zY%AR9|;i)x0j79M_{~ZL?pjtiTe8m$-KE3U#{!*4< zj24w3{}*MMza~Ka1G3D=oPUEX6FEE@pdN-ibR1rQx_6wY`|JXEYo$bgF@8SvdfudY z@5(+F{n9H)Sh&2ok1>o2j}2)J8j}DWNkNejtrhI6h?XeU3COdf&`8!+T?8WY$zH1^?8?w7ERr4&+-6K82wlr6w%edl-9-kZgpXdE|UBZz0Rb}#bKMx80@`CY}=vr5}O zCs5;9ganoPeJrOR{QU}i{R(nOW4!M$EP6ORlfzJ)8s1rQ7ctn6?wo(iUBY7Oe8c!E z=Uv+yFxn!izyo*!lnV9RCy>5d?Q`2rt3u}yUDWK95qQYLX3SPV3^`z}6CRnRw@Div zK8WZ6N)FjZ9q2Tj`; z!7hz!u57wU9fWT=tAlhD!1}-)FnM}w_)mR4bJZ$vK_n#dBMIl9q@Bj1U)sx=orJrMT|C4|fYTtc8u_Wj6DnfIj z)qF0@VI{Q-yr0n6HJEz5weDK z>u+fS6?8ehdcZC=E#uZ-441Q{nykn|7BUHT2N4DovhqD=V=1MKsWYNpwwz@t7)@?~ z-`Sj_Yf^*5_+;fods?iyg&Uc*6%u2(a6wVJ7)Z_I{ySu7tM>{r6vUFh@f@IsRjeU4 ztf$R%uicse_>4?UA1nA!sR%sO>vFKM1Awdz6vi9+5zPx~c{6#uQnB0R`Kos#pT#gV z0;#zs#39~`>dQ8mUDjh}#X8#&{ z`!}Tnxc!fzBcofwM92ee$8l) zAZ5ixtvCfZ+jpEIuQ=O(K>hqH@u@mj*R}L~+7iwSJ_xl67q=nAW`;r+dB}6H>8dau z-I&_035FbnCygV6NY16)>~@k@F>kxTa{C|5G0niU36C#iW!|sq+eM!c(oq3?aNNMJ zq~@tySauyOqF7YAL%I&li0BjutSlz}oo)bdT{-QIdF`jF50ABCaC@2ipMpHBUO2)n zVv)8h+8ehbjzjXQLLwJ)L?ZH{tzhdiLTO(bGB;E@@B^J9wp#{;)fF>9ciF5UN}ws(0XyQxjPP0)4LY9`~_%Si@Ds}1Tq@gjyG^z zX5A1@q|B$n(#V6o@>IlJ;hm~-rf^f!dN1&s0*Y5zEd&ppcLL?pbV zJU@q~0a6lSp=dG6TT$(h6jp<4DG`cg%(668d?^Sp5-%{YwkaU zt3T--JAY{TPU?oNy^{W4BeN zv!LOBR4%?x|0k7;P`BC@1JbJp8n@;Ds#btGQjqZ|iT@Fg(jNsi(qBPtoRfNfgWPn; z73RJ~*mFdl(oS3>oGAJe9QM|x->kIp=^-+GOK?OosoLaJbry-LPIOq+Ey6aJ6)ax9 zYaQJcly<2@1;h_D2?mD7{U9hc==jKRWJ~y#h=i=-QSzsSx4NZ0j#Z`y(+ut;*c9ER zU1`m!8?vUU8{l_nR<1vU*yf&=%v_k?JjJdf)V(}MxB1>R7jj_LX|n(ACgLR4H*EZ` z#uGH}oR;)Y4R+t3M})rvQ!-+QY0eu!Kd3PF{;Etg3TUU{4OQdbQ7*0c{?S66e3|Ou z5RWz)jdP(Sq~!B^c+nzqA=j>3FhCI?hR60no19&#Bk)P7g?6dD#nhXCT^?0=*Atn% z1O7UM+8J*ssi&Kvue5Emx;it(+Vc|T9GE==_Y_UzEWBf}N-CqK87Z2hr*(ACIcGoG z=rCmPshEmTTE_T8WKv5+O7vINZF|Vz>Ki5yLFx(xxGl@pR-+8?F47M z6hu`;;2Cf+6q`jvTEUb(2d!e~WJ!tLrs*fjOz~2*MxWzSaZtA6lNr-7OFZpX77KH~ zB#c zkXuHec^fEps!T?TD?7~?N+pbgWD;&rJioPt zgE;Zm0)f}f2yiokEG<6+J20*m@X1H7=EPxd6=Ac1gn&##XWX3wN9j7Y+65euUX}V9 zjh~HZ$M?rny;VTlHACBC5u^Z#X3SbGWJwL+yEuT*9qDXcw!QhQM`vr1?CWc|S0)QU z7Fhmb=#M#$8RvQhhw0z!Cmq8B0ut}S=V61 zQsu)XpiS=VF1E1}^JT58*Pf__xSm}MR7GtCu=}N{ZPdSQ4NThNK3_EN35-jn^ zA_!`oI8|2`LqLO_;QY!G3CFxc{hBkq`>QlWg3~^jQy%^ay+) z+&?>cvg8JMNdgXCojx>qgjlIK&hV6PLmZZ;W-!^1ftC;T8f=);!Z%%sW@fbZlO%k& zhHY?=eQMN{!S)X^e%7FQ+sBn6fir2CFDUtB#Q$p#TF{Cg_2-%94|ye#_<+pRfRqE;>(_$`a9jjKpz6Xf$sN6fB&&qH;$;nGkeb=S#{>aJH|3u-=xa5UN|$9 z^uz^&hIaCxH6ga$7eDZ_Wtfh>$vEdC?%j`SDC0b+BiUt_0Mz>j`)vbP*&p}Lb{Cx; zvQ`0wXqgz`SL-YZbMq*zE%zQfIfhWk!ClH%85MY2ek)I_v6zi!ubGY{HTAVL{l>hy z#HolBO`bL$*i`*4n!Nh(Oy3_H6ca^Z`Q-WlQ({E(1DX0)K;6Y}EF1ckE7_#n{hyUh z{(@!Wd=I5E@v(ENbz(D$DtvUGgZk=JNSX%saiZ zPXyVqwR1ZpD|yQ!Gr-AscDS@B8|0}oMloO9R!ynx(3rTc6%K8!NFfcbf{ufqrH&1` z9Er%p58ZziCp4`3%(1XZ6K9NQeC3-E$RL@YF)XWQNU3PJv~gtD^cE`OB9A}f8mrhlSnq~%BJKOv$MM|AGO-4o^M(lo?fVir3*JVlv2@luF7lFW2wr}&7FP( zRnl~Ss@E)){p|sO;lXljYF_GjLyPVh0Bt5PG`;b@S!y0cjWdM1@@)dnYvaeyd2S)exAgo7vw1&KN`pr@kYLcl-KyIrNZ0dtg zkXF7(g1O*Z>8pTugcs?{IZQN}@O8T+PiZ}xUkTE78`=9dlc&IqDY7{!WB3!(!^g@P zmaK9o&q+Nt4BLR6zfG5W$&&6&$Z(#t?sz&egDg3(6SN{3AMsUXVnKF-y5dtep@MD+ zV63UecL%#A7kaE4_OS|eucEsaXvDbo^K4!wYnGyHjL3AjvjWHS?!D?8LHOP%0U`7AuYovM^m+AE!_Z;O_)9$y zcKyJ*GQ6UO!Bm5SG&FbNt)}9-qCPt~llAF=qFl~+AQh}D!^tWlh{becPqZ1pT&s{T zDLDQZo?sck#Opk>6VprYc$*c!_C2pG=FgG7&QX%7u{d>k!oj0=Gx!~1sIobWt!8$J%lHSH$<9ta z#rO?(UJOhJ`XdxH)mzv^OnCEbIEjYI>k|b|gi3y5Ii}9o^K=tY8Jo9)T8oB@`%Rkr z^**|Ot9QBi24;8Y2$Ttn+1d{~3O_0&ctQWjGKbd;;`LtdAWG7x^-WMFSjXY%Ss7iK zTH~80e~F`U2gHfIUw2;C3=npV1Nj+p??#dX@w-|0LSy-c-FENLH>_n1o#$tL_=@X^ z02>Hjo(mXi6Xy>>;q_h@Neb`kMlommye5eu-a2*qbR`+|H`c~3@43IZHMwW@5fh4F z8CFEhiJEwr9#}m^3=^%Qr3nG8-ZHoQz8m~TU2O_$GlH5z+#knbd=^M z?W0@6fE30ZI?jxt*An=`^z-q;17prs2v2p-d4cwxk z@4X-$ecRbJ84qNHWNXb+i|w5Y*)i-S`%jzdQbL`3W_#%^m$FRGTq;z|H<$uyjo&`r z{*}Y{t*gO+s8=h=)zZ`n( z_l?3=#T1SR;fyZpMhdXCeP!s4&&G(jN_`kysgS&(_jfLn(IzL$n~(9&qbC^{y-BSl z!j~b(>1+U+nhJKJa@>OJ=;5#ECRnK-GALtcoWw;U#DxbXW45O~8x!kU5?Yq&TWXWH zE+pMdN!KFQ-&TEc5VShEg4xR`>r0v{qub2J(v+yrh8ShZ)CzLq$#*0m@>ut4yK$lS z^&}M)AM;6oc?(2)qS?hwJ;=t`lOy2JxUgh2OBNd*4BeBN{hg26WRsOJ)?KD`A&wY-n~7=mKM> z9{D|-uRNID-CgZ);dxs+2=i00t>DpS5aZi<+VOCGhxlHCN?m(I^ME6<8+FpSTL8qN z@Vdj>dKb;C0p6GAMvG~;Cz=f2Smoz(*|T6brH>!1BOb+moEvY;tGrHZPQ-W1oiG=p zqK#H^>lPoGIH{8FC;QHbrlw#IwK1xiKJ^-R@AK;JUhw@&@P~@JL@}n`w@vIq+mRha ze(uPMI+>%GGAVN=E%L_1^5B5cJLhGt8*YUirtmRWSNBa8<_c0?;v!A8F#obKY{yG?b z(5*#WYZ%58J_(h_MM^xd4J~h!S=BS^#JY_2b#IhikbkZpCT15&0&xynxU@sNp1&Vx zXbUped^)VKt*u=oYc<(SSqL{+*LGzMQj`J3GR#a!86H+zZSoe%N(O1~meRr*)TT|F z!La9h)HVUyz2=NU%Tba&AG`tBG^$@$MP{IM@!%ia^i>X(K<}rEMbxxG*a}2$} zG1^#GdOZHbKxwHe-r-c&Hh}rTRf%25Rf(OdN2l^em@ihhzYAF_TVjVCgD~M-J=WvT z2JaakNxqp`MoinW(yV~9uOZsl|0LpVwqG*x#aqVN{ieDaF? z11}~C@Z&p%;C7N{KbN=VvrR}85|KB`(e5fdQOz|RFeJI0pPtS~?%_N`;{_(hmQ(|n zg}I6II<2WUf;3%xvP$MGogl3^QyE`;HdmiBWPHn_uZPIm^dbZaw`jXLr=zcwHk zct(KkuN2^@Y@$?t(qR*;5*#yO!}&qw(*@_!Xndp@1haa$;7)BD6L&ArZzr+xQA#x{ zX#KT5(=xNA86`thNAl}2;ZOL9O09awE(by7u+V5D

tqe6m2Z_&emei%~$mU5>%b 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 diff --git a/libs/mf/README.md b/libs/mf/README.md index 61ab577b..fee9d2c3 100644 --- a/libs/mf/README.md +++ b/libs/mf/README.md @@ -22,9 +22,7 @@ Module Federation allows loading separately compiled and deployed code (like mic ## Supporting Several Technologies -✅ Supports webpack, rspack (experimental, nextgen), esbuild - -✅ Supports Module Federation and Native Federation +✅ Supports webpack and rspack (experimental, nextgen) ✅ Supports the Angular CLI and Nx @@ -61,13 +59,13 @@ We are going to provide at least one major per Angular major to keep track with Beginning with Angular 13, we had to add some changes to adjust to the Angular CLI. Please see the next section for this. -## webpack, rspack, and esbuild +## webpack and rspack -Since version 19, the plugin's ng-add schematic asks whether you want to use the traditional Webpack-based builder, the (currently experimental, fast, next-generation) rspack builder, or esbuild (fast, Angular CLI's new default). +The plugin's ng-add schematic asks whether you want to use the traditional Webpack-based builder or the (currently experimental, fast, next-generation) rspack builder. Both are supported via Module Federation. -The first two are supported via Module Federation. For the rspack integration, we build on `@nx/angular-rspack` — originally Colum Ferry's awesome community project (`@ng-rsbuild/plugin-angular` / `@ng-rspack/build`), now maintained by the Nx team. So, all credit for making rspack work with Angular goes to Colum and the Nx team. +For the rspack integration, we build on `@nx/angular-rspack` — originally Colum Ferry's awesome community project (`@ng-rsbuild/plugin-angular` / `@ng-rspack/build`), now maintained by the Nx team. So, all credit for making rspack work with Angular goes to Colum and the Nx team. -The esbuild integration is technically a wrapper around the Angular CLI's new default builder, the ApplicationBuilder. It is powered by Native Federation, our bundler-agnostic implementation based on web standards like ECMAScript modules and Import Maps. +Looking for the esbuild/ApplicationBuilder route? That is Native Federation, which now lives in its own organization: [github.com/native-federation](https://github.com/native-federation). ## Update diff --git a/libs/mf/src/schematics/init-webpack/schematic.ts b/libs/mf/src/schematics/init-webpack/schematic.ts index 23e12828..b99a4f85 100644 --- a/libs/mf/src/schematics/init-webpack/schematic.ts +++ b/libs/mf/src/schematics/init-webpack/schematic.ts @@ -240,7 +240,7 @@ export default function config(options: MfSchematicSchema): Rule { `\nWARNING: This package uses the traditional webpack-based Module Federation implementation and not the fast new esbuild-based ApplicationBuilder.`, ); console.warn( - `\nFor new projects, consider Native Federation as an alternative: https://shorturl.at/0ZQ0j`, + `\nFor new projects, consider Native Federation as an alternative: https://github.com/native-federation`, ); console.warn( `\nHowever, if you want to add a new host or remote to an existing Module Federation-based system, this package is what you are looking for.`, diff --git a/libs/mf/src/schematics/init/schema.d.ts b/libs/mf/src/schematics/init/schema.d.ts index 3ad98085..d5d3a3c1 100644 --- a/libs/mf/src/schematics/init/schema.d.ts +++ b/libs/mf/src/schematics/init/schema.d.ts @@ -4,8 +4,5 @@ export interface InitSchema { nxBuilders: boolean | undefined; type: 'host' | 'dynamic-host' | 'remote' | 'legacy'; skipConfirmation: boolean; - stack: - | 'module-federation-webpack' - | 'module-federation-rsbuild-experimental' - | 'native-federation-esbuild'; + stack: 'module-federation-webpack' | 'module-federation-rsbuild-experimental'; } diff --git a/libs/mf/src/schematics/init/schema.json b/libs/mf/src/schematics/init/schema.json index ad06dd3f..ae0581fb 100644 --- a/libs/mf/src/schematics/init/schema.json +++ b/libs/mf/src/schematics/init/schema.json @@ -35,8 +35,7 @@ "type": "string", "enum": [ "module-federation-webpack", - "module-federation-rsbuild-experimental", - "native-federation-esbuild" + "module-federation-rsbuild-experimental" ], "x-aliases": ["s"], "x-prompt": { @@ -50,10 +49,6 @@ { "value": "module-federation-rsbuild-experimental", "label": "Module Federation with rspack (experimental, nextgen)" - }, - { - "value": "native-federation-esbuild", - "label": "Native Federation with esbuild (bundler-agnostic)" } ] } diff --git a/libs/mf/src/schematics/init/schematic.ts b/libs/mf/src/schematics/init/schematic.ts index 1b846610..d502c38b 100644 --- a/libs/mf/src/schematics/init/schematic.ts +++ b/libs/mf/src/schematics/init/schematic.ts @@ -1,16 +1,9 @@ import { chain, Rule, schematic, noop } from '@angular-devkit/schematics'; -import { - NodePackageInstallTask, - RunSchematicTask, -} from '@angular-devkit/schematics/tasks'; - import { InitSchema } from './schema'; -type DelegationOptions = Omit; - export function init(options: InitSchema): Rule { - return async function (tree, context) { + return async function () { options = normalizeOptions(options); const { stack, ...delegationOptions } = options; @@ -21,31 +14,11 @@ export function init(options: InitSchema): Rule { case 'module-federation-rsbuild-experimental': return chain([schematic('init-rspack', delegationOptions)]); - - case 'native-federation-esbuild': - initNativeFederation(context, delegationOptions); } return noop(); }; } -function initNativeFederation(context, delegationOptions: DelegationOptions) { - context.addTask( - new RunSchematicTask( - '@angular-architects/native-federation', - 'init', - delegationOptions, - ), - [ - context.addTask( - new NodePackageInstallTask({ - packageName: '@angular-architects/native-federation@^19.0.0', - }), - ), - ], - ); -} - function normalizeOptions(options: InitSchema): InitSchema { return { ...options, diff --git a/libs/native-federation-core/LICENSE b/libs/native-federation-core/LICENSE deleted file mode 100644 index 3edc92e0..00000000 --- a/libs/native-federation-core/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright 2021 Softarc Consulting GmbH - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/libs/native-federation-core/README.md b/libs/native-federation-core/README.md deleted file mode 100644 index a25f91fb..00000000 --- a/libs/native-federation-core/README.md +++ /dev/null @@ -1,509 +0,0 @@ -# @softarc/native-federation - -Native Federation is a "browser-native" implementation of the successful mental model behind wepback Module Federation for building Micro Frontends and plugin-based solutions. It can be **used with any framework and build tool** for implementing **Micro Frontends** and plugin-based architectures. - -## Features - -- ✅ Mental Model of Module Federation -- ✅ Future Proof: Independent of build tools like webpack and frameworks -- ✅ Embraces Import Maps -- an emerging browser technology -- and EcmaScript modules -- ✅ Easy to configure -- ✅ Blazing Fast: The reference implementation not only uses the fast esbuild; it also caches already built shared dependencies (like Angular itself). However, as mentioned above, feel free to use it with any other build tool. - -## Stack - -This library allows to augment your build process, to configure hosts (Micro Frontend shells) and remotes (Micro Frontends), and to load remotes at runtime. - -While this core library can be used with any framework and build tool, there is a higher level API on top of it. It hooks into the Angular CLI and provides a builder and schematics: - -![Stack](https://github.com/angular-architects/module-federation-plugin/raw/main/libs/native-federation-core/stack.png) - -> Please find the [Angular-based version here](https://www.npmjs.com/package/@angular-architects/native-federation). - -> Please find the [vite plugin here](https://www.npmjs.com/package/@gioboa/vite-module-federation). - -Also, other higher level abstractions on top of this core library are possible. - -## About the Mental Model - -The underlying mental model allows for runtime integration: Loading a part of a separately built and deployed application into your's. This is needed for Micro Frontend architectures but also for plugin-based solutions. - -For this, the mental model introduces several concepts: - -- **Remote:** The remote is a separately built and deployed application. It can **expose EcmaScript** modules that can be loaded into other applications. -- **Host:** The host loads one or several remotes on demand. For your framework's perspective, this looks like traditional lazy loading. The big difference is that the host doesn't know the remotes at compilation time. -- **Shared Dependencies:** If a several remotes and the host use the same library, you might not want to download it several times. Instead, you might want to just download it once and share it at runtime. For this use case, the mental model allows for defining such shared dependencies. -- **Version Mismatch:** If two or more applications use a different version of the same shared library, we need to prevent a version mismatch. To deal with it, the mental model defines several strategies, like falling back to another version that fits the application, using a different compatible one (according to semantic versioning) or throwing an error. - -## Example - -- [VanillaJS example](https://github.com/manfredsteyer/native-federation-core-microfrontend). -- [React example](https://github.com/manfredsteyer/native-federation-react-example) - - This example also shows the **watch mode** for compiling a federated application -- [Vite + Svelte example](https://github.com/gioboa/svelte-microfrontend-demo) -- [Vite + Angular example powered by AnalogJS](https://github.com/manfredsteyer/native-federation-vite-angular-demo) -- **Your Example:** If you have an example with aspects not covered here, let us know. We are happy to link it here. - -## Credits - -Big thanks to: - -- [Zack Jackson](https://twitter.com/ScriptedAlchemy) for originally coming up with the great idea of Module Federation and its successful mental model -- [Tobias Koppers](https://twitter.com/wSokra) for helping to make Module Federation a first class citizen of webpack -- [Florian Rappl](https://twitter.com/FlorianRappl) for an good discussion about these topics during a speakers dinner in Nuremberg -- [The Nx Team](https://twitter.com/NxDevTools), esp. [Colum Ferry](https://twitter.com/FerryColum), who seamlessly integrated webpack Module Federation into Nx and hence helped to spread the word about it (Nx + Module Federation === ❤️) -- [Michael Egger-Zikes](https://twitter.com/MikeZks) for contributing to our Module Federation efforts and brining in valuable feedback -- The Angular CLI-Team, esp. [Alan Agius](https://twitter.com/AlanAgius4) and [Charles Lyding](https://twitter.com/charleslyding), for working on the experimental esbuild builder for Angular -- [Giorgio Boa](https://twitter.com/giorgio_boa) for implementing the awesome vite plugin for module federation. - -## Using this Library - -### Installing the Library - -``` -npm i @softarc/native-federation -``` - -As Native Federation is tooling agnostic, we need an adapter to make it work with specific build tools. The package `@softarc/native-federation-esbuild` contains a simple adapter that uses esbuild: - -``` -npm i @softarc/native-federation-esbuild -``` - -In some situations, this builder also delegates to rollup. This is necessary b/c esbuild does not provide all features we need (yet). We hope to minimize the usage of rollup in the future. - -You can also provide your own adapter by providing a function aligning with the `BuildAdapter` type. - -### Augment your Build Process - -Just call three helper methods provided by our `federationBuilder` in your build process to adjust it for Native Federation. - -```typescript -import * as esbuild from 'esbuild'; -import * as path from 'path'; -import * as fs from 'fs'; -import { esBuildAdapter } from '@softarc/native-federation-esbuild'; -import { federationBuilder } from '@softarc/native-federation/build'; - - -const projectName = 'shell'; -const tsConfig = 'tsconfig.json'; -const outputPath = `dist/${projectName}`; - -/* - * Step 1: Initialize Native Federation -*/ -await federationBuilder.init({ - options: { - workspaceRoot: path.join(__dirname, '..'), - outputPath, - tsConfig, - federationConfig: `${projectName}/federation.config.js`, - verbose: false, - }, - - /* - * As this core lib is tooling-agnostic, you - * need a simple adapter for your bundler. - * It's just a matter of one function. - */ - adapter: esBuildAdapter -}); - -/* - * Step 2: Trigger your build process - * - * You can use any tool for this. Here, we go - * with a very simple esbuild-based build. - * - * Just respect the externals in - * `federationBuilder.externals`. -*/ - -[...] - -await esbuild.build({ - [...] - external: federationBuilder.externals, - [...] -}); - -[...] - -/* - * Step 3: Let the build method do the additional tasks - * for supporting Native Federation -*/ - -await federationBuilder.build(); -``` - -The method `federationBuilder.build` bundles the shared and exposed parts of your app. - -### Configuring Hosts - -The `withNativeFederation` function sets up a configuration for your applications. This is an example configuration for a host: - -The `shareAll` helper shares all your dependencies defined in your `package.json`. The `package.json` is look up as described above: - -```typescript -// shell/federation.config.js - -const { withNativeFederation, shareAll } = require('@softarc/native-federation/build'); - -module.exports = withNativeFederation({ - name: 'host', - - shared: { - ...shareAll({ - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - includeSecondaries: false, - }), - }, -}); -``` - -The options passed to shareAll are applied to all dependencies found in your `package.json`. - -This might come in handy in an mono repo scenario and when doing some experiments/ trouble shooting. - -> Since v21.1 it's also possible to add overrides to the shareAll for specific packages. - -```typescript -// shell/federation.config.js - -const { withNativeFederation, shareAll } = require('@softarc/native-federation/build'); - -module.exports = withNativeFederation({ - name: 'host', - - shared: { - ...shareAll( - { - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - }, - { - overrides: { - 'package-a/themes/xyz': { - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - includeSecondaries: { skip: '@package-a/themes/xyz/*' }, - build: 'package', - }, - 'package-b': { - singleton: false, - strictVersion: true, - requiredVersion: 'auto', - includeSecondaries: { skip: 'package-b/icons/*' }, - build: 'package', - }, - }, - }, - ), - }, -}); -``` - -### Share Helper - -The helper function share adds some additional options for the shared dependencies: - -```typescript -shared: share({ - "package-a": { - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - includeSecondaries: true - }, - [...] -}) -``` - -The added options are `requireVersion: 'auto'` and `includeSecondaries`. - -#### requireVersion: 'auto' - -If you set `requireVersion` to `'auto'`, the helper takes the version defined in your `package.json`. - -This helps to solve issues with not (fully) met peer dependencies and secondary entry points (see Pitfalls section below). - -By default, it takes the `package.json` that is closest to the caller (normally the `webpack.config.js`). However, you can pass the path to an other `package.json` using the second optional parameter. Also, you need to define the shared libray within the node dependencies in your `package.json`. - -Instead of setting requireVersion to auto time and again, you can also skip this option and call `setInferVersion(true)` before: - -```typescript -setInferVersion(true); -``` - -#### includeSecondaries - -If set to `true`, all secondary entry points are added too. In the case of `@angular/common` this is also `@angular/common/http`, `@angular/common/http/testing`, `@angular/common/testing`, `@angular/common/http/upgrade`, and `@angular/common/locales`. This exhaustive list shows that using this option for `@angular/common` is not the best idea because normally, you don't need most of them. - -> `includeSecondaries` is true by default. - -However, this option can come in handy for quick experiments or if you want to quickly share a package like `@angular/material` that comes with a myriad of secondary entry points. - -Even if you share too much, Native Federation will only load the needed ones at runtime. However, please keep in mind that shared packages can not be tree-shaken. - -To skip some secondary entry points, you can assign a configuration option instead of `true`: - -```typescript -shared: share({ - "@angular/common": { - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - includeSecondaries: { - skip: ['@angular/common/http/testing'] - } - }, - [...] -}) -``` - -### includeSecondaries - -Since v21 it's also possible to resolve Glob exports by enabling the `globResolve` property: - -```typescript -shared: share({ - "package-a": { - singleton: true, - strictVersion: true, - requiredVersion: "auto", - includeSecondaries: {resolveGlob: true} - }, - [...] -}) -``` - -This is disabled by default since it will create a bundle of every valid exported file it finds, **Only use this feature in combination with `ignoreUnusedDeps` flag**. If you want to specifically skip certain parts of the glob export, you can also use the wildcard in the skip section: - -```typescript -shared: share({ - "package-a/themes/xyz": { - singleton: true, - strictVersion: true, - requiredVersion: "auto", - includeSecondaries: {skip: "package-a/themes/xyz/*", resolveGlob: true} - }, - [...] -}) -``` - -Finally, it's also possible to break out of the "removeUnusedDep" for a specific external if desired, for example when sharing a whole suite of external modules. This can be handy when you want to avoid the chance of cross-version secondary entrypoints being used by the different micro frontends. E.g. mfe1 uses @angular/core v20.1.0 and mfe2 uses @angular/core/rxjs-interop v20.0.8, then you might want to use consistent use of v20.1.0 so rxjs-interop should be exported by mfe1. The "keepAll" prop allows you to enforce this: - -```typescript -shared: share({ - "@angular/core": { - singleton: true, - strictVersion: true, - requiredVersion: "auto", - includeSecondaries: {keepAll: true} - }, - [...] -}) -``` - -The API for configuring and using Native Federation is very similar to the one provided by our Module Federation plugin [@angular-architects/module-federation](https://www.npmjs.com/package/@angular-architects/native-federation). Hence, most the articles on it are also valid for Native Federation. - -### Sharing - -The `shareAll`-helper used here shares all dependencies found in your `package.json`. Hence, they only need to be loaded once (instead of once per remote and host). If you don't want to share all of them, you can opt-out of sharing by using the `skip` option: - -```typescript -module.exports = withNativeFederation({ - [...] - - // Don't share my-lib - skip: [ - 'my-lib' - ] - - [...] -} -``` - -### Sharing Mapped Paths (Monorepo-internal Libraries) - -Paths mapped in your `tsconfig.json` are shared by default too. While they are part of your (mono) repository, they are treaded like libraries: - -```json -{ - "compilerOptions": { - [...] - "paths": { - "shared-lib": [ - "libs/shared-lib/index.ts" - ] - } - } -} -``` - -If you don't want to share (all of) them, put their names into the skip array (see above). - -### Configuring Remotes - -When configuring a remote, you can expose files that can be loaded into the shell at runtime: - -```javascript -const { withNativeFederation, shareAll } = require('@softarc/native-federation/build'); - -module.exports = withNativeFederation({ - name: 'mfe1', - - exposes: { - './component': './mfe1/component', - }, - - shared: { - ...shareAll({ - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - includeSecondaries: false, - }), - }, -}); -``` - -### Initializing a Host - -On startup, call the `initFederation` method. It takes a mapping between the names of remotes and their `remoteEntry.json`. This is a file containing meta data generated by the augmented build process (see above). - -```typescript -import { initFederation } from '@softarc/native-federation'; - -(async () => { - await initFederation({ - mfe1: 'http://localhost:3001/remoteEntry.json', - }); - - await import('./app'); -})(); -``` - -You can also pass the name of a file with the key data about your remotes: - -```typescript -import { initFederation } from '@softarc/native-federation'; - -(async () => { - await initFederation('assets/manifest.json'); - - await import('./app'); -})(); -``` - -Following the ideas of our friends at [Nrwl](https://nrwl.io), we call such a file a manifest: - -```json -{ - "mfe1": "http://localhost:3001/remoteEntry.json" -} -``` - -Manifests allow to adjust your application to different environments without any recompilation. - -## Initializing a Remote - -For initializing a remote, also call `initFederation`. If you don't plan to load further remotes into your remote, you don't need to pass any parameters: - -```typescript -import { initFederation } from '@softarc/native-federation'; - -(async () => { - await initFederation(); - await import('./component'); -})(); -``` - -### Loading a Remote - -To load a remote, just call the `loadRemoteModule` function: - -```typescript -const module = await loadRemoteModule({ - remoteName: 'mfe1', - exposedModule: './component', -}); -``` - -If you know the type of the loaded module (perhaps you have a shared interface), you can use it as a type parameter: - -```typescript -const module = await loadRemoteModule({ - remoteName: 'mfe1', - exposedModule: './component', -}); -``` - -### Polyfill - -This library uses Import Maps. As currently not all browsers support this emerging browser feature, we need a polyfill. We recommend the polyfill `es-module-shims` which has been developed for production use cases: - -```html - - - - - -``` - -The script with the type `esms-options` configures the polyfill. This library was built for shim mode. In this mode, the polyfill provides some additional features beyond the proposal for Import Maps. These features, for instance, allow for dynamically creating an import map after loading a first EcmaScript module. Native Federation uses this possibility. - -To make the polyfill to load your EcmaScript modules (bundles) in shim mode, assign the type `module-shim`. - -## React and Other CommonJS Libs - -Native Federation uses Web Standards like EcmaScript Modules. Most libs and frameworks support them meanwhile. Unfortunately, React still uses CommonJS (und UMD). We do our best to convert these libs to EcmaScript Modules. In the case of React there are some challenges due to the dynamic way the React bundles use the `exports` object. - -As the community is moving to EcmaScrpt Modules, we expect that these issues will vanish over time. In between, we provide some solutions for dealing with CommonJS-based libraries using `exports` in a dynamic way. - -One of them is `fileReplacemnts`: - -```javascript -import { reactReplacements } from '@softarc/native-federation-esbuild/src/lib/react-replacements'; -import { createEsBuildAdapter } from '@softarc/native-federation-esbuild'; - -[...] - -createEsBuildAdapter({ - plugins: [], - fileReplacements: reactReplacements.prod -}) -``` - -Please note that the adapter comes with `fileReplacements` settings for React for both, `dev` mode and `prod` mode. For similar libraries you can add your own replacements. Also, using the `compensateExports` property, you can activate some additional logic for such libraries to make sure the exports are not lost - -```javascript -createEsBuildAdapter({ - plugins: [], - fileReplacements: reactReplacements.prod, - compensateExports: [new RegExp('/my-lib/')], -}); -``` - -The default value for `compensateExports` is `[new RegExp('/react/')]`. - -## More: Blog Articles - -Find out more about our work including Micro Frontends and Module Federation but also about alternatives to these approaches in our [blog](https://www.angulararchitects.io/en/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/). - -## More: Angular Architecture Workshop (100% online, interactive) - -In our [Angular Architecture Workshop](https://www.angulararchitects.io/en/angular-workshops/advanced-angular-enterprise-architecture-incl-ivy/), we cover all these topics and far more. We provide different options and alternatives and show up their consequences. - -[Details: Angular Architecture Workshop](https://www.angulararchitects.io/en/angular-workshops/advanced-angular-enterprise-architecture-incl-ivy/) diff --git a/libs/native-federation-core/build.ts b/libs/native-federation-core/build.ts deleted file mode 100644 index 362bc1b8..00000000 --- a/libs/native-federation-core/build.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './src/build'; diff --git a/libs/native-federation-core/eslint.config.mjs b/libs/native-federation-core/eslint.config.mjs deleted file mode 100644 index c710a898..00000000 --- a/libs/native-federation-core/eslint.config.mjs +++ /dev/null @@ -1,33 +0,0 @@ -import baseConfig from '../../eslint.base.config.mjs'; -import jsoncEslintParser from 'jsonc-eslint-parser'; - -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: {}, - }, - { - files: ['**/*.json'], - rules: { - '@nx/dependency-checks': 'warn', - }, - languageOptions: { - parser: jsoncEslintParser, - }, - }, - { - ignores: ['src/lib/core/build-adapter.js'], - }, -]; diff --git a/libs/native-federation-core/jest.config.ts b/libs/native-federation-core/jest.config.ts deleted file mode 100644 index 042d1157..00000000 --- a/libs/native-federation-core/jest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'native-federation-core', - preset: '../../jest.preset.js', - testEnvironment: 'node', - transform: { - '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], - }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/native-federation-core', -}; diff --git a/libs/native-federation-core/package.json b/libs/native-federation-core/package.json deleted file mode 100644 index 572e1eef..00000000 --- a/libs/native-federation-core/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "@softarc/native-federation", - "version": "3.5.5", - "type": "commonjs", - "license": "MIT", - "dependencies": { - "json5": "^2.2.0", - "chalk": "^5.6.2", - "@softarc/native-federation-runtime": "~3.5.4", - "fast-glob": "^3.3.3", - "@softarc/sheriff-core": "^0.19.2" - } -} diff --git a/libs/native-federation-core/project.json b/libs/native-federation-core/project.json deleted file mode 100644 index 49400c58..00000000 --- a/libs/native-federation-core/project.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "native-federation-core", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/native-federation-core/src", - "projectType": "library", - "targets": { - "build": { - "executor": "@nx/js:tsc", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/libs/native-federation-core", - "main": "libs/native-federation-core/src/index.ts", - "tsConfig": "libs/native-federation-core/tsconfig.lib.json", - "assets": [ - "libs/native-federation-core/*.md", - "libs/native-federation-core/LICENSE" - ] - } - }, - "publish": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-core npm {args.ver} {args.tag}" - } - }, - "publish-local": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-core verdaccio {args.ver}" - } - } - }, - "tags": ["org:softarc", "scope:nf"] -} diff --git a/libs/native-federation-core/src/build.ts b/libs/native-federation-core/src/build.ts deleted file mode 100644 index 090846ef..00000000 --- a/libs/native-federation-core/src/build.ts +++ /dev/null @@ -1,37 +0,0 @@ -export { DEFAULT_SKIP_LIST } from './lib/core/default-skip-list'; - -export { - NormalizedFederationConfig, - SharedConfig, -} from './lib/config/federation-config'; -export { withNativeFederation } from './lib/config/with-native-federation'; -export { - BuildAdapter, - BuildAdapterOptions, - BuildKind, - BuildResult, - EntryPoint, - setBuildAdapter, -} from './lib/core/build-adapter'; -export { buildForFederation } from './lib/core/build-for-federation'; -export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings'; -export { FederationOptions } from './lib/core/federation-options'; -export { getExternals } from './lib/core/get-externals'; -export { loadFederationConfig } from './lib/core/load-federation-config'; -export { writeFederationInfo } from './lib/core/write-federation-info'; -export { writeImportMap } from './lib/core/write-import-map'; -export { MappedPath } from './lib/utils/mapped-paths'; -export { RebuildQueue } from './lib/utils/rebuild-queue'; -export { - findRootTsConfigJson, - share, - shareAll, -} from './lib/config/share-utils'; -export { - BuildHelperParams, - federationBuilder, -} from './lib/core/federation-builder'; -export * from './lib/utils/build-result-map'; -export { hashFile } from './lib/utils/hash-file'; -export * from './lib/utils/errors'; -export { logger, setLogLevel } from './lib/utils/logger'; diff --git a/libs/native-federation-core/src/config.ts b/libs/native-federation-core/src/config.ts deleted file mode 100644 index 4fbabc71..00000000 --- a/libs/native-federation-core/src/config.ts +++ /dev/null @@ -1,2 +0,0 @@ -// For parity with our other libs -export * from './build'; diff --git a/libs/native-federation-core/src/index.ts b/libs/native-federation-core/src/index.ts deleted file mode 100644 index d538c163..00000000 --- a/libs/native-federation-core/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@softarc/native-federation-runtime'; diff --git a/libs/native-federation-core/src/lib/config/configuration-context.ts b/libs/native-federation-core/src/lib/config/configuration-context.ts deleted file mode 100644 index 8f73cdc1..00000000 --- a/libs/native-federation-core/src/lib/config/configuration-context.ts +++ /dev/null @@ -1,18 +0,0 @@ -export interface ConfigurationContext { - workspaceRoot?: string; - packageJson?: string; -} - -let _context: ConfigurationContext = {}; - -export function useWorkspace(workspaceRoot: string): void { - _context = { ..._context, workspaceRoot }; -} - -export function usePackageJson(packageJson?: string): void { - _context = { ..._context, packageJson }; -} - -export function getConfigContext(): ConfigurationContext { - return _context; -} diff --git a/libs/native-federation-core/src/lib/config/federation-config.ts b/libs/native-federation-core/src/lib/config/federation-config.ts deleted file mode 100644 index 763c5e8d..00000000 --- a/libs/native-federation-core/src/lib/config/federation-config.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { PreparedSkipList, SkipList } from '../core/default-skip-list'; -import { MappedPath } from '../utils/mapped-paths'; - -export interface SharedConfig { - singleton?: boolean; - strictVersion?: boolean; - requiredVersion?: string; - version?: string; - includeSecondaries?: boolean; - transient?: boolean; - platform?: 'browser' | 'node'; - build?: 'default' | 'separate'; - packageInfo?: { - entryPoint: string; - version: string; - esm: boolean; - }; -} - -export interface FederationConfig { - name?: string; - exposes?: Record; - shared?: Record; - sharedMappings?: Array; - skip?: SkipList; - externals?: string[]; - features?: { - mappingVersion?: boolean; - ignoreUnusedDeps?: boolean; - }; -} - -export interface NormalizedSharedConfig { - singleton: boolean; - strictVersion: boolean; - requiredVersion: string; - version?: string; - includeSecondaries?: boolean; - platform: 'browser' | 'node'; - build: 'default' | 'separate' | 'package'; - packageInfo?: { - entryPoint: string; - version: string; - esm: boolean; - }; -} - -export interface NormalizedFederationConfig { - name: string; - exposes: Record; - shared: Record; - sharedMappings: Array; - skip: PreparedSkipList; - externals: string[]; - features: { - mappingVersion: boolean; - ignoreUnusedDeps: boolean; - }; -} diff --git a/libs/native-federation-core/src/lib/config/share-utils.ts b/libs/native-federation-core/src/lib/config/share-utils.ts deleted file mode 100644 index 5689aef1..00000000 --- a/libs/native-federation-core/src/lib/config/share-utils.ts +++ /dev/null @@ -1,648 +0,0 @@ -import path = require('path'); -import fs = require('fs'); -import { cwd } from 'process'; -import { SharedConfig } from './federation-config'; -import { - DEFAULT_SKIP_LIST, - isInSkipList, - PreparedSkipList, - prepareSkipList, - SkipList, -} from '../core/default-skip-list'; -import { - findDepPackageJson, - getVersionMaps, - VersionMap, -} from '../utils/package-info'; -import { getConfigContext } from './configuration-context'; -import { logger } from '../utils/logger'; - -import { - KeyValuePair, - resolvePackageJsonExportsWildcard, -} from '../utils/resolve-wildcard-keys'; - -let inferVersion = false; - -export const DEFAULT_SECONDARIES_SKIP_LIST = [ - '@angular/router/upgrade', - '@angular/common/upgrade', -]; - -type IncludeSecondariesOptions = - | { skip: string | string[]; resolveGlob?: boolean; keepAll?: boolean } - | boolean; -type CustomSharedConfig = SharedConfig & { - includeSecondaries?: IncludeSecondariesOptions; -}; -type ConfigObject = Record; -type Config = (string | ConfigObject)[] | ConfigObject; - -export function findRootTsConfigJson(): string { - const packageJson = findPackageJson(cwd()); - const projectRoot = path.dirname(packageJson); - const tsConfigBaseJson = path.join(projectRoot, 'tsconfig.base.json'); - const tsConfigJson = path.join(projectRoot, 'tsconfig.json'); - - if (fs.existsSync(tsConfigBaseJson)) { - return tsConfigBaseJson; - } else if (fs.existsSync(tsConfigJson)) { - return tsConfigJson; - } - - throw new Error('Neither a tsconfig.json nor a tsconfig.base.json was found'); -} - -function findPackageJson(folder: string): string { - while ( - !fs.existsSync(path.join(folder, 'package.json')) && - path.dirname(folder) !== folder - ) { - folder = path.dirname(folder); - } - - const filePath = path.join(folder, 'package.json'); - if (fs.existsSync(filePath)) { - return filePath; - } - - throw new Error( - 'no package.json found. Searched the following folder and all parents: ' + - folder, - ); -} - -function lookupVersion(key: string, workspaceRoot: string): string { - const versionMaps = getVersionMaps(workspaceRoot, workspaceRoot); - - for (const versionMap of versionMaps) { - const version = lookupVersionInMap(key, versionMap); - - if (version) { - return version; - } - } - - throw new Error( - `Shared Dependency ${key} has requiredVersion:'auto'. However, this dependency is not found in your package.json`, - ); -} - -function lookupVersionInMap(key: string, versions: VersionMap): string | null { - const parts = key.split('/'); - if (parts.length >= 2 && parts[0].startsWith('@')) { - key = parts[0] + '/' + parts[1]; - } else { - key = parts[0]; - } - - if (key.toLowerCase() === '@angular-architects/module-federation-runtime') { - key = '@angular-architects/module-federation'; - } - - if (!versions[key]) { - return null; - } - return versions[key]; -} - -function _findSecondaries( - libPath: string, - excludes: string[], - shareObject: SharedConfig, - acc: Record, - preparedSkipList: PreparedSkipList, -): void { - const files = fs.readdirSync(libPath); - - const secondaries = files - .map((f) => path.join(libPath, f)) - .filter( - (f) => fs.lstatSync(f).isDirectory() && !f.endsWith('node_modules'), - ); - - for (const s of secondaries) { - if (fs.existsSync(path.join(s, 'package.json'))) { - const secondaryLibName = s - .replace(/\\/g, '/') - .replace(/^.*node_modules[/]/, ''); - - const inCustomSkipList = excludes.some( - (e) => - e === secondaryLibName || - (e.endsWith('*') && secondaryLibName.startsWith(e.slice(0, -1))), - ); - if (inCustomSkipList) continue; - - if (isInSkipList(secondaryLibName, preparedSkipList)) { - continue; - } - - acc[secondaryLibName] = { ...shareObject }; - } - - _findSecondaries(s, excludes, shareObject, acc, preparedSkipList); - } -} - -function findSecondaries( - libPath: string, - excludes: string[], - shareObject: SharedConfig, - preparedSkipList: PreparedSkipList, -): Record { - const acc = {} as Record; - _findSecondaries(libPath, excludes, shareObject, acc, preparedSkipList); - return acc; -} - -function getSecondaries( - includeSecondaries: IncludeSecondariesOptions, - libPath: string, - key: string, - shareObject: SharedConfig, - preparedSkipList: PreparedSkipList, -): Record | null { - let exclude = [...DEFAULT_SECONDARIES_SKIP_LIST]; - - let resolveGlob = false; - if (typeof includeSecondaries === 'object') { - if (Array.isArray(includeSecondaries.skip)) { - exclude = includeSecondaries.skip; - } else if (typeof includeSecondaries.skip === 'string') { - exclude = [includeSecondaries.skip]; - } - resolveGlob = !!includeSecondaries.resolveGlob; - } - - // const libPath = path.join(path.dirname(packagePath), 'node_modules', key); - - if (!fs.existsSync(libPath)) { - return {}; - } - - const configured = readConfiguredSecondaries( - key, - libPath, - exclude, - shareObject, - preparedSkipList, - resolveGlob, - ); - if (configured) { - return configured; - } - - // Fallback: Search folders - const secondaries = findSecondaries( - libPath, - exclude, - shareObject, - preparedSkipList, - ); - return secondaries; -} - -function readConfiguredSecondaries( - parent: string, - libPath: string, - exclude: string[], - shareObject: SharedConfig, - preparedSkipList: PreparedSkipList, - resolveGlob: boolean, -): Record | null { - const libPackageJson = path.join(libPath, 'package.json'); - - if (!fs.existsSync(libPackageJson)) { - return null; - } - - const packageJson = JSON.parse(fs.readFileSync(libPackageJson, 'utf-8')); - - const version = packageJson['version'] as string; - const esm = packageJson['type'] === 'module'; - - const exports = packageJson['exports'] as Record< - string, - Record - >; - - if (!exports) { - return null; - } - - const keys = Object.keys(exports).filter( - (key) => - key != '.' && - key != './package.json' && - key.startsWith('./') && - (exports[key]['default'] || - exports[key]['import'] || - typeof exports[key] === 'string'), - ); - - const result = {} as Record; - const discoveredFiles = new Set(); - - for (const key of keys) { - const secondaryName = path.join(parent, key).replace(/\\/g, '/'); - - const inCustomSkipList = exclude.some( - (e) => - e === secondaryName || - (e.endsWith('*') && secondaryName.startsWith(e.slice(0, -1))), - ); - if (inCustomSkipList) continue; - - if (isInSkipList(secondaryName, preparedSkipList)) { - continue; - } - - const entry = getDefaultEntry(exports, key); - - if (typeof entry !== 'string') { - console.log('No entry point found for ' + secondaryName); - continue; - } - - if ( - !entry?.endsWith('.js') && - !entry?.endsWith('.mjs') && - !entry?.endsWith('.cjs') - ) { - continue; - } - - const items = resolveGlobSecondaries( - key, - libPath, - parent, - secondaryName, - entry, - { discovered: discoveredFiles, skip: exclude }, - resolveGlob, - ); - items.forEach((e) => - discoveredFiles.add(typeof e === 'string' ? e : e.value), - ); - - for (const item of items) { - if (typeof item === 'object') { - result[item.key] = { - ...shareObject, - packageInfo: { - entryPoint: item.value, - version: shareObject.version ?? version, - esm, - }, - }; - } else { - result[item] = { - ...shareObject, - }; - } - } - } - - return result; -} - -function resolveGlobSecondaries( - key: string, - libPath: string, - parent: string, - secondaryName: string, - entry: string, - excludes: { discovered: Set; skip: string[] }, - resolveGlob: boolean, -): Array { - let items: Array = []; - if (key.includes('*')) { - if (!resolveGlob) return items; - const expanded = resolvePackageJsonExportsWildcard(key, entry, libPath); - items = expanded - .map((e) => ({ - key: path.join(parent, e.key), - value: path.join(libPath, e.value), - })) - .filter((i) => { - if ( - excludes.skip.some((e) => - e.endsWith('*') ? i.key.startsWith(e.slice(0, -1)) : e === i.key, - ) - ) { - return false; - } - if (excludes.discovered.has(i.value)) { - return false; - } - return true; - }); - } else { - items = [secondaryName]; - } - return items; -} - -function getDefaultEntry( - exports: Record>, - key: string, -) { - let entry = ''; - if (typeof exports[key] === 'string') { - entry = exports[key] as unknown as string; - } - - if (!entry) { - entry = exports[key]?.['default']; - if (typeof entry === 'object') { - entry = entry['default']; - } - } - - if (!entry) { - entry = exports[key]?.['import']; - if (typeof entry === 'object') { - entry = entry['import'] ?? entry['default']; - } - } - - if (!entry) { - entry = exports[key]?.['require']; - if (typeof entry === 'object') { - entry = entry['require'] ?? entry['default']; - } - } - - return entry; -} - -export function shareAll( - config: CustomSharedConfig = {}, - opts: { - skipList?: SkipList; - projectPath?: string; - overrides?: Config; - } = {}, -): Config | null { - // let workspacePath: string | undefined = undefined; - const projectPath = inferProjectPath(opts.projectPath); - - // workspacePath = getConfigContext().workspaceRoot ?? ''; - - // if (!workspacePath) { - // workspacePath = projectPath; - // } - - const versionMaps = getVersionMaps(projectPath, projectPath); - const sharedExternals: Config = {}; - const preparedSkipList = prepareSkipList(opts.skipList ?? DEFAULT_SKIP_LIST); - - for (const versions of versionMaps) { - for (const key in versions) { - if (isInSkipList(key, preparedSkipList)) { - continue; - } - if ( - !!opts.overrides && - Object.keys(opts.overrides).some((o) => key.startsWith(o)) - ) { - continue; - } - - const inferVersion = - !config.requiredVersion || config.requiredVersion === 'auto'; - const requiredVersion = inferVersion - ? versions[key] - : config.requiredVersion; - - if (!sharedExternals[key]) { - sharedExternals[key] = { ...config, requiredVersion }; - } - } - } - - return { - ...share( - sharedExternals, - opts.projectPath, - opts.skipList ?? DEFAULT_SKIP_LIST, - ), - ...(!opts.overrides - ? {} - : share( - opts.overrides, - opts.projectPath, - opts.skipList ?? DEFAULT_SKIP_LIST, - )), - }; -} - -function inferProjectPath(projectPath: string | undefined) { - if (!projectPath && getConfigContext().packageJson) { - projectPath = path.dirname(getConfigContext().packageJson || ''); - } - - if (!projectPath && getConfigContext().workspaceRoot) { - projectPath = getConfigContext().workspaceRoot || ''; - } - - if (!projectPath) { - projectPath = cwd(); - } - return projectPath; -} - -export function setInferVersion(infer: boolean): void { - inferVersion = infer; -} - -type TransientDependency = { - packageName: string; - requiredVersion: string; - packagePath: string; -}; - -function findTransientDeps( - configuredShareObjects: Config, - projectRoot: string, - preparedSkipList: PreparedSkipList, -): TransientDependency[] { - const discovered = new Set(); - const result: TransientDependency[] = []; - - const packageNames = Object.keys(configuredShareObjects) as Array< - keyof typeof configuredShareObjects - >; - - for (const packageName of packageNames) { - const shareConfig = configuredShareObjects[packageName]; - - if (typeof shareConfig === 'object' && shareConfig.transient) { - logger.warn( - 'PLEASE NOTE: The transient flag in your federation.config.js', - ); - logger.warn( - 'is deprecated. Please remove it. Meanwhile, Native Federation', - ); - logger.warn('uses the underlying bundler for splitting transient'); - logger.warn('dependencies into separate chunks, _when_ necessary.'); - const packagePath = path.join( - projectRoot, - 'node_modules', - packageName, - 'package.json', - ); - _findTransientDeps( - packagePath, - projectRoot, - preparedSkipList, - discovered, - result, - ); - } - } - - return result; -} - -function _findTransientDeps( - packagePath: string, - projectRoot: string, - preparedSkipList: PreparedSkipList, - discovered: Set, - result: TransientDependency[], -) { - if (!fs.existsSync(packagePath)) { - return; - } - - const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8')); - - const deps = Object.keys(packageJson.dependencies ?? {}); - for (const dep of deps) { - const depPackageJson = path.join( - projectRoot, - 'node_modules', - dep, - 'package.json', - ); - const depPath = path.dirname(depPackageJson); - - if ( - !discovered.has(depPackageJson) && - !isInSkipList(dep, preparedSkipList) && - fs.existsSync(depPackageJson) - ) { - discovered.add(depPackageJson); - const version = packageJson.dependencies[dep]; - result.push({ - packageName: dep, - requiredVersion: version, - packagePath: depPath, - }); - _findTransientDeps( - depPackageJson, - projectRoot, - preparedSkipList, - discovered, - result, - ); - } - } -} - -export function share( - configuredShareObjects: Config, - projectPath = '', - skipList = DEFAULT_SKIP_LIST, -): Config { - projectPath = inferProjectPath(projectPath); - const packagePath = findPackageJson(projectPath); - - const packageDirectory = path.dirname(packagePath); - - const preparedSkipList = prepareSkipList(skipList); - - const transientDeps = findTransientDeps( - configuredShareObjects, - packageDirectory, - preparedSkipList, - ); - - const transientShareObject = transientDeps.reduce( - (acc, curr) => ({ - ...acc, - [curr.packageName]: { path: curr.packagePath }, - }), - {}, - ); - - const shareObjects = { - ...configuredShareObjects, - ...transientShareObject, - }; - - const result: any = {}; - let includeSecondaries; - - for (const key in shareObjects) { - includeSecondaries = false; - const shareObject = (shareObjects as any)[key]; - - if ( - shareObject.requiredVersion === 'auto' || - (inferVersion && typeof shareObject.requiredVersion === 'undefined') - ) { - const version = lookupVersion(key, projectPath); - - shareObject.requiredVersion = version; - shareObject.version = version.replace(/^\D*/, ''); - } - - if (typeof shareObject.includeSecondaries === 'undefined') { - shareObject.includeSecondaries = true; - } - - if (shareObject.includeSecondaries) { - includeSecondaries = shareObject.includeSecondaries; - delete shareObject.includeSecondaries; - if (includeSecondaries?.keepAll) shareObject.includeSecondaries = true; - } - - result[key] = shareObject; - - if (includeSecondaries) { - const libPackageJson = findDepPackageJson(key, path.dirname(packagePath)); - - if (!libPackageJson) { - logger.error('Could not find folder containing dep ' + key); - continue; - } - - const libPath = path.dirname(libPackageJson); - - const secondaries = getSecondaries( - includeSecondaries, - libPath, - key, - shareObject, - preparedSkipList, - ); - if (secondaries) { - addSecondaries(secondaries, result); - } - } - } - - return result; -} - -function addSecondaries( - secondaries: Record, - result: Record, -) { - for (const key in secondaries) { - result[key] = secondaries[key]; - } -} diff --git a/libs/native-federation-core/src/lib/config/with-native-federation.ts b/libs/native-federation-core/src/lib/config/with-native-federation.ts deleted file mode 100644 index 17724cb6..00000000 --- a/libs/native-federation-core/src/lib/config/with-native-federation.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { getMappedPaths, MappedPath } from '../utils/mapped-paths'; -import { shareAll, findRootTsConfigJson } from './share-utils'; -import { - FederationConfig, - NormalizedFederationConfig, - NormalizedSharedConfig, -} from './federation-config'; -import { - isInSkipList, - PreparedSkipList, - prepareSkipList, -} from '../core/default-skip-list'; -import { logger } from '../utils/logger'; -import { DEFAULT_SERVER_DEPS_LIST } from '../core/default-server-deps-list'; - -export function withNativeFederation( - config: FederationConfig, -): NormalizedFederationConfig { - const skip = prepareSkipList(config.skip ?? []); - - const normalized: NormalizedFederationConfig = { - name: config.name ?? '', - exposes: config.exposes ?? {}, - shared: normalizeShared(config, skip), - sharedMappings: normalizeSharedMappings(config, skip), - skip, - externals: config.externals ?? [], - features: { - mappingVersion: config.features?.mappingVersion ?? false, - ignoreUnusedDeps: config.features?.ignoreUnusedDeps ?? false, - }, - }; - - // This is for being backwards compatible - if (!normalized.features.ignoreUnusedDeps) { - normalized.shared = filterShared(normalized.shared); - } - - return normalized; -} - -function filterShared( - shared: Record, -): Record { - const keys = Object.keys(shared).filter( - (k) => !k.startsWith('@angular/common/locales'), - ); - - const filtered = keys.reduce( - (acc, curr) => ({ - ...acc, - [curr]: shared[curr], - }), - {}, - ); - - return filtered; -} - -function normalizeShared( - config: FederationConfig, - skip: PreparedSkipList, -): Record { - let result: Record = {}; - - const shared = config.shared; - - if (!shared) { - result = shareAll({ - singleton: true, - strictVersion: true, - requiredVersion: 'auto', - platform: 'browser', - }) as Record; - } else { - result = Object.keys(shared).reduce( - (acc, cur) => ({ - ...acc, - [cur.replace(/\\/g, '/')]: { - requiredVersion: shared[cur].requiredVersion ?? 'auto', - singleton: shared[cur].singleton ?? false, - strictVersion: shared[cur].strictVersion ?? false, - version: shared[cur].version, - includeSecondaries: shared[cur].includeSecondaries, - packageInfo: shared[cur].packageInfo, - platform: shared[cur].platform ?? getDefaultPlatform(cur), - build: shared[cur].build ?? 'default', - }, - }), - {}, - ); - - //result = share(result) as Record; - } - - result = Object.keys(result) - .filter((key) => !isInSkipList(key, skip)) - .reduce( - (acc, cur) => ({ - ...acc, - [cur]: result[cur], - }), - {}, - ); - - return result; -} - -function normalizeSharedMappings( - config: FederationConfig, - skipList: PreparedSkipList, -): Array { - const rootTsConfigPath = findRootTsConfigJson(); - - const paths = getMappedPaths({ - rootTsConfigPath, - sharedMappings: config.sharedMappings, - }); - - const result = paths.filter((p) => !isInSkipList(p.key, skipList)); - - const importsWithDot = paths.filter((p) => p.key.includes('.')); - if (importsWithDot.length > 0) { - importsWithDot.forEach((e) => { - logger.warn(`Shared mapping import '${e}' contains a dot.`); - }); - logger.warn('details: https://github.com/vitejs/vite/issues/21036'); - throw new Error( - 'Native-federation does not support dots (.) in imports paths. ', - ); - } - - return result; -} - -function getDefaultPlatform(cur: string): 'browser' | 'node' { - if (DEFAULT_SERVER_DEPS_LIST.find((e) => cur.startsWith(e))) { - return 'node'; - } else { - return 'browser'; - } -} diff --git a/libs/native-federation-core/src/lib/core/build-adapter.d.ts b/libs/native-federation-core/src/lib/core/build-adapter.d.ts deleted file mode 100644 index d83b8828..00000000 --- a/libs/native-federation-core/src/lib/core/build-adapter.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MappedPath } from '../utils/mapped-paths'; -export type BuildKind = - | 'shared-package' - | 'shared-mapping' - | 'exposed' - | 'mapping-or-exposed'; -export interface EntryPoint { - fileName: string; - outName: string; -} -export interface BuildAdapterOptions { - entryPoints: EntryPoint[]; - tsConfigPath?: string; - external: Array; - outdir: string; - mappedPaths: MappedPath[]; - packageName?: string; - esm?: boolean; - dev?: boolean; - watch?: boolean; - kind: BuildKind; - hash: boolean; -} -export interface BuildResult { - fileName: string; -} -export type BuildAdapter = ( - options: BuildAdapterOptions, -) => Promise; -export declare function setBuildAdapter(buildAdapter: BuildAdapter): void; -export declare function getBuildAdapter(): BuildAdapter; diff --git a/libs/native-federation-core/src/lib/core/build-adapter.ts b/libs/native-federation-core/src/lib/core/build-adapter.ts deleted file mode 100644 index b29cf30a..00000000 --- a/libs/native-federation-core/src/lib/core/build-adapter.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { logger } from '../utils/logger'; -import { MappedPath } from '../utils/mapped-paths'; - -let _buildAdapter: BuildAdapter = async () => { - // TODO: add logger - logger.error('Please set a BuildAdapter!'); - return []; -}; - -export type BuildKind = - | 'shared-package' - | 'shared-mapping' - | 'exposed' - | 'mapping-or-exposed'; - -export interface EntryPoint { - fileName: string; - outName: string; -} - -export interface BuildAdapterOptions { - entryPoints: EntryPoint[]; - tsConfigPath?: string; - external: Array; - outdir: string; - mappedPaths: MappedPath[]; - packageName?: string; - esm?: boolean; - dev?: boolean; - watch?: boolean; - kind: BuildKind; - hash: boolean; - platform?: 'browser' | 'node'; - optimizedMappings?: boolean; - signal?: AbortSignal; -} - -export interface BuildResult { - fileName: string; -} - -export type BuildAdapter = ( - options: BuildAdapterOptions, -) => Promise; - -export function setBuildAdapter(buildAdapter: BuildAdapter): void { - _buildAdapter = buildAdapter; -} - -export function getBuildAdapter(): BuildAdapter { - return _buildAdapter; -} diff --git a/libs/native-federation-core/src/lib/core/build-for-federation.ts b/libs/native-federation-core/src/lib/core/build-for-federation.ts deleted file mode 100644 index 6b4e80a1..00000000 --- a/libs/native-federation-core/src/lib/core/build-for-federation.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { FederationInfo, SharedInfo } from '@softarc/native-federation-runtime'; -import { - NormalizedFederationConfig, - NormalizedSharedConfig, -} from '../config/federation-config'; -import { - ArtefactInfo, - bundleExposedAndMappings, - describeExposed, - describeSharedMappings, -} from './bundle-exposed-and-mappings'; -import { bundleShared } from './bundle-shared'; -import { FederationOptions } from './federation-options'; -import { writeFederationInfo } from './write-federation-info'; -import { writeImportMap } from './write-import-map'; -import { logger } from '../utils/logger'; -import { getCachePath } from './../utils/bundle-caching'; -import { normalizePackageName } from '../utils/normalize'; -import { AbortedError } from '../utils/errors'; -import { resolveProjectName } from '../utils/config-utils'; - -export interface BuildParams { - skipMappingsAndExposed: boolean; - skipShared: boolean; - signal?: AbortSignal; -} - -export const defaultBuildParams: BuildParams = { - skipMappingsAndExposed: false, - skipShared: false, -}; - -const sharedPackageInfoCache: SharedInfo[] = []; - -export async function buildForFederation( - config: NormalizedFederationConfig, - fedOptions: FederationOptions, - externals: string[], - buildParams = defaultBuildParams, -): Promise { - const signal = buildParams.signal; - - let artefactInfo: ArtefactInfo | undefined; - - if (!buildParams.skipMappingsAndExposed) { - const start = process.hrtime(); - artefactInfo = await bundleExposedAndMappings( - config, - fedOptions, - externals, - signal, - ); - logger.measure( - start, - '[build artifacts] - To bundle all mappings and exposed.', - ); - - if (signal?.aborted) - throw new AbortedError( - '[buildForFederation] After exposed-and-mappings bundle', - ); - } - - const exposedInfo = !artefactInfo - ? describeExposed(config, fedOptions) - : artefactInfo.exposes; - - const cacheProjectFolder = resolveProjectName(config); - const pathToCache = getCachePath( - fedOptions.workspaceRoot, - cacheProjectFolder, - ); - - if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { - logger.info('Checksum matched, re-using cached externals.'); - } - - if (!buildParams.skipShared && sharedPackageInfoCache.length === 0) { - const { sharedBrowser, sharedServer, separateBrowser, separateServer } = - splitShared(config.shared); - - if (Object.keys(sharedBrowser).length > 0) { - notifyBundling('browser-shared'); - const start = process.hrtime(); - const sharedPackageInfoBrowser = await bundleShared( - sharedBrowser, - config, - fedOptions, - externals, - 'browser', - { pathToCache, bundleName: 'browser-shared' }, - ); - - logger.measure( - start, - '[build artifacts] - To bundle all shared browser externals', - ); - - sharedPackageInfoCache.push(...sharedPackageInfoBrowser); - - if (signal?.aborted) - throw new AbortedError( - '[buildForFederation] After shared-browser bundle', - ); - } - - if (Object.keys(sharedServer).length > 0) { - notifyBundling('browser-shared'); - const start = process.hrtime(); - const sharedPackageInfoServer = await bundleShared( - sharedServer, - config, - fedOptions, - externals, - 'node', - { pathToCache, bundleName: 'node-shared' }, - ); - logger.measure( - start, - '[build artifacts] - To bundle all shared node externals', - ); - sharedPackageInfoCache.push(...sharedPackageInfoServer); - - if (signal?.aborted) - throw new AbortedError('[buildForFederation] After shared-node bundle'); - } - - if (Object.keys(separateBrowser).length > 0) { - notifyBundling('browser-shared'); - const start = process.hrtime(); - const separatePackageInfoBrowser = await bundleSeparatePackages( - separateBrowser, - externals, - config, - fedOptions, - 'browser', - pathToCache, - ); - logger.measure( - start, - '[build artifacts] - To bundle all separate browser externals', - ); - sharedPackageInfoCache.push(...separatePackageInfoBrowser); - - if (signal?.aborted) - throw new AbortedError( - '[buildForFederation] After separate-browser bundle', - ); - } - - if (Object.keys(separateServer).length > 0) { - notifyBundling('browser-shared'); - const start = process.hrtime(); - const separatePackageInfoServer = await bundleSeparatePackages( - separateServer, - externals, - config, - fedOptions, - 'node', - pathToCache, - ); - logger.measure( - start, - '[build artifacts] - To bundle all separate node externals', - ); - sharedPackageInfoCache.push(...separatePackageInfoServer); - } - - if (signal?.aborted) - throw new AbortedError('[buildForFederation] After separate-node bundle'); - } - - const sharedMappingInfo = !artefactInfo - ? describeSharedMappings(config, fedOptions) - : artefactInfo.mappings; - - const sharedInfo = [...sharedPackageInfoCache, ...sharedMappingInfo]; - const buildNotificationsEndpoint = - fedOptions.buildNotifications?.enable && fedOptions.dev - ? fedOptions.buildNotifications?.endpoint - : undefined; - const federationInfo: FederationInfo = { - name: config.name, - shared: sharedInfo, - exposes: exposedInfo, - buildNotificationsEndpoint, - }; - - writeFederationInfo(federationInfo, fedOptions); - writeImportMap(sharedInfo, fedOptions); - - return federationInfo; -} - -type SplitSharedResult = { - sharedServer: Record; - sharedBrowser: Record; - separateBrowser: Record; - separateServer: Record; -}; - -function inferPackageFromSecondary(secondary: string): string { - const parts = secondary.split('/'); - if (secondary.startsWith('@') && parts.length >= 2) { - return parts[0] + '/' + parts[1]; - } - return parts[0]; -} - -async function bundleSeparatePackages( - separateBrowser: Record, - externals: string[], - config: NormalizedFederationConfig, - fedOptions: FederationOptions, - platform: 'node' | 'browser', - pathToCache: string, -) { - const groupedByPackage: Record< - string, - Record - > = {}; - - for (const [key, shared] of Object.entries(separateBrowser)) { - const packageName = - shared.build === 'separate' ? key : inferPackageFromSecondary(key); - if (!groupedByPackage[packageName]) { - groupedByPackage[packageName] = {}; - } - groupedByPackage[packageName][key] = shared; - } - - const bundlePromises = Object.entries(groupedByPackage).map( - async ([packageName, sharedGroup]) => { - return bundleShared( - sharedGroup, - config, - fedOptions, - externals.filter((e) => !e.startsWith(packageName)), - platform, - { - pathToCache, - bundleName: `${platform}-${normalizePackageName(packageName)}`, - }, - ); - }, - ); - - const buildResults = await Promise.all(bundlePromises); - return buildResults.flat(); -} - -function notifyBundling(platform: string) { - logger.info('Preparing shared npm packages for the platform ' + platform); - logger.notice('This only needs to be done once, as results are cached'); - logger.notice( - "Skip packages you don't want to share in your federation config", - ); -} - -function splitShared( - shared: Record, -): SplitSharedResult { - const sharedServer: Record = {}; - const sharedBrowser: Record = {}; - const separateBrowser: Record = {}; - const separateServer: Record = {}; - - for (const key in shared) { - const obj = shared[key]; - - if (obj.platform === 'node') { - if (obj.build === 'default') { - sharedServer[key] = obj; - } else { - separateServer[key] = obj; - } - } else if (obj.platform === 'browser') { - if (obj.build === 'default') { - sharedBrowser[key] = obj; - } else { - separateBrowser[key] = obj; - } - } - } - - return { - sharedBrowser, - sharedServer, - separateBrowser, - separateServer, - }; -} diff --git a/libs/native-federation-core/src/lib/core/bundle-exposed-and-mappings.ts b/libs/native-federation-core/src/lib/core/bundle-exposed-and-mappings.ts deleted file mode 100644 index 3906d1fe..00000000 --- a/libs/native-federation-core/src/lib/core/bundle-exposed-and-mappings.ts +++ /dev/null @@ -1,180 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import { ExposesInfo, SharedInfo } from '@softarc/native-federation-runtime'; -import { NormalizedFederationConfig } from '../config/federation-config'; -import { - createBuildResultMap, - lookupInResultMap, -} from '../utils/build-result-map'; -import { bundle } from '../utils/build-utils'; -import { logger } from '../utils/logger'; -import { normalize } from '../utils/normalize'; -import { FederationOptions } from './federation-options'; -import { AbortedError } from '../utils/errors'; - -export interface ArtefactInfo { - mappings: SharedInfo[]; - exposes: ExposesInfo[]; -} - -export async function bundleExposedAndMappings( - config: NormalizedFederationConfig, - fedOptions: FederationOptions, - externals: string[], - signal?: AbortSignal, -): Promise { - if (signal?.aborted) { - throw new AbortedError( - '[bundle-exposed-and-mappings] Aborted before bundling', - ); - } - - const shared = config.sharedMappings.map((sm) => { - const entryPoint = sm.path; - const tmp = sm.key.replace(/[^A-Za-z0-9]/g, '_'); - const outFilePath = tmp + '.js'; - return { fileName: entryPoint, outName: outFilePath, key: sm.key }; - }); - const exposes = Object.keys(config.exposes).map((key) => { - const entryPoint = config.exposes[key]; - const outFilePath = key + '.js'; - return { fileName: entryPoint, outName: outFilePath, key }; - }); - - const entryPoints = [...shared, ...exposes]; - - const hash = !fedOptions.dev; - - logger.info('Building federation artefacts'); - - let result; - try { - result = await bundle({ - entryPoints, - outdir: fedOptions.outputPath, - tsConfigPath: fedOptions.tsConfig, - external: externals, - dev: !!fedOptions.dev, - watch: fedOptions.watch, - mappedPaths: config.sharedMappings, - kind: 'mapping-or-exposed', - hash, - optimizedMappings: config.features.ignoreUnusedDeps, - signal, - }); - if (signal?.aborted) { - throw new AbortedError( - '[bundle-exposed-and-mappings] Aborted after bundle', - ); - } - } catch (error) { - if (!(error instanceof AbortedError)) { - logger.error('Error building federation artefacts'); - } - throw error; - } - - const resultMap = createBuildResultMap(result, hash); - - const sharedResult: Array = []; - - for (const item of shared) { - sharedResult.push({ - packageName: item.key, - outFileName: lookupInResultMap(resultMap, item.outName), - requiredVersion: '', - singleton: true, - strictVersion: false, - version: config.features.mappingVersion - ? getMappingVersion(item.fileName) - : '', - dev: !fedOptions.dev - ? undefined - : { - entryPoint: normalize(path.normalize(item.fileName)), - }, - }); - } - - const exposedResult: Array = []; - - for (const item of exposes) { - exposedResult.push({ - key: item.key, - outFileName: lookupInResultMap(resultMap, item.outName), - dev: !fedOptions.dev - ? undefined - : { - entryPoint: normalize( - path.join(fedOptions.workspaceRoot, item.fileName), - ), - }, - }); - } - - return { mappings: sharedResult, exposes: exposedResult }; -} - -export function describeExposed( - config: NormalizedFederationConfig, - options: FederationOptions, -): Array { - const result: Array = []; - - for (const key in config.exposes) { - const localPath = normalize( - path.normalize(path.join(options.workspaceRoot, config.exposes[key])), - ); - - result.push({ - key, - outFileName: '', - dev: !options.dev - ? undefined - : { - entryPoint: localPath, - }, - }); - } - - return result; -} - -export function describeSharedMappings( - config: NormalizedFederationConfig, - fedOptions: FederationOptions, -): Array { - const result: Array = []; - - for (const m of config.sharedMappings) { - result.push({ - packageName: m.key, - outFileName: '', - requiredVersion: '', - singleton: true, - strictVersion: false, - version: config.features.mappingVersion ? getMappingVersion(m.path) : '', - dev: !fedOptions.dev - ? undefined - : { - entryPoint: normalize(path.normalize(m.path)), - }, - }); - } - - return result; -} - -function getMappingVersion(fileName: string): string { - const entryFileDir = path.dirname(fileName); - const cand1 = path.join(entryFileDir, 'package.json'); - const cand2 = path.join(path.dirname(entryFileDir), 'package.json'); - - const packageJsonPath = [cand1, cand2].find((cand) => fs.existsSync(cand)); - if (packageJsonPath) { - const json = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - return json.version ?? ''; - } - return ''; -} diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts deleted file mode 100644 index 784ac2a6..00000000 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ /dev/null @@ -1,272 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import { - NormalizedFederationConfig, - NormalizedSharedConfig, -} from '../config/federation-config'; -import { bundle } from '../utils/build-utils'; -import { getPackageInfo, PackageInfo } from '../utils/package-info'; -import { SharedInfo } from '@softarc/native-federation-runtime'; -import { FederationOptions } from './federation-options'; -import { logger } from '../utils/logger'; -import crypto from 'crypto'; -import { DEFAULT_EXTERNAL_LIST } from './default-external-list'; -import { BuildResult } from './build-adapter'; -import { - deriveInternalName, - isSourceFile, - rewriteChunkImports, -} from '../utils/rewrite-chunk-imports'; -import { - cacheEntry, - getChecksum, - getFilename, -} from './../utils/bundle-caching'; - -export async function bundleShared( - sharedBundles: Record, - config: NormalizedFederationConfig, - fedOptions: FederationOptions, - externals: string[], - platform: 'browser' | 'node' = 'browser', - cacheOptions: { pathToCache: string; bundleName: string }, -): Promise> { - const checksum = getChecksum(sharedBundles, fedOptions.dev ? '1' : '0'); - const folder = fedOptions.packageJson - ? path.dirname(fedOptions.packageJson) - : fedOptions.workspaceRoot; - - const bundleCache = cacheEntry( - cacheOptions.pathToCache, - getFilename(cacheOptions.bundleName, fedOptions.dev), - ); - - if (fedOptions?.cacheExternalArtifacts) { - const cacheMetadata = bundleCache.getMetadata(checksum); - if (cacheMetadata) { - logger.debug( - `Checksum of ${cacheOptions.bundleName} matched, Skipped artifact bundling`, - ); - bundleCache.copyFiles( - path.resolve(fedOptions.workspaceRoot, fedOptions.outputPath), - ); - return cacheMetadata.externals; - } - } - - bundleCache.clear(); - - const inferredPackageInfos = Object.keys(sharedBundles) - .filter((packageName) => !sharedBundles[packageName].packageInfo) - .map((packageName) => getPackageInfo(packageName, folder)) - .filter((pi) => !!pi) as PackageInfo[]; - - const configuredPackageInfos = Object.keys(sharedBundles) - .filter((packageName) => !!sharedBundles[packageName].packageInfo) - .map((packageName) => ({ - packageName, - ...sharedBundles[packageName].packageInfo, - })) as PackageInfo[]; - - const packageInfos = [...inferredPackageInfos, ...configuredPackageInfos]; - - const configState = - 'BUNDLER_CHUNKS' + // TODO: Replace this with lib version - fs.readFileSync(path.join(__dirname, '../../../package.json')) + - JSON.stringify(config); - - const allEntryPoints = packageInfos.map((pi) => { - const encName = pi.packageName.replace(/[^A-Za-z0-9]/g, '_'); - const outName = createOutName(pi, configState, fedOptions, encName); - return { fileName: pi.entryPoint, outName }; - }); - - const fullOutputPath = path.resolve( - fedOptions.workspaceRoot, - fedOptions.outputPath, - ); - - const expectedResults = allEntryPoints.map((ep) => - path.join(fullOutputPath, ep.outName), - ); - const entryPoints = allEntryPoints.filter( - (ep) => !fs.existsSync(path.join(cacheOptions.pathToCache, ep.outName)), - ); - - // If we build for the browser and don't remote unused deps from the shared config, - // we need to exclude typical node libs to avoid compilation issues - const useDefaultExternalList = - platform === 'browser' && !config.features.ignoreUnusedDeps; - - const additionalExternals = useDefaultExternalList - ? DEFAULT_EXTERNAL_LIST - : []; - - let bundleResult: BuildResult[] | null = null; - - try { - bundleResult = await bundle({ - entryPoints, - tsConfigPath: fedOptions.tsConfig, - external: [...additionalExternals, ...externals], - outdir: cacheOptions.pathToCache, - mappedPaths: config.sharedMappings, - dev: fedOptions.dev, - kind: 'shared-package', - hash: false, - platform, - optimizedMappings: config.features.ignoreUnusedDeps, - }); - - const cachedFiles = bundleResult.map((br) => path.basename(br.fileName)); - rewriteImports(cachedFiles, cacheOptions.pathToCache); - } catch (e) { - logger.error('Error bundling shared npm package '); - if (e instanceof Error) { - logger.error(e.message); - } - - logger.error('For more information, run in verbose mode'); - - logger.notice(''); - logger.notice(''); - - logger.notice('** Important Information: ***'); - logger.notice( - 'The error message above shows an issue with bundling a node_module.', - ); - logger.notice( - 'In most cases this is because you (indirectly) shared a Node.js package,', - ); - logger.notice('while Native Federation builds for the browser.'); - logger.notice( - 'You can move such packages into devDependencies or skip them in your federation.config.js.', - ); - logger.notice(''); - logger.notice('More Details: https://bit.ly/nf-issue'); - - logger.notice(''); - logger.notice(''); - - logger.verbose(e); - throw e; - } - - const outFileNames = [...expectedResults]; - - const result = buildResult(packageInfos, sharedBundles, outFileNames); - - // TODO: Decide whether/when to add .map files - const chunks = bundleResult.filter( - (br) => - !br.fileName.endsWith('.map') && - !result.find((r) => r.outFileName === path.basename(br.fileName)), - ); - - addChunksToResult(chunks, result, fedOptions.dev); - - bundleCache.persist({ - checksum, - externals: result, - files: bundleResult.map( - (r) => r.fileName.split(path.sep).pop() ?? r.fileName, - ), - }); - - bundleCache.copyFiles( - path.resolve(fedOptions.workspaceRoot, fedOptions.outputPath), - ); - - return result; -} - -function rewriteImports(cachedFiles: string[], cachePath: string) { - const newSourceFiles = cachedFiles.filter((cf) => isSourceFile(cf)); - - for (const sourceFile of newSourceFiles) { - const sourceFilePath = path.join(cachePath, sourceFile); - rewriteChunkImports(sourceFilePath); - } -} - -function createOutName( - pi: PackageInfo, - configState: string, - fedOptions: FederationOptions, - encName: string, -) { - const hashBase = pi.version + '_' + pi.entryPoint + '_' + configState; - const hash = calcHash(hashBase); - - const outName = fedOptions.dev - ? `${encName}.${hash}-dev.js` - : `${encName}.${hash}.js`; - return outName; -} - -function buildResult( - packageInfos: PackageInfo[], - sharedBundles: Record, - outFileNames: string[], -) { - return packageInfos.map((pi) => { - const shared = sharedBundles[pi.packageName]; - return { - packageName: pi.packageName, - outFileName: path.basename(outFileNames.shift() || ''), - requiredVersion: shared.requiredVersion, - singleton: shared.singleton, - strictVersion: shared.strictVersion, - version: pi.version, - // TODO: Decide whether/when we need debug infos - // dev: !fedOptions.dev - // ? undefined - // : { - // entryPoint: normalize(pi.entryPoint), - // }, - } as SharedInfo; - }); -} - -function addChunksToResult( - chunks: BuildResult[], - result: SharedInfo[], - dev?: boolean, -) { - for (const item of chunks) { - const fileName = path.basename(item.fileName); - result.push({ - singleton: false, - strictVersion: false, - // Here, the version does not matter because - // a) a chunk split off by the bundler does - // not have a version and b) it gets a hash - // code as part of the file name to be unique - // when requested via a _versioned_ package. - // - // For the same reason, we don't need to - // take care of singleton and strictVersion. - version: '0.0.0', - requiredVersion: '0.0.0', - packageName: deriveInternalName(fileName), - outFileName: fileName, - // dev: dev - // ? undefined - // : { - // entryPoint: normalize(fileName), - // }, - }); - } -} - -function calcHash(hashBase: string) { - const hash = crypto - .createHash('sha256') - .update(hashBase) - .digest('base64') - .replace(/\//g, '_') - .replace(/\+/g, '-') - .replace(/=/g, '') - .substring(0, 10); - return hash; -} diff --git a/libs/native-federation-core/src/lib/core/default-external-list.ts b/libs/native-federation-core/src/lib/core/default-external-list.ts deleted file mode 100644 index 0875b739..00000000 --- a/libs/native-federation-core/src/lib/core/default-external-list.ts +++ /dev/null @@ -1,33 +0,0 @@ -const NODE_PACKAGES = [ - 'assert', - 'buffer', - 'child_process', - 'cluster', - 'crypto', - 'dgram', - 'dns', - 'events', - 'fs', - 'http', - 'https', - 'module', - 'net', - 'os', - 'path', - 'querystring', - 'readline', - 'stream', - 'timers', - 'tls', - 'tty', - 'url', - 'util', - 'v8', - 'vm', - 'zlib', -]; - -export const DEFAULT_EXTERNAL_LIST = NODE_PACKAGES.flatMap((p) => [ - p, - 'node:' + p, -]); diff --git a/libs/native-federation-core/src/lib/core/default-server-deps-list.ts b/libs/native-federation-core/src/lib/core/default-server-deps-list.ts deleted file mode 100644 index b5224276..00000000 --- a/libs/native-federation-core/src/lib/core/default-server-deps-list.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const DEFAULT_SERVER_DEPS_LIST: string[] = [ - '@angular/platform-server', - '@angular/platform-server/init', - '@angular/ssr', -]; - -export const DEFAULT_SERVER_DEPS_SET = new Set(DEFAULT_SERVER_DEPS_LIST); diff --git a/libs/native-federation-core/src/lib/core/default-skip-list.ts b/libs/native-federation-core/src/lib/core/default-skip-list.ts deleted file mode 100644 index f19ad0c4..00000000 --- a/libs/native-federation-core/src/lib/core/default-skip-list.ts +++ /dev/null @@ -1,63 +0,0 @@ -export type SkipFn = (name: string) => boolean; -export type SkipListEntry = string | RegExp | SkipFn; -export type SkipList = SkipListEntry[]; - -export const DEFAULT_SKIP_LIST: SkipList = [ - '@softarc/native-federation-runtime', - '@softarc/native-federation', - '@softarc/native-federation-core', - '@softarc/native-federation-node', - '@softarc/native-federation-esbuild', - '@angular-architects/native-federation', - '@angular-architects/native-federation-runtime', - 'es-module-shims', - 'zone.js', - 'tslib/', - '@angular/localize', - '@angular/localize/init', - '@angular/localize/tools', - // '@angular/platform-server', - // '@angular/platform-server/init', - // '@angular/ssr', - /\/schematics(\/|$)/, - /^@nx\/angular/, - (pkg) => pkg.startsWith('@angular/') && !!pkg.match(/\/testing(\/|$)/), - (pkg) => pkg.startsWith('@types/'), - 'express', - // (pkg) => pkg.startsWith('@angular/common/locales'), -]; - -export type PreparedSkipList = { - strings: Set; - functions: SkipFn[]; - regexps: RegExp[]; -}; - -export function prepareSkipList(skipList: SkipList): PreparedSkipList { - return { - strings: new Set( - skipList.filter((e) => typeof e === 'string') as string[], - ), - functions: skipList.filter((e) => typeof e === 'function') as SkipFn[], - regexps: skipList.filter((e) => typeof e === 'object') as RegExp[], - }; -} - -export function isInSkipList( - entry: string, - skipList: PreparedSkipList, -): boolean { - if (skipList.strings.has(entry)) { - return true; - } - - if (skipList.functions.find((f) => f(entry))) { - return true; - } - - if (skipList.regexps.find((r) => r.test(entry))) { - return true; - } - - return false; -} diff --git a/libs/native-federation-core/src/lib/core/federation-builder.ts b/libs/native-federation-core/src/lib/core/federation-builder.ts deleted file mode 100644 index a0df749e..00000000 --- a/libs/native-federation-core/src/lib/core/federation-builder.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { FederationInfo } from '@softarc/native-federation-runtime'; -import { - getConfigContext, - usePackageJson, - useWorkspace, -} from '../config/configuration-context'; -import { NormalizedFederationConfig } from '../config/federation-config'; -import { BuildAdapter, setBuildAdapter } from './build-adapter'; -import { buildForFederation, defaultBuildParams } from './build-for-federation'; -import { FederationOptions } from './federation-options'; -import { getExternals } from './get-externals'; -import { loadFederationConfig } from './load-federation-config'; - -export interface BuildHelperParams { - options: FederationOptions; - adapter: BuildAdapter; -} - -let externals: string[] = []; -let config: NormalizedFederationConfig; -let fedOptions: FederationOptions; -let fedInfo: FederationInfo; - -async function init(params: BuildHelperParams): Promise { - setBuildAdapter(params.adapter); - fedOptions = params.options; - useWorkspace(params.options.workspaceRoot); - usePackageJson(params.options.packageJson); - config = await loadFederationConfig(fedOptions); - params.options.workspaceRoot = - getConfigContext().workspaceRoot ?? params.options.workspaceRoot; - externals = getExternals(config); -} - -async function build(buildParams = defaultBuildParams): Promise { - fedInfo = await buildForFederation( - config, - fedOptions, - externals, - buildParams, - ); -} - -export const federationBuilder = { - init, - build, - get federationInfo() { - return fedInfo; - }, - get externals(): string[] { - return externals; - }, - get config(): NormalizedFederationConfig { - return config; - }, -}; diff --git a/libs/native-federation-core/src/lib/core/federation-options.ts b/libs/native-federation-core/src/lib/core/federation-options.ts deleted file mode 100644 index 5139cdb3..00000000 --- a/libs/native-federation-core/src/lib/core/federation-options.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { BuildNotificationOptions } from '@softarc/native-federation-runtime'; - -export interface FederationOptions { - workspaceRoot: string; - outputPath: string; - federationConfig: string; - cacheExternalArtifacts?: boolean; - tsConfig?: string; - verbose?: boolean; - dev?: boolean; - watch?: boolean; - packageJson?: string; - entryPoint?: string; - buildNotifications?: BuildNotificationOptions; -} diff --git a/libs/native-federation-core/src/lib/core/get-externals.ts b/libs/native-federation-core/src/lib/core/get-externals.ts deleted file mode 100644 index e28e6bb4..00000000 --- a/libs/native-federation-core/src/lib/core/get-externals.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NormalizedFederationConfig } from '../config/federation-config'; - -export function getExternals(config: NormalizedFederationConfig) { - const shared = Object.keys(config.shared); - const sharedMappings = config.sharedMappings.map((m) => m.key); - const externals = [...shared, ...sharedMappings, ...config.externals]; - return externals; -} diff --git a/libs/native-federation-core/src/lib/core/load-federation-config.ts b/libs/native-federation-core/src/lib/core/load-federation-config.ts deleted file mode 100644 index 77ade144..00000000 --- a/libs/native-federation-core/src/lib/core/load-federation-config.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NormalizedFederationConfig } from '../config/federation-config'; -import { FederationOptions } from './federation-options'; -import * as path from 'path'; -import * as fs from 'fs'; -import { removeUnusedDeps } from './remove-unused-deps'; - -export async function loadFederationConfig( - fedOptions: FederationOptions, -): Promise { - const fullConfigPath = path.join( - fedOptions.workspaceRoot, - fedOptions.federationConfig, - ); - - if (!fs.existsSync(fullConfigPath)) { - throw new Error('Expected ' + fullConfigPath); - } - - const config = (await import(fullConfigPath)) - ?.default as NormalizedFederationConfig; - - if (config.features.ignoreUnusedDeps && !fedOptions.entryPoint) { - throw new Error( - `The feature ignoreUnusedDeps needs the application's entry point. Please set it in your federation options!`, - ); - } - - if (config.features.ignoreUnusedDeps) { - // const entryPoint = path.join(fedOptions.workspaceRoot, fedOptions.entryPoint ?? ''); - - return removeUnusedDeps( - config, - fedOptions.entryPoint ?? '', - fedOptions.workspaceRoot, - ); - } - - return config; -} diff --git a/libs/native-federation-core/src/lib/core/remove-unused-deps.ts b/libs/native-federation-core/src/lib/core/remove-unused-deps.ts deleted file mode 100644 index bd7a6b06..00000000 --- a/libs/native-federation-core/src/lib/core/remove-unused-deps.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { getProjectData, ProjectData } from '@softarc/sheriff-core'; -import path from 'path'; -import fs from 'fs'; -import { cwd } from 'process'; -import { NormalizedFederationConfig } from '../config/federation-config'; -import { getPackageInfo, PackageInfo } from '../utils/package-info'; -import { getExternalImports as extractExternalImports } from '../utils/get-external-imports'; -import { MappedPath } from '../utils/mapped-paths'; -import { normalizePackageName } from '../utils/normalize'; -import { resolveProjectName } from '../utils/config-utils'; - -export function removeUnusedDeps( - config: NormalizedFederationConfig, - main: string, - workspaceRoot: string, -): NormalizedFederationConfig { - const fileInfos = getProjectData(main, cwd(), { - includeExternalLibraries: true, - }); - - const usedDeps = findUsedDeps(fileInfos, workspaceRoot, config); - const usedPackageNames = usedDeps.usedPackageNames; - const usedMappings = usedDeps.usedMappings; - - const projectName = resolveProjectName(config); - const usedPackageNamesWithTransient = addTransientDeps( - usedPackageNames, - workspaceRoot, - projectName, - ); - const filteredShared = filterShared(config, usedPackageNamesWithTransient); - - return { - ...config, - shared: filteredShared, - sharedMappings: [...usedMappings], - }; -} - -function filterShared( - config: NormalizedFederationConfig, - usedPackageNamesWithTransient: Set, -) { - return Object.entries(config.shared) - .filter( - ([shared, meta]) => - !!meta.includeSecondaries || usedPackageNamesWithTransient.has(shared), - ) - .reduce((acc, [shared, meta]) => ({ ...acc, [shared]: meta }), {}); -} - -function findUsedDeps( - fileInfos: ProjectData, - workspaceRoot: string, - config: NormalizedFederationConfig, -) { - const usedPackageNames = new Set(); - const usedMappings = new Set(); - - for (const fileName of Object.keys(fileInfos)) { - const fileInfo = fileInfos[fileName]; - - if (!fileInfo) { - continue; - } - - const libs = [ - ...(fileInfo.externalLibraries || []), - ...(fileInfo.unresolvedImports || []), - ]; - - for (const pckg of libs) { - usedPackageNames.add(pckg); - } - - const fullFileName = path.join(workspaceRoot, fileName); - const mappings = config.sharedMappings.filter((sm) => - fullFileName.startsWith(sm.path), - ); - - for (const mapping of mappings) { - usedMappings.add(mapping); - } - } - return { usedPackageNames, usedMappings }; -} - -function addTransientDeps( - packages: Set, - workspaceRoot: string, - projectName: string, -) { - const packagesAndPeers = new Set([...packages]); - const discovered = new Set(packagesAndPeers); - const stack = [...packagesAndPeers]; - - while (stack.length > 0) { - const dep = stack.pop(); - - if (!dep) { - continue; - } - - const pInfo = getPackageInfo(dep, workspaceRoot); - - if (!pInfo) { - continue; - } - - const peerDeps = getExternalImports(pInfo, workspaceRoot, projectName); - - for (const peerDep of peerDeps) { - if (!discovered.has(peerDep)) { - discovered.add(peerDep); - stack.push(peerDep); - packagesAndPeers.add(peerDep); - } - } - } - return packagesAndPeers; -} - -function getExternalImports( - pInfo: PackageInfo, - workspaceRoot: string, - projectName: string, -) { - const encodedPackageName = normalizePackageName(pInfo.packageName); - const cacheFileName = `${encodedPackageName}-${pInfo.version}.deps.json`; - const cachePath = path.join( - workspaceRoot, - 'node_modules/.cache/native-federation/_externals-metadata', - projectName, - ); - const cacheFilePath = path.join(cachePath, cacheFileName); - - const cacheHit = fs.existsSync(cacheFilePath); - - let peerDeps; - if (cacheHit) { - peerDeps = JSON.parse(fs.readFileSync(cacheFilePath, 'utf-8')); - } else { - peerDeps = extractExternalImports(pInfo.entryPoint); - fs.mkdirSync(cachePath, { recursive: true }); - fs.writeFileSync( - cacheFilePath, - JSON.stringify(peerDeps, undefined, 2), - 'utf-8', - ); - } - return peerDeps; -} diff --git a/libs/native-federation-core/src/lib/core/write-federation-info.ts b/libs/native-federation-core/src/lib/core/write-federation-info.ts deleted file mode 100644 index ffb44f1e..00000000 --- a/libs/native-federation-core/src/lib/core/write-federation-info.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { FederationInfo } from '@softarc/native-federation-runtime'; -import * as fs from 'fs'; -import * as path from 'path'; -import { FederationOptions } from './federation-options'; - -export function writeFederationInfo( - federationInfo: FederationInfo, - fedOptions: FederationOptions, -) { - const metaDataPath = path.resolve( - fedOptions.workspaceRoot, - fedOptions.outputPath, - 'remoteEntry.json', - ); - fs.writeFileSync(metaDataPath, JSON.stringify(federationInfo, null, 2)); -} diff --git a/libs/native-federation-core/src/lib/core/write-import-map.ts b/libs/native-federation-core/src/lib/core/write-import-map.ts deleted file mode 100644 index 97d7e52c..00000000 --- a/libs/native-federation-core/src/lib/core/write-import-map.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import { SharedInfo } from '@softarc/native-federation-runtime'; -import { FederationOptions } from './federation-options'; - -export function writeImportMap( - sharedInfo: SharedInfo[], - fedOption: FederationOptions, -) { - const imports = sharedInfo.reduce((acc, cur) => { - return { - ...acc, - [cur.packageName]: cur.outFileName, - }; - }, {}); - - const importMap = { imports }; - const importMapPath = path.resolve( - fedOption.workspaceRoot, - fedOption.outputPath, - 'importmap.json', - ); - fs.writeFileSync(importMapPath, JSON.stringify(importMap, null, 2)); -} diff --git a/libs/native-federation-core/src/lib/utils/build-result-map.ts b/libs/native-federation-core/src/lib/utils/build-result-map.ts deleted file mode 100644 index ffba4b4d..00000000 --- a/libs/native-federation-core/src/lib/utils/build-result-map.ts +++ /dev/null @@ -1,35 +0,0 @@ -import path from 'path'; -import { BuildResult } from '../core/build-adapter'; - -export function createBuildResultMap( - buildResult: BuildResult[], - isHashed: boolean, -): Record { - const map: Record = {}; - - for (const item of buildResult) { - const resultName = path.basename(item.fileName); - let requestName = resultName; - - if (isHashed) { - const start = resultName.lastIndexOf('-'); - const end = resultName.lastIndexOf('.'); - - const part1 = resultName.substring(0, start); - const part2 = resultName.substring(end); - - requestName = part1 + part2; - } - map[requestName] = resultName; - } - - return map; -} - -export function lookupInResultMap( - map: Record, - requestName: string, -): string { - const key = path.basename(requestName); - return map[key]; -} diff --git a/libs/native-federation-core/src/lib/utils/build-utils.ts b/libs/native-federation-core/src/lib/utils/build-utils.ts deleted file mode 100644 index 51957501..00000000 --- a/libs/native-federation-core/src/lib/utils/build-utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { BuildAdapterOptions, getBuildAdapter } from '../core/build-adapter'; - -export async function bundle(options: BuildAdapterOptions) { - const adapter = getBuildAdapter(); - return await adapter(options); -} diff --git a/libs/native-federation-core/src/lib/utils/bundle-caching.ts b/libs/native-federation-core/src/lib/utils/bundle-caching.ts deleted file mode 100644 index c4344434..00000000 --- a/libs/native-federation-core/src/lib/utils/bundle-caching.ts +++ /dev/null @@ -1,117 +0,0 @@ -import path from 'path'; -import fs from 'fs'; -import crypto from 'crypto'; -import { NormalizedSharedConfig } from '../config/federation-config'; -import { SharedInfo } from '@softarc/native-federation-runtime'; -import { logger } from '../utils/logger'; - -export const getCachePath = (workspaceRoot: string, project: string) => - path.join(workspaceRoot, 'node_modules/.cache/native-federation', project); - -export const getFilename = (title: string, dev?: boolean) => { - const devSuffix = dev ? '-dev' : ''; - return `${title}${devSuffix}.meta.json`; -}; - -export const getChecksum = ( - shared: Record, - dev: '1' | '0', -): string => { - const denseExternals = Object.keys(shared) - .sort() - .reduce((clean, external) => { - return ( - clean + - ':' + - external + - (shared[external].version ? `@${shared[external].version}` : '') - ); - }, 'deps'); - - return crypto - .createHash('sha256') - .update(denseExternals + `:dev=${dev}`) - .digest('hex'); -}; - -export const cacheEntry = (pathToCache: string, fileName: string) => ({ - getMetadata: ( - checksum: string, - ): - | { - checksum: string; - externals: SharedInfo[]; - files: string[]; - } - | undefined => { - const metadataFile = path.join(pathToCache, fileName); - if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile)) - return undefined; - - const cachedResult: { - checksum: string; - externals: SharedInfo[]; - files: string[]; - } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - if (cachedResult.checksum !== checksum) return undefined; - return cachedResult; - }, - persist: (payload: { - checksum: string; - externals: SharedInfo[]; - files: string[]; - }) => { - fs.writeFileSync( - path.join(pathToCache, fileName), - JSON.stringify(payload), - 'utf-8', - ); - }, - copyFiles: (fullOutputPath: string) => { - const metadataFile = path.join(pathToCache, fileName); - if (!fs.existsSync(metadataFile)) - throw new Error( - 'Error copying artifacts to dist, metadata file could not be found.', - ); - - const cachedResult: { - externals: SharedInfo[]; - files: string[]; - } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - - fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); - - cachedResult.files.forEach((file) => { - const cachedFile = path.join(pathToCache, file); - const distFileName = path.join(fullOutputPath, file); - - if (fs.existsSync(cachedFile)) { - fs.copyFileSync(cachedFile, distFileName); - } - }); - }, - clear: () => { - const metadataFile = path.join(pathToCache, fileName); - if (!fs.existsSync(pathToCache)) { - fs.mkdirSync(pathToCache, { recursive: true }); - logger.debug(`Creating cache folder '${pathToCache}' for '${fileName}'.`); - return; - } - if (!fs.existsSync(metadataFile)) return; - - logger.debug(`Purging cached bundle '${metadataFile}'.`); - - const cachedResult: { - checksum: string; - externals: SharedInfo[]; - files: string[]; - } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - - cachedResult.files.forEach((file) => { - const cachedFile = path.join(pathToCache, file); - if (fs.existsSync(cachedFile)) fs.unlinkSync(cachedFile); - }); - - fs.unlinkSync(metadataFile); - }, -}); diff --git a/libs/native-federation-core/src/lib/utils/config-utils.ts b/libs/native-federation-core/src/lib/utils/config-utils.ts deleted file mode 100644 index 214b6e20..00000000 --- a/libs/native-federation-core/src/lib/utils/config-utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NormalizedFederationConfig } from '../config/federation-config'; -import { logger } from './logger'; -import { normalizePackageName } from './normalize'; - -export function resolveProjectName(config: NormalizedFederationConfig): string { - const normalizedProjectName = normalizePackageName(config.name); - if (normalizedProjectName.length < 1) { - logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to 'shell' cache folder (could collide with other projects in the workspace).", - ); - } - - return normalizedProjectName.length < 1 ? 'shell' : normalizedProjectName; -} diff --git a/libs/native-federation-core/src/lib/utils/errors.ts b/libs/native-federation-core/src/lib/utils/errors.ts deleted file mode 100644 index 1f843ec4..00000000 --- a/libs/native-federation-core/src/lib/utils/errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class AbortedError extends Error { - constructor(message: string) { - super(message); - this.name = 'AbortedError'; - Object.setPrototypeOf(this, AbortedError.prototype); - } -} diff --git a/libs/native-federation-core/src/lib/utils/get-external-imports.ts b/libs/native-federation-core/src/lib/utils/get-external-imports.ts deleted file mode 100644 index 29dde53d..00000000 --- a/libs/native-federation-core/src/lib/utils/get-external-imports.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import * as ts from 'typescript'; - -export function getExternalImports(entryFilePath: string) { - const visited = new Set(); - const externals = new Set(); - - function isExternal(specifier: string) { - return !specifier.startsWith('.') && !path.isAbsolute(specifier); - } - - function normalizeExternal(specifier: string) { - return specifier; - } - - function resolveAsFileOrDirectory(p: string) { - const abs = path.resolve(p); - - if (fs.existsSync(abs) && fs.statSync(abs).isFile()) return abs; - - const extensions = ['.ts', '.js', '.mjs', '.cjs']; - for (const ext of extensions) { - if (fs.existsSync(abs + ext) && fs.statSync(abs + ext).isFile()) { - return abs + ext; - } - } - - if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) { - for (const file of extensions.map((e) => 'index' + e)) { - const indexPath = path.join(abs, file); - if (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) { - return indexPath; - } - } - } - - return null; - } - - function visit(filePath: string) { - const absPath = path.resolve(filePath); - if (visited.has(absPath)) return; - visited.add(absPath); - - const resolved = resolveAsFileOrDirectory(absPath); - if (!resolved) return; - - const source = fs.readFileSync(resolved, 'utf8'); - const sourceFile = ts.createSourceFile( - resolved, - source, - ts.ScriptTarget.Latest, - true, - ); - - function walk(node: ts.Node) { - if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) { - const moduleSpecifier = node.moduleSpecifier; - if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier)) { - const spec = moduleSpecifier.text; - if (isExternal(spec)) { - externals.add(normalizeExternal(spec)); - } else { - const resolvedPath = resolveAsFileOrDirectory( - path.resolve(path.dirname(resolved ?? ''), spec), - ); - if (resolvedPath) visit(resolvedPath); - } - } - } - - if ( - ts.isCallExpression(node) && - ts.isIdentifier(node.expression) && - node.expression.kind === ts.SyntaxKind.Identifier && - node.expression.escapedText === 'require' && - node.arguments.length === 1 && - ts.isStringLiteral(node.arguments[0]) - ) { - const spec = node.arguments[0].text; - if (isExternal(spec)) { - externals.add(normalizeExternal(spec)); - } else { - const resolvedPath = resolveAsFileOrDirectory( - path.resolve(path.dirname(resolved ?? ''), spec), - ); - if (resolvedPath) visit(resolvedPath); - } - } - - ts.forEachChild(node, walk); - } - - ts.forEachChild(sourceFile, walk); - } - - visit(entryFilePath); - - return Array.from(externals); -} diff --git a/libs/native-federation-core/src/lib/utils/hash-file.ts b/libs/native-federation-core/src/lib/utils/hash-file.ts deleted file mode 100644 index 233ae3a5..00000000 --- a/libs/native-federation-core/src/lib/utils/hash-file.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as crypto from 'crypto'; -import * as fs from 'fs'; - -export function hashFile(fileName: string): string { - const fileBuffer = fs.readFileSync(fileName); - const hashSum = crypto.createHash('md5'); - hashSum.update(fileBuffer); - return hashSum.digest('hex'); -} diff --git a/libs/native-federation-core/src/lib/utils/logger.d.ts b/libs/native-federation-core/src/lib/utils/logger.d.ts deleted file mode 100644 index 9eb60d81..00000000 --- a/libs/native-federation-core/src/lib/utils/logger.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { LogLevels } from 'npmlog'; -export declare const logger: { - error: (msg: any) => void; - warn: (msg: any) => void; - notice: (msg: any) => void; - info: (msg: any) => void; - verbose: (msg: any) => void; - debug: (msg: any) => void; -}; -export declare const setLogLevel: (level: LogLevels | 'debug') => void; diff --git a/libs/native-federation-core/src/lib/utils/logger.ts b/libs/native-federation-core/src/lib/utils/logger.ts deleted file mode 100644 index 87b01c08..00000000 --- a/libs/native-federation-core/src/lib/utils/logger.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import chalk from 'chalk'; - -let verbose = false; - -export const logger = { - warn: (msg: any) => console.warn(chalk.bgYellow.ansi256(15)(' WARN '), msg), - error: (msg: any) => console.error(chalk.bgRed.ansi256(15)(' ERRR '), msg), - notice: (msg: any) => console.log(chalk.bgYellowBright.black(' NOTE '), msg), - info: (msg: any) => console.log(chalk.bgGreen.ansi256(15)(' INFO '), msg), - verbose: (msg: any) => - verbose && console.log(chalk.bgGreen.ansi256(15)(' DBG! '), msg), - debug: (msg: any) => - verbose && console.log(chalk.bgGreen.ansi256(15)(' DBG! '), msg), - measure: (start: [number, number], milestone: string) => { - if (!verbose) return; - - const [totalSeconds, nanoseconds] = process.hrtime(start); - const minutes = Math.floor(totalSeconds / 60); - const seconds = totalSeconds % 60; - const milliseconds = nanoseconds / 1000000; - - const msFormatted = milliseconds.toFixed(3); - const timeStr = `${minutes.toString().padStart(2, '0')}:${seconds - .toString() - .padStart(2, '0')}:${msFormatted.padStart(7, '0')}ms`; - - console.log( - chalk.bgGreen.ansi256(15)(' DBG! '), - `${timeStr} - ${milestone}`, - ); - }, -}; - -export const setLogLevel = (level: string) => { - verbose = level === 'verbose'; -}; diff --git a/libs/native-federation-core/src/lib/utils/mapped-paths.ts b/libs/native-federation-core/src/lib/utils/mapped-paths.ts deleted file mode 100644 index 833861f0..00000000 --- a/libs/native-federation-core/src/lib/utils/mapped-paths.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import * as JSON5 from 'json5'; -import { logger } from '../utils/logger'; -import { resolveTsConfigWildcard } from './resolve-wildcard-keys'; - -export interface MappedPath { - key: string; - path: string; -} - -export interface GetMappedPathsOptions { - rootTsConfigPath: string; - sharedMappings?: string[]; - rootPath?: string; -} - -export function getMappedPaths({ - rootTsConfigPath, - sharedMappings, - rootPath, -}: GetMappedPathsOptions): Array { - const result: Array = []; - - if (!path.isAbsolute(rootTsConfigPath)) { - throw new Error( - 'SharedMappings.register: tsConfigPath needs to be an absolute path!', - ); - } - - if (!rootPath) { - rootPath = path.normalize(path.dirname(rootTsConfigPath)); - } - const shareAllMappings = !sharedMappings; - - if (!sharedMappings) { - sharedMappings = []; - } - const globSharedMappings = sharedMappings - .filter((m) => m.endsWith('*')) - .map((m) => m.slice(0, -1)); - - const tsConfig = JSON5.parse( - fs.readFileSync(rootTsConfigPath, { encoding: 'utf-8' }), - ); - - const mappings = tsConfig?.compilerOptions?.paths; - - if (!mappings) { - return result; - } - - for (const key in mappings) { - if (mappings[key].length > 1) { - logger.warn( - '[shared-mapping][' + - key + - '] A mapping path with more than 1 entryPoint is currently not supported, falling back to the first path.', - ); - } - const libPaths = key.includes('*') - ? resolveTsConfigWildcard(key, mappings[key][0], rootPath).map( - ({ key, value }) => ({ - key, - path: path.normalize(path.join(rootPath, value)), - }), - ) - : [{ key, path: path.normalize(path.join(rootPath, mappings[key][0])) }]; - - libPaths - .filter( - (mapping) => - shareAllMappings || - sharedMappings.includes(mapping.key) || - globSharedMappings.some((m) => mapping.key.startsWith(m)), - ) - .forEach((mapping) => { - result.push(mapping); - }); - } - - return result; -} diff --git a/libs/native-federation-core/src/lib/utils/normalize.ts b/libs/native-federation-core/src/lib/utils/normalize.ts deleted file mode 100644 index 4c81b515..00000000 --- a/libs/native-federation-core/src/lib/utils/normalize.ts +++ /dev/null @@ -1,22 +0,0 @@ -export function normalize(path: string, trailingSlash?: boolean): string { - let cand = path.replace(/\\/g, '/'); - - if (typeof trailingSlash === 'undefined') { - return cand; - } - - while (cand.endsWith('/')) { - cand = cand.substring(0, cand.length - 1); - } - - if (trailingSlash) { - return cand + '/'; - } - - return cand; -} - -export function normalizePackageName(fileName: string) { - const sanitized = fileName.replace(/[^A-Za-z0-9]/g, '_'); - return sanitized.startsWith('_') ? sanitized.slice(1) : sanitized; -} diff --git a/libs/native-federation-core/src/lib/utils/package-info.ts b/libs/native-federation-core/src/lib/utils/package-info.ts deleted file mode 100644 index e4591df3..00000000 --- a/libs/native-federation-core/src/lib/utils/package-info.ts +++ /dev/null @@ -1,395 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { logger } from './logger'; -import { normalize } from './normalize'; - -export interface PackageInfo { - packageName: string; - entryPoint: string; - version: string; - esm: boolean; -} - -export interface PartialPackageJson { - module: string; - main: string; -} - -export type VersionMap = Record; - -export type PackageJsonInfo = { - content: any; - directory: string; -}; - -export type ExportCondition = - | 'import' - | 'require' - | 'node' - | 'cjs' - | 'esm' - | 'default' - | 'types' - | 'browser' - | (string & {}); - -export const isESMExport = (e: string): boolean | undefined => { - if (e === 'import' || e === 'module-sync') return true; - // Common ESM conventions - if (e === 'module' || e === 'esm' || /^es20\d{2}$/.test(e)) return true; - - if (e === 'require') return false; - // Common CJS conventions - if (e === 'cjs' || e === 'commonjs') return false; - - // Ambiguous - return undefined; -}; -export type ExportEntry = - | string - | undefined - | { [key in ExportCondition]?: ExportEntry } - | ExportEntry[]; - -export type PackageJsonExports = - | string - | ExportEntry - | { [path: `.${string}`]: ExportEntry }; - -const packageCache: Record = {}; - -export function findPackageJsonFiles( - project: string, - workspace: string, -): string[] { - return expandFolders(project, workspace) - .map((f) => path.join(f, 'package.json')) - .filter((f) => fs.existsSync(f)); -} - -export function expandFolders(child: string, parent: string): string[] { - const result: string[] = []; - parent = normalize(parent, true); - child = normalize(child, true); - - if (!child.startsWith(parent)) { - throw new Error( - `Workspace folder ${path} needs to be a parent of the project folder ${child}`, - ); - } - - let current = child; - - while (current !== parent) { - result.push(current); - - const cand = normalize(path.dirname(current), true); - if (cand === current) { - break; - } - current = cand; - } - result.push(parent); - return result; -} - -export function getPackageInfo( - packageName: string, - workspaceRoot: string, -): PackageInfo | null { - workspaceRoot = normalize(workspaceRoot, true); - - const packageJsonInfos = getPackageJsonFiles(workspaceRoot, workspaceRoot); - - for (const info of packageJsonInfos) { - const cand = _getPackageInfo(packageName, info.directory); - if (cand) { - return cand; - } - } - - logger.warn('No meta data found for shared lib ' + packageName); - return null; -} - -function getVersionMapCacheKey(project: string, workspace: string): string { - return `${project}**${workspace}`; -} - -export function getVersionMaps( - project: string, - workspace: string, -): VersionMap[] { - return getPackageJsonFiles(project, workspace).map((json) => ({ - ...json.content['dependencies'], - })); -} - -export function getPackageJsonFiles( - project: string, - workspace: string, -): PackageJsonInfo[] { - const cacheKey = getVersionMapCacheKey(project, workspace); - - let maps = packageCache[cacheKey]; - - if (maps) { - return maps; - } - - maps = findPackageJsonFiles(project, workspace).map((f) => { - const content = JSON.parse(fs.readFileSync(f, 'utf-8')); - const directory = normalize(path.dirname(f), true); - const result: PackageJsonInfo = { - content, - directory, - }; - return result; - }); - - packageCache[cacheKey] = maps; - return maps; -} - -export function findDepPackageJson( - packageName: string, - projectRoot: string, -): string | null { - const mainPkgName = getPkgFolder(packageName); - - let mainPkgPath = path.join(projectRoot, 'node_modules', mainPkgName); - let mainPkgJsonPath = path.join(mainPkgPath, 'package.json'); - - let directory = projectRoot; - - while (path.dirname(directory) !== directory) { - if (fs.existsSync(mainPkgJsonPath)) { - break; - } - - directory = normalize(path.dirname(directory), true); - - mainPkgPath = path.join(directory, 'node_modules', mainPkgName); - mainPkgJsonPath = path.join(mainPkgPath, 'package.json'); - } - - if (!fs.existsSync(mainPkgJsonPath)) { - // TODO: Add logger - // context.logger.warn('No package.json found for ' + packageName); - logger.verbose( - 'No package.json found for ' + packageName + ' in ' + mainPkgPath, - ); - - return null; - } - return mainPkgJsonPath; -} - -function replaceGlob(target: ExportEntry, replacement: string): ExportEntry { - if (!target) return undefined; - if (typeof target === 'string') return target.replace('*', replacement); - return Object.entries(target).reduce( - (a, [k, v]) => ({ - ...a, - [k]: replaceGlob(v!, replacement), - }), - {} as Omit, - ); -} - -function findOptimalExport( - target: ExportEntry, - info: PackageInfo, - isESM: boolean | undefined = undefined, -): PackageInfo | undefined { - if (typeof target === 'string') { - return { - ...info, - entryPoint: path.join(info.entryPoint, target), - esm: isESM ?? info.esm, - }; - } - if (!target) return undefined; - if (Array.isArray(target)) return findOptimalExport(target[0], info, isESM); - - const exportTypes = Object.keys(target); - - // We prefer ESM exports for native support. - if (typeof isESM === 'undefined') { - const esmExport = exportTypes.find((e) => isESMExport(e)); - if (esmExport) { - return findOptimalExport(target[esmExport], info, true); - } - } - - // Node.js looks at the exports object and uses the first key that matches the current environment. - const secondBestEntry = - 'default' in target && target['default'] - ? 'default' - : exportTypes.filter((e) => e !== 'types')[0]; - const secondBestExport: ExportEntry = target[secondBestEntry]; - - return findOptimalExport( - secondBestExport, - info, - isESM ?? isESMExport(secondBestEntry), - ); -} - -export function _getPackageInfo( - packageName: string, - directory: string, -): PackageInfo | null { - const mainPkgName = getPkgFolder(packageName); - const mainPkgJsonPath = findDepPackageJson(packageName, directory); - - if (!mainPkgJsonPath) { - return null; - } - - const mainPkgPath = path.dirname(mainPkgJsonPath); - const mainPkgJson = readJson(mainPkgJsonPath); - - const version = mainPkgJson['version'] as string; - const esm = mainPkgJson['type'] === 'module'; - - if (!version) { - logger.warn('No version found for ' + packageName); - return null; - } - - const pathToSecondary = path.relative(mainPkgName, packageName); - const relSecondaryPath = !pathToSecondary - ? '.' - : './' + pathToSecondary.replace(/\\/g, '/'); - - let secondaryEntryPoint: ExportEntry = undefined; - - const packageJsonExportsEntry = Object.keys(mainPkgJson?.exports ?? []).find( - (e) => { - if (e === relSecondaryPath) return true; - if (e === './*') return true; - if (!e.endsWith('*')) return false; - const globPath = e.substring(0, e.length - 1); - return relSecondaryPath.startsWith(globPath); - }, - ); - - if (packageJsonExportsEntry) { - secondaryEntryPoint = mainPkgJson?.exports?.[packageJsonExportsEntry]; - - if (packageJsonExportsEntry.endsWith('*')) { - const replacement = relSecondaryPath.substring( - packageJsonExportsEntry.length - 1, - ); - secondaryEntryPoint = replaceGlob(secondaryEntryPoint, replacement); - } - } - - if (secondaryEntryPoint) { - const info = findOptimalExport(secondaryEntryPoint, { - entryPoint: mainPkgPath, - packageName, - version, - esm, - }); - if (info) return info; - } - - if (mainPkgJson['module'] && relSecondaryPath === '.') { - return { - entryPoint: path.join(mainPkgPath, mainPkgJson['module']), - packageName, - version, - esm: true, - }; - } - - const secondaryPgkPath = path.join(mainPkgPath, relSecondaryPath); - const secondaryPgkJsonPath = path.join(secondaryPgkPath, 'package.json'); - let secondaryPgkJson: PartialPackageJson | null = null; - if (fs.existsSync(secondaryPgkJsonPath)) { - secondaryPgkJson = readJson(secondaryPgkJsonPath); - } - - if (secondaryPgkJson && secondaryPgkJson.module) { - return { - entryPoint: path.join(secondaryPgkPath, secondaryPgkJson.module), - packageName, - version, - esm: true, - }; - } - - let cand = path.join(secondaryPgkPath, 'index.mjs'); - if (fs.existsSync(cand)) { - return { - entryPoint: cand, - packageName, - version, - esm: true, - }; - } - - if (secondaryPgkJson && secondaryPgkJson.main) { - return { - entryPoint: path.join(secondaryPgkPath, secondaryPgkJson.main), - packageName, - version, - esm, - }; - } - - cand = path.join(secondaryPgkPath, 'index.js'); - if (fs.existsSync(cand)) { - return { - entryPoint: cand, - packageName, - version, - esm, - }; - } - - cand = secondaryPgkPath + '.js'; - if (fs.existsSync(cand)) { - return { - entryPoint: cand, - packageName, - version, - esm, - }; - } - - cand = secondaryPgkPath + '.mjs'; - if (fs.existsSync(cand)) { - return { - entryPoint: cand, - packageName, - version, - esm, - }; - } - - logger.warn('No entry point found for ' + packageName); - logger.warn( - "If you don't need this package, skip it in your federation.config.js or consider moving it into devDependencies in your package.json", - ); - - return null; -} - -function readJson(mainPkgJsonPath: string) { - return JSON.parse(fs.readFileSync(mainPkgJsonPath, 'utf-8')); -} - -function getPkgFolder(packageName: string) { - const parts = packageName.split('/'); - - let folder = parts[0]; - - if (folder.startsWith('@')) { - folder += '/' + parts[1]; - } - - return folder; -} diff --git a/libs/native-federation-core/src/lib/utils/rebuild-queue.ts b/libs/native-federation-core/src/lib/utils/rebuild-queue.ts deleted file mode 100644 index a2023cf9..00000000 --- a/libs/native-federation-core/src/lib/utils/rebuild-queue.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { logger } from './logger'; - -interface BuildControl { - controller: AbortController; - buildFinished: { resolve: () => void; promise: Promise }; -} - -export class RebuildQueue { - private activeBuilds: Map = new Map(); - private buildCounter = 0; - - async enqueue( - rebuildFn: (signal: AbortSignal) => Promise, - ): Promise { - const buildId = ++this.buildCounter; - - const pendingCancellations = Array.from(this.activeBuilds.values()).map( - (buildInfo) => { - buildInfo.controller.abort(); - return buildInfo.buildFinished.promise; - }, - ); - - if (pendingCancellations.length > 0) { - logger.info(`Aborting ${pendingCancellations.length} bundling task(s)..`); - } - - if (pendingCancellations.length > 0) { - await Promise.all(pendingCancellations); - } - - let buildFinished: () => void; - const completionPromise = new Promise((resolve) => { - buildFinished = resolve; - }); - - const control: BuildControl = { - controller: new AbortController(), - buildFinished: { - resolve: buildFinished!, - promise: completionPromise, - }, - }; - this.activeBuilds.set(buildId, control); - - try { - await rebuildFn(control.controller.signal); - } finally { - control.buildFinished.resolve(); - this.activeBuilds.delete(buildId); - } - } - - dispose(): void { - for (const [_, buildInfo] of this.activeBuilds) { - buildInfo.controller.abort(); - } - this.activeBuilds.clear(); - } -} diff --git a/libs/native-federation-core/src/lib/utils/resolve-glob.ts b/libs/native-federation-core/src/lib/utils/resolve-glob.ts deleted file mode 100644 index d5b665ad..00000000 --- a/libs/native-federation-core/src/lib/utils/resolve-glob.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -export function resolveGlobSync( - pattern: string, - baseDir = process.cwd(), -): string[] { - if (pattern.startsWith('./')) { - pattern = pattern.substring(2); - } - - const segments = pattern.split('/').filter(Boolean); - const results: string[] = []; - - function search(dir: string, segmentIndex: number) { - if (segmentIndex >= segments.length) { - results.push(dir); - return; - } - - const segment = segments[segmentIndex]; - const entries = fs.readdirSync(dir, { withFileTypes: true }); - - if (segment === '*') { - entries - .filter((entry) => entry.isDirectory()) - .forEach((entry) => - search(path.join(dir, entry.name), segmentIndex + 1), - ); - } else { - entries - .filter((entry) => entry.name === segment) - .forEach((entry) => - search(path.join(dir, entry.name), segmentIndex + 1), - ); - } - } - - search(baseDir, 0); - return results; -} diff --git a/libs/native-federation-core/src/lib/utils/resolve-wildcard-keys.ts b/libs/native-federation-core/src/lib/utils/resolve-wildcard-keys.ts deleted file mode 100644 index b0f9685a..00000000 --- a/libs/native-federation-core/src/lib/utils/resolve-wildcard-keys.ts +++ /dev/null @@ -1,166 +0,0 @@ -import fg from 'fast-glob'; -import * as fs from 'fs'; -import * as path from 'path'; -import { logger } from './logger'; - -export type KeyValuePair = { - key: string; - value: string; -}; - -// TypeScript's module resolution for directories checks these in order -// @see https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution -const TS_INDEX_FILES = [ - 'index.ts', - 'index.tsx', - 'index.mts', - 'index.cts', - 'index.d.ts', - 'index.js', - 'index.jsx', - 'index.mjs', - 'index.cjs', -]; - -/** - * Resolves tsconfig wildcard paths. - * - * In tsconfig.json, paths like `@features/*` → `libs/features/src/*` work as follows: - * - The `*` captures a single path segment (the module name) - * - When importing `@features/feature-a`, TypeScript captures `feature-a` - * - It then replaces `*` in the value pattern: `libs/features/src/feature-a` - * - * For discovery, we find all directories at the wildcard position that TypeScript - * would recognize as valid modules (directories with index files or package.json). - * -// @see https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution - */ -export function resolveTsConfigWildcard( - keyPattern: string, - valuePattern: string, - cwd: string, -): KeyValuePair[] { - const normalizedPattern = valuePattern.replace(/^\.?\/+/, ''); - - const asteriskIndex = normalizedPattern.indexOf('*'); - if (asteriskIndex === -1) { - return []; - } - - const prefix = normalizedPattern.substring(0, asteriskIndex); - const suffix = normalizedPattern.substring(asteriskIndex + 1); - - const searchPath = path.join(cwd, prefix); - - let entries: string[]; - try { - entries = fs.readdirSync(searchPath); - } catch { - return []; - } - - const keys: KeyValuePair[] = []; - - for (const entry of entries) { - const entryPath = path.join(searchPath, entry); - - let stats: fs.Stats; - try { - stats = fs.statSync(entryPath); - } catch { - continue; - } - - if (!stats.isDirectory()) { - // Skipping individual files, we only process modules - continue; - } - - let modulePath = path.join(prefix, entry, suffix).replace(/\\/g, '/'); - const fullPath = path.join(cwd, modulePath); - - let fullPathStats: fs.Stats; - try { - fullPathStats = fs.statSync(fullPath); - } catch { - continue; - } - - const key = keyPattern.replace('*', entry); - - if (fullPathStats.isDirectory()) { - const indexFile = TS_INDEX_FILES.find((indexFile) => - fs.existsSync(path.join(fullPath, indexFile)), - ); - - if (!indexFile) { - logger.warn( - `[shared-mappings] Internal lib '${key}' does not contain an entryPoint (barrel file).`, - ); - continue; - } - modulePath = path.join(modulePath, indexFile); - } else if (!fullPathStats.isFile()) { - continue; - } - - keys.push({ - key, - value: modulePath, - }); - } - - return keys; -} - -/** - * Resolves package.json exports wildcard patterns. - * - * In package.json exports, patterns like `./features/*.js` → `./src/features/*.js` work as follows: - * - The `*` is a literal string replacement that can include path separators - * - Importing `pkg/features/a/b.js` captures `a/b` and replaces `*` → `./src/features/a/b.js` - * - This matches actual files, not directories - * - * @see https://nodejs.org/api/packages.html#subpath-patterns - */ -export function resolvePackageJsonExportsWildcard( - keyPattern: string, - valuePattern: string, - cwd: string, -): KeyValuePair[] { - const normalizedPattern = valuePattern.replace(/^\.?\/+/, ''); - - const asteriskIndex = normalizedPattern.indexOf('*'); - if (asteriskIndex === -1) { - return []; - } - - const prefix = normalizedPattern.substring(0, asteriskIndex); - const suffix = normalizedPattern.substring(asteriskIndex + 1); - - // fast-glob requires **/* pattern for matching files at any depth - const files = fg.sync(prefix + '**/*' + suffix, { - cwd, - onlyFiles: true, - deep: Infinity, - }); - - const keys: KeyValuePair[] = []; - - for (const file of files) { - const relPath = file.replace(/\\/g, '/').replace(/^\.\//, ''); - - const captured = suffix - ? relPath.slice(prefix.length, -suffix.length) - : relPath.slice(prefix.length); - - const key = keyPattern.replace('*', captured); - - keys.push({ - key, - value: relPath, - }); - } - - return keys; -} diff --git a/libs/native-federation-core/src/lib/utils/rewrite-chunk-imports.ts b/libs/native-federation-core/src/lib/utils/rewrite-chunk-imports.ts deleted file mode 100644 index cb06331b..00000000 --- a/libs/native-federation-core/src/lib/utils/rewrite-chunk-imports.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as ts from 'typescript'; -import * as fs from 'fs'; -import * as path from 'path'; - -export const INTERNAL_SCOPE = '@nf-internal'; - -export function rewriteChunkImports(filePath: string) { - const sourceCode = fs.readFileSync(filePath, 'utf-8'); - - const sourceFile = ts.createSourceFile( - path.basename(filePath), - sourceCode, - ts.ScriptTarget.ESNext, - true, - ts.ScriptKind.JS, - ); - - const printer = ts.createPrinter(); - - function visit(node: ts.Node): ts.Node { - // import ... from '...' - if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) { - const moduleSpecifier = node.moduleSpecifier; - if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier)) { - const text = moduleSpecifier.text; - if (text.startsWith('./')) { - const newModuleSpecifier = ts.factory.createStringLiteral( - deriveInternalName(text), - ); - - if (ts.isImportDeclaration(node)) { - return ts.factory.updateImportDeclaration( - node, - node.modifiers, - node.importClause, - newModuleSpecifier, - node.assertClause, - ); - } else { - return ts.factory.updateExportDeclaration( - node, - node.modifiers, - node.isTypeOnly, - node.exportClause, - newModuleSpecifier, - node.assertClause, - ); - } - } - } - } - - // import('./...') - if ( - ts.isCallExpression(node) && - node.expression.kind === ts.SyntaxKind.ImportKeyword - ) { - const [arg] = node.arguments; - if (arg && ts.isStringLiteral(arg)) { - const text = arg.text; - if (text.startsWith('./')) { - const newArg = ts.factory.createStringLiteral( - deriveInternalName(text), - ); - return ts.factory.updateCallExpression( - node, - node.expression, - node.typeArguments, - [newArg], - ); - } - } - } - - return ts.visitEachChild(node, visit, undefined); - } - - const transformed = ts.transform(sourceFile, [ - (context) => (node) => ts.visitNode(node as any, visit), - ]); - const updatedSourceFile = transformed.transformed[0]; - - const result = printer.printFile(updatedSourceFile as ts.SourceFile); - - fs.writeFileSync(filePath, result, 'utf-8'); -} - -export function isSourceFile(fileName: string): boolean { - return !!fileName.match(/.(m|c)?js$/); -} - -export function deriveInternalName(fileName: string): string { - if (fileName.startsWith('./')) { - fileName = fileName.slice(2); - } - - const packageName = fileName.replace(/.(m|c)?js$/, ''); - return INTERNAL_SCOPE + '/' + packageName; -} diff --git a/libs/native-federation-core/stack.png b/libs/native-federation-core/stack.png deleted file mode 100644 index a59f9af7422c83311d77b35197ea7bf5bc77ee89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11702 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/tsconfig.spec.json' }], - }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/native-federation-esbuild', -}; diff --git a/libs/native-federation-esbuild/package.json b/libs/native-federation-esbuild/package.json deleted file mode 100644 index f2e4d360..00000000 --- a/libs/native-federation-esbuild/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@softarc/native-federation-esbuild", - "version": "3.5.5", - "type": "commonjs", - "dependencies": { - "@rollup/plugin-commonjs": "^28.0.3", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-replace": "^6.0.2", - "rollup": "^4.52.0", - "rollup-plugin-node-externals": "^8.1.1", - "esbuild": "^0.27.1", - "signale": "^1.4.0", - "acorn": "^8.8.1" - } -} diff --git a/libs/native-federation-esbuild/project.json b/libs/native-federation-esbuild/project.json deleted file mode 100644 index a451c8b4..00000000 --- a/libs/native-federation-esbuild/project.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "native-federation-esbuild", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/native-federation-esbuild/src", - "projectType": "library", - "targets": { - "build": { - "executor": "@nx/js:tsc", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/libs/native-federation-esbuild", - "main": "libs/native-federation-esbuild/src/index.ts", - "tsConfig": "libs/native-federation-esbuild/tsconfig.lib.json", - "assets": [ - "libs/native-federation-esbuild/*.md", - "libs/native-federation-esbuild/LICENSE" - ] - } - }, - "publish": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-esbuild npm {args.ver} {args.tag}" - } - }, - "publish-local": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-esbuild verdaccio {args.ver}" - } - } - }, - "tags": ["org:softarc", "scope:nf"] -} diff --git a/libs/native-federation-esbuild/src/index.ts b/libs/native-federation-esbuild/src/index.ts deleted file mode 100644 index b74a290f..00000000 --- a/libs/native-federation-esbuild/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib/adapter'; diff --git a/libs/native-federation-esbuild/src/lib/adapter.ts b/libs/native-federation-esbuild/src/lib/adapter.ts deleted file mode 100644 index d3ba363a..00000000 --- a/libs/native-federation-esbuild/src/lib/adapter.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { - BuildAdapter, - BuildAdapterOptions, - BuildResult, -} from '@softarc/native-federation/build'; -import * as esbuild from 'esbuild'; -import { rollup } from 'rollup'; -import resolve from '@rollup/plugin-node-resolve'; -import { nodeExternals } from 'rollup-plugin-node-externals'; -import * as fs from 'fs'; -import path from 'path'; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const commonjs = require('@rollup/plugin-commonjs'); - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const replace = require('@rollup/plugin-replace'); - -export const esBuildAdapter: BuildAdapter = createEsBuildAdapter({ - plugins: [], -}); - -export type ReplacementConfig = { - file: string; -}; - -export interface EsBuildAdapterConfig { - plugins: esbuild.Plugin[]; - fileReplacements?: Record; - skipRollup?: boolean; - compensateExports?: RegExp[]; - loader?: { [ext: string]: esbuild.Loader }; -} - -export function createEsBuildAdapter(config: EsBuildAdapterConfig) { - if (!config.compensateExports) { - config.compensateExports = [new RegExp('/react/')]; - } - - return async (options: BuildAdapterOptions): Promise => { - const { entryPoints, external, outdir, hash } = options; - - // TODO: Do we need to prepare packages anymore as esbuild has evolved? - - for (const entryPoint of entryPoints) { - const isPkg = entryPoint.fileName.includes('node_modules'); - const pkgName = isPkg ? inferPkgName(entryPoint.fileName) : ''; - const tmpFolder = `node_modules/.tmp/${pkgName}`; - - if (isPkg) { - await prepareNodePackage( - entryPoint.fileName, - external, - tmpFolder, - config, - !!options.dev, - ); - - entryPoint.fileName = tmpFolder; - } - } - - const ctx = await esbuild.context({ - entryPoints: entryPoints.map((ep) => ({ - in: ep.fileName, - out: path.parse(ep.outName).name, - })), - write: false, - outdir, - entryNames: hash ? '[name]-[hash]' : '[name]', - external, - loader: config.loader, - bundle: true, - sourcemap: options.dev, - minify: !options.dev, - format: 'esm', - target: ['esnext'], - plugins: [...config.plugins], - }); - - const result = await ctx.rebuild(); - const writtenFiles = writeResult(result, outdir); - ctx.dispose(); - return writtenFiles.map((fileName) => ({ fileName })); - - // const normEntryPoint = entryPoint.replace(/\\/g, '/'); - // if ( - // isPkg && - // config?.compensateExports?.find((regExp) => regExp.exec(normEntryPoint)) - // ) { - // logger.verbose('compensate exports for ' + tmpFolder); - // compensateExports(tmpFolder, outfile); - // } - }; -} - -function writeResult( - result: esbuild.BuildResult, - outdir: string, -) { - const outputFiles = result.outputFiles || []; - const writtenFiles: string[] = []; - for (const outFile of outputFiles) { - const fileName = path.basename(outFile.path); - const filePath = path.join(outdir, fileName); - fs.writeFileSync(filePath, outFile.contents); - writtenFiles.push(filePath); - } - - return writtenFiles; -} - -// TODO: Unused, to delete? -// function compensateExports(entryPoint: string, outfile?: string): void { -// const inExports = collectExports(entryPoint); -// const outExports = outfile ? collectExports(outfile) : inExports; -// -// if (!outExports.hasDefaultExport || outExports.hasFurtherExports) { -// return; -// } -// const defaultName = outExports.defaultExportName; -// -// let exports = '/*Try to compensate missing exports*/\n\n'; -// for (const exp of inExports.exports) { -// exports += `let ${exp}$softarc = ${defaultName}.${exp};\n`; -// exports += `export { ${exp}$softarc as ${exp} };\n`; -// } -// -// const target = outfile ?? entryPoint; -// fs.appendFileSync(target, exports, 'utf-8'); -// } - -async function prepareNodePackage( - entryPoint: string, - external: string[], - tmpFolder: string, - config: EsBuildAdapterConfig, - dev: boolean, -) { - if (config.fileReplacements) { - entryPoint = replaceEntryPoint( - entryPoint, - normalize(config.fileReplacements), - ); - } - - const env = dev ? 'development' : 'production'; - - const result = await rollup({ - input: entryPoint, - - plugins: [ - commonjs(), - nodeExternals({ include: external }), - resolve(), - replace({ - preventAssignment: true, - values: { - 'process.env.NODE_ENV': `"${env}"`, - }, - }), - ], - }); - - await result.write({ - format: 'esm', - file: tmpFolder, - sourcemap: dev, - exports: 'named', - }); -} - -function inferPkgName(entryPoint: string) { - return entryPoint - .replace(/.*?node_modules/g, '') - .replace(/[^A-Za-z0-9.]/g, '_'); -} - -function normalize( - config: Record, -): Record { - const result: Record = {}; - for (const key in config) { - if (typeof config[key] === 'string') { - result[key] = { - file: config[key] as string, - }; - } else { - result[key] = config[key] as ReplacementConfig; - } - } - return result; -} - -function replaceEntryPoint( - entryPoint: string, - fileReplacements: Record, -): string { - entryPoint = entryPoint.replace(/\\/g, '/'); - - for (const key in fileReplacements) { - entryPoint = entryPoint.replace( - new RegExp(`${key}$`), - fileReplacements[key].file, - ); - } - - return entryPoint; -} diff --git a/libs/native-federation-esbuild/src/lib/collect-exports.ts b/libs/native-federation-esbuild/src/lib/collect-exports.ts deleted file mode 100644 index 0f896b61..00000000 --- a/libs/native-federation-esbuild/src/lib/collect-exports.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as fs from 'fs'; -import { parse } from 'acorn'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Node = any; - -type visitFn = (node: Node) => void; - -export function collectExports(path: string) { - const src = fs.readFileSync(path, 'utf8'); - - const parseTree = parse(src, { - ecmaVersion: 'latest', - allowHashBang: true, - sourceType: 'module', - }); - - let hasDefaultExport = false; - let hasFurtherExports = false; - let defaultExportName = ''; - const exports = new Set(); - - traverse(parseTree, (node) => { - if ( - node.type === 'AssignmentExpression' && - node?.left?.object?.name === 'exports' // && - ) { - exports.add(node.left.property?.name); - return; - } - - if (hasDefaultExport && hasFurtherExports) { - return; - } - - if (node.type !== 'ExportNamedDeclaration') { - return; - } - - if (!node.specifiers) { - hasFurtherExports = true; - return; - } - - for (const s of node.specifiers) { - if (isDefaultExport(s)) { - defaultExportName = s?.local?.name; - hasDefaultExport = true; - } else { - hasFurtherExports = true; - } - } - }); - - return { - hasDefaultExport, - hasFurtherExports, - defaultExportName, - exports: [...exports], - }; -} - -function traverse(node: Node, visit: visitFn) { - visit(node); - for (const key in node) { - const prop = node[key]; - if (prop && typeof prop === 'object') { - traverse(prop as Node, visit); - } else if (Array.isArray(prop)) { - for (const sub of prop) { - traverse(sub, visit); - } - } - } -} - -function isDefaultExport(exportSpecifier: Node) { - return ( - exportSpecifier.exported?.type === 'Identifier' && - exportSpecifier.exported?.name === 'default' - ); -} diff --git a/libs/native-federation-esbuild/src/lib/react-replacements.ts b/libs/native-federation-esbuild/src/lib/react-replacements.ts deleted file mode 100644 index 99d7d1c4..00000000 --- a/libs/native-federation-esbuild/src/lib/react-replacements.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ReplacementConfig } from './adapter'; - -export const reactReplacements: Record< - string, - Record -> = { - dev: { - 'node_modules/react/index.js': { - file: 'node_modules/react/cjs/react.development.js', - }, - 'node_modules/react/jsx-dev-runtime.js': { - file: 'node_modules/react/cjs/react-jsx-dev-runtime.development.js', - }, - 'node_modules/react/jsx-runtime.js': { - file: 'node_modules/react/cjs/react-jsx-runtime.development.js', - }, - 'node_modules/react-dom/index.js': { - file: 'node_modules/react-dom/cjs/react-dom.development.js', - }, - }, - prod: { - 'node_modules/react/index.js': { - file: 'node_modules/react/cjs/react.production.min.js', - }, - 'node_modules/react/jsx-dev-runtime.js': { - file: 'node_modules/react/cjs/react-jsx-dev-runtime.production.min.js', - }, - 'node_modules/react/jsx-runtime.js': { - file: 'node_modules/react/cjs/react-jsx-runtime.production.min.js', - }, - 'node_modules/react-dom/index.js': { - file: 'node_modules/react-dom/cjs/react-dom.production.min.js', - }, - }, -}; diff --git a/libs/native-federation-esbuild/tsconfig.json b/libs/native-federation-esbuild/tsconfig.json deleted file mode 100644 index f8ee24a1..00000000 --- a/libs/native-federation-esbuild/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "commonjs", - "forceConsistentCasingInFileNames": true, - // "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] -} diff --git a/libs/native-federation-esbuild/tsconfig.lib.json b/libs/native-federation-esbuild/tsconfig.lib.json deleted file mode 100644 index 33eca2c2..00000000 --- a/libs/native-federation-esbuild/tsconfig.lib.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "declaration": true, - "types": ["node"] - }, - "include": ["src/**/*.ts"], - "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] -} diff --git a/libs/native-federation-esbuild/tsconfig.spec.json b/libs/native-federation-esbuild/tsconfig.spec.json deleted file mode 100644 index 9b2a121d..00000000 --- a/libs/native-federation-esbuild/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/libs/native-federation-node/LICENSE b/libs/native-federation-node/LICENSE deleted file mode 100644 index 3edc92e0..00000000 --- a/libs/native-federation-node/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright 2021 Softarc Consulting GmbH - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/libs/native-federation-node/README.md b/libs/native-federation-node/README.md deleted file mode 100644 index 1a218d3b..00000000 --- a/libs/native-federation-node/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# native-federation-node - -This library was generated with [Nx](https://nx.dev). - -## Building - -Run `nx build native-federation-node` to build the library. - -## Running unit tests - -Run `nx test native-federation-node` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/native-federation-node/build/create-data-url.js b/libs/native-federation-node/build/create-data-url.js deleted file mode 100644 index 5d4555d5..00000000 --- a/libs/native-federation-node/build/create-data-url.js +++ /dev/null @@ -1,16 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const loaderSource = '../src/lib/utils/import-map-loader.js'; -const dataUrlFile = '../src/lib/utils/loader-as-data-url.js'; - -const filePath = path.resolve(__dirname, loaderSource); -const fileContent = fs.readFileSync(filePath, 'utf8'); -const base64Content = Buffer.from(fileContent).toString('base64'); - -const dataUrl = `data:text/javascript;base64,${base64Content}`; - -const outputPath = path.resolve(__dirname, dataUrlFile); -fs.writeFileSync(outputPath, `export const resolver = "${base64Content}";\n`); - -console.log('Created data url for loader'); diff --git a/libs/native-federation-node/eslint.config.mjs b/libs/native-federation-node/eslint.config.mjs deleted file mode 100644 index 5b32b75f..00000000 --- a/libs/native-federation-node/eslint.config.mjs +++ /dev/null @@ -1,34 +0,0 @@ -import baseConfig from '../../eslint.base.config.mjs'; -import jsoncEslintParser from 'jsonc-eslint-parser'; - -export default [ - ...baseConfig, - { - // esbuild bundle produced by update-local-nf.sh, not hand-written source. - ignores: ['src/lib/utils/fstart.mjs'], - }, - { - 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: {}, - }, - { - files: ['**/*.json'], - rules: { - '@nx/dependency-checks': 'error', - }, - languageOptions: { - parser: jsoncEslintParser, - }, - }, -]; diff --git a/libs/native-federation-node/jest.config.ts b/libs/native-federation-node/jest.config.ts deleted file mode 100644 index eebe998e..00000000 --- a/libs/native-federation-node/jest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'native-federation-node', - preset: '../../jest.preset.js', - testEnvironment: 'node', - transform: { - '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], - }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/native-federation-node', -}; diff --git a/libs/native-federation-node/package.json b/libs/native-federation-node/package.json deleted file mode 100644 index f5b59950..00000000 --- a/libs/native-federation-node/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "@softarc/native-federation-node", - "version": "3.5.5", - "license": "MIT", - "dependencies": { - "@softarc/native-federation-runtime": "~3.5.4", - "tslib": "^2.8.1" - } -} diff --git a/libs/native-federation-node/project.json b/libs/native-federation-node/project.json deleted file mode 100644 index efa7f7c8..00000000 --- a/libs/native-federation-node/project.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "native-federation-node", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/native-federation-node/src", - "projectType": "library", - "release": { - "version": { - "currentVersionResolver": "git-tag", - "preserveLocalDependencyProtocols": false, - "manifestRootsToUpdate": ["dist/{projectRoot}"] - } - }, - "tags": ["org:softarc", "scope:nf"], - "targets": { - "build": { - "executor": "@nx/js:tsc", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/libs/native-federation-node", - "tsConfig": "libs/native-federation-node/tsconfig.lib.json", - "packageJson": "libs/native-federation-node/package.json", - "main": "libs/native-federation-node/src/index.ts", - "assets": ["libs/native-federation-node/*.md"] - } - }, - "publish": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-node npm {args.ver} {args.tag}" - } - }, - "publish-local": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-node verdaccio {args.ver}" - } - }, - "nx-release-publish": { - "options": { - "packageRoot": "dist/{projectRoot}" - } - } - } -} diff --git a/libs/native-federation-node/src/index.ts b/libs/native-federation-node/src/index.ts deleted file mode 100644 index 34a84160..00000000 --- a/libs/native-federation-node/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib/node/init-node-federation'; diff --git a/libs/native-federation-node/src/lib/node/init-node-federation.ts b/libs/native-federation-node/src/lib/node/init-node-federation.ts deleted file mode 100644 index 92f00cd9..00000000 --- a/libs/native-federation-node/src/lib/node/init-node-federation.ts +++ /dev/null @@ -1,94 +0,0 @@ -import * as fs from 'node:fs/promises'; -import { register } from 'node:module'; -import * as path from 'node:path'; -import { pathToFileURL } from 'node:url'; - -import { - FederationInfo, - processRemoteInfos, - ImportMap, - mergeImportMaps, - processHostInfo, -} from '@softarc/native-federation-runtime'; -import { IMPORT_MAP_FILE_NAME } from '../utils/import-map-loader'; -import { resolver } from '../utils/loader-as-data-url'; - -export type InitNodeFederationOptions = { - remotesOrManifestUrl: Record | string; - relBundlePath: string; - throwIfRemoteNotFound: boolean; - cacheTag?: string; -}; - -const defaultOptions: InitNodeFederationOptions = { - remotesOrManifestUrl: {}, - relBundlePath: '../browser', - throwIfRemoteNotFound: false, -}; - -export async function initNodeFederation( - options: Partial, -): Promise { - const mergedOptions = { ...defaultOptions, ...options }; - const importMap = await createNodeImportMap(mergedOptions); - - // const normalized = resolveAndComposeImportMap(importMap) as ImportMap; - // await writeImportMap(normalized); - - await writeImportMap(importMap); - await writeResolver(); - - register(pathToFileURL('./federation-resolver.mjs').href); -} - -async function createNodeImportMap( - options: InitNodeFederationOptions, -): Promise { - const { remotesOrManifestUrl, relBundlePath } = options; - - const remotes = - typeof remotesOrManifestUrl === 'object' - ? remotesOrManifestUrl - : await loadFsManifest(remotesOrManifestUrl); - - const hostInfo = await loadFsFederationInfo(relBundlePath); - const hostImportMap = await processHostInfo(hostInfo, './' + relBundlePath); - const remotesImportMap = await processRemoteInfos(remotes, { - throwIfRemoteNotFound: options.throwIfRemoteNotFound, - cacheTag: options.cacheTag, - }); - - const importMap = mergeImportMaps(hostImportMap, remotesImportMap); - - return importMap; -} - -async function loadFsManifest( - manifestUrl: string, -): Promise> { - const content = await fs.readFile(manifestUrl, 'utf-8'); - const manifest = JSON.parse(content) as Record; - return manifest; -} - -async function loadFsFederationInfo( - relBundlePath: string, -): Promise { - const manifestPath = path.join(relBundlePath, 'remoteEntry.json'); - const content = await fs.readFile(manifestPath, 'utf-8'); - const manifest = JSON.parse(content) as FederationInfo; - return manifest; -} - -async function writeImportMap(map: ImportMap): Promise { - await fs.writeFile( - IMPORT_MAP_FILE_NAME, - JSON.stringify(map, null, 2), - 'utf-8', - ); -} - -async function writeResolver() { - const buffer = Buffer.from(resolver, 'base64'); - await fs.writeFile('federation-resolver.mjs', buffer, 'utf-8'); -} diff --git a/libs/native-federation-node/src/lib/utils/fstart-args-parser.ts b/libs/native-federation-node/src/lib/utils/fstart-args-parser.ts deleted file mode 100644 index 85c015d6..00000000 --- a/libs/native-federation-node/src/lib/utils/fstart-args-parser.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as fs from 'node:fs'; - -export type FStartArgs = { - entry: string; - relBundlePath: string; - remotesOrManifestUrl: string; -}; - -const defaultArgs: FStartArgs = { - entry: './server.mjs', - remotesOrManifestUrl: '../browser/federation.manifest.json', - relBundlePath: '../browser/', -}; - -export function parseFStartArgs() { - const args: FStartArgs = { - entry: '', - remotesOrManifestUrl: '', - relBundlePath: '', - }; - - let key = ''; - for (let i = 2; i < process.argv.length; i++) { - const cand = process.argv[i]; - - if (cand.startsWith('--')) { - const candKey = cand.substring(2); - if (defaultArgs[candKey]) { - key = candKey; - } else { - console.error(`switch ${cand} not supported!`); - exitWithUsage(defaultArgs); - } - } else if (key) { - args[key] = cand; - key = ''; - } else { - console.error(`unreladed value ${cand}!`); - exitWithUsage(defaultArgs); - } - } - - applyDefaultArgs(args); - - return args; -} - -function applyDefaultArgs(args: FStartArgs) { - if (args.relBundlePath && !args.remotesOrManifestUrl) { - const cand = defaultArgs.relBundlePath + 'federation.manifest.json'; - if (fs.existsSync(cand)) { - args.remotesOrManifestUrl = cand; - } - } - - args.entry = args.entry || defaultArgs.entry; - args.relBundlePath = args.relBundlePath || defaultArgs.relBundlePath; - args.remotesOrManifestUrl = - args.remotesOrManifestUrl || defaultArgs.remotesOrManifestUrl; - - if (!fs.existsSync(args.remotesOrManifestUrl)) { - args.remotesOrManifestUrl = undefined; - } -} - -function exitWithUsage(defaultArgs: FStartArgs) { - let args = ''; - for (const key in defaultArgs) { - args += `[--${key} ${defaultArgs[key]}] `; - } - - console.log('usage: nfstart ' + args); - process.exit(1); -} diff --git a/libs/native-federation-node/src/lib/utils/fstart.mjs b/libs/native-federation-node/src/lib/utils/fstart.mjs deleted file mode 100644 index 2f264b28..00000000 --- a/libs/native-federation-node/src/lib/utils/fstart.mjs +++ /dev/null @@ -1,439 +0,0 @@ -// libs/native-federation-node/src/lib/node/init-node-federation.ts -import { register } from 'node:module'; -import { pathToFileURL } from 'node:url'; -import * as fs2 from 'node:fs/promises'; -import * as path2 from 'node:path'; - -// libs/native-federation-runtime/src/lib/model/global-cache.ts -var nfNamespace = '__NATIVE_FEDERATION__'; -var global2 = globalThis; -global2[nfNamespace] ??= { - externals: /* @__PURE__ */ new Map(), - remoteNamesToRemote: /* @__PURE__ */ new Map(), - baseUrlToRemoteNames: /* @__PURE__ */ new Map(), -}; -var globalCache = global2[nfNamespace]; - -// libs/native-federation-runtime/src/lib/model/externals.ts -var externals = globalCache.externals; -function getExternalKey(shared) { - return `${shared.packageName}@${shared.version}`; -} -function getExternalUrl(shared) { - const packageKey = getExternalKey(shared); - return externals.get(packageKey); -} -function setExternalUrl(shared, url2) { - const packageKey = getExternalKey(shared); - externals.set(packageKey, url2); -} - -// libs/native-federation-runtime/src/lib/model/import-map.ts -function mergeImportMaps(map1, map2) { - return { - imports: { ...map1.imports, ...map2.imports }, - scopes: { ...map1.scopes, ...map2.scopes }, - }; -} - -// libs/native-federation-runtime/src/lib/model/remotes.ts -var remoteNamesToRemote = globalCache.remoteNamesToRemote; -var baseUrlToRemoteNames = globalCache.baseUrlToRemoteNames; -function addRemote(remoteName, remote) { - remoteNamesToRemote.set(remoteName, remote); - baseUrlToRemoteNames.set(remote.baseUrl, remoteName); -} - -// libs/native-federation-runtime/src/lib/utils/path-utils.ts -function getDirectory(url2) { - const parts = url2.split('/'); - parts.pop(); - return parts.join('/'); -} -function joinPaths(path1, path22) { - while (path1.endsWith('/')) { - path1 = path1.substring(0, path1.length - 1); - } - if (path22.startsWith('./')) { - path22 = path22.substring(2, path22.length); - } - return `${path1}/${path22}`; -} - -// libs/native-federation-runtime/src/lib/watch-federation-build.ts -function watchFederationBuildCompletion(endpoint) { - const eventSource = new EventSource(endpoint); - eventSource.onmessage = function (event) { - const data = JSON.parse(event.data); - if (data.type === 'federation-rebuild-complete' /* COMPLETED */) { - console.log('[Federation] Rebuild completed, reloading...'); - window.location.reload(); - } - }; - eventSource.onerror = function (event) { - console.warn('[Federation] SSE connection error:', event); - }; -} - -// libs/native-federation-runtime/src/lib/init-federation.ts -async function processRemoteInfos( - remotes, - options = { throwIfRemoteNotFound: false }, -) { - const processRemoteInfoPromises = Object.keys(remotes).map( - async (remoteName) => { - try { - let url2 = remotes[remoteName]; - if (options.cacheTag) { - const addAppend = remotes[remoteName].includes('?') ? '&' : '?'; - url2 += `${addAppend}t=${options.cacheTag}`; - } - return await processRemoteInfo(url2, remoteName); - } catch (e) { - const error = `Error loading remote entry for ${remoteName} from file ${remotes[remoteName]}`; - if (options.throwIfRemoteNotFound) { - throw new Error(error); - } - console.error(error); - return null; - } - }, - ); - const remoteImportMaps = await Promise.all(processRemoteInfoPromises); - const importMap = remoteImportMaps.reduce( - (acc, remoteImportMap) => - remoteImportMap ? mergeImportMaps(acc, remoteImportMap) : acc, - { imports: {}, scopes: {} }, - ); - return importMap; -} -async function processRemoteInfo(federationInfoUrl, remoteName) { - const baseUrl = getDirectory(federationInfoUrl); - const remoteInfo = await loadFederationInfo(federationInfoUrl); - if (!remoteName) { - remoteName = remoteInfo.name; - } - if (remoteInfo.buildNotificationsEndpoint) { - watchFederationBuildCompletion( - baseUrl + remoteInfo.buildNotificationsEndpoint, - ); - } - const importMap = createRemoteImportMap(remoteInfo, remoteName, baseUrl); - addRemote(remoteName, { ...remoteInfo, baseUrl }); - return importMap; -} -function createRemoteImportMap(remoteInfo, remoteName, baseUrl) { - const imports = processExposed(remoteInfo, remoteName, baseUrl); - const scopes = processRemoteImports(remoteInfo, baseUrl); - return { imports, scopes }; -} -async function loadFederationInfo(url2) { - const info = await fetch(url2).then((r) => r.json()); - return info; -} -function processRemoteImports(remoteInfo, baseUrl) { - const scopes = {}; - const scopedImports = {}; - for (const shared of remoteInfo.shared) { - const outFileName = - getExternalUrl(shared) ?? joinPaths(baseUrl, shared.outFileName); - setExternalUrl(shared, outFileName); - scopedImports[shared.packageName] = outFileName; - } - scopes[baseUrl + '/'] = scopedImports; - return scopes; -} -function processExposed(remoteInfo, remoteName, baseUrl) { - const imports = {}; - for (const exposed of remoteInfo.exposes) { - const key = joinPaths(remoteName, exposed.key); - const value = joinPaths(baseUrl, exposed.outFileName); - imports[key] = value; - } - return imports; -} -async function processHostInfo(hostInfo, relBundlesPath = './') { - const imports = hostInfo.shared.reduce( - (acc, cur) => ({ - ...acc, - [cur.packageName]: relBundlesPath + cur.outFileName, - }), - {}, - ); - for (const shared of hostInfo.shared) { - setExternalUrl(shared, relBundlesPath + shared.outFileName); - } - return { imports, scopes: {} }; -} - -// libs/native-federation-node/src/lib/utils/import-map-loader.js -import path from 'path'; -import url from 'url'; -import { promises as fs } from 'fs'; -var IMPORT_MAP_FILE_NAME = 'node.importmap'; -var baseURL = url.pathToFileURL(process.cwd()) + path.sep; -function resolveAndComposeImportMap(parsed) { - if (!isPlainObject(parsed)) { - throw Error(`Invalid import map - top level must be an object`); - } - let sortedAndNormalizedImports = {}; - if (Object.prototype.hasOwnProperty.call(parsed, 'imports')) { - if (!isPlainObject(parsed.imports)) { - throw Error(`Invalid import map - "imports" property must be an object`); - } - sortedAndNormalizedImports = sortAndNormalizeSpecifierMap( - parsed.imports, - baseURL, - ); - } - let sortedAndNormalizedScopes = {}; - if (Object.prototype.hasOwnProperty.call(parsed, 'scopes')) { - if (!isPlainObject(parsed.scopes)) { - throw Error(`Invalid import map - "scopes" property must be an object`); - } - sortedAndNormalizedScopes = sortAndNormalizeScopes(parsed.scopes, baseURL); - } - const invalidKeys = Object.keys(parsed).filter( - (key) => key !== 'imports' && key !== 'scopes', - ); - if (invalidKeys.length > 0) { - console.warn( - `Invalid top-level key${invalidKeys.length > 0 ? 's' : ''} in import map - ${invalidKeys.join(', ')}`, - ); - } - return { - imports: sortedAndNormalizedImports, - scopes: sortedAndNormalizedScopes, - }; -} -function sortAndNormalizeSpecifierMap(map, baseURL2) { - const normalized = {}; - for (let specifierKey in map) { - const value = map[specifierKey]; - const normalizedSpecifierKey = normalizeSpecifierKey( - specifierKey, - baseURL2, - ); - if (normalizedSpecifierKey === null) { - continue; - } - let addressURL = parseURLLikeSpecifier(value, baseURL2); - if (addressURL === null) { - console.warn( - `Invalid URL address for import map specifier '${specifierKey}'`, - ); - normalized[normalizedSpecifierKey] = null; - continue; - } - if (specifierKey.endsWith('/') && !addressURL.endsWith('/')) { - console.warn( - `Invalid URL address for import map specifier '${specifierKey}' - since the specifier ends in slash, so must the address`, - ); - normalized[normalizedSpecifierKey] = null; - continue; - } - normalized[normalizedSpecifierKey] = addressURL; - } - return normalized; -} -function normalizeSpecifierKey(key) { - if (key === '') { - console.warn(`Specifier keys in import maps may not be the empty string`); - return null; - } - return parseURLLikeSpecifier(key, baseURL) || key; -} -function parseURLLikeSpecifier(specifier, baseURL2) { - const useBaseUrlAsParent = - specifier.startsWith('/') || - specifier.startsWith('./') || - specifier.startsWith('../'); - try { - return new URL(specifier, useBaseUrlAsParent ? baseURL2 : void 0).href; - } catch { - return null; - } -} -function sortAndNormalizeScopes(map, baseURL2) { - let normalized = {}; - for (let scopePrefix in map) { - const potentialSpecifierMap = map[scopePrefix]; - if (!isPlainObject(potentialSpecifierMap)) { - throw TypeError( - `The value of scope ${scopePrefix} must be a JSON object`, - ); - } - let scopePrefixURL; - try { - scopePrefixURL = new URL(scopePrefix, baseURL2).href; - } catch { - console.warn( - `Scope prefix URL '${scopePrefix}' was not parseable in import map`, - ); - continue; - } - normalized[scopePrefixURL] = sortAndNormalizeSpecifierMap( - potentialSpecifierMap, - baseURL2, - ); - } - return normalized; -} -function isPlainObject(obj) { - return obj === Object(obj) && !Array.isArray(obj); -} -var importMapPromise = getImportMapPromise(); -async function getImportMapPromise() { - const relativePath = process.env.IMPORT_MAP_PATH || IMPORT_MAP_FILE_NAME; - const importMapPath = path.resolve(process.cwd(), relativePath); - let str; - try { - str = await fs.readFile(importMapPath); - } catch (err) { - return emptyMap(); - } - let json; - try { - json = await JSON.parse(str); - } catch (err) { - throw Error( - `Import map at ${importMapPath} contains invalid json: ${err.message}`, - ); - } - return resolveAndComposeImportMap(json); -} -global.nodeLoader = global.nodeLoader || {}; -global.nodeLoader.setImportMapPromise = function setImportMapPromise(promise) { - importMapPromise = promise.then((map) => { - return resolveAndComposeImportMap(map); - }); -}; -function emptyMap() { - return { imports: {}, scopes: {} }; -} - -// libs/native-federation-node/src/lib/utils/loader-as-data-url.js -var resolver = - 'aW1wb3J0IHBhdGggZnJvbSAncGF0aCc7CmltcG9ydCB1cmwgZnJvbSAndXJsJzsKaW1wb3J0IHsgcHJvbWlzZXMgYXMgZnMgfSBmcm9tICdmcyc7CgpleHBvcnQgY29uc3QgSU1QT1JUX01BUF9GSUxFX05BTUUgPSAnbm9kZS5pbXBvcnRtYXAnOwoKY29uc3QgYmFzZVVSTCA9IHVybC5wYXRoVG9GaWxlVVJMKHByb2Nlc3MuY3dkKCkpICsgcGF0aC5zZXA7CgovLyBodHRwczovL3dpY2cuZ2l0aHViLmlvL2ltcG9ydC1tYXBzLyNuZXctcmVzb2x2ZS1hbGdvcml0aG0KZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVTcGVjaWZpZXIoaW1wb3J0TWFwLCBzcGVjaWZpZXIsIHBhcmVudFVSTCkgewogIGxldCBjdXJyZW50QmFzZVVSTDsKICBpZiAocGFyZW50VVJMKSB7CiAgICBjb25zdCBsYXN0U2xhc2hJbmRleCA9IHBhcmVudFVSTC5sYXN0SW5kZXhPZihwYXRoLnNlcCk7CiAgICBjdXJyZW50QmFzZVVSTCA9IHBhcmVudFVSTC5zbGljZSgwLCBsYXN0U2xhc2hJbmRleCArIDEpOwogIH0gZWxzZSB7CiAgICBjdXJyZW50QmFzZVVSTCA9IGJhc2VVUkw7CiAgfQogIGNvbnN0IG5vcm1hbGl6ZWRTcGVjaWZpZXIgPQogICAgcGFyc2VVUkxMaWtlU3BlY2lmaWVyKHNwZWNpZmllciwgY3VycmVudEJhc2VVUkwpIHx8IHNwZWNpZmllcjsKICBmb3IgKGxldCBzY29wZVByZWZpeCBpbiBpbXBvcnRNYXAuc2NvcGVzKSB7CiAgICBpZiAoCiAgICAgIHNjb3BlUHJlZml4ID09PSBjdXJyZW50QmFzZVVSTCB8fAogICAgICAoc2NvcGVQcmVmaXguZW5kc1dpdGgoJy8nKSAmJiBjdXJyZW50QmFzZVVSTC5zdGFydHNXaXRoKHNjb3BlUHJlZml4KSkKICAgICkgewogICAgICBjb25zdCBzY29wZUltcG9ydHNNYXRjaCA9IHJlc29sdmVJbXBvcnRzTWF0Y2goCiAgICAgICAgbm9ybWFsaXplZFNwZWNpZmllciwKICAgICAgICBpbXBvcnRNYXAuc2NvcGVzW3Njb3BlUHJlZml4XSwKICAgICAgKTsKICAgICAgaWYgKHNjb3BlSW1wb3J0c01hdGNoKSB7CiAgICAgICAgcmV0dXJuIHNjb3BlSW1wb3J0c01hdGNoOwogICAgICB9CiAgICB9IGVsc2UgewogICAgICBjb25zdCB0b3BMZXZlbEltcG9ydHNNYXRjaCA9IHJlc29sdmVJbXBvcnRzTWF0Y2goCiAgICAgICAgbm9ybWFsaXplZFNwZWNpZmllciwKICAgICAgICBpbXBvcnRNYXAuaW1wb3J0cywKICAgICAgKTsKICAgICAgaWYgKHRvcExldmVsSW1wb3J0c01hdGNoKSB7CiAgICAgICAgcmV0dXJuIHRvcExldmVsSW1wb3J0c01hdGNoOwogICAgICB9CiAgICB9CiAgfQoKICByZXR1cm4gcmVzb2x2ZUltcG9ydHNNYXRjaChub3JtYWxpemVkU3BlY2lmaWVyLCBpbXBvcnRNYXAuaW1wb3J0cyk7Cn0KCi8vIGh0dHBzOi8vd2ljZy5naXRodWIuaW8vaW1wb3J0LW1hcHMvI3Jlc29sdmUtYW4taW1wb3J0cy1tYXRjaApmdW5jdGlvbiByZXNvbHZlSW1wb3J0c01hdGNoKG5vcm1hbGl6ZWRTcGVjaWZpZXIsIHNwZWNpZmllck1hcCkgewogIGZvciAobGV0IHNwZWNpZmllcktleSBpbiBzcGVjaWZpZXJNYXApIHsKICAgIGNvbnN0IHJlc29sdXRpb25SZXN1bHQgPSBzcGVjaWZpZXJNYXBbc3BlY2lmaWVyS2V5XTsKCiAgICBpZiAoc3BlY2lmaWVyS2V5ID09PSBub3JtYWxpemVkU3BlY2lmaWVyKSB7CiAgICAgIGlmIChyZXNvbHV0aW9uUmVzdWx0ID09PSBudWxsKSB7CiAgICAgICAgdGhyb3cgVHlwZUVycm9yKAogICAgICAgICAgYFRoZSBpbXBvcnQgbWFwIHJlc29sdXRpb24gb2YgJHtzcGVjaWZpZXJLZXl9IGZhaWxlZCBkdWUgdG8gYSBudWxsIGVudHJ5YCwKICAgICAgICApOwogICAgICB9CiAgICAgIHJldHVybiByZXNvbHV0aW9uUmVzdWx0OwogICAgfSBlbHNlIGlmICgKICAgICAgc3BlY2lmaWVyS2V5LmVuZHNXaXRoKCcvJykgJiYKICAgICAgbm9ybWFsaXplZFNwZWNpZmllci5zdGFydHNXaXRoKHNwZWNpZmllcktleSkKICAgICkgewogICAgICBpZiAocmVzb2x1dGlvblJlc3VsdCA9PT0gbnVsbCkgewogICAgICAgIHRocm93IFR5cGVFcnJvcigKICAgICAgICAgIGBUaGUgaW1wb3J0IG1hcCByZXNvbHV0aW9uIG9mICR7c3BlY2lmaWVyS2V5fSBmYWlsZWQgZHVlIHRvIGEgbnVsbCBlbnRyeWAsCiAgICAgICAgKTsKICAgICAgfQogICAgICBjb25zdCBhZnRlclByZWZpeCA9IG5vcm1hbGl6ZWRTcGVjaWZpZXIuc2xpY2Uoc3BlY2lmaWVyS2V5Lmxlbmd0aCk7CiAgICAgIHRyeSB7CiAgICAgICAgcmV0dXJuIG5ldyBVUkwoYWZ0ZXJQcmVmaXgsIHJlc29sdXRpb25SZXN1bHQpLmhyZWY7CiAgICAgIH0gY2F0Y2ggewogICAgICAgIHRocm93IFR5cGVFcnJvcigKICAgICAgICAgIGBUaGUgaW1wb3J0IG1hcCByZXNvbHV0aW9uIG9mICR7c3BlY2lmaWVyS2V5fSBmYWlsZWQgZHVlIHRvIFVSTCBwYXJzZSBmYWlsdXJlYCwKICAgICAgICApOwogICAgICB9CiAgICB9CiAgfQoKICByZXR1cm4gbnVsbDsKfQoKLy8gaHR0cHM6Ly93aWNnLmdpdGh1Yi5pby9pbXBvcnQtbWFwcy8jcGFyc2luZwpleHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUFuZENvbXBvc2VJbXBvcnRNYXAocGFyc2VkKSB7CiAgLy8gU3RlcCAyCiAgaWYgKCFpc1BsYWluT2JqZWN0KHBhcnNlZCkpIHsKICAgIHRocm93IEVycm9yKGBJbnZhbGlkIGltcG9ydCBtYXAgLSB0b3AgbGV2ZWwgbXVzdCBiZSBhbiBvYmplY3RgKTsKICB9CgogIC8vIFN0ZXAgMwogIGxldCBzb3J0ZWRBbmROb3JtYWxpemVkSW1wb3J0cyA9IHt9OwoKICAvLyBTdGVwIDQKICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHBhcnNlZCwgJ2ltcG9ydHMnKSkgewogICAgLy8gU3RlcCA0LjEKICAgIGlmICghaXNQbGFpbk9iamVjdChwYXJzZWQuaW1wb3J0cykpIHsKICAgICAgdGhyb3cgRXJyb3IoYEludmFsaWQgaW1wb3J0IG1hcCAtICJpbXBvcnRzIiBwcm9wZXJ0eSBtdXN0IGJlIGFuIG9iamVjdGApOwogICAgfQoKICAgIC8vIFN0ZXAgNC4yCiAgICBzb3J0ZWRBbmROb3JtYWxpemVkSW1wb3J0cyA9IHNvcnRBbmROb3JtYWxpemVTcGVjaWZpZXJNYXAoCiAgICAgIHBhcnNlZC5pbXBvcnRzLAogICAgICBiYXNlVVJMLAogICAgKTsKICB9CgogIC8vIFN0ZXAgNQogIGxldCBzb3J0ZWRBbmROb3JtYWxpemVkU2NvcGVzID0ge307CgogIC8vIFN0ZXAgNgogIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocGFyc2VkLCAnc2NvcGVzJykpIHsKICAgIC8vIFN0ZXAgNi4xCiAgICBpZiAoIWlzUGxhaW5PYmplY3QocGFyc2VkLnNjb3BlcykpIHsKICAgICAgdGhyb3cgRXJyb3IoYEludmFsaWQgaW1wb3J0IG1hcCAtICJzY29wZXMiIHByb3BlcnR5IG11c3QgYmUgYW4gb2JqZWN0YCk7CiAgICB9CgogICAgLy8gU3RlcCA2LjIKICAgIHNvcnRlZEFuZE5vcm1hbGl6ZWRTY29wZXMgPSBzb3J0QW5kTm9ybWFsaXplU2NvcGVzKHBhcnNlZC5zY29wZXMsIGJhc2VVUkwpOwogIH0KCiAgLy8gU3RlcCA3CiAgY29uc3QgaW52YWxpZEtleXMgPSBPYmplY3Qua2V5cyhwYXJzZWQpLmZpbHRlcigKICAgIChrZXkpID0+IGtleSAhPT0gJ2ltcG9ydHMnICYmIGtleSAhPT0gJ3Njb3BlcycsCiAgKTsKICBpZiAoaW52YWxpZEtleXMubGVuZ3RoID4gMCkgewogICAgY29uc29sZS53YXJuKAogICAgICBgSW52YWxpZCB0b3AtbGV2ZWwga2V5JHsKICAgICAgICBpbnZhbGlkS2V5cy5sZW5ndGggPiAwID8gJ3MnIDogJycKICAgICAgfSBpbiBpbXBvcnQgbWFwIC0gJHtpbnZhbGlkS2V5cy5qb2luKCcsICcpfWAsCiAgICApOwogIH0KCiAgLy8gU3RlcCA4CiAgcmV0dXJuIHsKICAgIGltcG9ydHM6IHNvcnRlZEFuZE5vcm1hbGl6ZWRJbXBvcnRzLAogICAgc2NvcGVzOiBzb3J0ZWRBbmROb3JtYWxpemVkU2NvcGVzLAogIH07Cn0KCi8vIGh0dHBzOi8vd2ljZy5naXRodWIuaW8vaW1wb3J0LW1hcHMvI3NvcnQtYW5kLW5vcm1hbGl6ZS1hLXNwZWNpZmllci1tYXAKZnVuY3Rpb24gc29ydEFuZE5vcm1hbGl6ZVNwZWNpZmllck1hcChtYXAsIGJhc2VVUkwpIHsKICBjb25zdCBub3JtYWxpemVkID0ge307CgogIGZvciAobGV0IHNwZWNpZmllcktleSBpbiBtYXApIHsKICAgIGNvbnN0IHZhbHVlID0gbWFwW3NwZWNpZmllcktleV07CgogICAgY29uc3Qgbm9ybWFsaXplZFNwZWNpZmllcktleSA9IG5vcm1hbGl6ZVNwZWNpZmllcktleShzcGVjaWZpZXJLZXksIGJhc2VVUkwpOwogICAgaWYgKG5vcm1hbGl6ZWRTcGVjaWZpZXJLZXkgPT09IG51bGwpIHsKICAgICAgY29udGludWU7CiAgICB9CgogICAgbGV0IGFkZHJlc3NVUkwgPSBwYXJzZVVSTExpa2VTcGVjaWZpZXIodmFsdWUsIGJhc2VVUkwpOwogICAgaWYgKGFkZHJlc3NVUkwgPT09IG51bGwpIHsKICAgICAgY29uc29sZS53YXJuKAogICAgICAgIGBJbnZhbGlkIFVSTCBhZGRyZXNzIGZvciBpbXBvcnQgbWFwIHNwZWNpZmllciAnJHtzcGVjaWZpZXJLZXl9J2AsCiAgICAgICk7CiAgICAgIG5vcm1hbGl6ZWRbbm9ybWFsaXplZFNwZWNpZmllcktleV0gPSBudWxsOwogICAgICBjb250aW51ZTsKICAgIH0KCiAgICBpZiAoc3BlY2lmaWVyS2V5LmVuZHNXaXRoKCcvJykgJiYgIWFkZHJlc3NVUkwuZW5kc1dpdGgoJy8nKSkgewogICAgICBjb25zb2xlLndhcm4oCiAgICAgICAgYEludmFsaWQgVVJMIGFkZHJlc3MgZm9yIGltcG9ydCBtYXAgc3BlY2lmaWVyICcke3NwZWNpZmllcktleX0nIC0gc2luY2UgdGhlIHNwZWNpZmllciBlbmRzIGluIHNsYXNoLCBzbyBtdXN0IHRoZSBhZGRyZXNzYCwKICAgICAgKTsKICAgICAgbm9ybWFsaXplZFtub3JtYWxpemVkU3BlY2lmaWVyS2V5XSA9IG51bGw7CiAgICAgIGNvbnRpbnVlOwogICAgfQoKICAgIG5vcm1hbGl6ZWRbbm9ybWFsaXplZFNwZWNpZmllcktleV0gPSBhZGRyZXNzVVJMOwogIH0KCiAgcmV0dXJuIG5vcm1hbGl6ZWQ7Cn0KCi8vIGh0dHBzOi8vd2ljZy5naXRodWIuaW8vaW1wb3J0LW1hcHMvI25vcm1hbGl6ZS1hLXNwZWNpZmllci1rZXkKZnVuY3Rpb24gbm9ybWFsaXplU3BlY2lmaWVyS2V5KGtleSkgewogIGlmIChrZXkgPT09ICcnKSB7CiAgICBjb25zb2xlLndhcm4oYFNwZWNpZmllciBrZXlzIGluIGltcG9ydCBtYXBzIG1heSBub3QgYmUgdGhlIGVtcHR5IHN0cmluZ2ApOwogICAgcmV0dXJuIG51bGw7CiAgfQoKICByZXR1cm4gcGFyc2VVUkxMaWtlU3BlY2lmaWVyKGtleSwgYmFzZVVSTCkgfHwga2V5Owp9CgovLyBodHRwczovL3dpY2cuZ2l0aHViLmlvL2ltcG9ydC1tYXBzLyNwYXJzZS1hLXVybC1saWtlLWltcG9ydC1zcGVjaWZpZXIKZnVuY3Rpb24gcGFyc2VVUkxMaWtlU3BlY2lmaWVyKHNwZWNpZmllciwgYmFzZVVSTCkgewogIGNvbnN0IHVzZUJhc2VVcmxBc1BhcmVudCA9CiAgICBzcGVjaWZpZXIuc3RhcnRzV2l0aCgnLycpIHx8CiAgICBzcGVjaWZpZXIuc3RhcnRzV2l0aCgnLi8nKSB8fAogICAgc3BlY2lmaWVyLnN0YXJ0c1dpdGgoJy4uLycpOwoKICB0cnkgewogICAgcmV0dXJuIG5ldyBVUkwoc3BlY2lmaWVyLCB1c2VCYXNlVXJsQXNQYXJlbnQgPyBiYXNlVVJMIDogdW5kZWZpbmVkKS5ocmVmOwogIH0gY2F0Y2ggewogICAgcmV0dXJuIG51bGw7CiAgfQp9CgovLyBodHRwczovL3dpY2cuZ2l0aHViLmlvL2ltcG9ydC1tYXBzLyNzb3J0LWFuZC1ub3JtYWxpemUtc2NvcGVzCmZ1bmN0aW9uIHNvcnRBbmROb3JtYWxpemVTY29wZXMobWFwLCBiYXNlVVJMKSB7CiAgbGV0IG5vcm1hbGl6ZWQgPSB7fTsKCiAgZm9yIChsZXQgc2NvcGVQcmVmaXggaW4gbWFwKSB7CiAgICBjb25zdCBwb3RlbnRpYWxTcGVjaWZpZXJNYXAgPSBtYXBbc2NvcGVQcmVmaXhdOwogICAgaWYgKCFpc1BsYWluT2JqZWN0KHBvdGVudGlhbFNwZWNpZmllck1hcCkpIHsKICAgICAgdGhyb3cgVHlwZUVycm9yKAogICAgICAgIGBUaGUgdmFsdWUgb2Ygc2NvcGUgJHtzY29wZVByZWZpeH0gbXVzdCBiZSBhIEpTT04gb2JqZWN0YCwKICAgICAgKTsKICAgIH0KCiAgICBsZXQgc2NvcGVQcmVmaXhVUkw7CiAgICB0cnkgewogICAgICBzY29wZVByZWZpeFVSTCA9IG5ldyBVUkwoc2NvcGVQcmVmaXgsIGJhc2VVUkwpLmhyZWY7CiAgICB9IGNhdGNoIHsKICAgICAgY29uc29sZS53YXJuKAogICAgICAgIGBTY29wZSBwcmVmaXggVVJMICcke3Njb3BlUHJlZml4fScgd2FzIG5vdCBwYXJzZWFibGUgaW4gaW1wb3J0IG1hcGAsCiAgICAgICk7CiAgICAgIGNvbnRpbnVlOwogICAgfQoKICAgIG5vcm1hbGl6ZWRbc2NvcGVQcmVmaXhVUkxdID0gc29ydEFuZE5vcm1hbGl6ZVNwZWNpZmllck1hcCgKICAgICAgcG90ZW50aWFsU3BlY2lmaWVyTWFwLAogICAgICBiYXNlVVJMLAogICAgKTsKICB9CgogIHJldHVybiBub3JtYWxpemVkOwp9CgpmdW5jdGlvbiBpc1BsYWluT2JqZWN0KG9iaikgewogIHJldHVybiBvYmogPT09IE9iamVjdChvYmopICYmICFBcnJheS5pc0FycmF5KG9iaik7Cn0KCi8vIC0tLQoKbGV0IGltcG9ydE1hcFByb21pc2UgPSBnZXRJbXBvcnRNYXBQcm9taXNlKCk7CgpleHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZShzcGVjaWZpZXIsIGNvbnRleHQsIGRlZmF1bHRSZXNvbHZlKSB7CiAgY29uc3QgeyBwYXJlbnRVUkwgPSBudWxsIH0gPSBjb250ZXh0OwogIGNvbnN0IGltcG9ydE1hcCA9IGF3YWl0IGltcG9ydE1hcFByb21pc2U7CiAgY29uc3QgaW1wb3J0TWFwVXJsID0gcmVzb2x2ZVNwZWNpZmllcihpbXBvcnRNYXAsIHNwZWNpZmllciwgcGFyZW50VVJMKTsKCiAgcmV0dXJuIGRlZmF1bHRSZXNvbHZlKGltcG9ydE1hcFVybCA/PyBzcGVjaWZpZXIsIGNvbnRleHQsIGRlZmF1bHRSZXNvbHZlKTsKfQoKZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWQodXJsLCBjb250ZXh0LCBkZWZhdWx0TG9hZCkgewogIGlmICh1cmwuc3RhcnRzV2l0aCgnaHR0cDovLycpIHx8IHVybC5zdGFydHNXaXRoKCdodHRwczovLycpKSB7CiAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCh1cmwpOwogICAgaWYgKCFyZXMub2spIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZmV0Y2ggbW9kdWxlIGZyb20gJHt1cmx9YCk7CiAgICB9CiAgICBjb25zdCBzb3VyY2UgPSBhd2FpdCByZXMudGV4dCgpOwogICAgcmV0dXJuIHsKICAgICAgc2hvcnRDaXJjdWl0OiB0cnVlLAogICAgICBmb3JtYXQ6ICdtb2R1bGUnLAogICAgICBzb3VyY2UsCiAgICB9OwogIH0KCiAgaWYgKCF1cmwuc3RhcnRzV2l0aCgnbm9kZTonKSkgewogICAgY29udGV4dC5mb3JtYXQgPSAnbW9kdWxlJzsKICB9CgogIHJldHVybiBkZWZhdWx0TG9hZCh1cmwsIGNvbnRleHQsIGRlZmF1bHRMb2FkKTsKfQoKYXN5bmMgZnVuY3Rpb24gZ2V0SW1wb3J0TWFwUHJvbWlzZSgpIHsKICBjb25zdCByZWxhdGl2ZVBhdGggPSBwcm9jZXNzLmVudi5JTVBPUlRfTUFQX1BBVEggfHwgSU1QT1JUX01BUF9GSUxFX05BTUU7CiAgY29uc3QgaW1wb3J0TWFwUGF0aCA9IHBhdGgucmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCByZWxhdGl2ZVBhdGgpOwoKICBsZXQgc3RyOwogIHRyeSB7CiAgICBzdHIgPSBhd2FpdCBmcy5yZWFkRmlsZShpbXBvcnRNYXBQYXRoKTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHJldHVybiBlbXB0eU1hcCgpOwogIH0KCiAgbGV0IGpzb247CiAgdHJ5IHsKICAgIGpzb24gPSBhd2FpdCBKU09OLnBhcnNlKHN0cik7CiAgfSBjYXRjaCAoZXJyKSB7CiAgICB0aHJvdyBFcnJvcigKICAgICAgYEltcG9ydCBtYXAgYXQgJHtpbXBvcnRNYXBQYXRofSBjb250YWlucyBpbnZhbGlkIGpzb246ICR7ZXJyLm1lc3NhZ2V9YCwKICAgICk7CiAgfQoKICByZXR1cm4gcmVzb2x2ZUFuZENvbXBvc2VJbXBvcnRNYXAoanNvbik7Cn0KCmdsb2JhbC5ub2RlTG9hZGVyID0gZ2xvYmFsLm5vZGVMb2FkZXIgfHwge307CgpnbG9iYWwubm9kZUxvYWRlci5zZXRJbXBvcnRNYXBQcm9taXNlID0gZnVuY3Rpb24gc2V0SW1wb3J0TWFwUHJvbWlzZShwcm9taXNlKSB7CiAgaW1wb3J0TWFwUHJvbWlzZSA9IHByb21pc2UudGhlbigobWFwKSA9PiB7CiAgICByZXR1cm4gcmVzb2x2ZUFuZENvbXBvc2VJbXBvcnRNYXAobWFwKTsKICB9KTsKfTsKCmZ1bmN0aW9uIGVtcHR5TWFwKCkgewogIHJldHVybiB7IGltcG9ydHM6IHt9LCBzY29wZXM6IHt9IH07Cn0K'; - -// libs/native-federation-node/src/lib/node/init-node-federation.ts -var defaultOptions = { - remotesOrManifestUrl: {}, - relBundlePath: '../browser', - throwIfRemoteNotFound: false, -}; -async function initNodeFederation(options) { - const mergedOptions = { ...defaultOptions, ...options }; - const importMap = await createNodeImportMap(mergedOptions); - await writeImportMap(importMap); - await writeResolver(); - register(pathToFileURL('./federation-resolver.mjs').href); -} -async function createNodeImportMap(options) { - const { remotesOrManifestUrl, relBundlePath } = options; - const remotes = - typeof remotesOrManifestUrl === 'object' - ? remotesOrManifestUrl - : await loadFsManifest(remotesOrManifestUrl); - const hostInfo = await loadFsFederationInfo(relBundlePath); - const hostImportMap = await processHostInfo(hostInfo, './' + relBundlePath); - const remotesImportMap = await processRemoteInfos(remotes, { - throwIfRemoteNotFound: options.throwIfRemoteNotFound, - cacheTag: options.cacheTag, - }); - const importMap = mergeImportMaps(hostImportMap, remotesImportMap); - return importMap; -} -async function loadFsManifest(manifestUrl) { - const content = await fs2.readFile(manifestUrl, 'utf-8'); - const manifest = JSON.parse(content); - return manifest; -} -async function loadFsFederationInfo(relBundlePath) { - const manifestPath = path2.join(relBundlePath, 'remoteEntry.json'); - const content = await fs2.readFile(manifestPath, 'utf-8'); - const manifest = JSON.parse(content); - return manifest; -} -async function writeImportMap(map) { - await fs2.writeFile( - IMPORT_MAP_FILE_NAME, - JSON.stringify(map, null, 2), - 'utf-8', - ); -} -async function writeResolver() { - const buffer = Buffer.from(resolver, 'base64'); - await fs2.writeFile('federation-resolver.mjs', buffer, 'utf-8'); -} - -// libs/native-federation-node/src/lib/utils/fstart-args-parser.ts -import * as fs3 from 'node:fs'; -var defaultArgs = { - entry: './server.mjs', - remotesOrManifestUrl: '../browser/federation.manifest.json', - relBundlePath: '../browser/', -}; -function parseFStartArgs() { - const args2 = { - entry: '', - remotesOrManifestUrl: '', - relBundlePath: '', - }; - let key = ''; - for (let i = 2; i < process.argv.length; i++) { - const cand = process.argv[i]; - if (cand.startsWith('--')) { - const candKey = cand.substring(2); - if (defaultArgs[candKey]) { - key = candKey; - } else { - console.error(`switch ${cand} not supported!`); - exitWithUsage(defaultArgs); - } - } else if (key) { - args2[key] = cand; - key = ''; - } else { - console.error(`unreladed value ${cand}!`); - exitWithUsage(defaultArgs); - } - } - applyDefaultArgs(args2); - return args2; -} -function applyDefaultArgs(args2) { - if (args2.relBundlePath && !args2.remotesOrManifestUrl) { - const cand = defaultArgs.relBundlePath + 'federation.manifest.json'; - if (fs3.existsSync(cand)) { - args2.remotesOrManifestUrl = cand; - } - } - args2.entry = args2.entry || defaultArgs.entry; - args2.relBundlePath = args2.relBundlePath || defaultArgs.relBundlePath; - args2.remotesOrManifestUrl = - args2.remotesOrManifestUrl || defaultArgs.remotesOrManifestUrl; - if (!fs3.existsSync(args2.remotesOrManifestUrl)) { - args2.remotesOrManifestUrl = void 0; - } -} -function exitWithUsage(defaultArgs2) { - let args2 = ''; - for (const key in defaultArgs2) { - args2 += `[--${key} ${defaultArgs2[key]}] `; - } - console.log('usage: nfstart ' + args2); - process.exit(1); -} - -// libs/native-federation-node/src/lib/utils/fstart.ts -var args = parseFStartArgs(); -(async () => { - await initNodeFederation({ - ...(args.remotesOrManifestUrl - ? { remotesOrManifestUrl: args.remotesOrManifestUrl } - : {}), - relBundlePath: args.relBundlePath, - }); - await import(args.entry); -})(); diff --git a/libs/native-federation-node/src/lib/utils/fstart.ts b/libs/native-federation-node/src/lib/utils/fstart.ts deleted file mode 100644 index 9b5cacc0..00000000 --- a/libs/native-federation-node/src/lib/utils/fstart.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { initNodeFederation } from '../node/init-node-federation'; -import { parseFStartArgs } from './fstart-args-parser'; - -const args = parseFStartArgs(); - -(async () => { - await initNodeFederation({ - ...(args.remotesOrManifestUrl - ? { remotesOrManifestUrl: args.remotesOrManifestUrl } - : {}), - relBundlePath: args.relBundlePath, - }); - - await import(args.entry); -})(); diff --git a/libs/native-federation-node/src/lib/utils/import-map-loader.js b/libs/native-federation-node/src/lib/utils/import-map-loader.js deleted file mode 100644 index d92e7821..00000000 --- a/libs/native-federation-node/src/lib/utils/import-map-loader.js +++ /dev/null @@ -1,298 +0,0 @@ -import path from 'path'; -import url from 'url'; -import { promises as fs } from 'fs'; - -export const IMPORT_MAP_FILE_NAME = 'node.importmap'; - -const baseURL = url.pathToFileURL(process.cwd()) + path.sep; - -// https://wicg.github.io/import-maps/#new-resolve-algorithm -export function resolveSpecifier(importMap, specifier, parentURL) { - let currentBaseURL; - if (parentURL) { - const lastSlashIndex = parentURL.lastIndexOf(path.sep); - currentBaseURL = parentURL.slice(0, lastSlashIndex + 1); - } else { - currentBaseURL = baseURL; - } - const normalizedSpecifier = - parseURLLikeSpecifier(specifier, currentBaseURL) || specifier; - for (const scopePrefix in importMap.scopes) { - if ( - scopePrefix === currentBaseURL || - (scopePrefix.endsWith('/') && currentBaseURL.startsWith(scopePrefix)) - ) { - const scopeImportsMatch = resolveImportsMatch( - normalizedSpecifier, - importMap.scopes[scopePrefix], - ); - if (scopeImportsMatch) { - return scopeImportsMatch; - } - } else { - const topLevelImportsMatch = resolveImportsMatch( - normalizedSpecifier, - importMap.imports, - ); - if (topLevelImportsMatch) { - return topLevelImportsMatch; - } - } - } - - return resolveImportsMatch(normalizedSpecifier, importMap.imports); -} - -// https://wicg.github.io/import-maps/#resolve-an-imports-match -function resolveImportsMatch(normalizedSpecifier, specifierMap) { - for (const specifierKey in specifierMap) { - const resolutionResult = specifierMap[specifierKey]; - - if (specifierKey === normalizedSpecifier) { - if (resolutionResult === null) { - throw TypeError( - `The import map resolution of ${specifierKey} failed due to a null entry`, - ); - } - return resolutionResult; - } else if ( - specifierKey.endsWith('/') && - normalizedSpecifier.startsWith(specifierKey) - ) { - if (resolutionResult === null) { - throw TypeError( - `The import map resolution of ${specifierKey} failed due to a null entry`, - ); - } - const afterPrefix = normalizedSpecifier.slice(specifierKey.length); - try { - return new URL(afterPrefix, resolutionResult).href; - } catch { - throw TypeError( - `The import map resolution of ${specifierKey} failed due to URL parse failure`, - ); - } - } - } - - return null; -} - -// https://wicg.github.io/import-maps/#parsing -export function resolveAndComposeImportMap(parsed) { - // Step 2 - if (!isPlainObject(parsed)) { - throw Error(`Invalid import map - top level must be an object`); - } - - // Step 3 - let sortedAndNormalizedImports = {}; - - // Step 4 - if (Object.prototype.hasOwnProperty.call(parsed, 'imports')) { - // Step 4.1 - if (!isPlainObject(parsed.imports)) { - throw Error(`Invalid import map - "imports" property must be an object`); - } - - // Step 4.2 - sortedAndNormalizedImports = sortAndNormalizeSpecifierMap( - parsed.imports, - baseURL, - ); - } - - // Step 5 - let sortedAndNormalizedScopes = {}; - - // Step 6 - if (Object.prototype.hasOwnProperty.call(parsed, 'scopes')) { - // Step 6.1 - if (!isPlainObject(parsed.scopes)) { - throw Error(`Invalid import map - "scopes" property must be an object`); - } - - // Step 6.2 - sortedAndNormalizedScopes = sortAndNormalizeScopes(parsed.scopes, baseURL); - } - - // Step 7 - const invalidKeys = Object.keys(parsed).filter( - (key) => key !== 'imports' && key !== 'scopes', - ); - if (invalidKeys.length > 0) { - console.warn( - `Invalid top-level key${ - invalidKeys.length > 0 ? 's' : '' - } in import map - ${invalidKeys.join(', ')}`, - ); - } - - // Step 8 - return { - imports: sortedAndNormalizedImports, - scopes: sortedAndNormalizedScopes, - }; -} - -// https://wicg.github.io/import-maps/#sort-and-normalize-a-specifier-map -function sortAndNormalizeSpecifierMap(map, baseURL) { - const normalized = {}; - - for (const specifierKey in map) { - const value = map[specifierKey]; - - const normalizedSpecifierKey = normalizeSpecifierKey(specifierKey, baseURL); - if (normalizedSpecifierKey === null) { - continue; - } - - const addressURL = parseURLLikeSpecifier(value, baseURL); - if (addressURL === null) { - console.warn( - `Invalid URL address for import map specifier '${specifierKey}'`, - ); - normalized[normalizedSpecifierKey] = null; - continue; - } - - if (specifierKey.endsWith('/') && !addressURL.endsWith('/')) { - console.warn( - `Invalid URL address for import map specifier '${specifierKey}' - since the specifier ends in slash, so must the address`, - ); - normalized[normalizedSpecifierKey] = null; - continue; - } - - normalized[normalizedSpecifierKey] = addressURL; - } - - return normalized; -} - -// https://wicg.github.io/import-maps/#normalize-a-specifier-key -function normalizeSpecifierKey(key) { - if (key === '') { - console.warn(`Specifier keys in import maps may not be the empty string`); - return null; - } - - return parseURLLikeSpecifier(key, baseURL) || key; -} - -// https://wicg.github.io/import-maps/#parse-a-url-like-import-specifier -function parseURLLikeSpecifier(specifier, baseURL) { - const useBaseUrlAsParent = - specifier.startsWith('/') || - specifier.startsWith('./') || - specifier.startsWith('../'); - - try { - return new URL(specifier, useBaseUrlAsParent ? baseURL : undefined).href; - } catch { - return null; - } -} - -// https://wicg.github.io/import-maps/#sort-and-normalize-scopes -function sortAndNormalizeScopes(map, baseURL) { - const normalized = {}; - - for (const scopePrefix in map) { - const potentialSpecifierMap = map[scopePrefix]; - if (!isPlainObject(potentialSpecifierMap)) { - throw TypeError( - `The value of scope ${scopePrefix} must be a JSON object`, - ); - } - - let scopePrefixURL; - try { - scopePrefixURL = new URL(scopePrefix, baseURL).href; - } catch { - console.warn( - `Scope prefix URL '${scopePrefix}' was not parseable in import map`, - ); - continue; - } - - normalized[scopePrefixURL] = sortAndNormalizeSpecifierMap( - potentialSpecifierMap, - baseURL, - ); - } - - return normalized; -} - -function isPlainObject(obj) { - return obj === Object(obj) && !Array.isArray(obj); -} - -// --- - -let importMapPromise = getImportMapPromise(); - -export async function resolve(specifier, context, defaultResolve) { - const { parentURL = null } = context; - const importMap = await importMapPromise; - const importMapUrl = resolveSpecifier(importMap, specifier, parentURL); - - return defaultResolve(importMapUrl ?? specifier, context, defaultResolve); -} - -export async function load(url, context, defaultLoad) { - if (url.startsWith('http://') || url.startsWith('https://')) { - const res = await fetch(url); - if (!res.ok) { - throw new Error(`Failed to fetch module from ${url}`); - } - const source = await res.text(); - return { - shortCircuit: true, - format: 'module', - source, - }; - } - - if (!url.startsWith('node:')) { - context.format = 'module'; - } - - return defaultLoad(url, context, defaultLoad); -} - -async function getImportMapPromise() { - const relativePath = process.env.IMPORT_MAP_PATH || IMPORT_MAP_FILE_NAME; - const importMapPath = path.resolve(process.cwd(), relativePath); - - let str; - try { - str = await fs.readFile(importMapPath); - } catch (err) { - return emptyMap(); - } - - let json; - try { - json = await JSON.parse(str); - } catch (err) { - throw Error( - `Import map at ${importMapPath} contains invalid json: ${err.message}`, - ); - } - - return resolveAndComposeImportMap(json); -} - -global.nodeLoader = global.nodeLoader || {}; - -global.nodeLoader.setImportMapPromise = function setImportMapPromise(promise) { - importMapPromise = promise.then((map) => { - return resolveAndComposeImportMap(map); - }); -}; - -function emptyMap() { - return { imports: {}, scopes: {} }; -} diff --git a/libs/native-federation-node/src/lib/utils/loader-as-data-url.js b/libs/native-federation-node/src/lib/utils/loader-as-data-url.js deleted file mode 100644 index 7dd93431..00000000 --- a/libs/native-federation-node/src/lib/utils/loader-as-data-url.js +++ /dev/null @@ -1,2 +0,0 @@ -export const resolver = - 'aW1wb3J0IHBhdGggZnJvbSAncGF0aCc7CmltcG9ydCB1cmwgZnJvbSAndXJsJzsKaW1wb3J0IHsgcHJvbWlzZXMgYXMgZnMgfSBmcm9tICdmcyc7CgpleHBvcnQgY29uc3QgSU1QT1JUX01BUF9GSUxFX05BTUUgPSAnbm9kZS5pbXBvcnRtYXAnOwoKY29uc3QgYmFzZVVSTCA9IHVybC5wYXRoVG9GaWxlVVJMKHByb2Nlc3MuY3dkKCkpICsgcGF0aC5zZXA7CgovLyBodHRwczovL3dpY2cuZ2l0aHViLmlvL2ltcG9ydC1tYXBzLyNuZXctcmVzb2x2ZS1hbGdvcml0aG0KZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVTcGVjaWZpZXIoaW1wb3J0TWFwLCBzcGVjaWZpZXIsIHBhcmVudFVSTCkgewogIGxldCBjdXJyZW50QmFzZVVSTDsKICBpZiAocGFyZW50VVJMKSB7CiAgICBjb25zdCBsYXN0U2xhc2hJbmRleCA9IHBhcmVudFVSTC5sYXN0SW5kZXhPZihwYXRoLnNlcCk7CiAgICBjdXJyZW50QmFzZVVSTCA9IHBhcmVudFVSTC5zbGljZSgwLCBsYXN0U2xhc2hJbmRleCArIDEpOwogIH0gZWxzZSB7CiAgICBjdXJyZW50QmFzZVVSTCA9IGJhc2VVUkw7CiAgfQogIGNvbnN0IG5vcm1hbGl6ZWRTcGVjaWZpZXIgPQogICAgcGFyc2VVUkxMaWtlU3BlY2lmaWVyKHNwZWNpZmllciwgY3VycmVudEJhc2VVUkwpIHx8IHNwZWNpZmllcjsKICBmb3IgKGNvbnN0IHNjb3BlUHJlZml4IGluIGltcG9ydE1hcC5zY29wZXMpIHsKICAgIGlmICgKICAgICAgc2NvcGVQcmVmaXggPT09IGN1cnJlbnRCYXNlVVJMIHx8CiAgICAgIChzY29wZVByZWZpeC5lbmRzV2l0aCgnLycpICYmIGN1cnJlbnRCYXNlVVJMLnN0YXJ0c1dpdGgoc2NvcGVQcmVmaXgpKQogICAgKSB7CiAgICAgIGNvbnN0IHNjb3BlSW1wb3J0c01hdGNoID0gcmVzb2x2ZUltcG9ydHNNYXRjaCgKICAgICAgICBub3JtYWxpemVkU3BlY2lmaWVyLAogICAgICAgIGltcG9ydE1hcC5zY29wZXNbc2NvcGVQcmVmaXhdLAogICAgICApOwogICAgICBpZiAoc2NvcGVJbXBvcnRzTWF0Y2gpIHsKICAgICAgICByZXR1cm4gc2NvcGVJbXBvcnRzTWF0Y2g7CiAgICAgIH0KICAgIH0gZWxzZSB7CiAgICAgIGNvbnN0IHRvcExldmVsSW1wb3J0c01hdGNoID0gcmVzb2x2ZUltcG9ydHNNYXRjaCgKICAgICAgICBub3JtYWxpemVkU3BlY2lmaWVyLAogICAgICAgIGltcG9ydE1hcC5pbXBvcnRzLAogICAgICApOwogICAgICBpZiAodG9wTGV2ZWxJbXBvcnRzTWF0Y2gpIHsKICAgICAgICByZXR1cm4gdG9wTGV2ZWxJbXBvcnRzTWF0Y2g7CiAgICAgIH0KICAgIH0KICB9CgogIHJldHVybiByZXNvbHZlSW1wb3J0c01hdGNoKG5vcm1hbGl6ZWRTcGVjaWZpZXIsIGltcG9ydE1hcC5pbXBvcnRzKTsKfQoKLy8gaHR0cHM6Ly93aWNnLmdpdGh1Yi5pby9pbXBvcnQtbWFwcy8jcmVzb2x2ZS1hbi1pbXBvcnRzLW1hdGNoCmZ1bmN0aW9uIHJlc29sdmVJbXBvcnRzTWF0Y2gobm9ybWFsaXplZFNwZWNpZmllciwgc3BlY2lmaWVyTWFwKSB7CiAgZm9yIChjb25zdCBzcGVjaWZpZXJLZXkgaW4gc3BlY2lmaWVyTWFwKSB7CiAgICBjb25zdCByZXNvbHV0aW9uUmVzdWx0ID0gc3BlY2lmaWVyTWFwW3NwZWNpZmllcktleV07CgogICAgaWYgKHNwZWNpZmllcktleSA9PT0gbm9ybWFsaXplZFNwZWNpZmllcikgewogICAgICBpZiAocmVzb2x1dGlvblJlc3VsdCA9PT0gbnVsbCkgewogICAgICAgIHRocm93IFR5cGVFcnJvcigKICAgICAgICAgIGBUaGUgaW1wb3J0IG1hcCByZXNvbHV0aW9uIG9mICR7c3BlY2lmaWVyS2V5fSBmYWlsZWQgZHVlIHRvIGEgbnVsbCBlbnRyeWAsCiAgICAgICAgKTsKICAgICAgfQogICAgICByZXR1cm4gcmVzb2x1dGlvblJlc3VsdDsKICAgIH0gZWxzZSBpZiAoCiAgICAgIHNwZWNpZmllcktleS5lbmRzV2l0aCgnLycpICYmCiAgICAgIG5vcm1hbGl6ZWRTcGVjaWZpZXIuc3RhcnRzV2l0aChzcGVjaWZpZXJLZXkpCiAgICApIHsKICAgICAgaWYgKHJlc29sdXRpb25SZXN1bHQgPT09IG51bGwpIHsKICAgICAgICB0aHJvdyBUeXBlRXJyb3IoCiAgICAgICAgICBgVGhlIGltcG9ydCBtYXAgcmVzb2x1dGlvbiBvZiAke3NwZWNpZmllcktleX0gZmFpbGVkIGR1ZSB0byBhIG51bGwgZW50cnlgLAogICAgICAgICk7CiAgICAgIH0KICAgICAgY29uc3QgYWZ0ZXJQcmVmaXggPSBub3JtYWxpemVkU3BlY2lmaWVyLnNsaWNlKHNwZWNpZmllcktleS5sZW5ndGgpOwogICAgICB0cnkgewogICAgICAgIHJldHVybiBuZXcgVVJMKGFmdGVyUHJlZml4LCByZXNvbHV0aW9uUmVzdWx0KS5ocmVmOwogICAgICB9IGNhdGNoIHsKICAgICAgICB0aHJvdyBUeXBlRXJyb3IoCiAgICAgICAgICBgVGhlIGltcG9ydCBtYXAgcmVzb2x1dGlvbiBvZiAke3NwZWNpZmllcktleX0gZmFpbGVkIGR1ZSB0byBVUkwgcGFyc2UgZmFpbHVyZWAsCiAgICAgICAgKTsKICAgICAgfQogICAgfQogIH0KCiAgcmV0dXJuIG51bGw7Cn0KCi8vIGh0dHBzOi8vd2ljZy5naXRodWIuaW8vaW1wb3J0LW1hcHMvI3BhcnNpbmcKZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVBbmRDb21wb3NlSW1wb3J0TWFwKHBhcnNlZCkgewogIC8vIFN0ZXAgMgogIGlmICghaXNQbGFpbk9iamVjdChwYXJzZWQpKSB7CiAgICB0aHJvdyBFcnJvcihgSW52YWxpZCBpbXBvcnQgbWFwIC0gdG9wIGxldmVsIG11c3QgYmUgYW4gb2JqZWN0YCk7CiAgfQoKICAvLyBTdGVwIDMKICBsZXQgc29ydGVkQW5kTm9ybWFsaXplZEltcG9ydHMgPSB7fTsKCiAgLy8gU3RlcCA0CiAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChwYXJzZWQsICdpbXBvcnRzJykpIHsKICAgIC8vIFN0ZXAgNC4xCiAgICBpZiAoIWlzUGxhaW5PYmplY3QocGFyc2VkLmltcG9ydHMpKSB7CiAgICAgIHRocm93IEVycm9yKGBJbnZhbGlkIGltcG9ydCBtYXAgLSAiaW1wb3J0cyIgcHJvcGVydHkgbXVzdCBiZSBhbiBvYmplY3RgKTsKICAgIH0KCiAgICAvLyBTdGVwIDQuMgogICAgc29ydGVkQW5kTm9ybWFsaXplZEltcG9ydHMgPSBzb3J0QW5kTm9ybWFsaXplU3BlY2lmaWVyTWFwKAogICAgICBwYXJzZWQuaW1wb3J0cywKICAgICAgYmFzZVVSTCwKICAgICk7CiAgfQoKICAvLyBTdGVwIDUKICBsZXQgc29ydGVkQW5kTm9ybWFsaXplZFNjb3BlcyA9IHt9OwoKICAvLyBTdGVwIDYKICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHBhcnNlZCwgJ3Njb3BlcycpKSB7CiAgICAvLyBTdGVwIDYuMQogICAgaWYgKCFpc1BsYWluT2JqZWN0KHBhcnNlZC5zY29wZXMpKSB7CiAgICAgIHRocm93IEVycm9yKGBJbnZhbGlkIGltcG9ydCBtYXAgLSAic2NvcGVzIiBwcm9wZXJ0eSBtdXN0IGJlIGFuIG9iamVjdGApOwogICAgfQoKICAgIC8vIFN0ZXAgNi4yCiAgICBzb3J0ZWRBbmROb3JtYWxpemVkU2NvcGVzID0gc29ydEFuZE5vcm1hbGl6ZVNjb3BlcyhwYXJzZWQuc2NvcGVzLCBiYXNlVVJMKTsKICB9CgogIC8vIFN0ZXAgNwogIGNvbnN0IGludmFsaWRLZXlzID0gT2JqZWN0LmtleXMocGFyc2VkKS5maWx0ZXIoCiAgICAoa2V5KSA9PiBrZXkgIT09ICdpbXBvcnRzJyAmJiBrZXkgIT09ICdzY29wZXMnLAogICk7CiAgaWYgKGludmFsaWRLZXlzLmxlbmd0aCA+IDApIHsKICAgIGNvbnNvbGUud2FybigKICAgICAgYEludmFsaWQgdG9wLWxldmVsIGtleSR7CiAgICAgICAgaW52YWxpZEtleXMubGVuZ3RoID4gMCA/ICdzJyA6ICcnCiAgICAgIH0gaW4gaW1wb3J0IG1hcCAtICR7aW52YWxpZEtleXMuam9pbignLCAnKX1gLAogICAgKTsKICB9CgogIC8vIFN0ZXAgOAogIHJldHVybiB7CiAgICBpbXBvcnRzOiBzb3J0ZWRBbmROb3JtYWxpemVkSW1wb3J0cywKICAgIHNjb3Blczogc29ydGVkQW5kTm9ybWFsaXplZFNjb3BlcywKICB9Owp9CgovLyBodHRwczovL3dpY2cuZ2l0aHViLmlvL2ltcG9ydC1tYXBzLyNzb3J0LWFuZC1ub3JtYWxpemUtYS1zcGVjaWZpZXItbWFwCmZ1bmN0aW9uIHNvcnRBbmROb3JtYWxpemVTcGVjaWZpZXJNYXAobWFwLCBiYXNlVVJMKSB7CiAgY29uc3Qgbm9ybWFsaXplZCA9IHt9OwoKICBmb3IgKGNvbnN0IHNwZWNpZmllcktleSBpbiBtYXApIHsKICAgIGNvbnN0IHZhbHVlID0gbWFwW3NwZWNpZmllcktleV07CgogICAgY29uc3Qgbm9ybWFsaXplZFNwZWNpZmllcktleSA9IG5vcm1hbGl6ZVNwZWNpZmllcktleShzcGVjaWZpZXJLZXksIGJhc2VVUkwpOwogICAgaWYgKG5vcm1hbGl6ZWRTcGVjaWZpZXJLZXkgPT09IG51bGwpIHsKICAgICAgY29udGludWU7CiAgICB9CgogICAgY29uc3QgYWRkcmVzc1VSTCA9IHBhcnNlVVJMTGlrZVNwZWNpZmllcih2YWx1ZSwgYmFzZVVSTCk7CiAgICBpZiAoYWRkcmVzc1VSTCA9PT0gbnVsbCkgewogICAgICBjb25zb2xlLndhcm4oCiAgICAgICAgYEludmFsaWQgVVJMIGFkZHJlc3MgZm9yIGltcG9ydCBtYXAgc3BlY2lmaWVyICcke3NwZWNpZmllcktleX0nYCwKICAgICAgKTsKICAgICAgbm9ybWFsaXplZFtub3JtYWxpemVkU3BlY2lmaWVyS2V5XSA9IG51bGw7CiAgICAgIGNvbnRpbnVlOwogICAgfQoKICAgIGlmIChzcGVjaWZpZXJLZXkuZW5kc1dpdGgoJy8nKSAmJiAhYWRkcmVzc1VSTC5lbmRzV2l0aCgnLycpKSB7CiAgICAgIGNvbnNvbGUud2FybigKICAgICAgICBgSW52YWxpZCBVUkwgYWRkcmVzcyBmb3IgaW1wb3J0IG1hcCBzcGVjaWZpZXIgJyR7c3BlY2lmaWVyS2V5fScgLSBzaW5jZSB0aGUgc3BlY2lmaWVyIGVuZHMgaW4gc2xhc2gsIHNvIG11c3QgdGhlIGFkZHJlc3NgLAogICAgICApOwogICAgICBub3JtYWxpemVkW25vcm1hbGl6ZWRTcGVjaWZpZXJLZXldID0gbnVsbDsKICAgICAgY29udGludWU7CiAgICB9CgogICAgbm9ybWFsaXplZFtub3JtYWxpemVkU3BlY2lmaWVyS2V5XSA9IGFkZHJlc3NVUkw7CiAgfQoKICByZXR1cm4gbm9ybWFsaXplZDsKfQoKLy8gaHR0cHM6Ly93aWNnLmdpdGh1Yi5pby9pbXBvcnQtbWFwcy8jbm9ybWFsaXplLWEtc3BlY2lmaWVyLWtleQpmdW5jdGlvbiBub3JtYWxpemVTcGVjaWZpZXJLZXkoa2V5KSB7CiAgaWYgKGtleSA9PT0gJycpIHsKICAgIGNvbnNvbGUud2FybihgU3BlY2lmaWVyIGtleXMgaW4gaW1wb3J0IG1hcHMgbWF5IG5vdCBiZSB0aGUgZW1wdHkgc3RyaW5nYCk7CiAgICByZXR1cm4gbnVsbDsKICB9CgogIHJldHVybiBwYXJzZVVSTExpa2VTcGVjaWZpZXIoa2V5LCBiYXNlVVJMKSB8fCBrZXk7Cn0KCi8vIGh0dHBzOi8vd2ljZy5naXRodWIuaW8vaW1wb3J0LW1hcHMvI3BhcnNlLWEtdXJsLWxpa2UtaW1wb3J0LXNwZWNpZmllcgpmdW5jdGlvbiBwYXJzZVVSTExpa2VTcGVjaWZpZXIoc3BlY2lmaWVyLCBiYXNlVVJMKSB7CiAgY29uc3QgdXNlQmFzZVVybEFzUGFyZW50ID0KICAgIHNwZWNpZmllci5zdGFydHNXaXRoKCcvJykgfHwKICAgIHNwZWNpZmllci5zdGFydHNXaXRoKCcuLycpIHx8CiAgICBzcGVjaWZpZXIuc3RhcnRzV2l0aCgnLi4vJyk7CgogIHRyeSB7CiAgICByZXR1cm4gbmV3IFVSTChzcGVjaWZpZXIsIHVzZUJhc2VVcmxBc1BhcmVudCA/IGJhc2VVUkwgOiB1bmRlZmluZWQpLmhyZWY7CiAgfSBjYXRjaCB7CiAgICByZXR1cm4gbnVsbDsKICB9Cn0KCi8vIGh0dHBzOi8vd2ljZy5naXRodWIuaW8vaW1wb3J0LW1hcHMvI3NvcnQtYW5kLW5vcm1hbGl6ZS1zY29wZXMKZnVuY3Rpb24gc29ydEFuZE5vcm1hbGl6ZVNjb3BlcyhtYXAsIGJhc2VVUkwpIHsKICBjb25zdCBub3JtYWxpemVkID0ge307CgogIGZvciAoY29uc3Qgc2NvcGVQcmVmaXggaW4gbWFwKSB7CiAgICBjb25zdCBwb3RlbnRpYWxTcGVjaWZpZXJNYXAgPSBtYXBbc2NvcGVQcmVmaXhdOwogICAgaWYgKCFpc1BsYWluT2JqZWN0KHBvdGVudGlhbFNwZWNpZmllck1hcCkpIHsKICAgICAgdGhyb3cgVHlwZUVycm9yKAogICAgICAgIGBUaGUgdmFsdWUgb2Ygc2NvcGUgJHtzY29wZVByZWZpeH0gbXVzdCBiZSBhIEpTT04gb2JqZWN0YCwKICAgICAgKTsKICAgIH0KCiAgICBsZXQgc2NvcGVQcmVmaXhVUkw7CiAgICB0cnkgewogICAgICBzY29wZVByZWZpeFVSTCA9IG5ldyBVUkwoc2NvcGVQcmVmaXgsIGJhc2VVUkwpLmhyZWY7CiAgICB9IGNhdGNoIHsKICAgICAgY29uc29sZS53YXJuKAogICAgICAgIGBTY29wZSBwcmVmaXggVVJMICcke3Njb3BlUHJlZml4fScgd2FzIG5vdCBwYXJzZWFibGUgaW4gaW1wb3J0IG1hcGAsCiAgICAgICk7CiAgICAgIGNvbnRpbnVlOwogICAgfQoKICAgIG5vcm1hbGl6ZWRbc2NvcGVQcmVmaXhVUkxdID0gc29ydEFuZE5vcm1hbGl6ZVNwZWNpZmllck1hcCgKICAgICAgcG90ZW50aWFsU3BlY2lmaWVyTWFwLAogICAgICBiYXNlVVJMLAogICAgKTsKICB9CgogIHJldHVybiBub3JtYWxpemVkOwp9CgpmdW5jdGlvbiBpc1BsYWluT2JqZWN0KG9iaikgewogIHJldHVybiBvYmogPT09IE9iamVjdChvYmopICYmICFBcnJheS5pc0FycmF5KG9iaik7Cn0KCi8vIC0tLQoKbGV0IGltcG9ydE1hcFByb21pc2UgPSBnZXRJbXBvcnRNYXBQcm9taXNlKCk7CgpleHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZShzcGVjaWZpZXIsIGNvbnRleHQsIGRlZmF1bHRSZXNvbHZlKSB7CiAgY29uc3QgeyBwYXJlbnRVUkwgPSBudWxsIH0gPSBjb250ZXh0OwogIGNvbnN0IGltcG9ydE1hcCA9IGF3YWl0IGltcG9ydE1hcFByb21pc2U7CiAgY29uc3QgaW1wb3J0TWFwVXJsID0gcmVzb2x2ZVNwZWNpZmllcihpbXBvcnRNYXAsIHNwZWNpZmllciwgcGFyZW50VVJMKTsKCiAgcmV0dXJuIGRlZmF1bHRSZXNvbHZlKGltcG9ydE1hcFVybCA/PyBzcGVjaWZpZXIsIGNvbnRleHQsIGRlZmF1bHRSZXNvbHZlKTsKfQoKZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWQodXJsLCBjb250ZXh0LCBkZWZhdWx0TG9hZCkgewogIGlmICh1cmwuc3RhcnRzV2l0aCgnaHR0cDovLycpIHx8IHVybC5zdGFydHNXaXRoKCdodHRwczovLycpKSB7CiAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaCh1cmwpOwogICAgaWYgKCFyZXMub2spIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZmV0Y2ggbW9kdWxlIGZyb20gJHt1cmx9YCk7CiAgICB9CiAgICBjb25zdCBzb3VyY2UgPSBhd2FpdCByZXMudGV4dCgpOwogICAgcmV0dXJuIHsKICAgICAgc2hvcnRDaXJjdWl0OiB0cnVlLAogICAgICBmb3JtYXQ6ICdtb2R1bGUnLAogICAgICBzb3VyY2UsCiAgICB9OwogIH0KCiAgaWYgKCF1cmwuc3RhcnRzV2l0aCgnbm9kZTonKSkgewogICAgY29udGV4dC5mb3JtYXQgPSAnbW9kdWxlJzsKICB9CgogIHJldHVybiBkZWZhdWx0TG9hZCh1cmwsIGNvbnRleHQsIGRlZmF1bHRMb2FkKTsKfQoKYXN5bmMgZnVuY3Rpb24gZ2V0SW1wb3J0TWFwUHJvbWlzZSgpIHsKICBjb25zdCByZWxhdGl2ZVBhdGggPSBwcm9jZXNzLmVudi5JTVBPUlRfTUFQX1BBVEggfHwgSU1QT1JUX01BUF9GSUxFX05BTUU7CiAgY29uc3QgaW1wb3J0TWFwUGF0aCA9IHBhdGgucmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCByZWxhdGl2ZVBhdGgpOwoKICBsZXQgc3RyOwogIHRyeSB7CiAgICBzdHIgPSBhd2FpdCBmcy5yZWFkRmlsZShpbXBvcnRNYXBQYXRoKTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHJldHVybiBlbXB0eU1hcCgpOwogIH0KCiAgbGV0IGpzb247CiAgdHJ5IHsKICAgIGpzb24gPSBhd2FpdCBKU09OLnBhcnNlKHN0cik7CiAgfSBjYXRjaCAoZXJyKSB7CiAgICB0aHJvdyBFcnJvcigKICAgICAgYEltcG9ydCBtYXAgYXQgJHtpbXBvcnRNYXBQYXRofSBjb250YWlucyBpbnZhbGlkIGpzb246ICR7ZXJyLm1lc3NhZ2V9YCwKICAgICk7CiAgfQoKICByZXR1cm4gcmVzb2x2ZUFuZENvbXBvc2VJbXBvcnRNYXAoanNvbik7Cn0KCmdsb2JhbC5ub2RlTG9hZGVyID0gZ2xvYmFsLm5vZGVMb2FkZXIgfHwge307CgpnbG9iYWwubm9kZUxvYWRlci5zZXRJbXBvcnRNYXBQcm9taXNlID0gZnVuY3Rpb24gc2V0SW1wb3J0TWFwUHJvbWlzZShwcm9taXNlKSB7CiAgaW1wb3J0TWFwUHJvbWlzZSA9IHByb21pc2UudGhlbigobWFwKSA9PiB7CiAgICByZXR1cm4gcmVzb2x2ZUFuZENvbXBvc2VJbXBvcnRNYXAobWFwKTsKICB9KTsKfTsKCmZ1bmN0aW9uIGVtcHR5TWFwKCkgewogIHJldHVybiB7IGltcG9ydHM6IHt9LCBzY29wZXM6IHt9IH07Cn0K'; diff --git a/libs/native-federation-node/tsconfig.json b/libs/native-federation-node/tsconfig.json deleted file mode 100644 index d0450ca6..00000000 --- a/libs/native-federation-node/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "ES2020", - "target": "ES2020", - "allowJs": true - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] -} diff --git a/libs/native-federation-node/tsconfig.lib.json b/libs/native-federation-node/tsconfig.lib.json deleted file mode 100644 index 67d4a142..00000000 --- a/libs/native-federation-node/tsconfig.lib.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "ES2022", - "target": "ES2022", - "outDir": "../../dist/out-tsc", - "declaration": true, - "types": ["node"] - }, - "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], - "include": [ - "src/**/*.ts", - "src/lib/node/import-map-store.ts.bak", - "src/lib/node/federation-resolver.ts.bak", - "../native-federation/tools/fstart-args-parser.ts", - "../native-federation/tools/fstart.ts" - ] -} diff --git a/libs/native-federation-node/tsconfig.spec.json b/libs/native-federation-node/tsconfig.spec.json deleted file mode 100644 index 9b2a121d..00000000 --- a/libs/native-federation-node/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/libs/native-federation-runtime/LICENSE b/libs/native-federation-runtime/LICENSE deleted file mode 100644 index 3edc92e0..00000000 --- a/libs/native-federation-runtime/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright 2021 Softarc Consulting GmbH - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/libs/native-federation-runtime/README.md b/libs/native-federation-runtime/README.md deleted file mode 100644 index 7c63e575..00000000 --- a/libs/native-federation-runtime/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# native-federation-runtime - -This library was generated with [Nx](https://nx.dev). - -## Running unit tests - -Run `nx test native-federation-runtime` to execute the unit tests. diff --git a/libs/native-federation-runtime/eslint.config.mjs b/libs/native-federation-runtime/eslint.config.mjs deleted file mode 100644 index 19d08fca..00000000 --- a/libs/native-federation-runtime/eslint.config.mjs +++ /dev/null @@ -1,50 +0,0 @@ -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import baseConfig from '../../eslint.config.mjs'; -import baseConfig1 from '../../eslint.base.config.mjs'; -import nx from '@nx/eslint-plugin'; -import jsoncEslintParser from 'jsonc-eslint-parser'; - -export default [ - ...baseConfig, - ...baseConfig1, - ...nx.configs['flat/angular'], - { - files: ['**/*.ts'], - rules: { - '@angular-eslint/directive-selector': [ - 'error', - { - type: 'attribute', - prefix: 'angularArchitects', - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'error', - { - type: 'element', - prefix: 'angular-architects', - style: 'kebab-case', - }, - ], - }, - }, - ...nx.configs['flat/angular-template'], - { - files: ['**/*.json'], - rules: { - '@nx/dependency-checks': 'warn', - }, - languageOptions: { - parser: jsoncEslintParser, - }, - }, - { - ignores: [ - '**/*.spec.ts', - '**/*.integration.spec.ts', - '__test-helpers__/**/*', - ], - }, -]; diff --git a/libs/native-federation-runtime/ng-package.json b/libs/native-federation-runtime/ng-package.json deleted file mode 100644 index eb310f98..00000000 --- a/libs/native-federation-runtime/ng-package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/libs/native-federation-runtime", - "lib": { - "entryFile": "src/index.ts" - } -} diff --git a/libs/native-federation-runtime/package-lock.json b/libs/native-federation-runtime/package-lock.json deleted file mode 100644 index b9f7f03e..00000000 --- a/libs/native-federation-runtime/package-lock.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@softarc/native-federation-runtime", - "version": "2.0.10", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@softarc/native-federation-runtime", - "version": "2.0.10", - "dependencies": { - "tslib": "^2.3.0" - }, - "devDependencies": { - "@types/node": "^22.5.4" - } - }, - "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "license": "0BSD" - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/libs/native-federation-runtime/package.json b/libs/native-federation-runtime/package.json deleted file mode 100644 index 17f25242..00000000 --- a/libs/native-federation-runtime/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@softarc/native-federation-runtime", - "version": "3.5.5", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "devDependencies": { - "@types/node": "^24.11.0", - "vitest": "^3.0.0", - "@vitest/ui": "^3.0.0" - }, - "scripts": { - "test": "vitest run", - "test:watch": "vitest", - "test:ui": "vitest --ui", - "test:coverage": "vitest run --coverage", - "test:unit": "vitest run --project=unit", - "test:integration": "vitest run --project=integration" - }, - "msw": { - "workerDirectory": [ - "public" - ] - } -} diff --git a/libs/native-federation-runtime/project.json b/libs/native-federation-runtime/project.json deleted file mode 100644 index a4dbdbf7..00000000 --- a/libs/native-federation-runtime/project.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "native-federation-runtime", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "projectType": "library", - "sourceRoot": "libs/native-federation-runtime/src", - "prefix": "angular-architects", - "tags": ["org:softarc", "scope:nf"], - "targets": { - "build": { - "executor": "@nx/angular:package", - "outputs": ["{workspaceRoot}/dist/{projectRoot}"], - "defaultConfiguration": "production", - "options": { - "project": "libs/native-federation-runtime/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "libs/native-federation-runtime/tsconfig.lib.prod.json" - }, - "development": { - "tsConfig": "libs/native-federation-runtime/tsconfig.lib.json" - } - } - }, - "publish": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-runtime npm {args.ver} {args.tag}" - } - }, - "publish-local": { - "dependsOn": ["build"], - "executor": "nx:run-commands", - "options": { - "command": "node tools/scripts/publish.mjs native-federation-runtime verdaccio {args.ver}" - } - }, - "test": { - "executor": "@nx/vitest:test", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "passWithNoTests": true, - "reportsDirectory": "../../coverage/libs/native-federation-runtime", - "configFile": "libs/native-federation-runtime/vitest.config.mts" - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": [ - "libs/native-federation-runtime/**/*.ts", - "libs/native-federation-runtime/package.json" - ] - } - } - } -} diff --git a/libs/native-federation-runtime/public/mockServiceWorker.js b/libs/native-federation-runtime/public/mockServiceWorker.js deleted file mode 100644 index 30600c86..00000000 --- a/libs/native-federation-runtime/public/mockServiceWorker.js +++ /dev/null @@ -1,349 +0,0 @@ -/* eslint-disable */ -/* tslint:disable */ - -/** - * Mock Service Worker. - * @see https://github.com/mswjs/msw - * - Please do NOT modify this file. - */ - -const PACKAGE_VERSION = '2.12.3'; -const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'; -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse'); -const activeClientIds = new Set(); - -addEventListener('install', function () { - self.skipWaiting(); -}); - -addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()); -}); - -addEventListener('message', async function (event) { - const clientId = Reflect.get(event.source || {}, 'id'); - - if (!clientId || !self.clients) { - return; - } - - const client = await self.clients.get(clientId); - - if (!client) { - return; - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }); - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }); - break; - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, - }); - break; - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId); - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: { - client: { - id: client.id, - frameType: client.frameType, - }, - }, - }); - break; - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId); - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId; - }); - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister(); - } - - break; - } - } -}); - -addEventListener('fetch', function (event) { - const requestInterceptedAt = Date.now(); - - // Bypass navigation requests. - if (event.request.mode === 'navigate') { - return; - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if ( - event.request.cache === 'only-if-cached' && - event.request.mode !== 'same-origin' - ) { - return; - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been terminated (still remains active until the next reload). - if (activeClientIds.size === 0) { - return; - } - - const requestId = crypto.randomUUID(); - event.respondWith(handleRequest(event, requestId, requestInterceptedAt)); -}); - -/** - * @param {FetchEvent} event - * @param {string} requestId - * @param {number} requestInterceptedAt - */ -async function handleRequest(event, requestId, requestInterceptedAt) { - const client = await resolveMainClient(event); - const requestCloneForEvents = event.request.clone(); - const response = await getResponse( - event, - client, - requestId, - requestInterceptedAt, - ); - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - const serializedRequest = await serializeRequest(requestCloneForEvents); - - // Clone the response so both the client and the library could consume it. - const responseClone = response.clone(); - - sendToClient( - client, - { - type: 'RESPONSE', - payload: { - isMockedResponse: IS_MOCKED_RESPONSE in response, - request: { - id: requestId, - ...serializedRequest, - }, - response: { - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - headers: Object.fromEntries(responseClone.headers.entries()), - body: responseClone.body, - }, - }, - }, - responseClone.body ? [serializedRequest.body, responseClone.body] : [], - ); - } - - return response; -} - -/** - * Resolve the main client for the given event. - * Client that issues a request doesn't necessarily equal the client - * that registered the worker. It's with the latter the worker should - * communicate with during the response resolving phase. - * @param {FetchEvent} event - * @returns {Promise} - */ -async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId); - - if (activeClientIds.has(event.clientId)) { - return client; - } - - if (client?.frameType === 'top-level') { - return client; - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }); - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible'; - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id); - }); -} - -/** - * @param {FetchEvent} event - * @param {Client | undefined} client - * @param {string} requestId - * @param {number} requestInterceptedAt - * @returns {Promise} - */ -async function getResponse(event, client, requestId, requestInterceptedAt) { - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const requestClone = event.request.clone(); - - function passthrough() { - // Cast the request headers to a new Headers instance - // so the headers can be manipulated with. - const headers = new Headers(requestClone.headers); - - // Remove the "accept" header value that marked this request as passthrough. - // This prevents request alteration and also keeps it compliant with the - // user-defined CORS policies. - const acceptHeader = headers.get('accept'); - if (acceptHeader) { - const values = acceptHeader.split(',').map((value) => value.trim()); - const filteredValues = values.filter( - (value) => value !== 'msw/passthrough', - ); - - if (filteredValues.length > 0) { - headers.set('accept', filteredValues.join(', ')); - } else { - headers.delete('accept'); - } - } - - return fetch(requestClone, { headers }); - } - - // Bypass mocking when the client is not active. - if (!client) { - return passthrough(); - } - - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough(); - } - - // Notify the client that a request has been intercepted. - const serializedRequest = await serializeRequest(event.request); - const clientMessage = await sendToClient( - client, - { - type: 'REQUEST', - payload: { - id: requestId, - interceptedAt: requestInterceptedAt, - ...serializedRequest, - }, - }, - [serializedRequest.body], - ); - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data); - } - - case 'PASSTHROUGH': { - return passthrough(); - } - } - - return passthrough(); -} - -/** - * @param {Client} client - * @param {any} message - * @param {Array} transferrables - * @returns {Promise} - */ -function sendToClient(client, message, transferrables = []) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel(); - - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error); - } - - resolve(event.data); - }; - - client.postMessage(message, [ - channel.port2, - ...transferrables.filter(Boolean), - ]); - }); -} - -/** - * @param {Response} response - * @returns {Response} - */ -function respondWithMock(response) { - // Setting response status code to 0 is a no-op. - // However, when responding with a "Response.error()", the produced Response - // instance will have status code set to 0. Since it's not possible to create - // a Response instance with status code 0, handle that use-case separately. - if (response.status === 0) { - return Response.error(); - } - - const mockedResponse = new Response(response.body, response); - - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, - }); - - return mockedResponse; -} - -/** - * @param {Request} request - */ -async function serializeRequest(request) { - return { - url: request.url, - mode: request.mode, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: await request.arrayBuffer(), - keepalive: request.keepalive, - }; -} diff --git a/libs/native-federation-runtime/src/index.ts b/libs/native-federation-runtime/src/index.ts deleted file mode 100644 index cbb978c4..00000000 --- a/libs/native-federation-runtime/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './lib/get-shared'; -export * from './lib/init-federation'; -export * from './lib/load-remote-module'; -export * from './lib/model/build-notifications-options'; -export * from './lib/model/federation-info'; -export * from './lib/model/import-map'; diff --git a/libs/native-federation-runtime/src/lib/__test-helpers__/dom-helpers.ts b/libs/native-federation-runtime/src/lib/__test-helpers__/dom-helpers.ts deleted file mode 100644 index 344eedc9..00000000 --- a/libs/native-federation-runtime/src/lib/__test-helpers__/dom-helpers.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * DOM test helpers for federation tests - */ - -/** - * Gets all importmap-shim scripts from document head - */ -export const getImportMapScripts = (): NodeListOf => { - return document.querySelectorAll('script[type="importmap-shim"]'); -}; - -/** - * Removes all importmap-shim scripts from document head - */ -export const clearImportMapScripts = (): void => { - const scripts = getImportMapScripts(); - scripts.forEach((script) => script.remove()); -}; - -/** - * Gets the parsed content of the first importmap-shim script - */ -export const getImportMapContent = (): { - imports: Record; - scopes: Record>; -} | null => { - const scripts = getImportMapScripts(); - if (scripts.length === 0) { - return null; - } - - try { - return JSON.parse(scripts[0].innerHTML); - } catch { - return null; - } -}; - -/** - * Asserts that an importmap script exists in the DOM - */ -export const assertImportMapExists = (): void => { - const scripts = getImportMapScripts(); - if (scripts.length === 0) { - throw new Error('Expected importmap-shim script to exist in document head'); - } -}; - -/** - * Gets the count of importmap scripts in the document - */ -export const getImportMapScriptCount = (): number => { - return getImportMapScripts().length; -}; - -/** - * Clears all DOM side effects from federation initialization - */ -export const clearFederationDOMEffects = (): void => { - clearImportMapScripts(); - // Add more cleanup here if federation adds other DOM elements -}; diff --git a/libs/native-federation-runtime/src/lib/__test-helpers__/federation-fixtures.ts b/libs/native-federation-runtime/src/lib/__test-helpers__/federation-fixtures.ts deleted file mode 100644 index 565c9936..00000000 --- a/libs/native-federation-runtime/src/lib/__test-helpers__/federation-fixtures.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { FederationInfo } from '../model/federation-info'; - -/** - * Test fixture builder for FederationInfo objects - */ -export const createFederationInfo = ( - overrides?: Partial, -): FederationInfo => ({ - name: 'default-host', - exposes: [], - shared: [], - ...overrides, -}); - -/** - * Creates a host federation info with shared dependencies - */ -export const createHostInfo = (name = 'host'): FederationInfo => ({ - name, - exposes: [], - shared: [ - { - singleton: true, - strictVersion: true, - requiredVersion: '^18.0.0', - packageName: 'angular', - outFileName: 'angular.js', - }, - { - singleton: true, - strictVersion: false, - requiredVersion: '^18.2.0', - packageName: 'rxjs', - outFileName: 'rxjs.js', - }, - ], -}); - -/** - * Creates a remote MFE federation info with exposes and shared - */ -export const createRemoteInfo = ( - name = 'mfe1', - exposes: Array<{ key: string; outFileName: string }> = [], -): FederationInfo => ({ - name, - exposes: - exposes.length > 0 - ? exposes - : [ - { - key: './Component', - outFileName: 'Component.js', - }, - ], - shared: [ - { - singleton: true, - strictVersion: false, - requiredVersion: '^4.0.0', - packageName: 'lodash', - outFileName: 'lodash.js', - }, - ], -}); - -/** - * Creates a minimal remote info without dependencies - */ -export const createMinimalRemoteInfo = ( - name = 'minimal-mfe', -): FederationInfo => ({ - name, - exposes: [ - { - key: './Module', - outFileName: 'Module.js', - }, - ], - shared: [], -}); - -/** - * Test URLs constants - */ -export const TEST_URLS = { - HOST_REMOTE_ENTRY: './remoteEntry.json', - MFE1_BASE: 'http://localhost:3000/mfe1', - MFE1_REMOTE_ENTRY: 'http://localhost:3000/mfe1/remoteEntry.json', - MFE2_BASE: 'http://localhost:4000/mfe2', - MFE2_REMOTE_ENTRY: 'http://localhost:4000/mfe2/remoteEntry.json', - INVALID_URL: - 'http://invalid-domain-that-does-not-exist.test/remoteEntry.json', -} as const; - -/** - * Creates a remote config object for initFederation - */ -export const createRemoteConfig = ( - ...remotes: Array<{ name: string; url: string }> -): Record => { - return remotes.reduce( - (acc, { name, url }) => { - acc[name] = url; - return acc; - }, - {} as Record, - ); -}; diff --git a/libs/native-federation-runtime/src/lib/__test-helpers__/module-fixtures.ts b/libs/native-federation-runtime/src/lib/__test-helpers__/module-fixtures.ts deleted file mode 100644 index 7f40bff9..00000000 --- a/libs/native-federation-runtime/src/lib/__test-helpers__/module-fixtures.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Test fixtures for loadRemoteModule tests - */ - -/** - * Creates a mock ES module with exports - */ -export const createMockModule = (exports: T): T => exports; - -/** - * Common mock modules for testing - */ -export const MOCK_MODULES = { - Component: createMockModule({ - default: class MockComponent { - name = 'MockComponent'; - }, - namedExport: 'test-value', - }), - - Button: createMockModule({ - default: class MockButton { - name = 'MockButton'; - click() { - return 'clicked'; - } - }, - }), - - Service: createMockModule({ - DataService: class DataService { - getData() { - return { data: 'test' }; - } - }, - ApiService: class ApiService { - fetch() { - return Promise.resolve({ ok: true }); - } - }, - }), - - EmptyModule: createMockModule({}), - - SimpleValue: createMockModule({ - value: 42, - message: 'Hello from remote', - }), -}; - -/** - * Creates a mock module URL for testing - */ -export const createModuleUrl = (baseUrl: string, fileName: string): string => { - return `${baseUrl}/${fileName}`; -}; - -/** - * Fallback components for testing - */ -export const FALLBACK_COMPONENTS = { - DefaultComponent: class DefaultComponent { - name = 'DefaultComponent'; - }, - - ErrorComponent: class ErrorComponent { - name = 'ErrorComponent'; - error = true; - }, - - NullFallback: null, -}; diff --git a/libs/native-federation-runtime/src/lib/__test-helpers__/msw-handlers.ts b/libs/native-federation-runtime/src/lib/__test-helpers__/msw-handlers.ts deleted file mode 100644 index 687cc9d0..00000000 --- a/libs/native-federation-runtime/src/lib/__test-helpers__/msw-handlers.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { delay, http, HttpResponse } from 'msw'; -import type { FederationInfo } from '../model/federation-info'; -import { - createHostInfo, - createRemoteInfo, - TEST_URLS, -} from './federation-fixtures'; - -/** - * MSW request handlers for federation tests - */ - -/** - * Creates a handler that returns the host remoteEntry.json - */ -export const hostRemoteEntryHandler = ( - info: FederationInfo = createHostInfo(), - options?: { delay?: number }, -) => { - return http.get(TEST_URLS.HOST_REMOTE_ENTRY, async () => { - if (options?.delay) { - await delay(options.delay); - } - return HttpResponse.json(info); - }); -}; - -/** - * Creates a handler for a remote MFE remoteEntry.json - */ -export const remoteEntryHandler = ( - url: string, - info: FederationInfo, - options?: { delay?: number; status?: number }, -) => { - return http.get(url, async () => { - if (options?.delay) { - await delay(options.delay); - } - - if (options?.status && options.status !== 200) { - return new HttpResponse(null, { status: options.status }); - } - - return HttpResponse.json(info); - }); -}; - -/** - * Creates a handler that returns 404 for any remoteEntry.json - */ -export const notFoundHandler = (url: string) => { - return http.get(url, () => { - return new HttpResponse(null, { status: 404 }); - }); -}; - -/** - * Creates a handler that returns network error - */ -export const networkErrorHandler = (url: string) => { - return http.get(url, () => { - return HttpResponse.error(); - }); -}; - -/** - * Creates a handler that returns malformed JSON - */ -export const malformedJsonHandler = (url: string) => { - return http.get(url, () => { - return new HttpResponse('{ invalid json', { - headers: { - 'Content-Type': 'application/json', - }, - }); - }); -}; - -/** - * Creates a handler that times out - */ -export const timeoutHandler = (url: string, timeoutMs = 5000) => { - return http.get(url, async () => { - await delay(timeoutMs); - return HttpResponse.json({}); - }); -}; - -/** - * Default handlers for common scenarios - */ -export const defaultHandlers = [ - hostRemoteEntryHandler(), - remoteEntryHandler(TEST_URLS.MFE1_REMOTE_ENTRY, createRemoteInfo('mfe1')), - remoteEntryHandler( - TEST_URLS.MFE2_REMOTE_ENTRY, - createRemoteInfo('mfe2', [{ key: './Button', outFileName: 'Button.js' }]), - ), -]; - -/** - * Creates handlers for a complete federation scenario - */ -export const createFederationHandlers = (config: { - host?: FederationInfo; - remotes?: Array<{ url: string; info: FederationInfo }>; -}) => { - const handlers = [hostRemoteEntryHandler(config.host || createHostInfo())]; - - if (config.remotes) { - config.remotes.forEach(({ url, info }) => { - handlers.push(remoteEntryHandler(url, info)); - }); - } - - return handlers; -}; - -/** - * Creates a handler that returns a JavaScript module - */ -export const moduleHandler = (url: string, moduleContent: any) => { - return http.get(url, () => { - // Return JavaScript module as text - const moduleCode = ` - export default ${JSON.stringify(moduleContent.default || {})}; - ${Object.entries(moduleContent) - .filter(([key]) => key !== 'default') - .map( - ([key, value]) => `export const ${key} = ${JSON.stringify(value)};`, - ) - .join('\n')} - `; - - return new HttpResponse(moduleCode, { - headers: { - 'Content-Type': 'application/javascript', - }, - }); - }); -}; - -/** - * Creates a handler that returns a 404 for a module - */ -export const moduleNotFoundHandler = (url: string) => { - return http.get(url, () => { - return new HttpResponse(null, { status: 404 }); - }); -}; - -/** - * Creates a handler that returns invalid JavaScript - */ -export const invalidModuleHandler = (url: string) => { - return http.get(url, () => { - return new HttpResponse('this is not valid javascript {{{', { - headers: { - 'Content-Type': 'application/javascript', - }, - }); - }); -}; diff --git a/libs/native-federation-runtime/src/lib/get-shared.ts b/libs/native-federation-runtime/src/lib/get-shared.ts deleted file mode 100644 index 9692df82..00000000 --- a/libs/native-federation-runtime/src/lib/get-shared.ts +++ /dev/null @@ -1,75 +0,0 @@ -type Type = new () => T; - -type NativeFederationContainer = { - __NATIVE_FEDERATION__: { - baseUrlToRemoteNames: Map; - externals: Map; - }; -}; - -export type ShareObject = { - version: string; - scope?: string; - get: () => Promise<() => Type>; - shareConfig?: { - singleton?: boolean; - requiredVersion: string; - }; -}; - -export type ShareConfig = { - [pkgName: string]: Array; -}; - -export type ShareOptions = { - singleton: boolean; - requiredVersionPrefix: '^' | '~' | '>' | '>=' | ''; -}; - -const defaultShareOptions: ShareOptions = { - singleton: false, - requiredVersionPrefix: '', -}; - -export function getShared(options = defaultShareOptions) { - const nfc = window as unknown as NativeFederationContainer; - const externals = nfc.__NATIVE_FEDERATION__.externals; - const shared: ShareConfig = {}; - - const allKeys = [...externals.keys()]; - const keys = allKeys - .filter( - (k) => - !k.startsWith('/@id/') && - !k.startsWith('@angular-architects/module-federation') && - !k.endsWith('@'), - ) - .sort(); - - for (const key of keys) { - const idx = key.lastIndexOf('@'); - const pkgName = key.substring(0, idx); - const version = key.substring(idx + 1); - const path = externals.get(key) ?? ''; - - const shareObj: ShareObject = { - version, - get: async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const lib = await (window as any).importShim(path); - return () => lib; - }, - shareConfig: { - singleton: options.singleton, - requiredVersion: options.requiredVersionPrefix + version, - }, - }; - - if (!shared[pkgName]) { - shared[pkgName] = []; - } - - shared[pkgName].push(shareObj); - } - return shared; -} diff --git a/libs/native-federation-runtime/src/lib/init-federation.integration.spec.ts b/libs/native-federation-runtime/src/lib/init-federation.integration.spec.ts deleted file mode 100644 index bc27b185..00000000 --- a/libs/native-federation-runtime/src/lib/init-federation.integration.spec.ts +++ /dev/null @@ -1,763 +0,0 @@ -import { http, HttpResponse } from 'msw'; -import { setupWorker } from 'msw/browser'; -import { - afterAll, - afterEach, - beforeAll, - beforeEach, - describe, - expect, - it, -} from 'vitest'; -import { - clearFederationDOMEffects, - getImportMapContent, - getImportMapScriptCount, - getImportMapScripts, -} from './__test-helpers__/dom-helpers'; -import { - createFederationInfo, - createHostInfo, - createMinimalRemoteInfo, - createRemoteConfig, - createRemoteInfo, - TEST_URLS, -} from './__test-helpers__/federation-fixtures'; -import { - createFederationHandlers, - hostRemoteEntryHandler, - malformedJsonHandler, - networkErrorHandler, - notFoundHandler, - remoteEntryHandler, -} from './__test-helpers__/msw-handlers'; -import { initFederation } from './init-federation'; - -// ============================================================================ -// TEST UTILITIES -// ============================================================================ - -/** - * Helper to capture console errors during test execution. - * Returns a cleanup function that restores the original console.error. - * - * @param onError - Optional callback to capture error messages - * @returns Cleanup function to restore console.error - */ -function captureConsoleErrors(onError?: (...args: any[]) => void): () => void { - const originalError = console.error; - console.error = onError || (() => {}); - return () => { - console.error = originalError; - }; -} - -/** - * Helper to capture console error messages in an array. - * Returns both the messages array and a cleanup function. - * - * @returns Tuple of [messages array, cleanup function] - */ -function captureConsoleErrorMessages(): [string[], () => void] { - const messages: string[] = []; - const cleanup = captureConsoleErrors((...args: any[]) => { - messages.push(args.join(' ')); - }); - return [messages, cleanup]; -} - -/** - * Integration tests for initFederation using MSW for network mocking - */ -describe('initFederation - Browser Integration Test', () => { - const worker = setupWorker(); - - beforeAll(async () => { - await worker.start({ - onUnhandledRequest: 'error', - quiet: false, - }); - }); - - afterAll(() => worker.stop()); - - beforeEach(() => { - clearFederationDOMEffects(); - }); - - afterEach(() => { - worker.resetHandlers(); - }); - - // ========================================================================== - // BASIC INITIALIZATION TESTS - // ========================================================================== - // These tests verify the fundamental initialization flows: - // - Host-only setup (no remotes) - // - Single remote integration - // - Multiple remotes working together - describe('Basic Initialization', () => { - it('should initialize federation without remotes', async () => { - const hostInfo = createHostInfo(); - worker.use(hostRemoteEntryHandler(hostInfo)); - - const result = await initFederation({}); - - expect(result).toEqual( - expect.objectContaining({ - imports: expect.objectContaining({ - angular: './angular.js', - rxjs: './rxjs.js', - }), - scopes: {}, - }), - ); - }); - - it('should initialize federation with empty host info', async () => { - const hostInfo = createFederationInfo({ name: 'empty-host' }); - worker.use(hostRemoteEntryHandler(hostInfo)); - - const result = await initFederation({}); - - expect(result).toEqual({ - imports: {}, - scopes: {}, - }); - }); - - it('should initialize federation with one remote', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1'); - - worker.use( - hostRemoteEntryHandler(hostInfo), - remoteEntryHandler(TEST_URLS.MFE1_REMOTE_ENTRY, remoteInfo), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports).toEqual( - expect.objectContaining({ - 'mfe1/Component': `${TEST_URLS.MFE1_BASE}/Component.js`, - }), - ); - expect(result.scopes).toHaveProperty(`${TEST_URLS.MFE1_BASE}/`); - expect(result.scopes[`${TEST_URLS.MFE1_BASE}/`]).toEqual( - expect.objectContaining({ - lodash: `${TEST_URLS.MFE1_BASE}/lodash.js`, - }), - ); - }); - - it('should initialize federation with multiple remotes', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const mfe1Info = createRemoteInfo('mfe1', [ - { key: './Component', outFileName: 'Component.js' }, - ]); - const mfe2Info = createRemoteInfo('mfe2', [ - { key: './Button', outFileName: 'Button.js' }, - ]); - - worker.use( - ...createFederationHandlers({ - host: hostInfo, - remotes: [ - { url: TEST_URLS.MFE1_REMOTE_ENTRY, info: mfe1Info }, - { url: TEST_URLS.MFE2_REMOTE_ENTRY, info: mfe2Info }, - ], - }), - ); - - const result = await initFederation( - createRemoteConfig( - { name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }, - { name: 'mfe2', url: TEST_URLS.MFE2_REMOTE_ENTRY }, - ), - ); - - expect(result.imports).toEqual( - expect.objectContaining({ - 'mfe1/Component': `${TEST_URLS.MFE1_BASE}/Component.js`, - 'mfe2/Button': `${TEST_URLS.MFE2_BASE}/Button.js`, - }), - ); - expect(result.scopes).toHaveProperty(`${TEST_URLS.MFE1_BASE}/`); - expect(result.scopes).toHaveProperty(`${TEST_URLS.MFE2_BASE}/`); - }); - }); - - // ========================================================================== - // DOM MANIPULATION TESTS - // ========================================================================== - // These tests verify that initFederation correctly manipulates the DOM: - // - Script tag injection - // - Importmap structure and content - // - Multiple initialization calls - describe('DOM Manipulation', () => { - it('should append importmap-shim script to document head', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - worker.use(hostRemoteEntryHandler(hostInfo)); - - await initFederation({}); - - const scripts = getImportMapScripts(); - expect(scripts.length).toBe(1); - expect(scripts[0].type).toBe('importmap-shim'); - }); - - it('should create importmap with correct structure', async () => { - const hostInfo = createHostInfo(); - worker.use(hostRemoteEntryHandler(hostInfo)); - - await initFederation({}); - - const importMapContent = getImportMapContent(); - expect(importMapContent).not.toBeNull(); - expect(importMapContent).toHaveProperty('imports'); - expect(importMapContent).toHaveProperty('scopes'); - expect(importMapContent!.imports).toEqual( - expect.objectContaining({ - angular: './angular.js', - rxjs: './rxjs.js', - }), - ); - }); - - it('should handle multiple calls without creating duplicate scripts', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - worker.use(hostRemoteEntryHandler(hostInfo)); - - await initFederation({}); - const firstCount = getImportMapScriptCount(); - - await initFederation({}); - const secondCount = getImportMapScriptCount(); - - // Each call adds a new script (this is the actual behavior) - expect(secondCount).toBe(firstCount + 1); - }); - }); - - // ========================================================================== - // CACHE TAG HANDLING TESTS - // ========================================================================== - // These tests verify cache busting functionality: - // - CacheTag applied to host requests - // - CacheTag applied to all remote requests - // - Correct parameter separator (? or &) based on existing query params - describe('Cache Tag Handling', () => { - it('should apply cacheTag to host request', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - let capturedUrl = ''; - - worker.use( - http.get('./remoteEntry.json', ({ request }) => { - capturedUrl = request.url; - return HttpResponse.json(hostInfo); - }), - ); - - await initFederation({}, { cacheTag: 'v1.0.0' }); - - expect(capturedUrl).toContain('t=v1.0.0'); - }); - - it('should apply cacheTag to all remote requests', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1'); - const capturedUrls: string[] = []; - - worker.use( - http.get('./remoteEntry.json', ({ request }) => { - capturedUrls.push(request.url); - return HttpResponse.json(hostInfo); - }), - http.get(TEST_URLS.MFE1_REMOTE_ENTRY, ({ request }) => { - capturedUrls.push(request.url); - return HttpResponse.json(remoteInfo); - }), - ); - - await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - { cacheTag: 'v2.5.1' }, - ); - - expect(capturedUrls.length).toBe(2); - expect(capturedUrls.every((url) => url.includes('t=v2.5.1'))).toBe(true); - }); - - it('should append cacheTag with & when URL has existing query params', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1'); - const urlWithParams = `${TEST_URLS.MFE1_REMOTE_ENTRY}?env=prod`; - let capturedRemoteUrl = ''; - - worker.use( - hostRemoteEntryHandler(hostInfo), - http.get(TEST_URLS.MFE1_REMOTE_ENTRY, ({ request }) => { - capturedRemoteUrl = request.url; - return HttpResponse.json(remoteInfo); - }), - ); - - await initFederation( - createRemoteConfig({ name: 'mfe1', url: urlWithParams }), - { cacheTag: 'v1.0.0' }, - ); - - expect(capturedRemoteUrl).toContain('env=prod'); - expect(capturedRemoteUrl).toContain('&t=v1.0.0'); - }); - }); - - // ========================================================================== - // IMPORT MAP MERGING TESTS - // ========================================================================== - // These tests verify that import maps from different sources merge correctly: - // - Host shared deps in root imports - // - Remote exposed modules in root imports - // - Remote shared deps in scoped imports - // - Handling overlapping dependencies between remotes - describe('Import Map Merging', () => { - it('should merge host and remote import maps correctly', async () => { - const hostInfo = createHostInfo(); - const remoteInfo = createRemoteInfo('mfe1', [ - { key: './Button', outFileName: 'Button.js' }, - ]); - - worker.use( - ...createFederationHandlers({ - host: hostInfo, - remotes: [{ url: TEST_URLS.MFE1_REMOTE_ENTRY, info: remoteInfo }], - }), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - // Host shared dependencies in root imports - expect(result.imports['angular']).toBe('./angular.js'); - expect(result.imports['rxjs']).toBe('./rxjs.js'); - - // Remote exposed modules in root imports - expect(result.imports['mfe1/Button']).toBe( - `${TEST_URLS.MFE1_BASE}/Button.js`, - ); - - // Remote shared dependencies in scopes - expect(result.scopes[`${TEST_URLS.MFE1_BASE}/`]['lodash']).toBe( - `${TEST_URLS.MFE1_BASE}/lodash.js`, - ); - }); - - it('should handle multiple remotes with overlapping shared dependencies', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const sharedDep = { - singleton: true, - strictVersion: false, - requiredVersion: '^4.0.0', - packageName: 'lodash', - outFileName: 'lodash.js', - }; - - const mfe1Info = createFederationInfo({ - name: 'mfe1', - exposes: [{ key: './Component', outFileName: 'Component.js' }], - shared: [sharedDep], - }); - - const mfe2Info = createFederationInfo({ - name: 'mfe2', - exposes: [{ key: './Service', outFileName: 'Service.js' }], - shared: [sharedDep], - }); - - worker.use( - ...createFederationHandlers({ - host: hostInfo, - remotes: [ - { url: TEST_URLS.MFE1_REMOTE_ENTRY, info: mfe1Info }, - { url: TEST_URLS.MFE2_REMOTE_ENTRY, info: mfe2Info }, - ], - }), - ); - - const result = await initFederation( - createRemoteConfig( - { name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }, - { name: 'mfe2', url: TEST_URLS.MFE2_REMOTE_ENTRY }, - ), - ); - - // Both remotes should have lodash in their scopes - expect(result.scopes[`${TEST_URLS.MFE1_BASE}/`]['lodash']).toBeDefined(); - expect(result.scopes[`${TEST_URLS.MFE2_BASE}/`]['lodash']).toBeDefined(); - }); - }); - - // ========================================================================== - // ERROR HANDLING TESTS - // ========================================================================== - // These tests verify resilient error handling: - // - 404 responses handled gracefully - // - Network errors don't crash the app - // - Malformed JSON handled properly - // - Host failures are fatal (must have host) - // - Partial success when some remotes fail - describe('Error Handling', () => { - it('should handle 404 response for remote gracefully when throwIfRemoteNotFound is false', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - worker.use( - hostRemoteEntryHandler(hostInfo), - notFoundHandler(TEST_URLS.MFE1_REMOTE_ENTRY), - ); - - const [errorMessages, cleanup] = captureConsoleErrorMessages(); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports).toEqual({}); - expect(result.scopes).toEqual({}); - expect( - errorMessages.some((msg) => - msg.includes('Error loading remote entry for mfe1'), - ), - ).toBe(true); - - cleanup(); - }); - - it('should throw error when remote not found and throwIfRemoteNotFound is true', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - worker.use( - hostRemoteEntryHandler(hostInfo), - notFoundHandler(TEST_URLS.MFE1_REMOTE_ENTRY), - ); - - // TODO Note: throwIfRemoteNotFound is not exposed in InitFederationOptions, - // but it's used internally. We test the default behavior (false) above. - // To properly test this, we would need to expose it in the public API - // or test processRemoteInfos directly. - - // For now, this test documents the expected behavior if the option were exposed - const { processRemoteInfos } = await import('./init-federation'); - - await expect( - processRemoteInfos( - createRemoteConfig({ - name: 'mfe1', - url: TEST_URLS.MFE1_REMOTE_ENTRY, - }), - { throwIfRemoteNotFound: true }, - ), - ).rejects.toThrow('Error loading remote entry for mfe1'); - }); - - it('should handle network errors gracefully', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - worker.use( - hostRemoteEntryHandler(hostInfo), - networkErrorHandler(TEST_URLS.MFE1_REMOTE_ENTRY), - ); - - let errorCalled = false; - const cleanup = captureConsoleErrors(() => { - errorCalled = true; - }); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports).toEqual({}); - expect(result.scopes).toEqual({}); - expect(errorCalled).toBe(true); - - cleanup(); - }); - - it('should handle malformed JSON response', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - worker.use( - hostRemoteEntryHandler(hostInfo), - malformedJsonHandler(TEST_URLS.MFE1_REMOTE_ENTRY), - ); - - let errorCalled = false; - const cleanup = captureConsoleErrors(() => { - errorCalled = true; - }); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports).toEqual({}); - expect(result.scopes).toEqual({}); - expect(errorCalled).toBe(true); - - cleanup(); - }); - - it('should handle host remoteEntry.json failure', async () => { - worker.use(notFoundHandler(TEST_URLS.HOST_REMOTE_ENTRY)); - - await expect(initFederation({})).rejects.toThrow(); - }); - - it('should continue with successful remotes when some fail', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const mfe2Info = createRemoteInfo('mfe2'); - - worker.use( - hostRemoteEntryHandler(hostInfo), - notFoundHandler(TEST_URLS.MFE1_REMOTE_ENTRY), - remoteEntryHandler(TEST_URLS.MFE2_REMOTE_ENTRY, mfe2Info), - ); - - const [errorMessages, cleanup] = captureConsoleErrorMessages(); - - const result = await initFederation( - createRemoteConfig( - { name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }, - { name: 'mfe2', url: TEST_URLS.MFE2_REMOTE_ENTRY }, - ), - ); - - // mfe2 should be loaded successfully - expect(result.imports['mfe2/Component']).toBeDefined(); - expect(result.scopes[`${TEST_URLS.MFE2_BASE}/`]).toBeDefined(); - - // mfe1 should not be in the result - expect(result.imports['mfe1/Component']).toBeUndefined(); - - expect( - errorMessages.some((msg) => - msg.includes('Error loading remote entry for mfe1'), - ), - ).toBe(true); - - cleanup(); - }); - }); - - // ========================================================================== - // EDGE CASES TESTS - // ========================================================================== - // These tests verify boundary conditions: - // - Remotes with no exposed modules - // - Remotes with no shared dependencies - // - Empty remotes configuration - // - Special characters in names - // - URL formatting edge cases - describe('Edge Cases', () => { - it('should handle remote with no exposes', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createFederationInfo({ - name: 'mfe1', - exposes: [], - shared: [ - { - singleton: true, - strictVersion: false, - requiredVersion: '^4.0.0', - packageName: 'lodash', - outFileName: 'lodash.js', - }, - ], - }); - - worker.use( - ...createFederationHandlers({ - host: hostInfo, - remotes: [{ url: TEST_URLS.MFE1_REMOTE_ENTRY, info: remoteInfo }], - }), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(Object.keys(result.imports)).toHaveLength(0); - expect(result.scopes[`${TEST_URLS.MFE1_BASE}/`]['lodash']).toBeDefined(); - }); - - it('should handle remote with no shared dependencies', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createMinimalRemoteInfo('mfe1'); - - worker.use( - ...createFederationHandlers({ - host: hostInfo, - remotes: [{ url: TEST_URLS.MFE1_REMOTE_ENTRY, info: remoteInfo }], - }), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports['mfe1/Module']).toBeDefined(); - expect(result.scopes[`${TEST_URLS.MFE1_BASE}/`]).toEqual({}); - }); - - it('should handle empty remotes object', async () => { - const hostInfo = createHostInfo(); - worker.use(hostRemoteEntryHandler(hostInfo)); - - const result = await initFederation({}); - - expect(result.imports).toEqual({ - angular: './angular.js', - rxjs: './rxjs.js', - }); - expect(result.scopes).toEqual({}); - }); - - it('should handle special characters in remote names', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('my-mfe-1', [ - { key: './Component', outFileName: 'Component.js' }, - ]); - - worker.use( - hostRemoteEntryHandler(hostInfo), - remoteEntryHandler(TEST_URLS.MFE1_REMOTE_ENTRY, remoteInfo), - ); - - const result = await initFederation( - createRemoteConfig({ - name: 'my-mfe-1', - url: TEST_URLS.MFE1_REMOTE_ENTRY, - }), - ); - - expect(result.imports['my-mfe-1/Component']).toBeDefined(); - }); - - it('should handle URLs with trailing slashes', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1'); - const urlWithTrailingSlash = `${TEST_URLS.MFE1_BASE}/remoteEntry.json`; - - worker.use( - hostRemoteEntryHandler(hostInfo), - remoteEntryHandler(urlWithTrailingSlash, remoteInfo), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: urlWithTrailingSlash }), - ); - - expect(result.imports['mfe1/Component']).toBeDefined(); - }); - - it('should handle remote with multiple exposed modules', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1', [ - { key: './Component', outFileName: 'Component.js' }, - { key: './Button', outFileName: 'Button.js' }, - { key: './Service', outFileName: 'Service.js' }, - ]); - - worker.use( - hostRemoteEntryHandler(hostInfo), - remoteEntryHandler(TEST_URLS.MFE1_REMOTE_ENTRY, remoteInfo), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports['mfe1/Component']).toBe( - `${TEST_URLS.MFE1_BASE}/Component.js`, - ); - expect(result.imports['mfe1/Button']).toBe( - `${TEST_URLS.MFE1_BASE}/Button.js`, - ); - expect(result.imports['mfe1/Service']).toBe( - `${TEST_URLS.MFE1_BASE}/Service.js`, - ); - }); - - it('should handle remote with nested exposed paths', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1', [ - { key: './components/Button', outFileName: 'components-Button.js' }, - { - key: './services/api/DataService', - outFileName: 'services-api-DataService.js', - }, - ]); - - worker.use( - hostRemoteEntryHandler(hostInfo), - remoteEntryHandler(TEST_URLS.MFE1_REMOTE_ENTRY, remoteInfo), - ); - - const result = await initFederation( - createRemoteConfig({ name: 'mfe1', url: TEST_URLS.MFE1_REMOTE_ENTRY }), - ); - - expect(result.imports['mfe1/components/Button']).toBe( - `${TEST_URLS.MFE1_BASE}/components-Button.js`, - ); - expect(result.imports['mfe1/services/api/DataService']).toBe( - `${TEST_URLS.MFE1_BASE}/services-api-DataService.js`, - ); - }); - }); - - // ========================================================================== - // MANIFEST LOADING TESTS - // ========================================================================== - // These tests verify manifest-based configuration: - // - Loading remotes from a manifest.json file - // - Cache busting applied to manifest URL - describe('Manifest Loading', () => { - it('should load remotes from manifest URL', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const remoteInfo = createRemoteInfo('mfe1'); - const manifestUrl = 'http://localhost:3000/federation-manifest.json'; - const manifest = { - mfe1: TEST_URLS.MFE1_REMOTE_ENTRY, - }; - - worker.use( - http.get(manifestUrl, () => HttpResponse.json(manifest)), - hostRemoteEntryHandler(hostInfo), - remoteEntryHandler(TEST_URLS.MFE1_REMOTE_ENTRY, remoteInfo), - ); - - const result = await initFederation(manifestUrl); - - expect(result.imports['mfe1/Component']).toBeDefined(); - }); - - it('should apply cacheTag to manifest URL', async () => { - const hostInfo = createFederationInfo({ name: 'host' }); - const manifestUrl = 'http://localhost:3000/federation-manifest.json'; - const manifest = {}; - let capturedUrl = ''; - - worker.use( - http.get(manifestUrl, ({ request }) => { - capturedUrl = request.url; - return HttpResponse.json(manifest); - }), - hostRemoteEntryHandler(hostInfo), - ); - - await initFederation(manifestUrl, { cacheTag: 'v1.0.0' }); - - expect(capturedUrl).toContain('t=v1.0.0'); - }); - }); -}); diff --git a/libs/native-federation-runtime/src/lib/init-federation.ts b/libs/native-federation-runtime/src/lib/init-federation.ts deleted file mode 100644 index 5ac72b8f..00000000 --- a/libs/native-federation-runtime/src/lib/init-federation.ts +++ /dev/null @@ -1,432 +0,0 @@ -import { getExternalUrl, setExternalUrl } from './model/externals'; -import { - FederationInfo, - InitFederationOptions, - ProcessRemoteInfoOptions, -} from './model/federation-info'; -import { - ImportMap, - Imports, - mergeImportMaps, - Scopes, -} from './model/import-map'; -import { addRemote } from './model/remotes'; -import { appendImportMap } from './utils/add-import-map'; -import { getDirectory, joinPaths } from './utils/path-utils'; -import { watchFederationBuildCompletion } from './watch-federation-build'; - -/** - * Initializes the Native Federation runtime for the host application. - * - * This is the main entry point for setting up federation. It performs the following: - * 1. Loads the host's remoteEntry.json to discover shared dependencies - * 2. Loads each remote's remoteEntry.json to discover exposed modules - * 3. Creates an ES Module import map with proper scoping - * 4. Injects the import map into the DOM as a - -`; - - indexContent = indexContent.replace( - /]*><\/script>/, - '', - ); - indexContent = indexContent.replace( - /]*><\/script>/, - '', - ); - - indexContent = indexContent.replace(/()/, `$1\n\t\t${htmlFragment}`); - return indexContent; -} diff --git a/libs/native-federation/tsconfig.json b/libs/native-federation/tsconfig.json deleted file mode 100644 index 4f59ddb6..00000000 --- a/libs/native-federation/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", - "forceConsistentCasingInFileNames": true, - "allowJs": true, - // "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - // "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "types": ["node"] - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] -} diff --git a/libs/native-federation/tsconfig.lib.json b/libs/native-federation/tsconfig.lib.json deleted file mode 100644 index 0af17487..00000000 --- a/libs/native-federation/tsconfig.lib.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "composite": true, - "outDir": "../../dist/out-tsc", - "declaration": true, - "types": ["node"] - }, - "include": ["**/*.ts", "**/*.js"], - "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] -} diff --git a/libs/native-federation/tsconfig.spec.json b/libs/native-federation/tsconfig.spec.json deleted file mode 100644 index 5eccfe56..00000000 --- a/libs/native-federation/tsconfig.spec.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "composite": true, - "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/nx.json b/nx.json index ba85e85d..b25d9390 100644 --- a/nx.json +++ b/nx.json @@ -18,11 +18,6 @@ "sharedGlobals": [] }, "targetDefaults": { - "@angular-architects/native-federation:build": { - "cache": true, - "dependsOn": ["^build"], - "inputs": ["production", "^production"] - }, "@angular-devkit/build-angular:application": { "cache": true, "dependsOn": ["^build"], @@ -58,20 +53,6 @@ "targetName": "e2e", "componentTestingTargetName": "component-test" } - }, - { - "plugin": "@nx/vite/plugin", - "options": { - "buildTargetName": "build", - "testTargetName": "test", - "serveTargetName": "serve", - "devTargetName": "dev", - "previewTargetName": "preview", - "serveStaticTargetName": "serve-static", - "typecheckTargetName": "typecheck", - "buildDepsTargetName": "build-deps", - "watchDepsTargetName": "watch-deps" - } } ], "generators": { diff --git a/package-lock.json b/package-lock.json index 9a4f4f1c..24395514 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,11 +17,9 @@ "@angular/platform-browser": "^22.0.0", "@angular/platform-browser-dynamic": "^22.0.0", "@angular/router": "^22.0.0", - "@chialab/esbuild-plugin-commonjs": "^0.19.0", "@module-federation/enhanced": "^2.2.2", "@module-federation/runtime-core": "^2.2.2", "@rsbuild/core": "^1.1.13", - "es-module-shims": "^1.5.12", "rxjs": "^7.8.2", "tslib": "^2.3.0", "zone.js": "^0.16.0" @@ -45,44 +43,28 @@ "@nx/jest": "~23.1.0", "@nx/js": "~23.1.0", "@nx/node": "~23.1.0", - "@nx/plugin": "~23.1.0", - "@nx/vite": "~23.1.0", - "@nx/vitest": "~23.1.0", "@nx/web": "~23.1.0", "@nx/webpack": "~23.1.0", "@nx/workspace": "~23.1.0", - "@playwright/test": "^1.60.0", - "@rollup/plugin-commonjs": "^28.0.3", - "@rollup/plugin-json": "^6.0.0", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-replace": "^6.0.2", "@schematics/angular": "^22.0.0", - "@softarc/sheriff-core": "^0.19.2", "@swc-node/register": "1.11.1", "@swc/cli": "~0.8.0", "@swc/core": "~1.15.1", "@swc/helpers": "~0.5.12", - "@types/browser-sync": "^2.29.0", "@types/jest": "^30.0.0", "@types/node": "^24.9.0", "@types/trusted-types": "^2.0.7", "@typescript-eslint/eslint-plugin": "^8.58.0", "@typescript-eslint/parser": "^8.58.0", "@typescript-eslint/utils": "^8.58.0", - "@vitest/browser": "^4.0.0", - "@vitest/browser-playwright": "^4.0.18", - "@vitest/ui": "^4.0.0", - "acorn": "^8.8.1", "angular-eslint": "^22.0.0", "autoprefixer": "^10.4.0", - "browser-sync": "^3.0.2", "callsite": "^1.0.0", "chalk": "^5.6.2", "chokidar": "^5.0.0", "cross-spawn": "^7.0.6", "cypress": "15.10.0", "dotenv": "^17.3.1", - "esbuild": ">=0.27.1 <0.27.4 || >0.27.4", "eslint": "~9.38.0", "eslint-config-prettier": "~10.1.8", "eslint-plugin-cypress": "~5.2.0", @@ -93,11 +75,8 @@ "jest-preset-angular": "~17.0.0", "json5": "^2.2.0", "jsonc-eslint-parser": "^2.1.0", - "mrmime": "^2.0.1", - "msw": "^2.12.3", "ng-packagr": "^22.0.0", "node-fetch": "^3.3.2", - "node-watch": "^0.7.3", "nx": "~23.0.0", "postcss": "^8.3.9", "postcss-import": "~16.1.0", @@ -105,9 +84,6 @@ "postcss-url": "10.1.3", "pre-commit": "1.2.2", "prettier": "^3.6.2", - "rollup": "^4.52.0", - "rollup-plugin-esbuild": "^6.2.1", - "rollup-plugin-node-externals": "^8.1.1", "semver": "~7.7.1", "ts-jest": "29.4.11", "ts-node": "~10.9.1", @@ -115,8 +91,6 @@ "typescript": "~6.0.0", "typescript-eslint": "^8.58.0", "verdaccio": "^6.2.1", - "vite": "^7.0.0", - "vitest": "^4.0.0", "word-wrap": "^1.2.5" }, "peerDependencies": { @@ -3534,13 +3508,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@blazediff/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", - "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", - "dev": true, - "license": "MIT" - }, "node_modules/@borewit/text-codec": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", @@ -3573,69 +3540,6 @@ "dev": true, "license": "(Apache-2.0 AND BSD-3-Clause)" }, - "node_modules/@chialab/cjs-to-esm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@chialab/cjs-to-esm/-/cjs-to-esm-0.19.2.tgz", - "integrity": "sha512-si+dfCzglT+zh+4okOjA+BKnb2SfdmD81pEYW9qgtzWQbHSSai4niz7kd0Q7JtfHSMlttYkYw1TSybl1M7ETpA==", - "license": "MIT", - "dependencies": { - "@chialab/estransform": "^0.20.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@chialab/esbuild-plugin-commonjs": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-commonjs/-/esbuild-plugin-commonjs-0.19.1.tgz", - "integrity": "sha512-jqX7SmHjelH90DrIUZoB9iGoMdML4hKCwqomZrfvfgx6t22llDXP7MkawHmrny/kZmreA0Ytjsp46dsV51OioQ==", - "license": "MIT", - "dependencies": { - "@chialab/cjs-to-esm": "^0.19.1", - "@chialab/esbuild-rna": "^0.19.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@chialab/esbuild-rna": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@chialab/esbuild-rna/-/esbuild-rna-0.19.3.tgz", - "integrity": "sha512-os4rxgolT256uO6OPP2FZE8/CPdSFN7j7QrqPWjLI2ZsPRds4K0nQTEtJm8FOEmWhGJZxFa4ofxzwFIhWyabhw==", - "license": "MIT", - "dependencies": { - "@chialab/estransform": "^0.20.0", - "@chialab/node-resolve": "^0.19.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@chialab/estransform": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@chialab/estransform/-/estransform-0.20.0.tgz", - "integrity": "sha512-ziaUvRJy520iI5qCRTdMoYvXShLFa6QsIUR8Pj7H/4sJzUOia1OY3QgAAsuGcBjj30nsBSqc4W0Y/jF6phMYQA==", - "license": "MIT", - "dependencies": { - "@napi-rs/magic-string": "^0.3.4", - "@parcel/source-map": "^2.0.0", - "cjs-module-lexer": "^1.2.2", - "es-module-lexer": "^1.0.0", - "oxc-parser": ">=0.114.0 <1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@chialab/node-resolve": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@chialab/node-resolve/-/node-resolve-0.19.1.tgz", - "integrity": "sha512-J4i4YJNaFuYG6UWpum9y8XfICWsWxoCawy6HQtU2lDqp915oboxXvpZ3lBdA5Llb8VexCKQZYufY8QXPyzU62Q==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@colordx/core": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.5.0.tgz", @@ -7973,263 +7877,6 @@ "win32" ] }, - "node_modules/@mswjs/interceptors": { - "version": "0.41.9", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz", - "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@napi-rs/magic-string": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string/-/magic-string-0.3.4.tgz", - "integrity": "sha512-DEWl/B99RQsyMT3F9bvrXuhL01/eIQp/dtNSE3G1jQ4mTGRcP4iHWxoPZ577WrbjUinrNgvRA5+08g8fkPgimQ==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/magic-string-android-arm-eabi": "0.3.4", - "@napi-rs/magic-string-android-arm64": "0.3.4", - "@napi-rs/magic-string-darwin-arm64": "0.3.4", - "@napi-rs/magic-string-darwin-x64": "0.3.4", - "@napi-rs/magic-string-freebsd-x64": "0.3.4", - "@napi-rs/magic-string-linux-arm-gnueabihf": "0.3.4", - "@napi-rs/magic-string-linux-arm64-gnu": "0.3.4", - "@napi-rs/magic-string-linux-arm64-musl": "0.3.4", - "@napi-rs/magic-string-linux-x64-gnu": "0.3.4", - "@napi-rs/magic-string-linux-x64-musl": "0.3.4", - "@napi-rs/magic-string-win32-arm64-msvc": "0.3.4", - "@napi-rs/magic-string-win32-ia32-msvc": "0.3.4", - "@napi-rs/magic-string-win32-x64-msvc": "0.3.4" - } - }, - "node_modules/@napi-rs/magic-string-android-arm-eabi": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-android-arm-eabi/-/magic-string-android-arm-eabi-0.3.4.tgz", - "integrity": "sha512-sszAYxqtzzJ4FDerDNHcqL9NhqPhj8W4DNiOanXYy50mA5oojlRtaAFPiB5ZMrWDBM32v5Q30LrmxQ4eTtu2Dg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-android-arm64": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-android-arm64/-/magic-string-android-arm64-0.3.4.tgz", - "integrity": "sha512-jdQ6HuO0X5rkX4MauTcWR4HWdgjakTOmmzqXg8L26+jOHVVG1LZE+Su5qvV4bP8vMb2h+vPE+JsnwqSmWymu3Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-darwin-arm64": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-darwin-arm64/-/magic-string-darwin-arm64-0.3.4.tgz", - "integrity": "sha512-6NmMtvURce9/oq09XBZmuIeI6lPLGtEJ2ZPO/QzL3nLZa6wygiCnO/sFACKYNg5/73ET5HMMTeuogE1JI+r2Lw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-darwin-x64": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-darwin-x64/-/magic-string-darwin-x64-0.3.4.tgz", - "integrity": "sha512-f9LmfMiUAKDOtl0meOuLYeVb6OERrgGzrTg1Tn3R3fTAShM2kxRbfAuPE9ljuXxIFzOv/uqRNLSl/LqCJwpREA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-freebsd-x64": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-freebsd-x64/-/magic-string-freebsd-x64-0.3.4.tgz", - "integrity": "sha512-rqduQ4odiDK4QdM45xHWRTU4wtFIfpp8g8QGpz+3qqg7ivldDqbbNOrBaf6Oeu77uuEvWggnkyuChotfKgJdJQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-linux-arm-gnueabihf": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-arm-gnueabihf/-/magic-string-linux-arm-gnueabihf-0.3.4.tgz", - "integrity": "sha512-pVaJEdEpiPqIfq3M4+yMAATS7Z9muDcWYn8H7GFH1ygh8GwgLgKfy/n/lG2M6zp18Mwd0x7E2E/qg9GgCyUzoQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-linux-arm64-gnu": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-arm64-gnu/-/magic-string-linux-arm64-gnu-0.3.4.tgz", - "integrity": "sha512-9FwoAih/0tzEZx0BjYYIxWkSRMjonIn91RFM3q3MBs/evmThXUYXUqLNa1PPIkK1JoksswtDi48qWWLt8nGflQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-linux-arm64-musl": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-arm64-musl/-/magic-string-linux-arm64-musl-0.3.4.tgz", - "integrity": "sha512-wCR7R+WPOcAKmVQc1s6h6HwfwW1vL9pM8BjUY9Ljkdb8wt1LmZEmV2Sgfc1SfbRQzbyl+pKeufP6adRRQVzYDA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-linux-x64-gnu": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-x64-gnu/-/magic-string-linux-x64-gnu-0.3.4.tgz", - "integrity": "sha512-sbxFDpYnt5WFbxQ1xozwOvh5A7IftqSI0WnE9O7KsQIOi0ej2dvFbfOW4tmFkvH/YP8KJELo5AhP2+kEq1DpYA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-linux-x64-musl": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-x64-musl/-/magic-string-linux-x64-musl-0.3.4.tgz", - "integrity": "sha512-jN4h/7e2Ul8v3UK5IZu38NXLMdzVWhY4uEDlnwuUAhwRh26wBQ1/pLD97Uy/Z3dFNBQPcsv60XS9fOM1YDNT6w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-win32-arm64-msvc": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-win32-arm64-msvc/-/magic-string-win32-arm64-msvc-0.3.4.tgz", - "integrity": "sha512-gMUyTRHLWpzX2ntJFCbW2Gnla9Y/WUmbkZuW5SBAo/Jo8QojHn76Y4PNgnoXdzcsV9b/45RBxurYKAfFg9WTyg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-win32-ia32-msvc": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-win32-ia32-msvc/-/magic-string-win32-ia32-msvc-0.3.4.tgz", - "integrity": "sha512-QIMauMOvEHgL00K9np/c9CT/CRtLOz3mRTQqcZ9XGzSoAMrpxH71KSpDJrKl7h7Ro6TZ+hJ0C3T+JVuTCZNv4A==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/magic-string-win32-x64-msvc": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@napi-rs/magic-string-win32-x64-msvc/-/magic-string-win32-x64-msvc-0.3.4.tgz", - "integrity": "sha512-V8FMSf828MzOI3P6/765MR7zHU6CUZqiyPhmAnwYoKFNxfv7oCviN/G6NcENeCdcYOvNgh5fYzaNLB96ndId5A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@napi-rs/nice": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", @@ -10084,20 +9731,6 @@ "win32" ] }, - "node_modules/@nx/plugin": { - "version": "23.1.0", - "resolved": "https://registry.npmjs.org/@nx/plugin/-/plugin-23.1.0.tgz", - "integrity": "sha512-WkpBKtc1rb5eASU9qEq7pOVUTrkO/SCABH6ddvYhhGzJyfiiuvEotMEu+3PbRR66scZR4kgeXr+jiD8kh4s9/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/devkit": "23.1.0", - "@nx/eslint": "23.1.0", - "@nx/jest": "23.1.0", - "@nx/js": "23.1.0", - "tslib": "^2.3.0" - } - }, "node_modules/@nx/rspack": { "version": "23.1.0", "resolved": "https://registry.npmjs.org/@nx/rspack/-/rspack-23.1.0.tgz", @@ -10599,58 +10232,6 @@ } } }, - "node_modules/@nx/vite": { - "version": "23.1.0", - "resolved": "https://registry.npmjs.org/@nx/vite/-/vite-23.1.0.tgz", - "integrity": "sha512-4nzc3mM55k+AcGxQzT8IKHoZH1dCROiY0CO0CD2H0sinwthJXxhL+zG1H3xowkKM7so6hkuf2JvGdNOeL4OocQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/devkit": "23.1.0", - "@nx/js": "23.1.0", - "@nx/vitest": "23.1.0", - "@phenomnomnominal/tsquery": "~6.2.0", - "ajv": "^8.0.0", - "enquirer": "~2.3.6", - "picomatch": "4.0.4", - "semver": "^7.6.3", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@nx/vitest": { - "version": "23.1.0", - "resolved": "https://registry.npmjs.org/@nx/vitest/-/vitest-23.1.0.tgz", - "integrity": "sha512-uv9lpbpOaEvrDjFToDKxeCsN+0thq4giIHUdOgcH+oHuqfxZ2speYGKCQz+6ICQg0nxcsCyuN34Ww6jJX3EVzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/devkit": "23.1.0", - "@nx/js": "23.1.0", - "@phenomnomnominal/tsquery": "~6.2.0", - "semver": "^7.6.3", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@nx/eslint": "23.1.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "vitest": "^3.0.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@nx/eslint": { - "optional": true - }, - "vite": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, "node_modules/@nx/web": { "version": "23.1.0", "resolved": "https://registry.npmjs.org/@nx/web/-/web-23.1.0.tgz", @@ -11637,438 +11218,6 @@ "node": ">=12" } }, - "node_modules/@open-draft/deferred-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", - "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.142.0.tgz", - "integrity": "sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.142.0.tgz", - "integrity": "sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.142.0.tgz", - "integrity": "sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.142.0.tgz", - "integrity": "sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.142.0.tgz", - "integrity": "sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.142.0.tgz", - "integrity": "sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.142.0.tgz", - "integrity": "sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.142.0.tgz", - "integrity": "sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.142.0.tgz", - "integrity": "sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.142.0.tgz", - "integrity": "sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.142.0.tgz", - "integrity": "sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.142.0.tgz", - "integrity": "sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.142.0.tgz", - "integrity": "sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.142.0.tgz", - "integrity": "sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.142.0.tgz", - "integrity": "sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.142.0.tgz", - "integrity": "sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.142.0.tgz", - "integrity": "sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.2", - "@emnapi/runtime": "1.11.2", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", - "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.0.tgz", - "integrity": "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^2.0.0-alpha.3", - "@emnapi/runtime": "^2.0.0-alpha.3" - } - }, - "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.142.0.tgz", - "integrity": "sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.142.0.tgz", - "integrity": "sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.142.0.tgz", - "integrity": "sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", - "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { "version": "11.24.2", "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz", @@ -12420,18 +11569,6 @@ "win32" ] }, - "node_modules/@parcel/source-map": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", - "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", - "license": "MIT", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": "^12.18.3 || >=14" - } - }, "node_modules/@parcel/watcher": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", @@ -12971,56 +12108,6 @@ "url": "https://opencollective.com/pkgr" } }, - "node_modules/@playwright/test": { - "version": "1.62.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.0.tgz", - "integrity": "sha512-9zOJ6ZQRAena31MpOH9VSzIz8Ou3YJ/wtY/eQm5T2uhfhG7/U3COrMS8xOtUrZrp9OgdmzEnIYODye3nY1VqzA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.62.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.9", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", - "integrity": "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "fdir": "^6.2.0", - "is-reference": "1.2.1", - "magic-string": "^0.30.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", @@ -13042,53 +12129,6 @@ } } }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", - "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", - "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/@rollup/pluginutils": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", @@ -13988,26 +13028,6 @@ "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@softarc/sheriff-core": { - "version": "0.19.6", - "resolved": "https://registry.npmjs.org/@softarc/sheriff-core/-/sheriff-core-0.19.6.tgz", - "integrity": "sha512-KACxHG9sS7kNWgnnBODzdr14kMLMrJVlQKc+tViUP03p2fRwNhESOA49bz51Yn7dro1mbtMmmFjICLmZSJDZZA==", - "dev": true, - "license": "MIT", - "bin": { - "sheriff": "src/bin/main.js" - }, - "peerDependencies": { - "typescript": ">=4.8" - } - }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -14564,94 +13584,6 @@ "@types/node": "*" } }, - "node_modules/@types/browser-sync": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/@types/browser-sync/-/browser-sync-2.29.1.tgz", - "integrity": "sha512-jAMsEkLpNURfpS4XIN9BX7SY+uCoTkPjLIovwssV/3e/FPwg9hYusbCXmGNfC3T6W/6d3iP3clxy9cvevjFKtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/micromatch": "^2", - "@types/node": "*", - "@types/serve-static": "*", - "chokidar": "^3.0.0" - } - }, - "node_modules/@types/browser-sync/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/@types/browser-sync/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/browser-sync/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@types/browser-sync/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -14673,23 +13605,6 @@ "@types/node": "*" } }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -14891,16 +13806,6 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, - "node_modules/@types/micromatch": { - "version": "2.3.35", - "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-2.3.35.tgz", - "integrity": "sha512-J749bHo/Zu56w0G0NI/IGHLQPiSsjx//0zJhfEVAN95K/xM5C8ZDmhkXtU3qns0sBOao7HuQzr8XV1/2o5LbXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-glob": "*" - } - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -14918,13 +13823,6 @@ "undici-types": "~7.18.0" } }, - "node_modules/@types/parse-glob": { - "version": "3.0.32", - "resolved": "https://registry.npmjs.org/@types/parse-glob/-/parse-glob-3.0.32.tgz", - "integrity": "sha512-n4xmml2WKR12XeQprN8L/sfiVPa8FHS3k+fxp4kSr/PA2GsGUgFND+bvISJxM0y5QdvzNEGjEVU3eIrcKks/pA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -14946,13 +13844,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", @@ -14980,27 +13871,6 @@ "@types/express": "*" } }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@types/set-cookie-parser": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", - "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", @@ -15032,13 +13902,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/statuses": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", - "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/tmp": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", @@ -16534,205 +15397,6 @@ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@vitest/browser": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.10.tgz", - "integrity": "sha512-UDwuWGwXj646CBx/bQHOaJSX7np0I8JL/UKQYa1e4QrVHH8VdWtx8eaOuf8sy0ShwDgR6NjJAsp5eF6vjF6qng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@blazediff/core": "1.9.1", - "@vitest/mocker": "4.1.10", - "@vitest/utils": "4.1.10", - "magic-string": "^0.30.21", - "pngjs": "^7.0.0", - "sirv": "^3.0.2", - "tinyrainbow": "^3.1.0", - "ws": "^8.19.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "4.1.10" - } - }, - "node_modules/@vitest/browser-playwright": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.10.tgz", - "integrity": "sha512-nMoXGEiRpT7m3W7NsbvrM2aKNwiNHZf+zEpUCvMteGjZFvfT96Q9fh7QyB98dvDWXiKvrLxA7bJ1mCOOv+JQPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/browser": "4.1.10", - "@vitest/mocker": "4.1.10", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "playwright": "*", - "vitest": "4.1.10" - }, - "peerDependenciesMeta": { - "playwright": { - "optional": false - } - } - }, - "node_modules/@vitest/expect": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", - "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", - "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.10", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/mocker/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", - "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", - "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.10", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", - "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.10", - "@vitest/utils": "4.1.10", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", - "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/ui": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.10.tgz", - "integrity": "sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.10", - "fflate": "^0.8.2", - "flatted": "^3.4.2", - "pathe": "^2.0.3", - "sirv": "^3.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "4.1.10" - } - }, - "node_modules/@vitest/utils": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", - "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.10", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -17566,16 +16230,6 @@ "node": ">=0.8" } }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -17593,16 +16247,6 @@ "dev": true, "license": "MIT" }, - "node_modules/async-each-series": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", - "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -18053,16 +16697,6 @@ ], "license": "MIT" }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, "node_modules/baseline-browser-mapping": { "version": "2.11.6", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.6.tgz", @@ -18467,252 +17101,6 @@ "node": ">=8" } }, - "node_modules/browser-sync": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.4.tgz", - "integrity": "sha512-mcYOIy4BW6sWSEnTSBjQwWsnbx2btZX78ajTTjdNfyC/EqQVcIe0nQR6894RNAMtvlfAnLaH9L2ka97zpvgenA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "browser-sync-client": "^3.0.4", - "browser-sync-ui": "^3.0.4", - "bs-recipes": "1.3.4", - "chalk": "4.1.2", - "chokidar": "^3.5.1", - "connect": "3.6.6", - "connect-history-api-fallback": "^1", - "dev-ip": "^1.0.1", - "easy-extender": "^2.3.4", - "eazy-logger": "^4.1.0", - "etag": "^1.8.1", - "fresh": "^0.5.2", - "fs-extra": "3.0.1", - "http-proxy": "^1.18.1", - "immutable": "^3", - "micromatch": "^4.0.8", - "opn": "5.3.0", - "portscanner": "2.2.0", - "raw-body": "^2.3.2", - "resp-modifier": "6.0.2", - "rx": "4.1.0", - "send": "^0.19.0", - "serve-index": "^1.9.1", - "serve-static": "^1.16.2", - "server-destroy": "1.0.1", - "socket.io": "^4.4.1", - "ua-parser-js": "^1.0.33", - "yargs": "^17.3.1" - }, - "bin": { - "browser-sync": "dist/bin.js" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/browser-sync-client": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.4.tgz", - "integrity": "sha512-+ew5ubXzGRKVjquBL3u6najS40TG7GxCdyBll0qSRc/n+JRV9gb/yDdRL1IAgRHqjnJTdqeBKKIQabjvjRSYRQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "etag": "1.8.1", - "fresh": "0.5.2", - "mitt": "^1.1.3" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/browser-sync-ui": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.4.tgz", - "integrity": "sha512-5Po3YARCZ/8yQHFzvrSjn8+hBUF7ZWac39SHsy8Tls+7tE62iq6pYWxpVU6aOOMAGD21RwFQhQeqmJPf70kHEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async-each-series": "0.1.1", - "chalk": "4.1.2", - "connect-history-api-fallback": "^1", - "immutable": "^3", - "server-destroy": "1.0.1", - "socket.io-client": "^4.4.1", - "stream-throttle": "^0.1.3" - } - }, - "node_modules/browser-sync-ui/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/browser-sync-ui/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-sync/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/browser-sync/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/browser-sync/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/browser-sync/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/browser-sync/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/browser-sync/node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/browser-sync/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/browser-sync/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-sync/node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/browserify-zlib": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", @@ -18770,13 +17158,6 @@ "node": ">= 6" } }, - "node_modules/bs-recipes": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", - "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", - "dev": true, - "license": "ISC" - }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -19102,16 +17483,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -19207,12 +17578,6 @@ "node": ">=8" } }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "license": "MIT" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -19541,13 +17906,6 @@ "node": ">=4.0.0" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true, - "license": "MIT" - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -19663,49 +18021,6 @@ "dev": true, "license": "MIT" }, - "node_modules/connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/content-disposition": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", @@ -21049,18 +19364,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -21095,18 +19398,6 @@ "node": ">= 16.0.0" } }, - "node_modules/dev-ip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", - "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", - "dev": true, - "bin": { - "dev-ip": "lib/dev-ip.js" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", @@ -21312,60 +19603,6 @@ "dev": true, "license": "MIT" }, - "node_modules/easy-extender": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", - "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", - "dev": true, - "dependencies": { - "lodash": "^4.17.10" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/eazy-logger": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.1.0.tgz", - "integrity": "sha512-+mn7lRm+Zf1UT/YaH8WXtpU6PIV2iOjzP6jgKoiaq/VNrjYKp+OHZGe2znaLgDeFkw8cL9ffuaUm+nNnzcYyGw==", - "dev": true, - "dependencies": { - "chalk": "4.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eazy-logger/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eazy-logger/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -21464,76 +19701,6 @@ "once": "^1.4.0" } }, - "node_modules/engine.io": { - "version": "6.6.9", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz", - "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "@types/ws": "^8.5.12", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.21.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz", - "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.21.0", - "xmlhttprequest-ssl": "~2.1.1" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/enhanced-resolve": { "version": "5.24.4", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.4.tgz", @@ -21668,18 +19835,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "license": "MIT" - }, - "node_modules/es-module-shims": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/es-module-shims/-/es-module-shims-1.10.1.tgz", - "integrity": "sha512-HSSkRLkqFEyX6GrCAHrSOR5iz/QzQJRqZUF7bFJOZ4aoSw0WoSggfsTIGN2yFbF8v6xjQFhT4HGP6b+0qQZmEQ==", - "license": "MIT" - }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -22324,16 +20479,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/expect-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", - "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -22803,13 +20948,6 @@ "node": "^12.20 || >= 14.13" } }, - "node_modules/fflate": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", - "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", - "dev": true, - "license": "MIT" - }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -22910,75 +21048,6 @@ "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/find-cache-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", @@ -23569,18 +21638,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fs-extra": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", - "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^3.0.0", - "universalify": "^0.1.0" - } - }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -23766,19 +21823,6 @@ "dev": true, "license": "MIT" }, - "node_modules/get-tsconfig": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", - "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -23975,16 +22019,6 @@ "devOptional": true, "license": "ISC" }, - "node_modules/graphql": { - "version": "16.14.2", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", - "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, "node_modules/gunzip-maybe": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz", @@ -24151,17 +22185,6 @@ "he": "bin/he" } }, - "node_modules/headers-polyfill": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", - "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/set-cookie-parser": "^2.4.10", - "set-cookie-parser": "^3.0.1" - } - }, "node_modules/hono": { "version": "4.12.32", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.32.tgz", @@ -24622,16 +22645,6 @@ "node": ">=0.10.0" } }, - "node_modules/immutable": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.3.tgz", - "integrity": "sha512-AUY/VyX0E5XlibOmWt10uabJzam1zlYjwiEgQSDc5+UIkFNaF9WM0JxXKaNMGf+F/ffUF+7kRKXM9A7C0xXqMg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -24965,13 +22978,6 @@ "node": ">=8" } }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true, - "license": "MIT" - }, "node_modules/is-network-error": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", @@ -24985,13 +22991,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "dev": true, - "license": "MIT" - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -25002,16 +23001,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-like": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", - "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lodash.isfinite": "^3.3.2" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -25059,16 +23048,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", @@ -26964,16 +24943,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -27274,12 +25243,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", - "dev": true - }, "node_modules/lines-and-columns": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", @@ -27505,13 +25468,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.isfinite": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", - "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -28340,13 +26296,6 @@ "node": ">= 18" } }, - "node_modules/mitt": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", - "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", - "dev": true, - "license": "MIT" - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -28411,133 +26360,6 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" } }, - "node_modules/msw": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.15.0.tgz", - "integrity": "sha512-2wQAmKkQKxRuXvYJxVhPGG0wZNBQyD06oJvxqw90XqLvptdqxdlHrFUfEteKkpaNORX3Xzc+HtEl/q0nfmN2wQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@inquirer/confirm": "^6.0.11", - "@mswjs/interceptors": "^0.41.3", - "@open-draft/deferred-promise": "^3.0.0", - "@types/statuses": "^2.0.6", - "cookie": "^1.1.1", - "graphql": "^16.13.2", - "headers-polyfill": "^5.0.1", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "path-to-regexp": "^6.3.0", - "picocolors": "^1.1.1", - "rettime": "^0.11.11", - "statuses": "^2.0.2", - "strict-event-emitter": "^0.5.1", - "tough-cookie": "^6.0.1", - "type-fest": "^5.5.0", - "until-async": "^3.0.2", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.8.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/msw/node_modules/tldts": { - "version": "7.4.9", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", - "integrity": "sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^7.4.9" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/msw/node_modules/tldts-core": { - "version": "7.4.9", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.9.tgz", - "integrity": "sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/msw/node_modules/tough-cookie": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", - "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^7.0.5" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", - "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/msw/node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -28862,16 +26684,6 @@ "node": ">=18" } }, - "node_modules/node-watch": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.4.tgz", - "integrity": "sha512-RinNxoz4W1cep1b928fuFhvAQ5ag/+1UlMDV7rbyGthBIgsiEouS4kvRayvvboxii4m8eolKOIBo3OjDqbc+uQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/nopt": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", @@ -29574,20 +27386,6 @@ "dev": true, "license": "MIT" }, - "node_modules/obug": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", - "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -29691,29 +27489,6 @@ "opener": "bin/opener-bin.js" } }, - "node_modules/opn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/opn/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -29930,50 +27705,6 @@ "dev": true, "license": "MIT" }, - "node_modules/outvariant": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", - "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", - "dev": true, - "license": "MIT" - }, - "node_modules/oxc-parser": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.142.0.tgz", - "integrity": "sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==", - "license": "MIT", - "dependencies": { - "@oxc-project/types": "^0.142.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.142.0", - "@oxc-parser/binding-android-arm64": "0.142.0", - "@oxc-parser/binding-darwin-arm64": "0.142.0", - "@oxc-parser/binding-darwin-x64": "0.142.0", - "@oxc-parser/binding-freebsd-x64": "0.142.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.142.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.142.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.142.0", - "@oxc-parser/binding-linux-arm64-musl": "0.142.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.142.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.142.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.142.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.142.0", - "@oxc-parser/binding-linux-x64-gnu": "0.142.0", - "@oxc-parser/binding-linux-x64-musl": "0.142.0", - "@oxc-parser/binding-openharmony-arm64": "0.142.0", - "@oxc-parser/binding-wasm32-wasi": "0.142.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.142.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.142.0", - "@oxc-parser/binding-win32-x64-msvc": "0.142.0" - } - }, "node_modules/oxc-resolver": { "version": "11.24.2", "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz", @@ -30390,13 +28121,6 @@ "dev": true, "license": "ISC" }, - "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -30407,13 +28131,6 @@ "node": ">=8" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/peek-stream": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", @@ -30723,63 +28440,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/playwright": { - "version": "1.62.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.0.tgz", - "integrity": "sha512-Z14dG305dgaLu6foB1TXQagFiW8JfSUIUaUuPaKQ6NtBPKF1P/qXcqfh6c6K/icPqdy37JmjbiBXf6JNg6Sylw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.62.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=20" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.62.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.0.tgz", - "integrity": "sha512-nsNRyq0r2zsG8AcRHWknc9QRA5XCueC7gWMrs+Gx2tlZn9hcl8zudfh00lhJPY1DE7NmZ6bDsT9g2yey8mXljA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/pngjs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", - "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.19.0" - } - }, "node_modules/portfinder": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", @@ -30794,31 +28454,6 @@ "node": ">= 10.12" } }, - "node_modules/portscanner": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", - "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "async": "^2.6.0", - "is-number-like": "^1.0.3" - }, - "engines": { - "node": ">=0.4", - "npm": ">=1.0.0" - } - }, - "node_modules/portscanner/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/postcss": { "version": "8.5.24", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.24.tgz", @@ -32918,16 +30553,6 @@ "node": ">=8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/resolve-url-loader": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", @@ -32980,36 +30605,6 @@ "node": ">=10" } }, - "node_modules/resp-modifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", - "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", - "dev": true, - "dependencies": { - "debug": "^2.2.0", - "minimatch": "^3.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/resp-modifier/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/resp-modifier/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/responselike": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-4.0.2.tgz", @@ -33057,13 +30652,6 @@ "node": ">= 4" } }, - "node_modules/rettime": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", - "integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -33160,49 +30748,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rollup-plugin-esbuild": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-esbuild/-/rollup-plugin-esbuild-6.2.1.tgz", - "integrity": "sha512-jTNOMGoMRhs0JuueJrJqbW8tOwxumaWYq+V5i+PD+8ecSCVkuX27tGW7BXqDgoULQ55rO7IdNxPcnsWtshz3AA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "get-tsconfig": "^4.10.0", - "unplugin-utils": "^0.2.4" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "esbuild": ">=0.18.0", - "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" - } - }, - "node_modules/rollup-plugin-node-externals": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-8.1.2.tgz", - "integrity": "sha512-EuB6/lolkMLK16gvibUjikERq5fCRVIGwD2xue/CrM8D0pz5GXD2V6N8IrgxegwbcUoKkUFI8VYCEEv8MMvgpA==", - "dev": true, - "funding": [ - { - "type": "patreon", - "url": "https://patreon.com/Septh" - }, - { - "type": "paypal", - "url": "https://paypal.me/septh07" - } - ], - "license": "MIT", - "engines": { - "node": ">= 21 || ^20.6.0 || ^18.19.0" - }, - "peerDependencies": { - "rollup": "^4.0.0" - } - }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -33275,13 +30820,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -34193,20 +31731,6 @@ "node": ">= 0.8.0" } }, - "node_modules/server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/set-cookie-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", - "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", - "dev": true, - "license": "MIT" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -34346,13 +31870,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -34384,21 +31901,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/sirv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -34479,90 +31981,6 @@ "url": "https://github.com/sponsors/cyyynthia" } }, - "node_modules/socket.io": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", - "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", - "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "~4.4.1", - "ws": "~8.21.0" - } - }, - "node_modules/socket.io-client": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", - "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", - "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -34884,13 +32302,6 @@ "node": ">=8" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -34901,13 +32312,6 @@ "node": ">= 0.8" } }, - "node_modules/std-env": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", - "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", - "dev": true, - "license": "MIT" - }, "node_modules/stdin-discarder": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", @@ -34938,30 +32342,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stream-throttle": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", - "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "commander": "^2.2.0", - "limiter": "^1.0.5" - }, - "bin": { - "throttleproxy": "bin/throttleproxy.js" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/stream-throttle/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, "node_modules/streamx": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", @@ -34974,13 +32354,6 @@ "text-decoder": "^1.1.0" } }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true, - "license": "MIT" - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -35721,23 +33094,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -35755,16 +33111,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyrainbow": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", - "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/tldts": { "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", @@ -35844,16 +33190,6 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", @@ -36495,33 +33831,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/ua-parser-js": { - "version": "1.0.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", - "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -36645,16 +33954,6 @@ "node": ">= 0.8.0" } }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unix-crypt-td-js": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", @@ -36672,23 +33971,6 @@ "node": ">= 0.8" } }, - "node_modules/unplugin-utils": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.2.5.tgz", - "integrity": "sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pathe": "^2.0.3", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, "node_modules/unrs-resolver": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", @@ -36727,16 +34009,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, - "node_modules/until-async": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", - "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/kettanaito" - } - }, "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", @@ -37710,103 +34982,6 @@ } } }, - "node_modules/vitest": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", - "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.10", - "@vitest/mocker": "4.1.10", - "@vitest/pretty-format": "4.1.10", - "@vitest/runner": "4.1.10", - "@vitest/snapshot": "4.1.10", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.10", - "@vitest/browser-preview": "4.1.10", - "@vitest/browser-webdriverio": "4.1.10", - "@vitest/coverage-istanbul": "4.1.10", - "@vitest/coverage-v8": "4.1.10", - "@vitest/ui": "4.1.10", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, - "node_modules/vitest/node_modules/es-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", - "dev": true, - "license": "MIT" - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -38883,23 +36058,6 @@ "dev": true, "license": "ISC" }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -39120,15 +36278,6 @@ "dev": true, "license": "MIT" }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 3062ae5a..9cd987b3 100644 --- a/package.json +++ b/package.json @@ -5,18 +5,11 @@ "scripts": { "ng": "nx", "nx": "nx", - "build": "npm run build:mf && npm run build:nf", - "build:mf": "nx run-many -t build --projects=tag:scope:mf* && nx run mf:post-build", - "build:nf": "nx run-many -t build --projects=tag:scope:nf* && nx run native-federation:post-build", + "build": "nx run-many -t build --projects=tag:scope:mf* && nx run mf:post-build", "lint": "nx workspace-lint && ng lint", "e2e": "ng e2e", - "test:e2e:runtime": "cd libs/native-federation-runtime && npm run test:e2e", - "publish": "npm run publish:mf && npm run publish:nf", - "publish:mf": "nx run-many -t publish --projects=tag:scope:mf*", - "publish:nf": "nx run-many -t publish --projects=tag:scope:nf*", - "publish-local": "npm run publish-local:mf && npm run publish-local:nf", - "publish-local:mf": "nx run-many -t publish-local --projects=tag:scope:mf*", - "publish-local:nf": "nx run-many -t publish-local --projects=tag:scope:nf*", + "publish": "nx run-many -t publish --projects=tag:scope:mf*", + "publish-local": "nx run-many -t publish-local --projects=tag:scope:mf*", "affected:apps": "nx affected:apps", "affected:libs": "nx affected:libs", "affected:build": "nx affected:build", @@ -34,7 +27,8 @@ "help": "nx help" }, "overrides": { - "minimatch": ">3.1.3 || >10.2.1" + "minimatch": ">3.1.3 || >10.2.1", + "esbuild": ">=0.27.1 <0.27.4 || >0.27.4" }, "precommit": "format:write", "private": true, @@ -47,11 +41,9 @@ "@angular/platform-browser": "^22.0.0", "@angular/platform-browser-dynamic": "^22.0.0", "@angular/router": "^22.0.0", - "@chialab/esbuild-plugin-commonjs": "^0.19.0", "@module-federation/enhanced": "^2.2.2", "@module-federation/runtime-core": "^2.2.2", "@rsbuild/core": "^1.1.13", - "es-module-shims": "^1.5.12", "rxjs": "^7.8.2", "tslib": "^2.3.0", "zone.js": "^0.16.0" @@ -78,44 +70,28 @@ "@nx/jest": "~23.1.0", "@nx/js": "~23.1.0", "@nx/node": "~23.1.0", - "@nx/plugin": "~23.1.0", - "@nx/vite": "~23.1.0", - "@nx/vitest": "~23.1.0", "@nx/web": "~23.1.0", "@nx/webpack": "~23.1.0", "@nx/workspace": "~23.1.0", - "@playwright/test": "^1.60.0", - "@rollup/plugin-commonjs": "^28.0.3", - "@rollup/plugin-json": "^6.0.0", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-replace": "^6.0.2", "@schematics/angular": "^22.0.0", - "@softarc/sheriff-core": "^0.19.2", "@swc-node/register": "1.11.1", "@swc/cli": "~0.8.0", "@swc/core": "~1.15.1", "@swc/helpers": "~0.5.12", - "@types/browser-sync": "^2.29.0", "@types/jest": "^30.0.0", "@types/node": "^24.9.0", "@types/trusted-types": "^2.0.7", "@typescript-eslint/eslint-plugin": "^8.58.0", "@typescript-eslint/parser": "^8.58.0", "@typescript-eslint/utils": "^8.58.0", - "@vitest/browser": "^4.0.0", - "@vitest/browser-playwright": "^4.0.18", - "@vitest/ui": "^4.0.0", - "acorn": "^8.8.1", "angular-eslint": "^22.0.0", "autoprefixer": "^10.4.0", - "browser-sync": "^3.0.2", "callsite": "^1.0.0", "chalk": "^5.6.2", "chokidar": "^5.0.0", "cross-spawn": "^7.0.6", "cypress": "15.10.0", "dotenv": "^17.3.1", - "esbuild": ">=0.27.1 <0.27.4 || >0.27.4", "eslint": "~9.38.0", "eslint-config-prettier": "~10.1.8", "eslint-plugin-cypress": "~5.2.0", @@ -126,11 +102,8 @@ "jest-preset-angular": "~17.0.0", "json5": "^2.2.0", "jsonc-eslint-parser": "^2.1.0", - "mrmime": "^2.0.1", - "msw": "^2.12.3", "ng-packagr": "^22.0.0", "node-fetch": "^3.3.2", - "node-watch": "^0.7.3", "nx": "~23.0.0", "postcss": "^8.3.9", "postcss-import": "~16.1.0", @@ -138,9 +111,6 @@ "postcss-url": "10.1.3", "pre-commit": "1.2.2", "prettier": "^3.6.2", - "rollup": "^4.52.0", - "rollup-plugin-esbuild": "^6.2.1", - "rollup-plugin-node-externals": "^8.1.1", "semver": "~7.7.1", "ts-jest": "29.4.11", "ts-node": "~10.9.1", @@ -148,8 +118,6 @@ "typescript": "~6.0.0", "typescript-eslint": "^8.58.0", "verdaccio": "^6.2.1", - "vite": "^7.0.0", - "vitest": "^4.0.0", "word-wrap": "^1.2.5" }, "nx": { diff --git a/serve.js b/serve.js deleted file mode 100644 index 43c4fde2..00000000 --- a/serve.js +++ /dev/null @@ -1,31 +0,0 @@ -const dist = 'dist/apps/playground'; -const src = 'apps/playground'; -const appName = 'playground'; -const port = 3000; - -const watch = require('node-watch'); -const spawn = require('cross-spawn'); - -var browserSync = require('browser-sync'); - -function build(appName) { - spawn.sync('npx', ['nx', 'build', appName], { stdio: 'inherit' }); -} - -build(appName); - -const bsInstance = browserSync.create(); -bsInstance.init({ - server: { - baseDir: dist, - index: 'index.html', - }, - port: port, - cors: true, - browser: true, -}); - -watch(src, { recursive: true }, () => { - build(appName); - bsInstance.reload(); -}); diff --git a/tsconfig.base.json b/tsconfig.base.json index fbfd2173..635883b7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -31,27 +31,9 @@ "@angular-architects/module-federation/rsbuild": [ "libs/mf/rsbuild/src/index.ts" ], - "@angular-architects/native-federation": [ - "libs/native-federation/src/index.ts" - ], "@angular-architects/playground-lib": [ "libs/playground-lib/src/index.ts" ], - "@softarc/native-federation": [ - "libs/native-federation-core/src/index.ts" - ], - "@softarc/native-federation-esbuild": [ - "libs/native-federation-esbuild/src/index.ts" - ], - "@softarc/native-federation-node": [ - "libs/native-federation-node/src/index.ts" - ], - "@softarc/native-federation-runtime": [ - "libs/native-federation-runtime/src/index.ts" - ], - "@softarc/native-federation/build": [ - "libs/native-federation-core/build.ts" - ], "mf-rsbuild": ["mf-rsbuild/src/index.ts"] } }, diff --git a/update-local-nf.sh b/update-local-nf.sh deleted file mode 100644 index 61d91728..00000000 --- a/update-local-nf.sh +++ /dev/null @@ -1,7 +0,0 @@ - - -npx esbuild --platform=node --bundle libs/native-federation-node/src/lib/utils/fstart.ts --format=esm --outfile=libs/native-federation-node/src/lib/utils/fstart.mjs - -node libs/native-federation/build/create-data-url.js -node libs/native-federation-node/build/create-data-url.js -npm run publish-local:nf From febe794a50e0a179a5f3c39d6a3b3d117349e3d6 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Thu, 6 Aug 2026 11:44:46 +0200 Subject: [PATCH 2/2] chore(demo): Add a webpack Module Federation demo setup Fills the gap left by 6faeafe, which removed the mfe1, mfe2 and playground apps -- all three Native Federation demos despite their names -- and noted that Module Federation demos would be rebuilt separately. A dynamic host and two remotes, all built against the sources in libs/: - shell (:4200) declares no build-time remotes. initFederation() fetches public/mf.manifest.json before Angular boots, so remotes can move or be added without rebuilding the host. - mfe1 (:4201) exposes './routes' and owns everything under /flights, while mfe2 (:4202) exposes './Component' and the shell routes to it directly. Both loadRemoteModule granularities are covered. - libs/playground-lib is shared as a singleton through sharedMappings, which is what 6faeafe kept it for. A name typed into the shell's login box is read back by both remotes out of the same AuthService instance. - Each app also boots standalone on its own port. `nx serve shell` starts all three, since the remotes are serve-time dependencies of the host. Angular 22 standalone components and route-level federation throughout, rather than the NgModule-era boilerplate the old demos carried. Verified in headless Chrome over CDP in both development and production configurations: both remotes render inside the shell, both observe the shell-set AuthService.userName, each remoteEntry.js loads cross-origin while @angular/core is downloaded from a single origin, and both remotes still boot standalone. Cypress cannot drive this -- its script rewriting breaks import.meta in the type="module" output the plugin emits. Three concessions to living inside the plugin's own repository, all documented in apps/README.md: - The webpack configs require ../../dist/libs/mf/webpack instead of @angular-architects/module-federation/webpack, since the package is never installed here. Both the build and serve targets dependsOn mf:build so the output exists before webpack loads the config. - The shell aliases the runtime to libs/mf/src and libs/mf-runtime/src rather than dist/. Building mf also rebuilds mf-runtime, and ng-packagr recreates dist/libs/mf-runtime, which yanks the module out from under a dev-server that is already watching it. Sources also mean editing a library live-reloads the demo. - shell/tsconfig.app.json sets strict: false, because libs/mf-runtime is itself compiled with strict: false and violates both strictNullChecks and noImplicitAny, which a strict program cannot consume. Only the app build is relaxed -- the specs still type-check strictly, and mfe1 and mfe2 stay fully strict. Making libs/mf-runtime strict-clean would remove this; it is 9 errors, all in dynamic-federation.ts, and the enhanced entry point is already clean. The configs match what init-webpack generates for an Nx workspace (@nx/angular:webpack-browser and :dev-server, commonChunk: false, extractLicenses: false, a CORS header per remote), but they were written by hand rather than produced by running the schematic, so they do not yet double as end-to-end coverage of it the way 6faeafe intended. One known cosmetic defect, documented rather than worked around: 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 runtime boilerplate, global CSS still applies through the separate stylesheet link, and production builds emit no styles script at all. --- README.md | 2 + apps/README.md | 99 ++++++++++++++++ apps/mfe1/eslint.config.mjs | 30 +++++ apps/mfe1/jest.config.ts | 22 ++++ apps/mfe1/project.json | 100 ++++++++++++++++ apps/mfe1/src/app/app.component.ts | 16 +++ apps/mfe1/src/app/app.routes.ts | 17 +++ .../flights/flight-search.component.spec.ts | 18 +++ .../app/flights/flight-search.component.ts | 39 +++++++ apps/mfe1/src/app/flights/flights.routes.ts | 15 +++ apps/mfe1/src/app/home/home.component.ts | 12 ++ apps/mfe1/src/bootstrap.ts | 9 ++ apps/mfe1/src/index.html | 12 ++ apps/mfe1/src/main.ts | 3 + apps/mfe1/src/polyfills.ts | 1 + apps/mfe1/src/styles.css | 8 ++ apps/mfe1/src/test-setup.ts | 2 + apps/mfe1/tsconfig.app.json | 10 ++ apps/mfe1/tsconfig.editor.json | 7 ++ apps/mfe1/tsconfig.json | 31 +++++ apps/mfe1/tsconfig.spec.json | 10 ++ apps/mfe1/webpack.config.js | 25 ++++ apps/mfe1/webpack.prod.config.js | 1 + apps/mfe2/eslint.config.mjs | 30 +++++ apps/mfe2/jest.config.ts | 22 ++++ apps/mfe2/project.json | 100 ++++++++++++++++ apps/mfe2/src/app/app.component.ts | 12 ++ apps/mfe2/src/app/app.routes.ts | 12 ++ .../app/dashboard/dashboard.component.spec.ts | 16 +++ .../src/app/dashboard/dashboard.component.ts | 26 +++++ apps/mfe2/src/bootstrap.ts | 9 ++ apps/mfe2/src/index.html | 12 ++ apps/mfe2/src/main.ts | 3 + apps/mfe2/src/polyfills.ts | 1 + apps/mfe2/src/styles.css | 8 ++ apps/mfe2/src/test-setup.ts | 2 + apps/mfe2/tsconfig.app.json | 10 ++ apps/mfe2/tsconfig.editor.json | 7 ++ apps/mfe2/tsconfig.json | 31 +++++ apps/mfe2/tsconfig.spec.json | 10 ++ apps/mfe2/webpack.config.js | 26 +++++ apps/mfe2/webpack.prod.config.js | 1 + apps/shell/eslint.config.mjs | 30 +++++ apps/shell/jest.config.ts | 22 ++++ apps/shell/project.json | 107 ++++++++++++++++++ apps/shell/public/mf.manifest.json | 4 + apps/shell/src/app/app.component.spec.ts | 27 +++++ apps/shell/src/app/app.component.ts | 48 ++++++++ apps/shell/src/app/app.routes.ts | 39 +++++++ apps/shell/src/app/home/home.component.ts | 18 +++ apps/shell/src/app/not-found.component.ts | 7 ++ apps/shell/src/bootstrap.ts | 9 ++ apps/shell/src/index.html | 12 ++ apps/shell/src/main.ts | 9 ++ apps/shell/src/polyfills.ts | 1 + apps/shell/src/styles.css | 8 ++ apps/shell/src/test-setup.ts | 2 + apps/shell/tsconfig.app.json | 17 +++ apps/shell/tsconfig.editor.json | 7 ++ apps/shell/tsconfig.json | 31 +++++ apps/shell/tsconfig.spec.json | 10 ++ apps/shell/webpack.config.js | 41 +++++++ apps/shell/webpack.prod.config.js | 1 + 63 files changed, 1277 insertions(+) create mode 100644 apps/README.md create mode 100644 apps/mfe1/eslint.config.mjs create mode 100644 apps/mfe1/jest.config.ts create mode 100644 apps/mfe1/project.json create mode 100644 apps/mfe1/src/app/app.component.ts create mode 100644 apps/mfe1/src/app/app.routes.ts create mode 100644 apps/mfe1/src/app/flights/flight-search.component.spec.ts create mode 100644 apps/mfe1/src/app/flights/flight-search.component.ts create mode 100644 apps/mfe1/src/app/flights/flights.routes.ts create mode 100644 apps/mfe1/src/app/home/home.component.ts create mode 100644 apps/mfe1/src/bootstrap.ts create mode 100644 apps/mfe1/src/index.html create mode 100644 apps/mfe1/src/main.ts create mode 100644 apps/mfe1/src/polyfills.ts create mode 100644 apps/mfe1/src/styles.css create mode 100644 apps/mfe1/src/test-setup.ts create mode 100644 apps/mfe1/tsconfig.app.json create mode 100644 apps/mfe1/tsconfig.editor.json create mode 100644 apps/mfe1/tsconfig.json create mode 100644 apps/mfe1/tsconfig.spec.json create mode 100644 apps/mfe1/webpack.config.js create mode 100644 apps/mfe1/webpack.prod.config.js create mode 100644 apps/mfe2/eslint.config.mjs create mode 100644 apps/mfe2/jest.config.ts create mode 100644 apps/mfe2/project.json create mode 100644 apps/mfe2/src/app/app.component.ts create mode 100644 apps/mfe2/src/app/app.routes.ts create mode 100644 apps/mfe2/src/app/dashboard/dashboard.component.spec.ts create mode 100644 apps/mfe2/src/app/dashboard/dashboard.component.ts create mode 100644 apps/mfe2/src/bootstrap.ts create mode 100644 apps/mfe2/src/index.html create mode 100644 apps/mfe2/src/main.ts create mode 100644 apps/mfe2/src/polyfills.ts create mode 100644 apps/mfe2/src/styles.css create mode 100644 apps/mfe2/src/test-setup.ts create mode 100644 apps/mfe2/tsconfig.app.json create mode 100644 apps/mfe2/tsconfig.editor.json create mode 100644 apps/mfe2/tsconfig.json create mode 100644 apps/mfe2/tsconfig.spec.json create mode 100644 apps/mfe2/webpack.config.js create mode 100644 apps/mfe2/webpack.prod.config.js create mode 100644 apps/shell/eslint.config.mjs create mode 100644 apps/shell/jest.config.ts create mode 100644 apps/shell/project.json create mode 100644 apps/shell/public/mf.manifest.json create mode 100644 apps/shell/src/app/app.component.spec.ts create mode 100644 apps/shell/src/app/app.component.ts create mode 100644 apps/shell/src/app/app.routes.ts create mode 100644 apps/shell/src/app/home/home.component.ts create mode 100644 apps/shell/src/app/not-found.component.ts create mode 100644 apps/shell/src/bootstrap.ts create mode 100644 apps/shell/src/index.html create mode 100644 apps/shell/src/main.ts create mode 100644 apps/shell/src/polyfills.ts create mode 100644 apps/shell/src/styles.css create mode 100644 apps/shell/src/test-setup.ts create mode 100644 apps/shell/tsconfig.app.json create mode 100644 apps/shell/tsconfig.editor.json create mode 100644 apps/shell/tsconfig.json create mode 100644 apps/shell/tsconfig.spec.json create mode 100644 apps/shell/webpack.config.js create mode 100644 apps/shell/webpack.prod.config.js diff --git a/README.md b/README.md index 6e6a03fc..8ecf8801 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ - [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 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/eslint.config.mjs b/apps/mfe1/eslint.config.mjs new file mode 100644 index 00000000..6aaf44f9 --- /dev/null +++ b/apps/mfe1/eslint.config.mjs @@ -0,0 +1,30 @@ +import baseConfig from '../../eslint.config.mjs'; +import nx from '@nx/eslint-plugin'; + +export default [ + ...baseConfig, + ...nx.configs['flat/angular'], + { + files: ['**/*.ts'], + rules: { + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: 'angularArchitects', + style: 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: 'angular-architects', + style: 'kebab-case', + }, + ], + '@angular-eslint/prefer-standalone': 'off', + }, + }, + ...nx.configs['flat/angular-template'], +]; diff --git a/apps/mfe1/jest.config.ts b/apps/mfe1/jest.config.ts new file mode 100644 index 00000000..f6f7c8bb --- /dev/null +++ b/apps/mfe1/jest.config.ts @@ -0,0 +1,22 @@ +export default { + displayName: 'mfe1', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../coverage/apps/mfe1', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + 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/mfe1/project.json b/apps/mfe1/project.json new file mode 100644 index 00000000..d857e059 --- /dev/null +++ b/apps/mfe1/project.json @@ -0,0 +1,100 @@ +{ + "name": "mfe1", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/mfe1/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/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": [], + "styles": ["apps/mfe1/src/styles.css"], + "scripts": [], + "commonChunk": false, + "extractLicenses": false + }, + "configurations": { + "production": { + "customWebpackConfig": { + "path": "apps/mfe1/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, + "options": { + "port": 4201, + "publicHost": "http://localhost:4201", + "headers": { + "Access-Control-Allow-Origin": "*" + } + }, + "configurations": { + "production": { + "buildTarget": "mfe1:build:production" + }, + "development": { + "buildTarget": "mfe1:build:development" + } + }, + "dependsOn": [ + { + "projects": ["mf"], + "target": "build" + } + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "options": { + "lintFilePatterns": ["apps/mfe1/**/*.ts", "apps/mfe1/**/*.html"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/apps/mfe1"], + "options": { + "jestConfig": "apps/mfe1/jest.config.ts" + } + } + } +} diff --git a/apps/mfe1/src/app/app.component.ts b/apps/mfe1/src/app/app.component.ts new file mode 100644 index 00000000..cedb9be8 --- /dev/null +++ b/apps/mfe1/src/app/app.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'angular-architects-root', + imports: [RouterOutlet, RouterLink], + template: ` +

mfe1 — standalone

+
+ + `, +}) +export class AppComponent {} 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/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)' }} +

+ + + + +
    + @for (flight of flights; track flight.id) { +
  • {{ flight.from }} → {{ flight.to }}
  • + } +
+ `, +}) +export class FlightSearchComponent { + private authService = inject(AuthService); + + userName = this.authService.userName; + + flights = [ + { id: 1, from: 'Amsterdam', to: 'Vienna' }, + { id: 2, from: 'Vienna', to: 'Graz' }, + { id: 3, from: 'Graz', to: 'Amsterdam' }, + ]; +} diff --git a/apps/mfe1/src/app/flights/flights.routes.ts b/apps/mfe1/src/app/flights/flights.routes.ts new file mode 100644 index 00000000..a381db7a --- /dev/null +++ b/apps/mfe1/src/app/flights/flights.routes.ts @@ -0,0 +1,15 @@ +import { Routes } from '@angular/router'; + +// Exposed as './routes' — see apps/mfe1/webpack.config.js. +export const FLIGHT_ROUTES: Routes = [ + { + path: '', + pathMatch: 'full', + redirectTo: 'search', + }, + { + path: 'search', + loadComponent: () => + import('./flight-search.component').then((m) => m.FlightSearchComponent), + }, +]; diff --git a/apps/mfe1/src/app/home/home.component.ts b/apps/mfe1/src/app/home/home.component.ts new file mode 100644 index 00000000..0fb99eb1 --- /dev/null +++ b/apps/mfe1/src/app/home/home.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'angular-architects-mfe1-home', + template: ` +

+ This page only shows up when mfe1 runs on its own. The shell never loads + it — it only pulls in the exposed ./routes. +

+ `, +}) +export class HomeComponent {} diff --git a/apps/mfe1/src/bootstrap.ts b/apps/mfe1/src/bootstrap.ts new file mode 100644 index 00000000..0791995c --- /dev/null +++ b/apps/mfe1/src/bootstrap.ts @@ -0,0 +1,9 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { provideRouter } from '@angular/router'; + +import { AppComponent } from './app/app.component'; +import { APP_ROUTES } from './app/app.routes'; + +bootstrapApplication(AppComponent, { + providers: [provideRouter(APP_ROUTES)], +}).catch((err) => console.error(err)); diff --git a/apps/mfe1/src/index.html b/apps/mfe1/src/index.html new file mode 100644 index 00000000..8ff2e44f --- /dev/null +++ b/apps/mfe1/src/index.html @@ -0,0 +1,12 @@ + + + + + mfe1 (remote) + + + + + + + diff --git a/apps/mfe1/src/main.ts b/apps/mfe1/src/main.ts new file mode 100644 index 00000000..d7d2e9ae --- /dev/null +++ b/apps/mfe1/src/main.ts @@ -0,0 +1,3 @@ +// 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 new file mode 100644 index 00000000..aa09a9ff --- /dev/null +++ b/apps/mfe1/src/polyfills.ts @@ -0,0 +1 @@ +import 'zone.js'; diff --git a/apps/mfe1/src/styles.css b/apps/mfe1/src/styles.css new file mode 100644 index 00000000..8c780472 --- /dev/null +++ b/apps/mfe1/src/styles.css @@ -0,0 +1,8 @@ +body { + font-family: system-ui, sans-serif; + margin: 1rem; +} + +nav a { + margin-right: 1rem; +} diff --git a/apps/mfe1/src/test-setup.ts b/apps/mfe1/src/test-setup.ts new file mode 100644 index 00000000..4555f138 --- /dev/null +++ b/apps/mfe1/src/test-setup.ts @@ -0,0 +1,2 @@ +import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +setupZoneTestEnv(); diff --git a/apps/mfe1/tsconfig.app.json b/apps/mfe1/tsconfig.app.json new file mode 100644 index 00000000..7d7a8070 --- /dev/null +++ b/apps/mfe1/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["**/*.test.ts", "**/*.spec.ts", "jest.config.ts"] +} diff --git a/apps/mfe1/tsconfig.editor.json b/apps/mfe1/tsconfig.editor.json new file mode 100644 index 00000000..20c4afdb --- /dev/null +++ b/apps/mfe1/tsconfig.editor.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "types": ["jest", "node"] + } +} diff --git a/apps/mfe1/tsconfig.json b/apps/mfe1/tsconfig.json new file mode 100644 index 00000000..c756d846 --- /dev/null +++ b/apps/mfe1/tsconfig.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "compilerOptions": { + "target": "es2020", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/mfe1/tsconfig.spec.json b/apps/mfe1/tsconfig.spec.json new file mode 100644 index 00000000..c5db0277 --- /dev/null +++ b/apps/mfe1/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} 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/eslint.config.mjs b/apps/mfe2/eslint.config.mjs new file mode 100644 index 00000000..6aaf44f9 --- /dev/null +++ b/apps/mfe2/eslint.config.mjs @@ -0,0 +1,30 @@ +import baseConfig from '../../eslint.config.mjs'; +import nx from '@nx/eslint-plugin'; + +export default [ + ...baseConfig, + ...nx.configs['flat/angular'], + { + files: ['**/*.ts'], + rules: { + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: 'angularArchitects', + style: 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: 'angular-architects', + style: 'kebab-case', + }, + ], + '@angular-eslint/prefer-standalone': 'off', + }, + }, + ...nx.configs['flat/angular-template'], +]; diff --git a/apps/mfe2/jest.config.ts b/apps/mfe2/jest.config.ts new file mode 100644 index 00000000..2d9d75c0 --- /dev/null +++ b/apps/mfe2/jest.config.ts @@ -0,0 +1,22 @@ +export default { + displayName: 'mfe2', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../coverage/apps/mfe2', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + 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/mfe2/project.json b/apps/mfe2/project.json new file mode 100644 index 00000000..f49855ff --- /dev/null +++ b/apps/mfe2/project.json @@ -0,0 +1,100 @@ +{ + "name": "mfe2", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/mfe2/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/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": [], + "styles": ["apps/mfe2/src/styles.css"], + "scripts": [], + "commonChunk": false, + "extractLicenses": false + }, + "configurations": { + "production": { + "customWebpackConfig": { + "path": "apps/mfe2/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, + "options": { + "port": 4202, + "publicHost": "http://localhost:4202", + "headers": { + "Access-Control-Allow-Origin": "*" + } + }, + "configurations": { + "production": { + "buildTarget": "mfe2:build:production" + }, + "development": { + "buildTarget": "mfe2:build:development" + } + }, + "dependsOn": [ + { + "projects": ["mf"], + "target": "build" + } + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "options": { + "lintFilePatterns": ["apps/mfe2/**/*.ts", "apps/mfe2/**/*.html"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/apps/mfe2"], + "options": { + "jestConfig": "apps/mfe2/jest.config.ts" + } + } + } +} diff --git a/apps/mfe2/src/app/app.component.ts b/apps/mfe2/src/app/app.component.ts new file mode 100644 index 00000000..779c77b4 --- /dev/null +++ b/apps/mfe2/src/app/app.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'angular-architects-root', + imports: [RouterOutlet], + template: ` +

mfe2 — standalone

+ + `, +}) +export class AppComponent {} 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)' }} +

+ + + `, +}) +export class DashboardComponent { + private authService = inject(AuthService); + + userName = this.authService.userName; +} diff --git a/apps/mfe2/src/bootstrap.ts b/apps/mfe2/src/bootstrap.ts new file mode 100644 index 00000000..0791995c --- /dev/null +++ b/apps/mfe2/src/bootstrap.ts @@ -0,0 +1,9 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { provideRouter } from '@angular/router'; + +import { AppComponent } from './app/app.component'; +import { APP_ROUTES } from './app/app.routes'; + +bootstrapApplication(AppComponent, { + providers: [provideRouter(APP_ROUTES)], +}).catch((err) => console.error(err)); diff --git a/apps/mfe2/src/index.html b/apps/mfe2/src/index.html new file mode 100644 index 00000000..48335b09 --- /dev/null +++ b/apps/mfe2/src/index.html @@ -0,0 +1,12 @@ + + + + + mfe2 (remote) + + + + + + + diff --git a/apps/mfe2/src/main.ts b/apps/mfe2/src/main.ts new file mode 100644 index 00000000..d7d2e9ae --- /dev/null +++ b/apps/mfe2/src/main.ts @@ -0,0 +1,3 @@ +// 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 new file mode 100644 index 00000000..aa09a9ff --- /dev/null +++ b/apps/mfe2/src/polyfills.ts @@ -0,0 +1 @@ +import 'zone.js'; diff --git a/apps/mfe2/src/styles.css b/apps/mfe2/src/styles.css new file mode 100644 index 00000000..8c780472 --- /dev/null +++ b/apps/mfe2/src/styles.css @@ -0,0 +1,8 @@ +body { + font-family: system-ui, sans-serif; + margin: 1rem; +} + +nav a { + margin-right: 1rem; +} diff --git a/apps/mfe2/src/test-setup.ts b/apps/mfe2/src/test-setup.ts new file mode 100644 index 00000000..4555f138 --- /dev/null +++ b/apps/mfe2/src/test-setup.ts @@ -0,0 +1,2 @@ +import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +setupZoneTestEnv(); diff --git a/apps/mfe2/tsconfig.app.json b/apps/mfe2/tsconfig.app.json new file mode 100644 index 00000000..7d7a8070 --- /dev/null +++ b/apps/mfe2/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["**/*.test.ts", "**/*.spec.ts", "jest.config.ts"] +} diff --git a/apps/mfe2/tsconfig.editor.json b/apps/mfe2/tsconfig.editor.json new file mode 100644 index 00000000..20c4afdb --- /dev/null +++ b/apps/mfe2/tsconfig.editor.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "types": ["jest", "node"] + } +} diff --git a/apps/mfe2/tsconfig.json b/apps/mfe2/tsconfig.json new file mode 100644 index 00000000..c756d846 --- /dev/null +++ b/apps/mfe2/tsconfig.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "compilerOptions": { + "target": "es2020", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/mfe2/tsconfig.spec.json b/apps/mfe2/tsconfig.spec.json new file mode 100644 index 00000000..c5db0277 --- /dev/null +++ b/apps/mfe2/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} 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/shell/eslint.config.mjs b/apps/shell/eslint.config.mjs new file mode 100644 index 00000000..6aaf44f9 --- /dev/null +++ b/apps/shell/eslint.config.mjs @@ -0,0 +1,30 @@ +import baseConfig from '../../eslint.config.mjs'; +import nx from '@nx/eslint-plugin'; + +export default [ + ...baseConfig, + ...nx.configs['flat/angular'], + { + files: ['**/*.ts'], + rules: { + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: 'angularArchitects', + style: 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: 'angular-architects', + style: 'kebab-case', + }, + ], + '@angular-eslint/prefer-standalone': 'off', + }, + }, + ...nx.configs['flat/angular-template'], +]; diff --git a/apps/shell/jest.config.ts b/apps/shell/jest.config.ts new file mode 100644 index 00000000..5f73a87c --- /dev/null +++ b/apps/shell/jest.config.ts @@ -0,0 +1,22 @@ +export default { + displayName: 'shell', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../coverage/apps/shell', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + 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: ` +

shell — dynamic host

+ + + + + +
+ + + `, + styles: ` + label { + display: block; + margin: 1rem 0; + } + nav { + margin-bottom: 1rem; + } + .active { + font-weight: bold; + } + `, +}) +export class AppComponent { + // Public so the template can bind straight to the shared singleton. + authService = inject(AuthService); +} diff --git a/apps/shell/src/app/app.routes.ts b/apps/shell/src/app/app.routes.ts new file mode 100644 index 00000000..272cf6c3 --- /dev/null +++ b/apps/shell/src/app/app.routes.ts @@ -0,0 +1,39 @@ +import { loadRemoteModule } from '@angular-architects/module-federation'; +import { Routes } from '@angular/router'; + +export const APP_ROUTES: Routes = [ + { + path: '', + pathMatch: 'full', + loadComponent: () => + import('./home/home.component').then((m) => m.HomeComponent), + }, + + // mfe1 exposes routes, so it owns everything under /flights. + { + path: 'flights', + loadChildren: () => + loadRemoteModule({ + type: 'manifest', + remoteName: 'mfe1', + exposedModule: './routes', + }).then((m) => m.FLIGHT_ROUTES), + }, + + // mfe2 exposes a single component, which the shell routes to itself. + { + path: 'dashboard', + loadComponent: () => + loadRemoteModule({ + type: 'manifest', + remoteName: 'mfe2', + exposedModule: './Component', + }).then((m) => m.DashboardComponent), + }, + + { + path: '**', + loadComponent: () => + import('./not-found.component').then((m) => m.NotFoundComponent), + }, +]; diff --git a/apps/shell/src/app/home/home.component.ts b/apps/shell/src/app/home/home.component.ts new file mode 100644 index 00000000..d6381d50 --- /dev/null +++ b/apps/shell/src/app/home/home.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'angular-architects-home', + 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: `

Not found.

`, +}) +export class NotFoundComponent {} diff --git a/apps/shell/src/bootstrap.ts b/apps/shell/src/bootstrap.ts new file mode 100644 index 00000000..0791995c --- /dev/null +++ b/apps/shell/src/bootstrap.ts @@ -0,0 +1,9 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { provideRouter } from '@angular/router'; + +import { AppComponent } from './app/app.component'; +import { APP_ROUTES } from './app/app.routes'; + +bootstrapApplication(AppComponent, { + providers: [provideRouter(APP_ROUTES)], +}).catch((err) => console.error(err)); diff --git a/apps/shell/src/index.html b/apps/shell/src/index.html new file mode 100644 index 00000000..30123164 --- /dev/null +++ b/apps/shell/src/index.html @@ -0,0 +1,12 @@ + + + + + shell (host) + + + + + + + diff --git a/apps/shell/src/main.ts b/apps/shell/src/main.ts new file mode 100644 index 00000000..5068d1fd --- /dev/null +++ b/apps/shell/src/main.ts @@ -0,0 +1,9 @@ +import { initFederation } from '@angular-architects/module-federation'; + +// The manifest is a deployed asset, not a build-time constant — that is the +// whole point of a dynamic host. Nothing may import a shared library before +// this resolves, hence the dynamic import of ./bootstrap. +initFederation('mf.manifest.json') + .catch((err) => console.error(err)) + .then(() => import('./bootstrap')) + .catch((err) => console.error(err)); diff --git a/apps/shell/src/polyfills.ts b/apps/shell/src/polyfills.ts new file mode 100644 index 00000000..aa09a9ff --- /dev/null +++ b/apps/shell/src/polyfills.ts @@ -0,0 +1 @@ +import 'zone.js'; diff --git a/apps/shell/src/styles.css b/apps/shell/src/styles.css new file mode 100644 index 00000000..8c780472 --- /dev/null +++ b/apps/shell/src/styles.css @@ -0,0 +1,8 @@ +body { + font-family: system-ui, sans-serif; + margin: 1rem; +} + +nav a { + margin-right: 1rem; +} diff --git a/apps/shell/src/test-setup.ts b/apps/shell/src/test-setup.ts new file mode 100644 index 00000000..4555f138 --- /dev/null +++ b/apps/shell/src/test-setup.ts @@ -0,0 +1,2 @@ +import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +setupZoneTestEnv(); diff --git a/apps/shell/tsconfig.app.json b/apps/shell/tsconfig.app.json new file mode 100644 index 00000000..df12f72b --- /dev/null +++ b/apps/shell/tsconfig.app.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [], + // In-repo demo only. The inherited `paths` resolve + // @angular-architects/module-federation to libs/mf-runtime/src, which is + // itself compiled with `strict: false` (see libs/mf-runtime/tsconfig.json) + // and violates both strictNullChecks and noImplicitAny. A strict program + // cannot consume those sources, so this build matches the library. Only + // tsconfig.app.json is relaxed — the specs still type-check strictly. + "strict": false + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["**/*.test.ts", "**/*.spec.ts", "jest.config.ts"] +} diff --git a/apps/shell/tsconfig.editor.json b/apps/shell/tsconfig.editor.json new file mode 100644 index 00000000..20c4afdb --- /dev/null +++ b/apps/shell/tsconfig.editor.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "types": ["jest", "node"] + } +} diff --git a/apps/shell/tsconfig.json b/apps/shell/tsconfig.json new file mode 100644 index 00000000..c756d846 --- /dev/null +++ b/apps/shell/tsconfig.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "compilerOptions": { + "target": "es2020", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/shell/tsconfig.spec.json b/apps/shell/tsconfig.spec.json new file mode 100644 index 00000000..c5db0277 --- /dev/null +++ b/apps/shell/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/apps/shell/webpack.config.js b/apps/shell/webpack.config.js new file mode 100644 index 00000000..3f846d37 --- /dev/null +++ b/apps/shell/webpack.config.js @@ -0,0 +1,41 @@ +const path = require('path'); + +// 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'); + +// No `remotes` here on purpose: this is a *dynamic* host. It learns about mfe1 +// and mfe2 at runtime from public/mf.manifest.json, so remotes can be added, +// moved or taken down without rebuilding the shell. +const config = withModuleFederationPlugin({ + name: 'shell', + + shared: { + ...shareAll({ + singleton: true, + strictVersion: true, + requiredVersion: 'auto', + }), + }, + + sharedMappings: ['@angular-architects/playground-lib'], +}); + +// In-repo demo only. Angular's webpack build resolves bare specifiers from +// node_modules and ignores tsconfig `paths`, and this repo never installs its +// own package. These mirror the two `paths` entries the shell relies on. +// They must point at sources, 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 that is already watching it. +const lib = (p) => path.join(__dirname, '../..', p); +Object.assign(config.resolve.alias, { + '@angular-architects/module-federation': lib('libs/mf/src/index.ts'), + '@angular-architects/module-federation-runtime': lib( + 'libs/mf-runtime/src/index.ts', + ), +}); + +module.exports = config; diff --git a/apps/shell/webpack.prod.config.js b/apps/shell/webpack.prod.config.js new file mode 100644 index 00000000..0c9447df --- /dev/null +++ b/apps/shell/webpack.prod.config.js @@ -0,0 +1 @@ +module.exports = require('./webpack.config');