rman 1.3.0 → 2.0.0-beta.3
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/README.md +65 -31
- package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
- package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
- package/builtins/platforms/node/commands/ci.command.js +60 -0
- package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
- package/builtins/platforms/node/commands/clean.command.js +74 -0
- package/builtins/platforms/node/index.d.ts +8 -0
- package/builtins/platforms/node/index.js +8 -0
- package/builtins/platforms/node/node-config.interface.d.ts +76 -0
- package/builtins/platforms/node/node-config.interface.js +7 -0
- package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
- package/builtins/platforms/node/node-manifest.provider.js +125 -0
- package/builtins/platforms/node/node.platform.d.ts +53 -0
- package/builtins/platforms/node/node.platform.js +134 -0
- package/builtins/platforms/node/services/ci.service.d.ts +47 -0
- package/builtins/platforms/node/services/ci.service.js +213 -0
- package/builtins/platforms/node/services/clean.service.d.ts +53 -0
- package/builtins/platforms/node/services/clean.service.js +237 -0
- package/builtins/platforms/node/services/publish.service.d.ts +114 -0
- package/builtins/platforms/node/services/publish.service.js +371 -0
- package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
- package/builtins/platforms/node/services/version-plan.service.js +58 -0
- package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
- package/builtins/platforms/node/utils/workspace-range.js +28 -0
- package/builtins/presets/index.d.ts +15 -0
- package/builtins/presets/index.js +57 -0
- package/builtins/presets/node.d.ts +16 -0
- package/builtins/presets/node.js +57 -0
- package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +52 -31
- package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
- package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
- package/builtins/publish-targets/docker/docker.target.js +40 -0
- package/builtins/publish-targets/docker/index.d.ts +2 -0
- package/builtins/publish-targets/docker/index.js +2 -0
- package/builtins/publish-targets/npm/index.d.ts +2 -0
- package/builtins/publish-targets/npm/index.js +2 -0
- package/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
- package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
- package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
- package/builtins/publish-targets/npm/npm-view.js +71 -0
- package/cli.d.ts +8 -0
- package/cli.js +228 -93
- package/commands/build.command.d.ts +177 -3
- package/commands/build.command.js +20 -10
- package/commands/changelog.command.d.ts +296 -3
- package/commands/changelog.command.js +127 -43
- package/commands/config.command.d.ts +44 -3
- package/commands/config.command.js +53 -24
- package/commands/diff.command.d.ts +37 -3
- package/commands/diff.command.js +25 -16
- package/commands/exec.command.d.ts +193 -3
- package/commands/exec.command.js +60 -58
- package/commands/github-release.command.d.ts +154 -3
- package/commands/github-release.command.js +67 -37
- package/commands/import.command.d.ts +36 -3
- package/commands/import.command.js +28 -20
- package/commands/info.command.d.ts +35 -7
- package/commands/info.command.js +36 -30
- package/commands/lint.command.d.ts +176 -0
- package/commands/lint.command.js +34 -0
- package/commands/list.command.d.ts +163 -3
- package/commands/list.command.js +109 -71
- package/commands/publish.command.d.ts +231 -0
- package/commands/publish.command.js +304 -0
- package/commands/run.command.d.ts +186 -6
- package/commands/run.command.js +26 -72
- package/commands/test.command.d.ts +173 -3
- package/commands/test.command.js +16 -10
- package/commands/version.command.d.ts +330 -3
- package/commands/version.command.js +292 -78
- package/commands.d.ts +32 -0
- package/commands.js +28 -0
- package/constants.js +1 -1
- package/core/application.d.ts +116 -0
- package/core/application.js +160 -0
- package/core/classes/package.d.ts +219 -0
- package/core/classes/package.js +214 -0
- package/core/classes/registry.d.ts +30 -0
- package/core/classes/registry.js +47 -0
- package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
- package/core/{repository.js → classes/repository.js} +195 -75
- package/core/classes/service.d.ts +49 -0
- package/core/classes/service.js +40 -0
- package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
- package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
- package/core/classes/workspace.d.ts +275 -0
- package/core/classes/workspace.js +537 -0
- package/core/command-builder.d.ts +14 -0
- package/core/command-builder.js +94 -0
- package/core/config/config-file-scope.d.ts +107 -0
- package/core/config/config-file-scope.js +226 -0
- package/core/config/config-interpolator.d.ts +164 -0
- package/core/config/config-interpolator.js +577 -0
- package/core/config/config-paths.d.ts +65 -0
- package/core/config/config-paths.js +78 -0
- package/core/config/config-reader.d.ts +192 -0
- package/core/config/config-reader.js +727 -0
- package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
- package/core/config/merge-config.js +260 -0
- package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
- package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
- package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
- package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
- package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
- package/core/interfaces/manifest.js +132 -0
- package/core/interfaces/plugin.d.ts +174 -0
- package/core/interfaces/plugin.js +124 -0
- package/core/interfaces/publish-target.d.ts +136 -0
- package/core/interfaces/publish-target.js +43 -0
- package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
- package/core/interfaces/run-step.js +1 -0
- package/core/plugin-loader.d.ts +25 -0
- package/core/plugin-loader.js +53 -0
- package/index.d.ts +122 -24
- package/index.js +66 -16
- package/interfaces/config-scope.interface.d.ts +223 -0
- package/interfaces/config-scope.interface.js +1 -0
- package/interfaces/rman-config.interface.d.ts +821 -203
- package/interfaces/rman-config.interface.js +78 -1
- package/package.json +3 -1
- package/services/change-hash.service.d.ts +42 -8
- package/services/change-hash.service.js +92 -13
- package/services/changelog.service.d.ts +91 -52
- package/services/changelog.service.js +460 -74
- package/services/config-service.d.ts +8 -0
- package/services/config-service.js +3 -0
- package/services/exec.service.d.ts +23 -12
- package/services/exec.service.js +14 -9
- package/services/github-release.service.d.ts +45 -34
- package/services/github-release.service.js +15 -12
- package/services/import.service.d.ts +25 -14
- package/services/import.service.js +9 -5
- package/services/list.service.d.ts +67 -13
- package/services/list.service.js +62 -15
- package/services/run.service.d.ts +35 -22
- package/services/run.service.js +316 -233
- package/services/system-info.d.ts +1 -1
- package/services/version-plan.service.d.ts +35 -8
- package/services/version-plan.service.js +96 -36
- package/services/version.service.d.ts +34 -14
- package/services/version.service.js +35 -16
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/utils/bin-path.d.ts +6 -7
- package/utils/bin-path.js +7 -18
- package/utils/branch-guard.d.ts +30 -1
- package/utils/branch-guard.js +31 -0
- package/utils/color-yaml.d.ts +8 -0
- package/utils/color-yaml.js +86 -0
- package/utils/exec.d.ts +10 -0
- package/utils/exec.js +1 -1
- package/utils/git.d.ts +26 -0
- package/utils/git.js +65 -0
- package/utils/logger.d.ts +2 -2
- package/utils/logger.js +1 -1
- package/utils/package-filter.d.ts +135 -8
- package/utils/package-filter.js +204 -16
- package/utils/printable-config.d.ts +9 -1
- package/utils/printable-config.js +46 -1
- package/utils/progress-panel.d.ts +9 -0
- package/utils/progress-panel.js +23 -1
- package/utils/release-version.d.ts +1 -1
- package/utils/run-bin.d.ts +10 -0
- package/utils/run-bin.js +1 -1
- package/utils/run-options.d.ts +97 -0
- package/utils/run-options.js +81 -0
- package/utils/version-group.d.ts +31 -0
- package/utils/version-group.js +43 -0
- package/utils/version-stamp.d.ts +1 -1
- package/utils/version-stamp.js +1 -1
- package/commands/changed.command.d.ts +0 -3
- package/commands/changed.command.js +0 -29
- package/core/config.d.ts +0 -409
- package/core/config.js +0 -939
- package/core/extends-config.d.ts +0 -27
- package/core/extends-config.js +0 -89
- package/core/manifest.js +0 -150
- package/core/merge-config.js +0 -219
- package/core/package.d.ts +0 -83
- package/core/package.js +0 -106
- package/core/plugin.d.ts +0 -112
- package/core/plugin.js +0 -189
- package/core/workspace.d.ts +0 -68
- package/core/workspace.js +0 -83
- /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
export declare const APPEND_PREFIX = "+";
|
|
1
|
+
/** Where a repository keeps command modules of its own, as a glob or a list of them. */
|
|
2
|
+
export declare const COMMANDS_KEY = "commands";
|
|
4
3
|
/**
|
|
5
|
-
* Keys that **append
|
|
4
|
+
* Keys that **append rather than replace**, which is the only append rman has.
|
|
5
|
+
*
|
|
6
|
+
* **There used to be a `+key` prefix on every key, and it is gone.** It said "add to what this
|
|
7
|
+
* resolved to below", which is exactly what `value` says - and `value` says it better: it composes
|
|
8
|
+
* (three layers each deriving from the one under them), it can reorder or filter rather than only
|
|
9
|
+
* append, and it does not need the merge to keep an append *outstanding* until the layer it belongs
|
|
10
|
+
* to turns up. The prefix also carried a bug `value` does not: appending onto a value that was a
|
|
11
|
+
* sole `${{ }}` expression returning an array nested it, because the merge promoted the expression
|
|
12
|
+
* *string* to a list and interpolation only later turned that element into the array (measured,
|
|
13
|
+
* `[['a','b'],'c']` where a literal list gave `['a','b','c']`).
|
|
14
|
+
*
|
|
15
|
+
* What is left is this list, where appending is not a choice the author makes per layer but what
|
|
16
|
+
* the key *means*.
|
|
6
17
|
*
|
|
7
18
|
* `plugins` is the whole list because it is the one key where replacing is never what anyone meant:
|
|
8
19
|
* every other setting has a value a closer layer can sensibly overrule, while a plugin *adds
|
|
@@ -10,6 +21,12 @@ export declare const APPEND_PREFIX = "+";
|
|
|
10
21
|
* brought. Replacing was the silent failure - `extends`-ing a toolchain config and then adding a
|
|
11
22
|
* plugin of your own dropped the toolchain's, and what you noticed was `Unknown argument: publish`.
|
|
12
23
|
*
|
|
24
|
+
* `commands` and `publishTargets` are the same kind of statement - what this repository has, in
|
|
25
|
+
* instances or in globs naming them - so they append for the same reason: a repository adding one
|
|
26
|
+
* of its own never means "and stop loading the ones my shared config brought". All three are what
|
|
27
|
+
* a config *contributes*, and a contribution list is exactly the case where replacing is never
|
|
28
|
+
* what anyone meant.
|
|
29
|
+
*
|
|
13
30
|
* Do not extend this list casually: a key that always appends can never be *un*-said by a closer
|
|
14
31
|
* layer, which is only acceptable where the value is a set of contributions rather than a decision.
|
|
15
32
|
*/
|
|
@@ -24,7 +41,7 @@ export declare const ALWAYS_APPEND: readonly string[];
|
|
|
24
41
|
* carry one.
|
|
25
42
|
*
|
|
26
43
|
* A symbol is invisible to `Object.entries`, `JSON.stringify` and js-yaml, so the chain travels
|
|
27
|
-
* through `mergeConfig
|
|
44
|
+
* through `mergeConfig` and `rman config` without either of them having to know it
|
|
28
45
|
* is there.
|
|
29
46
|
*
|
|
30
47
|
* Each entry is a link, not a single slot: three layers each deriving from the one below need
|
|
@@ -51,47 +68,29 @@ export interface PreviousValue {
|
|
|
51
68
|
}
|
|
52
69
|
/** Only these two can ask for `value`, so only these two are worth remembering a previous for. */
|
|
53
70
|
export declare function carriesPreviousValue(value: unknown): boolean;
|
|
54
|
-
/** `"+before"` -> `"before"`, or `undefined` for a key that isn't an append. */
|
|
55
|
-
export declare function appendTarget(key: string): string | undefined;
|
|
56
71
|
/**
|
|
57
72
|
* Merges `source` onto `target` in place, the way every layer of rman config is combined - a
|
|
58
73
|
* directory's own file forms, the directory chain, `"[selector]"` blocks, and an `extends` base.
|
|
59
74
|
* One implementation for all of them, so they cannot disagree about what an append means.
|
|
60
75
|
*
|
|
61
|
-
* Plain keys replace
|
|
62
|
-
* adds
|
|
76
|
+
* Plain keys replace and nested objects merge recursively, as a deep merge always has. The two
|
|
77
|
+
* things it adds are the contribution keys, which append instead (`ALWAYS_APPEND`), and the record
|
|
78
|
+
* of what each key held underneath, so a closer layer can derive from it as `value`.
|
|
63
79
|
*
|
|
64
80
|
* ```yaml
|
|
65
|
-
* # the root says
|
|
66
|
-
* # a package
|
|
67
|
-
* # it resolves to
|
|
81
|
+
* # the root says before: "rm ./build"
|
|
82
|
+
* # a package says before: "${{ [...value, 'rm ./cache'] }}"
|
|
83
|
+
* # it resolves to before: ["rm ./build", "rm ./cache"]
|
|
68
84
|
* ```
|
|
69
85
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* A scalar is promoted to a list on the way, so neither side has to be written as an array. With
|
|
75
|
-
* nothing inherited, `+key` simply produces a one-element list.
|
|
86
|
+
* That is the half a shared config cannot live without: a base declaring `before: ["rm ./build"]`
|
|
87
|
+
* otherwise forces every repository wanting one more step to restate the whole list - which is a
|
|
88
|
+
* copy of the base, silently frozen at the version it was copied from.
|
|
76
89
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* scalar has nothing to append to, so `+key` behaves exactly as `key` would. Nothing is silently
|
|
80
|
-
* dropped - the value still lands.
|
|
81
|
-
*
|
|
82
|
-
* `key` and `+key` in the same object are both honored, in that order: the replacement happens
|
|
83
|
-
* first, then the append lands on top of it.
|
|
90
|
+
* **A `+key` prefix used to mean this and is refused now**, naming the key and what to write
|
|
91
|
+
* instead - see the check at the top of the loop.
|
|
84
92
|
*/
|
|
85
93
|
export declare function mergeConfig(target: Record<string, any>, source: Record<string, any>,
|
|
86
94
|
/** The file `source` was read from, recorded per key - see `ORIGINS`. A caller merging a value it
|
|
87
95
|
* built rather than read (a selector block already carrying its own origins) passes nothing. */
|
|
88
96
|
origin?: string): Record<string, any>;
|
|
89
|
-
/**
|
|
90
|
-
* Turns any `+key` still outstanding into its plain key, as a list - the "nothing was inherited"
|
|
91
|
-
* case, where an append simply is the whole value.
|
|
92
|
-
*
|
|
93
|
-
* Called once on a fully resolved config, and only there: until then an outstanding append may
|
|
94
|
-
* still find the layer it belongs to, and a config handed to a command must carry no `+key` at all
|
|
95
|
-
* or every reader would have to know about them.
|
|
96
|
-
*/
|
|
97
|
-
export declare function finalizeConfig<T>(config: T): T;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { isPlainObject } from '@jsopen/objects';
|
|
3
|
+
/** Where a repository keeps command modules of its own, as a glob or a list of them. */
|
|
4
|
+
export const COMMANDS_KEY = 'commands';
|
|
5
|
+
/** The keys whose string entries are globs, and so have to be anchored to the file that wrote
|
|
6
|
+
* them - see `anchorContributions`. The same four that always append. */
|
|
7
|
+
const GLOB_KEYS = ['plugins', 'platforms', COMMANDS_KEY, 'publishTargets'];
|
|
8
|
+
/**
|
|
9
|
+
* Keys that **append rather than replace**, which is the only append rman has.
|
|
10
|
+
*
|
|
11
|
+
* **There used to be a `+key` prefix on every key, and it is gone.** It said "add to what this
|
|
12
|
+
* resolved to below", which is exactly what `value` says - and `value` says it better: it composes
|
|
13
|
+
* (three layers each deriving from the one under them), it can reorder or filter rather than only
|
|
14
|
+
* append, and it does not need the merge to keep an append *outstanding* until the layer it belongs
|
|
15
|
+
* to turns up. The prefix also carried a bug `value` does not: appending onto a value that was a
|
|
16
|
+
* sole `${{ }}` expression returning an array nested it, because the merge promoted the expression
|
|
17
|
+
* *string* to a list and interpolation only later turned that element into the array (measured,
|
|
18
|
+
* `[['a','b'],'c']` where a literal list gave `['a','b','c']`).
|
|
19
|
+
*
|
|
20
|
+
* What is left is this list, where appending is not a choice the author makes per layer but what
|
|
21
|
+
* the key *means*.
|
|
22
|
+
*
|
|
23
|
+
* `plugins` is the whole list because it is the one key where replacing is never what anyone meant:
|
|
24
|
+
* every other setting has a value a closer layer can sensibly overrule, while a plugin *adds
|
|
25
|
+
* commands and seams*, and a repository naming one has no wish to lose the ones its shared config
|
|
26
|
+
* brought. Replacing was the silent failure - `extends`-ing a toolchain config and then adding a
|
|
27
|
+
* plugin of your own dropped the toolchain's, and what you noticed was `Unknown argument: publish`.
|
|
28
|
+
*
|
|
29
|
+
* `commands` and `publishTargets` are the same kind of statement - what this repository has, in
|
|
30
|
+
* instances or in globs naming them - so they append for the same reason: a repository adding one
|
|
31
|
+
* of its own never means "and stop loading the ones my shared config brought". All three are what
|
|
32
|
+
* a config *contributes*, and a contribution list is exactly the case where replacing is never
|
|
33
|
+
* what anyone meant.
|
|
34
|
+
*
|
|
35
|
+
* Do not extend this list casually: a key that always appends can never be *un*-said by a closer
|
|
36
|
+
* layer, which is only acceptable where the value is a set of contributions rather than a decision.
|
|
37
|
+
*/
|
|
38
|
+
export const ALWAYS_APPEND = ['plugins', 'platforms', COMMANDS_KEY, 'publishTargets'];
|
|
39
|
+
/**
|
|
40
|
+
* Where a key keeps what it is replacing, so the replacement can be handed it back as `value`.
|
|
41
|
+
*
|
|
42
|
+
* A **symbol-keyed chain on the containing object**, one entry per key, rather than something
|
|
43
|
+
* attached to the value itself. It started as a wrapper around a *function*, which is the only kind
|
|
44
|
+
* of value you can hang a property on - and that is exactly why it had to change: `value` belongs to
|
|
45
|
+
* an expression (`"${{ [...value, 'x'] }}"`) just as much as to a function, and a string cannot
|
|
46
|
+
* carry one.
|
|
47
|
+
*
|
|
48
|
+
* A symbol is invisible to `Object.entries`, `JSON.stringify` and js-yaml, so the chain travels
|
|
49
|
+
* through `mergeConfig` and `rman config` without either of them having to know it
|
|
50
|
+
* is there.
|
|
51
|
+
*
|
|
52
|
+
* Each entry is a link, not a single slot: three layers each deriving from the one below need
|
|
53
|
+
* `A <- expr2 <- expr3`, and one slot would have lost `A` the moment `expr3` arrived.
|
|
54
|
+
*/
|
|
55
|
+
export const PREVIOUS_VALUES = Symbol('rman.previousValues');
|
|
56
|
+
/**
|
|
57
|
+
* Which file each key came from, so an error can name it.
|
|
58
|
+
*
|
|
59
|
+
* A config is merged from several files before anything reads it - a directory's own four forms, an
|
|
60
|
+
* `extends` base, every `"[selector]"` block, and one layer per directory from the root down - so by
|
|
61
|
+
* the time an expression fails, `version.commitMessage` could have been written in any of them.
|
|
62
|
+
* Saying only the key sends the reader looking through all of them.
|
|
63
|
+
*
|
|
64
|
+
* Recorded the same way `PREVIOUS_VALUES` is, for the same reason: a symbol on the containing
|
|
65
|
+
* object, invisible to `Object.entries`, `JSON.stringify` and js-yaml, so it travels with the config
|
|
66
|
+
* without any reader having to know it is there.
|
|
67
|
+
*/
|
|
68
|
+
export const ORIGINS = Symbol('rman.origins');
|
|
69
|
+
/** Only these two can ask for `value`, so only these two are worth remembering a previous for. */
|
|
70
|
+
export function carriesPreviousValue(value) {
|
|
71
|
+
return typeof value === 'function' || (typeof value === 'string' && value.includes('${{'));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Merges `source` onto `target` in place, the way every layer of rman config is combined - a
|
|
75
|
+
* directory's own file forms, the directory chain, `"[selector]"` blocks, and an `extends` base.
|
|
76
|
+
* One implementation for all of them, so they cannot disagree about what an append means.
|
|
77
|
+
*
|
|
78
|
+
* Plain keys replace and nested objects merge recursively, as a deep merge always has. The two
|
|
79
|
+
* things it adds are the contribution keys, which append instead (`ALWAYS_APPEND`), and the record
|
|
80
|
+
* of what each key held underneath, so a closer layer can derive from it as `value`.
|
|
81
|
+
*
|
|
82
|
+
* ```yaml
|
|
83
|
+
* # the root says before: "rm ./build"
|
|
84
|
+
* # a package says before: "${{ [...value, 'rm ./cache'] }}"
|
|
85
|
+
* # it resolves to before: ["rm ./build", "rm ./cache"]
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* That is the half a shared config cannot live without: a base declaring `before: ["rm ./build"]`
|
|
89
|
+
* otherwise forces every repository wanting one more step to restate the whole list - which is a
|
|
90
|
+
* copy of the base, silently frozen at the version it was copied from.
|
|
91
|
+
*
|
|
92
|
+
* **A `+key` prefix used to mean this and is refused now**, naming the key and what to write
|
|
93
|
+
* instead - see the check at the top of the loop.
|
|
94
|
+
*/
|
|
95
|
+
export function mergeConfig(target, source,
|
|
96
|
+
/** The file `source` was read from, recorded per key - see `ORIGINS`. A caller merging a value it
|
|
97
|
+
* built rather than read (a selector block already carrying its own origins) passes nothing. */
|
|
98
|
+
origin) {
|
|
99
|
+
for (const [key, value] of Object.entries(source)) {
|
|
100
|
+
/**
|
|
101
|
+
* **A retired `+key` is refused, not ignored.** rman validates no config keys at all - there is
|
|
102
|
+
* no schema behind `.rmanrc`/`.rmanrc.yml` any more - so an unknown key is silent, and a
|
|
103
|
+
* repository upgrading from 1.x with `+before:` in its config would simply lose that step with
|
|
104
|
+
* nothing said. Measured before this check: `+include: ['extra']` resolved to the inherited
|
|
105
|
+
* list unchanged, exactly as if the line were not there.
|
|
106
|
+
*
|
|
107
|
+
* Here rather than in a validator, because this is the one function every layer passes through,
|
|
108
|
+
* and it is the only place that still knows which file the key came from.
|
|
109
|
+
*/
|
|
110
|
+
if (key.length > 1 && key.startsWith('+')) {
|
|
111
|
+
const plain = key.slice(1);
|
|
112
|
+
throw new Error(`"${key}" is no longer a config key${origin ? ` (${origin})` : ''}. The \`+key\` prefix is ` +
|
|
113
|
+
`gone: write "${plain}" as a value that derives from what it inherited - ` +
|
|
114
|
+
`\`${plain}: ({ value }) => [...value, 'x']\`, or \`"\${{ [...value, 'x'] }}"\` in YAML. ` +
|
|
115
|
+
`\`value\` is the layers below this one, and spreads as empty when there are none.`);
|
|
116
|
+
}
|
|
117
|
+
/** `plugins`/`commands`/`publishTargets`: additive at every layer, so the closer one adds
|
|
118
|
+
* rather than takes over. A glob among them is anchored to its own file on the way in - see
|
|
119
|
+
* `anchorContributions`. */
|
|
120
|
+
if (ALWAYS_APPEND.includes(key)) {
|
|
121
|
+
appendList(target, key, GLOB_KEYS.includes(key) ? anchorContributions(value, origin) : value);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
assignMerged(target, key, value, source, origin);
|
|
125
|
+
}
|
|
126
|
+
return target;
|
|
127
|
+
}
|
|
128
|
+
function assignMerged(target, key, value, source, origin) {
|
|
129
|
+
/** A source that already carries origins wins over the caller's: an `extends` base keeps the file
|
|
130
|
+
* its own keys were written in, rather than being attributed to the file that named it. */
|
|
131
|
+
const from = source[ORIGINS]?.[key] ?? origin;
|
|
132
|
+
if (from !== undefined) {
|
|
133
|
+
const carrier = target;
|
|
134
|
+
/** **Non-enumerable**, like `PREVIOUS_VALUES`: `expect`'s `toEqual` compares symbol properties,
|
|
135
|
+
* so a plain assignment turned every config-shape assertion in the suite into a diff about
|
|
136
|
+
* bookkeeping (measured, five specs at once). Nothing should see this but the error messages. */
|
|
137
|
+
if (!carrier[ORIGINS])
|
|
138
|
+
Object.defineProperty(target, ORIGINS, { value: {}, writable: true });
|
|
139
|
+
carrier[ORIGINS][key] = from;
|
|
140
|
+
}
|
|
141
|
+
if (isPlainObject(value)) {
|
|
142
|
+
if (!isPlainObject(target[key]))
|
|
143
|
+
target[key] = {};
|
|
144
|
+
mergeConfig(target[key], value, from);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A value that can ask for `value` **replaces** like any other - and remembers what it replaced:
|
|
149
|
+
*
|
|
150
|
+
* ```js
|
|
151
|
+
* '[*]': { clean: { include: ({ vars }) => [vars.buildDir] } }
|
|
152
|
+
* '[*]': { clean: { include: "${{ [...value, pkg.basename + '.log'] }}" } }
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* Chained here rather than at resolution time because only the merge knows the order of the
|
|
156
|
+
* layers - by the time `interpolateConfig` sees the config they have collapsed into one object,
|
|
157
|
+
* and whatever a closer layer said has already taken the place of what it was derived from.
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* **A chain already on `source` is carried over**, the way its `ORIGINS` are just above - and for
|
|
161
|
+
* the same reason: `source` may itself be several layers already merged, and only the merge knows
|
|
162
|
+
* their order.
|
|
163
|
+
*
|
|
164
|
+
* Without this, a chain recorded *inside* a source was silently dropped whenever the key was
|
|
165
|
+
* absent from the target, because the link below was only ever recorded for a key being
|
|
166
|
+
* *replaced*. The shape that hit it is the ordinary one for a shared config: a base and its
|
|
167
|
+
* consumer both writing `"[*]"`. Those two blocks merge into one before `matchingSelectors` sees
|
|
168
|
+
* them - recording the chain on the merged block - and that block is then merged into a `result`
|
|
169
|
+
* which has no such key, so the chain went nowhere.
|
|
170
|
+
*
|
|
171
|
+
* Measured, on a base declaring `clean.include: ['build']` and a consumer's `"[*]"` deriving from
|
|
172
|
+
* it: `${{ [...value, 'dist'] }}` answered `['dist']`, losing `build` outright. It answers
|
|
173
|
+
* `['build', 'dist']` now. Two *different* selectors (`"[*]"` then `"[pkg-a]"`) always worked,
|
|
174
|
+
* and so did an unmarked key, which is why this went unnoticed - those merge into a target that
|
|
175
|
+
* already holds the key.
|
|
176
|
+
*/
|
|
177
|
+
const sourceChain = source[PREVIOUS_VALUES]?.[key];
|
|
178
|
+
if (carriesPreviousValue(value) && (key in target || sourceChain)) {
|
|
179
|
+
const carrier = target;
|
|
180
|
+
const chain = (carrier[PREVIOUS_VALUES] ??= {});
|
|
181
|
+
/** The layer the target itself stands for, bottom-most of the three: what it holds now, over
|
|
182
|
+
* whatever *that* was derived from. Absent when the target never had the key. */
|
|
183
|
+
const below = key in target ? { value: target[key], previous: chain[key] } : chain[key];
|
|
184
|
+
/** Bottom-up: the target's layer, then `source`'s own intermediate ones, then `value` on top -
|
|
185
|
+
* so `source`'s chain keeps its internal order and gets the target's spliced beneath it. */
|
|
186
|
+
chain[key] = sourceChain ? graftChain(sourceChain, below) : below;
|
|
187
|
+
}
|
|
188
|
+
target[key] = Array.isArray(value) ? [...value] : value;
|
|
189
|
+
}
|
|
190
|
+
/** `node`'s chain, copied with `tail` spliced in beneath its deepest link - so two layer-stacks
|
|
191
|
+
* join into one without either being mutated (a chain is shared by every package that resolved
|
|
192
|
+
* through it, so grafting in place would rewrite what the others see). */
|
|
193
|
+
function graftChain(node, tail) {
|
|
194
|
+
return { value: node.value, previous: node.previous ? graftChain(node.previous, tail) : tail };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Appends `value` to `target[key]`, de-duplicating **only** an `ALWAYS_APPEND` key.
|
|
198
|
+
*
|
|
199
|
+
* That asymmetry is the point. `plugins` appends without being asked, so a repository and the config
|
|
200
|
+
* it extends both naming `'node'` is the ordinary case rather than a mistake, and the list is
|
|
201
|
+
* also what `rman config` prints. An explicit `+before`, by contrast, was *written* - repeating a
|
|
202
|
+
* step that is already there is a strange thing to ask for, but it is what was asked for, and
|
|
203
|
+
* silently collapsing it would make one layer's list depend on another's contents.
|
|
204
|
+
*
|
|
205
|
+
* By identity (`===`), which covers a string by value and a plugin object by reference; two
|
|
206
|
+
* *different* objects claiming one plugin name are caught where it counts, at registration - see
|
|
207
|
+
* `loadPlugins`.
|
|
208
|
+
*/
|
|
209
|
+
function appendList(target, key, value) {
|
|
210
|
+
const merged = [...toList(target[key] ?? []), ...toList(value)];
|
|
211
|
+
target[key] = ALWAYS_APPEND.includes(key) ? merged.filter((entry, at) => merged.indexOf(entry) === at) : merged;
|
|
212
|
+
}
|
|
213
|
+
function toList(value) {
|
|
214
|
+
return Array.isArray(value) ? value : [value];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Anchors every glob in `plugins`/`commands`/`publishTargets` to the directory of the file that
|
|
218
|
+
* declared it, on the way in. A non-string entry - an instance written straight into the config -
|
|
219
|
+
* passes through untouched.
|
|
220
|
+
*
|
|
221
|
+
* **Done here, at the merge, because this is the last moment the answer is known.** `commands`
|
|
222
|
+
* always appends, so one resolved list ends up holding entries from the repository's own
|
|
223
|
+
* `.rmanrc`, from each `extends` base, and from every directory above - and `ORIGINS` records one
|
|
224
|
+
* file per *key*, not per element, so after the merge there is nothing left to attribute them by.
|
|
225
|
+
* Rewriting each glob as it arrives makes the merge trivially correct and costs one `path.resolve`.
|
|
226
|
+
*
|
|
227
|
+
* It is what lets a **shared config ship its own contributions**: `commands: './commands/*.js'`
|
|
228
|
+
* in a published package means that package's directory, wherever the repository inheriting it
|
|
229
|
+
* happens to sit. All three keys behave the same way; `plugins` used to resolve every entry
|
|
230
|
+
* against the repository root instead, whatever file declared it, and that asymmetry is gone.
|
|
231
|
+
*
|
|
232
|
+
* `origin` is absent when a caller merges a value it built rather than read (a selector block, the
|
|
233
|
+
* directory chain layering already-resolved configs); those globs have been anchored already, and
|
|
234
|
+
* an absolute path is left alone by `path.resolve` anyway.
|
|
235
|
+
*/
|
|
236
|
+
function anchorContributions(value, origin) {
|
|
237
|
+
if (!origin)
|
|
238
|
+
return value;
|
|
239
|
+
const dir = path.dirname(origin);
|
|
240
|
+
const anchor = (entry) => typeof entry === 'string' && !looksLikePackageName(entry) ? path.resolve(dir, entry) : entry;
|
|
241
|
+
return Array.isArray(value) ? value.map(anchor) : anchor(value);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Whether an entry is shaped like a **package name** rather than a glob - `rman-node`,
|
|
245
|
+
* `@panates/rman-node`, but not `*.js`, `./x.js` or `commands/*.mjs`.
|
|
246
|
+
*
|
|
247
|
+
* Such an entry is left unanchored, because it is not a relative path and turning it into one
|
|
248
|
+
* destroys the only evidence of what the author meant. A package name is never valid in these keys
|
|
249
|
+
* (a package's config arrives through `extends`), so the whole value of keeping it intact is the
|
|
250
|
+
* error message: anchored, `plugins: ['rman-node']` failed with `glob ".../rman-node" matched no
|
|
251
|
+
* file`, which sends the reader to check their paths. `loadPlugins` can now say what they actually
|
|
252
|
+
* wrote and what to write instead - which two doc pages already promised it did.
|
|
253
|
+
*
|
|
254
|
+
* Shape, not a resolver call: decidable without touching the disk, and a wrong guess only chooses
|
|
255
|
+
* which of two error messages a failing entry gets. An extension is excluded so a bare `plugin.js`
|
|
256
|
+
* beside the config is still anchored as the relative path it is.
|
|
257
|
+
*/
|
|
258
|
+
function looksLikePackageName(entry) {
|
|
259
|
+
return /^(?:@[a-z0-9-~][\w.-]*\/)?[a-z0-9-~][\w.-]*$/i.test(entry) && !/\.[cm]?js$/i.test(entry);
|
|
260
|
+
}
|
|
@@ -15,7 +15,7 @@ export declare function resolveConfigTarget(target: string, from: string, label:
|
|
|
15
15
|
* repository cannot resolve it.
|
|
16
16
|
*
|
|
17
17
|
* A globally installed rman's siblings *are* the globally installed packages, so this is what makes
|
|
18
|
-
* `rman ci` work on a fresh clone - the command
|
|
18
|
+
* `rman ci` work on a fresh clone - the command came from `rman-node` then, and `ci` exists to create
|
|
19
19
|
* the very `node_modules` the plugin would otherwise have to be found in. Measured: with both
|
|
20
20
|
* installed globally, a clone answered `"plugins" target "rman-node" could not be resolved ... is
|
|
21
21
|
* it installed in this repository?`, which was true and useless.
|
|
@@ -39,7 +39,7 @@ export function resolveConfigTarget(target, from, label) {
|
|
|
39
39
|
* repository cannot resolve it.
|
|
40
40
|
*
|
|
41
41
|
* A globally installed rman's siblings *are* the globally installed packages, so this is what makes
|
|
42
|
-
* `rman ci` work on a fresh clone - the command
|
|
42
|
+
* `rman ci` work on a fresh clone - the command came from `rman-node` then, and `ci` exists to create
|
|
43
43
|
* the very `node_modules` the plugin would otherwise have to be found in. Measured: with both
|
|
44
44
|
* installed globally, a clone answered `"plugins" target "rman-node" could not be resolved ... is
|
|
45
45
|
* it installed in this repository?`, which was true and useless.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ArgumentsCamelCase, Argv } from 'yargs';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
2
|
+
import type { RmanConfig as CommandDeclaration } from '../../interfaces/rman-config.interface.js';
|
|
3
|
+
import type { Logger } from '../../utils/logger.js';
|
|
4
|
+
import type { RunBinOptions, RunBinResult } from '../../utils/run-bin.js';
|
|
5
|
+
import type { Package } from '../classes/package.js';
|
|
6
|
+
import type { Repository } from '../classes/repository.js';
|
|
6
7
|
/** Where a repository keeps its own commands - one module per command, named after it. */
|
|
7
8
|
export declare const CUSTOM_COMMAND_DIR = ".rman";
|
|
8
9
|
/**
|
|
@@ -100,10 +101,25 @@ declare module 'yargs' {
|
|
|
100
101
|
* that come with it; reimplementing that loop here would only lose them.
|
|
101
102
|
*/
|
|
102
103
|
export declare function defineCommand(command: CustomCommand): CustomCommand;
|
|
103
|
-
|
|
104
|
+
/**
|
|
105
|
+
* One command module that loaded, in whichever form it exported.
|
|
106
|
+
*
|
|
107
|
+
* Both forms are accepted, and the same pair is accepted for a command written straight into
|
|
108
|
+
* `.rmanrc "commands"` - one key, one set of rules. The declarative factory is what rman asks a
|
|
109
|
+
* command author to write; a repository's own command should not be stuck on the older object
|
|
110
|
+
* shape just because it lives in a file rather than in a config.
|
|
111
|
+
*/
|
|
112
|
+
export interface LoadedCommand {
|
|
104
113
|
/** The command's name - its file's basename, or the first word of an explicit `command`. */
|
|
105
114
|
name: string;
|
|
106
115
|
file: string;
|
|
116
|
+
/** The declarative form (`app => ({ ... })`). `cli.ts` runs it where a plugin's and a built-in's
|
|
117
|
+
* own factories run, because it wants `app.repository` and loading happens before one exists. */
|
|
118
|
+
register?: CommandDeclaration.CommandRegisterFunction;
|
|
119
|
+
/** The `defineCommand({ ... })` object form, already checked and named. */
|
|
120
|
+
custom?: CustomCommand & {
|
|
121
|
+
command: string;
|
|
122
|
+
};
|
|
107
123
|
}
|
|
108
124
|
/** A module that couldn't be loaded or doesn't look like a command. Reported, never thrown: one
|
|
109
125
|
* unparseable file must not take `rman publish` down with it. */
|
|
@@ -112,15 +128,32 @@ export interface CommandLoadError {
|
|
|
112
128
|
reason: string;
|
|
113
129
|
}
|
|
114
130
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
131
|
+
* The globs a repository's own commands are loaded from when it names none: `.rman/*.{js,mjs,cjs}`
|
|
132
|
+
* under the repository root.
|
|
133
|
+
*
|
|
134
|
+
* **`.rman/` is this default, not a second mechanism.** It used to be a hardcoded directory scan
|
|
135
|
+
* beside which `commands` would have been a third source of repository-level commands - and a
|
|
136
|
+
* third precedence question. Making it the default value instead leaves one source, one slot, and
|
|
137
|
+
* a zero-config path that behaves exactly as it did.
|
|
138
|
+
*/
|
|
139
|
+
export declare function defaultCommandGlobs(rootDir: string): string[];
|
|
140
|
+
/**
|
|
141
|
+
* Loads every command module matching `patterns` - absolute globs, already anchored to whichever
|
|
142
|
+
* config file declared them (see `anchorContributions`).
|
|
143
|
+
*
|
|
144
|
+
* A repository matching nothing pays for one glob and no imports, which matters because this runs
|
|
145
|
+
* on *every* rman invocation, `info` included.
|
|
146
|
+
*
|
|
147
|
+
* **Deduplicated by resolved path**, because `commands` appends at every level and cascades: the
|
|
148
|
+
* root's glob reaches each package's resolved config too, so the same file is named more than once
|
|
149
|
+
* as a matter of course rather than as a mistake. Loading it twice would register the command
|
|
150
|
+
* twice, which yargs does not survive.
|
|
118
151
|
*
|
|
119
152
|
* Each failure is collected rather than thrown, so the rest of the CLI keeps working; the caller
|
|
120
153
|
* warns about them. What is *not* tolerated is a module that would shadow a built-in - see
|
|
121
154
|
* `assertNoBuiltinShadowing`.
|
|
122
155
|
*/
|
|
123
|
-
export declare function loadCustomCommands(
|
|
156
|
+
export declare function loadCustomCommands(patterns: string[]): Promise<{
|
|
124
157
|
commands: LoadedCommand[];
|
|
125
158
|
errors: CommandLoadError[];
|
|
126
159
|
}>;
|
|
@@ -130,4 +163,7 @@ export declare function loadCustomCommands(rootDir: string): Promise<{
|
|
|
130
163
|
* `rman publish` that is safe to guess at. Silently preferring either one would leave whoever typed
|
|
131
164
|
* it unable to tell which ran.
|
|
132
165
|
*/
|
|
133
|
-
export declare function assertNoBuiltinShadowing(commands:
|
|
166
|
+
export declare function assertNoBuiltinShadowing(commands: readonly {
|
|
167
|
+
name: string;
|
|
168
|
+
file: string;
|
|
169
|
+
}[], builtins: readonly string[]): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import fastGlob from 'fast-glob';
|
|
4
4
|
/** Where a repository keeps its own commands - one module per command, named after it. */
|
|
5
5
|
export const CUSTOM_COMMAND_DIR = '.rman';
|
|
6
6
|
/** Loadable module forms, matching what a `.rmanrc.cjs`/`.mjs`/`.js` config already accepts. A
|
|
@@ -41,30 +41,58 @@ export function defineCommand(command) {
|
|
|
41
41
|
return command;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* The globs a repository's own commands are loaded from when it names none: `.rman/*.{js,mjs,cjs}`
|
|
45
|
+
* under the repository root.
|
|
46
|
+
*
|
|
47
|
+
* **`.rman/` is this default, not a second mechanism.** It used to be a hardcoded directory scan
|
|
48
|
+
* beside which `commands` would have been a third source of repository-level commands - and a
|
|
49
|
+
* third precedence question. Making it the default value instead leaves one source, one slot, and
|
|
50
|
+
* a zero-config path that behaves exactly as it did.
|
|
51
|
+
*/
|
|
52
|
+
export function defaultCommandGlobs(rootDir) {
|
|
53
|
+
return [path.join(rootDir, CUSTOM_COMMAND_DIR, `*{${EXTENSIONS.join(',')}}`)];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Loads every command module matching `patterns` - absolute globs, already anchored to whichever
|
|
57
|
+
* config file declared them (see `anchorContributions`).
|
|
58
|
+
*
|
|
59
|
+
* A repository matching nothing pays for one glob and no imports, which matters because this runs
|
|
60
|
+
* on *every* rman invocation, `info` included.
|
|
61
|
+
*
|
|
62
|
+
* **Deduplicated by resolved path**, because `commands` appends at every level and cascades: the
|
|
63
|
+
* root's glob reaches each package's resolved config too, so the same file is named more than once
|
|
64
|
+
* as a matter of course rather than as a mistake. Loading it twice would register the command
|
|
65
|
+
* twice, which yargs does not survive.
|
|
47
66
|
*
|
|
48
67
|
* Each failure is collected rather than thrown, so the rest of the CLI keeps working; the caller
|
|
49
68
|
* warns about them. What is *not* tolerated is a module that would shadow a built-in - see
|
|
50
69
|
* `assertNoBuiltinShadowing`.
|
|
51
70
|
*/
|
|
52
|
-
export async function loadCustomCommands(
|
|
53
|
-
const dir = path.join(rootDir, CUSTOM_COMMAND_DIR);
|
|
71
|
+
export async function loadCustomCommands(patterns) {
|
|
54
72
|
const commands = [];
|
|
55
73
|
const errors = [];
|
|
56
|
-
if (!
|
|
74
|
+
if (!patterns.length)
|
|
57
75
|
return { commands, errors };
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
/** Sorted so the order a command is registered in does not depend on the filesystem, and
|
|
77
|
+
* `absolute` because a pattern may name a directory outside the repository entirely - which is
|
|
78
|
+
* exactly what a shared config shipping its own commands does. */
|
|
79
|
+
const files = await fastGlob(patterns.map(p => p.split(path.sep).join('/')), { absolute: true, onlyFiles: true });
|
|
80
|
+
for (const file of [...new Set(files.map(f => path.resolve(f)))].sort()) {
|
|
62
81
|
try {
|
|
63
82
|
const mod = await import(pathToFileURL(file).href);
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
const exported = mod?.default ?? mod?.command;
|
|
84
|
+
const basename = path.basename(file, path.extname(file));
|
|
85
|
+
/** The declarative form. Nothing to check here beyond its shape - the factory has not run,
|
|
86
|
+
* so there is no metadata to validate yet; `cli.ts` checks what it returns. */
|
|
87
|
+
if (typeof exported === 'function') {
|
|
88
|
+
commands.push({ name: basename, file, register: exported });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!exported || typeof exported !== 'object') {
|
|
92
|
+
throw new Error('no command exported - end the module with `export default defineCommand({ ... })`, or with ' +
|
|
93
|
+
'the declarative `export default app => ({ ... })`');
|
|
67
94
|
}
|
|
95
|
+
const command = exported;
|
|
68
96
|
if (typeof command.handler !== 'function')
|
|
69
97
|
throw new Error('"handler" is missing, or is not a function');
|
|
70
98
|
if (typeof command.describe !== 'string' || !command.describe) {
|
|
@@ -72,10 +100,9 @@ export async function loadCustomCommands(rootDir) {
|
|
|
72
100
|
}
|
|
73
101
|
const declared = command.command?.trim();
|
|
74
102
|
commands.push({
|
|
75
|
-
|
|
76
|
-
command: declared || path.basename(entry.name, path.extname(entry.name)),
|
|
77
|
-
name: (declared || path.basename(entry.name, path.extname(entry.name))).split(/\s+/)[0],
|
|
103
|
+
name: (declared || basename).split(/\s+/)[0],
|
|
78
104
|
file,
|
|
105
|
+
custom: { ...command, command: declared || basename },
|
|
79
106
|
});
|
|
80
107
|
}
|
|
81
108
|
catch (e) {
|