@ws-test-realm/admin-kit 0.2.4-ng16 → 0.2.5-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.
@@ -1,16 +1,26 @@
1
1
  #!/usr/bin/env node
2
- // ws-sync-paths — regenerate tsconfig.federation.json from current workspace
3
- // state. Scans angular.json for sibling-dist aliases and reads wsconfig.json
4
- // for the @ws-remote/* mapping target. Safe to run any time; written
5
- // automatically by ws-init-workspace, ws-wire-host, ws-generate-module, and
6
- // ws-drop-module.
2
+ // ws-sync-paths — refresh the workspace's federation wiring from current
3
+ // state. Does two things:
4
+ //
5
+ // 1. Regenerates `tsconfig.federation.json` from angular.json sibling
6
+ // projects (workspace-local dist paths).
7
+ // 2. Refreshes `node_modules/<name>` symlinks pointing at the fiddle's
8
+ // `.federation/<name>/` for each cross-workspace federation sibling
9
+ // published into the fiddle. Skips any name that's a workspace-local
10
+ // project (npm-workspaces already owns its symlink). With these symlinks
11
+ // in place, cross-workspace consumers import federation siblings via
12
+ // ordinary bare specifiers — no per-workspace package.json file: refs.
13
+ //
14
+ // Safe to run any time. Invoked automatically by ws-init-workspace,
15
+ // ws-wire-host, ws-generate-module, ws-drop-module, and the workspace's
16
+ // postinstall hook.
7
17
  //
8
18
  // Usage:
9
19
  // ws-sync-paths
10
20
 
11
21
  const fs = require("fs");
12
22
  const path = require("path");
13
- const { rebuildFederationTsconfig } = require("../lib/wire-host");
23
+ const { rebuildFederationTsconfig, syncFederationSymlinks } = require("../lib/wire-host");
14
24
 
