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.
Files changed (187) hide show
  1. package/README.md +65 -31
  2. package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
  3. package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
  4. package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
  5. package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
  6. package/builtins/platforms/node/commands/ci.command.js +60 -0
  7. package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
  8. package/builtins/platforms/node/commands/clean.command.js +74 -0
  9. package/builtins/platforms/node/index.d.ts +8 -0
  10. package/builtins/platforms/node/index.js +8 -0
  11. package/builtins/platforms/node/node-config.interface.d.ts +76 -0
  12. package/builtins/platforms/node/node-config.interface.js +7 -0
  13. package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
  14. package/builtins/platforms/node/node-manifest.provider.js +125 -0
  15. package/builtins/platforms/node/node.platform.d.ts +53 -0
  16. package/builtins/platforms/node/node.platform.js +134 -0
  17. package/builtins/platforms/node/services/ci.service.d.ts +47 -0
  18. package/builtins/platforms/node/services/ci.service.js +213 -0
  19. package/builtins/platforms/node/services/clean.service.d.ts +53 -0
  20. package/builtins/platforms/node/services/clean.service.js +237 -0
  21. package/builtins/platforms/node/services/publish.service.d.ts +114 -0
  22. package/builtins/platforms/node/services/publish.service.js +371 -0
  23. package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
  24. package/builtins/platforms/node/services/version-plan.service.js +58 -0
  25. package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
  26. package/builtins/platforms/node/utils/workspace-range.js +28 -0
  27. package/builtins/presets/index.d.ts +15 -0
  28. package/builtins/presets/index.js +57 -0
  29. package/builtins/presets/node.d.ts +16 -0
  30. package/builtins/presets/node.js +57 -0
  31. package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +52 -31
  32. package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
  33. package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
  34. package/builtins/publish-targets/docker/docker.target.js +40 -0
  35. package/builtins/publish-targets/docker/index.d.ts +2 -0
  36. package/builtins/publish-targets/docker/index.js +2 -0
  37. package/builtins/publish-targets/npm/index.d.ts +2 -0
  38. package/builtins/publish-targets/npm/index.js +2 -0
  39. package/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
  40. package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
  41. package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
  42. package/builtins/publish-targets/npm/npm-view.js +71 -0
  43. package/cli.d.ts +8 -0
  44. package/cli.js +228 -93
  45. package/commands/build.command.d.ts +177 -3
  46. package/commands/build.command.js +20 -10
  47. package/commands/changelog.command.d.ts +296 -3
  48. package/commands/changelog.command.js +127 -43
  49. package/commands/config.command.d.ts +44 -3
  50. package/commands/config.command.js +53 -24
  51. package/commands/diff.command.d.ts +37 -3
  52. package/commands/diff.command.js +25 -16
  53. package/commands/exec.command.d.ts +193 -3
  54. package/commands/exec.command.js +60 -58
  55. package/commands/github-release.command.d.ts +154 -3
  56. package/commands/github-release.command.js +67 -37
  57. package/commands/import.command.d.ts +36 -3
  58. package/commands/import.command.js +28 -20
  59. package/commands/info.command.d.ts +35 -7
  60. package/commands/info.command.js +36 -30
  61. package/commands/lint.command.d.ts +176 -0
  62. package/commands/lint.command.js +34 -0
  63. package/commands/list.command.d.ts +163 -3
  64. package/commands/list.command.js +109 -71
  65. package/commands/publish.command.d.ts +231 -0
  66. package/commands/publish.command.js +304 -0
  67. package/commands/run.command.d.ts +186 -6
  68. package/commands/run.command.js +26 -72
  69. package/commands/test.command.d.ts +173 -3
  70. package/commands/test.command.js +16 -10
  71. package/commands/version.command.d.ts +330 -3
  72. package/commands/version.command.js +292 -78
  73. package/commands.d.ts +32 -0
  74. package/commands.js +28 -0
  75. package/constants.js +1 -1
  76. package/core/application.d.ts +116 -0
  77. package/core/application.js +160 -0
  78. package/core/classes/package.d.ts +219 -0
  79. package/core/classes/package.js +214 -0
  80. package/core/classes/registry.d.ts +30 -0
  81. package/core/classes/registry.js +47 -0
  82. package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
  83. package/core/{repository.js → classes/repository.js} +195 -75
  84. package/core/classes/service.d.ts +49 -0
  85. package/core/classes/service.js +40 -0
  86. package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
  87. package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
  88. package/core/classes/workspace.d.ts +275 -0
  89. package/core/classes/workspace.js +537 -0
  90. package/core/command-builder.d.ts +14 -0
  91. package/core/command-builder.js +94 -0
  92. package/core/config/config-file-scope.d.ts +107 -0
  93. package/core/config/config-file-scope.js +226 -0
  94. package/core/config/config-interpolator.d.ts +164 -0
  95. package/core/config/config-interpolator.js +577 -0
  96. package/core/config/config-paths.d.ts +65 -0
  97. package/core/config/config-paths.js +78 -0
  98. package/core/config/config-reader.d.ts +192 -0
  99. package/core/config/config-reader.js +727 -0
  100. package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
  101. package/core/config/merge-config.js +260 -0
  102. package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
  103. package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
  104. package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
  105. package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
  106. package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
  107. package/core/interfaces/manifest.js +132 -0
  108. package/core/interfaces/plugin.d.ts +174 -0
  109. package/core/interfaces/plugin.js +124 -0
  110. package/core/interfaces/publish-target.d.ts +136 -0
  111. package/core/interfaces/publish-target.js +43 -0
  112. package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
  113. package/core/interfaces/run-step.js +1 -0
  114. package/core/plugin-loader.d.ts +25 -0
  115. package/core/plugin-loader.js +53 -0
  116. package/index.d.ts +122 -24
  117. package/index.js +66 -16
  118. package/interfaces/config-scope.interface.d.ts +223 -0
  119. package/interfaces/config-scope.interface.js +1 -0
  120. package/interfaces/rman-config.interface.d.ts +821 -203
  121. package/interfaces/rman-config.interface.js +78 -1
  122. package/package.json +3 -1
  123. package/services/change-hash.service.d.ts +42 -8
  124. package/services/change-hash.service.js +92 -13
  125. package/services/changelog.service.d.ts +91 -52
  126. package/services/changelog.service.js +460 -74
  127. package/services/config-service.d.ts +8 -0
  128. package/services/config-service.js +3 -0
  129. package/services/exec.service.d.ts +23 -12
  130. package/services/exec.service.js +14 -9
  131. package/services/github-release.service.d.ts +45 -34
  132. package/services/github-release.service.js +15 -12
  133. package/services/import.service.d.ts +25 -14
  134. package/services/import.service.js +9 -5
  135. package/services/list.service.d.ts +67 -13
  136. package/services/list.service.js +62 -15
  137. package/services/run.service.d.ts +35 -22
  138. package/services/run.service.js +316 -233
  139. package/services/system-info.d.ts +1 -1
  140. package/services/version-plan.service.d.ts +35 -8
  141. package/services/version-plan.service.js +96 -36
  142. package/services/version.service.d.ts +34 -14
  143. package/services/version.service.js +35 -16
  144. package/services.d.ts +1 -1
  145. package/services.js +1 -1
  146. package/utils/bin-path.d.ts +6 -7
  147. package/utils/bin-path.js +7 -18
  148. package/utils/branch-guard.d.ts +30 -1
  149. package/utils/branch-guard.js +31 -0
  150. package/utils/color-yaml.d.ts +8 -0
  151. package/utils/color-yaml.js +86 -0
  152. package/utils/exec.d.ts +10 -0
  153. package/utils/exec.js +1 -1
  154. package/utils/git.d.ts +26 -0
  155. package/utils/git.js +65 -0
  156. package/utils/logger.d.ts +2 -2
  157. package/utils/logger.js +1 -1
  158. package/utils/package-filter.d.ts +135 -8
  159. package/utils/package-filter.js +204 -16
  160. package/utils/printable-config.d.ts +9 -1
  161. package/utils/printable-config.js +46 -1
  162. package/utils/progress-panel.d.ts +9 -0
  163. package/utils/progress-panel.js +23 -1
  164. package/utils/release-version.d.ts +1 -1
  165. package/utils/run-bin.d.ts +10 -0
  166. package/utils/run-bin.js +1 -1
  167. package/utils/run-options.d.ts +97 -0
  168. package/utils/run-options.js +81 -0
  169. package/utils/version-group.d.ts +31 -0
  170. package/utils/version-group.js +43 -0
  171. package/utils/version-stamp.d.ts +1 -1
  172. package/utils/version-stamp.js +1 -1
  173. package/commands/changed.command.d.ts +0 -3
  174. package/commands/changed.command.js +0 -29
  175. package/core/config.d.ts +0 -409
  176. package/core/config.js +0 -939
  177. package/core/extends-config.d.ts +0 -27
  178. package/core/extends-config.js +0 -89
  179. package/core/manifest.js +0 -150
  180. package/core/merge-config.js +0 -219
  181. package/core/package.d.ts +0 -83
  182. package/core/package.js +0 -106
  183. package/core/plugin.d.ts +0 -112
  184. package/core/plugin.js +0 -189
  185. package/core/workspace.d.ts +0 -68
  186. package/core/workspace.js +0 -83
  187. /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
