rman 2.13.0 → 2.15.0

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 (34) hide show
  1. package/README.md +52 -19
  2. package/builtins/platforms/node/node-dependency-updater.d.ts +116 -0
  3. package/builtins/platforms/node/node-dependency-updater.js +452 -0
  4. package/builtins/platforms/node/node.platform.d.ts +2 -0
  5. package/builtins/platforms/node/node.platform.js +2 -0
  6. package/builtins/platforms/node/services/version-plan.service.d.ts +1 -1
  7. package/builtins/platforms/node/services/version-plan.service.js +1 -1
  8. package/builtins/publish-targets/npm/npm-view.d.ts +31 -0
  9. package/builtins/publish-targets/npm/npm-view.js +47 -0
  10. package/commands/changelog.command.js +4 -3
  11. package/commands/deps.command.d.ts +175 -0
  12. package/commands/deps.command.js +252 -0
  13. package/commands/info.command.js +11 -5
  14. package/commands/version.command.js +2 -0
  15. package/commands.d.ts +2 -0
  16. package/commands.js +1 -0
  17. package/constants.js +1 -1
  18. package/core/interfaces/dependency-updater.d.ts +114 -0
  19. package/core/interfaces/dependency-updater.js +61 -0
  20. package/core/interfaces/manifest.js +1 -1
  21. package/core/interfaces/plugin.d.ts +5 -1
  22. package/core/interfaces/plugin.js +1 -1
  23. package/index.d.ts +2 -0
  24. package/index.js +2 -0
  25. package/package.json +1 -1
  26. package/services/change-hash.service.d.ts +2 -2
  27. package/services/change-hash.service.js +2 -2
  28. package/services/system-info.d.ts +3 -0
  29. package/services/system-info.js +1 -0
  30. package/services/version-plan.service.d.ts +2 -1
  31. package/services/version-plan.service.js +7 -5
  32. package/services/version.service.js +4 -4
  33. package/utils/package-filter.d.ts +1 -1
  34. package/utils/package-filter.js +1 -1
