@ws-test-realm/admin-kit 0.1.11 → 0.1.14
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/hooks/ngcc.js +41 -0
- package/lib/deploy-remotes.js +4 -14
- package/lib/federation-peers.js +109 -5
- package/lib/purge-remotes.js +1 -1
- package/package.json +2 -1
package/hooks/ngcc.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
};
|
package/lib/deploy-remotes.js
CHANGED
|
@@ -7,27 +7,17 @@ const { loadDeployContext, resolveTarget } = require("./target-resolution");
|
|
|
7
7
|
const { promotePeerDeps, syncFederationWorkspace } = require("./federation-peers");
|
|
8
8
|
|
|
9
9
|
function copyTypes(distLib, hostFederationDir) {
|
|
10
|
-
// Preserve any existing node_modules dir under the staged location —
|
|
11
|
-
// npm's workspace install populated it and the next syncFederationWorkspace
|
|
12
|
-
// run will reconcile. Replacing it on every type-copy would force a
|
|
13
|
-
// fresh per-lib install instead of letting npm hoist across siblings.
|
|
14
|
-
const preservedNodeModules = path.join(hostFederationDir, "node_modules");
|
|
15
|
-
let cached = null;
|
|
16
|
-
if (fs.existsSync(preservedNodeModules)) {
|
|
17
|
-
cached = preservedNodeModules + `.preserve-${process.pid}`;
|
|
18
|
-
fs.renameSync(preservedNodeModules, cached);
|
|
19
|
-
}
|
|
20
10
|
if (fs.existsSync(hostFederationDir)) {
|
|
21
11
|
fs.rmSync(hostFederationDir, { recursive: true, force: true });
|
|
22
12
|
}
|
|
23
13
|
fs.mkdirSync(hostFederationDir, { recursive: true });
|
|
24
14
|
fs.cpSync(distLib, hostFederationDir, { recursive: true });
|
|
25
|
-
if (cached) {
|
|
26
|
-
fs.renameSync(cached, preservedNodeModules);
|
|
27
|
-
}
|
|
28
15
|
// Promote peerDependencies → dependencies in the staged package.json so
|
|
29
16
|
// npm's workspace install actually installs them. The producer's source
|
|
30
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>/.
|
|
31
21
|
promotePeerDeps(hostFederationDir);
|
|
32
22
|
}
|
|
33
23
|
|
|
@@ -179,7 +169,7 @@ function deployRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
|
|
|
179
169
|
// conflicts. Consumers' TypeScript walks up from .federation/<id>/ and
|
|
180
170
|
// resolves transitive types without consumer-side dep management.
|
|
181
171
|
try {
|
|
182
|
-
syncFederationWorkspace(ctx.adminDir);
|
|
172
|
+
syncFederationWorkspace(ctx.adminDir, workspaceDir);
|
|
183
173
|
} catch (e) {
|
|
184
174
|
console.warn(
|
|
185
175
|
`\x1b[33mWarning:\x1b[0m federation peer install failed: ${e.message}`
|
package/lib/federation-peers.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
+
const Module = require("module");
|
|
3
4
|
const { execSync } = require("child_process");
|
|
4
5
|
|
|
5
6
|
// Mirror a federation lib's peerDependencies into its dependencies in the
|
|
@@ -22,13 +23,19 @@ function promotePeerDeps(libDir) {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
// Reconcile the entire .federation/ dir as an npm workspace root. Lists
|
|
25
|
-
// every lib subdir as a workspace,
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
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.
|
|
29
36
|
//
|
|
30
37
|
// Idempotent. Re-runs at the end of every deploy.
|
|
31
|
-
function syncFederationWorkspace(adminDir) {
|
|
38
|
+
function syncFederationWorkspace(adminDir, producerWorkspaceDir) {
|
|
32
39
|
const federationDir = path.join(adminDir, ".federation");
|
|
33
40
|
if (!fs.existsSync(federationDir)) return { installed: false };
|
|
34
41
|
|
|
@@ -45,9 +52,98 @@ function syncFederationWorkspace(adminDir) {
|
|
|
45
52
|
cwd: federationDir,
|
|
46
53
|
stdio: "inherit",
|
|
47
54
|
});
|
|
55
|
+
|
|
56
|
+
runFederationHooks({ federationDir, libs, producerWorkspaceDir });
|
|
57
|
+
|
|
48
58
|
return { installed: true, libs };
|
|
49
59
|
}
|
|
50
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
|
+
|
|
51
147
|
function listFederationLibs(federationDir) {
|
|
52
148
|
return fs
|
|
53
149
|
.readdirSync(federationDir, { withFileTypes: true })
|
|
@@ -73,6 +169,14 @@ function writeWorkspaceManifest(federationDir, libs) {
|
|
|
73
169
|
);
|
|
74
170
|
}
|
|
75
171
|
|
|
172
|
+
function safeReadJson(file) {
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
76
180
|
module.exports = {
|
|
77
181
|
promotePeerDeps,
|
|
78
182
|
syncFederationWorkspace,
|
package/lib/purge-remotes.js
CHANGED
|
@@ -127,7 +127,7 @@ function purgeRemotes({
|
|
|
127
127
|
// claiming workspace slots).
|
|
128
128
|
if (actions.some((a) => a.typesRemoved)) {
|
|
129
129
|
try {
|
|
130
|
-
syncFederationWorkspace(ctx.adminDir);
|
|
130
|
+
syncFederationWorkspace(ctx.adminDir, workspaceDir);
|
|
131
131
|
} catch (e) {
|
|
132
132
|
console.warn(
|
|
133
133
|
`\x1b[33mWarning:\x1b[0m federation peer resync failed: ${e.message}`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ws-test-realm/admin-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
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.",
|
|
5
5
|
"license": "Artistic-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"bin",
|
|
23
23
|
"lib",
|
|
24
|
+
"hooks",
|
|
24
25
|
"template",
|
|
25
26
|
"template-module",
|
|
26
27
|
"README.md"
|