rman 1.3.0 → 2.0.0-beta.2

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 (153) hide show
  1. package/README.md +63 -19
  2. package/cli.d.ts +5 -0
  3. package/cli.js +214 -88
  4. package/commands/build.command.d.ts +177 -3
  5. package/commands/build.command.js +20 -10
  6. package/commands/changed.command.d.ts +80 -3
  7. package/commands/changed.command.js +19 -12
  8. package/commands/changelog.command.d.ts +192 -3
  9. package/commands/changelog.command.js +87 -43
  10. package/commands/config.command.d.ts +44 -3
  11. package/commands/config.command.js +30 -19
  12. package/commands/diff.command.d.ts +37 -3
  13. package/commands/diff.command.js +25 -16
  14. package/commands/exec.command.d.ts +193 -3
  15. package/commands/exec.command.js +60 -58
  16. package/commands/github-release.command.d.ts +154 -3
  17. package/commands/github-release.command.js +67 -37
  18. package/commands/import.command.d.ts +36 -3
  19. package/commands/import.command.js +28 -20
  20. package/commands/info.command.d.ts +35 -7
  21. package/commands/info.command.js +36 -30
  22. package/commands/list.command.d.ts +163 -3
  23. package/commands/list.command.js +109 -71
  24. package/commands/publish.command.d.ts +231 -0
  25. package/commands/publish.command.js +304 -0
  26. package/commands/run.command.d.ts +186 -6
  27. package/commands/run.command.js +26 -72
  28. package/commands/test.command.d.ts +173 -3
  29. package/commands/test.command.js +16 -10
  30. package/commands/version.command.d.ts +317 -3
  31. package/commands/version.command.js +149 -69
  32. package/commands.d.ts +32 -0
  33. package/commands.js +28 -0
  34. package/constants.js +1 -1
  35. package/core/application.d.ts +116 -0
  36. package/core/application.js +143 -0
  37. package/core/command-builder.d.ts +14 -0
  38. package/core/command-builder.js +78 -0
  39. package/core/config.d.ts +140 -19
  40. package/core/config.js +258 -74
  41. package/core/core-services.d.ts +14 -0
  42. package/core/core-services.js +30 -0
  43. package/core/core-targets.d.ts +14 -0
  44. package/core/core-targets.js +16 -0
  45. package/core/custom-command.d.ts +42 -6
  46. package/core/custom-command.js +44 -17
  47. package/core/extends-config.d.ts +13 -5
  48. package/core/extends-config.js +52 -12
  49. package/core/load-config-module.d.ts +28 -0
  50. package/core/load-config-module.js +42 -0
  51. package/core/manifest.d.ts +47 -25
  52. package/core/manifest.js +51 -69
  53. package/core/merge-config.d.ts +33 -34
  54. package/core/merge-config.js +137 -92
  55. package/core/package.d.ts +145 -17
  56. package/core/package.js +128 -36
  57. package/core/plugin-loader.d.ts +65 -0
  58. package/core/plugin-loader.js +234 -0
  59. package/core/plugin.d.ts +135 -90
  60. package/core/plugin.js +70 -173
  61. package/core/publish-target.d.ts +124 -0
  62. package/core/publish-target.js +30 -0
  63. package/core/registry.d.ts +30 -0
  64. package/core/registry.js +47 -0
  65. package/core/repository.d.ts +72 -9
  66. package/core/repository.js +293 -44
  67. package/core/resolve-target.d.ts +1 -1
  68. package/core/resolve-target.js +1 -1
  69. package/core/service.d.ts +49 -0
  70. package/core/service.js +40 -0
  71. package/core/version-scheme.d.ts +23 -1
  72. package/core/version-scheme.js +29 -1
  73. package/core/workspace.d.ts +84 -33
  74. package/core/workspace.js +63 -22
  75. package/index.d.ts +111 -14
  76. package/index.js +85 -9
  77. package/interfaces/rman-config.interface.d.ts +725 -202
  78. package/interfaces/rman-config.interface.js +61 -1
  79. package/package.json +2 -1
  80. package/plugins/builtins.d.ts +44 -0
  81. package/plugins/builtins.js +33 -0
  82. package/plugins/detect.d.ts +78 -0
  83. package/plugins/detect.js +70 -0
  84. package/plugins/node/augmentation/rman.augmentation.d.ts +84 -0
  85. package/plugins/node/augmentation/rman.augmentation.js +1 -0
  86. package/plugins/node/augmentation/system-info.augmentation.d.ts +26 -0
  87. package/plugins/node/augmentation/system-info.augmentation.js +79 -0
  88. package/plugins/node/commands/ci.command.d.ts +131 -0
  89. package/plugins/node/commands/ci.command.js +59 -0
  90. package/plugins/node/commands/clean.command.d.ts +183 -0
  91. package/plugins/node/commands/clean.command.js +73 -0
  92. package/plugins/node/index.d.ts +29 -0
  93. package/plugins/node/index.js +40 -0
  94. package/plugins/node/node-config.interface.d.ts +77 -0
  95. package/plugins/node/node-config.interface.js +7 -0
  96. package/plugins/node/node-manifest.provider.d.ts +68 -0
  97. package/plugins/node/node-manifest.provider.js +125 -0
  98. package/plugins/node/node.platform.d.ts +53 -0
  99. package/plugins/node/node.platform.js +134 -0
  100. package/plugins/node/npm-publish-target.d.ts +73 -0
  101. package/plugins/node/npm-publish-target.js +96 -0
  102. package/plugins/node/services/ci.service.d.ts +47 -0
  103. package/plugins/node/services/ci.service.js +213 -0
  104. package/plugins/node/services/clean.service.d.ts +53 -0
  105. package/plugins/node/services/clean.service.js +237 -0
  106. package/plugins/node/services/publish.service.d.ts +114 -0
  107. package/plugins/node/services/publish.service.js +371 -0
  108. package/plugins/node/services/version-plan.service.d.ts +44 -0
  109. package/plugins/node/services/version-plan.service.js +58 -0
  110. package/plugins/node/utils/npm-view.d.ts +48 -0
  111. package/plugins/node/utils/npm-view.js +71 -0
  112. package/plugins/node/utils/workspace-range.d.ts +26 -0
  113. package/plugins/node/utils/workspace-range.js +28 -0
  114. package/services/change-hash.service.d.ts +2 -2
  115. package/services/change-hash.service.js +2 -2
  116. package/services/changelog.service.d.ts +62 -51
  117. package/services/changelog.service.js +14 -11
  118. package/services/docker-publish.service.d.ts +50 -29
  119. package/services/docker-publish.service.js +43 -20
  120. package/services/exec.service.d.ts +23 -12
  121. package/services/exec.service.js +14 -9
  122. package/services/github-release.service.d.ts +44 -33
  123. package/services/github-release.service.js +13 -10
  124. package/services/import.service.d.ts +25 -14
  125. package/services/import.service.js +9 -5
  126. package/services/list.service.d.ts +62 -10
  127. package/services/list.service.js +62 -15
  128. package/services/run.service.d.ts +22 -13
  129. package/services/run.service.js +262 -223
  130. package/services/version-plan.service.d.ts +27 -4
  131. package/services/version-plan.service.js +42 -15
  132. package/services/version.service.d.ts +31 -11
  133. package/services/version.service.js +29 -13
  134. package/targets/docker.target.d.ts +53 -0
  135. package/targets/docker.target.js +40 -0
  136. package/utils/bin-path.d.ts +6 -7
  137. package/utils/bin-path.js +7 -18
  138. package/utils/branch-guard.d.ts +29 -0
  139. package/utils/branch-guard.js +31 -0
  140. package/utils/exec.d.ts +10 -0
  141. package/utils/exec.js +1 -1
  142. package/utils/logger.d.ts +1 -1
  143. package/utils/logger.js +1 -1
  144. package/utils/package-filter.d.ts +127 -7
  145. package/utils/package-filter.js +197 -16
  146. package/utils/printable-config.d.ts +1 -1
  147. package/utils/printable-config.js +1 -1
  148. package/utils/run-bin.d.ts +10 -0
  149. package/utils/run-bin.js +1 -1
  150. package/utils/run-options.d.ts +97 -0
  151. package/utils/run-options.js +81 -0
  152. package/utils/version-stamp.d.ts +1 -1
  153. package/utils/version-stamp.js +1 -1
