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
@@ -1,35 +1,10 @@
1
1
  import { Package } from '../core/package.js';
2
- import { Repository } from '../core/repository.js';
3
- export declare namespace GithubReleaseService {
4
- /** Injectable "does this release already exist" check - mainly for tests, so they don't depend
5
- * on network access or a real GitHub token. Same shape as `DockerPublishService.Deps`' own
6
- * `imageExists`. */
7
- interface Deps {
8
- releaseExists?: (repository: string, tag: string) => Promise<boolean>;
9
- }
10
- interface Options {
11
- /** Uncommitted local changes anywhere in the repository make the release `'skip'` instead of
12
- * `'error'` - same as `version`/`publish`'s other targets. */
13
- ignoreDirty?: boolean;
14
- /** `owner/repo` override - otherwise the root's own `githubRelease.repository`, falling back
15
- * to the `origin` remote's URL. */
16
- repository?: string;
17
- }
18
- /** The repository's release outcome - see `getPlan`. At most one of these: a GitHub Release
19
- * belongs to the repository, not to a package. */
20
- interface Entry {
21
- /** Always the repository root - a release covers the whole source tree, not one package. */
22
- package: Package;
23
- /** The repository's own release version (see `buildRootEntry`). */
24
- version: string;
25
- status: 'publish' | 'skip' | 'up-to-date' | 'error';
26
- /** The tag this release belongs to - the repository release tag on a calendar version, the
27
- * single shared version's tag otherwise. */
28
- tag?: string;
29
- /** `owner/repo` this release lands in - unset only when it couldn't be resolved (an `'error'`). */
30
- repository?: string;
31
- reason?: string;
32
- }
2
+ import { Service } from '../core/service.js';
3
+ /**
4
+ * A service class - see `ListService` for the shape and `Service` for the three measured
5
+ * consequences a namespace had. `repository` left the signature because the application carries it.
6
+ */
7
+ export declare class GithubReleaseService extends Service {
33
8
  /**
34
9
  * Computes what `github-release` *would* do - **one** release per run.
35
10
  *
@@ -51,7 +26,7 @@ export declare namespace GithubReleaseService {
51
26
  * such release", is `'error'` too - a blocking misconfiguration rather than a silent "not
52
27
  * released yet" that only fails later.
53
28
  */
54
- function getPlan(repository: Repository, options?: Options, deps?: Deps): Promise<Entry[]>;
29
+ getPlan(options?: GithubReleaseService.Options, deps?: GithubReleaseService.Deps): Promise<GithubReleaseService.Entry[]>;
55
30
  /**
56
31
  * Creates the repository's GitHub Release, then uploads whatever `githubRelease.assets` globs
57
32
  * match, across every package. The body covers **every** package that shipped under this
@@ -65,5 +40,41 @@ export declare namespace GithubReleaseService {
65
40
  * An existing release for the tag is updated rather than treated as a failure, so a re-run after
66
41
  * a partial failure converges.
67
42
  */
68
- function applyPlan(repository: Repository, plan: Entry[]): Promise<Entry[]>;
43
+ applyPlan(plan: GithubReleaseService.Entry[]): Promise<GithubReleaseService.Entry[]>;
44
+ }
45
+ export declare namespace GithubReleaseService {
46
+ /** Injectable "does this release already exist" check - mainly for tests, so they don't depend
47
+ * on network access or a real GitHub token. Same shape as `DockerPublishService.Deps`' own
48
+ * `imageExists`. */
49
+ interface Deps {
50
+ releaseExists?: (repository: string, tag: string) => Promise<boolean>;
51
+ }
52
+ interface Options {
53
+ /** Uncommitted local changes anywhere in the repository make the release `'skip'` instead of
54
+ * `'error'` - same as `version`/`publish`'s other targets. */
55
+ ignoreDirty?: boolean;
56
+ /** `owner/repo` override - otherwise the root's own `githubRelease.repository`, falling back
57
+ * to the `origin` remote's URL. */
58
+ repository?: string;
59
+ }
60
+ /** The repository's release outcome - see `getPlan`. At most one of these: a GitHub Release
61
+ * belongs to the repository, not to a package. */
62
+ interface Entry {
63
+ /** Always the repository root - a release covers the whole source tree, not one package. */
64
+ package: Package;
65
+ /** The repository's own release version (see `buildRootEntry`). */
66
+ version: string;
67
+ status: 'publish' | 'skip' | 'up-to-date' | 'error';
68
+ /** The tag this release belongs to - the repository release tag on a calendar version, the
69
+ * single shared version's tag otherwise. */
70
+ tag?: string;
71
+ /** `owner/repo` this release lands in - unset only when it couldn't be resolved (an `'error'`). */
72
+ repository?: string;
73
+ reason?: string;
74
+ }
75
+ }
76
+ declare module '../core/service.js' {
77
+ interface ServiceMap {
78
+ githubRelease: GithubReleaseService;
79
+ }
69
80
  }
@@ -3,12 +3,15 @@ import path from 'node:path';
3
3
  import fastGlob from 'fast-glob';
4
4
  import { Package } from '../core/package.js';
5
5
  import { Repository } from '../core/repository.js';
6
+ import { Service } from '../core/service.js';
6
7
  import { GitHelper } from '../utils/git.js';
7
8
  import { expandReleaseTag, isCalendarVersion, releaseTagPattern } from '../utils/release-version.js';
8
9
  import { ChangeHashService } from './change-hash.service.js';
9
- import { ChangelogService } from './changelog.service.js';
10
- export var GithubReleaseService;
11
- (function (GithubReleaseService) {
10
+ /**
11
+ * A service class - see `ListService` for the shape and `Service` for the three measured
12
+ * consequences a namespace had. `repository` left the signature because the application carries it.
13
+ */
14
+ export class GithubReleaseService extends Service {
12
15
  /**
13
16
  * Computes what `github-release` *would* do - **one** release per run.
14
17
  *
@@ -30,7 +33,8 @@ export var GithubReleaseService;
30
33
  * such release", is `'error'` too - a blocking misconfiguration rather than a silent "not
31
34
  * released yet" that only fails later.
32
35
  */
33
- async function getPlan(repository, options = {}, deps = {}) {
36
+ async getPlan(options = {}, deps = {}) {
37
+ const repository = this.repository;
34
38
  const root = repository.rootPackage;
35
39
  const git = new GitHelper({ cwd: repository.dirname });
36
40
  const base = { package: root, version: root.version };
@@ -88,7 +92,6 @@ export var GithubReleaseService;
88
92
  return [{ ...base, tag, repository: repo, status: 'error', reason: e.message }];
89
93
  }
90
94
  }
91
- GithubReleaseService.getPlan = getPlan;
92
95
  /**
93
96
  * Creates the repository's GitHub Release, then uploads whatever `githubRelease.assets` globs
94
97
  * match, across every package. The body covers **every** package that shipped under this
@@ -102,7 +105,8 @@ export var GithubReleaseService;
102
105
  * An existing release for the tag is updated rather than treated as a failure, so a re-run after
103
106
  * a partial failure converges.
104
107
  */
105
- async function applyPlan(repository, plan) {
108
+ async applyPlan(plan) {
109
+ const repository = this.repository;
106
110
  const entry = plan.find(e => e.status === 'publish');
107
111
  if (!entry)
108
112
  return plan;
@@ -122,8 +126,7 @@ export var GithubReleaseService;
122
126
  return plan.map(e2 => (e2 === entry ? { ...e2, status: 'error', reason: e.message } : e2));
123
127
  }
124
128
  }
125
- GithubReleaseService.applyPlan = applyPlan;
126
- })(GithubReleaseService || (GithubReleaseService = {}));
129
+ }
127
130
  const GITHUB_API = 'https://api.github.com';
128
131
  const GITHUB_UPLOADS = 'https://uploads.github.com';
129
132
  /** The tag naming this repository's release. A calendar root version means several version lines,
@@ -198,9 +201,9 @@ async function buildReleaseNotes(repository, git, releaseTag) {
198
201
  return '';
199
202
  const sections = [];
200
203
  for (const pkg of [...repository.getPackages(), root]) {
201
- const entries = await ChangelogService.getEntries(repository, {
204
+ const entries = await repository.app.getService('changelog').getEntries({
202
205
  from: previous,
203
- root: true,
206
+ fromRoot: true,
204
207
  includeSkipped: true,
205
208
  scope: pkg.name,
206
209
  version: pkg.version,
@@ -1,16 +1,9 @@
1
- import type { Repository } from '../core/repository.js';
2
- export declare namespace ImportService {
3
- interface Options {
4
- /** Subdirectory (relative to the repository root) the new package is placed under - default `'packages'`. */
5
- dest?: string;
6
- }
7
- interface Result {
8
- /** The imported package's name (from its own `package.json`, or its directory basename). */
9
- name: string;
10
- /** Where it was placed, absolute. */
11
- targetDir: string;
12
- commitCount: number;
13
- }
1
+ import { Service } from '../core/service.js';
2
+ /**
3
+ * A service class - see `ListService` for the shape and `Service` for the three measured
4
+ * consequences a namespace had. `repository` left the signature because the application carries it.
5
+ */
6
+ export declare class ImportService extends Service {
14
7
  /**
15
8
  * Imports `sourcePath` (a local clone of some other git repository) as a new package under this
16
9
  * repository, preserving its **entire commit history** - every original commit, author, date and
@@ -26,5 +19,23 @@ export declare namespace ImportService {
26
19
  * merge (which real `git subtree` would give, at the cost of far less predictable behavior
27
20
  * across git versions - the reason this - and lerna - avoid it).
28
21
  */
29
- function importRepo(repository: Repository, sourcePath: string, options?: Options): Promise<Result>;
22
+ importRepo(sourcePath: string, options?: ImportService.Options): Promise<ImportService.Result>;
23
+ }
24
+ export declare namespace ImportService {
25
+ interface Options {
26
+ /** Subdirectory (relative to the repository root) the new package is placed under - default `'packages'`. */
27
+ dest?: string;
28
+ }
29
+ interface Result {
30
+ /** The imported package's name (from its own `package.json`, or its directory basename). */
31
+ name: string;
32
+ /** Where it was placed, absolute. */
33
+ targetDir: string;
34
+ commitCount: number;
35
+ }
36
+ }
37
+ declare module '../core/service.js' {
38
+ interface ServiceMap {
39
+ import: ImportService;
40
+ }
30
41
  }
