cliguard 0.3.0 → 0.3.1
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.
|
@@ -21,6 +21,23 @@ export declare class CacAdapter implements CliAdapter {
|
|
|
21
21
|
private loadCac;
|
|
22
22
|
/** Handles `export default`, `module.exports = cli`, and named exports. */
|
|
23
23
|
private findCac;
|
|
24
|
+
/**
|
|
25
|
+
* Structural check, not `instanceof CAC` - see CommanderAdapter's
|
|
26
|
+
* identical-purpose `looksLikeCommand` for why: the target project's
|
|
27
|
+
* own `cac` install is almost always a separate copy from any `cac`
|
|
28
|
+
* cliguard itself could resolve, even at the identical version, so
|
|
29
|
+
* `instanceof` fails by construction. This also removes the only
|
|
30
|
+
* reason this adapter ever needed `cac` installed in cliguard's own
|
|
31
|
+
* environment - `require("cac")` from cliguard's own (often
|
|
32
|
+
* `npx`-isolated) location previously gated every use of this adapter
|
|
33
|
+
* behind a package cliguard could rarely actually see, even when the
|
|
34
|
+
* target project had it. The target file's own `require("cac")` /
|
|
35
|
+
* `import("cac")`, resolved from *its* location by `loadModule`, is
|
|
36
|
+
* the only place `cac` needs to be installed now - and if it isn't,
|
|
37
|
+
* that failure surfaces below via the real load error, same as any
|
|
38
|
+
* other missing dependency.
|
|
39
|
+
*/
|
|
40
|
+
private looksLikeCac;
|
|
24
41
|
/**
|
|
25
42
|
* CAC's root instance carries global options (`cli.option(...)`,
|
|
26
43
|
* exposed via `globalCommand`) but no description of its own and no
|
|
@@ -2,23 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CacAdapter = void 0;
|
|
4
4
|
const load_module_1 = require("./load-module");
|
|
5
|
-
/**
|
|
6
|
-
* `cac` is an optional peer dependency (see package.json) - a project
|
|
7
|
-
* whose target CLI is built with Commander has no reason to install it.
|
|
8
|
-
* Loaded lazily, only when this adapter actually runs, so importing this
|
|
9
|
-
* file (which `bin.ts` does unconditionally, to register every adapter)
|
|
10
|
-
* never requires `cac` to be present.
|
|
11
|
-
*/
|
|
12
|
-
function loadCacClass() {
|
|
13
|
-
try {
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports -- deliberate lazy load of an optional peer dependency
|
|
15
|
-
const cacModule = require("cac");
|
|
16
|
-
return cacModule.CAC;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
throw new Error("cliguard: the CAC adapter needs the `cac` package. Run `npm install --save-dev cac`.");
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
5
|
/**
|
|
23
6
|
* Extracts a Contract from a target file that exports a `cac()` `CAC`
|
|
24
7
|
* instance. Never parses --help output - every field comes straight from
|
|
@@ -48,10 +31,8 @@ class CacAdapter {
|
|
|
48
31
|
};
|
|
49
32
|
}
|
|
50
33
|
async loadCac(entryPath) {
|
|
51
|
-
const CacClass = loadCacClass();
|
|
52
34
|
const { viaImport, viaRequire } = await (0, load_module_1.loadModule)(entryPath);
|
|
53
|
-
const cli = this.findCac(viaImport.moduleExports
|
|
54
|
-
this.findCac(viaRequire.moduleExports, CacClass);
|
|
35
|
+
const cli = this.findCac(viaImport.moduleExports) ?? this.findCac(viaRequire.moduleExports);
|
|
55
36
|
if (cli)
|
|
56
37
|
return cli;
|
|
57
38
|
// See CommanderAdapter's identical block for why both real errors -
|
|
@@ -63,22 +44,48 @@ class CacAdapter {
|
|
|
63
44
|
` require() failed: ${viaRequire.error ?? "module loaded, but exported no CAC instance"}`);
|
|
64
45
|
}
|
|
65
46
|
/** Handles `export default`, `module.exports = cli`, and named exports. */
|
|
66
|
-
findCac(moduleExports
|
|
67
|
-
if (moduleExports
|
|
47
|
+
findCac(moduleExports) {
|
|
48
|
+
if (this.looksLikeCac(moduleExports)) {
|
|
68
49
|
return moduleExports;
|
|
69
50
|
}
|
|
70
51
|
if (moduleExports && typeof moduleExports === "object") {
|
|
71
52
|
const exportsObject = moduleExports;
|
|
72
|
-
if (exportsObject.default
|
|
53
|
+
if (this.looksLikeCac(exportsObject.default)) {
|
|
73
54
|
return exportsObject.default;
|
|
74
55
|
}
|
|
75
56
|
for (const value of Object.values(exportsObject)) {
|
|
76
|
-
if (value
|
|
57
|
+
if (this.looksLikeCac(value))
|
|
77
58
|
return value;
|
|
78
59
|
}
|
|
79
60
|
}
|
|
80
61
|
return undefined;
|
|
81
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Structural check, not `instanceof CAC` - see CommanderAdapter's
|
|
65
|
+
* identical-purpose `looksLikeCommand` for why: the target project's
|
|
66
|
+
* own `cac` install is almost always a separate copy from any `cac`
|
|
67
|
+
* cliguard itself could resolve, even at the identical version, so
|
|
68
|
+
* `instanceof` fails by construction. This also removes the only
|
|
69
|
+
* reason this adapter ever needed `cac` installed in cliguard's own
|
|
70
|
+
* environment - `require("cac")` from cliguard's own (often
|
|
71
|
+
* `npx`-isolated) location previously gated every use of this adapter
|
|
72
|
+
* behind a package cliguard could rarely actually see, even when the
|
|
73
|
+
* target project had it. The target file's own `require("cac")` /
|
|
74
|
+
* `import("cac")`, resolved from *its* location by `loadModule`, is
|
|
75
|
+
* the only place `cac` needs to be installed now - and if it isn't,
|
|
76
|
+
* that failure surfaces below via the real load error, same as any
|
|
77
|
+
* other missing dependency.
|
|
78
|
+
*/
|
|
79
|
+
looksLikeCac(value) {
|
|
80
|
+
if (!value || typeof value !== "object")
|
|
81
|
+
return false;
|
|
82
|
+
const candidate = value;
|
|
83
|
+
return (Array.isArray(candidate.commands) &&
|
|
84
|
+
typeof candidate.globalCommand === "object" &&
|
|
85
|
+
candidate.globalCommand !== null &&
|
|
86
|
+
typeof candidate.command === "function" &&
|
|
87
|
+
typeof candidate.parse === "function");
|
|
88
|
+
}
|
|
82
89
|
/**
|
|
83
90
|
* CAC's root instance carries global options (`cli.option(...)`,
|
|
84
91
|
* exposed via `globalCommand`) but no description of its own and no
|
|
@@ -13,6 +13,21 @@ export declare class CommanderAdapter implements CliAdapter {
|
|
|
13
13
|
private loadCommand;
|
|
14
14
|
/** Handles `export default`, `module.exports = program`, and named exports. */
|
|
15
15
|
private findCommand;
|
|
16
|
+
/**
|
|
17
|
+
* Structural check, not `instanceof Command`. The target CLI almost
|
|
18
|
+
* always has its own separate install of `commander` - a different
|
|
19
|
+
* copy than the one this adapter imports, even at the identical
|
|
20
|
+
* version - because `npx cliguard` installs cliguard (and its pinned
|
|
21
|
+
* `commander`) into its own isolated location, unrelated to the target
|
|
22
|
+
* project's `node_modules`. Node gives every resolved copy of a
|
|
23
|
+
* package its own class identity ("dual package hazard"), so
|
|
24
|
+
* `instanceof` fails by construction in that - extremely common - case.
|
|
25
|
+
* Verified against a real external consumer project via `npx cliguard`
|
|
26
|
+
* with its own separate `commander` install, both at a different major
|
|
27
|
+
* version and at the identical version to this package's own
|
|
28
|
+
* `^12.1.0` - `instanceof` failed in both; this doesn't.
|
|
29
|
+
*/
|
|
30
|
+
private looksLikeCommand;
|
|
16
31
|
/** Recurses into `command.commands` so root and every subcommand at any depth go through the same mapping. */
|
|
17
32
|
private mapCommand;
|
|
18
33
|
private mapOption;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CommanderAdapter = void 0;
|
|
4
|
-
const commander_1 = require("commander");
|
|
5
4
|
const load_module_1 = require("./load-module");
|
|
6
5
|
/**
|
|
7
6
|
* Extracts a Contract from a target file that exports a Commander.js
|
|
@@ -45,21 +44,45 @@ class CommanderAdapter {
|
|
|
45
44
|
}
|
|
46
45
|
/** Handles `export default`, `module.exports = program`, and named exports. */
|
|
47
46
|
findCommand(moduleExports) {
|
|
48
|
-
if (moduleExports
|
|
47
|
+
if (this.looksLikeCommand(moduleExports)) {
|
|
49
48
|
return moduleExports;
|
|
50
49
|
}
|
|
51
50
|
if (moduleExports && typeof moduleExports === "object") {
|
|
52
51
|
const exportsObject = moduleExports;
|
|
53
|
-
if (exportsObject.default
|
|
52
|
+
if (this.looksLikeCommand(exportsObject.default)) {
|
|
54
53
|
return exportsObject.default;
|
|
55
54
|
}
|
|
56
55
|
for (const value of Object.values(exportsObject)) {
|
|
57
|
-
if (value
|
|
56
|
+
if (this.looksLikeCommand(value))
|
|
58
57
|
return value;
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
return undefined;
|
|
62
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Structural check, not `instanceof Command`. The target CLI almost
|
|
64
|
+
* always has its own separate install of `commander` - a different
|
|
65
|
+
* copy than the one this adapter imports, even at the identical
|
|
66
|
+
* version - because `npx cliguard` installs cliguard (and its pinned
|
|
67
|
+
* `commander`) into its own isolated location, unrelated to the target
|
|
68
|
+
* project's `node_modules`. Node gives every resolved copy of a
|
|
69
|
+
* package its own class identity ("dual package hazard"), so
|
|
70
|
+
* `instanceof` fails by construction in that - extremely common - case.
|
|
71
|
+
* Verified against a real external consumer project via `npx cliguard`
|
|
72
|
+
* with its own separate `commander` install, both at a different major
|
|
73
|
+
* version and at the identical version to this package's own
|
|
74
|
+
* `^12.1.0` - `instanceof` failed in both; this doesn't.
|
|
75
|
+
*/
|
|
76
|
+
looksLikeCommand(value) {
|
|
77
|
+
if (!value || typeof value !== "object")
|
|
78
|
+
return false;
|
|
79
|
+
const candidate = value;
|
|
80
|
+
return (Array.isArray(candidate.options) &&
|
|
81
|
+
Array.isArray(candidate.commands) &&
|
|
82
|
+
typeof candidate.name === "function" &&
|
|
83
|
+
typeof candidate.action === "function" &&
|
|
84
|
+
typeof candidate.opts === "function");
|
|
85
|
+
}
|
|
63
86
|
/** Recurses into `command.commands` so root and every subcommand at any depth go through the same mapping. */
|
|
64
87
|
mapCommand(command) {
|
|
65
88
|
return {
|