15
25
  function main() {
16
26
  const cwd = process.cwd();
@@ -20,12 +30,20 @@ function main() {
20
30
  }
21
31
  try {
22
32
  const r = rebuildFederationTsconfig(cwd);
23
- const siblings = Object.keys(r.paths).filter((k) => k !== "@ws-remote/*");
33
+ const siblings = Object.keys(r.paths).filter((k) => k !== "*");
24
34
  console.log(`Wrote ${r.file}`);
25
35
  console.log(` sibling aliases: ${siblings.length ? siblings.join(", ") : "(none)"}`);
26
- console.log(
27
- ` @ws-remote/* -> ${r.federationTarget || "(not wired; run ws-wire-host <wpm-root>)"}`
28
- );
36
+
37
+ const sym = syncFederationSymlinks(cwd);
38
+ if (sym.skipped) {
39
+ console.log(`Federation symlinks: ${sym.skipped}`);
40
+ } else {
41
+ const parts = [];
42
+ if (sym.created.length) parts.push(`created [${sym.created.join(", ")}]`);
43
+ if (sym.updated.length) parts.push(`updated [${sym.updated.join(", ")}]`);
44
+ if (sym.kept.length) parts.push(`kept [${sym.kept.join(", ")}]`);
45
+ console.log(`Federation symlinks: ${parts.length ? parts.join(", ") : "(none)"}`);
46
+ }
29
47
  } catch (e) {
30
48
  console.error(e.message);
31
49
  process.exit(1);
package/lib/wire-host.js CHANGED
@@ -51,17 +51,12 @@ function rebuildFederationTsconfig(workspaceDir) {
51
51
  if (wsconfig.wpmRoot) {
52
52
  const hostDir = resolveHostDir(wsconfig.wpmRoot);
53
53
  federationTarget = path.join(hostDir, ".federation", "*");
54
- paths["@ws-remote/*"] = [federationTarget];
55
54
  }
56
- // Catch-all fallback: any unresolved import resolves against this
57
- // workspace's own node_modules. Required for cross-workspace federation
58
- // siblings (e.g. @ws-remote/ws-framework) whose .d.ts files reference
59
- // transitive deps (@angular/material/form-field, etc.) — TS walks up
60
- // from the .d.ts file's location to find node_modules, but federation
61
- // remotes live under <wpmRoot>/admin/.federation/ outside any consumer
62
- // workspace's tree. This catch-all lets TS fall back to the consumer's
63
- // node_modules regardless of where the .d.ts lives. Must come AFTER
64
- // specific paths so they win on prefix match.
55
+ // Catch-all fallback: unresolved imports resolve against this workspace's
56
+ // own node_modules. Cross-workspace federation siblings reach the consumer
57
+ // via admin-kit-managed symlinks at `node_modules/<name>` pointing at
58
+ // `<wpmRoot>/admin/.federation/<name>/` (see syncFederationSymlinks); from
59
+ // the consumer's POV they look like ordinary packages.
65
60
  paths["*"] = ["./node_modules/*"];
66
61
  // tsconfig.federation.json is the leaf of the workspace-local extends
67
62
  // chain. Its sole responsibility is to layer workspace-specific `paths`
@@ -73,7 +68,13 @@ function rebuildFederationTsconfig(workspaceDir) {
73
68
  // by bumping admin-kit, no local file edits required.
74
69
  const config = {
75
70
  extends: "@ws-test-realm/admin-kit/tsconfig.workspace.json",
76
- compilerOptions: { paths },
71
+ compilerOptions: {
72
+ // `paths` are interpreted relative to `baseUrl`; both belong on the
73
+ // workspace-local layer (this file). TS requires baseUrl when paths
74
+ // are set.
75
+ baseUrl: "./",
76
+ paths,
77
+ },
77
78
  };
78
79
  const file = path.join(workspaceDir, "tsconfig.federation.json");
79
80
  fs.writeFileSync(file, JSON.stringify(config, null, "\t") + "\n");
@@ -102,9 +103,81 @@ function applyWpmRoot(workspaceDir, wpmRoot, hostId) {
102
103
  };
103
104
  }
104
105
 
106
+ // For each federation lib published into the fiddle's `.federation/<name>/`,
107
+ // ensure a symlink at `<workspace>/node_modules/<name>` points at it. This
108
+ // makes cross-workspace federation siblings indistinguishable from in-workspace
109
+ // ones for everything downstream: TS resolves bare imports through
110
+ // node_modules; native-federation's `getPackageInfo` finds `package.json`;
111
+ // esbuild externalizes via the share map normally; the federation runtime
112
+ // dedupes to a singleton chunk at runtime via `singleton: true`.
113
+ //
114
+ // Skips any name that is itself a workspace-local project (entries in
115
+ // angular.json), because for those the npm-workspaces machinery already
116
+ // owns `node_modules/<name>` and points it at `projects/<name>`.
117
+ //
118
+ // Idempotent: leaves correct existing symlinks untouched, replaces stale
119
+ // ones, creates missing ones.
120
+ function syncFederationSymlinks(workspaceDir) {
121
+ const wsconfig = readWsconfig(workspaceDir);
122
+ if (!wsconfig.wpmRoot) {
123
+ return { skipped: "no wpmRoot wired", created: [], updated: [], kept: [] };
124
+ }
125
+ const hostDir = resolveHostDir(wsconfig.wpmRoot);
126
+ const federationDir = path.join(hostDir, ".federation");
127
+ if (!fs.existsSync(federationDir)) {
128
+ return { skipped: `no ${federationDir}`, created: [], updated: [], kept: [] };
129
+ }
130
+ const angularJsonPath = path.join(workspaceDir, "angular.json");
131
+ const localProjects = new Set();
132
+ if (fs.existsSync(angularJsonPath)) {
133
+ const angular = JSON.parse(fs.readFileSync(angularJsonPath, "utf8"));
134
+ for (const k of Object.keys(angular.projects || {})) localProjects.add(k);
135
+ }
136
+ const nodeModulesDir = path.join(workspaceDir, "node_modules");
137
+ fs.mkdirSync(nodeModulesDir, { recursive: true });
138
+
139
+ const created = [];
140
+ const updated = [];
141
+ const kept = [];
142
+
143
+ for (const entry of fs.readdirSync(federationDir, { withFileTypes: true })) {
144
+ if (!entry.isDirectory()) continue;
145
+ const name = entry.name;
146
+ if (localProjects.has(name)) continue;
147
+ if (!fs.existsSync(path.join(federationDir, name, "package.json"))) continue;
148
+
149
+ const target = path.join(federationDir, name);
150
+ const linkPath = path.join(nodeModulesDir, name);
151
+
152
+ if (fs.existsSync(linkPath)) {
153
+ const stat = fs.lstatSync(linkPath);
154
+ if (stat.isSymbolicLink()) {
155
+ const current = fs.readlinkSync(linkPath);
156
+ const currentAbs = path.isAbsolute(current)
157
+ ? current
158
+ : path.resolve(path.dirname(linkPath), current);
159
+ if (currentAbs === target) {
160
+ kept.push(name);
161
+ continue;
162
+ }
163
+ fs.unlinkSync(linkPath);
164
+ fs.symlinkSync(target, linkPath);
165
+ updated.push(name);
166
+ continue;
167
+ }
168
+ // Real directory or file — don't touch (could be npm-managed).
169
+ continue;
170
+ }
171
+ fs.symlinkSync(target, linkPath);
172
+ created.push(name);
173
+ }
174
+ return { created, updated, kept };
175
+ }
176
+
105
177
  module.exports = {
106
178
  readWsconfig,
107
179
  writeWsconfig,
108
180
  applyWpmRoot,
109
181
  rebuildFederationTsconfig,
182
+ syncFederationSymlinks,
110
183
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ws-test-realm/admin-kit",
3
- "version": "0.2.4-ng16",
3
+ "version": "0.2.5-ng16",
4
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": {