@@ -6,10 +6,29 @@ import type { Package } from '../core/package.js';
6
6
  * `.rmanrc "skip"`, is applied by `filterPackages` itself rather than being an option here: it is
7
7
  * the repository's statement, not the caller's. */
8
8
  export interface PackageFilterOptions {
9
- /** Only include packages whose name matches at least one of these globs (e.g. `@scope/*`). */
9
+ /** Only include packages whose name matches at least one of these globs (e.g. `@scope/*`), or
10
+ * **`"/"`** for the repository's own root package - see `ROOT_SELECTOR`. */
10
11
  scope?: string | string[];
11
- /** Exclude packages whose name matches at least one of these globs - applied after `scope`. */
12
+ /** Exclude packages whose name matches at least one of these globs (or `"/"`) - applied after
13
+ * `scope`. */
12
14
  ignore?: string | string[];
15
+ /**
16
+ * Only include packages belonging to one of these **platforms** (`Package.provider`) - `['node']`,
17
+ * or `'node,cargo'` as one comma-separated value.
18
+ *
19
+ * **Which technology a package belongs to only became a per-package question when the walk
20
+ * started finding nested packages of another platform**, and this is the filter that question
21
+ * needs: in a polyglot repository `rman run build --platform node` is the whole npm half of it,
22
+ * and there was no way to say that. Where every package is one technology it selects everything
23
+ * or nothing, which is the honest answer rather than a flag that does nothing.
24
+ *
25
+ * **Names, not globs.** A platform's name is a short identifier a plugin chose (`'node'`), so the
26
+ * set of valid values is known and finite - which is also why a name that no package in the
27
+ * repository belongs to is an **error** listing the ones that are there, the same call
28
+ * `publish --target` makes. A glob would put this back to guessing, and a typo back to a silent
29
+ * empty result.
30
+ */
31
+ platform?: string | string[];
13
32
  /** Also include every package the matched set depends on (transitively) - e.g. to build
14
33
  * everything a scoped app actually needs. */
15
34
  deps?: boolean;
@@ -17,6 +36,99 @@ export interface PackageFilterOptions {
17
36
  * everything that could be affected by a scoped library's change. */
18
37
  dependents?: boolean;
19
38
  }
39
+ /**
40
+ * **`--scope /` is the repository's own root package**, the one selector that is not a glob.
41
+ *
42
+ * The same `/` `.rmanrc`'s `"[/]"` block uses, and for the same reason stated there: *the root is
43
+ * never selected by name.* A glob matches package names, and a name can be anything - so
44
+ * `--scope rman-repo` happened to work (measured) while being exactly the name-based addressing the
45
+ * config selectors were redesigned to remove. `/` is structural, cannot collide with a package
46
+ * (nothing can be named it), and gives "the root" one spelling across config and CLI.
47
+ *
48
+ * Accepted by `--ignore` too. The asymmetry would be the thing to remember, and `--ignore /` -
49
+ * every package but the root - is a real thing to want of `clean`.
50
+ *
51
+ * **It selects nothing on a command whose candidates exclude the root**, which is most of them:
52
+ * `repository.packages` holds the workspace members only, so `rman list --scope /` and
53
+ * `rman run build --scope /` match nothing and say so. That is the honest answer rather than a
54
+ * special case - the root has no `list` row and contributes only `pre`/`post` bookends to `run`.
55
+ * `clean` and `changelog`, which put the root in their candidate list on purpose, are where it bites.
56
+ */
57
+ export declare const ROOT_SELECTOR = "/";
58
+ /**
59
+ * `--scope`/`--ignore`/`--deps`/`--dependents` as a **declaration** rather than a builder call -
60
+ * spread into a command's `config` block:
61
+ *
62
+ * ```ts
63
+ * config: { ...packageFilterOptions, ...branchGuardOptions, changelog: { ... } }
64
+ * ```
65
+ *
66
+ * **`satisfies`, never a `: Record<...>` annotation.** An annotation widens `type: 'string'` back to
67
+ * `string`, and every type derived from the declaration - the config contribution, the option's own
68
+ * value type - collapses with it. `satisfies` checks the shape and keeps the literals, and it also
69
+ * catches a misspelled key *here*, at the group's own line, rather than in the ten commands that
70
+ * spread it. The `Argv` chaining this replaces could not: a typo there was simply a new option.
71
+ *
72
+ * **Every option is `target: 'cli'`, including the ones that have a config twin.** A shared group
73
+ * belongs to no command, so declaring one `'both'` would contribute `version.scope` - a key nothing
74
+ * reads. Where a `.rmanrc` equivalent exists it is a core key in its own right (`allowBranch`), and
75
+ * a command that reads it names it in `configKeys`.
76
+ */
77
+ export declare const packageFilterOptions: {
78
+ scope: {
79
+ target: "cli";
80
+ describe: string;
81
+ type: "string";
82
+ };
83
+ ignore: {
84
+ target: "cli";
85
+ describe: string;
86
+ type: "string";
87
+ };
88
+ platform: {
89
+ target: "cli";
90
+ describe: string;
91
+ type: "string";
92
+ };
93
+ deps: {
94
+ target: "cli";
95
+ describe: string;
96
+ type: "boolean";
97
+ };
98
+ dependents: {
99
+ target: "cli";
100
+ describe: string;
101
+ type: "boolean";
102
+ };
103
+ };
104
+ /**
105
+ * `--from-root`/`-r` - a group of one, and a function because its text is the command's own word
106
+ * for what it does. Spread it like the others: `...fromRootOption('Build')`.
107
+ *
108
+ * Only where a command scopes by the current directory; see `applyFromRootOption` for why adding it
109
+ * elsewhere is worse than leaving it out.
110
+ *
111
+ * **It was `--root` through 1.x, and the name said the opposite of what it does.** Every reader
112
+ * spells it the same single line - `options.fromRoot ? undefined : repository.currentPackage` - so
113
+ * the flag means *ignore where I am standing*, i.e. the **whole repository**. `--root` reads as "the
114
+ * root alone", which is the narrowest possible set rather than the widest, and the confusion was
115
+ * about to become a contradiction: a `--root-only` beside a `--root` that meant "everything" is
116
+ * unreadable.
117
+ *
118
+ * **`-r` is kept, and a two-letter `-fr` is not possible.** yargs' `short-option-groups` is on by
119
+ * default, so `-fr` parses as `-f -r` and `.strict()` answers `Unknown arguments: f, r` (measured).
120
+ * Turning that off makes `-fr` work and breaks every grouped short - `rman list -sj` works today.
121
+ * So the long name carries the meaning and the incumbent short stays.
122
+ */
123
+ export declare function fromRootOption(verb: string): {
124
+ fromRoot: {
125
+ target: "cli";
126
+ cliName: string;
127
+ alias: string;
128
+ describe: string;
129
+ type: "boolean";
130
+ };
131
+ };
20
132
  /** `--scope`/`--ignore`/`--deps`/`--dependents`, the same shape and describe text in every command
21
133
  * that supports them - mirrors `run.command.ts`'s own `applyRunOptions`. */
22
134
  export declare function applyPackageFilterOptions<T>(cmd: Argv<T>): Argv<T>;
@@ -24,7 +136,8 @@ export declare function readPackageFilterOptions(args: any): PackageFilterOption
24
136
  /**
25
137
  * Narrows `packages` (the full, already-resolved list - toposort order, if any, is preserved)
26
138
  * down to what `options` selects. `scope`/`ignore` match against each package's bare name (glob
27
- * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...); `ignore` is applied after `scope`, on
139
+ * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...), plus `ROOT_SELECTOR` (`"/"`) for the root
140
+ * package, which is matched structurally rather than by name; `ignore` is applied after `scope`, on
28
141
  * whatever it left. `deps`/`dependents` then each independently expand *that* matched set along
29
142
  * `Package.dependencies` (already the full transitive closure - see
30
143
  * `Repository`'s own `_updateDependencies`) and their results are unioned in - so `--deps
@@ -46,7 +159,7 @@ export declare function filterPackages(packages: Package[], options: PackageFilt
46
159
  */
47
160
  applySkip?: boolean): Package[];
