@ws-test-realm/admin-kit 0.1.7

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 (46) hide show
  1. package/README.md +89 -0
  2. package/bin/ws-drop-module.js +40 -0
  3. package/bin/ws-generate-module.js +37 -0
  4. package/bin/ws-init-workspace.js +190 -0
  5. package/bin/ws-modules.js +179 -0
  6. package/bin/ws-pack-remote.js +196 -0
  7. package/bin/ws-sync-paths.js +35 -0
  8. package/bin/ws-wire-host.js +67 -0
  9. package/bin/ws-wire-pom.js +132 -0
  10. package/lib/alias-discovery.js +40 -0
  11. package/lib/build-modules.js +84 -0
  12. package/lib/cli-args.js +55 -0
  13. package/lib/deploy-remotes.js +249 -0
  14. package/lib/drop-module.js +71 -0
  15. package/lib/emit-descriptor.js +31 -0
  16. package/lib/generate-module.js +105 -0
  17. package/lib/identity.js +49 -0
  18. package/lib/jar-glob.js +55 -0
  19. package/lib/pack-into-jar.js +74 -0
  20. package/lib/pom.js +84 -0
  21. package/lib/shared-deps.js +90 -0
  22. package/lib/synthetic-entry.js +27 -0
  23. package/lib/template-copy.js +136 -0
  24. package/lib/topo-sort.js +134 -0
  25. package/lib/webpack-factory.js +104 -0
  26. package/lib/wire-host.js +96 -0
  27. package/package.json +33 -0
  28. package/template/angular.json +14 -0
  29. package/template/package.json +26 -0
  30. package/template/tsconfig.json +7 -0
  31. package/template/wsconfig.json.example +4 -0
  32. package/template-module/assets/i18n/de.json +1 -0
  33. package/template-module/assets/i18n/en.json +1 -0
  34. package/template-module/assets/i18n/fr.json +1 -0
  35. package/template-module/assets/i18n/general/de.json +1 -0
  36. package/template-module/assets/i18n/general/en.json +1 -0
  37. package/template-module/assets/i18n/general/fr.json +1 -0
  38. package/template-module/ng-package.json +8 -0
  39. package/template-module/package.json +14 -0
  40. package/template-module/src/lib/__name__.module.ts +11 -0
  41. package/template-module/src/lib/components/__name__/__name__.component.html +1 -0
  42. package/template-module/src/lib/components/__name__/__name__.component.scss +0 -0
  43. package/template-module/src/lib/components/__name__/__name__.component.ts +8 -0
  44. package/template-module/src/public-api.ts +6 -0
  45. package/template-module/tsconfig.lib.json +13 -0
  46. package/template-module/tsconfig.lib.prod.json +9 -0
