module-federation-angular-adapter 0.2200.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +115 -0
  3. package/builders.json +15 -0
  4. package/collection.json +27 -0
  5. package/generators.json +12 -0
  6. package/migration-collection.json +6 -0
  7. package/package.json +69 -0
  8. package/src/builders/build/builder.d.ts +7 -0
  9. package/src/builders/build/builder.d.ts.map +1 -0
  10. package/src/builders/build/builder.js +413 -0
  11. package/src/builders/build/federation-build-notifier.d.ts +70 -0
  12. package/src/builders/build/federation-build-notifier.d.ts.map +1 -0
  13. package/src/builders/build/federation-build-notifier.js +186 -0
  14. package/src/builders/build/get-externals.d.ts +18 -0
  15. package/src/builders/build/get-externals.d.ts.map +1 -0
  16. package/src/builders/build/get-externals.js +19 -0
  17. package/src/builders/build/i18n.d.ts +21 -0
  18. package/src/builders/build/i18n.d.ts.map +1 -0
  19. package/src/builders/build/i18n.js +73 -0
  20. package/src/builders/build/schema.d.ts +35 -0
  21. package/src/builders/build/schema.json +93 -0
  22. package/src/builders/build/setup-builder-env-variables.d.ts +1 -0
  23. package/src/builders/build/setup-builder-env-variables.d.ts.map +1 -0
  24. package/src/builders/build/setup-builder-env-variables.js +9 -0
  25. package/src/builders/build/update-index-html.d.ts +5 -0
  26. package/src/builders/build/update-index-html.d.ts.map +1 -0
  27. package/src/builders/build/update-index-html.js +33 -0
  28. package/src/builders/remote/assets.d.ts +14 -0
  29. package/src/builders/remote/assets.d.ts.map +1 -0
  30. package/src/builders/remote/assets.js +49 -0
  31. package/src/builders/remote/builder.d.ts +13 -0
  32. package/src/builders/remote/builder.d.ts.map +1 -0
  33. package/src/builders/remote/builder.js +153 -0
  34. package/src/builders/remote/change-watcher.d.ts +10 -0
  35. package/src/builders/remote/change-watcher.d.ts.map +1 -0
  36. package/src/builders/remote/change-watcher.js +35 -0
  37. package/src/builders/remote/infer-config-path.d.ts +2 -0
  38. package/src/builders/remote/infer-config-path.d.ts.map +1 -0
  39. package/src/builders/remote/infer-config-path.js +10 -0
  40. package/src/builders/remote/resolve-ng-options.d.ts +24 -0
  41. package/src/builders/remote/resolve-ng-options.d.ts.map +1 -0
  42. package/src/builders/remote/resolve-ng-options.js +28 -0
  43. package/src/builders/remote/schema.d.ts +26 -0
  44. package/src/builders/remote/schema.json +116 -0
  45. package/src/config/angular-locales.d.ts +6 -0
  46. package/src/config/angular-locales.d.ts.map +1 -0
  47. package/src/config/angular-locales.js +23 -0
  48. package/src/config/angular-skip-list.d.ts +3 -0
  49. package/src/config/angular-skip-list.d.ts.map +1 -0
  50. package/src/config/angular-skip-list.js +23 -0
  51. package/src/config/with-module-federation.d.ts +72 -0
  52. package/src/config/with-module-federation.d.ts.map +1 -0
  53. package/src/config/with-module-federation.js +72 -0
  54. package/src/config.d.ts +4 -0
  55. package/src/config.d.ts.map +1 -0
  56. package/src/config.js +3 -0
  57. package/src/generators/native-federation/files/src/index.ts__template__ +1 -0
  58. package/src/generators/native-federation/generator.d.ts +4 -0
  59. package/src/generators/native-federation/generator.d.ts.map +1 -0
  60. package/src/generators/native-federation/generator.js +45 -0
  61. package/src/generators/native-federation/schema.d.ts +5 -0
  62. package/src/generators/native-federation/schema.json +29 -0
  63. package/src/index.d.ts +82 -0
  64. package/src/index.d.ts.map +1 -0
  65. package/src/index.js +132 -0
  66. package/src/internal.d.ts +2 -0
  67. package/src/internal.d.ts.map +1 -0
  68. package/src/internal.js +1 -0
  69. package/src/schematics/appbuilder/schema.d.ts +3 -0
  70. package/src/schematics/appbuilder/schema.json +17 -0
  71. package/src/schematics/appbuilder/schematic.d.ts +5 -0
  72. package/src/schematics/appbuilder/schematic.d.ts.map +1 -0
  73. package/src/schematics/appbuilder/schematic.js +80 -0
  74. package/src/schematics/init/files/federation.config.mjs__tmpl__ +35 -0
  75. package/src/schematics/init/schema.d.ts +5 -0
  76. package/src/schematics/init/schema.json +30 -0
  77. package/src/schematics/init/schematic.d.ts +6 -0
  78. package/src/schematics/init/schematic.d.ts.map +1 -0
  79. package/src/schematics/init/schematic.js +38 -0
  80. package/src/schematics/init/steps/add-dependencies.d.ts +3 -0
  81. package/src/schematics/init/steps/add-dependencies.d.ts.map +1 -0
  82. package/src/schematics/init/steps/add-dependencies.js +25 -0
  83. package/src/schematics/init/steps/generate-federation-config.d.ts +4 -0
  84. package/src/schematics/init/steps/generate-federation-config.d.ts.map +1 -0
  85. package/src/schematics/init/steps/generate-federation-config.js +15 -0
  86. package/src/schematics/init/steps/generate-remote-map.d.ts +2 -0
  87. package/src/schematics/init/steps/generate-remote-map.d.ts.map +1 -0
  88. package/src/schematics/init/steps/generate-remote-map.js +20 -0
  89. package/src/schematics/init/steps/make-main-async.d.ts +4 -0
  90. package/src/schematics/init/steps/make-main-async.d.ts.map +1 -0
  91. package/src/schematics/init/steps/make-main-async.js +35 -0
  92. package/src/schematics/init/steps/normalize-options.d.ts +20 -0
  93. package/src/schematics/init/steps/normalize-options.d.ts.map +1 -0
  94. package/src/schematics/init/steps/normalize-options.js +64 -0
  95. package/src/schematics/init/steps/update-package-json.d.ts +3 -0
  96. package/src/schematics/init/steps/update-package-json.d.ts.map +1 -0
  97. package/src/schematics/init/steps/update-package-json.js +19 -0
  98. package/src/schematics/init/steps/update-polyfills.d.ts +3 -0
  99. package/src/schematics/init/steps/update-polyfills.d.ts.map +1 -0
  100. package/src/schematics/init/steps/update-polyfills.js +21 -0
  101. package/src/schematics/init/steps/update-workspace-config.d.ts +4 -0
  102. package/src/schematics/init/steps/update-workspace-config.d.ts.map +1 -0
  103. package/src/schematics/init/steps/update-workspace-config.js +76 -0
  104. package/src/schematics/remove/schema.d.ts +3 -0
  105. package/src/schematics/remove/schema.json +17 -0
  106. package/src/schematics/remove/schematic.d.ts +5 -0
  107. package/src/schematics/remove/schematic.d.ts.map +1 -0
  108. package/src/schematics/remove/schematic.js +106 -0
  109. package/src/tools/esbuild/angular-bundler.d.ts +10 -0
  110. package/src/tools/esbuild/angular-bundler.d.ts.map +1 -0
  111. package/src/tools/esbuild/angular-bundler.js +134 -0
  112. package/src/tools/esbuild/create-awaitable-compiler-plugin.d.ts +6 -0
  113. package/src/tools/esbuild/create-awaitable-compiler-plugin.d.ts.map +1 -0
  114. package/src/tools/esbuild/create-awaitable-compiler-plugin.js +29 -0
  115. package/src/tools/esbuild/create-federation-tsconfig.d.ts +7 -0
  116. package/src/tools/esbuild/create-federation-tsconfig.d.ts.map +1 -0
  117. package/src/tools/esbuild/create-federation-tsconfig.js +46 -0
  118. package/src/tools/esbuild/shared-mappings-plugin.d.ts +4 -0
  119. package/src/tools/esbuild/shared-mappings-plugin.d.ts.map +1 -0
  120. package/src/tools/esbuild/shared-mappings-plugin.js +33 -0
  121. package/src/tools/mf/build-for-federation.d.ts +70 -0
  122. package/src/tools/mf/build-for-federation.d.ts.map +1 -0
  123. package/src/tools/mf/build-for-federation.js +75 -0
  124. package/src/tools/mf/federation-entry-points.d.ts +23 -0
  125. package/src/tools/mf/federation-entry-points.d.ts.map +1 -0
  126. package/src/tools/mf/federation-entry-points.js +13 -0
  127. package/src/tools/mf/federation-plugin.d.ts +11 -0
  128. package/src/tools/mf/federation-plugin.d.ts.map +1 -0
  129. package/src/tools/mf/federation-plugin.js +12 -0
  130. package/src/tools/mf/federation-side-build.d.ts +21 -0
  131. package/src/tools/mf/federation-side-build.d.ts.map +1 -0
  132. package/src/tools/mf/federation-side-build.js +27 -0
  133. package/src/tools/mf/to-plugin-config.d.ts +37 -0
  134. package/src/tools/mf/to-plugin-config.d.ts.map +1 -0
  135. package/src/tools/mf/to-plugin-config.js +33 -0
  136. package/src/utils/check-for-invalid-imports.d.ts +2 -0
  137. package/src/utils/check-for-invalid-imports.d.ts.map +1 -0
  138. package/src/utils/check-for-invalid-imports.js +29 -0
  139. package/src/utils/normalize-build-options.d.ts +22 -0
  140. package/src/utils/normalize-build-options.d.ts.map +1 -0
  141. package/src/utils/normalize-build-options.js +45 -0
  142. package/src/utils/normalize-context-options.d.ts +24 -0
  143. package/src/utils/normalize-context-options.d.ts.map +1 -0
  144. package/src/utils/normalize-context-options.js +18 -0