48
161
  /**
49
- * `--root`/`-r`, with one describe text instead of four near-identical ones.
162
+ * `--from-root`/`-r`, with one describe text instead of four near-identical ones.
50
163
  *
51
164
  * **It only means anything where a command scopes by the current directory** - `run`/`build`/`test`,
52
165
  * `exec`, `clean`, `changelog` and `diff` narrow to `Repository.currentPackage` when you stand
@@ -54,10 +167,17 @@ applySkip?: boolean): Package[];
54
167
  * repository (`version`, `publish`, `list`, `changed`) it would be a flag that does nothing, which
55
168
  * is worse than not offering it: a no-op flag reads as a promise.
56
169
  *
170
+ * That same rule is why there is no `--root-only` beside it, however naturally the pair reads: it
171
+ * would be a no-op on `run`/`build`/`test` (the root is not in `repository.packages` at all and
172
+ * contributes only `pre`/`post` bookends), identical to this flag on `diff`, already what this flag
173
+ * does on `config`, and on `clean` actively misleading - the root's own sweep recurses through
174
+ * `packages/*`, so a `--root-only` there deletes *more* than a package-scoped run, not less
175
+ * (measured). Where the root genuinely is a candidate, `--scope /` says so - see `ROOT_SELECTOR`.
176
+ *
57
177
  * `verb` is the command's own word for what it does, so the text stays the sentence each command was
58
178
  * already saying.
59
179
  */