@@ -0,0 +1,196 @@
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
+ });
@@ -0,0 +1,35 @@
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.
7
+ //
8
+ // Usage:
9
+ // ws-sync-paths
10
+
11
+ const fs = require("fs");
12
+ const path = require("path");
13
+ const { rebuildFederationTsconfig } = require("../lib/wire-host");
14
+
15
+ function main() {
16
+ const cwd = process.cwd();
17
+ if (!fs.existsSync(path.join(cwd, "angular.json"))) {
18
+ console.error(`No angular.json in ${cwd} (not a workspace).`);
19
+ process.exit(1);
20
+ }
21
+ try {
22
+ const r = rebuildFederationTsconfig(cwd);
23
+ const siblings = Object.keys(r.paths).filter((k) => k !== "@ws-remote/*");
24
+ console.log(`Wrote ${r.file}`);
25
+ 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
+ );
29
+ } catch (e) {
30
+ console.error(e.message);
31
+ process.exit(1);
32
+ }
33
+ }
34
+
35
+ main();
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ // ws-wire-host — write/merge the wpm-root pointer into <pwd>/wsconfig.json.
3
+ // Re-runnable to re-point an existing workspace at a different wpm project.
4
+ //
5
+ // The admin project itself is derived at consumption time from
6
+ // `<wpmRoot>/modules.json` (adminBuildFolder field), so we record only the
7
+ // wpm root here.
8
+ //
9
+ // Usage:
10
+ // ws-wire-host <wpm-root-path> [--host-id <id>]
11
+ //
12
+ // Defaults:
13
+ // --host-id admin
14
+
15
+ const { applyWpmRoot } = require("../lib/wire-host");
16
+
17
+ function parseArgs(argv) {
18
+ const aliases = { "host-id": "hostId" };
19
+ const args = {};
20
+ const positional = [];
21
+ for (let i = 2; i < argv.length; i++) {
22
+ const a = argv[i];
23
+ if (a.startsWith("--")) {
24
+ const key = a.slice(2);
25
+ const mapped = aliases[key];
26
+ if (!mapped) {
27
+ console.error(`Unknown flag: --${key}`);
28
+ process.exit(1);
29
+ }
30
+ const val = argv[i + 1];
31
+ if (val === undefined || val.startsWith("--")) {
32
+ args[mapped] = true;
33
+ } else {
34
+ args[mapped] = val;
35
+ i++;
36
+ }
37
+ } else {
38
+ positional.push(a);
39
+ }
40
+ }
41
+ args.wpmRoot = positional[0];
42
+ return args;
43
+ }
44
+
45
+ function main() {
46
+ const args = parseArgs(process.argv);
47
+ if (!args.wpmRoot) {
48
+ console.error("Usage: ws-wire-host <wpm-root-path> [--host-id <id>]");
49
+ process.exit(1);
50
+ }
51
+ const hostId =
52
+ args.hostId && args.hostId !== true ? args.hostId : "admin";
53
+ try {
54
+ const result = applyWpmRoot(process.cwd(), args.wpmRoot, hostId);
55
+ console.log(
56
+ `Wired wsconfig.json:\n wpmRoot = ${result.wpmRoot}\n hostId = ${result.hostId}`
57
+ );
58
+ console.log(
59
+ `Wrote tsconfig.federation.json:\n @ws-remote/* -> ${result.federationTarget}`
60
+ );
61
+ } catch (e) {
62
+ console.error(e.message);
63
+ process.exit(1);
64
+ }
65
+ }
66
+
67
+ main();
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ // ws-wire-pom — record a pom path for one of the workspace's projects.
3
+ //
4
+ // Usage:
5
+ // ws-wire-pom [<module-name>] <pom-path>
6
+ //
7
+ // <pom-path> may be absolute or relative to PWD. The script resolves it,
8
+ // validates it points at a real pom file within the workspace's module scope
9
+ // (parent dir of the workspace), then writes `wsmodules.pom` into the matching
10
+ // `projects/<name>/package.json` as a path relative to that project dir.
11
+ //
12
+ // If the workspace has exactly one project in angular.json, the module name
13
+ // argument can be omitted (it's implied). With more than one project it must
14
+ // be specified explicitly.
15
+
16
+ const fs = require("fs");
17
+ const path = require("path");
18
+ const {
19
+ validatePomPath,
20
+ relativeFromProject,
21
+ readArtifactId,
22
+ getScopeRoot,
23
+ } = require("../lib/pom");
24
+
25
+ function parseArgs(argv) {
26
+ const positional = [];
27
+ for (let i = 2; i < argv.length; i++) {
28
+ const a = argv[i];
29
+ if (a.startsWith("--")) {
30
+ console.error(`Unknown flag: ${a}`);
31
+ process.exit(1);
32
+ }
33
+ positional.push(a);
34
+ }
35
+ return positional;
36
+ }
37
+
38
+ function resolveProjectName(workspaceDir, explicit) {
39
+ const angularJsonPath = path.join(workspaceDir, "angular.json");
40
+ if (!fs.existsSync(angularJsonPath)) {
41
+ throw new Error(`No angular.json in ${workspaceDir} (not a workspace).`);
42
+ }
43
+ const projects = Object.keys(
44
+ JSON.parse(fs.readFileSync(angularJsonPath, "utf8")).projects || {}
45
+ );
46
+ if (explicit) {
47
+ if (!projects.includes(explicit)) {
48
+ throw new Error(
49
+ `Unknown project '${explicit}'. Workspace has: ${projects.join(", ") || "(none)"}`
50
+ );
51
+ }
52
+ return explicit;
53
+ }
54
+ if (projects.length === 0) {
55
+ throw new Error(
56
+ `Workspace has no projects. Run \`npm run generate-module <name>\` first.`
57
+ );
58
+ }
59
+ if (projects.length > 1) {
60
+ throw new Error(
61
+ `Multiple projects in workspace (${projects.join(", ")}); specify module name explicitly: ws-wire-pom <name> <pom>`
62
+ );
63
+ }
64
+ return projects[0];
65
+ }
66
+
67
+ function writePomField(workspaceDir, projectName, relPath) {
68
+ const pkgPath = path.join(workspaceDir, "projects", projectName, "package.json");
69
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
70
+ pkg.wsmodules = pkg.wsmodules || {};
71
+ pkg.wsmodules.pom = relPath;
72
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, "\t") + "\n");
73
+ return pkgPath;
74
+ }
75
+
76
+ function main() {
77
+ const args = parseArgs(process.argv);
78
+ if (!args.length || args.length > 2) {
79
+ console.error(
80
+ "Usage: ws-wire-pom [<module-name>] <pom-path>\n" +
81
+ " Module name is required when the workspace has multiple projects."
82
+ );
83
+ process.exit(1);
84
+ }
85
+
86
+ const workspaceDir = process.cwd();
87
+ const [first, second] = args;
88
+ const pomArg = second || first;
89
+ const moduleArg = second ? first : null;
90
+
91
+ let projectName;
92
+ try {
93
+ projectName = resolveProjectName(workspaceDir, moduleArg);
94
+ } catch (e) {
95
+ console.error(e.message);
96
+ process.exit(1);
97
+ }
98
+
99
+ const absPom = path.resolve(workspaceDir, pomArg);
100
+ try {
101
+ validatePomPath(workspaceDir, absPom);
102
+ } catch (e) {
103
+ console.error(e.message);
104
+ process.exit(1);
105
+ }
106
+
107
+ // Read artifactId now to fail fast if the target isn't a real pom (wrong
108
+ // file type, missing top-level <artifactId>, malformed XML, etc.). Avoids
109
+ // writing a bad wsmodules.pom path that would only surface at deploy time.
110
+ let artifactId;
111
+ try {
112
+ artifactId = readArtifactId(absPom);
113
+ } catch (e) {
114
+ console.error(e.message);
115
+ process.exit(1);
116
+ }
117
+
118
+ const rel = relativeFromProject(workspaceDir, projectName, absPom);
119
+ const pkgPath = writePomField(workspaceDir, projectName, rel);
120
+
121
+ console.log(`Wired pom for project '${projectName}':`);
122
+ console.log(` package.json: ${pkgPath}`);
123
+ console.log(` wsmodules.pom = ${JSON.stringify(rel)}`);
124
+ console.log(` resolves to: ${absPom}`);
125
+ console.log(` scope root: ${getScopeRoot(workspaceDir)}`);
126
+ if (artifactId) {
127
+ console.log(` artifactId: ${artifactId}`);
128
+ console.log(` jar glob: ${artifactId}-*.jar`);
129
+ }
130
+ }
131
+
132
+ main();
@@ -0,0 +1,40 @@
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 };
@@ -0,0 +1,84 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { execSync } = require("child_process");
4
+ const { buildDependencyGraph, topologicalSort } = require("./topo-sort");
5
+
6
+ function loadOrder({ workspaceDir, restrictTo, withDeps = false }) {
7
+ const angularJsonPath = path.join(workspaceDir, "angular.json");
8
+ if (!fs.existsSync(angularJsonPath)) {
9
+ throw new Error(`No angular.json in ${workspaceDir} (not a workspace).`);
10
+ }
11
+ const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, "utf8"));
12
+ const { graph } = buildDependencyGraph(workspaceDir, angularJson, restrictTo, { withDeps });
13
+ const order = topologicalSort(graph);
14
+ return { order, angularJson };
15
+ }
16
+
17
+ // A project declares itself a library (not a federated remote) by setting
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.
21
+ function isLibrary(workspaceDir, name) {
22
+ const pkgPath = path.join(workspaceDir, "projects", name, "package.json");
23
+ if (!fs.existsSync(pkgPath)) return false;
24
+ try {
25
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
26
+ return pkg.wsmodules && pkg.wsmodules.kind === "library";
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ function partitionByKind(workspaceDir, names) {
33
+ const remotes = [];
34
+ const libraries = [];
35
+ for (const n of names) {
36
+ if (isLibrary(workspaceDir, n)) libraries.push(n);
37
+ else remotes.push(n);
38
+ }
39
+ return { remotes, libraries };
40
+ }
41
+
42
+ function buildLibs({ workspaceDir, restrictTo = [], withDeps = false }) {
43
+ const { order } = loadOrder({ workspaceDir, restrictTo, withDeps });
44
+
45
+ console.log(`\n=== Build order ===`);
46
+ order.forEach((n, i) => console.log(` ${i + 1}. ${n}`));
47
+
48
+ for (const name of order) {
49
+ console.log(`\n=== ng build ${name} ===`);
50
+ execSync(`npx ng build ${name}`, {
51
+ cwd: workspaceDir,
52
+ stdio: "inherit",
53
+ });
54
+ }
55
+
56
+ console.log(`\nBuilt ${order.length} lib(s).`);
57
+ return { order };
58
+ }
59
+
60
+ function packRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
61
+ const { order } = loadOrder({ workspaceDir, restrictTo, withDeps });
62
+ const { remotes, libraries } = partitionByKind(workspaceDir, order);
63
+
64
+ console.log(`\n=== Pack order ===`);
65
+ remotes.forEach((n, i) => console.log(` ${i + 1}. ${n}`));
66
+ if (libraries.length) {
67
+ console.log(
68
+ ` (skipping libraries: ${libraries.join(", ")} — wsmodules.kind=library)`
69
+ );
70
+ }
71
+
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
+ return { order: remotes, libraries };
82
+ }
83
+
84
+ module.exports = { buildLibs, packRemotes, loadOrder, isLibrary, partitionByKind };
@@ -0,0 +1,55 @@
1
+ // Shared CLI arg parsing + selection-mode resolution for ws-build-libs,
2
+ // ws-pack-remotes, ws-deploy-remotes.
3
+ //
4
+ // Selection modes:
5
+ // inert — no positional args; print usage, do nothing
6
+ // all — single positional `all`; operate on every project
7
+ // named — one or more project names; operate on exactly those
8
+ // `--with-deps` expands the named set to include each name's transitive
9
+ // in-workspace deps. `--skip-install` is build-libs-specific.
10
+
11
+ function parseArgv(argv) {
12
+ const positional = [];
13
+ const flags = {};
14
+ for (let i = 2; i < argv.length; i++) {
15
+ const a = argv[i];
16
+ if (a.startsWith("--")) flags[a.slice(2)] = true;
17
+ else positional.push(a);
18
+ }
19
+ return { positional, flags };
20
+ }
21
+
22
+ function resolveSelection(positional) {
23
+ if (!positional.length) return { mode: "inert" };
24
+ if (positional.includes("all")) {
25
+ if (positional.length > 1) {
26
+ throw new Error("`all` cannot be combined with project names");
27
+ }
28
+ return { mode: "all", restrictTo: [] };
29
+ }
30
+ return { mode: "named", restrictTo: positional };
31
+ }
32
+
33
+ // Pack/deploy that run as `postbuild` lifecycle hooks of `npm run build` can
34
+ // re-derive the original args from npm's `npm_config_argv` env var. Returns
35
+ // the trailing positional+flag tokens from the parent command, or null if
36
+ // not running as a postbuild hook (or env var missing/malformed).
37
+ function inheritArgsFromPostbuild() {
38
+ if (process.env.npm_lifecycle_event !== "postbuild") return null;
39
+ const raw = process.env.npm_config_argv;
40
+ if (!raw) return null;
41
+ try {
42
+ const parsed = JSON.parse(raw);
43
+ const orig = parsed.original || [];
44
+ // orig looks like ["run", "build", "crm", "db-login", "--with-deps"]
45
+ // Drop the leading `run|run-script <script>` pair.
46
+ if (orig[0] === "run" || orig[0] === "run-script") {
47
+ return orig.slice(2);
48
+ }
49
+ return null;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ module.exports = { parseArgv, resolveSelection, inheritArgsFromPostbuild };