@ws-test-realm/admin-kit 0.1.14 → 0.2.1-ng16

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.
package/bin/ws-purge.js CHANGED
File without changes
@@ -16,8 +16,8 @@ function loadOrder({ workspaceDir, restrictTo, withDeps = false }) {
16
16
 
17
17
  // A project declares itself a library (not a federated remote) by setting
18
18
  // `wsmodules.kind = "library"` in its own package.json. Default kind is
19
- // "remote": build/pack/deploy all run. Libraries are still built (their dist
20
- // is consumed by sibling projects) but skip pack/deploy.
19
+ // "remote": build/deploy both run. Libraries are still built (their dist
20
+ // is consumed by sibling projects) but skip deploy.
21
21
  function isLibrary(workspaceDir, name) {
22
22
  const pkgPath = path.join(workspaceDir, "projects", name, "package.json");
23
23
  if (!fs.existsSync(pkgPath)) return false;
@@ -53,31 +53,27 @@ function buildLibs({ workspaceDir, restrictTo = [], withDeps = false }) {
53
53
  });
54
54
  }
55
55
 
56
- console.log(`\nBuilt ${order.length} lib(s).`);
56
+ console.log(`\nBuilt ${order.length} project(s).`);
57
57
  return { order };
58
58
  }
59
59
 
60
+ // Under native-federation the artifact (`remoteEntry.json` + chunks) is
61
+ // produced directly by `ng build` via the @angular-architects/native-federation
62
+ // builder. There is no separate webpack pack step. Kept as a no-op so the
63
+ // `npm run wire = ws-modules --build --pack --deploy` convention keeps
64
+ // working without churn.
60
65
  function packRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
61
66
  const { order } = loadOrder({ workspaceDir, restrictTo, withDeps });
62
67
  const { remotes, libraries } = partitionByKind(workspaceDir, order);
63
68
 
64
- console.log(`\n=== Pack order ===`);
65
- remotes.forEach((n, i) => console.log(` ${i + 1}. ${n}`));
69
+ console.log(`\n=== Pack (no-op under native-federation) ===`);
70
+ remotes.forEach((n, i) => console.log(` ${i + 1}. ${n} (artifact already produced by ng build)`));
66
71
  if (libraries.length) {
67
72
  console.log(
68
73
  ` (skipping libraries: ${libraries.join(", ")} — wsmodules.kind=library)`
69
74
  );
70
75
  }
71
76
 
72
- for (const name of remotes) {
73
- console.log(`\n=== ws-pack-remote ${name} ===`);
74
- execSync(
75
- `ws-pack-remote --project projects/${name} --workspace .`,
76
- { cwd: workspaceDir, stdio: "inherit" }
77
- );
78
- }
79
-
80
- console.log(`\nPacked ${remotes.length} remote(s).`);
81
77
  return { order: remotes, libraries };
82
78
  }
83
79
 
package/lib/cli-args.js CHANGED
@@ -1,5 +1,5 @@
1
- // Shared CLI arg parsing + selection-mode resolution for ws-build-libs,
2
- // ws-pack-remotes, ws-deploy-remotes.
1
+ // Shared CLI arg parsing + selection-mode resolution for the ws-modules
2
+ // driver (build + deploy across an admin-modules workspace).
3
3
  //
4
4
  // Selection modes:
5
5
  // inert — no positional args; print usage, do nothing
@@ -4,27 +4,47 @@ const { packIntoJar } = require("./pack-into-jar");
4
4
  const { loadOrder, partitionByKind } = require("./build-modules");
5
5
  const { findMatches } = require("./jar-glob");
6
6
  const { loadDeployContext, resolveTarget } = require("./target-resolution");
7
- const { promotePeerDeps, syncFederationWorkspace } = require("./federation-peers");
7
+ const { emitDescriptor } = require("./emit-descriptor");
8
+ const { readIdentity } = require("./identity");
9
+
10
+ // Resolve the build output for a native-federation remote. The Angular
11
+ // application builder (esbuild) emits to `dist/<id>/browser/` by default;
12
+ // older builder configurations may still write to `dist/<id>/`. Probe both
13
+ // and return whichever contains `remoteEntry.json`.
14
+ function resolveBuildOutput(workspaceDir, id) {
15
+ const candidates = [
16
+ path.join(workspaceDir, "dist", id, "browser"),
17
+ path.join(workspaceDir, "dist", id),
18
+ ];
19
+ for (const c of candidates) {
20
+ if (fs.existsSync(path.join(c, "remoteEntry.json"))) return c;
21
+ }
22
+ throw new Error(
23
+ `Native-federation artifact not found for '${id}'. Expected remoteEntry.json under one of: ${candidates.join(
24
+ ", "
25
+ )}. Did the build step run?`
26
+ );
27
+ }
8
28
 