@@ -0,0 +1,175 @@
1
+ import type { RmanApplication } from '../core/application.js';
2
+ import { type RmanConfig } from '../interfaces/rman-config.interface.js';
3
+ /** Hoisted for `ArgsOf` - see `version.command.ts` and `RmanConfig.ArgsOf` for why. */
4
+ declare const COMMAND: "deps [names..]";
5
+ declare const config: {
6
+ scope: {
7
+ target: "cli";
8
+ describe: string;
9
+ type: "string";
10
+ };
11
+ ignore: {
12
+ target: "cli";
13
+ describe: string;
14
+ type: "string";
15
+ };
16
+ platform: {
17
+ target: "cli";
18
+ describe: string;
19
+ type: "string";
20
+ };
21
+ upgrade: {
22
+ target: "cli";
23
+ alias: string;
24
+ describe: string;
25
+ type: "boolean";
26
+ };
27
+ /** Its `choices` and its default are the version schemes' own sizes, so they are filled in where
28
+ * the command is built - see `targetOption`. */
29
+ target: {
30
+ target: "both";
31
+ describe: string;
32
+ type: "string";
33
+ };
34
+ reject: {
35
+ target: "both";
36
+ describe: string;
37
+ type: "string";
38
+ array: true;
39
+ };
40
+ minAge: {
41
+ target: "both";
42
+ cliName: string;
43
+ describe: string;
44
+ type: "number";
45
+ };
46
+ types: {
47
+ target: "config";
48
+ describe: string;
49
+ type: "string";
50
+ array: true;
51
+ };
52
+ concurrency: {
53
+ target: "cli";
54
+ describe: string;
55
+ type: "number";
56
+ };
57
+ verify: {
58
+ target: "cli";
59
+ describe: string;
60
+ type: "boolean";
61
+ };
62
+ json: {
63
+ target: "cli";
64
+ alias: string;
65
+ describe: string;
66
+ type: "boolean";
67
+ };
68
+ };
69
+ type Args = RmanConfig.ArgsOf<typeof config, typeof COMMAND>;
70
+ /**
71
+ * `rman deps` - which dependencies have a newer version, and with `--upgrade`, moving them there.
72
+ *
73
+ * A version is offered only when every rule that can be seen allows it: the package's own
74
+ * `deps.target`/`reject`/`minAge`, the peer ranges of the other dependencies it names, the runtime
75
+ * the package supports, and what its sibling packages ask of a shared dependency. Each version held
76
+ * back is printed with the rule that held it.
77
+ */
78
+ declare const depsCommand: (app: RmanApplication) => {
79
+ command: "deps [names..]";
80
+ describe: string;
81
+ config: {
82
+ target: {
83
+ target: "both";
84
+ describe: string;
85
+ type: "string";
86
+ } & {
87
+ choices: string[];
88
+ };
89
+ scope: {
90
+ target: "cli";
91
+ describe: string;
92
+ type: "string";
93
+ };
94
+ ignore: {
95
+ target: "cli";
96
+ describe: string;
97
+ type: "string";
98
+ };
99
+ platform: {
100
+ target: "cli";
101
+ describe: string;
102
+ type: "string";
103
+ };
104
+ upgrade: {
105
+ target: "cli";
106
+ alias: string;
107
+ describe: string;
108
+ type: "boolean";
109
+ };
110
+ reject: {
111
+ target: "both";
112
+ describe: string;
113
+ type: "string";
114
+ array: true;
115
+ };
116
+ minAge: {
117
+ target: "both";
118
+ cliName: string;
119
+ describe: string;
120
+ type: "number";
121
+ };
122
+ types: {
123
+ target: "config";
124
+ describe: string;
125
+ type: "string";
126
+ array: true;
127
+ };
128
+ concurrency: {
129
+ target: "cli";
130
+ describe: string;
131
+ type: "number";
132
+ };
133
+ verify: {
134
+ target: "cli";
135
+ describe: string;
136
+ type: "boolean";
137
+ };
138
+ json: {
139
+ target: "cli";
140
+ alias: string;
141
+ describe: string;
142
+ type: "boolean";
143
+ };
144
+ };
145
+ positionals: {
146
+ names: {
147
+ describe: string;
148
+ type: "string";
149
+ };
150
+ };
151
+ examples: {
152
+ command: string;
153
+ description: string;
154
+ }[];
155
+ handler: (args: Args) => Promise<void>;
156
+ };
157
+ export default depsCommand;
158
+ /** The rest of `deps.*` - what an option cannot describe. */
159
+ export interface DepsExtraKeys {
160
+ /**
161
+ * The largest move for the dependencies a glob matches, ahead of `target` - a package that
162
+ * releases a major every few weeks, or one that has stayed on `0.x` for years, where a `0.x`
163
+ * minor counts as a major. The last glob matching a name wins.
164
+ *
165
+ * @example { "@types/node": "major", "esbuild": "major" }
166
+ */
167
+ targets?: Record<string, string>;
168
+ }
169
+ /** `deps`'s own keys on `RmanConfig`: derived from the `config` block, plus `DepsExtraKeys`. */
170
+ declare module '../interfaces/rman-config.interface.js' {
171
+ namespace RmanConfig {
172
+ interface CommandConfigs extends RmanConfig.CommandContribution<ReturnType<typeof depsCommand>, DepsExtraKeys> {
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,252 @@
1
+ import path from 'node:path';
2
+ import colors from 'ansi-colors';
3
+ import { semverScheme } from '../core/classes/version-scheme.js';
4
+ import { DependencyUpdater } from '../core/interfaces/dependency-updater.js';
5
+ import { registerCommand } from '../interfaces/rman-config.interface.js';
6
+ import { filterPackages, packageFilterOptions, readPackageFilterOptions } from '../utils/package-filter.js';
7
+ /** Hoisted for `ArgsOf` - see `version.command.ts` and `RmanConfig.ArgsOf` for why. */
8
+ const COMMAND = 'deps [names..]';
9
+ const { scope, ignore, platform } = packageFilterOptions;
10
+ const config = {
11
+ /* `--deps`/`--dependents` are left out: on a command called `deps`, `rman deps --deps` would read
12
+ * as something other than "and the packages these depend on". */
13
+ scope,
14
+ ignore,
15
+ platform,
16
+ upgrade: {
17
+ target: 'cli',
18
+ alias: 'u',
19
+ describe: 'Write the new ranges to the manifests, then check that they still install',
20
+ type: 'boolean',
21
+ },
22
+ /** Its `choices` and its default are the version schemes' own sizes, so they are filled in where
23
+ * the command is built - see `targetOption`. */
24
+ target: {
25
+ target: 'both',
26
+ describe: 'The largest move a dependency may make',
27
+ type: 'string',
28
+ },
29
+ reject: {
30
+ target: 'both',
31
+ describe: 'Leave the dependencies matching these globs alone (repeatable)',
32
+ type: 'string',
33
+ array: true,
34
+ },
35
+ minAge: {
36
+ target: 'both',
37
+ cliName: 'min-age',
38
+ describe: 'Only move to a version published at least this many days ago',
39
+ type: 'number',
40
+ },
41
+ types: {
42
+ target: 'config',
43
+ describe: "The dependency kinds to look at, in the ecosystem's own words - default: every kind",
44
+ type: 'string',
45
+ array: true,
46
+ },
47
+ concurrency: {
48
+ target: 'cli',
49
+ describe: `How many registry lookups run at once (default: ${defaultConcurrency()})`,
50
+ type: 'number',
51
+ },
52
+ verify: {
53
+ target: 'cli',
54
+ describe: "After --upgrade, ask the ecosystem's own resolver whether the result installs (default: true)",
55
+ type: 'boolean',
56
+ },
57
+ json: {
58
+ target: 'cli',
59
+ alias: 'j',
60
+ describe: 'Print the plan as JSON - one entry per package and dependency',
61
+ type: 'boolean',
62
+ },
63
+ };
64
+ /**
65
+ * `rman deps` - which dependencies have a newer version, and with `--upgrade`, moving them there.
66
+ *
67
+ * A version is offered only when every rule that can be seen allows it: the package's own
68
+ * `deps.target`/`reject`/`minAge`, the peer ranges of the other dependencies it names, the runtime
69
+ * the package supports, and what its sibling packages ask of a shared dependency. Each version held
70
+ * back is printed with the rule that held it.
71
+ */
72
+ /* **The command is the core's and the answer is the technology's** - see `DependencyUpdater`. A
73
+ * package whose platform has no updater is left alone, which in a polyglot repository is the right
74
+ * answer for the half rman cannot speak for. */
75
+ const depsCommand = registerCommand(app => {
76
+ const repository = app.repository;
77
+ return {
78
+ command: COMMAND,
79
+ describe: 'Lists the dependencies that have a newer version - pass -u to upgrade them',
80
+ config: { ...config, target: targetOption(app) },
81
+ positionals: {
82
+ names: { describe: 'Only these dependencies (globs)', type: 'string' },
83
+ },
84
+ examples: [
85
+ { command: '$0 deps', description: '# What could move, and what holds it back' },
86
+ { command: '$0 deps -u', description: '# Write the new ranges, then check that they install' },
87
+ { command: '$0 deps --target major', description: '# Across a major too' },
88
+ { command: '$0 deps "@types/*"', description: '# Only the dependencies matching this glob' },
89
+ ],
90
+ handler: async (args) => {
91
+ const options = {
92
+ names: args.names?.length ? args.names.map(String) : undefined,
93
+ target: args.target,
94
+ reject: args.reject?.map(String),
95
+ minAge: args.minAge,
96
+ concurrency: Math.max(1, args.concurrency ?? defaultConcurrency()),
97
+ };
98
+ const ctx = { app, repository, options };
99
+ /** The root holds a monorepo's shared tooling, so it is asked too. */
100
+ const candidates = repository.monorepo ? [repository.rootPackage, ...repository.packages] : repository.packages;
101
+ const selected = filterPackages([...candidates], readPackageFilterOptions(args));
102
+ if (!selected.length)
103
+ throw logged('No package matched.');
104
+ const byUpdater = new Map();
105
+ for (const pkg of selected) {
106
+ const updater = pkg.platform.dependencyUpdater;
107
+ if (!updater)
108
+ continue;
109
+ byUpdater.set(updater, [...(byUpdater.get(updater) ?? []), pkg]);
110
+ }
111
+ if (!byUpdater.size) {
112
+ throw logged('None of the selected packages belongs to a technology that can update its dependencies.');
113
+ }
114
+ const plans = new Map();
115
+ for (const [updater, packages] of byUpdater)
116
+ plans.set(updater, await updater.getPlan(ctx, packages));
117
+ const all = [...plans.values()].flat();
118
+ if (args.json)
119
+ console.log(JSON.stringify(jsonPlan(all), undefined, 2));
120
+ else
121
+ printPlan(all, repository.dirname);
122
+ const errors = all.filter(e => e.status === 'error');
123
+ const updates = all.filter(e => e.status === 'update');
124
+ if (!args.upgrade) {
125
+ if (updates.length && !args.json)
126
+ console.log(colors.gray('\nRun "rman deps -u" to upgrade.'));
127
+ if (errors.length)
128
+ throw logged(`${errors.length} dependency lookup(s) failed - see above.`);
129
+ return;
130
+ }
131
+ const applied = [];
132
+ for (const [updater, plan] of plans) {
133
+ if (plan.some(e => e.status === 'update'))
134
+ applied.push(await updater.applyPlan(ctx, plan));
135
+ }
136
+ const files = applied.flatMap(a => a.files);
137
+ if (files.length && args.verify !== false) {
138
+ for (const [updater, packages] of byUpdater) {
139
+ const refused = await updater.verify?.(ctx, packages);
140
+ if (!refused)
141
+ continue;
142
+ for (const a of applied)
143
+ a.restore();
144
+ throw logged(`The upgraded manifests do not install, so they were put back:\n${refused}`);
145
+ }
146
+ }
147
+ if (!args.json) {
148
+ for (const file of files)
149
+ console.log(colors.green('updated'), path.relative(repository.dirname, file));
150
+ }
151
+ if (errors.length)
152
+ throw logged(`${errors.length} dependency lookup(s) failed - see above.`);
153
+ },
154
+ };
155
+ });
156
+ export default depsCommand;
157
+ /** Registry lookups are child processes waiting on the network, not CPU work, so the default is a
158
+ * fixed number rather than the core count. */
159
+ function defaultConcurrency() {
160
+ return 16;
161
+ }
162
+ /**
163
+ * `--target`: one of the version schemes' sizes, and by default every size but the largest.
164
+ *
165
+ * Read off the technologies this repository loaded rather than written down, because the sizes are
166
+ * a scheme's - semver's three are the only ones today.
167
+ */
168
+ function targetOption(app) {
169
+ const schemes = [...app.platforms].map(p => p.manifestProvider.versionScheme ?? semverScheme);
170
+ const sizes = [...new Set((schemes.length ? schemes : [semverScheme]).flatMap(s => s.bumpNames))];
171
+ const defaults = [...new Set(schemes.map(s => DependencyUpdater.defaultTarget(s.bumpNames)))];
172
+ return {
173
+ ...config.target,
174
+ describe: `${config.target.describe} (default: ${defaults.join(' / ') || DependencyUpdater.defaultTarget(semverScheme.bumpNames)})`,
175
+ choices: sizes,
176
+ };
177
+ }
178
+ function jsonPlan(entries) {
179
+ return entries.map(e => ({
180
+ package: e.package.name,
181
+ name: e.name,
182
+ types: e.types,
183
+ status: e.status,
184
+ current: e.current,
185
+ target: e.target,
186
+ latest: e.latest,
187
+ available: e.available,
188
+ bump: e.bump,
189
+ reason: e.reason,
190
+ }));
191
+ }
192
+ /**
193
+ * One block per package with something to say: what moves, grouped by size, smallest first - and
194
+ * then **what does not**, each with the reason it stays.
195
+ */
196
+ /* **The second half is the one `npm-check-updates` taught people to read.** Under `--target minor`
197
+ * a major that was left behind has to be on the screen, or a run reporting three patches looks like
198
+ * a repository that is current. A dependency that moved but not as far as it could appears in both:
199
+ * once under its size, once under "not updated" with what held it. */
200
+ function printPlan(entries, root) {
201
+ const updated = entries.filter(e => e.status === 'update');
202
+ const behind = entries.filter(e => e.status === 'held' || e.status === 'skipped' || e.status === 'error' || (e.status === 'update' && e.reason));
203
+ if (!updated.length && !behind.length) {
204
+ console.log(colors.gray('Every dependency is up to date.'));
205
+ return;
206
+ }
207
+ const shown = [...updated, ...behind];
208
+ const nameWidth = Math.max(...shown.map(e => e.name.length));
209
+ const currentWidth = Math.max(...shown.map(e => e.current.length));
210
+ const row = (e, rest) => ` ${e.name.padEnd(nameWidth)} ${e.current.padEnd(currentWidth)} ${rest}`;
211
+ for (const pkg of [...new Set(shown.map(e => e.package))]) {
212
+ const where = path.relative(root, pkg.dirname);
213
+ console.log(colors.cyan(pkg.name) + (where ? colors.gray(` (${where})`) : ''));
214
+ const sizes = pkg.versionScheme.bumpNames;
215
+ for (const size of [...sizes, undefined]) {
216
+ const group = updated.filter(e => e.package === pkg && (size ? e.bump === size : !sizes.includes(e.bump)));
217
+ if (!group.length)
218
+ continue;
219
+ const paint = sizeColor(size, sizes);
220
+ console.log(` ${paint(size ?? 'other')}`);
221
+ for (const e of group)
222
+ console.log(row(e, `→ ${paint(e.target ?? '')}`));
223
+ }
224
+ const left = behind.filter(e => e.package === pkg);
225
+ if (!left.length)
226
+ continue;
227
+ console.log(` ${colors.yellow('not updated')}`);
228
+ const newest = (e) => (e.status === 'error' ? 'error' : (e.available ?? e.latest ?? ''));
229
+ const width = Math.max(...left.map(e => newest(e).length));
230
+ for (const e of left) {
231
+ const paint = e.status === 'error' ? colors.red : (text) => text;
232
+ console.log(row(e, `${paint(newest(e).padEnd(width))} ${colors.gray(e.reason ?? '')}`));
233
+ }
234
+ }
235
+ }
236
+ /** The colours `npm-check-updates` uses - the largest size red, the next cyan, the rest green - read
237
+ * off the scheme's own order so a scheme with other names still gets them. */
238
+ function sizeColor(size, sizes) {
239
+ const rank = size ? sizes.indexOf(size) : -1;
240
+ if (rank === sizes.length - 1)
241
+ return colors.red;
242
+ if (rank === sizes.length - 2)
243
+ return colors.cyan;
244
+ return colors.green;
245
+ }
246
+ /** The `logged` convention: printed here, so `runCli`'s catch does not print it a second time. */
247
+ function logged(message) {
248
+ console.error(colors.red(message));
249
+ const err = new Error(message);
250
+ err.logged = true;
251
+ return err;
252
+ }
@@ -60,13 +60,19 @@ function printSystemInfo(systemInfo) {
60
60
  }
61
61
  function printRepositoryInfo(info) {
62
62
  console.log('', colors.whiteBright('Repository') + ':');
63
- console.log(' ' + colors.reset('Type') + ' :', colors.yellowBright(info.type === 'monorepo' ? 'Monorepo' : 'Single package'));
64
- console.log(' ' + colors.reset('Name') + ' :', colors.yellowBright(info.name || '(none)'));
65
- console.log(' ' + colors.reset('Version') + ' :', colors.yellowBright(info.version || '(none)'));
66
- console.log(' ' + colors.reset('Root') + ' :', colors.yellowBright(info.root));
63
+ const rows = [
64
+ ['Type', colors.yellowBright(info.type === 'monorepo' ? 'Monorepo' : 'Single package')],
65
+ ['Name', colors.yellowBright(info.name || '(none)')],
66
+ ['Version', colors.yellowBright(info.version || '(none)')],
67
+ ['Root', colors.yellowBright(info.root)],
68
+ ['Platforms', colors.yellowBright(info.platforms.join(', ') || '(none)')],
69
+ ];
67
70
  if (info.type === 'monorepo') {
68
- console.log(' ' + colors.reset('Packages') + ' :', colors.yellowBright(String(info.packageCount)), colors.gray('(run "list" to see them)'));
71
+ rows.push(['Packages', colors.yellowBright(String(info.packageCount)), colors.gray('(run "list" to see them)')]);
69
72
  }
73
+ const width = Math.max(...rows.map(([label]) => label.length));
74
+ for (const [label, ...values] of rows)
75
+ console.log(' ' + colors.reset(label.padEnd(width)) + ' :', ...values);
70
76
  }
71
77
  /**
72
78
  * `rman info` - environment and repository, and back in the core because most of what it reports
@@ -210,6 +210,8 @@ const versionCommand = registerCommand(app => {
210
210
  name: e.package.name,
211
211
  selector: e.package.selector,
212
212
  isRoot: e.package.isRoot,
213
+ /** `rman list --json`'s spelling, so a consumer joins the two on it. */
214
+ groupKey: e.groupKey,
213
215
  group: e.group,
214
216
  status: e.status,
215
217
  from: e.from,
package/commands.d.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  import './commands/build.command.js';
16
16
  import './commands/changelog.command.js';
17
17
  import './commands/config.command.js';
18
+ import './commands/deps.command.js';
18
19
  import './commands/diff.command.js';
19
20
  import './commands/exec.command.js';
20
21
  import './commands/github-release.command.js';
@@ -27,5 +28,6 @@ import './commands/test.command.js';
27
28
  import './commands/version.command.js';
28
29
  /** The config shapes a command declares by hand, where an option cannot describe them - see
29
30
  * `RmanConfig.CommandContribution`'s `Extra`. Exported so a plugin can name one. */
31
+ export type { DepsExtraKeys } from './commands/deps.command.js';
30
32
  export type { PublishExtraKeys, PublishTargetConfigs } from './commands/publish.command.js';
31
33
  export type { VersionExtraKeys, VersionStampEntry } from './commands/version.command.js';
package/commands.js CHANGED
@@ -15,6 +15,7 @@
15
15
  import './commands/build.command.js';
16
16
  import './commands/changelog.command.js';
17
17
  import './commands/config.command.js';
18
+ import './commands/deps.command.js';
18
19
  import './commands/diff.command.js';
19
20
  import './commands/exec.command.js';
20
21
  import './commands/github-release.command.js';
package/constants.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.13.0';
1
+ export const version = '2.15.0';
@@ -0,0 +1,114 @@
1
+ import type { RmanApplication } from '../application.js';
2
+ import type { Package } from '../classes/package.js';
3
+ import type { Repository } from '../classes/repository.js';
4
+ /**
5
+ * **How one technology answers "are this package's dependencies current, and what may they move
6
+ * to"** - the half of `rman deps` that is an ecosystem's.
7
+ *
8
+ * The command owns the repository's half: which packages are asked, printing the plan, `--json`,
9
+ * writing only under `--upgrade`, and undoing the writes when `verify` refuses them.
10
+ */
11
+ export interface DependencyUpdater {
12
+ /** What each dependency of `packages` would move to - never writes. */
13
+ getPlan(ctx: DependencyUpdater.Context, packages: readonly Package[]): Promise<DependencyUpdater.Entry[]>;
14
+ /** Writes every `'update'` entry of `plan` to its manifest, and returns how to take that back. */
15
+ applyPlan(ctx: DependencyUpdater.Context, plan: readonly DependencyUpdater.Entry[]): Promise<DependencyUpdater.Applied>;
16
+ /**
17
+ * Asks the ecosystem's own resolver whether the manifests as they now stand can be installed,
18
+ * once `applyPlan` has written them. Returns why not, or `undefined` when they can.
19
+ *
20
+ * Omit it when the ecosystem has no resolver to ask; the plan's own checks then stand alone.
21
+ */
22
+ verify?(ctx: DependencyUpdater.Context, packages: readonly Package[]): Promise<string | undefined>;
23
+ }
24
+ export declare namespace DependencyUpdater {
25
+ /** What one call is handed. */
26
+ interface Context {
27
+ readonly app: RmanApplication;
28
+ readonly repository: Repository;
29
+ readonly options: Options;
30
+ }
31
+ /** The run's own settings, read off the command line. A package's `.rmanrc "deps"` block supplies
32
+ * the rest - see `settingsFor`. */
33
+ interface Options {
34
+ /** Only the dependencies whose names match one of these globs. */
35
+ names?: string[];
36
+ /** Overrides every package's `deps.target`. */
37
+ target?: string;
38
+ /** Added to every package's `deps.reject`. */
39
+ reject?: string[];
40
+ /** Overrides every package's `deps.minAge`. */
41
+ minAge?: number;
42
+ /** How many registry lookups run at once. */
43
+ concurrency: number;
44
+ /** Reported as each dependency name's registry lookup finishes. */
45
+ onProgress?(done: number, total: number): void;
46
+ }
47
+ /** One package's settings, once the command line has been laid over its config. */
48
+ interface Settings {
49
+ /** The largest move allowed, as one of the package's version scheme's `bumpNames` - `minor`
50
+ * keeps every dependency inside its major. */
51
+ target: string;
52
+ /** `deps.targets`: a dependency-name glob, and the largest move for the names it matches -
53
+ * ahead of `target`. Empty when `--target` was given, which overrides both. */
54
+ targets: Record<string, string>;
55
+ reject: string[];
56
+ minAge: number;
57
+ /** The dependency kinds to look at, in the ecosystem's own words - `undefined` is every one. */
58
+ types?: string[];
59
+ }
60
+ /**
61
+ * One dependency of one package.
62
+ *
63
+ * - `'update'`: `target` is the new range; `reason` says why it is lower than `latest`, if it is.
64
+ * - `'held'`: a newer version exists and is allowed, but another dependency's rule refuses it -
65
+ * `reason` names that rule.
66
+ * - `'skipped'`: a newer version exists, and the package's own settings leave it out - a major
67
+ * under `target: minor`, or one younger than `minAge`. `reason` says which.
68
+ * - `'up-to-date'`: nothing newer exists.
69
+ * - `'error'`: the registry gave no answer.
70
+ */
71
+ interface Entry {
72
+ package: Package;
73
+ /** The dependency's name. */
74
+ name: string;
75
+ /** Where the package declares it, in the ecosystem's own words - `devDependencies`. */
76
+ types: string[];
77
+ /** The range as the manifest declares it. */
78
+ current: string;
79
+ status: 'update' | 'held' | 'skipped' | 'up-to-date' | 'error';
80
+ /** The range it is rewritten to, on an `'update'`. */
81
+ target?: string;
82
+ /** The newest version the package's own settings allow, before any other dependency's rule. */
83
+ latest?: string;
84
+ /** The newest version there is, whatever the settings say - what a reader compares against to
85
+ * see what was left behind. */
86
+ available?: string;
87
+ /** How far `target` moves, in the version scheme's own words - `major`. */
88
+ bump?: string;
89
+ reason?: string;
90
+ }
91
+ /** What `applyPlan` wrote. */
92
+ interface Applied {
93
+ /** Every file it changed, absolute. */
94
+ files: string[];
95
+ /** Puts every one of them back as it was. */
96
+ restore(): void;
97
+ }
98
+ /**
99
+ * Every move but the largest - `minor` under semver. The largest is the one a scheme reserves for a
100
+ * break, and taking a break is a decision a person makes, not a default.
101
+ */
102
+ function defaultTarget(bumps: readonly string[]): string;
103
+ /**
104
+ * The largest move `settings` allow for the dependency `name`, and the key that said so - the
105
+ * **last** `deps.targets` glob matching it, else `deps.target`.
106
+ */
107
+ function targetFor(settings: Settings, name: string): {
108
+ size: string;
109
+ from: string;
110
+ };
111
+ /** The settings `pkg` is planned with: `options` from the command line over its `.rmanrc "deps"`,
112
+ * and the defaults under both. */
113
+ function settingsFor(pkg: Package, options: Options): Settings;
114
+ }
@@ -0,0 +1,61 @@
1
+ import micromatch from 'micromatch';
2
+ export var DependencyUpdater;
3
+ (function (DependencyUpdater) {
4
+ /**
5
+ * Every move but the largest - `minor` under semver. The largest is the one a scheme reserves for a
6
+ * break, and taking a break is a decision a person makes, not a default.
7
+ */
8
+ /* **It was `latest` - every move - and that was the wrong default**, measured the moment it ran:
9
+ * a `^5.3.0` came back as `^7.0.2`. The values are the scheme's own `bumpNames` rather than a list
10
+ * written here, for the reason `version` gives: `patch`/`minor`/`major` are semver's words, and a
11
+ * four-part scheme would offer four. */
12
+ function defaultTarget(bumps) {
13
+ return bumps[Math.max(0, bumps.length - 2)];
14
+ }
15
+ DependencyUpdater.defaultTarget = defaultTarget;
16
+ /**
17
+ * The largest move `settings` allow for the dependency `name`, and the key that said so - the
18
+ * **last** `deps.targets` glob matching it, else `deps.target`.
19
+ */
20
+ /* Last rather than first, because that is how two `"[selector]"` blocks layer: what is written
21
+ * later wins. */
22
+ function targetFor(settings, name) {
23
+ let found;
24
+ for (const [glob, size] of Object.entries(settings.targets)) {
25
+ if (micromatch.isMatch(name, glob))
26
+ found = { size, from: `deps.targets["${glob}"]` };
27
+ }
28
+ return found ?? { size: settings.target, from: 'deps.target' };
29
+ }
30
+ DependencyUpdater.targetFor = targetFor;
31
+ /** The settings `pkg` is planned with: `options` from the command line over its `.rmanrc "deps"`,
32
+ * and the defaults under both. */
33
+ function settingsFor(pkg, options) {
34
+ const config = pkg.config.deps ?? {};
35
+ const bumps = pkg.versionScheme.bumpNames;
36
+ const target = options.target ?? config.target ?? defaultTarget(bumps);
37
+ if (!bumps.includes(target)) {
38
+ throw new Error(`Invalid "deps.target" for "${pkg.name}": "${target}" (expected one of: ${bumps.join(', ')}, ` +
39
+ `the "${pkg.versionScheme.name}" scheme's own sizes)`);
40
+ }
41
+ const targets = options.target ? {} : { ...(config.targets ?? {}) };
42
+ for (const [glob, size] of Object.entries(targets)) {
43
+ if (!bumps.includes(size)) {
44
+ throw new Error(`Invalid "deps.targets" entry "${glob}" for "${pkg.name}": "${size}" (expected one of: ${bumps.join(', ')})`);
45
+ }
46
+ }
47
+ return {
48
+ target,
49
+ targets,
50
+ reject: [...toList(config.reject), ...(options.reject ?? [])],
51
+ minAge: options.minAge ?? config.minAge ?? 0,
52
+ types: config.types?.length ? [...config.types] : undefined,
53
+ };
54
+ }
55
+ DependencyUpdater.settingsFor = settingsFor;
56
+ })(DependencyUpdater || (DependencyUpdater = {}));
57
+ function toList(value) {
58
+ if (value === undefined)
59
+ return [];
60
+ return Array.isArray(value) ? [...value] : [value];
61
+ }
@@ -53,7 +53,7 @@ export var Manifest;
53
53
  }
54
54
  }
55
55
  throw new Error(`No manifest provider recognizes "${dir}", so there is nowhere to write its version.\n` +
56
- ` A repository's ".rmanrc" names its technologies - see "plugins" (e.g. ['node']).`);
56
+ ` A repository's ".rmanrc" names its technologies - see "platforms", or "extends" (e.g. "rman:node").`);
57
57
  }
58
58
  Manifest.write = write;
59
59
  /**