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
@@ -0,0 +1,134 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import parseNpmScript from '@netlify/parse-npm-script';
4
+ import glob from 'fast-glob';
5
+ import { NodeManifestProvider } from './node-manifest.provider.js';
6
+ import { NodeVersionPlanService } from './services/version-plan.service.js';
7
+ /** Stands in for a script the package does not declare. `#` is a shell comment, so were it ever to
8
+ * reach a shell it would do nothing - but it is filtered out above instead. */
9
+ const PLACEHOLDER = '#';
10
+ export class NodePlatform {
11
+ /** The ecosystem, not the file - this is what every package it reads reports as
12
+ * `pkg.provider === 'node'`. `manifestProvider.fileName` already says `package.json`. */
13
+ name = 'node';
14
+ manifestProvider = new NodeManifestProvider();
15
+ versionPlanner = new NodeVersionPlanService();
16
+ /**
17
+ * npm's `workspaces` globs, resolved against `dir` - the directories directly below it that hold
18
+ * a `package.json`.
19
+ *
20
+ * **Asked of every directory the walk reaches, not only of the repository root**, which is what
21
+ * the seam changing from a layout to a per-directory question bought: a `package.json` naming
22
+ * `workspaces` is a workspace root wherever it sits, so a Node sub-workspace inside a larger
23
+ * repository is now found by the same code that finds the top one. `deep: 0` already made the
24
+ * globs one level, so nothing here had to change to make that true.
25
+ */
26
+ getWorkspace(dir) {
27
+ const manifest = path.join(dir, 'package.json');
28
+ if (!fs.existsSync(manifest))
29
+ return undefined;
30
+ let patterns;
31
+ try {
32
+ patterns = JSON.parse(fs.readFileSync(manifest, 'utf-8'))?.workspaces;
33
+ }
34
+ catch {
35
+ /** A malformed `package.json` is not this provider's error to report - `Package` will do it
36
+ * with the file in hand when something actually reads the manifest. */
37
+ return undefined;
38
+ }
39
+ if (!Array.isArray(patterns))
40
+ return undefined;
41
+ const packageDirs = [];
42
+ for (const pattern of patterns) {
43
+ if (typeof pattern !== 'string')
44
+ continue;
45
+ const dirs = glob.sync(pattern, { cwd: dir, absolute: true, deep: 0, onlyDirectories: true });
46
+ for (const d of dirs) {
47
+ if (fs.existsSync(path.join(d, 'package.json')))
48
+ packageDirs.push(d);
49
+ }
50
+ }
51
+ return packageDirs;
52
+ }
53
+ /**
54
+ * Teaches `run` about `package.json#scripts`.
55
+ *
56
+ * rman's core resolves a script from `.rmanrc` alone - `before`/`exec`/`after`. That a script might
57
+ * *also* live in `package.json`, that `prebuild` and `postbuild` run around `build`, and that
58
+ * `a && b` is two steps rather than one, are all facts about npm, so they are here.
59
+ *
60
+ * Registered as a `RunService` step source rather than by wrapping anything: the npm part of `run`
61
+ * is inside step resolution, not at its entry point, so there is nothing a wrapper could reach.
62
+ * The core decides what to do with what this returns - see `getScriptSteps` for the precedence.
63
+ */
64
+ getRunSteps(pkg, script) {
65
+ const scripts = pkg.manifest.raw?.scripts;
66
+ if (!scripts || typeof scripts !== 'object')
67
+ return undefined;
68
+ const declared = (name) => typeof scripts[name] === 'string' && !!scripts[name];
69
+ if (!declared(script) && !declared('pre' + script) && !declared('post' + script))
70
+ return undefined;
71
+ /**
72
+ * `parseNpmScript` expands npm's own lifecycle - `pre<script>`, the script, `post<script>` - and
73
+ * splits each on `&&` into the commands npm would run in sequence. The placeholder matters: it
74
+ * asks for a script the package may not declare itself (it may have only a `prebuild`), and
75
+ * without something there the parser reports the whole chain as absent.
76
+ */
77
+ const json = { ...pkg.manifest.raw, scripts: { ...scripts } };
78
+ json.scripts[script] = json.scripts[script] || PLACEHOLDER;
79
+ const info = parseNpmScript(json, 'npm run ' + script);
80
+ if (!info?.raw?.length)
81
+ return undefined;
82
+ const slots = { before: [], exec: [], after: [] };
83
+ for (const step of info.steps) {
84
+ const slot = slotOf(step.name, script);
85
+ if (!slot)
86
+ continue;
87
+ for (const command of Array.isArray(step.parsed) ? step.parsed : [step.parsed]) {
88
+ if (command === PLACEHOLDER)
89
+ continue;
90
+ slots[slot].push(command);
91
+ }
92
+ }
93
+ return slots;
94
+ }
95
+ /**
96
+ * Where npm puts a repository's locally installed executables: `node_modules/.bin`, at **every**
97
+ * level from `cwd` up to the filesystem root - which is how npm itself resolves a binary, so a
98
+ * package's `eslint` is found whether it was installed in that package or hoisted to the workspace
99
+ * root.
100
+ *
101
+ * Adapted from [npm-run-path](https://github.com/sindresorhus/npm-run-path), and it used to sit in
102
+ * rman's core. It is npm's directory layout from end to end: a Cargo or Go repository has no
103
+ * `node_modules` to walk, and nothing here would ever fire for it.
104
+ *
105
+ * **The running `node`'s own directory goes last**, after the walk, and its position is
106
+ * load-bearing. It is there so a script calling `node` gets the interpreter rman itself runs on
107
+ * rather than whatever the shell would pick. It also puts rman's own bin directory ahead of the
108
+ * inherited PATH, which is a measured trap: a nested `rman` invocation inside a `run` script
109
+ * resolves to the globally installed one, not to the repository's. Shim it in
110
+ * `<root>/node_modules/.bin` when that has to be overridden - the walk above reaches there first.
111
+ */
112
+ getBinPaths(cwd) {
113
+ const result = [];
114
+ let previous;
115
+ let dir = path.resolve(cwd);
116
+ while (previous !== dir) {
117
+ result.push(path.join(dir, 'node_modules/.bin'));
118
+ previous = dir;
119
+ dir = path.resolve(dir, '..');
120
+ }
121
+ result.push(path.resolve(cwd, process.execPath, '..'));
122
+ return result;
123
+ }
124
+ }
125
+ /** npm's lifecycle names, mapped onto rman's three slots. */
126
+ function slotOf(stepName, script) {
127
+ if (stepName === 'pre' + script)
128
+ return 'before';
129
+ if (stepName === script)
130
+ return 'exec';
131
+ if (stepName === 'post' + script)
132
+ return 'after';
133
+ return undefined;
134
+ }
@@ -0,0 +1,47 @@
1
+ import type { Repository } from '../../../../core/classes/repository.js';
2
+ import type { LogLevel } from '../../../../utils/logger.js';
3
+ import type { PackageFilterOptions } from '../../../../utils/package-filter.js';
4
+ export declare namespace CiService {
5
+ /**
6
+ * The package managers this plugin knows how to drive, and the `choices` both `ci --package-manager`
7
+ * and the `npm` publish target's own flag offer.
8
+ *
9
+ * Exported rather than module-private, which it was: the publish target needs the same list, and
10
+ * a second copy of it is the kind of drift that shows up as a flag accepting a value nothing can
11
+ * act on. One list, one place it is written down.
12
+ */
13
+ const PACKAGE_MANAGERS: readonly ["npm", "yarn", "pnpm", "bun"];
14
+ type PackageManager = (typeof PACKAGE_MANAGERS)[number];
15
+ interface Options extends PackageFilterOptions {
16
+ packageManager?: PackageManager;
17
+ /** Show the live progress panel while running. Default true, same as `run`/`build`; auto-disabled
18
+ * when stdout isn't a TTY. Doesn't affect what's printed once done - see `run`. */
19
+ progress?: boolean;
20
+ /** Verbosity of the classic per-step log (only applies when the live panel is off). Falls back to
21
+ * the root's `.rmanrc logLevel`, then 'info' - see `resolveRootLogLevel`. */
22
+ logLevel?: LogLevel;
23
+ }
24
+ /** `.rmanrc packageManager` (root only) picks the package manager used for the final install;
25
+ * explicit CLI value wins over it. Defaults to 'npm'. */
26
+ function resolvePackageManager(repository: Repository, cliValue?: PackageManager): PackageManager;
27
+ /** Deletes `node_modules` and any known lockfile directly under `dirname`. Returns the names
28
+ * that actually existed (and were removed), so the caller can log only those. */
29
+ function wipe(dirname: string): Promise<string[]>;
30
+ /**
31
+ * `ci`: a from-scratch, reproducible install for CI pipelines. For every package (root
32
+ * included), deletes `node_modules` and any lockfile - or, if the package defines its own
33
+ * `"ci"` script, runs that instead. Once every package is clean, installs once at the root
34
+ * with the configured package manager (`npm`/`yarn`/`pnpm`/`bun`).
35
+ *
36
+ * Uses the same live progress panel as `run`/`build` (see `../utils/progress-panel.ts`) while it
37
+ * runs, falling back to a plain rmdir/clean/run/install log line per step when the panel is off.
38
+ *
39
+ * Unlike `run`/`build`, it does *not* end with a per-package success tally: `ci`'s packages don't
40
+ * have independently meaningful outcomes the way a build or test run does - wiping a package is
41
+ * trivial and the one step that can genuinely fail, the install, is a single operation for the
42
+ * whole repository. Counting "N succeeded" across packages would just be noise, so only actual
43
+ * failures get called out (by name, with whatever output they produced), followed by one plain
44
+ * completed/failed line.
45
+ */
46
+ function reinstall(repository: Repository, options?: Options): Promise<void>;
47
+ }
@@ -0,0 +1,213 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import colors from 'ansi-colors';
4
+ import { exec } from '../../../../utils/exec.js';
5
+ import { Logger, resolveRootLogLevel } from '../../../../utils/logger.js';
6
+ import { filterPackages } from '../../../../utils/package-filter.js';
7
+ import { formatDuration, ProgressPanel } from '../../../../utils/progress-panel.js';
8
+ export var CiService;
9
+ (function (CiService) {
10
+ /**
11
+ * The package managers this plugin knows how to drive, and the `choices` both `ci --package-manager`
12
+ * and the `npm` publish target's own flag offer.
13
+ *
14
+ * Exported rather than module-private, which it was: the publish target needs the same list, and
15
+ * a second copy of it is the kind of drift that shows up as a flag accepting a value nothing can
16
+ * act on. One list, one place it is written down.
17
+ */
18
+ CiService.PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm', 'bun'];
19
+ /** `.rmanrc packageManager` (root only) picks the package manager used for the final install;
20
+ * explicit CLI value wins over it. Defaults to 'npm'. */
21
+ function resolvePackageManager(repository, cliValue) {
22
+ if (cliValue)
23
+ return cliValue;
24
+ const configured = repository.config?.packageManager;
25
+ if (configured === undefined)
26
+ return 'npm';
27
+ if (CiService.PACKAGE_MANAGERS.includes(configured))
28
+ return configured;
29
+ throw new Error(`Invalid "packageManager" in .rmanrc: "${configured}" (expected one of: ${CiService.PACKAGE_MANAGERS.join(', ')})`);
30
+ }
31
+ CiService.resolvePackageManager = resolvePackageManager;
32
+ /** Deletes `node_modules` and any known lockfile directly under `dirname`. Returns the names
33
+ * that actually existed (and were removed), so the caller can log only those. */
34
+ async function wipe(dirname) {
35
+ const removed = [];
36
+ for (const name of ['node_modules', ...LOCK_FILES]) {
37
+ const target = path.join(dirname, name);
38
+ const existed = await fs
39
+ .access(target)
40
+ .then(() => true)
41
+ .catch(() => false);
42
+ if (!existed)
43
+ continue;
44
+ await fs.rm(target, { recursive: true, force: true });
45
+ removed.push(name);
46
+ }
47
+ return removed;
48
+ }
49
+ CiService.wipe = wipe;
50
+ /**
51
+ * `ci`: a from-scratch, reproducible install for CI pipelines. For every package (root
52
+ * included), deletes `node_modules` and any lockfile - or, if the package defines its own
53
+ * `"ci"` script, runs that instead. Once every package is clean, installs once at the root
54
+ * with the configured package manager (`npm`/`yarn`/`pnpm`/`bun`).
55
+ *
56
+ * Uses the same live progress panel as `run`/`build` (see `../utils/progress-panel.ts`) while it
57
+ * runs, falling back to a plain rmdir/clean/run/install log line per step when the panel is off.
58
+ *
59
+ * Unlike `run`/`build`, it does *not* end with a per-package success tally: `ci`'s packages don't
60
+ * have independently meaningful outcomes the way a build or test run does - wiping a package is
61
+ * trivial and the one step that can genuinely fail, the install, is a single operation for the
62
+ * whole repository. Counting "N succeeded" across packages would just be noise, so only actual
63
+ * failures get called out (by name, with whatever output they produced), followed by one plain
64
+ * completed/failed line.
65
+ */
66
+ async function reinstall(repository, options = {}) {
67
+ const packageManager = resolvePackageManager(repository, options.packageManager);
68
+ const logger = new Logger(options.logLevel ?? resolveRootLogLevel(repository));
69
+ // root is handled separately below - for a non-monorepo, getPackages() would otherwise
70
+ // include it a second time (it doubles as "the" package).
71
+ const packages = filterPackages(repository.getPackages().filter(p => p !== repository.rootPackage), options);
72
+ const progress = options.progress ?? true;
73
+ const panel = new ProgressPanel('CI', !!process.stdout.isTTY && progress);
74
+ const runStartedAt = Date.now();
75
+ panel.start();
76
+ const items = [];
77
+ let failed = false;
78
+ try {
79
+ const results = await Promise.allSettled(packages.map(pkg => {
80
+ const item = panel.addItem(pkg.name);
81
+ items.push(item);
82
+ return ciForPackage(pkg, item, panel.enabled, logger);
83
+ }));
84
+ if (results.some(r => r.status === 'rejected'))
85
+ failed = true;
86
+ const rootItem = panel.addItem('root');
87
+ items.push(rootItem);
88
+ try {
89
+ await ciForRoot(repository, rootItem, panel.enabled, packageManager, logger);
90
+ }
91
+ catch {
92
+ failed = true;
93
+ }
94
+ }
95
+ finally {
96
+ panel.stop();
97
+ }
98
+ for (const item of items) {
99
+ if (item.status !== 'failed')
100
+ continue;
101
+ logger.error(colors.red.bold('X'), item.name);
102
+ if (item.log.length)
103
+ logger.error(colors.red(item.log.join('\n')));
104
+ }
105
+ const totalElapsed = formatDuration(Date.now() - runStartedAt);
106
+ const summary = [failed ? colors.red('ci failed') : colors.green('ci completed'), colors.gray(`(${totalElapsed})`)];
107
+ if (failed)
108
+ logger.error(...summary);
109
+ else
110
+ logger.info(...summary);
111
+ if (failed) {
112
+ const err = new Error('"ci" failed');
113
+ err.logged = true;
114
+ throw err;
115
+ }
116
+ }
117
+ CiService.reinstall = reinstall;
118
+ })(CiService || (CiService = {}));
119
+ const LOCK_FILES = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lock', 'bun.lockb'];
120
+ /** Runs a shell `command` in `cwd`, driving `item` for the live panel when it's on, or falling
121
+ * back to the plain "classic" log line `logLine` prints first - the same split `run`'s classic
122
+ * log uses. `logLine` is expected to go through a `Logger` (so it respects `--log-level`), not a
123
+ * raw `console.log`. */
124
+ async function runStep(app, item, panelEnabled, cwd, command, logLine) {
125
+ if (panelEnabled) {
126
+ await exec(command, {
127
+ app,
128
+ cwd,
129
+ stdio: 'pipe',
130
+ onLine: line => {
131
+ item.log.push(line);
132
+ item.lastLine = line;
133
+ },
134
+ });
135
+ }
136
+ else {
137
+ logLine();
138
+ await exec(command, { cwd, app, stdio: 'inherit' });
139
+ }
140
+ }
141
+ /** Logs (or reflects onto the live panel) the outcome of a `wipe()` call - including the "nothing
142
+ * to remove" case, which otherwise prints nothing at all in the classic log and can look like the
143
+ * package was never processed (most workspace layouts hoist deps to the root's `node_modules`,
144
+ * so an individual package legitimately has nothing of its own to wipe most of the time). */
145
+ function logWipeResult(item, panelEnabled, logger, label, removed) {
146
+ if (panelEnabled) {
147
+ item.lastLine = removed.length ? `removed ${removed.join(', ')}` : 'already clean';
148
+ return;
149
+ }
150
+ if (!removed.length) {
151
+ logger.info(colors.gray('clean'), colors.cyan(label));
152
+ return;
153
+ }
154
+ for (const name of removed)
155
+ logger.info(colors.yellow('rmdir'), colors.cyan(label), name);
156
+ }
157
+ /** A package can opt out of the default wipe by defining its own `"ci"` npm script - if present,
158
+ * that runs instead of the wipe, same as any other rman script. */
159
+ async function ciForPackage(pkg, item, panelEnabled, logger) {
160
+ item.status = 'running';
161
+ item.startedAt = Date.now();
162
+ try {
163
+ const script = pkg.manifest.raw.scripts?.ci;
164
+ if (typeof script === 'string' && script) {
165
+ item.currentStep = 'ci';
166
+ await runStep(pkg.repository.app, item, panelEnabled, pkg.dirname, script, () => logger.info(colors.cyan('run'), colors.cyan(item.name), script));
167
+ }
168
+ else {
169
+ item.currentStep = 'wipe';
170
+ const removed = await CiService.wipe(pkg.dirname);
171
+ logWipeResult(item, panelEnabled, logger, item.name, removed);
172
+ }
173
+ item.status = 'success';
174
+ }
175
+ catch (e) {
176
+ item.status = 'failed';
177
+ throw e;
178
+ }
179
+ finally {
180
+ item.finishedAt = Date.now();
181
+ }
182
+ }
183
+ /** The root gets one extra step over a regular package once it isn't opting out with its own
184
+ * `"ci"` script: after its own wipe, it installs once for the whole repository. */
185
+ async function ciForRoot(repository, item, panelEnabled, packageManager, logger) {
186
+ item.status = 'running';
187
+ item.startedAt = Date.now();
188
+ try {
189
+ const script = repository.rootPackage.manifest.raw.scripts?.ci;
190
+ if (typeof script === 'string' && script) {
191
+ item.currentStep = 'ci';
192
+ await runStep(repository.app, item, panelEnabled, repository.dirname, script, () => logger.info(colors.cyan('run'), colors.cyan('root'), script));
193
+ }
194
+ else {
195
+ item.stepsTotal = 2;
196
+ item.stepIndex = 0;
197
+ item.currentStep = 'wipe';
198
+ const removed = await CiService.wipe(repository.dirname);
199
+ logWipeResult(item, panelEnabled, logger, 'root', removed);
200
+ item.stepIndex = 1;
201
+ item.currentStep = 'install';
202
+ await runStep(repository.app, item, panelEnabled, repository.dirname, `${packageManager} install`, () => logger.info(colors.cyan('install'), `Running "${packageManager} install"`));
203
+ }
204
+ item.status = 'success';
205
+ }
206
+ catch (e) {
207
+ item.status = 'failed';
208
+ throw e;
209
+ }
210
+ finally {
211
+ item.finishedAt = Date.now();
212
+ }
213
+ }
@@ -0,0 +1,53 @@
1
+ import type { Repository } from '../../../../core/classes/repository.js';
2
+ import type { LogLevel } from '../../../../utils/logger.js';
3
+ import type { PackageFilterOptions } from '../../../../utils/package-filter.js';
4
+ export declare namespace CleanService {
5
+ interface Options extends PackageFilterOptions {
6
+ /** Show the live progress panel. Default true; auto-disabled when stdout isn't a TTY. */
7
+ progress?: boolean;
8
+ /** Report what would be removed without actually removing anything. Default false. */
9
+ dryRun?: boolean;
10
+ /** Clean the whole repository even when the current directory is inside a single package
11
+ * (which otherwise scopes cleaning to just that package - see `Repository.currentPackage`).
12
+ * Has no effect when already at the repository root, or outside any known package. */
13
+ fromRoot?: boolean;
14
+ /** Verbosity of the classic per-item log (only applies when the live panel is off). Falls back
15
+ * to the root's `.rmanrc logLevel`, then 'info' - see `resolveRootLogLevel`. */
16
+ logLevel?: LogLevel;
17
+ }
18
+ /**
19
+ * `clean`: removes build output across every package (root included) - the replacement for
20
+ * `ts-cleanup`, plus whatever else `.rmanrc clean.include`/`clean.exclude` says to remove.
21
+ *
22
+ * **The `node` built-in's rather than always present, because what it knows how to delete is not
23
+ * repository-shaped knowledge but TypeScript-shaped**: a compiled `.js`/`.js.map`/`.d.ts` beside
24
+ * its `.ts` source, a `*.tsbuildinfo`, and a `node_modules` to skip while looking. A Cargo or Go
25
+ * repository has `cargo clean` and `go clean` and nothing here would fire for it, so a core
26
+ * `clean` was a command that only appeared to be general.
27
+ *
28
+ * The `clean.include`/`clean.exclude` globs move with it, and that is the one cost worth naming:
29
+ * they are ecosystem-neutral, so a repository wanting only those now has to name this plugin (or
30
+ * write the two `rm` lines as a `run` script). Keeping a stub `clean` in core for them would mean
31
+ * two commands with one name and a precedence rule between them - worse than the honest move.
32
+ *
33
+ * For every package not opted out via its own (cascaded) `clean.skip: true`:
34
+ * - deletes compiled `.js`/`.js.map`/`.d.ts` files under its `src`/`test` (see `cleanTsArtifacts`);
35
+ * - deletes any `*.tsbuildinfo` incremental-build cache file anywhere in it;
36
+ * - deletes anything matching its own (cascaded) `clean.include` glob(s), minus `clean.exclude`.
37
+ *
38
+ * `include`/`exclude` are resolved relative to *that* package's own directory - a root-level
39
+ * `.rmanrc` pattern like `packages/*\/build` is naturally evaluated from the repository root
40
+ * (spanning every package in one pass), while a package's own override in its own `.rmanrc`
41
+ * (e.g. `include: ./cache`) only ever reaches that one package, since a package normally
42
+ * overrides rather than merges with the root's value for the same key.
43
+ *
44
+ * Never touches `node_modules` - that's `ci`'s job, not this one.
45
+ *
46
+ * Run from inside a single package's own directory, it only cleans that package unless
47
+ * `options.fromRoot` says otherwise (see `Repository.currentPackage`).
48
+ *
49
+ * Uses the same live progress panel as `run`/`build` (see `../utils/progress-panel.ts`) - unlike
50
+ * `ci`, cleaning genuinely is independent per-package work, so the final per-package tally stays.
51
+ */
52
+ function clean(repository: Repository, options?: Options): Promise<void>;
53
+ }