package/src/index.js ADDED
@@ -0,0 +1,132 @@
1
+ // Phase 1 — orchestrator swap (M1.2/M1.3).
2
+ // Runtime moved from `@softarc/native-federation-orchestrator` to
3
+ // `@module-federation/runtime` (`createInstance` + `loadRemote`). The NF-shaped
4
+ // public surface (`initFederation`/`loadRemoteModule`) is kept for adoption
5
+ // familiarity; NF-only options (`shimMode`, `sse`, `cacheTag`) are dropped.
6
+ // NB finding #6: MF-esbuild still shares modules via es-module-shims import maps,
7
+ // so the loader is unchanged — only the registration/manifest API differs.
8
+ // M3.5: the NF `@softarc/.../domain` re-export and the `Imports`/`Scopes`/`ImportMap`
9
+ // import-map types are removed — this entry is now pure MF.
10
+ import { createInstance, } from '@module-federation/runtime';
11
+ /**
12
+ * Framework packages that MUST resolve to a single instance, or Angular trips
13
+ * `NG0203`. Registered as MF singletons (M0.2's mandated config).
14
+ *
15
+ * `requiredVersion: false` for now — the singleton is enforced but the version
16
+ * check is relaxed, because resolving the installed version (NF's
17
+ * `requiredVersion: 'auto'`) is a config-build-time concern owned by
18
+ * `withModuleFederation` in **M3.1**; once that lands it feeds concrete ranges
19
+ * here. The actual module sharing flows through es-module-shims import maps at
20
+ * runtime (finding #6) — this map only declares the singleton/version contract.
21
+ */
22
+ export const DEFAULT_ANGULAR_SHARED = {
23
+ '@angular/core': { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
24
+ '@angular/common': { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
25
+ '@angular/common/http': { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
26
+ '@angular/router': { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
27
+ '@angular/platform-browser': { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
28
+ rxjs: { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
29
+ 'zone.js': { shareConfig: { singleton: true, strictVersion: true, requiredVersion: false } },
30
+ };
31
+ function toRemotes(remotes) {
32
+ return Object.entries(remotes).map(([name, entry]) => ({ name, entry }));
33
+ }
34
+ /** Strip a leading `./` so `'./Component'` → `'Component'` for the MF id. */
35
+ function exposedKey(exposedModule) {
36
+ return exposedModule.replace(/^\.\//, '');
37
+ }
38
+ function normalizeOptions(optionsOrRemoteName, exposedModule) {
39
+ if (typeof optionsOrRemoteName === 'string' && exposedModule) {
40
+ return { remoteName: optionsOrRemoteName, exposedModule };
41
+ }
42
+ if (typeof optionsOrRemoteName === 'object' && !exposedModule) {
43
+ return optionsOrRemoteName;
44
+ }
45
+ throw new Error('unexpected arguments: please pass options or a remoteName/exposedModule-pair');
46
+ }
47
+ function logClientError(error) {
48
+ if (typeof window !== 'undefined') {
49
+ console.error(error);
50
+ }
51
+ }
52
+ /**
53
+ * Derive a remote's name from its `mf-manifest.json` URL (the lazy path's entry
54
+ * must be the JSON manifest — its top-level `name`, per M0.4 — not `remoteEntry.js`).
55
+ */
56
+ async function resolveRemoteNameFromEntry(remoteEntry) {
57
+ const res = await fetch(remoteEntry);
58
+ if (!res.ok) {
59
+ throw new Error(`Failed to fetch remote manifest at ${remoteEntry}: ${res.status} ${res.statusText}`);
60
+ }
61
+ const info = (await res.json());
62
+ if (!info.name) {
63
+ throw new Error(`manifest at ${remoteEntry} does not declare a 'name'`);
64
+ }
65
+ return info.name;
66
+ }
67
+ async function loadFromInstance(mf, optionsOrRemoteName, exposedModule) {
68
+ const options = normalizeOptions(optionsOrRemoteName, exposedModule);
69
+ const { fallback } = options;
70
+ try {
71
+ // Lazy path: a remote not declared at initFederation time. Resolve its name
72
+ // from the manifest if omitted, then register it before loading.
73
+ if (!options.remoteName && options.remoteEntry) {
74
+ options.remoteName = await resolveRemoteNameFromEntry(options.remoteEntry);
75
+ }
76
+ if (options.remoteEntry && options.remoteName) {
77
+ mf.registerRemotes([{ name: options.remoteName, entry: options.remoteEntry }], { force: true });
78
+ }
79
+ if (!options.remoteName) {
80
+ throw new Error('loadRemoteModule: pass remoteName, or a remoteEntry pointing at an mf-manifest.json that declares a name');
81
+ }
82
+ const id = `${options.remoteName}/${exposedKey(options.exposedModule)}`;
83
+ const module = await mf.loadRemote(id);
84
+ if (module === null) {
85
+ throw new Error(`loadRemote returned null for '${id}'`);
86
+ }
87
+ return module;
88
+ }
89
+ catch (err) {
90
+ if (fallback) {
91
+ logClientError('error loading remote module: ' +
92
+ (err instanceof Error ? err.message : String(err)));
93
+ return fallback;
94
+ }
95
+ throw err;
96
+ }
97
+ }
98
+ /**
99
+ * Initialise Module Federation for an Angular host.
100
+ *
101
+ * Unlike NF's promise-returning init, this is **synchronous** — MF's
102
+ * `createInstance` is sync and remotes load lazily on first `loadRemoteModule`.
103
+ *
104
+ * ```ts
105
+ * const { loadRemoteModule } = initFederation({ mfe1: 'http://localhost:4201/remoteEntry.js' });
106
+ * const m = await loadRemoteModule('mfe1', './Component');
107
+ * ```
108
+ *
109
+ * @param remotes - name → remoteEntry/manifest URL map. (A bare manifest-URL
110
+ * string, like NF's `remotesOrManifestUrl`, is deferred to M1.7.)
111
+ * @param options - {@link InitFederationOptions}.
112
+ */
113
+ export function initFederation(remotes = {}, options) {
114
+ if (typeof remotes === 'string') {
115
+ throw new Error('initFederation: passing a manifest URL string is not supported yet (M1.7); pass a { name: entryUrl } map');
116
+ }
117
+ const mf = createInstance({
118
+ name: options?.name ?? 'host',
119
+ remotes: toRemotes(remotes),
120
+ // Host `@angular/*` (+ rxjs/zone.js) singletons (M1.4); caller overrides win.
121
+ shared: { ...DEFAULT_ANGULAR_SHARED, ...options?.shared },
122
+ plugins: options?.runtimePlugins ?? [],
123
+ shareStrategy: 'loaded-first',
124
+ });
125
+ const instance = {
126
+ instance: mf,
127
+ loadRemoteModule(optionsOrRemoteName, exposedModule) {
128
+ return loadFromInstance(mf, optionsOrRemoteName, exposedModule);
129
+ },
130
+ };
131
+ return instance;
132
+ }
@@ -0,0 +1,2 @@
1
+ export { runBuilder } from './builders/build/builder.js';
2
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1 @@
1
+ export { runBuilder } from './builders/build/builder.js';
@@ -0,0 +1,3 @@
1
+ export interface MfSchematicSchema {
2
+ project: string;
3
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "mf",
4
+ "title": "",
5
+ "type": "object",
6
+ "properties": {
7
+ "project": {
8
+ "type": "string",
9
+ "description": "The project to add module federation",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "Project name (press enter for default project)"
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,5 @@
1
+ import type { Rule, Tree } from '@angular-devkit/schematics';
2
+ import type { MfSchematicSchema } from './schema.js';
3
+ export default function remove(options: MfSchematicSchema): Rule;
4
+ export declare function getWorkspaceFileName(tree: Tree): string;
5
+ //# sourceMappingURL=schematic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schematic.d.ts","sourceRoot":"","sources":["../../../../src/schematics/appbuilder/schematic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAcrD,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAS/D;AAsFD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAUvD"}
@@ -0,0 +1,80 @@
1
+ import * as path from 'path';
2
+ export default function remove(options) {
3
+ return async function (tree /*, context*/) {
4
+ const workspaceFileName = getWorkspaceFileName(tree);
5
+ const workspace = JSON.parse(tree.read(workspaceFileName).toString('utf8'));
6
+ const normalized = normalizeOptions(options, workspace);
7
+ updateWorkspaceConfig(tree, normalized, workspace, workspaceFileName);
8
+ };
9
+ }
10
+ function updateWorkspaceConfig(tree, options, workspace, workspaceFileName) {
11
+ const { projectConfig } = options;
12
+ if (!projectConfig?.architect?.build || !projectConfig?.architect?.serve) {
13
+ throw new Error(`The project doesn't have a build or serve target in angular.json!`);
14
+ }
15
+ if (projectConfig.architect.esbuild) {
16
+ projectConfig.architect.esbuild.builder = '@angular/build:application';
17
+ projectConfig.architect.esbuild.options.browser = projectConfig.architect.esbuild.options.main;
18
+ delete projectConfig.architect.esbuild.options.main;
19
+ }
20
+ if (projectConfig.architect['serve-original']) {
21
+ const target = projectConfig.architect['serve-original'];
22
+ if (target.configurations?.production) {
23
+ target.configurations.production.buildTarget =
24
+ target.configurations.production.buildTarget.replace(':build:', ':esbuild:');
25
+ }
26
+ if (target.configurations?.development) {
27
+ target.configurations.development.buildTarget =
28
+ target.configurations.development.buildTarget.replace(':build:', ':esbuild:');
29
+ }
30
+ }
31
+ if (projectConfig.architect.serve) {
32
+ const target = projectConfig.architect.serve;
33
+ target.options.target = target.options.target.replace(':esbuild:', ':serve-original:');
34
+ delete target.options.port;
35
+ }
36
+ tree.overwrite(workspaceFileName, JSON.stringify(workspace, null, '\t'));
37
+ }
38
+ function normalizeOptions(options, workspace) {
39
+ const projects = Object.keys(workspace.projects);
40
+ if (!options.project && projects.length === 0) {
41
+ throw new Error(`No default project found. Please specifiy a project name!`);
42
+ }
43
+ if (!options.project) {
44
+ console.log('Using first configured project as default project: ' + projects[0]);
45
+ options.project = projects[0];
46
+ }
47
+ const projectName = options.project;
48
+ const projectConfig = workspace.projects[projectName];
49
+ if (!projectConfig) {
50
+ throw new Error(`Project ${projectName} not found in angular.json.`);
51
+ }
52
+ const projectRoot = projectConfig.root?.replace(/\\/g, '/');
53
+ const projectSourceRoot = projectConfig.sourceRoot?.replace(/\\/g, '/');
54
+ const manifestPath = path
55
+ .join(projectRoot, 'src/assets/federation.manifest.json')
56
+ .replace(/\\/g, '/');
57
+ const main = projectConfig.architect.build.options.main;
58
+ if (!projectConfig.architect.build.options.polyfills) {
59
+ projectConfig.architect.build.options.polyfills = [];
60
+ }
61
+ const polyfills = projectConfig.architect.build.options.polyfills;
62
+ return {
63
+ polyfills,
64
+ projectName,
65
+ projectRoot,
66
+ projectSourceRoot,
67
+ manifestPath,
68
+ projectConfig,
69
+ main,
70
+ };
71
+ }
72
+ export function getWorkspaceFileName(tree) {
73
+ if (tree.exists('angular.json')) {
74
+ return 'angular.json';
75
+ }
76
+ if (tree.exists('workspace.json')) {
77
+ return 'workspace.json';
78
+ }
79
+ throw new Error("angular.json or workspace.json expected! Did you call this in your project's root?");
80
+ }
@@ -0,0 +1,35 @@
1
+ import { withModuleFederation, shareAll } from 'module-federation-angular-adapter/config';
2
+
3
+ export default withModuleFederation({
4
+ name: '<%=project%>',
5
+
6
+ <% if (type === 'remote') { %>
7
+
8
+ exposes: {
9
+ './Component': './<%=appComponentPath%>',
10
+ },
11
+ <% } %>
12
+ shared: {
13
+ ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
14
+
15
+ // Share all of @angular/core, including its secondary entry points, to
16
+ // prevent version mismatches across the federation.
17
+ '@angular/core': {
18
+ singleton: true,
19
+ strictVersion: true,
20
+ requiredVersion: 'auto',
21
+ includeSecondaries: true,
22
+ },
23
+ },
24
+
25
+ skip: [
26
+ 'rxjs/ajax',
27
+ 'rxjs/fetch',
28
+ 'rxjs/testing',
29
+ 'rxjs/webSocket',
30
+ // Add further packages you don't need at runtime
31
+ ],
32
+
33
+ // Please read our FAQ about sharing libs:
34
+ // https://shorturl.at/jmzH0
35
+ });
@@ -0,0 +1,5 @@
1
+ export interface NfSchematicSchema {
2
+ project: string;
3
+ port: string;
4
+ type: 'host' | 'dynamic-host' | 'remote';
5
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "nf",
4
+ "title": "",
5
+ "type": "object",
6
+ "properties": {
7
+ "project": {
8
+ "type": "string",
9
+ "description": "The project to add native-federation federation",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "Project name (press enter for default project)"
15
+ },
16
+ "port": {
17
+ "type": "number",
18
+ "description": "The port to use for the federated module (remote, micro frontend, etc.)",
19
+ "$default": {
20
+ "$source": "argv",
21
+ "index": 1
22
+ }
23
+ },
24
+ "type": {
25
+ "enum": ["host", "dynamic-host", "remote"],
26
+ "type": "string",
27
+ "default": "remote"
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,6 @@
1
+ import { type Rule } from '@angular-devkit/schematics';
2
+ import type { NfSchematicSchema } from './schema.js';
3
+ export { updatePackageJson } from './steps/update-package-json.js';
4
+ export { getWorkspaceFileName } from './steps/normalize-options.js';
5
+ export default function config(options: NfSchematicSchema): Rule;
6
+ //# sourceMappingURL=schematic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schematic.d.ts","sourceRoot":"","sources":["../../../../src/schematics/init/schematic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,IAAI,EAAO,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAcrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAsD/D"}
@@ -0,0 +1,38 @@
1
+ import { chain, noop, url } from '@angular-devkit/schematics';
2
+ import * as path from 'path';
3
+ import { normalizeOptions, getWorkspaceFileName, } from './steps/normalize-options.js';
4
+ import { updatePolyfills } from './steps/update-polyfills.js';
5
+ import { generateRemoteMap } from './steps/generate-remote-map.js';
6
+ import { generateFederationConfig } from './steps/generate-federation-config.js';
7
+ import { updateWorkspaceConfig } from './steps/update-workspace-config.js';
8
+ import { addDependencies } from './steps/add-dependencies.js';
9
+ import { makeMainAsync } from './steps/make-main-async.js';
10
+ export { updatePackageJson } from './steps/update-package-json.js';
11
+ export { getWorkspaceFileName } from './steps/normalize-options.js';
12
+ export default function config(options) {
13
+ return async function (tree, context) {
14
+ const workspaceFileName = getWorkspaceFileName(tree);
15
+ const workspace = JSON.parse(tree.read(workspaceFileName)?.toString('utf8') ?? '{}');
16
+ const normalized = normalizeOptions(options, workspace, tree);
17
+ const { polyfills, projectName, projectRoot, projectSourceRoot, manifestPath, manifestRelPath, main, } = normalized;
18
+ updatePolyfills(tree, polyfills);
19
+ const remoteMap = await generateRemoteMap(workspace, projectName);
20
+ if (options.type === 'dynamic-host' && !tree.exists(manifestPath)) {
21
+ tree.create(manifestPath, JSON.stringify(remoteMap, null, '\t'));
22
+ }
23
+ const federationConfigPath = path.join(projectRoot, 'federation.config.mjs');
24
+ const exists = tree.exists(federationConfigPath);
25
+ const cand1 = path.join(projectSourceRoot, 'app', 'app.component.ts').replace(/\\/g, '/');
26
+ const cand2 = path.join(projectSourceRoot, 'app', 'app.ts').replace(/\\/g, '/');
27
+ const appComponent = tree.exists(cand1) ? cand1 : tree.exists(cand2) ? cand2 : 'update-this.ts';
28
+ const generateRule = !exists
29
+ ? generateFederationConfig(url('./files'), remoteMap, projectRoot, projectSourceRoot, appComponent, options)
30
+ : noop;
31
+ updateWorkspaceConfig(tree, normalized, workspace, workspaceFileName);
32
+ addDependencies(tree, context);
33
+ return chain([
34
+ generateRule,
35
+ makeMainAsync(main, options, remoteMap, manifestRelPath),
36
+ ]);
37
+ };
38
+ }
@@ -0,0 +1,3 @@
1
+ import type { SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ export declare function addDependencies(tree: Tree, context: SchematicContext): void;
3
+ //# sourceMappingURL=add-dependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-dependencies.d.ts","sourceRoot":"","sources":["../../../../../src/schematics/init/steps/add-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAoBzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAW3E"}
@@ -0,0 +1,25 @@
1
+ import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks/index.js';
2
+ import { addPackageJsonDependency, NodeDependencyType, } from '@schematics/angular/utility/dependencies';
3
+ // Runtime dependencies a consuming app needs for Module Federation v2. Pinned to
4
+ // the same versions the adapter is built against to avoid runtime version skew.
5
+ const RUNTIME_DEPENDENCIES = [
6
+ // The MF-esbuild container shares modules via es-module-shims import maps
7
+ // (loaded on the page through the polyfills).
8
+ { name: 'es-module-shims', version: '^2.8.0' },
9
+ { name: '@module-federation/runtime', version: '2.6.0' },
10
+ { name: '@module-federation/sdk', version: '2.6.0' },
11
+ // Imported by the generated container as a bare specifier resolved from the
12
+ // app's node_modules — undeclared upstream, so it must be listed explicitly.
13
+ { name: '@module-federation/webpack-bundler-runtime', version: '2.6.0' },
14
+ ];
15
+ export function addDependencies(tree, context) {
16
+ for (const dep of RUNTIME_DEPENDENCIES) {
17
+ addPackageJsonDependency(tree, {
18
+ name: dep.name,
19
+ type: NodeDependencyType.Default,
20
+ version: dep.version,
21
+ overwrite: false,
22
+ });
23
+ }
24
+ context.addTask(new NodePackageInstallTask());
25
+ }
@@ -0,0 +1,4 @@
1
+ import { type Source } from '@angular-devkit/schematics';
2
+ import type { NfSchematicSchema } from '../schema.js';
3
+ export declare function generateFederationConfig(templateSource: Source, remoteMap: Record<string, string>, projectRoot: string, projectSourceRoot: string, appComponentPath: string, options: NfSchematicSchema): import("@angular-devkit/schematics").Rule;
4
+ //# sourceMappingURL=generate-federation-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-federation-config.d.ts","sourceRoot":"","sources":["../../../../../src/schematics/init/steps/generate-federation-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,MAAM,EAAY,MAAM,4BAA4B,CAAC;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,iBAAiB,6CAe3B"}
@@ -0,0 +1,15 @@
1
+ import { apply, mergeWith, move, template } from '@angular-devkit/schematics';
2
+ export function generateFederationConfig(templateSource, remoteMap, projectRoot, projectSourceRoot, appComponentPath, options) {
3
+ const applied = apply(templateSource, [
4
+ template({
5
+ projectRoot,
6
+ projectSourceRoot,
7
+ appComponentPath,
8
+ remoteMap,
9
+ ...options,
10
+ tmpl: '',
11
+ }),
12
+ move(projectRoot),
13
+ ]);
14
+ return mergeWith(applied);
15
+ }
@@ -0,0 +1,2 @@
1
+ export declare function generateRemoteMap(workspace: any, projectName: string): Record<string, string>;
2
+ //# sourceMappingURL=generate-remote-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-remote-map.d.ts","sourceRoot":"","sources":["../../../../../src/schematics/init/steps/generate-remote-map.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,0BAwBpE"}
@@ -0,0 +1,20 @@
1
+ import { strings } from '@angular-devkit/core';
2
+ export function generateRemoteMap(workspace, projectName) {
3
+ const result = {};
4
+ for (const p in workspace.projects) {
5
+ const project = workspace.projects[p];
6
+ const projectType = project.projectType ?? 'application';
7
+ if (p !== projectName &&
8
+ projectType === 'application' &&
9
+ project?.architect?.serve &&
10
+ project?.architect?.build) {
11
+ const pPort = project.architect['serve-original']?.options?.port ??
12
+ project.architect.serve?.options?.port ??
13
+ 4200;
14
+ result[strings.camelize(p)] = `http://localhost:${pPort}/remoteEntry.json`;
15
+ }
16
+ }
17
+ // No sibling projects → emit an empty map for the user to fill in, rather than
18
+ // injecting a surprising fake `mfe1` remote.
19
+ return result;
20
+ }
@@ -0,0 +1,4 @@
1
+ import type { Rule } from "@angular-devkit/schematics";
2
+ import type { NfSchematicSchema } from "../schema.js";
3
+ export declare function makeMainAsync(main: string, options: NfSchematicSchema, remoteMap: unknown, manifestRelPath: string): Rule;
4
+ //# sourceMappingURL=make-main-async.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"make-main-async.d.ts","sourceRoot":"","sources":["../../../../../src/schematics/init/steps/make-main-async.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAsBtD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,OAAO,EAClB,eAAe,EAAE,MAAM,GACtB,IAAI,CA0BN"}
@@ -0,0 +1,35 @@
1
+ import * as path from "path";
2
+ // The adapter's `initFederation` wrapper hides shimMode/logger/storage, so
3
+ // generated apps don't ship internal orchestrator options or `logLevel: 'debug'`.
4
+ const FEDERATION_IMPORT = `import { initFederation } from 'module-federation-angular-adapter';`;
5
+ function getFederationArg(options, remoteMap, manifestRelPath) {
6
+ switch (options.type) {
7
+ case "dynamic-host":
8
+ return `'${manifestRelPath}'`;
9
+ case "host":
10
+ return JSON.stringify(remoteMap, null, 2).replace(/"/g, "'");
11
+ default:
12
+ return `{ '${options.project}': './remoteEntry.json' }`;
13
+ }
14
+ }
15
+ export function makeMainAsync(main, options, remoteMap, manifestRelPath) {
16
+ return async function (tree) {
17
+ const mainPath = path.dirname(main);
18
+ const bootstrapName = path.join(mainPath, "bootstrap.ts");
19
+ if (tree.exists(bootstrapName)) {
20
+ console.info(`${bootstrapName} already exists.`);
21
+ return;
22
+ }
23
+ const mainContent = tree.read(main);
24
+ if (mainContent)
25
+ tree.create(bootstrapName, mainContent);
26
+ const federationArg = getFederationArg(options, remoteMap, manifestRelPath);
27
+ tree.overwrite(main, `${FEDERATION_IMPORT}
28
+
29
+ initFederation(${federationArg})
30
+ .catch(err => console.error(err))
31
+ .then(_ => import('./bootstrap'))
32
+ .catch(err => console.error(err));
33
+ `);
34
+ };
35
+ }
@@ -0,0 +1,20 @@
1
+ import type { Tree } from '@angular-devkit/schematics';
2
+ import type { NfSchematicSchema } from '../schema.js';
3
+ export type NormalizedOptions = {
4
+ polyfills: string;
5
+ projectName: string;
6
+ projectRoot: string;
7
+ projectSourceRoot: string;
8
+ manifestPath: string;
9
+ manifestRelPath: string;
10
+ projectConfig: any;
11
+ main: string;
12
+ port: number;
13
+ };
14
+ export type PackageJson = {
15
+ dependencies: Record<string, string>;
16
+ scripts: Record<string, string>;
17
+ };
18
+ export declare function getWorkspaceFileName(tree: Tree): string;
19
+ export declare function normalizeOptions(options: NfSchematicSchema, workspace: any, tree: Tree): NormalizedOptions;
20
+ //# sourceMappingURL=normalize-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-options.d.ts","sourceRoot":"","sources":["../../../../../src/schematics/init/steps/normalize-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,GAAG,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAUvD;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,GAAG,EACd,IAAI,EAAE,IAAI,GACT,iBAAiB,CAqEnB"}
@@ -0,0 +1,64 @@
1
+ import * as path from 'path';
2
+ export function getWorkspaceFileName(tree) {
3
+ if (tree.exists('angular.json')) {
4
+ return 'angular.json';
5
+ }
6
+ if (tree.exists('workspace.json')) {
7
+ return 'workspace.json';
8
+ }
9
+ throw new Error("angular.json or workspace.json expected! Did you call this in your project's root?");
10
+ }
11
+ export function normalizeOptions(options, workspace, tree) {
12
+ const projects = Object.keys(workspace.projects);
13
+ if (!options.project && projects.length === 0) {
14
+ throw new Error(`No default project found. Please specifiy a project name!`);
15
+ }
16
+ if (!options.project) {
17
+ console.log('Using first configured project as default project: ' + projects[0]);
18
+ options.project = projects[0];
19
+ }
20
+ const projectName = options.project;
21
+ const projectConfig = workspace.projects[projectName];
22
+ if (!projectConfig) {
23
+ throw new Error(`Project ${projectName} not found in angular.json.`);
24
+ }
25
+ const projectRoot = projectConfig.root?.replace(/\\/g, '/');
26
+ const projectSourceRoot = projectConfig.sourceRoot?.replace(/\\/g, '/');
27
+ const publicPath = path.join(projectRoot, 'public').replace(/\\/g, '/');
28
+ let manifestPath = path.join(publicPath, 'federation.manifest.json').replace(/\\/g, '/');
29
+ let manifestRelPath = 'federation.manifest.json';
30
+ const hasPublicFolder = tree
31
+ .getDir(projectRoot)
32
+ .subdirs.map(p => String(p))
33
+ .includes('public');
34
+ if (!hasPublicFolder) {
35
+ manifestPath = path
36
+ .join(projectRoot, 'src/assets/federation.manifest.json')
37
+ .replace(/\\/g, '/');
38
+ manifestRelPath = 'assets/federation.manifest.json';
39
+ }
40
+ const main = projectConfig.architect.build.options.main ||
41
+ projectConfig.architect.build.options.browser ||
42
+ projectConfig.architect.esbuild.options.main || // fallback, if esbuild is already set
43
+ projectConfig.architect.esbuild.options.browser;
44
+ if (!projectConfig.architect.build.options.polyfills) {
45
+ projectConfig.architect.build.options.polyfills = [];
46
+ }
47
+ if (typeof projectConfig.architect.build.options.polyfills === 'string') {
48
+ projectConfig.architect.build.options.polyfills = [
49
+ projectConfig.architect.build.options.polyfills,
50
+ ];
51
+ }
52
+ const polyfills = projectConfig.architect.build.options.polyfills;
53
+ return {
54
+ polyfills,
55
+ projectName,
56
+ projectRoot,
57
+ projectSourceRoot,
58
+ manifestPath,
59
+ manifestRelPath,
60
+ projectConfig,
61
+ main,
62
+ port: +(options.port || 4200),
63
+ };
64
+ }
@@ -0,0 +1,3 @@
1
+ import type { Tree } from '@angular-devkit/schematics';
2
+ export declare function updatePackageJson(tree: Tree): void;
3
+ //# sourceMappingURL=update-package-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-package-json.d.ts","sourceRoot":"","sources":["../../../../../src/schematics/init/steps/update-package-json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAGvD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CA0BlD"}
@@ -0,0 +1,19 @@
1
+ export function updatePackageJson(tree) {
2
+ const packageJson = tree.readJson('package.json') ?? {};
3
+ const scriptCall = 'node node_modules/@angular-architects/native-federation/src/patch-angular-build.js';
4
+ if (!packageJson?.['scripts']) {
5
+ packageJson['scripts'] = {};
6
+ }
7
+ let postInstall = (packageJson['scripts']?.['postinstall'] || '');
8
+ if (!postInstall) {
9
+ return;
10
+ }
11
+ if (postInstall.includes(scriptCall)) {
12
+ postInstall = postInstall.replace(scriptCall, '');
13
+ }
14
+ if (postInstall.endsWith(' && ')) {
15
+ postInstall = postInstall.substring(0, postInstall.length - 4);
16
+ }
17
+ packageJson['scripts']['postinstall'] = postInstall;
18
+ tree.overwrite('package.json', JSON.stringify(packageJson, null, 2));
19
+ }
@@ -0,0 +1,3 @@
1
+ import type { Tree } from '@angular-devkit/schematics';
2
+ export declare function updatePolyfills(tree: Tree, polyfills: string | string[]): void;
3
+ //# sourceMappingURL=update-polyfills.d.ts.map