@@ -1,5 +1,7 @@
1
- import type { Package } from './package.js';
2
- import { type VersionScheme } from './version-scheme.js';
1
+ import { RmanApplication } from '../application.js';
2
+ import type { Package } from '../classes/package.js';
3
+ import { type VersionScheme } from '../classes/version-scheme.js';
4
+ import type { Platform } from './plugin.js';
3
5
  /**
4
6
  * A package's identity, however its ecosystem happens to record it.
5
7
  *
@@ -13,7 +15,7 @@ export interface Manifest {
13
15
  /** Excluded from publishing by the package's own declaration (`package.json#private`). Not the
14
16
  * same as `.rmanrc "publish.skip"`, which is the *repository's* declaration about it. */
15
17
  private?: boolean;
16
- /** The document as the ecosystem wrote it. `rman-node`'s own commands read `package.json` fields
18
+ /** The document as the ecosystem wrote it. The `node` built-in's own commands read `package.json` fields
17
19
  * the core has no opinion about (`scripts`, `publishConfig`, `engines`) off this. */
18
20
  raw: any;
19
21
  }
@@ -23,7 +25,7 @@ export interface Manifest {
23
25
  * **The core has no provider.** "The name and version live in a `package.json`" is true of npm and
24
26
  * of nothing else - a `Cargo.toml`, a `pyproject.toml` and a `go.mod` each say the same thing
25
27
  * differently, and the version is not even in the same *kind* of place in all of them.
26
- * `rman-node` contributes the `package.json` one.
28
+ * The `node` built-in contributes the `package.json` one.
27
29
  *
28
30
  * Paired with `VersionScheme` on purpose: the ecosystem that decides *where* a version is written
29
31
  * is the one that decides *how* it is numbered, so a provider supplies both and a package gets a
@@ -32,7 +34,7 @@ export interface Manifest {
32
34
  export interface ManifestProvider {
33
35
  /**
34
36
  * **The ecosystem this provider speaks for**, surfaced on every package it reads as
35
- * `Package.provider` - `'node'` for `rman-node`. Short and about the technology, not about the
37
+ * `Package.provider` - `'node'` for the built-in of that name. Short and about the technology, not about the
36
38
  * file: `fileName` already says `package.json`, and a name repeating it would tell a caller
37
39
  * nothing it did not have.
38
40
  *
@@ -124,6 +126,23 @@ export interface ManifestProvider {
124
126
  scope?: string;
125
127
  unscopedName: string;
126
128
  };
129
+ /**
130
+ * **What addresses this package** - what a `"[glob]"` block and `--scope`/`--ignore` match
131
+ * against, and what has to be unique within a repository.
132
+ *
133
+ * **The platform's job, because a name is an ecosystem's promise and not rman's.** npm guarantees
134
+ * `package.json#name` exists and identifies the package, so the node built-in has nothing to do
135
+ * here and the default - the manifest's own name - is already its answer. An ecosystem where a
136
+ * package need not be named, or where the name is not unique, returns `undefined` and the
137
+ * repository assigns one with `.rmanrc "name"`.
138
+ *
139
+ * Separate from `Manifest.name`, which is what the package *calls itself*: those coincide for npm
140
+ * and need not anywhere else. A Go module's name is an import path, and `--scope github.com/x/y`
141
+ * is not how anyone would want to address it.
142
+ *
143
+ * Omit it and the manifest's name is used, so this seam existing changes nothing.
144
+ */
145
+ selector?(manifest: Manifest, dir: string): string | undefined;
127
146
  /**
128
147
  * Rewrites this manifest's references to in-repo packages that just got a new version.
129
148
  *
@@ -147,35 +166,37 @@ export interface ManifestProvider {
147
166
  *
148
167
  * A namespace rather than loose `addManifestProvider`/`readManifest` functions because a namespace
149
168
  * is what a plugin can *augment*: `declare module 'rman' { namespace Manifest { ... } }` is how
150
- * `rman-node` already adds to `SystemInfo`, and anything this seam grows later can arrive the
169
+ * the `node` built-in already adds to `SystemInfo`, and anything this seam grows later can arrive the
151
170
  * same way instead of as another top-level export.
152
171
  */
153
172
  export declare namespace Manifest {
154
- /** Registers a provider. Called by `loadPlugins` for each plugin's `manifest`, in `plugins`
155
- * declaration order - so which one answers is a function of the repository's own config. */
156
- function addProvider(provider: ManifestProvider): void;
157
- /** For tests, which would otherwise leak a provider into every later case in the process. */
158
- function clearProviders(): void;
159
173
  /**
160
- * Reads `dir`'s manifest through the first provider that recognizes it, with the scheme that
161
- * provider brings.
162
- *
163
- * **With no provider registered, or none recognizing the directory**, the fallback is a package
164
- * named after its own directory at version `0.0.0`. That is deliberately the least it can claim:
165
- * the directory name is a fact, and `0.0.0` is the version a thing has when nothing says
166
- * otherwise. The alternative - refusing to construct a package at all - would make `rman info` and
167
- * `rman list` fail in a repository whose `.rmanrc` simply names no plugin yet, which is exactly
168
- * when someone needs to run them.
174
+ * Reads `dir`'s manifest through **the platform that already claimed it**, with the scheme that
175
+ * platform brings.
176
+ *
177
+ * **It takes a platform rather than searching for one**, and that is where the walk changed
178
+ * things. This used to loop over every registered platform, once per `Package` constructed - so a
179
+ * package asked "who am I?" and the answer was whoever recognized it first, re-derived at every
180
+ * construction. The walk decides it once per directory now (`Workspace.walk` ->
181
+ * `app.platformFor`) and hands it to the package, which is also what lets a nested Cargo package
182
+ * sit inside a Node monorepo: the platform is a fact about the directory, established by whoever
183
+ * found it, not re-guessed by whoever reads it.
184
+ *
185
+ * **When that platform reads nothing** - `basePlatform`, or a provider that claimed the directory
186
+ * and then found nothing in it - the fallback is a package named after its own directory at
187
+ * version `0.0.0`. Deliberately the least it can claim: the directory name is a fact, and `0.0.0`
188
+ * is the version a thing has when nothing says otherwise. The alternative - refusing to construct
189
+ * a package at all - would make `rman info` and `rman list` fail in a repository whose `.rmanrc`
190
+ * simply names no plugin yet, which is exactly when someone needs to run them.
169
191
  */
170
- function read(dir: string): {
192
+ function read(platform: Platform, dir: string): {
171
193
  manifest: Manifest;
172
194
  versionScheme: VersionScheme;
173
195
  fileName: string;
174
- provider: string;
175
196
  };
176
197
  /** Writes through whichever provider recognizes `dir`. Throws when none does: a write that lands
177
198
  * nowhere is worse than one that fails, since the caller has already decided the new version. */
178
- function write(dir: string, manifest: Manifest): void;
199
+ function write(app: RmanApplication, dir: string, manifest: Manifest): void;
179
200
  /**
180
201
  * The packages `pkg` declares a dependency on, via whichever provider recognizes it.
181
202
  *
@@ -186,7 +207,7 @@ export declare namespace Manifest {
186
207
  function dependenciesOf(pkg: Package, candidates: readonly Package[]): Package[];
187
208
  /** `${{ pkg.scope }}`/`${{ pkg.unscopedName }}`, by whichever provider recognizes `dir` - and
188
209
  * "no scope, the name is its own unscoped form" when none has an opinion. */
189
- function splitName(dir: string, name: string): {
210
+ function splitName(app: RmanApplication, dir: string, name: string): {
190
211
  scope?: string;
191
212
  unscopedName: string;
192
213
  };
@@ -199,7 +220,7 @@ export declare namespace Manifest {
199
220
  function updateDependencyVersions(pkg: Package, bumped: ReadonlyMap<Package, string>): void;
200
221
  /**
201
222
  * Rewrites a hard-coded version in `content` through `pkg`'s own ecosystem - see
202
- * `ManifestProvider.stampVersion`.
223
+ * `Plugin.stampVersion`.
203
224
  *
204
225
  * `undefined` covers three cases the caller has to tell apart from each other, and cannot: no
205
226
  * provider claimed the package, the provider has no opinion about stamping, or it looked and found
@@ -217,6 +238,7 @@ export declare namespace Manifest {
217
238
  * plugin: git tags then answer the boundary question alone.
218
239
  */
219
240
  function publishedVersion(pkg: Package): Promise<string | undefined>;
220
- /** The file names providers look for, for an error message that can say what was expected. */
221
- function fileNames(): string[];
241
+ /** The file names the registered technologies look for, for an error message that can say what
242
+ * was expected. */
243
+ function fileNames(app: RmanApplication): string[];
222
244
  }
@@ -0,0 +1,132 @@
1
+ import path from 'node:path';
2
+ import { RmanApplication } from '../application.js';
3
+ import { semverScheme } from '../classes/version-scheme.js';
4
+ /**
5
+ * The registry, merged onto the `Manifest` interface so one name carries both the shape and the
6
+ * operations - `Manifest.read(dir)` returns a `Manifest`.
7
+ *
8
+ * A namespace rather than loose `addManifestProvider`/`readManifest` functions because a namespace
9
+ * is what a plugin can *augment*: `declare module 'rman' { namespace Manifest { ... } }` is how
10
+ * the `node` built-in already adds to `SystemInfo`, and anything this seam grows later can arrive the
11
+ * same way instead of as another top-level export.
12
+ */
13
+ export var Manifest;
14
+ (function (Manifest) {
15
+ /**
16
+ * Reads `dir`'s manifest through **the platform that already claimed it**, with the scheme that
17
+ * platform brings.
18
+ *
19
+ * **It takes a platform rather than searching for one**, and that is where the walk changed
20
+ * things. This used to loop over every registered platform, once per `Package` constructed - so a
21
+ * package asked "who am I?" and the answer was whoever recognized it first, re-derived at every
22
+ * construction. The walk decides it once per directory now (`Workspace.walk` ->
23
+ * `app.platformFor`) and hands it to the package, which is also what lets a nested Cargo package
24
+ * sit inside a Node monorepo: the platform is a fact about the directory, established by whoever
25
+ * found it, not re-guessed by whoever reads it.
26
+ *
27
+ * **When that platform reads nothing** - `basePlatform`, or a provider that claimed the directory
28
+ * and then found nothing in it - the fallback is a package named after its own directory at
29
+ * version `0.0.0`. Deliberately the least it can claim: the directory name is a fact, and `0.0.0`
30
+ * is the version a thing has when nothing says otherwise. The alternative - refusing to construct
31
+ * a package at all - would make `rman info` and `rman list` fail in a repository whose `.rmanrc`
32
+ * simply names no plugin yet, which is exactly when someone needs to run them.
33
+ */
34
+ function read(platform, dir) {
35
+ const provider = platform.manifestProvider;
36
+ const manifest = provider.read(dir);
37
+ return {
38
+ manifest: manifest ?? { name: path.basename(dir), version: '0.0.0', raw: {} },
39
+ versionScheme: provider.versionScheme ?? semverScheme,
40
+ /** Nothing read means nothing to name - a caller listing "the file I changed" has no file to
41
+ * list, which is correct rather than a placeholder that does not exist. */
42
+ fileName: manifest ? provider.fileName : '',
43
+ };
44
+ }
45
+ Manifest.read = read;
46
+ /** Writes through whichever provider recognizes `dir`. Throws when none does: a write that lands
47
+ * nowhere is worse than one that fails, since the caller has already decided the new version. */
48
+ function write(app, dir, manifest) {
49
+ for (const platform of app.platforms) {
50
+ if (platform.manifestProvider.read(dir)) {
51
+ platform.manifestProvider.write(dir, manifest);
52
+ return;
53
+ }
54
+ }
55
+ throw new Error(`No manifest provider recognizes "${dir}", so there is nowhere to write its version.\n` +
56
+ ` A repository's ".rmanrc" names its technologies - see "plugins" (e.g. ['node']).`);
57
+ }
58
+ Manifest.write = write;
59
+ /**
60
+ * The packages `pkg` declares a dependency on, via whichever provider recognizes it.
61
+ *
62
+ * No provider, or one with no opinion, means no declared dependencies - `.rmanrc "dependencies"`
63
+ * is then the only source, which is exactly right: a repository rman cannot read the manifests
64
+ * of can still describe its own graph by hand.
65
+ */
66
+ function dependenciesOf(pkg, candidates) {
67
+ return platformOf(pkg)?.manifestProvider.dependencies?.(pkg.manifest, candidates) ?? [];
68
+ }
69
+ Manifest.dependenciesOf = dependenciesOf;
70
+ /** `${{ pkg.scope }}`/`${{ pkg.unscopedName }}`, by whichever provider recognizes `dir` - and
71
+ * "no scope, the name is its own unscoped form" when none has an opinion. */
72
+ function splitName(app, dir, name) {
73
+ for (const platform of app.platforms) {
74
+ if (!platform.manifestProvider.read(dir))
75
+ continue;
76
+ return platform.manifestProvider.splitName?.(name) ?? { unscopedName: name };
77
+ }
78
+ return { unscopedName: name };
79
+ }
80
+ Manifest.splitName = splitName;
81
+ /**
82
+ * Refreshes `pkg`'s references to the packages in `bumped`, via whichever provider recognizes it.
83
+ *
84
+ * No provider, or one without an opinion, means nothing to rewrite - a repository whose packages
85
+ * reference each other by path has nothing here to go stale.
86
+ */
87
+ function updateDependencyVersions(pkg, bumped) {
88
+ platformOf(pkg)?.manifestProvider.updateDependencyVersions?.(pkg.manifest, bumped);
89
+ }
90
+ Manifest.updateDependencyVersions = updateDependencyVersions;
91
+ /**
92
+ * Rewrites a hard-coded version in `content` through `pkg`'s own ecosystem - see
93
+ * `Plugin.stampVersion`.
94
+ *
95
+ * `undefined` covers three cases the caller has to tell apart from each other, and cannot: no
96
+ * provider claimed the package, the provider has no opinion about stamping, or it looked and found
97
+ * nothing to change. All three mean "this file was not stamped", which is what `version` reports.
98
+ */
99
+ function stampVersion(pkg, file, content, version, options) {
100
+ return platformOf(pkg)?.manifestProvider.stampVersion?.(file, content, version, options);
101
+ }
102
+ Manifest.stampVersion = stampVersion;
103
+ /**
104
+ * What `pkg`'s own ecosystem's registry says its current version is - see
105
+ * `ManifestProvider.publishedVersion` for the one thing this is for and the one thing it must
106
+ * never be used for.
107
+ *
108
+ * `undefined` when the provider has no opinion, which includes every repository that names no
109
+ * plugin: git tags then answer the boundary question alone.
110
+ */
111
+ async function publishedVersion(pkg) {
112
+ return platformOf(pkg)?.manifestProvider.publishedVersion?.(pkg);
113
+ }
114
+ Manifest.publishedVersion = publishedVersion;
115
+ /** The file names the registered technologies look for, for an error message that can say what
116
+ * was expected. */
117
+ function fileNames(app) {
118
+ return [...app.platforms].map(platform => platform.manifestProvider.fileName);
119
+ }
120
+ Manifest.fileNames = fileNames;
121
+ /**
122
+ * The technology that claimed `pkg`, or `undefined` when none did - with nothing to look up.
123
+ *
124
+ * This used to search a registry by `pkg.provider` and fall back to re-reading the directory,
125
+ * because a package could be constructed before the provider that would claim it was registered.
126
+ * It cannot now: a package is handed its `Platform` at construction, by the application that
127
+ * resolved it. The `name` test is what "none claimed it" looks like - see `basePlatform`.
128
+ */
129
+ function platformOf(pkg) {
130
+ return pkg.platform.name ? pkg.platform : undefined;
131
+ }
132
+ })(Manifest || (Manifest = {}));
@@ -0,0 +1,174 @@
1
+ import type { RunService } from '../../services/run.service.js';
2
+ import type { VersionPlanService } from '../../services/version-plan.service.js';
3
+ import type { BinPath } from '../../utils/bin-path.js';
4
+ import type { RmanApplication } from '../application.js';
5
+ import type { Repository } from '../classes/repository.js';
6
+ import type { Workspace } from '../classes/workspace.js';
7
+ import type { ManifestProvider } from './manifest.js';
8
+ /**
9
+ * **One technology, as a whole** - Node, Cargo, Maven. Everything rman needs in order to treat a
10
+ * directory as a package of that technology.
11
+ *
12
+ * **This was called `Plugin`, and the name was a lie.** `manifestProvider` is required, which makes
13
+ * the type a *platform* by definition: a package that only ships commands never touches it, because
14
+ * commands are a config key. So the narrow thing took the narrow name, and `Plugin` became the
15
+ * broad one that may carry platforms - a plugin *provides* platforms, and may provide more than
16
+ * one (a package shipping both `maven` and `gradle` is one plugin, two platforms).
17
+ *
18
+ * These were six independent fields on a plugin, each with its own registry, and declaring one
19
+ * without the others type-checked. It is not a shape that admits sense: `getRunSteps` reads
20
+ * `pkg.manifest.raw?.scripts`, so contributing npm's step source without npm's manifest reader
21
+ * leaves it parsing whatever another technology produced. The coupling was already real; only the
22
+ * type failed to say so.
23
+ *
24
+ * **`RmanPlugin` and `TechStack` used to be two types and are now one.** The plugin existed solely to
25
+ * *register* the stack - its whole `init` was `ctx.addTechStack(...)` plus a command or two - and
26
+ * once a config carries commands and publish targets itself, that registration step has nothing
27
+ * left to do. What remains of a plugin is the technology, so that is what the type is.
28
+ *
29
+ * **`manifestProvider` stays grouped rather than flattened in here**, and it was tried the other
30
+ * way first: nine members about one file - reading it, writing it, what it declares, how its
31
+ * versions are numbered, stamped and looked up on a registry - are more legible as a named group
32
+ * than as nine siblings of `getBinPaths`. The name also keeps its distance from `Package.manifest`,
33
+ * which is the *data* this produces rather than the reader.
34
+ *
35
+ * **Everything optional is answered by its absence**, never by a default rman invented - see
36
+ * `basePlatform`.
37
+ */
38
+ export interface Platform {
39
+ /**
40
+ * **The ecosystem this speaks for**, surfaced on every package it reads as `Package.platform` -
41
+ * `'node'` for the built-in of that name. Short and about the technology, not about the file:
42
+ * `manifestProvider.fileName` already says `package.json`, and a name repeating it would tell a
43
+ * caller nothing it did not have.
44
+ *
45
+ * This is what lets code that *does* know one ecosystem check before acting on a package -
46
+ * `if (pkg.platform === 'node')` - which matters most in a repository holding more than one,
47
+ * since a manifest is read per directory and two packages can legitimately answer to different
48
+ * technologies. It is also the de-duplication key: registering twice under one name is refused,
49
+ * because it would define the same commands twice and yargs does not survive that.
50
+ */
51
+ name: string;
52
+ /** Where this technology's packages keep their identity, and how to change it - reading and
53
+ * writing the manifest, what it declares as dependencies, how its versions are numbered,
54
+ * stamped, and looked up on a registry. */
55
+ manifestProvider: ManifestProvider;
56
+ /** Where this technology's packages are, given the repository root - npm reads `workspaces`,
57
+ * Cargo a `[workspace] members`. `undefined` when it does not recognize the root. */
58
+ getWorkspace?: Workspace.Provider;
59
+ /** Directories to put in front of a child process's PATH, so a command an author wrote
60
+ * (`eslint .`) runs the repository's pinned copy - `node_modules/.bin` walked up the tree, for
61
+ * npm. Every plugin contributes, unlike the seams above where the first answer wins: a PATH is
62
+ * a list, and a polyglot repository wants both ecosystems' binaries reachable. */
63
+ getBinPaths?: BinPath.Provider;
64
+ /**
65
+ * What the *package itself* declares for a lifecycle script, from this technology's own files -
66
+ * npm's `package.json` `scripts`, with `pre<script>`/`<script>`/`post<script>`.
67
+ *
68
+ * **A query, not a hook, and not build-specific** - which is why it is `getRunSteps` rather than
69
+ * anything beginning `on`. It returns what the package declares and rman decides what to do with
70
+ * it; `run`/`build`/`test` ask, and so does `version`, whose `preversion`/`version`/`postversion`
71
+ * are the same shape (`runLifecycleSlot` -> `contributedSlots`). A name mentioning `build` would
72
+ * have been wrong about half its callers.
73
+ */
74
+ getRunSteps?: RunService.StepSource;
75
+ /** How this technology's releases are planned - where a package's change boundary comes from
76
+ * when it has no release tag, and how far into its group a bump reaches. */
77
+ versionPlanner?: VersionPlanService;
78
+ }
79
+ /**
80
+ * **A plugin is whatever a package contributes; a platform is one of the things it can contribute.**
81
+ *
82
+ * The broad half of the split. `Platform` is a technology and nothing else - required manifest
83
+ * reader, workspace layout, bin paths, steps, version planning. What is left over is this: an
84
+ * `init` for whatever the seams do not name yet, and the **declaration** that this plugin provides
85
+ * platforms.
86
+ *
87
+ * **`platforms` is a list, and that is not symmetry.** One package can speak for more than one
88
+ * technology - `maven` and `gradle` are the same toolchain family and a single plugin shipping both
89
+ * is the natural shape. A singular field would have made the second one a separate package for no
90
+ * reason.
91
+ *
92
+ * A bare `Platform` is accepted anywhere a `Plugin` is, as sugar for `{ name, platforms: [it] }` -
93
+ * which is what almost every entry is.
94
+ */
95
+ export interface Plugin {
96
+ /** How this plugin is named in messages, and the de-duplication key: registering twice under one
97
+ * name is refused. */
98
+ name: string;
99
+ /**
100
+ * Run once the application exists and this plugin is on it, **before anything has been
101
+ * discovered** - no packages, no configs, no repository.
102
+ *
103
+ * This is where a plugin registers what the config keys have no word for: a service, a seam the
104
+ * core does not name yet. Reached through `ctx.app`.
105
+ */
106
+ afterInitApplication?(ctx: PluginContext): void | Promise<void>;
107
+ /**
108
+ * Run once the repository is **resolved** - every package found, every config baked, the
109
+ * dependency graph linked.
110
+ *
111
+ * The stage `afterInitApplication` cannot reach: that one runs inside `Repository.create` before
112
+ * any package is known, so `ctx.app.repository` throws there.
113
+ */
114
+ afterInitRepository?(ctx: PluginRepositoryContext): void | Promise<void>;
115
+ }
116
+ export declare namespace Plugin {
117
+ /** What a value was declared as - see {@link declaredKind}. */
118
+ type DeclaredKind = 'platform' | 'plugin';
119
+ }
120
+ /**
121
+ * What {@link Plugin.afterInitApplication} is handed.
122
+ *
123
+ * An object rather than a bare parameter so a member added later breaks nothing already written
124
+ * against it - the same reason `CommandContext` is one, which has already paid for itself twice.
125
+ */
126
+ export interface PluginContext {
127
+ app: RmanApplication;
128
+ }
129
+ /** What {@link Plugin.afterInitRepository} is handed - the application, plus the repository that now
130
+ * exists. */
131
+ export interface PluginRepositoryContext extends PluginContext {
132
+ repository: Repository;
133
+ }
134
+ /**
135
+ * **What `value` was declared as**, or `undefined` for anything that was not - a plain object, an
136
+ * arbitrary module export, a config.
137
+ */
138
+ export declare function declaredKind(value: unknown): Plugin.DeclaredKind | undefined;
139
+ /** Whether `value` came from `definePlatform` or `definePlugin`. */
140
+ export declare function isDeclared(value: unknown): boolean;
141
+ /** Declares a **platform** - one technology, whole. Returns it unchanged apart from the mark. */
142
+ export declare function definePlatform(platform: Platform): Platform;
143
+ /** Declares a **plugin** - a name and whatever it wants to do at one of the two stages. Returns
144
+ * it unchanged apart from the mark. */
145
+ export declare function definePlugin(plugin: Plugin): Plugin;
146
+ /**
147
+ * Whether `value` is a platform - **what it was declared as**, not what it looks like.
148
+ */
149
+ export declare function isPlatform(value: unknown): value is Platform;
150
+ /** Whether `value` is a plugin - again by declaration, so a platform is not one. */
151
+ export declare function isPlugin(value: unknown): value is Plugin;
152
+ /**
153
+ * The platform a package gets when **none** claimed its directory - a repository naming no
154
+ * platform, or a directory none of the named ones recognized.
155
+ *
156
+ * It exists so `Package.platform` need not be optional. `Package.provider` was an empty string for
157
+ * exactly this case, and every reader had to know that; an object with an empty `name` says the
158
+ * same thing without a guard, and `pkg.provider === 'node'` - the check CLAUDE.md prescribes -
159
+ * reads the same either way.
160
+ *
161
+ * **Every documented behaviour of "no plugin" survives unchanged**, because the absences are the
162
+ * behaviour:
163
+ *
164
+ * - a reader that recognizes nothing -> `Manifest.read` falls through to its own empty manifest,
165
+ * exactly as it does when no plugin answers;
166
+ * - no `getWorkspace` -> a repository naming no plugin has no packages beyond itself, which is the
167
+ * boundary working rather than failing (`workspaces` in a `package.json` is npm's idea);
168
+ * - no `getBinPaths` -> nothing is prepended to a child process's PATH, so the inherited one
169
+ * stands on its own rather than being guessed at;
170
+ * - no `getRunSteps` -> a package's steps come from its `.rmanrc` alone, the core's only source;
171
+ * - no `versionPlanner` -> `version`/`changed` fail naming the key, rather than releasing a
172
+ * plausible but untrue set of packages from a default nobody chose.
173
+ */
174
+ export declare const basePlatform: Platform;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * **What `value` was declared as**, or `undefined` for anything that was not - a plain object, an
3
+ * arbitrary module export, a config.
4
+ */
5
+ /* **One mark carrying the kind, rather than one mark per type.** The mark used to be `true`, so it
6
+ * could answer *whether* something was declared and never *what* - which left `isPlatform` testing
7
+ * for `manifestProvider`, a structural guess, and left the loader unable to say the one useful
8
+ * thing about the commonest mistake: that a platform was put in `plugins`.
9
+ *
10
+ * A symbol per type was the alternative and this is the same information for less machinery. The
11
+ * key has to be a **hoisted function** (see `declaredKey`), which is a trap every additional key
12
+ * would repeat; `isDeclared` stays one lookup instead of an OR someone has to remember to extend;
13
+ * and a third declarable slots in as a new string - `publishTargets` is checked structurally today
14
+ * (`typeof target.name === 'string'`) and is the obvious next one, though nothing is being built
15
+ * for it here.
16
+ *
17
+ * **The value was `true` before this**, and `Symbol.for` is shared - so an object marked by an
18
+ * older copy of rman in the same process now reads as undeclared. 2.0 is unreleased and the mark
19
+ * is 2.x's own, so there is nothing in the field to be compatible with. */
20
+ export function declaredKind(value) {
21
+ if (!value || typeof value !== 'object')
22
+ return undefined;
23
+ const kind = value[declaredKey()];
24
+ return kind === 'platform' || kind === 'plugin' ? kind : undefined;
25
+ }
26
+ /** Whether `value` came from `definePlatform` or `definePlugin`. */
27
+ export function isDeclared(value) {
28
+ return declaredKind(value) !== undefined;
29
+ }
30
+ /** Declares a **platform** - one technology, whole. Returns it unchanged apart from the mark. */
31
+ export function definePlatform(platform) {
32
+ return brand(platform, 'platform');
33
+ }
34
+ /** Declares a **plugin** - a name and whatever it wants to do at one of the two stages. Returns
35
+ * it unchanged apart from the mark. */
36
+ export function definePlugin(plugin) {
37
+ return brand(plugin, 'plugin');
38
+ }
39
+ /**
40
+ * Whether `value` is a platform - **what it was declared as**, not what it looks like.
41
+ */
42
+ /* It used to ask whether `manifestProvider` was present, which was the only test available while
43
+ * one type was both halves. As a question about an arbitrary import that is a guess, and it gets
44
+ * the commonest mistake exactly backwards: a config object carrying a `manifestProvider`-shaped key
45
+ * would pass, and a platform someone forgot to declare would be reported as an rman 1.x plugin.
46
+ *
47
+ * The cost, stated: an undeclared platform-shaped object is no longer a platform. Everything
48
+ * arriving through a config has to be declared anyway, and every `registerPlugin` caller in the
49
+ * tree passes a `definePlatform(...)` result. */
50
+ export function isPlatform(value) {
51
+ return declaredKind(value) === 'platform';
52
+ }
53
+ /** Whether `value` is a plugin - again by declaration, so a platform is not one. */
54
+ export function isPlugin(value) {
55
+ return declaredKind(value) === 'plugin';
56
+ }
57
+ /**
58
+ * The platform a package gets when **none** claimed its directory - a repository naming no
59
+ * platform, or a directory none of the named ones recognized.
60
+ *
61
+ * It exists so `Package.platform` need not be optional. `Package.provider` was an empty string for
62
+ * exactly this case, and every reader had to know that; an object with an empty `name` says the
63
+ * same thing without a guard, and `pkg.provider === 'node'` - the check CLAUDE.md prescribes -
64
+ * reads the same either way.
65
+ *
66
+ * **Every documented behaviour of "no plugin" survives unchanged**, because the absences are the
67
+ * behaviour:
68
+ *
69
+ * - a reader that recognizes nothing -> `Manifest.read` falls through to its own empty manifest,
70
+ * exactly as it does when no plugin answers;
71
+ * - no `getWorkspace` -> a repository naming no plugin has no packages beyond itself, which is the
72
+ * boundary working rather than failing (`workspaces` in a `package.json` is npm's idea);
73
+ * - no `getBinPaths` -> nothing is prepended to a child process's PATH, so the inherited one
74
+ * stands on its own rather than being guessed at;
75
+ * - no `getRunSteps` -> a package's steps come from its `.rmanrc` alone, the core's only source;
76
+ * - no `versionPlanner` -> `version`/`changed` fail naming the key, rather than releasing a
77
+ * plausible but untrue set of packages from a default nobody chose.
78
+ */
79
+ export const basePlatform = definePlatform({
80
+ name: '',
81
+ manifestProvider: {
82
+ name: '',
83
+ fileName: '',
84
+ /** Recognizes nothing, which is the point: `Manifest.read` falls through to its own empty
85
+ * manifest exactly as it does when no plugin answers. */
86
+ read: () => undefined,
87
+ write: () => undefined,
88
+ },
89
+ });
90
+ /**
91
+ * **Marks an object as declared through one of the factories below**, so `loadPlugins` can tell a
92
+ * 2.x contribution from anything else that happens to have the same shape.
93
+ *
94
+ * It exists for one measured case, and there is no structural check that could replace it: an rman
95
+ * **1.x plugin was `{ name, init }`**, and under this split a 2.x plugin that only runs an `init`
96
+ * is *also* `{ name, init }`. They are indistinguishable by shape. The guard used to be
97
+ * `manifestProvider` being required - which only worked while the one type was both halves.
98
+ *
99
+ * Non-enumerable, so it never reaches `JSON.stringify`, `rman config` or a `toEqual` diff, the same
100
+ * way `ORIGINS` and `PREVIOUS_VALUES` travel.
101
+ *
102
+ * The cost, stated rather than hidden: a plain object literal in `plugins` is no longer accepted.
103
+ * `definePlatform`/`definePlugin` is one import and one call, and it is the explicit declaration
104
+ * that a runtime check needs - a type cannot reach a JavaScript config.
105
+ *
106
+ * **A function rather than a `const`, and that is not a preference.** The file layout puts private
107
+ * declarations below the exported ones, and `basePlatform` is an exported const whose initializer
108
+ * *calls* `definePlatform` - so a `const DECLARED` below it sits in its temporal dead zone while the
109
+ * module is still evaluating. Measured: the whole suite failed to load with
110
+ * `ReferenceError: Cannot access 'DECLARED' before initialization`. A function declaration hoists,
111
+ * so the key is resolved when it is asked for instead of when the module reaches this line.
112
+ *
113
+ * **`Symbol.for`, so two copies of rman in one process agree about the mark.** A per-module symbol
114
+ * would have a plugin declared against one copy refused by the other - with a message about rman
115
+ * 1.x plugins, which is the one explanation guaranteed to be wrong.
116
+ */
117
+ function declaredKey() {
118
+ return Symbol.for('rman.declared');
119
+ }
120
+ /** Stamps the mark, non-enumerably, and hands the object back. */
121
+ function brand(value, kind) {
122
+ Object.defineProperty(value, declaredKey(), { value: kind, enumerable: false, configurable: true });
123
+ return value;
124
+ }