rman 1.2.5 → 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 +180 -50
  40. package/core/config.js +332 -153
  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 +138 -93
  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 -43
  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 +739 -212
  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
@@ -0,0 +1,59 @@
1
+ import { declareCommand } from '../../../interfaces/rman-config.interface.js';
2
+ import { assertAllowedBranch, branchGuardOptions, readBranchGuardOptions } from '../../../utils/branch-guard.js';
3
+ import { packageFilterOptions, readPackageFilterOptions } from '../../../utils/package-filter.js';
4
+ import { CiService } from '../services/ci.service.js';
5
+ /** Hoisted out of the metadata literal so the handler can be annotated against them - see
6
+ * `RmanConfig.ArgsOf` for why an inferred `argv` and the metadata's own typo checking cannot both
7
+ * work in one signature. */
8
+ const COMMAND = 'ci';
9
+ const config = {
10
+ ...packageFilterOptions,
11
+ ...branchGuardOptions,
12
+ packageManager: {
13
+ target: 'cli',
14
+ cliName: 'package-manager',
15
+ describe: 'Package manager to install with (default: npm, or .rmanrc "packageManager")',
16
+ choices: CiService.PACKAGE_MANAGERS,
17
+ },
18
+ progress: {
19
+ target: 'cli',
20
+ describe: 'Show a live progress panel while running (default: true; auto-disabled when not a TTY). ' +
21
+ 'Unlike run/build, completion is not reported as a per-package tally - only failures are called out.',
22
+ type: 'boolean',
23
+ },
24
+ };
25
+ /**
26
+ * `rman ci` - contributed by the `node` built-in rather than always present.
27
+ *
28
+ * **Declared, not built.** This was a `CustomCommand` with a hand-written `builder` chaining
29
+ * `applyBranchGuardOptions(applyPackageFilterOptions(cmd))`, which is the shape every built-in had
30
+ * before the options became data. A plugin's command uses the identical declaration now -
31
+ * `declareCommand` rather than `registerCommand`, which is the one difference and the reason for
32
+ * it: `registerCommand` pushes onto a module-level registry that `runCli` always walks, so a plugin
33
+ * using it would hand `ci` to repositories that are not Node ones.
34
+ *
35
+ * The repository arrives through `app` when the factory runs (in `cli.ts`, after `Repository.create`
36
+ * has attached one), rather than through a `CommandContext` per invocation.
37
+ */
38
+ const ciCommand = declareCommand(app => {
39
+ const repository = app.repository;
40
+ return {
41
+ command: COMMAND,
42
+ describe: 'Deletes node_modules and lockfiles in every package, then reinstalls from scratch',
43
+ /** Read, not owned: `packageManager` is a root-level key this package declares in
44
+ * `NodeConfigKeys`, and the `npm` publish target reads it too. */
45
+ configKeys: ['packageManager'],
46
+ config,
47
+ examples: [{ command: '$0 ci' }],
48
+ handler: async (args) => {
49
+ await assertAllowedBranch(repository, readBranchGuardOptions(args));
50
+ await CiService.reinstall(repository, {
51
+ ...readPackageFilterOptions(args),
52
+ packageManager: args.packageManager,
53
+ progress: args.progress,
54
+ logLevel: args.logLevel,
55
+ });
56
+ },
57
+ };
58
+ });
59
+ export default ciCommand;
@@ -0,0 +1,183 @@
1
+ import type { ConfigValue } from '../../../core/config.js';
2
+ import type { ArgsOf } from '../../../index.js';
3
+ /** Hoisted for `ArgsOf` - see `ci.command.ts` and `RmanConfig.ArgsOf`. */
4
+ declare const COMMAND: "clean";
5
+ declare const config: {
6
+ progress: {
7
+ target: "cli";
8
+ describe: string;
9
+ type: "boolean";
10
+ };
11
+ dryRun: {
12
+ target: "cli";
13
+ cliName: string;
14
+ describe: string;
15
+ type: "boolean";
16
+ };
17
+ /** Config-only: `.rmanrc "clean.skip"` excludes a package from `clean` entirely. Not a flag -
18
+ * a single run narrows with `--scope`/`--ignore`, which every command already has. */
19
+ skip: {
20
+ target: "config";
21
+ describe: string;
22
+ type: "boolean";
23
+ };
24
+ fromRoot: {
25
+ target: "cli";
26
+ cliName: string;
27
+ alias: string;
28
+ describe: string;
29
+ type: "boolean";
30
+ };
31
+ allowBranch: {
32
+ target: "cli";
33
+ cliName: string;
34
+ describe: string;
35
+ type: "string";
36
+ };
37
+ ignoreBranch: {
38
+ target: "cli";
39
+ cliName: string;
40
+ describe: string;
41
+ type: "string";
42
+ };
43
+ scope: {
44
+ target: "cli";
45
+ describe: string;
46
+ type: "string";
47
+ };
48
+ ignore: {
49
+ target: "cli";
50
+ describe: string;
51
+ type: "string";
52
+ };
53
+ platform: {
54
+ target: "cli";
55
+ describe: string;
56
+ type: "string";
57
+ };
58
+ deps: {
59
+ target: "cli";
60
+ describe: string;
61
+ type: "boolean";
62
+ };
63
+ dependents: {
64
+ target: "cli";
65
+ describe: string;
66
+ type: "boolean";
67
+ };
68
+ };
69
+ /**
70
+ * The rest of `clean.*` - the two keys an option cannot describe.
71
+ *
72
+ * `include`/`exclude` are each **a glob or a list of them**, and a `CommandOption` says one or the
73
+ * other: `type: 'string'` is a string, `array: true` beside it is a `string[]`. Neither is the
74
+ * union, and the union is what a config author actually writes (`include: 'build'` as often as
75
+ * `include: ['build', '*.tsbuildinfo']`). So they are written out and intersected in, which is what
76
+ * `Extra` is for - `skip` above is an ordinary option and is derived like everything else.
77
+ */
78
+ export interface CleanExtraKeys {
79
+ /** Extra files and directories to remove, beyond TypeScript's own output - globs relative to
80
+ * each package's own directory. Per-package cascaded; a package declaring its own `clean` block
81
+ * replaces the root's entirely for itself, rather than combining with it. */
82
+ include?: ConfigValue<string | string[]>;
83
+ /** Globs to keep, applied after `include`. */
84
+ exclude?: ConfigValue<string | string[]>;
85
+ }
86
+ type Args = ArgsOf<typeof config, typeof COMMAND>;
87
+ /**
88
+ * `rman clean` - contributed by the `node` built-in rather than always present, because what
89
+ * it deletes is TypeScript's output. See `CleanService.clean`.
90
+ *
91
+ * Declared the same way `ci` is - see that file for why a plugin uses `declareCommand` rather than
92
+ * `registerCommand`.
93
+ */
94
+ declare const cleanCommand: (app: import("../../../index.js").RmanApplication) => {
95
+ command: "clean";
96
+ describe: string;
97
+ /**
98
+ * **No `configKeys`, because `clean` is this command's *own* key now** - derived from the
99
+ * `config` block above and contributed to `RmanConfig` by `CommandContribution`, exactly as a
100
+ * built-in contributes its own. It used to be hand-written in `NodeConfigKeys` *and* named
101
+ * here, which is two statements about one key.
102
+ *
103
+ * **The contribution itself is in `augmentation/rman.augmentation.ts`, not here**, and that is
104
+ * not a stylistic choice: one `declare module 'rman'` block per package is the limit, and a
105
+ * second silently disables the first. Measured again while moving this - a second block left
106
+ * `SystemInfo.PackageManager` unresolved at four call sites, with nothing pointing at the
107
+ * cause. rman's own commands declare theirs beside themselves because they augment a *module
108
+ * path*, which has no such limit.
109
+ */
110
+ config: {
111
+ progress: {
112
+ target: "cli";
113
+ describe: string;
114
+ type: "boolean";
115
+ };
116
+ dryRun: {
117
+ target: "cli";
118
+ cliName: string;
119
+ describe: string;
120
+ type: "boolean";
121
+ };
122
+ /** Config-only: `.rmanrc "clean.skip"` excludes a package from `clean` entirely. Not a flag -
123
+ * a single run narrows with `--scope`/`--ignore`, which every command already has. */
124
+ skip: {
125
+ target: "config";
126
+ describe: string;
127
+ type: "boolean";
128
+ };
129
+ fromRoot: {
130
+ target: "cli";
131
+ cliName: string;
132
+ alias: string;
133
+ describe: string;
134
+ type: "boolean";
135
+ };
136
+ allowBranch: {
137
+ target: "cli";
138
+ cliName: string;
139
+ describe: string;
140
+ type: "string";
141
+ };
142
+ ignoreBranch: {
143
+ target: "cli";
144
+ cliName: string;
145
+ describe: string;
146
+ type: "string";
147
+ };
148
+ scope: {
149
+ target: "cli";
150
+ describe: string;
151
+ type: "string";
152
+ };
153
+ ignore: {
154
+ target: "cli";
155
+ describe: string;
156
+ type: "string";
157
+ };
158
+ platform: {
159
+ target: "cli";
160
+ describe: string;
161
+ type: "string";
162
+ };
163
+ deps: {
164
+ target: "cli";
165
+ describe: string;
166
+ type: "boolean";
167
+ };
168
+ dependents: {
169
+ target: "cli";
170
+ describe: string;
171
+ type: "boolean";
172
+ };
173
+ };
174
+ examples: ({
175
+ command: string;
176
+ description?: undefined;
177
+ } | {
178
+ command: string;
179
+ description: string;
180
+ })[];
181
+ handler: (args: Args) => Promise<void>;
182
+ };
183
+ export default cleanCommand;
@@ -0,0 +1,73 @@
1
+ import { declareCommand } from '../../../interfaces/rman-config.interface.js';
2
+ import { assertAllowedBranch, branchGuardOptions, readBranchGuardOptions } from '../../../utils/branch-guard.js';
3
+ import { fromRootOption, packageFilterOptions, readPackageFilterOptions } from '../../../utils/package-filter.js';
4
+ import { CleanService } from '../services/clean.service.js';
5
+ /** Hoisted for `ArgsOf` - see `ci.command.ts` and `RmanConfig.ArgsOf`. */
6
+ const COMMAND = 'clean';
7
+ const config = {
8
+ ...packageFilterOptions,
9
+ ...branchGuardOptions,
10
+ /** A group of one, and a function because its text is this command's own word for what it does. */
11
+ ...fromRootOption('Clean'),
12
+ progress: {
13
+ target: 'cli',
14
+ describe: 'Show a live progress panel (default: true; auto-disabled when not a TTY)',
15
+ type: 'boolean',
16
+ },
17
+ dryRun: {
18
+ target: 'cli',
19
+ cliName: 'dry-run',
20
+ describe: 'Report what would be removed without actually removing anything',
21
+ type: 'boolean',
22
+ },
23
+ /** Config-only: `.rmanrc "clean.skip"` excludes a package from `clean` entirely. Not a flag -
24
+ * a single run narrows with `--scope`/`--ignore`, which every command already has. */
25
+ skip: {
26
+ target: 'config',
27
+ describe: 'Excludes this package from clean entirely',
28
+ type: 'boolean',
29
+ },
30
+ };
31
+ /**
32
+ * `rman clean` - contributed by the `node` built-in rather than always present, because what
33
+ * it deletes is TypeScript's output. See `CleanService.clean`.
34
+ *
35
+ * Declared the same way `ci` is - see that file for why a plugin uses `declareCommand` rather than
36
+ * `registerCommand`.
37
+ */
38
+ const cleanCommand = declareCommand(app => {
39
+ const repository = app.repository;
40
+ return {
41
+ command: COMMAND,
42
+ describe: 'Removes compiled TypeScript output and any extra files/dirs configured via .rmanrc "clean"',
43
+ /**
44
+ * **No `configKeys`, because `clean` is this command's *own* key now** - derived from the
45
+ * `config` block above and contributed to `RmanConfig` by `CommandContribution`, exactly as a
46
+ * built-in contributes its own. It used to be hand-written in `NodeConfigKeys` *and* named
47
+ * here, which is two statements about one key.
48
+ *
49
+ * **The contribution itself is in `augmentation/rman.augmentation.ts`, not here**, and that is
50
+ * not a stylistic choice: one `declare module 'rman'` block per package is the limit, and a
51
+ * second silently disables the first. Measured again while moving this - a second block left
52
+ * `SystemInfo.PackageManager` unresolved at four call sites, with nothing pointing at the
53
+ * cause. rman's own commands declare theirs beside themselves because they augment a *module
54
+ * path*, which has no such limit.
55
+ */
56
+ config,
57
+ examples: [
58
+ { command: '$0 clean' },
59
+ { command: '$0 clean --dry-run', description: '# Preview what would be removed' },
60
+ ],
61
+ handler: async (args) => {
62
+ await assertAllowedBranch(repository, readBranchGuardOptions(args));
63
+ await CleanService.clean(repository, {
64
+ ...readPackageFilterOptions(args),
65
+ progress: args.progress,
66
+ dryRun: args.dryRun,
67
+ fromRoot: args.fromRoot,
68
+ logLevel: args.logLevel,
69
+ });
70
+ },
71
+ };
72
+ });
73
+ export default cleanCommand;
@@ -0,0 +1,29 @@
1
+ import './augmentation/rman.augmentation.js';
2
+ import { type Platform } from '../../core/plugin.js';
3
+ import type { RmanConfig } from '../../interfaces/rman-config.interface.js';
4
+ /**
5
+ * **The `node` built-in, as the config it contributes** - its technology, its two commands and its
6
+ * publish target, which is exactly what `extends: 'rman-node'` used to deliver.
7
+ *
8
+ * A *config* rather than a bare `Plugin`, and that distinction is the whole reason `plugins: ['node']`
9
+ * can replace an `extends`: a technology alone would bring the manifest reader and leave `rman clean`
10
+ * an unknown argument.
11
+ *
12
+ * **A function, so nothing here happens until a repository asks for it.** `augmentSystemInfo()`
13
+ * mutates the core's own `SystemInfo` in place, so calling it at import time would have `rman info`
14
+ * report npm's tooling in a Cargo repository that never named this built-in - which is the shape of
15
+ * "bundled" quietly becoming "always on". The type-only augmentation above is imported eagerly
16
+ * because a type costs nothing at runtime and a config author's editor wants it either way.
17
+ */
18
+ /**
19
+ * **The platform itself, for asking.** Detection puts a directory to every built-in's platform -
20
+ * `manifestProvider.read(dir)` is already "is this one of mine?" - and that question must be
21
+ * answerable without turning anything on. Constructed once: a `NodePlatform` holds no state and two
22
+ * of them answering differently is not a thing worth allowing.
23
+ *
24
+ * **Declared, like any other platform.** `loadPlugins` refuses anything that did not come through
25
+ * `definePlatform`/`definePlugin`, and a built-in is registered by the same loader as everything
26
+ * else - the mark is not something being inside rman excuses.
27
+ */
28
+ export declare const nodePlatform: Platform;
29
+ export declare function nodeBuiltin(): RmanConfig;
@@ -0,0 +1,40 @@
1
+ import './augmentation/rman.augmentation.js';
2
+ import { definePlatform } from '../../core/plugin.js';
3
+ import { augmentSystemInfo } from './augmentation/system-info.augmentation.js';
4
+ import ciCommand from './commands/ci.command.js';
5
+ import cleanCommand from './commands/clean.command.js';
6
+ import { NodePlatform } from './node.platform.js';
7
+ import { NpmPublishTarget } from './npm-publish-target.js';
8
+ /**
9
+ * **The `node` built-in, as the config it contributes** - its technology, its two commands and its
10
+ * publish target, which is exactly what `extends: 'rman-node'` used to deliver.
11
+ *
12
+ * A *config* rather than a bare `Plugin`, and that distinction is the whole reason `plugins: ['node']`
13
+ * can replace an `extends`: a technology alone would bring the manifest reader and leave `rman clean`
14
+ * an unknown argument.
15
+ *
16
+ * **A function, so nothing here happens until a repository asks for it.** `augmentSystemInfo()`
17
+ * mutates the core's own `SystemInfo` in place, so calling it at import time would have `rman info`
18
+ * report npm's tooling in a Cargo repository that never named this built-in - which is the shape of
19
+ * "bundled" quietly becoming "always on". The type-only augmentation above is imported eagerly
20
+ * because a type costs nothing at runtime and a config author's editor wants it either way.
21
+ */
22
+ /**
23
+ * **The platform itself, for asking.** Detection puts a directory to every built-in's platform -
24
+ * `manifestProvider.read(dir)` is already "is this one of mine?" - and that question must be
25
+ * answerable without turning anything on. Constructed once: a `NodePlatform` holds no state and two
26
+ * of them answering differently is not a thing worth allowing.
27
+ *
28
+ * **Declared, like any other platform.** `loadPlugins` refuses anything that did not come through
29
+ * `definePlatform`/`definePlugin`, and a built-in is registered by the same loader as everything
30
+ * else - the mark is not something being inside rman excuses.
31
+ */
32
+ export const nodePlatform = definePlatform(new NodePlatform());
33
+ export function nodeBuiltin() {
34
+ augmentSystemInfo();
35
+ return {
36
+ plugins: [nodePlatform],
37
+ commands: [ciCommand, cleanCommand],
38
+ publishTargets: [new NpmPublishTarget()],
39
+ };
40
+ }
@@ -0,0 +1,77 @@
1
+ import type { ConfigValue } from '../../core/config.js';
2
+ import type { RmanConfig, ScopedVars } from '../../interfaces/rman-config.interface.js';
3
+ import type { CiService } from './services/ci.service.js';
4
+ /**
5
+ * The `.rmanrc` keys that only mean something because the repository is a Node one.
6
+ *
7
+ * **One key, and it is here because no single command owns it**: `packageManager` is read by `ci`
8
+ * *and* by the `npm` publish target, so neither can contribute it. Everything else this package
9
+ * adds to `.rmanrc` is declared by whoever reads it - `clean.*` by `clean.command.ts` (through
10
+ * `CommandContribution`), `publish.npm.*` by the target, below.
11
+ *
12
+ * **Two surfaces, and they are not alternatives:**
13
+ *
14
+ * - the `declare module 'rman'` block in [`../augmentation/rman.augmentation.ts`] merges this into
15
+ * `RmanConfigKeys`, so `repository.config.packageManager` stays typed wherever it is read with no
16
+ * cast. It lives there because one such block per package is the limit;
17
+ * - `RmanNodeConfig` is the name a *config author* annotates with, which is what makes the import
18
+ * carrying that augmentation explicit instead of a side effect someone has to remember.
19
+ */
20
+ export interface NodeConfigKeys {
21
+ /**
22
+ * Which package manager `ci`/`publish` shell out to, and whose version `info` reports under
23
+ * `Binaries`. Root level only. Default `npm`.
24
+ *
25
+ * It used to be a core key, on the grounds that `info` read it - and that stopped being true the
26
+ * moment `SystemInfo`'s npm half moved here: measured, **nothing in rman's core reads it at
27
+ * all**, only the declaration was left behind. The value set was npm's tooling the whole time.
28
+ */
29
+ packageManager?: CiService.PackageManager;
30
+ }
31
+ export declare namespace RmanNodeConfig {
32
+ /**
33
+ * **`publish.npm.*`** - the `npm` publish target's own block, contributed through the
34
+ * `PublishTargetConfigs` slot rman's `publish` command exports for any target.
35
+ *
36
+ * Named after the target, the way `publish.docker.*` already was. It used to be `publish.directory`
37
+ * - a bare key sitting directly beside `target`, `skip` and `docker`, so the one block that was
38
+ * *not* a target's read as though it belonged to publishing in general. `target` and `skip` are
39
+ * genuinely `publish`'s own; `directory` never was.
40
+ */
41
+ interface NpmPublishOptions extends NpmPublishOptionsKeys, ScopedVars {
42
+ }
43
+ interface NpmPublishOptionsKeys {
44
+ /** Where this package's publishable output lives, relative to its own directory (e.g.
45
+ * `"build"`). Per-package cascaded, so a root `"[*]"` block can say it once for the whole
46
+ * repository instead of repeating `publishConfig.directory` in every `package.json` - which
47
+ * still wins when a package declares it, being the more specific statement.
48
+ *
49
+ * Publishing from such a directory means the manifest there is **generated by `publish`**,
50
+ * from the package's own - see `PublishService`. There is nothing to configure about it. */
51
+ directory?: ConfigValue<string>;
52
+ }
53
+ }
54
+ /**
55
+ * `.rmanrc` for a repository using this plugin: rman's own keys plus the ones above.
56
+ *
57
+ * ```js
58
+ * // .rmanrc.mjs
59
+ * import { defineConfig } from 'rman';
60
+ *
61
+ * export default defineConfig({
62
+ * plugins: ['node'],
63
+ * '[*]': { clean: { include: 'build' }, publish: { npm: { directory: 'build' } } },
64
+ * });
65
+ * ```
66
+ *
67
+ * `.mjs`, not `.ts`: rman loads `.rmanrc.cjs`/`.mjs`/`.js` and no TypeScript form, so the type
68
+ * reaches a config file through the editor rather than through a compiler. The JSON and YAML forms
69
+ * carry no type at all - since the JSON Schema was removed, they are unchecked.
70
+ */
71
+ export interface RmanNodeConfig extends RmanConfig {
72
+ }
73
+ /**
74
+ * Identity helper for authoring a `.rmanrc.mjs`/`.cjs`/`.js` config with this plugin's keys checked
75
+ * - rman's own `defineConfig` with a narrower parameter, nothing more. Returns `config` unchanged.
76
+ */
77
+ export declare function defineConfig(config: RmanNodeConfig): RmanNodeConfig;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Identity helper for authoring a `.rmanrc.mjs`/`.cjs`/`.js` config with this plugin's keys checked
3
+ * - rman's own `defineConfig` with a narrower parameter, nothing more. Returns `config` unchanged.
4
+ */
5
+ export function defineConfig(config) {
6
+ return config;
7
+ }
@@ -0,0 +1,68 @@
1
+ import type { ManifestProvider } from '../../core/manifest.js';
2
+ import { Manifest } from '../../core/manifest.js';
3
+ import type { Package } from '../../core/package.js';
4
+ export declare class NodeManifestProvider implements ManifestProvider {
5
+ name: string;
6
+ fileName: string;
7
+ read(dir: string): Manifest | undefined;
8
+ /**
9
+ * npm's four dependency fields, and only the entries naming a package of this repository -
10
+ * an external dependency is not an edge in rman's graph.
11
+ *
12
+ * Matched by name, which is npm's own identifier and unique by construction; an ecosystem where
13
+ * that does not hold resolves its own way, which is why this is the provider's job.
14
+ */
15
+ dependencies(manifest: Manifest, candidates: readonly Package[]): Package[];
16
+ /** npm's `@scope/name`. A name with no `/` has no scope and is its own unscoped form; the
17
+ * *last* `/` splits it, so `@scope/a/b` keeps `@scope/a` as the scope the registry would. */
18
+ splitName(name: string): {
19
+ scope?: string;
20
+ unscopedName: string;
21
+ };
22
+ /**
23
+ * Rewrites a sibling's range in all four fields after it was bumped.
24
+ *
25
+ * **A bare `"workspace:*"`/`"^"`/`"~"` selector is left alone**, and that is the load-bearing
26
+ * case: it resolves to the dependency's *current* version at publish time (see
27
+ * `resolveWorkspaceRange`), so rewriting it would replace a live reference with a frozen one.
28
+ * Only an explicit version after `workspace:` needs bumping, like a plain range.
29
+ */
30
+ updateDependencyVersions(manifest: Manifest, bumped: ReadonlyMap<Package, string>): void;
31
+ /**
32
+ * What the npm registry says this package's current version is - used *only* by
33
+ * `detectChangeHash`, to guess a tag name for a package that has no git tag yet. See
34
+ * `Plugin.publishedVersion` for why that is not a "has this been published" check.
35
+ *
36
+ * No `--registry`/`--userconfig`: a bare `npm view` run in the package's own directory already
37
+ * honours the repository's `.npmrc`, and the flags exist for `publish`'s CLI overrides, which
38
+ * this path has none of.
39
+ */
40
+ publishedVersion(pkg: Package): Promise<string | undefined>;
41
+ /**
42
+ * npm's source shape: a quoted string assigned to an identifier, which is what a `.ts`/`.js`
43
+ * version constant looks like. `constant` comes from the repository's own `version.stamp` entry,
44
+ * for a file whose identifier is not literally `version`.
45
+ *
46
+ * One line, because `stampVersionConstant` is exported for it - the *pattern* is shared by most
47
+ * languages, while *choosing* it is this ecosystem's call. A provider for a format with no
48
+ * identifier at all (a `pom.xml`) would ignore `constant` and match its own way.
49
+ */
50
+ stampVersion(file: string, content: string, version: string, options?: {
51
+ constant?: string;
52
+ }): string | undefined;
53
+ write(dir: string, manifest: Manifest): void;
54
+ }
55
+ /**
56
+ * `package.json` as rman's manifest: where an npm package's name and version are written.
57
+ *
58
+ * This is the deepest npm assumption that used to be in rman's core - `Package.name`,
59
+ * `Package.version`, `Package.json` and `Package.isPrivate` all read this file directly, which
60
+ * meant every command that asked a package what it was called was asking npm.
61
+ *
62
+ * The version scheme is left unset, so packages get the core's semver default: npm versions *are*
63
+ * semver, and saying so again here would only be a second place for the two to disagree.
64
+ */
65
+ export declare const packageJsonManifest: {};
66
+ /** npm's four dependency fields. Was `rman`'s `DEPENDENCY_KEYS`, which made the core carry npm's
67
+ * field names - `publish` imports it from here now. */
68
+ export declare const DEPENDENCY_KEYS: readonly ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
@@ -0,0 +1,125 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { Manifest } from '../../core/manifest.js';
4
+ import { stampVersionConstant } from '../../utils/version-stamp.js';
5
+ import { npmViewVersion } from './utils/npm-view.js';
6
+ import { parseWorkspaceRange } from './utils/workspace-range.js';
7
+ export class NodeManifestProvider {
8
+ name = 'node';
9
+ fileName = 'package.json';
10
+ read(dir) {
11
+ const file = path.join(dir, 'package.json');
12
+ if (!fs.existsSync(file))
13
+ return undefined;
14
+ const raw = JSON.parse(fs.readFileSync(file, 'utf-8'));
15
+ return {
16
+ /** A `package.json` with no `name` is unusual but legal, and `info` prints such a package
17
+ * rather than refusing it - so the directory name stands in, as it does for a package with
18
+ * no manifest at all. */
19
+ name: typeof raw?.name === 'string' && raw.name ? raw.name : path.basename(dir),
20
+ version: typeof raw?.version === 'string' && raw.version ? raw.version : '0.0.0',
21
+ private: !!raw?.private,
22
+ raw: raw ?? {},
23
+ };
24
+ }
25
+ /**
26
+ * npm's four dependency fields, and only the entries naming a package of this repository -
27
+ * an external dependency is not an edge in rman's graph.
28
+ *
29
+ * Matched by name, which is npm's own identifier and unique by construction; an ecosystem where
30
+ * that does not hold resolves its own way, which is why this is the provider's job.
31
+ */
32
+ dependencies(manifest, candidates) {
33
+ const declared = Object.assign({}, ...DEPENDENCY_KEYS.map(key => manifest.raw[key]));
34
+ const byName = new Map(candidates.map(p => [p.name, p]));
35
+ const result = [];
36
+ for (const name of Object.keys(declared)) {
37
+ const pkg = byName.get(name);
38
+ if (pkg && !result.includes(pkg))
39
+ result.push(pkg);
40
+ }
41
+ return result;
42
+ }
43
+ /** npm's `@scope/name`. A name with no `/` has no scope and is its own unscoped form; the
44
+ * *last* `/` splits it, so `@scope/a/b` keeps `@scope/a` as the scope the registry would. */
45
+ splitName(name) {
46
+ const at = name.lastIndexOf('/');
47
+ return at > 0 ? { scope: name.slice(0, at), unscopedName: name.slice(at + 1) } : { unscopedName: name };
48
+ }
49
+ /**
50
+ * Rewrites a sibling's range in all four fields after it was bumped.
51
+ *
52
+ * **A bare `"workspace:*"`/`"^"`/`"~"` selector is left alone**, and that is the load-bearing
53
+ * case: it resolves to the dependency's *current* version at publish time (see
54
+ * `resolveWorkspaceRange`), so rewriting it would replace a live reference with a frozen one.
55
+ * Only an explicit version after `workspace:` needs bumping, like a plain range.
56
+ */
57
+ updateDependencyVersions(manifest, bumped) {
58
+ const versionByName = new Map([...bumped].map(([pkg, version]) => [pkg.name, version]));
59
+ for (const depKey of DEPENDENCY_KEYS) {
60
+ const deps = manifest.raw[depKey];
61
+ if (!deps)
62
+ continue;
63
+ for (const depName of Object.keys(deps)) {
64
+ const to = versionByName.get(depName);
65
+ if (!to)
66
+ continue;
67
+ const workspace = parseWorkspaceRange(deps[depName]);
68
+ if (workspace) {
69
+ if (workspace.selector === 'explicit')
70
+ deps[depName] = `workspace:^${to}`;
71
+ continue;
72
+ }
73
+ deps[depName] = '^' + to;
74
+ }
75
+ }
76
+ }
77
+ /**
78
+ * What the npm registry says this package's current version is - used *only* by
79
+ * `detectChangeHash`, to guess a tag name for a package that has no git tag yet. See
80
+ * `Plugin.publishedVersion` for why that is not a "has this been published" check.
81
+ *
82
+ * No `--registry`/`--userconfig`: a bare `npm view` run in the package's own directory already
83
+ * honours the repository's `.npmrc`, and the flags exist for `publish`'s CLI overrides, which
84
+ * this path has none of.
85
+ */
86
+ publishedVersion(pkg) {
87
+ return npmViewVersion(pkg.name, pkg.dirname);
88
+ }
89
+ /**
90
+ * npm's source shape: a quoted string assigned to an identifier, which is what a `.ts`/`.js`
91
+ * version constant looks like. `constant` comes from the repository's own `version.stamp` entry,
92
+ * for a file whose identifier is not literally `version`.
93
+ *
94
+ * One line, because `stampVersionConstant` is exported for it - the *pattern* is shared by most
95
+ * languages, while *choosing* it is this ecosystem's call. A provider for a format with no
96
+ * identifier at all (a `pom.xml`) would ignore `constant` and match its own way.
97
+ */
98
+ stampVersion(file, content, version, options) {
99
+ return stampVersionConstant(content, version, options?.constant);
100
+ }
101
+ write(dir, manifest) {
102
+ /**
103
+ * The version is written back into `raw` before serializing, because `raw` is the document and
104
+ * `manifest.version` is rman's view of one field in it. Writing only the view would produce a
105
+ * file whose `version` never changed; writing only `raw` would leave the two disagreeing for
106
+ * anything still holding the manifest.
107
+ */
108
+ const raw = { ...manifest.raw, version: manifest.version };
109
+ fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(raw, undefined, 2) + '\n', 'utf-8');
110
+ }
111
+ }
112
+ /**
113
+ * `package.json` as rman's manifest: where an npm package's name and version are written.
114
+ *
115
+ * This is the deepest npm assumption that used to be in rman's core - `Package.name`,
116
+ * `Package.version`, `Package.json` and `Package.isPrivate` all read this file directly, which
117
+ * meant every command that asked a package what it was called was asking npm.
118
+ *
119
+ * The version scheme is left unset, so packages get the core's semver default: npm versions *are*
120
+ * semver, and saying so again here would only be a second place for the two to disagree.
121
+ */
122
+ export const packageJsonManifest = {};
123
+ /** npm's four dependency fields. Was `rman`'s `DEPENDENCY_KEYS`, which made the core carry npm's
124
+ * field names - `publish` imports it from here now. */
125
+ export const DEPENDENCY_KEYS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];