@@ -1,9 +1,13 @@
1
1
  import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
+ import { Service } from '../core/service.js';
4
5
  import { GitHelper } from '../utils/git.js';
5
- export var ImportService;
6
- (function (ImportService) {
6
+ /**
7
+ * A service class - see `ListService` for the shape and `Service` for the three measured
8
+ * consequences a namespace had. `repository` left the signature because the application carries it.
9
+ */
10
+ export class ImportService extends Service {
7
11
  /**
8
12
  * Imports `sourcePath` (a local clone of some other git repository) as a new package under this
9
13
  * repository, preserving its **entire commit history** - every original commit, author, date and
@@ -19,7 +23,8 @@ export var ImportService;
19
23
  * merge (which real `git subtree` would give, at the cost of far less predictable behavior
20
24
  * across git versions - the reason this - and lerna - avoid it).
21
25
  */
22
- async function importRepo(repository, sourcePath, options = {}) {
26
+ async importRepo(sourcePath, options = {}) {
27
+ const repository = this.repository;
23
28
  const absSource = path.resolve(sourcePath);
24
29
  if (!fs.existsSync(path.join(absSource, '.git'))) {
25
30
  throw new Error(`"${sourcePath}" is not a git repository (no .git found) - clone it locally first`);
@@ -48,8 +53,7 @@ export var ImportService;
48
53
  fs.rmSync(patchDir, { recursive: true, force: true });
49
54
  }
50
55
  }
51
- ImportService.importRepo = importRepo;
52
- })(ImportService || (ImportService = {}));
56
+ }
53
57
  /** The source repo's own `package.json` name, or its directory's basename when it has none (or
54
58
  * isn't even a package yet - a plain library repo being folded in). */
55
59
  function resolveSourceName(absSource) {
@@ -1,10 +1,44 @@
1
+ import { type PublishTargetName } from '../core/publish-target.js';
1
2
  import type { Repository } from '../core/repository.js';
2
- import type { RmanConfig } from '../interfaces/rman-config.interface.js';
3
+ import { Service } from '../core/service.js';
4
+ import type { DockerPublishOptions } from '../targets/docker.target.js';
3
5
  import { type PackageFilterOptions } from '../utils/package-filter.js';
6
+ /**
7
+ * **A class, and the shape every service follows now.**
8
+ *
9
+ * `repository` is gone from the signature - it comes from the application, which is what always
10
+ * made the parameter redundant: every caller had one and passed it, and no caller ever had two.
11
+ * What a namespace could not do is hold state without holding it *globally*, be subclassed, or be
12
+ * stubbed without mutating the module; `Service` records the three measured consequences.
13
+ *
14
+ * **Declared before the namespace below, which is not style.** A namespace merges *into* a class
15
+ * only when the class comes first - the other order is
16
+ * `Cannot use namespace 'ListService' as a type`. Merged, `ListService.Options` and
17
+ * `ListService.Item` still read exactly as they did, so nothing importing a type had to change.
18
+ */
19
+ export declare class ListService extends Service {
20
+ /**
21
+ * `list`: every package in the repository (or, with `changed`/`changedSince`, only the ones that
22
+ * have actually changed), each with its version, location, private flag, and change status
23
+ * relative to upstream. Pure data - no console output; `rman list`'s own command decides how to
24
+ * present it (table, JSON, parseable, names only, or a dependency graph).
25
+ */
26
+ getPackages(options?: ListService.Options): Promise<ListService.Item[]>;
27
+ }
4
28
  export declare namespace ListService {
5
29
  interface Options extends PackageFilterOptions {
6
30
  /** Topological order (dependencies before dependents) instead of lexical by directory. */
7
31
  toposort?: boolean;
32
+ /**
33
+ * Also report the **root package**, first, as the row the members' `depth` is measured from.
34
+ *
35
+ * Opt-in rather than always, because it changes what the answer *is*: `repository.packages` is
36
+ * the workspace members, so every other reader of this list - and the count `rman list` prints -
37
+ * is about them. `rman list`'s table asks for it because a tree needs a root to hang from; its
38
+ * `--json`, `--parseable` and `--short` forms do not, and a consumer parsing them sees exactly
39
+ * what it saw before.
40
+ */
41
+ includeRoot?: boolean;
8
42
  /** Only include packages that have changed since the last publish (dirty or committed but
9
43
  * not yet published) - or, with `changedSince`, since that specific commit/hash. */
10
44
  changed?: boolean;
@@ -12,23 +46,41 @@ export declare namespace ListService {
12
46
  }
13
47
  interface Item {
14
48
  name: string;
49
+ /** What addresses this package - `"[glob]"` and `--scope` match it. The same as `name` wherever
50
+ * the technology names its packages, which is every Node repository; see `Package.selector`. */
51
+ selector: string;
15
52
  version: string;
53
+ /** The technology that claimed this package's directory - `Package.provider`. Empty when none
54
+ * did, which is a repository naming no plugin. */
55
+ platform: string;
56
+ /**
57
+ * How far below the **root package** this one sits: `0` for the root itself, `1` for an ordinary
58
+ * member, more for a package nested inside another.
59
+ *
60
+ * A fact about the package rather than about this list's order, so it is still right under
61
+ * `--toposort` - which reorders the rows and leaves the nesting where it is.
62
+ */
63
+ depth: number;
64
+ /** Whether this row *is* the root package - present only when `includeRoot` asked for it, or in
65
+ * a single-package repository where the root is the one member. */
66
+ isRoot: boolean;
16
67
  location: string;
17
68
  private: boolean;
18
69
  status: Repository.PackageStatus;
19
70
  /** In-repo package names this one depends on - enough to build a dependency graph without a
20
71
  * second call, e.g. `Object.fromEntries(items.map(i => [i.name, i.dependencies]))`. */
21
72
  dependencies: string[];
22
- /** This package's own (cascaded) `.rmanrc "publish.target"` - `["npm"]` when unset, same
23
- * default `publish` itself uses. */
24
- publishTargets: RmanConfig.PublishTarget[];
73
+ /** Where this package actually ships - its own (cascaded) `.rmanrc "publish.target"` when it
74
+ * declares one, otherwise every registered target that claims it, which is the same question
75
+ * `publish` asks. Empty in a repository whose plugins contribute no target the package fits. */
76
+ publishTargets: PublishTargetName[];
25
77
  /** Present only when `"docker"` is one of `publishTargets` and `publish.docker` is configured -
26
78
  * the raw `.rmanrc` config, unresolved (no namespace prefixing - see `DockerPublishService`). */
27
- docker?: RmanConfig.DockerPublishOptions;
79
+ docker?: DockerPublishOptions;
80
+ }
81
+ }
82
+ declare module '../core/service.js' {
83
+ interface ServiceMap {
84
+ list: ListService;
28
85
  }
29
- /** `list`: every package in the repository (or, with `changed`/`changedSince`, only the ones
30
- * that have actually changed), each with its version, location, private flag, and change
31
- * status relative to upstream. Pure data - no console output; `rman list`'s own command
32
- * decides how to present it (table, JSON, parseable, names only, or a dependency graph). */
33
- function getPackages(repository: Repository, options?: Options): Promise<Item[]>;
34
86
  }
@@ -1,34 +1,81 @@
1
1
  import path from 'path';
2
+ import { targetsOf } from '../core/publish-target.js';
3
+ import { Service } from '../core/service.js';
2
4
  import { filterPackages } from '../utils/package-filter.js';
3
- export var ListService;
4
- (function (ListService) {
5
- /** `list`: every package in the repository (or, with `changed`/`changedSince`, only the ones
6
- * that have actually changed), each with its version, location, private flag, and change
7
- * status relative to upstream. Pure data - no console output; `rman list`'s own command
8
- * decides how to present it (table, JSON, parseable, names only, or a dependency graph). */
9
- async function getPackages(repository, options = {}) {
5
+ /**
6
+ * **A class, and the shape every service follows now.**
7
+ *
8
+ * `repository` is gone from the signature - it comes from the application, which is what always
9
+ * made the parameter redundant: every caller had one and passed it, and no caller ever had two.
10
+ * What a namespace could not do is hold state without holding it *globally*, be subclassed, or be
11
+ * stubbed without mutating the module; `Service` records the three measured consequences.
12
+ *
13
+ * **Declared before the namespace below, which is not style.** A namespace merges *into* a class
14
+ * only when the class comes first - the other order is
15
+ * `Cannot use namespace 'ListService' as a type`. Merged, `ListService.Options` and
16
+ * `ListService.Item` still read exactly as they did, so nothing importing a type had to change.
17
+ */
18
+ export class ListService extends Service {
19
+ /**
20
+ * `list`: every package in the repository (or, with `changed`/`changedSince`, only the ones that
21
+ * have actually changed), each with its version, location, private flag, and change status
22
+ * relative to upstream. Pure data - no console output; `rman list`'s own command decides how to
23
+ * present it (table, JSON, parseable, names only, or a dependency graph).
24
+ */
25
+ async getPackages(options = {}) {
26
+ const repository = this.repository;
10
27
  /** `false`: `list` is the inventory, so a package its own `.rmanrc "skip"` excludes is still
11
28
  * *in* the repository and still listed - hiding it would answer a different question. Every
12
29
  * other caller honours it, being a command that acts rather than reports. */
13
30
  const packages = filterPackages(repository.getPackages({ toposort: options.toposort }), options, false);
14
- const status = await repository.listStatus({ hash: options.changedSince });
15
- let items = packages.map(p => {
16
- const target = p.config.publish?.target;
17
- const publishTargets = Array.isArray(target) ? target : target ? [target] : ['npm'];
31
+ const status = await repository.listStatus({ hash: options.changedSince, includeRoot: options.includeRoot });
32
+ const item = (p) => {
33
+ /** **Asked, not assumed.** This read the config key directly and defaulted to `['npm']`, so a
34
+ * Cargo package in a polyglot repository was reported as shipping to npm - which is what
35
+ * `PublishTarget.claims` exists to answer, per target, from the ecosystem that knows. */
36
+ const publishTargets = targetsOf(this.app, p).map(t => t.name);
18
37
  return {
19
38
  name: p.name,
39
+ selector: p.selector,
20
40
  version: p.version,
41
+ platform: p.provider,
42
+ depth: depthOf(p),
43
+ isRoot: p === repository.rootPackage,
21
44
  location: path.relative(repository.dirname, p.dirname) || '.',
22
45
  private: p.isPrivate,
23
- status: status[p.name],
46
+ status: status[p.selector],
24
47
  dependencies: p.dependencies.map(d => d.name),
25
48
  publishTargets: [...publishTargets],
26
49
  docker: publishTargets.includes('docker') ? p.config.publish?.docker : undefined,
27
50
  };
28
- });
51
+ };
52
+ let items = packages.map(item);
29
53
  if (options.changed || options.changedSince)
30
54
  items = items.filter(it => it.status !== 'clean');
55
+ /**
56
+ * **The root goes on the front, after filtering, and is never filtered out.** It is the tree's
57
+ * own row - what the members' indentation hangs from - so a `--scope` that narrows the members
58
+ * still leaves it standing rather than removing the thing they are nested under. A glob never
59
+ * matches the root anyway (`ROOT_SELECTOR`), so there is no filter here to respect.
60
+ *
61
+ * Not in a single-package repository, where `repository.packages` already *is* `[rootPackage]`
62
+ * and it would appear twice.
63
+ */
64
+ if (options.includeRoot && repository.monorepo)
65
+ items.unshift(item(repository.rootPackage));
31
66
  return items;
32
67
  }
33
- ListService.getPackages = getPackages;
34
- })(ListService || (ListService = {}));
68
+ }
69
+ /**
70
+ * How far below the root `pkg` sits, by walking `parent` - `0` for the root itself.
71
+ *
72
+ * From the tree edge rather than by counting path segments, which would be a different number: a
73
+ * package in `packages/a` is two directories down and one *package* down, and it is the second that
74
+ * the indentation is about.
75
+ */
76
+ function depthOf(pkg) {
77
+ let depth = 0;
78
+ for (let at = pkg.parent; at; at = at.parent)
79
+ depth++;
80
+ return depth;
81
+ }
@@ -1,8 +1,23 @@
1
1
  import type { Package } from '../core/package.js';
2
2
  import type { Repository } from '../core/repository.js';
3
3
  import type { RunStepContext, RunStepFn, RunStepValue } from '../core/run-step.js';
4
+ import { Service } from '../core/service.js';
4
5
  import { type LogLevel } from '../utils/logger.js';
5
6
  import { type PackageFilterOptions } from '../utils/package-filter.js';
7
+ /**
8
+ * A service class - see `ListService` for the shape and `Service` for the three measured
9
+ * consequences a namespace had.
10
+ *
11
+ * **Only `runScript` became a method**, because only it takes a repository. `getConfig`,
12
+ * `parseIfExpr`, `normalizeScriptValue` and the rest take a `Package` or a raw value and stay
13
+ * functions on the namespace below - the same rule that leaves `ChangeHashService` a namespace
14
+ * entirely. Declared before the namespace, which TypeScript requires for the merge.
15
+ */
16
+ export declare class RunService extends Service {
17
+ runScript(script: string, options?: RunService.Options & {
18
+ commandName?: string;
19
+ }): Promise<void>;
20
+ }
6
21
  export declare namespace RunService {
7
22
  export interface Options extends PackageFilterOptions {
8
23
  /** Max packages built at once: `true`/omitted = CPU count, a number = that many, `false` = serial (1). */
@@ -24,7 +39,7 @@ export declare namespace RunService {
24
39
  * (which otherwise scopes the run to just that package, and skips the root's own pre/post
25
40
  * bookend - see `Repository.currentPackage`). Has no effect when already at the repository
26
41
  * root, or outside any known package. */
27
- root?: boolean;
42
+ fromRoot?: boolean;
28
43
  }
29
44
  /**
30
45
  * A package's `.rmanrc` (cascaded) can configure `run.<script>.*` - e.g.
@@ -89,21 +104,13 @@ export declare namespace RunService {
89
104
  *
90
105
  * The core knows one source: the config. **`package.json#scripts` is not a source the core has**,
91
106
  * because "a script lives in package.json" is true of a Node repository and of nothing else -
92
- * `rman-node` contributes that one (with npm's `pre<script>`/`post<script>` convention and its
107
+ * the `node` built-in contributes that one (with npm's `pre<script>`/`post<script>` convention and its
93
108
  * `&&` splitting), and a plugin for another ecosystem would contribute its own.
94
109
  *
95
110
  * Returns `undefined` for "this package declares nothing", not empty slots - the difference
96
111
  * decides whether the config's value applies.
97
112
  */
98
113
  export type StepSource = (pkg: Package, script: string) => ScriptSlots | undefined;
99
- /**
100
- * Registers a source. Called by `loadPlugins` for each plugin's `runSteps`, in `plugins`
101
- * declaration order - never as an import side effect, so what is registered is exactly what the
102
- * repository's `.rmanrc` asked for.
103
- */
104
- export function addStepSource(source: StepSource): void;
105
- /** For tests, which would otherwise leak a source into every later case in the process. */
106
- export function clearStepSources(): void;
107
114
  /**
108
115
  * What the *package itself* declares for the lifecycle `script`, from the contributed sources
109
116
  * alone - no `.rmanrc` involved. `undefined` when it declares nothing.
@@ -193,9 +200,6 @@ export declare namespace RunService {
193
200
  /** Evaluates a parsed `if` expression for one package. `statusCache` avoids repeat `git` calls
194
201
  * for the same reference hash across packages/scripts in a single run. */
195
202
  export function evaluateIf(repository: Repository, pkg: Package, node: IfNode, statusCache: Map<string, Record<string, Repository.PackageStatus>>): Promise<boolean>;
196
- export function runScript(repository: Repository, script: string, options?: Options & {
197
- commandName?: string;
198
- }): Promise<void>;
199
203
  export {};
200
204
  }
201
205
  /** Resolution order: explicit CLI flag > the package's resolved `.rmanrc` > `fallback`. */
@@ -208,3 +212,8 @@ export declare function resolveBool(cliValue: boolean | undefined, pkg: Package,
208
212
  export declare function resolveBail(cliValue: boolean | undefined, pkg: Package, script: string, fallback: boolean): boolean;
209
213
  export declare function resolveNumber(cliValue: number | undefined, pkg: Package, script: string, key: string, fallback: number): number;
210
214
  export declare function resolveLogLevel(cliValue: LogLevel | undefined, pkg: Package, script: string, fallback: LogLevel): LogLevel;
215
+ declare module '../core/service.js' {
216
+ interface ServiceMap {
217
+ run: RunService;
218
+ }
219
+ }