60
- export declare function applyRootOption<T>(cmd: Argv<T>, verb: string): Argv<T>;
61
- /** The `--root` flag as the services read it - beside `readPackageFilterOptions`, so a command
180
+ export declare function applyFromRootOption<T>(cmd: Argv<T>, verb: string): Argv<T>;
181
+ /** The `--from-root` flag as the services read it - beside `readPackageFilterOptions`, so a command
62
182
  * reads both the same way. */
63
- export declare function readRootOption(args: any): boolean | undefined;
183
+ export declare function readFromRootOption(args: any): boolean | undefined;
@@ -1,17 +1,120 @@
1
1
  import micromatch from 'micromatch';
2
+ /**
3
+ * **`--scope /` is the repository's own root package**, the one selector that is not a glob.
4
+ *
5
+ * The same `/` `.rmanrc`'s `"[/]"` block uses, and for the same reason stated there: *the root is
6
+ * never selected by name.* A glob matches package names, and a name can be anything - so
7
+ * `--scope rman-repo` happened to work (measured) while being exactly the name-based addressing the
8
+ * config selectors were redesigned to remove. `/` is structural, cannot collide with a package
9
+ * (nothing can be named it), and gives "the root" one spelling across config and CLI.
10
+ *
11
+ * Accepted by `--ignore` too. The asymmetry would be the thing to remember, and `--ignore /` -
12
+ * every package but the root - is a real thing to want of `clean`.
13
+ *
14
+ * **It selects nothing on a command whose candidates exclude the root**, which is most of them:
15
+ * `repository.packages` holds the workspace members only, so `rman list --scope /` and
16
+ * `rman run build --scope /` match nothing and say so. That is the honest answer rather than a
17
+ * special case - the root has no `list` row and contributes only `pre`/`post` bookends to `run`.
18
+ * `clean` and `changelog`, which put the root in their candidate list on purpose, are where it bites.
19
+ */
20
+ export const ROOT_SELECTOR = '/';
21
+ /**
22
+ * `--scope`/`--ignore`/`--deps`/`--dependents` as a **declaration** rather than a builder call -
23
+ * spread into a command's `config` block:
24
+ *
25
+ * ```ts
26
+ * config: { ...packageFilterOptions, ...branchGuardOptions, changelog: { ... } }
27
+ * ```
28
+ *
29
+ * **`satisfies`, never a `: Record<...>` annotation.** An annotation widens `type: 'string'` back to
30
+ * `string`, and every type derived from the declaration - the config contribution, the option's own
31
+ * value type - collapses with it. `satisfies` checks the shape and keeps the literals, and it also
32
+ * catches a misspelled key *here*, at the group's own line, rather than in the ten commands that
33
+ * spread it. The `Argv` chaining this replaces could not: a typo there was simply a new option.
34
+ *
35
+ * **Every option is `target: 'cli'`, including the ones that have a config twin.** A shared group
36
+ * belongs to no command, so declaring one `'both'` would contribute `version.scope` - a key nothing
37
+ * reads. Where a `.rmanrc` equivalent exists it is a core key in its own right (`allowBranch`), and
38
+ * a command that reads it names it in `configKeys`.
39
+ */
40
+ export const packageFilterOptions = {
41
+ scope: {
42
+ target: 'cli',
43
+ describe: 'Only include packages whose name matches this glob, or "/" for the root package (repeatable)',
44
+ // Deliberately 'string', not 'array': an array-typed option greedily swallows every
45
+ // following bare word as its own value, which would eat "exec"'s [command..] positional
46
+ // whole. yargs still collects repeated "--scope a --scope b" into an array either way.
47
+ type: 'string',
48
+ },
49
+ ignore: {
50
+ target: 'cli',
51
+ describe: 'Exclude packages matching this glob (or "/" for the root) - applied after --scope',
52
+ type: 'string',
53
+ },
54
+ platform: {
55
+ target: 'cli',
56
+ describe: 'Only include packages of these platforms, e.g. --platform=node,cargo (repeatable)',
57
+ type: 'string',
58
+ },
59
+ deps: {
60
+ target: 'cli',
61
+ describe: 'Also include every package the matched set depends on',
62
+ type: 'boolean',
63
+ },
64
+ dependents: {
65
+ target: 'cli',
66
+ describe: 'Also include every package that depends on the matched set',
67
+ type: 'boolean',
68
+ },
69
+ };
70
+ /**
71
+ * `--from-root`/`-r` - a group of one, and a function because its text is the command's own word
72
+ * for what it does. Spread it like the others: `...fromRootOption('Build')`.
73
+ *
74
+ * Only where a command scopes by the current directory; see `applyFromRootOption` for why adding it
75
+ * elsewhere is worse than leaving it out.
76
+ *
77
+ * **It was `--root` through 1.x, and the name said the opposite of what it does.** Every reader
78
+ * spells it the same single line - `options.fromRoot ? undefined : repository.currentPackage` - so
79
+ * the flag means *ignore where I am standing*, i.e. the **whole repository**. `--root` reads as "the
80
+ * root alone", which is the narrowest possible set rather than the widest, and the confusion was
81
+ * about to become a contradiction: a `--root-only` beside a `--root` that meant "everything" is
82
+ * unreadable.
83
+ *
84
+ * **`-r` is kept, and a two-letter `-fr` is not possible.** yargs' `short-option-groups` is on by
85
+ * default, so `-fr` parses as `-f -r` and `.strict()` answers `Unknown arguments: f, r` (measured).
86
+ * Turning that off makes `-fr` work and breaks every grouped short - `rman list -sj` works today.
87
+ * So the long name carries the meaning and the incumbent short stays.
88
+ */
89
+ export function fromRootOption(verb) {
90
+ return {
91
+ fromRoot: {
92
+ target: 'cli',
93
+ cliName: 'from-root',
94
+ alias: 'r',
95
+ describe: `${verb} across the whole repository even when the current directory is inside a single ` +
96
+ 'package (which otherwise scopes it to just that package). No effect elsewhere.',
97
+ type: 'boolean',
98
+ },
99
+ };
100
+ }
2
101
  /** `--scope`/`--ignore`/`--deps`/`--dependents`, the same shape and describe text in every command
3
102
  * that supports them - mirrors `run.command.ts`'s own `applyRunOptions`. */