9
- function copyTypes(distLib, hostFederationDir) {
10
- if (fs.existsSync(hostFederationDir)) {
11
- fs.rmSync(hostFederationDir, { recursive: true, force: true });
29
+ // Stage the build output + descriptor into a deploy-ready tree:
30
+ // <stagingDir>/<id>/{federation.json, remote/<all chunks + remoteEntry.json>}
31
+ // `remote/` mirrors the path the federation controller serves contributions
32
+ // under, so packing-into-jar and ext-copy are uniform.
33
+ function stageContribution({ workspaceDir, id, remoteName, hostId }) {
34
+ const buildDir = resolveBuildOutput(workspaceDir, id);
35
+ const stagingRoot = path.join(workspaceDir, "dist", "admin-remotes");
36
+ const idDir = path.join(stagingRoot, id);
37
+ if (fs.existsSync(idDir)) {
38
+ fs.rmSync(idDir, { recursive: true, force: true });
12
39
  }
13
- fs.mkdirSync(hostFederationDir, { recursive: true });
14
- fs.cpSync(distLib, hostFederationDir, { recursive: true });
15
- // Promote peerDependencies dependencies in the staged package.json so
16
- // npm's workspace install actually installs them. The producer's source
17
- // package.json is untouched; this only affects the host-staged copy.
18
- // syncFederationWorkspace runs once at the end of the deploy loop and
19
- // re-establishes <federationDir>/node_modules from scratch, so we don't
20
- // need to preserve any prior node_modules under <id>/.
21
- promotePeerDeps(hostFederationDir);
40
+ const remoteDir = path.join(idDir, "remote");
41
+ fs.mkdirSync(remoteDir, { recursive: true });
42
+ fs.cpSync(buildDir, remoteDir, { recursive: true });
43
+ emitDescriptor({ outDir: stagingRoot, id, hostId, remoteName });
44
+ return idDir;
22
45
  }
23
46
 
24
- // Copy the federation artifact (dist/admin-remotes/<id>/) to the ext target
25
- // dir (replace-existing semantics). Also warn if a same-id-named jar exists
26
- // in the modules folder — possible name-collision abuse signal.
27
- function deployExt({ workspaceDir, id, modulesDir, extDir }) {
47
+ function deployExt({ workspaceDir, id, modulesDir, extDir, stagedDir }) {
28
48
  const collisions = findMatches(modulesDir, `${id}-*.jar`);
29
49
  if (collisions.length) {
30
50
  const list = collisions.map((c) => path.join(modulesDir, c)).join(", ");
@@ -32,17 +52,11 @@ function deployExt({ workspaceDir, id, modulesDir, extDir }) {
32
52
  `\x1b[33mWarning:\x1b[0m a jar matching '${id}-*.jar' exists at ${list}; this project is targeting ext, but the name collision may indicate abuse.`
33
53
  );
34
54
  }
35
- const srcDir = path.join(workspaceDir, "dist", "admin-remotes", id);
36
- if (!fs.existsSync(srcDir)) {
37
- throw new Error(
38
- `Federation artifact missing: ${srcDir}. Run \`pack:remote\` first.`
39
- );
40
- }
41
55
  if (fs.existsSync(extDir)) {
42
56
  fs.rmSync(extDir, { recursive: true, force: true });
43
57
  }
44
58
  fs.mkdirSync(extDir, { recursive: true });
45
- fs.cpSync(srcDir, extDir, { recursive: true });
59
+ fs.cpSync(stagedDir, extDir, { recursive: true });
46
60
  let fileCount = 0;
47
61
  const walk = (d) => {
48
62
  for (const e of fs.readdirSync(d, { withFileTypes: true })) {
@@ -64,19 +78,13 @@ function deployOne({
64
78
  modulesDir,
65
79
  wsconfig,
66
80
  }) {
67
- const distRemoteDir = path.join(workspaceDir, "dist", "admin-remotes", id);
68
- const distLibDir = path.join(workspaceDir, "dist", id);
69
-
70
- if (!fs.existsSync(distRemoteDir)) {
71
- throw new Error(
72
- `Federation artifact missing: ${distRemoteDir}. Run \`pack:remote\` first.`
73
- );
74
- }
75
- if (!fs.existsSync(distLibDir)) {
76
- throw new Error(`Lib dist missing: ${distLibDir}. Run \`build\` first.`);
77
- }
78
-
79
- const hostFederationDir = path.join(adminDir, ".federation", id);
81
+ const identity = readIdentity(workspaceDir, id);
82
+ const stagedDir = stageContribution({
83
+ workspaceDir,
84
+ id,
85
+ remoteName: identity.remoteName,
86
+ hostId: identity.hostId,
87
+ });
80
88
 
81
89
  const target = resolveTarget({
82
90
  workspaceDir,
@@ -86,33 +94,29 @@ function deployOne({
86
94
  modulesFolder,
87
95
  });
88
96
 
89
- let artifactReport;
90
97
  if (target.kind === "jar") {
91
98
  const packResult = packIntoJar({
92
99
  jarPath: target.jarPath,
93
- sourceDir: distRemoteDir,
100
+ sourceDir: stagedDir,
94
101
  id,
95
102
  });
96
- artifactReport = {
103
+ return {
97
104
  kind: "jar",
98
105
  jarPath: target.jarPath,
99
106
  pomPath: target.pomPath,
100
107
  artifactId: target.artifactId,
101
108
  ...packResult,
102
109
  };
103
- } else {
104
- const extResult = deployExt({
105
- workspaceDir,
106
- id,
107
- modulesDir,
108
- extDir: target.extDir,
109
- });
110
- artifactReport = { kind: "ext", ...extResult };
111
110
  }
112
111
 
113
- copyTypes(distLibDir, hostFederationDir);
114
-
115
- return { ...artifactReport, hostFederationDir };
112
+ const extResult = deployExt({
113
+ workspaceDir,
114
+ id,
115
+ modulesDir,
116
+ extDir: target.extDir,
117
+ stagedDir,
118
+ });
119
+ return { kind: "ext", ...extResult };
116
120
  }
117
121
 
118
122
  function deployRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
@@ -157,26 +161,11 @@ function deployRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
157
161
  console.log(` ext dir: ${r.extDir}`);
158
162
  console.log(` copied files: ${r.copiedFiles}`);
159
163
  }
160
- console.log(` host types: ${r.hostFederationDir}`);
161
164
  results.push({ id, ...r });
162
165
  }
163
166
 
164
167
  console.log(`\nDeployed ${results.length} module(s).`);
165
168
 
166
- // Now that the .federation/<id>/ dirs are fresh, run a single workspace
167
- // install at .federation/ to populate node_modules. npm hoists shared
168
- // versions to .federation/node_modules/ and only nests on per-lib version
169
- // conflicts. Consumers' TypeScript walks up from .federation/<id>/ and
170
- // resolves transitive types without consumer-side dep management.
171
- try {
172
- syncFederationWorkspace(ctx.adminDir, workspaceDir);
173
- } catch (e) {
174
- console.warn(
175
- `\x1b[33mWarning:\x1b[0m federation peer install failed: ${e.message}`
176
- );
177
- console.warn(` Consumer workspaces may fail to type-check federated libs.`);
178
- }
179
-
180
169
  return { results };
181
170
  }
182
171
 
@@ -3,26 +3,20 @@ const path = require("path");
3
3
 
4
4
  // Write <out>/<id>/federation.json — the descriptor the admin federation
5
5
  // controller reads (both jar-side and ext-side).
6
- function emitDescriptor({
7
- outDir,
8
- id,
9
- hostId,
10
- remoteName,
11
- exposes,
12
- displayName,
13
- }) {
6
+ //
7
+ // Under native-federation, the only field the fiddle controller needs from
8
+ // each descriptor to build the manifest is `remoteName`. The host loads the
9
+ // remote by fetching `remoteEntry.json` at the asset URL the controller
10
+ // returns; no separate `exposedModule` lookup is required because the
11
+ // import-map carries the entry mapping directly.
12
+ function emitDescriptor({ outDir, id, hostId, remoteName }) {
14
13
  const dir = path.join(outDir, id);
15
14
  fs.mkdirSync(dir, { recursive: true });
16
15
  const file = path.join(dir, "federation.json");
17
16
  const descriptor = {
18
17
  hostId,
19
18
  contributionId: id,
20
- manifestEntry: {
21
- id,
22
- name: displayName,
23
- remoteName,
24
- exposedModule: exposes,
25
- },
19
+ manifestEntry: { remoteName },
26
20
  };
27
21
  fs.writeFileSync(file, JSON.stringify(descriptor, null, 2) + "\n", "utf8");
28
22
  return file;
@@ -51,26 +51,36 @@ function copyAndSubstitute(src, dest, tokens) {
51
51
  }
52
52
  }
53
53
 
54
+ // Register the project as a native-federation application in angular.json.
55
+ // `build` is the @angular-architects/native-federation:build target, which
56
+ // post-processes the underlying esbuild app target (`esbuild`) to emit
57
+ // remoteEntry.json + shared/exposed chunk bundles next to the application
58
+ // bundle. Names match the federation.config.js `name` derivation.
54
59
  function registerInAngularJson(workspaceDir, name) {
55
60
  const angularJsonPath = path.join(workspaceDir, "angular.json");
56
61
  const angular = JSON.parse(fs.readFileSync(angularJsonPath, "utf8"));
57
62
  angular.projects = angular.projects || {};
58
63
  angular.projects[name] = {
59
- projectType: "library",
64
+ projectType: "application",
65
+ schematics: {},
60
66
  root: `projects/${name}`,
61
67
  sourceRoot: `projects/${name}/src`,
62
68
  prefix: "ws",
63
69
  architect: {
64
70
  build: {
65
- builder: "@angular-devkit/build-angular:ng-packagr",
66
- options: { project: `projects/${name}/ng-package.json` },
67
- configurations: {
68
- production: {
69
- tsConfig: `projects/${name}/tsconfig.lib.prod.json`,
70
- },
71
- development: { tsConfig: `projects/${name}/tsconfig.lib.json` },
71
+ builder: "@angular-architects/native-federation:build",
72
+ options: { target: `${name}:esbuild` },
73
+ },
74
+ esbuild: {
75
+ builder: "@angular-devkit/build-angular:application",
76
+ options: {
77
+ outputPath: `dist/${name}`,
78
+ index: `projects/${name}/src/index.html`,
79
+ browser: `projects/${name}/src/main.ts`,
80
+ polyfills: [`projects/${name}/src/polyfills.ts`],
81
+ tsConfig: `projects/${name}/tsconfig.app.json`,
82
+ assets: [`projects/${name}/src/assets`],
72
83
  },
73
- defaultConfiguration: "production",
74
84
  },
75
85
  },
76
86
  };
package/lib/identity.js CHANGED
@@ -46,4 +46,22 @@ function resolveIdentity(args) {
46
46
  };
47
47
  }
48
48
 
49
- module.exports = { resolveIdentity, camelCase };
49
+ // Lightweight identity lookup used by deploy-remotes.js. Derives the
50
+ // federated remote name from the project directory name (camelCased) and
51
+ // picks up the host id from the workspace's wsconfig.json (default "admin").
52
+ // The project's own `federation.config.js` declares the same `name` value;
53
+ // keeping the derivation here means deploy doesn't have to evaluate that
54
+ // config file at deploy time.
55
+ function readIdentity(workspaceDir, id) {
56
+ let hostId = "admin";
57
+ try {
58
+ const wsconfigPath = path.join(workspaceDir, "wsconfig.json");
59
+ if (fs.existsSync(wsconfigPath)) {
60
+ const ws = JSON.parse(fs.readFileSync(wsconfigPath, "utf8"));
61
+ if (ws.hostId) hostId = ws.hostId;
62
+ }
63
+ } catch {}
64
+ return { id, hostId, remoteName: camelCase(id) };
65
+ }
66
+
67
+ module.exports = { resolveIdentity, camelCase, readIdentity };
@@ -22,9 +22,9 @@ function walk(dir, base = dir) {
22
22
  // Repack a jar to carry a federated remote contribution under
23
23
  // META-INF/federation/<id>/. Drops any pre-existing entries under that
24
24
  // prefix (so re-deploys are clean), then adds every file under sourceDir
25
- // at <prefix>/<relpath>. sourceDir is expected to be the
26
- // dist/admin-remotes/<id>/ tree (which already contains both
27
- // federation.json and remote/* per ws-pack-remote's emit).
25
+ // at <prefix>/<relpath>. sourceDir is expected to be the staged
26
+ // dist/admin-remotes/<id>/ tree (federation.json + remote/* with the
27
+ // native-federation `remoteEntry.json` + chunks).
28
28
  function packIntoJar({ jarPath, sourceDir, id }) {
29
29
  if (!fs.existsSync(jarPath)) {
30
30
  throw new Error(`Jar not found: ${jarPath}`);
@@ -3,7 +3,6 @@ const path = require("path");
3
3
  const { purgeJar } = require("./purge-jar");
4
4
  const { loadOrder, partitionByKind } = require("./build-modules");
5
5
  const { loadDeployContext, resolveTarget } = require("./target-resolution");
6
- const { syncFederationWorkspace } = require("./federation-peers");
7
6
 
8
7
  // Per-remote purge action. Returns a structured action record.
9
8
  function purgeOne({ workspaceDir, id, ctx }) {
@@ -122,19 +121,6 @@ function purgeRemotes({
122
121
 
123
122
  reportActions({ actions, skipped, errors });
124
123
 
125
- // Removed federation type dirs ⇒ workspaces list is stale; resync so
126
- // the remaining libs stay correctly installed (and orphaned ones stop
127
- // claiming workspace slots).
128
- if (actions.some((a) => a.typesRemoved)) {
129
- try {
130
- syncFederationWorkspace(ctx.adminDir, workspaceDir);
131
- } catch (e) {
132
- console.warn(
133
- `\x1b[33mWarning:\x1b[0m federation peer resync failed: ${e.message}`
134
- );
135
- }
136
- }
137
-
138
124
  return { actions, skipped, errors };
139
125
  }
140
126
 
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@ws-test-realm/admin-kit",
3
- "version": "0.1.14",
4
- "description": "Workflow CLI + scaffolding for Wiresphere admin-modules workspaces. Ships `ws-init-workspace` (init/merge into a project, stamps @wiresphere/shared's `getOverrides()` into the workspace's npm overrides block), `ws-modules` (build/pack/deploy driver), `ws-generate-module`/`ws-drop-module`, `ws-wire-host`, `ws-wire-pom`, `ws-sync-paths`, and `ws-pack-remote`. Depends on @wiresphere/devkit (toolchain BOM, peerDeps); pairs with @wiresphere/shared (runtime BOM + MF share map + overrides manifest). Requires npm 9+ for overrides + peerDep resolution to coexist cleanly.",
3
+ "version": "0.2.1-ng16",
4
+ "description": "Workflow CLI + scaffolding for Wiresphere admin-modules workspaces (Angular 16 + native-federation line). Ships `ws-init-workspace`, `ws-modules` (build+deploy driver), `ws-generate-module`/`ws-drop-module`, `ws-wire-host`, `ws-wire-pom`, `ws-sync-paths`, and `ws-purge`. Depends on @ws-test-realm/devkit (toolchain BOM) + @ws-test-realm/shared (runtime BOM + native-federation share map).",
5
5
  "license": "Artistic-2.0",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
9
  "bin": {
10
- "ws-pack-remote": "./bin/ws-pack-remote.js",
11
10
  "ws-init-workspace": "./bin/ws-init-workspace.js",
12
11
  "ws-wire-host": "./bin/ws-wire-host.js",
13
12
  "ws-wire-pom": "./bin/ws-wire-pom.js",
@@ -21,16 +20,15 @@
21
20
  "files": [
22
21
  "bin",
23
22
  "lib",
24
- "hooks",
25
23
  "template",
26
24
  "template-module",
27
25
  "README.md"
28
26
  ],
29
27
  "dependencies": {
30
- "@ws-test-realm/devkit": "^0.3.0",
31
- "@ws-test-realm/shared": "^0.3.0",
28
+ "@angular-architects/native-federation": "^16.0.0",
29
+ "@ws-test-realm/devkit": "file:/Users/ph/projects/ws-admin-aux/devkit",
30
+ "@ws-test-realm/shared": "file:/Users/ph/projects/ws-admin-aux/shared",
32
31
  "adm-zip": "^0.5.10",
33
- "fast-xml-parser": "^4.3.0",
34
- "tsconfig-paths-webpack-plugin": "^4.1.0"
32
+ "fast-xml-parser": "^4.3.0"
35
33
  }
36
34
  }
@@ -16,12 +16,12 @@
16
16
  "wire": "ws-modules --build --pack --deploy"
17
17
  },
18
18
  "prettier": "@ws-test-realm/devkit/prettier.config.js",
19
- "husky": {
20
- "hooks": {
21
- "pre-commit": "ws-pre-commit"
22
- }
23
- },
24
19
  "dependencies": {
25
- "@ws-test-realm/shared": "^0.3.0"
20
+ "@ws-test-realm/shared": "^0.4.0-ng16",
21
+ "@ws-test-realm/ws-core": "^0.1.0-ng16"
22
+ },
23
+ "devDependencies": {
24
+ "@ws-test-realm/admin-kit": "^0.2.0-ng16",
25
+ "@ws-test-realm/devkit": "^0.4.0-ng16"
26
26
  }
27
27
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "./tsconfig.federation.json",
2
+ "extends": "@ws-test-realm/devkit/tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "baseUrl": "./",
5
5
  "outDir": "./dist/out-tsc"
@@ -0,0 +1,11 @@
1
+ const { withNativeFederation } = require('@angular-architects/native-federation/config');
2
+ const { sharedDescriptors } = require('@ws-test-realm/shared');
3
+
4
+ module.exports = withNativeFederation({
5
+ name: '__camelName__Module',
6
+ exposes: {
7
+ './Module': './src/public-api.ts',
8
+ },
9
+ shared: sharedDescriptors(),
10
+ skip: ['rxjs/ajax', 'rxjs/fetch', 'rxjs/testing', 'rxjs/webSocket'],
11
+ });
@@ -2,13 +2,16 @@
2
2
  "name": "__name__",
3
3
  "version": "0.0.1",
4
4
  "peerDependencies": {
5
- "@angular/common": "12.2.0",
6
- "@angular/core": "12.2.0",
7
- "@angular/forms": "12.2.0",
8
- "@angular/router": "12.2.0",
9
- "rxjs": "6.5.3"
5
+ "@angular/common": "16.2.12",
6
+ "@angular/core": "16.2.12",
7
+ "@angular/forms": "16.2.12",
8
+ "@angular/router": "16.2.12",
9
+ "rxjs": "~7.8.0"
10
10
  },
11
11
  "dependencies": {
12
- "tslib": "^2.3.0"
12
+ "tslib": "^2.5.0"
13
+ },
14
+ "wsmodules": {
15
+ "target": "jar"
13
16
  }
14
17
  }
@@ -0,0 +1,8 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>__name__ federated remote</title>
6
+ </head>
7
+ <body></body>
8
+ </html>
@@ -0,0 +1,6 @@
1
+ // Federated remote entry — this remote is consumed by the host via
2
+ // `loadRemoteModule('__camelName__Module', './Module')`. main.ts exists
3
+ // because the native-federation builder wraps an `application` builder
4
+ // target, which requires an entry. The actual exposed surface is
5
+ // `./src/public-api.ts` (see federation.config.js).
6
+ export {};
@@ -0,0 +1 @@
1
+ import 'zone.js';
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/app",
5
+ "types": []
6
+ },
7
+ "files": ["src/main.ts", "src/polyfills.ts"],
8
+ "include": ["src/**/*.d.ts"]
9
+ }
@@ -1,196 +0,0 @@
1
- #!/usr/bin/env node
2
- // ws-pack-remote — pack a built lib's dist into a deploy-ready
3
- // federated remote artifact. See README.md for usage.
4
-
5
- const fs = require("fs");
6
- const path = require("path");
7
- const Module = require("module");
8
-
9
- const { resolveIdentity } = require("../lib/identity");
10
- const { discoverWorkspaceAliases } = require("../lib/alias-discovery");
11
- const { writeSyntheticEntry, cleanup } = require("../lib/synthetic-entry");
12
- const { buildSharedMap } = require("../lib/shared-deps");
13
- const { buildWebpackConfig } = require("../lib/webpack-factory");
14
- const { emitDescriptor } = require("../lib/emit-descriptor");
15
-
16
- function parseArgs(argv) {
17
- const aliases = {
18
- project: "project",
19
- workspace: "workspace",
20
- out: "out",
21
- id: "id",
22
- host: "host",
23
- "remote-name": "remoteName",
24
- exposes: "exposes",
25
- "display-name": "displayName",
26
- config: "config",
27
- };
28
- const args = {};
29
- for (let i = 2; i < argv.length; i++) {
30
- const a = argv[i];
31
- if (!a.startsWith("--")) continue;
32
- const key = a.slice(2);
33
- const mapped = aliases[key];
34
- if (!mapped) {
35
- console.error(`Unknown flag: --${key}`);
36
- process.exit(1);
37
- }
38
- const val = argv[i + 1];
39
- if (val === undefined || val.startsWith("--")) {
40
- args[mapped] = true;
41
- } else {
42
- args[mapped] = val;
43
- i++;
44
- }
45
- }
46
- return args;
47
- }
48
-
49
- function require_(args, key) {
50
- if (!args[key]) {
51
- console.error(`Missing required flag: --${kebab(key)}`);
52
- process.exit(1);
53
- }
54
- return args[key];
55
- }
56
-
57
- function kebab(s) {
58
- return s.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
59
- }
60
-
61
- async function main() {
62
- const args = parseArgs(process.argv);
63
- require_(args, "project");
64
- require_(args, "workspace");
65
-
66
- const workspaceDir = path.resolve(args.workspace);
67
- const identity = resolveIdentity(args);
68
- const outDir = args.out
69
- ? path.resolve(args.out)
70
- : path.join(workspaceDir, "dist", "admin-remotes");
71
-
72
- const libDist = path.join(workspaceDir, "dist", identity.id);
73
- if (!fs.existsSync(libDist)) {
74
- console.error(
75
- `Lib dist not found at ${libDist}. Run \`ng build ${identity.id}\` first.`
76
- );
77
- process.exit(1);
78
- }
79
-
80
- console.log(
81
- `ws-pack-remote: id=${identity.id} remoteName=${
82
- identity.remoteName
83
- } → ${path.join(outDir, identity.id)}/`
84
- );
85
-
86
- const aliasMap = discoverWorkspaceAliases(workspaceDir);
87
- if (!aliasMap[identity.id]) {
88
- console.error(
89
- `Lib '${identity.id}' not recognized as ng-package output (missing module/main in dist/${identity.id}/package.json).`
90
- );
91
- process.exit(1);
92
- }
93
-
94
- const synthetic = writeSyntheticEntry(identity.id);
95
-
96
- let webpack;
97
- try {
98
- const requireFromWorkspace = Module.createRequire(
99
- path.join(workspaceDir, "package.json")
100
- );
101
- webpack = requireFromWorkspace("webpack");
102
- } catch (e) {
103
- console.error(
104
- `Failed to load webpack from workspace ${workspaceDir}: ${e.message}`
105
- );
106
- cleanup(synthetic.dir);
107
- process.exit(1);
108
- }
109
-
110
- try {
111
- const sharedMap = buildSharedMap({
112
- workspaceDir,
113
- aliasMap,
114
- selfId: identity.id,
115
- sharedExtras: identity.sharedExtras,
116
- });
117
-
118
- const config = buildWebpackConfig({
119
- workspaceDir,
120
- syntheticEntry: synthetic.file,
121
- outDir,
122
- id: identity.id,
123
- remoteName: identity.remoteName,
124
- exposes: identity.exposes,
125
- aliasMap,
126
- sharedMap,
127
- });
128
-
129
- await runWebpack(webpack, config);
130
-
131
- // Copy the lib's static assets (i18n JSON, etc.) into the remote
132
- // dir so the controller serves them at the same URL space as the
133
- // chunks. ng-packagr emits these to `dist/<id>/assets/` per the
134
- // lib's ng-package.json `assets` field; the old *-remote projects
135
- // mirrored them via angular.json `assets` globs — same effect.
136
- const libAssets = path.join(workspaceDir, "dist", identity.id, "assets");
137
- if (fs.existsSync(libAssets)) {
138
- const targetAssets = path.join(outDir, identity.id, "remote", "assets");
139
- fs.cpSync(libAssets, targetAssets, { recursive: true });
140
- console.log(` + assets ${targetAssets}`);
141
- }
142
-
143
- const descriptorPath = emitDescriptor({
144
- outDir,
145
- id: identity.id,
146
- hostId: identity.hostId,
147
- remoteName: identity.remoteName,
148
- exposes: identity.exposes,
149
- displayName: identity.displayName,
150
- });
151
-
152
- console.log(` + descriptor ${descriptorPath}`);
153
- } finally {
154
- cleanup(synthetic.dir);
155
- }
156
- }
157
-
158
- function runWebpack(webpack, config) {
159
- return new Promise((resolve, reject) => {
160
- webpack(config, (err, stats) => {
161
- if (err) {
162
- reject(err);
163
- return;
164
- }
165
- const info = stats.toJson({ errors: true, warnings: true });
166
- if (stats.hasErrors()) {
167
- for (const e of info.errors) {
168
- console.error(e.message || e);
169
- }
170
- reject(new Error("webpack build failed"));
171
- return;
172
- }
173
- if (stats.hasWarnings()) {
174
- for (const w of info.warnings) {
175
- console.warn(w.message || w);
176
- }
177
- }
178
- console.log(
179
- stats.toString({
180
- colors: true,
181
- modules: false,
182
- children: false,
183
- chunks: false,
184
- chunkModules: false,
185
- entrypoints: false,
186
- })
187
- );
188
- resolve();
189
- });
190
- });
191
- }
192
-
193
- main().catch((e) => {
194
- console.error(e.stack || e.message || e);
195
- process.exit(1);
196
- });
package/hooks/ngcc.js DELETED
@@ -1,41 +0,0 @@
1
- // Built-in federation postInstall hook: run ngcc against the federation
2
- // node_modules tree so every @angular package gets `__processed_by_ivy_ngcc__`
3
- // markers and class definitions rewritten in place (ɵcmp/ɵdir/etc).
4
- //
5
- // Producers reference this hook from their lib's package.json:
6
- //
7
- // "wsmodules": {
8
- // "federationHooks": { "postInstall": "@ws-test-realm/admin-kit/hooks/ngcc" }
9
- // }
10
- //
11
- // The hook resolves `@angular/compiler-cli/ngcc` from the PRODUCER workspace
12
- // (where compiler-cli is installed as a dev dep, alongside ng-cli, etc.).
13
- // The federation tree itself only holds runtime peer deps and won't have
14
- // compiler-cli unless a lib explicitly declares it.
15
-
16
- const Module = require("module");
17
- const path = require("path");
18
-
19
- module.exports = function ngccHook({ federationDir, producerWorkspaceDir, libName }) {
20
- const requireFromProducer = Module.createRequire(
21
- path.join(producerWorkspaceDir, "package.json")
22
- );
23
- let ngcc;
24
- try {
25
- ngcc = requireFromProducer("@angular/compiler-cli/ngcc");
26
- } catch (e) {
27
- console.warn(
28
- `\x1b[33mWarning:\x1b[0m ngcc hook (${libName}): couldn't load @angular/compiler-cli/ngcc from ${producerWorkspaceDir}: ${e.message}`
29
- );
30
- return;
31
- }
32
-
33
- const basePath = path.join(federationDir, "node_modules");
34
- console.log(` ngcc: rewriting ${basePath}`);
35
- ngcc.process({
36
- basePath,
37
- propertiesToConsider: ["module_ivy_ngcc", "browser", "module", "main"],
38
- compileAllFormats: false,
39
- async: false,
40
- });
41
- };
@@ -1,40 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- // Scan <workspace>/dist/* for ng-package-style outputs and return a map
5
- // {<libName>: <absolute path to dist/<libName>>}. An ng-package output is
6
- // recognized by having a package.json that declares either "module" or
7
- // "main" — that distinguishes it from the *-remote outputs (which we want
8
- // to ignore — they are webpack bundles, not consumable libs).
9
- function discoverWorkspaceAliases(workspaceDir) {
10
- const distDir = path.join(workspaceDir, "dist");
11
- if (!fs.existsSync(distDir)) return {};
12
-
13
- const aliases = {};
14
- for (const name of fs.readdirSync(distDir)) {
15
- const dir = path.join(distDir, name);
16
- let stat;
17
- try {
18
- stat = fs.statSync(dir);
19
- } catch (e) {
20
- continue;
21
- }
22
- if (!stat.isDirectory()) continue;
23
-
24
- const pkgPath = path.join(dir, "package.json");
25
- if (!fs.existsSync(pkgPath)) continue;
26
-
27
- let pkg;
28
- try {
29
- pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
30
- } catch (e) {
31
- continue;
32
- }
33
- if (!pkg.module && !pkg.main) continue;
34
-
35
- aliases[name] = dir;
36
- }
37
- return aliases;
38
- }
39
-
40
- module.exports = { discoverWorkspaceAliases };
@@ -1,184 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const Module = require("module");
4
- const { execSync } = require("child_process");
5
-
6
- // Mirror a federation lib's peerDependencies into its dependencies in the
7
- // STAGED package.json under <adminDir>/.federation/<id>/. ng-packagr emits
8
- // peerDependencies — the canonical contract for a published library — but
9
- // for our publish-less federation we want npm to actually INSTALL those
10
- // peers (so consuming workspaces' TypeScript can resolve their types via
11
- // walk-up from .federation/<id>/). Promoting peer → dep in the staged copy
12
- // only doesn't touch the producer's source.
13
- function promotePeerDeps(libDir) {
14
- const pkgPath = path.join(libDir, "package.json");
15
- if (!fs.existsSync(pkgPath)) return false;
16
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
17
- if (!pkg.peerDependencies || !Object.keys(pkg.peerDependencies).length) {
18
- return false;
19
- }
20
- pkg.dependencies = Object.assign({}, pkg.dependencies, pkg.peerDependencies);
21
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
22
- return true;
23
- }
24
-
25
- // Reconcile the entire .federation/ dir as an npm workspace root. Lists
26
- // every lib subdir as a workspace, runs `npm install` (npm hoists shared
27
- // peer versions, nests only on per-lib disagreement), then runs each
28
- // lib's declared postInstall hooks against the freshly installed tree.
29
- //
30
- // Hooks let a federation lib ship arbitrary prep work that consumers run
31
- // automatically — e.g. ws-framework declares
32
- // `wsmodules.federationHooks.postInstall = "@ws-test-realm/admin-kit/hooks/ngcc"`
33
- // so the consumer's install pipeline Ivy-rewrites the Angular tree in
34
- // place. admin-kit knows nothing about Angular here; the producer's
35
- // declaration drives what runs.
36
- //
37
- // Idempotent. Re-runs at the end of every deploy.
38
- function syncFederationWorkspace(adminDir, producerWorkspaceDir) {
39
- const federationDir = path.join(adminDir, ".federation");
40
- if (!fs.existsSync(federationDir)) return { installed: false };
41
-
42
- const libs = listFederationLibs(federationDir);
43
- if (!libs.length) return { installed: false };
44
-
45
- writeWorkspaceManifest(federationDir, libs);
46
-
47
- console.log(
48
- `\n=== federation peers: install (${libs.length} workspace${libs.length === 1 ? "" : "s"}) ===`
49
- );
50
- console.log(` at ${federationDir}`);
51
- execSync("npm install --no-package-lock --no-audit --no-fund", {
52
- cwd: federationDir,
53
- stdio: "inherit",
54
- });
55
-
56
- runFederationHooks({ federationDir, libs, producerWorkspaceDir });
57
-
58
- return { installed: true, libs };
59
- }
60
-
61
- // Walk each federation lib's package.json for declared postInstall hooks
62
- // (`wsmodules.federationHooks.postInstall`, string or array). Dedupe by
63
- // hook reference — multiple libs declaring the same hook (e.g. all
64
- // Angular libs referencing the built-in ngcc hook) run it once. Each hook
65
- // is a CommonJS module exporting a default function with signature
66
- // `({ federationDir, libDir, libName, producerWorkspaceDir }) => void`.
67
- //
68
- // Hook reference resolution:
69
- // - "./..." or "../..." → relative to the lib's federation dir
70
- // - any other specifier → node `require` resolution, first trying
71
- // admin-kit's own neighborhood (built-in hooks
72
- // like @ws-test-realm/admin-kit/hooks/ngcc),
73
- // then the producer workspace (third-party
74
- // hook packages).
75
- function runFederationHooks({ federationDir, libs, producerWorkspaceDir }) {
76
- const queue = [];
77
- const seen = new Set();
78
- for (const libName of libs) {
79
- const libDir = path.join(federationDir, libName);
80
- const pkg = safeReadJson(path.join(libDir, "package.json"));
81
- const refs = collectHookRefs(pkg);
82
- for (const ref of refs) {
83
- if (seen.has(ref)) continue;
84
- seen.add(ref);
85
- queue.push({ ref, libDir, libName });
86
- }
87
- }
88
- if (!queue.length) return;
89
-
90
- console.log(
91
- `\n=== federation peers: postInstall hooks (${queue.length}) ===`
92
- );
93
- for (const { ref, libDir, libName } of queue) {
94
- console.log(` ${libName} → ${ref}`);
95
- let hookFn;
96
- try {
97
- hookFn = resolveHook({ ref, libDir, producerWorkspaceDir });
98
- } catch (e) {
99
- console.warn(
100
- ` \x1b[33mWarning:\x1b[0m couldn't load hook (${e.message})`
101
- );
102
- continue;
103
- }
104
- if (typeof hookFn !== "function") {
105
- console.warn(
106
- ` \x1b[33mWarning:\x1b[0m hook ${ref} did not export a function`
107
- );
108
- continue;
109
- }
110
- try {
111
- hookFn({ federationDir, libDir, libName, producerWorkspaceDir });
112
- } catch (e) {
113
- console.warn(
114
- ` \x1b[33mWarning:\x1b[0m hook ${ref} threw: ${e.message}`
115
- );
116
- }
117
- }
118
- }
119
-
120
- function collectHookRefs(pkg) {
121
- if (!pkg || !pkg.wsmodules || !pkg.wsmodules.federationHooks) return [];
122
- const v = pkg.wsmodules.federationHooks.postInstall;
123
- if (!v) return [];
124
- return Array.isArray(v) ? v : [v];
125
- }
126
-
127
- function resolveHook({ ref, libDir, producerWorkspaceDir }) {
128
- let resolvedPath;
129
- if (ref.startsWith("./") || ref.startsWith("../")) {
130
- resolvedPath = path.resolve(libDir, ref);
131
- } else {
132
- const adminKitRequire = Module.createRequire(
133
- path.join(__dirname, "..", "package.json")
134
- );
135
- try {
136
- resolvedPath = adminKitRequire.resolve(ref);
137
- } catch {
138
- const producerRequire = Module.createRequire(
139
- path.join(producerWorkspaceDir, "package.json")
140
- );
141
- resolvedPath = producerRequire.resolve(ref);
142
- }
143
- }
144
- return require(resolvedPath);
145
- }
146
-
147
- function listFederationLibs(federationDir) {
148
- return fs
149
- .readdirSync(federationDir, { withFileTypes: true })
150
- .filter((e) => e.isDirectory())
151
- .map((e) => e.name)
152
- .filter((name) => {
153
- if (name === "node_modules") return false;
154
- return fs.existsSync(path.join(federationDir, name, "package.json"));
155
- })
156
- .sort();
157
- }
158
-
159
- function writeWorkspaceManifest(federationDir, libs) {
160
- const pkg = {
161
- name: "ws-federation-host",
162
- private: true,
163
- version: "0.0.0",
164
- workspaces: libs.map((id) => `./${id}`),
165
- };
166
- fs.writeFileSync(
167
- path.join(federationDir, "package.json"),
168
- JSON.stringify(pkg, null, 2) + "\n"
169
- );
170
- }
171
-
172
- function safeReadJson(file) {
173
- try {
174
- return JSON.parse(fs.readFileSync(file, "utf8"));
175
- } catch {
176
- return null;
177
- }
178
- }
179
-
180
- module.exports = {
181
- promotePeerDeps,
182
- syncFederationWorkspace,
183
- listFederationLibs,
184
- };
@@ -1,90 +0,0 @@
1
- // Build the `shared` map for ModuleFederationPlugin.
2
- //
3
- // Layers (later wins):
4
- // 1. sharedDescriptors() from @wiresphere/shared (host baseline:
5
- // @angular/*, rxjs, etc.).
6
- // 2. Workspace-discovered libs (from alias map) as WORKSPACE_LIBS shape.
7
- // 3. Self-lib forced to import: false so the container does not load
8
- // itself back through the shared scope.
9
- // 4. sharedExtras from pack.config.json (project overrides).
10
- //
11
- // Then for any entry left with `requiredVersion: 'auto'` we resolve the
12
- // version from <workspaceDir>/node_modules/<pkg>/package.json BEFORE handing
13
- // the map to share(). share()'s built-in auto-probe reads the workspace's
14
- // own package.json, which no longer lists Angular/rxjs/etc. directly — those
15
- // are pulled in transitively via @wiresphere/shared. Probing node_modules
16
- // closes that gap.
17
- const fs = require("fs");
18
- const path = require("path");
19
-
20
- function packageRootOf(key) {
21
- if (key.startsWith("@")) {
22
- const parts = key.split("/");
23
- return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : key;
24
- }
25
- return key.split("/")[0];
26
- }
27
-
28
- function tryReadVersion(workspaceDir, pkgRoot) {
29
- const pkgJsonPath = path.join(
30
- workspaceDir,
31
- "node_modules",
32
- pkgRoot,
33
- "package.json"
34
- );
35
- try {
36
- return JSON.parse(fs.readFileSync(pkgJsonPath, "utf8")).version || null;
37
- } catch {
38
- return null;
39
- }
40
- }
41
-
42
- function resolveAutoVersions(map, workspaceDir) {
43
- const out = {};
44
- for (const [key, val] of Object.entries(map)) {
45
- if (val && val.requiredVersion === "auto") {
46
- const version = tryReadVersion(workspaceDir, packageRootOf(key));
47
- out[key] = Object.assign({}, val, {
48
- requiredVersion: version || false,
49
- });
50
- } else {
51
- out[key] = val;
52
- }
53
- }
54
- return out;
55
- }
56
-
57
- function buildSharedMap({ workspaceDir, aliasMap, selfId, sharedExtras }) {
58
- const wsShared = require("@ws-test-realm/shared");
59
- const { share } = require("@angular-architects/module-federation/webpack");
60
-
61
- const WORKSPACE_LIBS = wsShared.WORKSPACE_LIBS || {
62
- singleton: true,
63
- strictVersion: false,
64
- requiredVersion: false,
65
- };
66
-
67
- const map = Object.assign({}, wsShared.sharedDescriptors());
68
-
69
- for (const libName of Object.keys(aliasMap)) {
70
- if (libName.endsWith("-remote")) continue;
71
- if (libName === selfId) {
72
- if (!map[libName]) {
73
- map[libName] = Object.assign({}, WORKSPACE_LIBS);
74
- }
75
- } else {
76
- map[libName] = Object.assign({}, WORKSPACE_LIBS, {
77
- import: false,
78
- });
79
- }
80
- }
81
-
82
- for (const [k, v] of Object.entries(sharedExtras || {})) {
83
- map[k] = Object.assign({}, map[k] || {}, v);
84
- }
85
-
86
- const resolved = resolveAutoVersions(map, workspaceDir);
87
- return share(resolved, workspaceDir);
88
- }
89
-
90
- module.exports = { buildSharedMap };
@@ -1,27 +0,0 @@
1
- const fs = require("fs");
2
- const os = require("os");
3
- const path = require("path");
4
- const crypto = require("crypto");
5
-
6
- // Writes a temp directory containing a single `public-api.ts` that
7
- // re-exports everything from the lib's import path. Webpack's MF
8
- // `exposes['./Module']` points at this file; the import resolves
9
- // through the workspace alias for `<id>` (set up in webpack.resolve).
10
- function writeSyntheticEntry(id) {
11
- const hash = crypto.randomBytes(4).toString("hex");
12
- const dir = path.join(os.tmpdir(), "ws-pack-remote", `${id}-${hash}`);
13
- fs.mkdirSync(dir, { recursive: true });
14
- const file = path.join(dir, "public-api.ts");
15
- fs.writeFileSync(file, `export * from '${id}';\n`, "utf8");
16
- return { dir, file };
17
- }
18
-
19
- function cleanup(dir) {
20
- try {
21
- fs.rmSync(dir, { recursive: true, force: true });
22
- } catch (e) {
23
- // best-effort cleanup
24
- }
25
- }
26
-
27
- module.exports = { writeSyntheticEntry, cleanup };
@@ -1,120 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const Module = require("module");
4
- const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
5
-
6
- // Build the in-memory webpack config used to produce a federated remote
7
- // from a single synthetic entry that re-exports the lib's public API.
8
- //
9
- // We resolve the workspace's webpack + ModuleFederationPlugin so the
10
- // build runs against the same versions used by the workspace's own
11
- // dependency graph (avoids shared-scope ABI surprises).
12
- function buildWebpackConfig({
13
- workspaceDir,
14
- syntheticEntry,
15
- outDir,
16
- id,
17
- remoteName,
18
- exposes,
19
- aliasMap,
20
- sharedMap,
21
- }) {
22
- const requireFromWorkspace = Module.createRequire(
23
- path.join(workspaceDir, "package.json")
24
- );
25
- const ModuleFederationPlugin = requireFromWorkspace(
26
- "webpack/lib/container/ModuleFederationPlugin"
27
- );
28
- const babelLoader = requireFromWorkspace.resolve("babel-loader");
29
- const linkerPlugin = requireFromWorkspace.resolve(
30
- "@angular/compiler-cli/linker/babel"
31
- );
32
-
33
- const remoteOutDir = path.join(outDir, id, "remote");
34
-
35
- // tsconfig.federation.json is the SSOT for cross-workspace paths
36
- // (`@ws-remote/*` → `<adminDir>/.federation/*`, sibling lib aliases,
37
- // etc.). Without this plugin webpack ignores tsconfig `paths` and
38
- // walks node_modules — which fails for cross-workspace federation
39
- // siblings that only live in the host's federation dir.
40
- const federationTsconfig = path.join(workspaceDir, "tsconfig.federation.json");
41
- const resolvePlugins = [];
42
- if (fs.existsSync(federationTsconfig)) {
43
- resolvePlugins.push(
44
- new TsconfigPathsPlugin({ configFile: federationTsconfig })
45
- );
46
- }
47
-
48
- return {
49
- mode: "production",
50
- entry: syntheticEntry,
51
- context: workspaceDir,
52
- // Terser's default class-name mangling strips identifying info
53
- // Angular's runtime relies on (you get `'_' is neither
54
- // ComponentType nor DirectiveType` errors). Angular CLI's builder
55
- // configures Terser with Angular-aware options; we don't, so keep
56
- // minimization off until that config is ported. Output is larger
57
- // but functionally correct.
58
- optimization: {
59
- minimize: false,
60
- },
61
- output: {
62
- path: remoteOutDir,
63
- publicPath: "auto",
64
- uniqueName: remoteName,
65
- // Content-hash all chunks so the controller can serve them
66
- // with immutable caching. The MF plugin overrides this to
67
- // "remoteEntry.js" for the entry only — that one stays at a
68
- // stable URL and is revalidated each load.
69
- filename: "[name].[contenthash:10].js",
70
- chunkFilename: "[name].[contenthash:10].js",
71
- },
72
- resolve: {
73
- alias: aliasMap,
74
- plugins: resolvePlugins,
75
- extensions: [".ts", ".js", ".mjs", ".json"],
76
- // ngcc rewrites Angular packages in-place but leaves the
77
- // originals next to them, exposing both via package.json fields
78
- // (`module` for the partial-Ivy original, `module_ivy_ngcc` for
79
- // the full-Ivy processed copy). Webpack's default mainFields
80
- // doesn't know about the latter — it resolves to the partial
81
- // version and components show up as `'MatInput' is neither
82
- // ComponentType nor DirectiveType` because their `ɵdir` static
83
- // is missing. Angular CLI's builder injects this same ordering.
84
- mainFields: ["module_ivy_ngcc", "browser", "module", "main"],
85
- },
86
- module: {
87
- rules: [
88
- // Run the Angular partial-Ivy linker over every JS module.
89
- // ng-packagr emits libs in compilationMode "partial" by
90
- // default since v12; the runtime can't always process
91
- // partial declarations directly (we hit
92
- // `meta.deps.map is not a function` in ɵɵngDeclareFactory).
93
- // Babel-with-linker rewrites the partial declarations to
94
- // full Ivy at bundle time — same step that Angular CLI's
95
- // custom-webpack:browser builder does implicitly.
96
- {
97
- test: /\.m?js$/,
98
- loader: babelLoader,
99
- options: {
100
- compact: false,
101
- cacheDirectory: true,
102
- plugins: [linkerPlugin],
103
- },
104
- resolve: { fullySpecified: false },
105
- },
106
- ],
107
- },
108
- plugins: [
109
- new ModuleFederationPlugin({
110
- name: remoteName,
111
- library: { type: "window", name: remoteName },
112
- filename: "remoteEntry.js",
113
- exposes: { [exposes]: syntheticEntry },
114
- shared: sharedMap,
115
- }),
116
- ],
117
- };
118
- }
119
-
120
- module.exports = { buildWebpackConfig };
@@ -1,8 +0,0 @@
1
- {
2
- "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
- "dest": "../../dist/__name__",
4
- "assets": ["./assets"],
5
- "lib": {
6
- "entryFile": "src/public-api.ts"
7
- }
8
- }
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../out-tsc/lib",
5
- "target": "es2015",
6
- "declaration": true,
7
- "declarationMap": true,
8
- "inlineSources": true,
9
- "types": [],
10
- "lib": ["dom", "es2018"]
11
- },
12
- "exclude": ["**/*.spec.ts"]
13
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.lib.json",
3
- "compilerOptions": {
4
- "declarationMap": false
5
- },
6
- "angularCompilerOptions": {
7
- "compilationMode": "partial"
8
- }
9
- }