rman 2.0.0-beta.2 → 2.0.0-beta.4
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.
- package/README.md +2 -12
- package/{plugins/node/augmentation/rman.augmentation.d.ts → builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts} +4 -5
- package/{plugins → builtins/platforms}/node/augmentation/system-info.augmentation.js +1 -1
- package/{plugins → builtins/platforms}/node/commands/ci.command.d.ts +5 -3
- package/{plugins → builtins/platforms}/node/commands/ci.command.js +6 -5
- package/{plugins → builtins/platforms}/node/commands/clean.command.d.ts +6 -4
- package/{plugins → builtins/platforms}/node/commands/clean.command.js +6 -5
- package/builtins/platforms/node/index.d.ts +8 -0
- package/builtins/platforms/node/index.js +8 -0
- package/{plugins → builtins/platforms}/node/node-config.interface.d.ts +1 -2
- package/{plugins → builtins/platforms}/node/node-manifest.provider.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/node-manifest.provider.js +3 -3
- package/{plugins → builtins/platforms}/node/node.platform.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/ci.service.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/ci.service.js +4 -4
- package/{plugins → builtins/platforms}/node/services/clean.service.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/clean.service.js +3 -3
- package/{plugins → builtins/platforms}/node/services/publish.service.d.ts +4 -4
- package/{plugins → builtins/platforms}/node/services/publish.service.js +13 -7
- package/{plugins → builtins/platforms}/node/services/version-plan.service.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/version-plan.service.js +2 -2
- package/builtins/presets/index.d.ts +15 -0
- package/builtins/presets/index.js +57 -0
- package/builtins/presets/node.d.ts +16 -0
- package/builtins/presets/node.js +57 -0
- package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +6 -6
- package/{services → builtins/publish-targets/docker}/docker-publish.service.js +9 -9
- package/{targets → builtins/publish-targets/docker}/docker.target.d.ts +6 -5
- package/{targets → builtins/publish-targets/docker}/docker.target.js +1 -1
- package/builtins/publish-targets/docker/index.d.ts +2 -0
- package/builtins/publish-targets/docker/index.js +2 -0
- package/builtins/publish-targets/npm/index.d.ts +2 -0
- package/builtins/publish-targets/npm/index.js +2 -0
- package/{plugins/node → builtins/publish-targets/npm}/npm-publish-target.d.ts +4 -4
- package/{plugins/node → builtins/publish-targets/npm}/npm-publish-target.js +4 -5
- package/cli.d.ts +3 -0
- package/cli.js +16 -7
- package/commands/changelog.command.d.ts +106 -2
- package/commands/changelog.command.js +40 -0
- package/commands/config.command.js +23 -5
- package/commands/list.command.js +1 -1
- package/commands/publish.command.d.ts +1 -1
- package/commands/publish.command.js +1 -1
- package/commands/run.command.js +1 -1
- package/commands/version.command.d.ts +16 -3
- package/commands/version.command.js +143 -9
- package/commands.d.ts +0 -1
- package/commands.js +0 -1
- package/constants.js +1 -1
- package/core/application.d.ts +10 -10
- package/core/application.js +28 -11
- package/core/{package.d.ts → classes/package.d.ts} +12 -4
- package/core/{package.js → classes/package.js} +17 -1
- package/core/{repository.d.ts → classes/repository.d.ts} +22 -36
- package/core/{repository.js → classes/repository.js} +112 -241
- package/core/{service.d.ts → classes/service.d.ts} +2 -2
- package/core/classes/workspace.d.ts +277 -0
- package/core/classes/workspace.js +585 -0
- package/core/command-builder.js +17 -1
- package/core/config/config-file-scope.d.ts +107 -0
- package/core/config/config-file-scope.js +226 -0
- package/core/config/config-interpolator.d.ts +164 -0
- package/core/config/config-interpolator.js +577 -0
- package/core/config/config-paths.d.ts +65 -0
- package/core/config/config-paths.js +78 -0
- package/core/config/config-reader.d.ts +215 -0
- package/core/config/config-reader.js +851 -0
- package/core/{merge-config.js → config/merge-config.js} +4 -8
- package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +5 -5
- package/core/{manifest.d.ts → interfaces/manifest.d.ts} +3 -3
- package/core/{manifest.js → interfaces/manifest.js} +2 -2
- package/core/{plugin.d.ts → interfaces/plugin.d.ts} +44 -27
- package/core/{plugin.js → interfaces/plugin.js} +48 -10
- package/core/{publish-target.d.ts → interfaces/publish-target.d.ts} +24 -12
- package/core/{publish-target.js → interfaces/publish-target.js} +16 -3
- package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
- package/core/plugin-loader.d.ts +11 -51
- package/core/plugin-loader.js +21 -202
- package/index.d.ts +29 -28
- package/index.js +20 -46
- package/interfaces/config-scope.interface.d.ts +223 -0
- package/interfaces/config-scope.interface.js +1 -0
- package/interfaces/rman-config.interface.d.ts +100 -5
- package/interfaces/rman-config.interface.js +17 -0
- package/package.json +3 -2
- package/services/change-hash.service.d.ts +40 -6
- package/services/change-hash.service.js +90 -11
- package/services/changelog.service.d.ts +31 -3
- package/services/changelog.service.js +448 -65
- package/services/config-service.d.ts +8 -0
- package/services/config-service.js +3 -0
- package/services/exec.service.d.ts +2 -2
- package/services/exec.service.js +1 -1
- package/services/github-release.service.d.ts +3 -3
- package/services/github-release.service.js +3 -3
- package/services/import.service.d.ts +2 -2
- package/services/import.service.js +1 -1
- package/services/list.service.d.ts +9 -7
- package/services/list.service.js +2 -2
- package/services/run.service.d.ts +15 -11
- package/services/run.service.js +62 -18
- package/services/system-info.d.ts +1 -1
- package/services/version-plan.service.d.ts +8 -4
- package/services/version-plan.service.js +54 -21
- package/services/version.service.d.ts +5 -5
- package/services/version.service.js +7 -4
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/utils/branch-guard.d.ts +1 -1
- package/utils/color-yaml.d.ts +8 -0
- package/utils/color-yaml.js +86 -0
- package/utils/git.d.ts +26 -0
- package/utils/git.js +65 -0
- package/utils/logger.d.ts +1 -1
- package/utils/package-filter.d.ts +8 -1
- package/utils/package-filter.js +7 -0
- package/utils/printable-config.d.ts +8 -0
- package/utils/printable-config.js +45 -0
- package/utils/progress-panel.d.ts +9 -0
- package/utils/progress-panel.js +23 -1
- package/utils/release-version.d.ts +1 -1
- package/utils/run-options.js +1 -1
- package/utils/version-group.d.ts +31 -0
- package/utils/version-group.js +43 -0
- package/commands/changed.command.d.ts +0 -80
- package/commands/changed.command.js +0 -36
- package/core/config.d.ts +0 -530
- package/core/config.js +0 -1123
- package/core/core-services.d.ts +0 -14
- package/core/core-services.js +0 -30
- package/core/core-targets.d.ts +0 -14
- package/core/core-targets.js +0 -16
- package/core/extends-config.d.ts +0 -35
- package/core/extends-config.js +0 -129
- package/core/load-config-module.d.ts +0 -28
- package/core/load-config-module.js +0 -42
- package/core/workspace.d.ts +0 -119
- package/core/workspace.js +0 -124
- package/plugins/builtins.d.ts +0 -44
- package/plugins/builtins.js +0 -33
- package/plugins/detect.d.ts +0 -78
- package/plugins/detect.js +0 -70
- package/plugins/node/index.d.ts +0 -29
- package/plugins/node/index.js +0 -40
- /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
- /package/{plugins → builtins/platforms}/node/augmentation/system-info.augmentation.d.ts +0 -0
- /package/{plugins → builtins/platforms}/node/node-config.interface.js +0 -0
- /package/{plugins → builtins/platforms}/node/node.platform.js +0 -0
- /package/{plugins → builtins/platforms}/node/utils/workspace-range.d.ts +0 -0
- /package/{plugins → builtins/platforms}/node/utils/workspace-range.js +0 -0
- /package/{plugins/node/utils → builtins/publish-targets/npm}/npm-view.d.ts +0 -0
- /package/{plugins/node/utils → builtins/publish-targets/npm}/npm-view.js +0 -0
- /package/core/{registry.d.ts → classes/registry.d.ts} +0 -0
- /package/core/{registry.js → classes/registry.js} +0 -0
- /package/core/{service.js → classes/service.js} +0 -0
- /package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +0 -0
- /package/core/{version-scheme.js → classes/version-scheme.js} +0 -0
- /package/core/{merge-config.d.ts → config/merge-config.d.ts} +0 -0
- /package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +0 -0
- /package/core/{resolve-target.js → config/resolve-config-target.js} +0 -0
- /package/core/{custom-command.js → interfaces/custom-command.js} +0 -0
- /package/{plugins/node/augmentation/rman.augmentation.js → core/interfaces/run-step.js} +0 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import colors from 'ansi-colors';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import semver from 'semver';
|
|
5
|
-
import {
|
|
6
|
-
import { GitHelper } from '
|
|
7
|
-
import { RmanApplication } from '
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
4
|
+
import {} from '../../interfaces/config-scope.interface.js';
|
|
5
|
+
import { GitHelper } from '../../utils/git.js';
|
|
6
|
+
import { RmanApplication } from '../application.js';
|
|
7
|
+
import { ConfigFileScope } from '../config/config-file-scope.js';
|
|
8
|
+
import { ConfigInterpolator } from '../config/config-interpolator.js';
|
|
9
|
+
import { DEFERRED_PATHS } from '../config/config-paths.js';
|
|
10
|
+
import { Manifest } from '../interfaces/manifest.js';
|
|
11
11
|
import { Package } from './package.js';
|
|
12
|
-
import { loadPlugins, registerPlugin } from './plugin-loader.js';
|
|
13
12
|
import { Workspace } from './workspace.js';
|
|
14
13
|
export class Repository extends Package {
|
|
15
14
|
dirname;
|
|
@@ -17,8 +16,6 @@ export class Repository extends Package {
|
|
|
17
16
|
packages;
|
|
18
17
|
cwd;
|
|
19
18
|
rootPackage;
|
|
20
|
-
/** See the `defineProperty` in the constructor - what detection supplied, when it did. */
|
|
21
|
-
detectedBuiltin;
|
|
22
19
|
/** Commands the repository's plugins contributed, loaded during `create` because the workspace
|
|
23
20
|
* providers they bring are needed before any package can be found. `cli.ts` registers them. */
|
|
24
21
|
/**
|
|
@@ -36,14 +33,15 @@ export class Repository extends Package {
|
|
|
36
33
|
* and because reading it is a subprocess: a deep walk of a package must not spawn one. */
|
|
37
34
|
_git;
|
|
38
35
|
/**
|
|
39
|
-
*
|
|
40
|
-
* of the bytes - see `readStructuredFile`.
|
|
36
|
+
* What `${{ file... }}` and `${{ read(...) }}` answer from, for every package.
|
|
41
37
|
*
|
|
42
|
-
* **
|
|
43
|
-
* is built once per package, so a cache living there would re-read a
|
|
44
|
-
* for every package that mentions it.
|
|
38
|
+
* **One per repository rather than one per scope, and that is the whole point of it**:
|
|
39
|
+
* `configScope` is built once per package, so a parse cache living there would re-read a
|
|
40
|
+
* repository-level file once for every package that mentions it. The cache inside is keyed by the
|
|
41
|
+
* identity of the bytes (`mtimeNs:size`), because rman writes JSON while it runs - `version`
|
|
42
|
+
* rewrites every bumped manifest and then re-interpolates its deferred hooks.
|
|
45
43
|
*/
|
|
46
|
-
|
|
44
|
+
_files = new ConfigFileScope();
|
|
47
45
|
/**
|
|
48
46
|
* The application this repository belongs to - its services, its technologies, its logger.
|
|
49
47
|
*
|
|
@@ -68,15 +66,6 @@ export class Repository extends Package {
|
|
|
68
66
|
this.packages = packages;
|
|
69
67
|
this.cwd = cwd;
|
|
70
68
|
Object.defineProperty(this, 'app', { value: app, enumerable: false, writable: false });
|
|
71
|
-
/**
|
|
72
|
-
* What detection decided for this repository, or `undefined` when it declared its own
|
|
73
|
-
* technology (or when there was nothing to detect). Carried here because `_resolveConfigs` runs
|
|
74
|
-
* later and every read of the root's config has to agree with the one decision `create` made.
|
|
75
|
-
*
|
|
76
|
-
* Non-enumerable, like `app` and for the same reason: `{...repository}` and `toEqual` both walk
|
|
77
|
-
* a repository, and bookkeeping that shows up there turns spec failures into diffs about it.
|
|
78
|
-
*/
|
|
79
|
-
Object.defineProperty(this, 'detectedBuiltin', { value: undefined, enumerable: false, writable: true });
|
|
80
69
|
this.rootPackage = new Package(dirname, app, platform);
|
|
81
70
|
if (!monorepo)
|
|
82
71
|
this.packages = [this.rootPackage];
|
|
@@ -170,10 +159,10 @@ export class Repository extends Package {
|
|
|
170
159
|
repository: this._repositoryScope(),
|
|
171
160
|
/** `pkg.dirname`, not the repository root: a `"[*]"` block asking whether
|
|
172
161
|
* `tsconfig-build.json` exists has to be answered per package. */
|
|
173
|
-
file:
|
|
162
|
+
file: this._files.fileScope(pkg.dirname),
|
|
174
163
|
/** Same base directory as `file`, so one `"[*]"` declaration reads each package's own copy -
|
|
175
164
|
* and the cache is the repository's, so a file they *share* is parsed once. */
|
|
176
|
-
read:
|
|
165
|
+
read: this._files.readScope(pkg.dirname),
|
|
177
166
|
env: { ...process.env },
|
|
178
167
|
semver,
|
|
179
168
|
path,
|
|
@@ -258,75 +247,25 @@ export class Repository extends Package {
|
|
|
258
247
|
Object.defineProperty(this, 'children', { value: this.rootPackage.children, enumerable: true });
|
|
259
248
|
}
|
|
260
249
|
/**
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* **Its own step, before any config is resolved by a selector**, which is the ordering that makes
|
|
265
|
-
* the rest work: `_resolveConfigs` asks `resolveConfig` for each package *by selector*, so an
|
|
266
|
-
* address assigned afterwards would be applied to nothing.
|
|
267
|
-
*
|
|
268
|
-
* **`name` comes from the unmarked cascade only** - the same read `platform` gets, from the same
|
|
269
|
-
* cache, for the same reason. A `"[glob]"` block cannot set it (`assertSelectorBlocks` refuses
|
|
270
|
-
* one) because the glob matches the very thing the block would be setting.
|
|
271
|
-
*
|
|
272
|
-
* **Uniqueness is checked, and the cascade is the mistake it usually catches.** `name` cascades
|
|
273
|
-
* like every unmarked key, so one declaration above two packages gives both the same address -
|
|
274
|
-
* and the failure would otherwise be silent in the worst way: the config reaches both and
|
|
275
|
-
* `getPackage` returns whichever came first. The message names both directories, and says the
|
|
276
|
-
* cascade out loud when the two got it from one declaration.
|
|
250
|
+
* Bakes every package's config: the raw one the workspace cascaded, with every `${{ }}` and value
|
|
251
|
+
* function resolved against that package's scope.
|
|
277
252
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
throw new Error(`"name" takes the selector this package answers to - "${pkg.dirname}" gave ${typeof declared}.`);
|
|
285
|
-
}
|
|
286
|
-
pkg.selector = declared?.trim() || pkg.platformSelector();
|
|
287
|
-
declaredBy.set(pkg, declared !== undefined);
|
|
288
|
-
}
|
|
289
|
-
/** The root is left out: a glob never matches it and `"[/]"` needs no name, so it shares an
|
|
290
|
-
* address with nobody - see `Package.selector`. */
|
|
291
|
-
const bySelector = new Map();
|
|
292
|
-
for (const pkg of this.packages) {
|
|
293
|
-
const clash = bySelector.get(pkg.selector);
|
|
294
|
-
if (clash) {
|
|
295
|
-
const cascaded = declaredBy.get(pkg) && declaredBy.get(clash);
|
|
296
|
-
throw new Error(`Two packages answer to the selector "${pkg.selector}":\n ${clash.dirname}\n ${pkg.dirname}\n` +
|
|
297
|
-
` A selector has to be unique - "[${pkg.selector}]" and \`--scope ${pkg.selector}\` ` +
|
|
298
|
-
`cannot mean two packages.` +
|
|
299
|
-
(cascaded
|
|
300
|
-
? `\n Both got it from one cascading "name" declaration above them; declare it in ` +
|
|
301
|
-
`each package's own ".rmanrc" instead.`
|
|
302
|
-
: ''));
|
|
303
|
-
}
|
|
304
|
-
bySelector.set(pkg.selector, pkg);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
253
|
+
/* **The one step the workspace deliberately stops short of.** Resolving an expression needs
|
|
254
|
+
* `pkg`, `repository`, `file` and `git` - which belong to a repository rather than to its layout,
|
|
255
|
+
* and `configScope` is where the measured subtleties of building that scope live.
|
|
256
|
+
*
|
|
257
|
+
* `DEFERRED_PATHS` are left raw, so the resolved config is the only copy anyone needs - `version`
|
|
258
|
+
* reads its own hooks straight off it and interpolates them once `pkg.targetVersion` exists. */
|
|
307
259
|
async _resolveConfigs() {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
* own `.rmanrc "name"` when it assigned one and its platform's answer otherwise. They coincide
|
|
318
|
-
* for every Node repository; they are not the same question, and `name` was the wrong one to
|
|
319
|
-
* ask, since a package having one at all is an ecosystem's promise rather than rman's.
|
|
320
|
-
*
|
|
321
|
-
* The root passes its own too, although no glob can match it: `"[/]"` is applied on the
|
|
322
|
-
* strength of the target *being* the root and needs no selector at all (see `resolveConfig`).
|
|
323
|
-
*/
|
|
324
|
-
const rootRaw = await resolveConfig(this.dirname, this.dirname, cache, this.rootPackage.selector, this.detectedBuiltin);
|
|
325
|
-
this.config = interpolateConfig(rootRaw, this.configScope(this.rootPackage), { skip: DEFERRED_PATHS });
|
|
326
|
-
for (const pkg of this.packages) {
|
|
327
|
-
const raw = await resolveConfig(this.dirname, pkg.dirname, cache, pkg.selector, this.detectedBuiltin);
|
|
328
|
-
pkg.config = interpolateConfig(raw, this.configScope(pkg), { skip: DEFERRED_PATHS });
|
|
329
|
-
}
|
|
260
|
+
const interpolator = new ConfigInterpolator();
|
|
261
|
+
const bake = (pkg) => interpolator.interpolate({
|
|
262
|
+
config: pkg.rawConfig,
|
|
263
|
+
scope: this.configScope(pkg),
|
|
264
|
+
skip: DEFERRED_PATHS,
|
|
265
|
+
});
|
|
266
|
+
this.config = bake(this.rootPackage);
|
|
267
|
+
for (const pkg of this.packages)
|
|
268
|
+
pkg.config = bake(pkg);
|
|
330
269
|
if (this.monorepo)
|
|
331
270
|
this.rootPackage.config = this.config;
|
|
332
271
|
}
|
|
@@ -496,171 +435,103 @@ export class Repository extends Package {
|
|
|
496
435
|
* 3. **Ask the providers** for the layout. None recognizing it means a repository that is itself
|
|
497
436
|
* the one package.
|
|
498
437
|
*
|
|
499
|
-
* **A repository
|
|
438
|
+
* **A repository no technology recognizes has no packages beyond itself**, and that is the
|
|
500
439
|
* boundary working rather than failing: `workspaces` in a `package.json` is npm's idea, so it
|
|
501
|
-
* takes
|
|
502
|
-
*
|
|
440
|
+
* takes a platform that reads one for the directory to be a workspace at all. rman's own presets
|
|
441
|
+
* are laid under every root (see `DEFAULT_PRESETS`), so a plain Node repository needs no config
|
|
442
|
+
* file to say so; anything else arrives through `extends` or `platforms`.
|
|
503
443
|
*/
|
|
444
|
+
/* **`presets` is an opt-out, and it exists for the specs that must not have one.** rman's own
|
|
445
|
+
* presets are laid under every root, so a caller that brought its own ecosystem would otherwise
|
|
446
|
+
* get the `node` technology, `clean`, `ci` and the npm publish target beside it. That is right
|
|
447
|
+
* for a repository and wrong for a synthetic one: measured, the core's `publish` specs ran a
|
|
448
|
+
* **real `npm publish` against registry.npmjs.org**, and only a 404 stopped it - the failure
|
|
449
|
+
* `useLocalBin`'s doc already records for `docker`, arriving by a new route. `test/_fixture.ts`
|
|
450
|
+
* passes `[]`. */
|
|
504
451
|
static async create(root, options) {
|
|
505
452
|
const from = root || process.cwd();
|
|
506
453
|
const rootDir = Workspace.findRoot(from, options?.deep ?? 10);
|
|
507
|
-
/**
|
|
508
|
-
* One application per repository, made here unless the caller brought one.
|
|
509
|
-
*
|
|
510
|
-
* `runCli` passes its own so that `--log-level` reaches the logger; a spec that only wants a
|
|
511
|
-
* repository lets this make one, which is also what keeps two repositories in a single process
|
|
512
|
-
* from sharing anything - the thing that used to need five `clear*()` calls before every test.
|
|
513
|
-
*/
|
|
514
454
|
const app = options?.app ?? new RmanApplication();
|
|
515
|
-
/** The root's own config, raw: `plugins` is a list of package names, so it needs neither the
|
|
516
|
-
* package list (which does not exist yet) nor expression interpolation. */
|
|
517
|
-
const declared = await readDirConfig(rootDir);
|
|
518
|
-
/**
|
|
519
|
-
* **What this repository looks like, when nothing said** - the other half of shipping the
|
|
520
|
-
* built-ins in the box. See `detectBuiltin`.
|
|
521
|
-
*
|
|
522
|
-
* **Two conditions, and the second is the one that is easy to miss.** The config declaring no
|
|
523
|
-
* `plugins` is not the same as the *repository* having no technology: a programmatic caller -
|
|
524
|
-
* and every spec in this suite - registers one straight onto the application without writing a
|
|
525
|
-
* config at all. Guessing on top of that registers a second technology, and the first provider
|
|
526
|
-
* that recognizes a directory decides whether it holds a package. Measured with only the config
|
|
527
|
-
* condition: ten specs changed answer, seven of them about config cascading and three about
|
|
528
|
-
* `publish`'s flags.
|
|
529
|
-
*
|
|
530
|
-
* **`platforms`, not `plugins`, and the difference is what detection produces.** What would be
|
|
531
|
-
* added here is a *platform*, so what must not already be there is a platform - a plugin that
|
|
532
|
-
* only registers a command says nothing about which directories hold packages, and letting it
|
|
533
|
-
* suppress the guess would leave a Node repository undetected for having added a command.
|
|
534
|
-
*
|
|
535
|
-
* **A root `platform` counts as having said something**, like `plugins: []` does. Detection
|
|
536
|
-
* exists for a repository that stated nothing; one naming its technology has stated the very
|
|
537
|
-
* thing detection would be guessing at, and guessing anyway would register a *second* platform
|
|
538
|
-
* beside the declared one - which then competes for every directory the declaration did not
|
|
539
|
-
* cover.
|
|
540
|
-
*
|
|
541
|
-
* Decided **once**, here, and handed to every read that has to agree - `readDirConfig` has no
|
|
542
|
-
* business knowing about an application.
|
|
543
|
-
*/
|
|
544
|
-
const saidSomething = declared.plugins !== undefined || declared.platform !== undefined;
|
|
545
|
-
const detected = !saidSomething && app.platforms.size === 0 ? await detectBuiltin(rootDir) : undefined;
|
|
546
|
-
const rootConfig = detected ? await readDirConfig(rootDir, { inject: detected }) : declared;
|
|
547
455
|
/**
|
|
548
|
-
* **
|
|
456
|
+
* **The whole of discovery and config reading is the workspace's.** It reads each level's
|
|
457
|
+
* config, loads whatever that level names, decides which technology claims each directory,
|
|
458
|
+
* assigns the selectors and cascades every package's **raw** config.
|
|
549
459
|
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
* one line of prose in front of it breaks every `| jq`. The same reason `--help`'s degradation
|
|
553
|
-
* notice goes to stderr. Not at `silent`, where the caller asked for no narration.
|
|
460
|
+
* Technologies already on the application are handed over: a programmatic caller - and every
|
|
461
|
+
* spec in this suite - registers one without writing a config, and the walk has to see it.
|
|
554
462
|
*/
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
`write \`plugins: ['${detected.name}']\` in .rmanrc to state it, or \`plugins: []\` for none.`;
|
|
558
|
-
console.error(process.stderr.isTTY ? colors.gray(line) : line);
|
|
559
|
-
}
|
|
560
|
-
await loadPlugins(app, rootConfig);
|
|
561
|
-
/**
|
|
562
|
-
* **Its own cache, deliberately not shared with `_resolveConfigs`'.**
|
|
563
|
-
*
|
|
564
|
-
* Both cache `readDirConfig` per directory, and the two reads of the *root* are not the same
|
|
565
|
-
* read: `_resolveConfigs` passes `detectedBuiltin` as `inject` and this one passes nothing,
|
|
566
|
-
* since a built-in's contribution has no bearing on which platform a directory declares. The
|
|
567
|
-
* cache is keyed by directory alone, so one shared map would hand whichever ran first to the
|
|
568
|
-
* other - and for the root that is a config with or without the whole node built-in merged in.
|
|
569
|
-
*
|
|
570
|
-
* The cost is one extra read per directory in the chain, on a repository that is about to read
|
|
571
|
-
* every one of them again per package anyway.
|
|
572
|
-
*/
|
|
573
|
-
const platformCache = new Map();
|
|
574
|
-
/**
|
|
575
|
-
* **The walk**, which is where the package list comes from now - descending from the root,
|
|
576
|
-
* asking each directory's own technology where its children are. `Workspace.resolve` asked the
|
|
577
|
-
* root once, through whichever platform recognized it first; the tree is the shape discovery
|
|
578
|
-
* actually has, and it is what makes a nested package of another technology findable at all.
|
|
579
|
-
*/
|
|
580
|
-
const tree = await Workspace.walk(app, rootDir, {
|
|
463
|
+
const workspace = await Workspace.create(rootDir, {
|
|
464
|
+
app,
|
|
581
465
|
deep: options?.deep,
|
|
582
|
-
|
|
466
|
+
platforms: [...app.platforms],
|
|
467
|
+
presets: options?.presets,
|
|
583
468
|
});
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
/**
|
|
592
|
-
*
|
|
593
|
-
|
|
469
|
+
/** What the configs named, onto the application the commands read from. */
|
|
470
|
+
for (const platform of workspace.platforms)
|
|
471
|
+
app.platforms.add(platform);
|
|
472
|
+
for (const plugin of workspace.plugins) {
|
|
473
|
+
app.plugins.add(plugin);
|
|
474
|
+
await plugin.afterInitApplication?.({ app });
|
|
475
|
+
}
|
|
476
|
+
/** One loop, because `workspace.platforms` already holds every technology in play whatever
|
|
477
|
+
* route it arrived by. There used to be a second one over each plugin's own `platforms`, and
|
|
478
|
+
* by the end it was pure duplication of this. */
|
|
479
|
+
for (const platform of workspace.platforms) {
|
|
480
|
+
if (platform.versionPlanner)
|
|
481
|
+
app.versionPlanner = platform.versionPlanner;
|
|
482
|
+
}
|
|
483
|
+
/** **Publish targets the root config declared.** `publish` builds its `--target` choices from
|
|
484
|
+
* `app.publishTargets` when its factory runs, so they have to be there before the commands
|
|
485
|
+
* are. Read off the root's own level rather than a package's: a target is the repository's. */
|
|
486
|
+
for (const target of toList(workspace.rootPackage.rawConfig.publishTargets)) {
|
|
487
|
+
if (!target || typeof target !== 'object' || typeof target.name !== 'string') {
|
|
488
|
+
throw new Error(`"publishTargets" takes a target or a glob naming modules that export one - got ` +
|
|
489
|
+
`${target === null ? 'null' : typeof target}.`);
|
|
490
|
+
}
|
|
491
|
+
app.publishTargets.add(target);
|
|
492
|
+
}
|
|
493
|
+
const packages = workspace.packages;
|
|
494
|
+
const repo = new Repository(app, rootDir, packages.length > 0, packages, from, workspace.rootPackage.platform);
|
|
495
|
+
repo._adopt(workspace);
|
|
594
496
|
app.attachRepository(repo);
|
|
595
497
|
return Repository._init(repo);
|
|
596
498
|
}
|
|
597
|
-
/**
|
|
598
|
-
*
|
|
599
|
-
*
|
|
499
|
+
/**
|
|
500
|
+
* Takes the workspace's packages as this repository's own - the back-references a `Package` needs
|
|
501
|
+
* and the parent/child edges the walk established.
|
|
502
|
+
*/
|
|
503
|
+
/* `Repository extends Package` while the workspace built its own root package for the same
|
|
504
|
+
* directory, so both are, truthfully, the root. This keeps the workspace's - it is the one the
|
|
505
|
+
* walk handed a platform and a selector - and leaves `Repository` itself pointing at the same
|
|
506
|
+
* children. */
|
|
507
|
+
_adopt(workspace) {
|
|
508
|
+
const link = (pkg) => void Object.defineProperty(pkg, 'repository', {
|
|
509
|
+
value: this,
|
|
510
|
+
enumerable: false,
|
|
511
|
+
configurable: true,
|
|
512
|
+
writable: true,
|
|
513
|
+
});
|
|
514
|
+
link(this);
|
|
515
|
+
this.rootPackage = workspace.rootPackage;
|
|
516
|
+
link(this.rootPackage);
|
|
517
|
+
for (const pkg of this.packages)
|
|
518
|
+
link(pkg);
|
|
519
|
+
if (!this.monorepo)
|
|
520
|
+
this.packages = [this.rootPackage];
|
|
521
|
+
Object.defineProperty(this, 'children', { value: this.rootPackage.children, enumerable: true });
|
|
522
|
+
}
|
|
600
523
|
static async _init(repo) {
|
|
601
524
|
await repo._resolveConfigs();
|
|
602
525
|
repo._updateDependencies();
|
|
526
|
+
/** **Last, and that is the whole point of the second stage.** Every package is found, every
|
|
527
|
+
* config is baked and the dependency graph is linked - so a plugin handed this repository can
|
|
528
|
+
* read it rather than being told to wait for a command it may not contribute. */
|
|
529
|
+
for (const plugin of repo.app.plugins)
|
|
530
|
+
await plugin.afterInitRepository?.({ app: repo.app, repository: repo });
|
|
603
531
|
return repo;
|
|
604
532
|
}
|
|
605
533
|
}
|
|
606
|
-
/**
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
*
|
|
610
|
-
* **The unmarked cascade only**, which is what `resolveConfig` gives with no package name: a
|
|
611
|
-
* selector matches a package *name*, and this runs while the packages are still being found - the
|
|
612
|
-
* name is not known yet, and it is read *from* a manifest whose reader this key decides. So
|
|
613
|
-
* `"[/]"` reaches the root (its directory is the repository root, which needs no name) and a glob
|
|
614
|
-
* block contributes nothing here, which is the honest answer rather than a half-applied one.
|
|
615
|
-
*/
|
|
616
|
-
async function declaredPlatformAt(app, rootDir, dir, cache) {
|
|
617
|
-
const config = await resolveConfig(rootDir, dir, cache);
|
|
618
|
-
const declared = config.platform;
|
|
619
|
-
if (declared === undefined)
|
|
620
|
-
return undefined;
|
|
621
|
-
if (typeof declared !== 'string' || !declared.trim()) {
|
|
622
|
-
throw new Error(`"platform" takes a platform's name - ${originOf(config)} gave ${typeof declared}.`);
|
|
623
|
-
}
|
|
624
|
-
/**
|
|
625
|
-
* **An expression is refused rather than read as a literal.** `platform` is consulted before any
|
|
626
|
-
* package exists - it is what decides what a package *is* - so there is no `pkg` for an
|
|
627
|
-
* expression to be about, and `interpolateConfig` runs long afterwards. Passed through, a
|
|
628
|
-
* `${{ }}` here reached the lookup below as the raw text and failed as an unknown platform name,
|
|
629
|
-
* which sends the reader to check their `plugins`.
|
|
630
|
-
*/
|
|
631
|
-
if (declared.includes('${{')) {
|
|
632
|
-
throw new Error(`"platform" cannot be an expression (${originOf(config)}) - it is read while ` +
|
|
633
|
-
`the packages are still being found, so there is no package for one to be about. Write the ` +
|
|
634
|
-
`name, and use a package's own ".rmanrc" where the answer differs.`);
|
|
635
|
-
}
|
|
636
|
-
const registered = [...app.platforms].find(p => p.name === declared);
|
|
637
|
-
if (registered)
|
|
638
|
-
return registered;
|
|
639
|
-
/**
|
|
640
|
-
* **A built-in is loaded on the strength of being named**, from any level - `platform: 'node'` is
|
|
641
|
-
* enough, and it is what a repository holding one Node package among others writes.
|
|
642
|
-
*
|
|
643
|
-
* `platform()` and not `contribute()`, and that split is why the two halves exist: what arrives
|
|
644
|
-
* is the technology alone. Commands and publish targets come from root `plugins`, because they
|
|
645
|
-
* are repository-wide - a Node package inside a Cargo repository wants npm's manifest read, not
|
|
646
|
-
* an `rman clean` that would sweep the whole tree.
|
|
647
|
-
*/
|
|
648
|
-
const { BUILTIN_PLUGINS, builtinPluginNames } = await import('../plugins/builtins.js');
|
|
649
|
-
const builtin = BUILTIN_PLUGINS[declared];
|
|
650
|
-
if (builtin) {
|
|
651
|
-
const platform = builtin.platform();
|
|
652
|
-
registerPlugin(app, platform);
|
|
653
|
-
return platform;
|
|
654
|
-
}
|
|
655
|
-
const have = [...app.platforms].map(p => p.name).filter(Boolean);
|
|
656
|
-
throw new Error(`"platform" names "${declared}" (${originOf(config)}), which is not a platform ` +
|
|
657
|
-
`this repository has. ${have.length ? `Registered: ${have.join(', ')}. ` : 'None is registered. '}` +
|
|
658
|
-
`rman ships ${builtinPluginNames().join(', ')}; anything else arrives through "plugins".`);
|
|
659
|
-
}
|
|
660
|
-
/** Which file the `platform` key came from, for an error that can be acted on - `mergeConfig`
|
|
661
|
-
* records one per key under `ORIGINS`, and a config is merged from a directory's own four forms,
|
|
662
|
-
* an `extends` base and one layer per directory before anything reads it. */
|
|
663
|
-
function originOf(config) {
|
|
664
|
-
const origins = config[ORIGINS];
|
|
665
|
-
return origins?.platform ? `in "${origins.platform}"` : 'the "platform" key';
|
|
534
|
+
/** A key that takes one value or a list of them, as a list. */
|
|
535
|
+
function toList(value) {
|
|
536
|
+
return value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
666
537
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Logger } from '
|
|
2
|
-
import type { RmanApplication } from '
|
|
1
|
+
import type { Logger } from '../../utils/logger.js';
|
|
2
|
+
import type { RmanApplication } from '../application.js';
|
|
3
3
|
import type { Repository } from './repository.js';
|
|
4
4
|
/**
|
|
5
5
|
* **The one service shape.** Everything that does work on a repository is a class extending this,
|