4
103
  export function applyPackageFilterOptions(cmd) {
5
104
  return cmd
6
105
  .option('scope', {
7
- describe: 'Only include packages whose name matches this glob (repeatable)',
106
+ describe: 'Only include packages whose name matches this glob, or "/" for the root package (repeatable)',
8
107
  // Deliberately 'string', not 'array': an array-typed option greedily swallows every
9
108
  // following bare word as its own value, which would eat "exec"'s [command..] positional
10
109
  // whole. yargs still collects repeated "--scope a --scope b" into an array either way.
11
110
  type: 'string',
12
111
  })
13
112
  .option('ignore', {
14
- describe: 'Exclude packages whose name matches this glob (repeatable) - applied after --scope',
113
+ describe: 'Exclude packages matching this glob (or "/" for the root) - applied after --scope',
114
+ type: 'string',
115
+ })
116
+ .option('platform', {
117
+ describe: 'Only include packages of these platforms, e.g. --platform=node,cargo (repeatable)',
15
118
  type: 'string',
16
119
  })
17
120
  .option('deps', {
@@ -27,6 +130,7 @@ export function readPackageFilterOptions(args) {
27
130
  return {
28
131
  scope: args.scope,
29
132
  ignore: args.ignore,
133
+ platform: args.platform,
30
134
  deps: args.deps,
31
135
  dependents: args.dependents,
32
136
  };
@@ -34,7 +138,8 @@ export function readPackageFilterOptions(args) {
34
138
  /**
35
139
  * Narrows `packages` (the full, already-resolved list - toposort order, if any, is preserved)
36
140
  * down to what `options` selects. `scope`/`ignore` match against each package's bare name (glob
37
- * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...); `ignore` is applied after `scope`, on
141
+ * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...), plus `ROOT_SELECTOR` (`"/"`) for the root
142
+ * package, which is matched structurally rather than by name; `ignore` is applied after `scope`, on
38
143
  * whatever it left. `deps`/`dependents` then each independently expand *that* matched set along
39
144
  * `Package.dependencies` (already the full transitive closure - see
40
145
  * `Repository`'s own `_updateDependencies`) and their results are unioned in - so `--deps
@@ -57,12 +162,16 @@ export function filterPackages(packages, options,
57
162
  applySkip = true) {
58
163
  let matched = applySkip ? packages.filter(p => p.config?.skip !== true) : packages;
59
164
  if (options.scope) {
60
- const patterns = toArray(options.scope);
61
- matched = matched.filter(p => micromatch.isMatch(p.name, patterns));
165
+ const selects = selector(options.scope);
166
+ matched = matched.filter(p => selects(p));
62
167
  }
63
168
  if (options.ignore) {
64
- const patterns = toArray(options.ignore);
65
- matched = matched.filter(p => !micromatch.isMatch(p.name, patterns));
169
+ const selects = selector(options.ignore);
170
+ matched = matched.filter(p => !selects(p));
171
+ }
172
+ if (options.platform !== undefined) {
173
+ const wanted = platformNames(options.platform, packages);
174
+ matched = matched.filter(p => wanted.has(p.provider.toLowerCase()));
66
175
  }
67
176
  if (!options.deps && !options.dependents)
68
177
  return matched;
@@ -83,11 +192,8 @@ applySkip = true) {
83
192
  }
84
193
  return packages.filter(p => included.has(p));
85
194
  }
86
- function toArray(value) {
87
- return Array.isArray(value) ? value : [value];
88
- }
89
195
  /**
90
- * `--root`/`-r`, with one describe text instead of four near-identical ones.
196
+ * `--from-root`/`-r`, with one describe text instead of four near-identical ones.
91
197
  *
92
198
  * **It only means anything where a command scopes by the current directory** - `run`/`build`/`test`,
93
199
  * `exec`, `clean`, `changelog` and `diff` narrow to `Repository.currentPackage` when you stand
@@ -95,19 +201,94 @@ function toArray(value) {
95
201
  * repository (`version`, `publish`, `list`, `changed`) it would be a flag that does nothing, which
96
202
  * is worse than not offering it: a no-op flag reads as a promise.
97
203
  *
204
+ * That same rule is why there is no `--root-only` beside it, however naturally the pair reads: it
205
+ * would be a no-op on `run`/`build`/`test` (the root is not in `repository.packages` at all and
206
+ * contributes only `pre`/`post` bookends), identical to this flag on `diff`, already what this flag
207
+ * does on `config`, and on `clean` actively misleading - the root's own sweep recurses through
208
+ * `packages/*`, so a `--root-only` there deletes *more* than a package-scoped run, not less
209
+ * (measured). Where the root genuinely is a candidate, `--scope /` says so - see `ROOT_SELECTOR`.
210
+ *
98
211
  * `verb` is the command's own word for what it does, so the text stays the sentence each command was
99
212
  * already saying.
100
213
  */
101
- export function applyRootOption(cmd, verb) {
102
- return cmd.option('root', {
214
+ export function applyFromRootOption(cmd, verb) {
215
+ return cmd.option('from-root', {
103
216
  alias: 'r',
104
217
  describe: `${verb} across the whole repository even when the current directory is inside a single ` +
105
218
  'package (which otherwise scopes it to just that package). No effect elsewhere.',
106
219
  type: 'boolean',
107
220
  });
108
221
  }
109
- /** The `--root` flag as the services read it - beside `readPackageFilterOptions`, so a command
222
+ /** The `--from-root` flag as the services read it - beside `readPackageFilterOptions`, so a command
110
223
  * reads both the same way. */
111
- export function readRootOption(args) {
112
- return args.root;
224
+ export function readFromRootOption(args) {
225
+ return args.fromRoot;
226
+ }
227
+ function toArray(value) {
228
+ return Array.isArray(value) ? value : [value];
229
+ }
230
+ /**
231
+ * One `--scope`/`--ignore` value list as a predicate, with `ROOT_SELECTOR` lifted out of the globs.
232
+ *
233
+ * Split once rather than per package. The root is answered by `wantsRoot` alone and everything else
234
+ * by the globs, so the two never consult each other - and `micromatch` is asked only when a glob is
235
+ * left, since an empty pattern list must match no member rather than all of them, which is what
236
+ * makes a bare `--scope /` select the root **and nothing else**.
237
+ *
238
+ * **A glob is never offered the root**, which is the other half of `ROOT_SELECTOR` and the reason it
239
+ * is not merely a second spelling. `.rmanrc`'s selectors state the same rule - `"[my-*]"` cannot
240
+ * pick up a repository whose root package is called `my-repo`, and `"[*]"` means the members - and
241
+ * the CLI disagreed with it: measured, `rman clean --scope 'rman*'` selected this repository's root.
242
+ * For `clean` that is destructive rather than merely surprising, since the root's own sweep recurses
243
+ * through `packages/*`. Globs are the members, `/` is the root, in both vocabularies.
244
+ */
245
+ /**
246
+ * `--platform` as a set of lower-cased names, refusing one no package in the repository belongs to.
247
+ *
248
+ * **Comma-split, unlike `--scope`, and the asymmetry is the values' own.** A platform's name is a
249
+ * short identifier a plugin chose, so `--platform=node,cargo` cannot be ambiguous; a scope glob is
250
+ * arbitrary text, where splitting would take a character away from the pattern language. Repeating
251
+ * the flag works for both.
252
+ *
253
+ * **An unknown name is an error, where an unmatched `--scope` glob is not**, and the difference is
254
+ * whether rman knows the answer set. It does here: the platforms are the ones its packages belong
255
+ * to, so `--platform crago` is a typo rman can see, and left alone it is the silent empty result
256
+ * this whole codebase keeps ruling out. The message lists what the repository has - the same call
257
+ * `publish --target` makes against its registry.
258
+ *
259
+ * **Compared case-insensitively.** `Platform.name` is the authority on spelling and `--platform Node`
260
+ * is not a different request; refusing it would be pedantry with an empty result attached.
261
+ *
262
+ * Asked of the packages rather than of `app.platforms`, which `filterPackages` does not have and
263
+ * which would be the wrong set anyway: a registered platform that claimed no directory is not an
264
+ * answer to "what is in this repository".
265
+ */
266
+ function platformNames(value, packages) {
267
+ const wanted = toArray(value)
268
+ .flatMap(entry => entry.split(','))
269
+ .map(name => name.trim().toLowerCase())
270
+ .filter(Boolean);
271
+ /** An empty `provider` is a package no technology claimed, and there is no spelling for it - a
272
+ * flag selecting "none" would be `--platform ''`, which no shell makes pleasant. Left out of the
273
+ * known set, so naming it is refused like any other unknown. */
274
+ const present = new Set(packages.map(p => p.provider.toLowerCase()).filter(Boolean));
275
+ for (const name of wanted) {
276
+ if (present.has(name))
277
+ continue;
278
+ throw new Error(`--platform "${name}" matches no package in this repository. ` +
279
+ (present.size
280
+ ? `It holds: ${[...present].sort().join(', ')}.`
281
+ : 'No package here belongs to a platform - its ".rmanrc" names no "plugins".'));
282
+ }
283
+ return new Set(wanted);
284
+ }
285
+ function selector(value) {
286
+ const patterns = toArray(value);
287
+ const wantsRoot = patterns.includes(ROOT_SELECTOR);
288
+ const globs = patterns.filter(p => p !== ROOT_SELECTOR);
289
+ /** Against `pkg.selector`, which is what `.rmanrc`'s `"[glob]"` matches - one vocabulary, as the
290
+ * `/` above already is. It was `pkg.name`, and the two coincide for every Node repository; a
291
+ * package having a name at all is an ecosystem's promise, and a repository can assign a selector
292
+ * where its own does not offer one. */
293
+ return pkg => (pkg.isRoot ? wantsRoot : globs.length > 0 && micromatch.isMatch(pkg.selector, globs));
113
294
  }
@@ -5,7 +5,7 @@
5
5
  * Needed because a config legitimately holds functions now: a `run.<script>` or `version.<slot>`
6
6
  * step written as JavaScript, and an `if` written the same way. It was already needed before that,
7
7
  * though, which is the better argument for doing it here rather than at one call site - a
8
- * `plugins` entry given in its object form carries the plugin's seams, and `rman config --root`
8
+ * `plugins` entry given in its object form carries the plugin's seams, and `rman config --from-root`
9
9
  * died on one with `unacceptable kind of an object to dump [object Function]` (measured, on a
10
10
  * repository whose shared config did nothing more unusual than `extends` a plugin package).
11
11
  *
@@ -5,7 +5,7 @@
5
5
  * Needed because a config legitimately holds functions now: a `run.<script>` or `version.<slot>`
6
6
  * step written as JavaScript, and an `if` written the same way. It was already needed before that,
7
7
  * though, which is the better argument for doing it here rather than at one call site - a
8
- * `plugins` entry given in its object form carries the plugin's seams, and `rman config --root`
8
+ * `plugins` entry given in its object form carries the plugin's seams, and `rman config --from-root`
9
9
  * died on one with `unacceptable kind of an object to dump [object Function]` (measured, on a
10
10
  * repository whose shared config did nothing more unusual than `extends` a plugin package).
11
11
  *
@@ -1,5 +1,15 @@
1
+ import type { RmanApplication } from '../core/application.js';
1
2
  import { type LogLevel } from './logger.js';
2
3
  export interface RunBinOptions {
4
+ /**
5
+ * The application whose technologies put a repository's locally installed binaries on PATH -
6
+ * `node_modules/.bin` for a Node repository, whatever another technology uses.
7
+ *
8
+ * Passed rather than looked up, so a command run against one repository can never pick up the
9
+ * binaries of another in the same process. Omitted (a caller outside any repository) leaves the
10
+ * inherited PATH exactly as it was, which is also what a repository naming no plugin gets.
11
+ */
12
+ app?: RmanApplication;
3
13
  /** Where to run it, and the directory `node_modules/.bin` is resolved from. Default `process.cwd()`. */
4
14
  cwd?: string;
5
15
  /** 'inherit' streams the child's output straight to the terminal; 'pipe' captures it and resolves
package/utils/run-bin.js CHANGED
@@ -34,7 +34,7 @@ export async function runBin(bin, argv, options = {}) {
34
34
  const child = spawn(process.platform === 'win32' ? `${bin}.cmd` : bin, argv, {
35
35
  cwd,
36
36
  stdio: stdio === 'inherit' ? 'inherit' : 'pipe',
37
- env: BinPath.env({ cwd, env: options.env }),
37
+ env: BinPath.env({ cwd, env: options.env, app: options.app }),
38
38
  windowsHide: true,
39
39
  });
40
40
  /** So an interrupted rman does not leave this running - `exec` always did this and this did not,
@@ -0,0 +1,97 @@
1
+ import type { RunService } from '../services/run.service.js';
2
+ /**
3
+ * Everything `run`, `build` and `test` accept - the package filter, the branch guard, `--from-root`, and
4
+ * the scheduling flags on top.
5
+ *
6
+ * **In `utils/` rather than in `run.command.ts`, which is where the `Argv` version lived.** `build`
7
+ * and `test` had to import it from a sibling command, which reads as a dependency between commands
8
+ * and is not one: they share a declaration, the way they already share `packageFilterOptions`.
9
+ *
10
+ * Every option is `target: 'cli'`. The `.rmanrc` half of these lives at `run.<script>.concurrency`,
11
+ * `.bail`, `.topo`, `.progress` - keyed by script name, so it is the one config shape that cannot be
12
+ * derived from a flat option map and stays hand-written in `RmanConfig`.
13
+ */
14
+ export declare const runOptions: {
15
+ parallel: {
16
+ target: "cli";
17
+ describe: string;
18
+ /** No `type`: the flag takes a boolean *or* a number, and `coerce` is what says so - which is
19
+ * also where `OptionValue` reads this option's type from. */
20
+ coerce: (v: unknown) => boolean | number | undefined;
21
+ };
22
+ bail: {
23
+ target: "cli";
24
+ describe: string;
25
+ type: "boolean";
26
+ };
27
+ topo: {
28
+ target: "cli";
29
+ describe: string;
30
+ type: "boolean";
31
+ };
32
+ progress: {
33
+ target: "cli";
34
+ describe: string;
35
+ type: "boolean";
36
+ };
37
+ changed: {
38
+ target: "cli";
39
+ alias: string;
40
+ describe: string;
41
+ type: "boolean";
42
+ };
43
+ changedSince: {
44
+ target: "cli";
45
+ cliName: string;
46
+ describe: string;
47
+ type: "string";
48
+ /** Declared on the option rather than as a separate `.conflicts()` call - one place says what
49
+ * this flag is and what it cannot be combined with. */
50
+ conflicts: string;
51
+ };
52
+ fromRoot: {
53
+ target: "cli";
54
+ cliName: string;
55
+ alias: string;
56
+ describe: string;
57
+ type: "boolean";
58
+ };
59
+ allowBranch: {
60
+ target: "cli";
61
+ cliName: string;
62
+ describe: string;
63
+ type: "string";
64
+ };
65
+ ignoreBranch: {
66
+ target: "cli";
67
+ cliName: string;
68
+ describe: string;
69
+ type: "string";
70
+ };
71
+ scope: {
72
+ target: "cli";
73
+ describe: string;
74
+ type: "string";
75
+ };
76
+ ignore: {
77
+ target: "cli";
78
+ describe: string;
79
+ type: "string";
80
+ };
81
+ platform: {
82
+ target: "cli";
83
+ describe: string;
84
+ type: "string";
85
+ };
86
+ deps: {
87
+ target: "cli";
88
+ describe: string;
89
+ type: "boolean";
90
+ };
91
+ dependents: {
92
+ target: "cli";
93
+ describe: string;
94
+ type: "boolean";
95
+ };
96
+ };
97
+ export declare function readRunOptions(args: any): RunService.Options;