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.
- package/README.md +65 -31
- package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
- package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
- package/builtins/platforms/node/commands/ci.command.js +60 -0
- package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
- package/builtins/platforms/node/commands/clean.command.js +74 -0
- package/builtins/platforms/node/index.d.ts +8 -0
- package/builtins/platforms/node/index.js +8 -0
- package/builtins/platforms/node/node-config.interface.d.ts +76 -0
- package/builtins/platforms/node/node-config.interface.js +7 -0
- package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
- package/builtins/platforms/node/node-manifest.provider.js +125 -0
- package/builtins/platforms/node/node.platform.d.ts +53 -0
- package/builtins/platforms/node/node.platform.js +134 -0
- package/builtins/platforms/node/services/ci.service.d.ts +47 -0
- package/builtins/platforms/node/services/ci.service.js +213 -0
- package/builtins/platforms/node/services/clean.service.d.ts +53 -0
- package/builtins/platforms/node/services/clean.service.js +237 -0
- package/builtins/platforms/node/services/publish.service.d.ts +114 -0
- package/builtins/platforms/node/services/publish.service.js +371 -0
- package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
- package/builtins/platforms/node/services/version-plan.service.js +58 -0
- package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
- package/builtins/platforms/node/utils/workspace-range.js +28 -0
- 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 +52 -31
- package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
- package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
- package/builtins/publish-targets/docker/docker.target.js +40 -0
- 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/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
- package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
- package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
- package/builtins/publish-targets/npm/npm-view.js +71 -0
- package/cli.d.ts +8 -0
- package/cli.js +228 -93
- package/commands/build.command.d.ts +177 -3
- package/commands/build.command.js +20 -10
- package/commands/changelog.command.d.ts +296 -3
- package/commands/changelog.command.js +127 -43
- package/commands/config.command.d.ts +44 -3
- package/commands/config.command.js +53 -24
- package/commands/diff.command.d.ts +37 -3
- package/commands/diff.command.js +25 -16
- package/commands/exec.command.d.ts +193 -3
- package/commands/exec.command.js +60 -58
- package/commands/github-release.command.d.ts +154 -3
- package/commands/github-release.command.js +67 -37
- package/commands/import.command.d.ts +36 -3
- package/commands/import.command.js +28 -20
- package/commands/info.command.d.ts +35 -7
- package/commands/info.command.js +36 -30
- package/commands/lint.command.d.ts +176 -0
- package/commands/lint.command.js +34 -0
- package/commands/list.command.d.ts +163 -3
- package/commands/list.command.js +109 -71
- package/commands/publish.command.d.ts +231 -0
- package/commands/publish.command.js +304 -0
- package/commands/run.command.d.ts +186 -6
- package/commands/run.command.js +26 -72
- package/commands/test.command.d.ts +173 -3
- package/commands/test.command.js +16 -10
- package/commands/version.command.d.ts +330 -3
- package/commands/version.command.js +292 -78
- package/commands.d.ts +32 -0
- package/commands.js +28 -0
- package/constants.js +1 -1
- package/core/application.d.ts +116 -0
- package/core/application.js +160 -0
- package/core/classes/package.d.ts +219 -0
- package/core/classes/package.js +214 -0
- package/core/classes/registry.d.ts +30 -0
- package/core/classes/registry.js +47 -0
- package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
- package/core/{repository.js → classes/repository.js} +195 -75
- package/core/classes/service.d.ts +49 -0
- package/core/classes/service.js +40 -0
- package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
- package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
- package/core/classes/workspace.d.ts +275 -0
- package/core/classes/workspace.js +537 -0
- package/core/command-builder.d.ts +14 -0
- package/core/command-builder.js +94 -0
- 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 +192 -0
- package/core/config/config-reader.js +727 -0
- package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
- package/core/config/merge-config.js +260 -0
- package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
- package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
- package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
- package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
- package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
- package/core/interfaces/manifest.js +132 -0
- package/core/interfaces/plugin.d.ts +174 -0
- package/core/interfaces/plugin.js +124 -0
- package/core/interfaces/publish-target.d.ts +136 -0
- package/core/interfaces/publish-target.js +43 -0
- package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
- package/core/interfaces/run-step.js +1 -0
- package/core/plugin-loader.d.ts +25 -0
- package/core/plugin-loader.js +53 -0
- package/index.d.ts +122 -24
- package/index.js +66 -16
- package/interfaces/config-scope.interface.d.ts +223 -0
- package/interfaces/config-scope.interface.js +1 -0
- package/interfaces/rman-config.interface.d.ts +821 -203
- package/interfaces/rman-config.interface.js +78 -1
- package/package.json +3 -1
- package/services/change-hash.service.d.ts +42 -8
- package/services/change-hash.service.js +92 -13
- package/services/changelog.service.d.ts +91 -52
- package/services/changelog.service.js +460 -74
- package/services/config-service.d.ts +8 -0
- package/services/config-service.js +3 -0
- package/services/exec.service.d.ts +23 -12
- package/services/exec.service.js +14 -9
- package/services/github-release.service.d.ts +45 -34
- package/services/github-release.service.js +15 -12
- package/services/import.service.d.ts +25 -14
- package/services/import.service.js +9 -5
- package/services/list.service.d.ts +67 -13
- package/services/list.service.js +62 -15
- package/services/run.service.d.ts +35 -22
- package/services/run.service.js +316 -233
- package/services/system-info.d.ts +1 -1
- package/services/version-plan.service.d.ts +35 -8
- package/services/version-plan.service.js +96 -36
- package/services/version.service.d.ts +34 -14
- package/services/version.service.js +35 -16
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/utils/bin-path.d.ts +6 -7
- package/utils/bin-path.js +7 -18
- package/utils/branch-guard.d.ts +30 -1
- package/utils/branch-guard.js +31 -0
- package/utils/color-yaml.d.ts +8 -0
- package/utils/color-yaml.js +86 -0
- package/utils/exec.d.ts +10 -0
- package/utils/exec.js +1 -1
- package/utils/git.d.ts +26 -0
- package/utils/git.js +65 -0
- package/utils/logger.d.ts +2 -2
- package/utils/logger.js +1 -1
- package/utils/package-filter.d.ts +135 -8
- package/utils/package-filter.js +204 -16
- package/utils/printable-config.d.ts +9 -1
- package/utils/printable-config.js +46 -1
- 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-bin.d.ts +10 -0
- package/utils/run-bin.js +1 -1
- package/utils/run-options.d.ts +97 -0
- package/utils/run-options.js +81 -0
- package/utils/version-group.d.ts +31 -0
- package/utils/version-group.js +43 -0
- package/utils/version-stamp.d.ts +1 -1
- package/utils/version-stamp.js +1 -1
- package/commands/changed.command.d.ts +0 -3
- package/commands/changed.command.js +0 -29
- package/core/config.d.ts +0 -409
- package/core/config.js +0 -939
- package/core/extends-config.d.ts +0 -27
- package/core/extends-config.js +0 -89
- package/core/manifest.js +0 -150
- package/core/merge-config.js +0 -219
- package/core/package.d.ts +0 -83
- package/core/package.js +0 -106
- package/core/plugin.d.ts +0 -112
- package/core/plugin.js +0 -189
- package/core/workspace.d.ts +0 -68
- package/core/workspace.js +0 -83
- /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
package/core/extends-config.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { RmanConfig } from '../interfaces/rman-config.interface.js';
|
|
2
|
-
/** The key naming configs to inherit from, the way eslint and tsconfig spell it. */
|
|
3
|
-
export declare const EXTENDS_KEY = "extends";
|
|
4
|
-
/**
|
|
5
|
-
* Resolves `config`'s own `extends` into it: every named config is loaded, merged in declaration
|
|
6
|
-
* order, and `config`'s own keys land on top. Returns a new object with no `extends` left in it.
|
|
7
|
-
*
|
|
8
|
-
* The point is a shared package - `extends: "@panates/rman-monorepo"` - so a repository declares
|
|
9
|
-
* its house rules once instead of restating them. `+key` is what makes that liveable (see
|
|
10
|
-
* `mergeConfig`): without it, adding one step to a base's list means copying the list.
|
|
11
|
-
*
|
|
12
|
-
* `from` is the file the `extends` was written in, and everything resolves relative to **it**: a
|
|
13
|
-
* bare specifier through that file's own `node_modules`, a relative path against its directory.
|
|
14
|
-
* Resolving from rman's own location instead would look in rman's dependencies, where a
|
|
15
|
-
* repository's shared config has no reason to be.
|
|
16
|
-
*
|
|
17
|
-
* `seen` carries the chain being resolved, so a config that extends its way back to itself is
|
|
18
|
-
* reported rather than recursed into forever.
|
|
19
|
-
*/
|
|
20
|
-
export declare function resolveExtends(config: RmanConfig, from: string, seen?: string[]): Promise<RmanConfig>;
|
|
21
|
-
/**
|
|
22
|
-
* Refuses `extends` inside a `"[selector]"` block. A selector block is typed as a whole
|
|
23
|
-
* `RmanConfig`, so writing one there looks valid and would simply never be resolved - and a config
|
|
24
|
-
* that quietly does nothing is worse than one that won't load. Inheritance is a statement about
|
|
25
|
-
* the file, not about the packages it happens to name.
|
|
26
|
-
*/
|
|
27
|
-
export declare function assertNoSelectorExtends(config: RmanConfig, file: string): void;
|
package/core/extends-config.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
4
|
-
import * as yaml from 'js-yaml';
|
|
5
|
-
import { isSelectorKey } from './config.js';
|
|
6
|
-
import { mergeConfig } from './merge-config.js';
|
|
7
|
-
import { resolveConfigTarget } from './resolve-target.js';
|
|
8
|
-
/** The key naming configs to inherit from, the way eslint and tsconfig spell it. */
|
|
9
|
-
export const EXTENDS_KEY = 'extends';
|
|
10
|
-
/**
|
|
11
|
-
* Resolves `config`'s own `extends` into it: every named config is loaded, merged in declaration
|
|
12
|
-
* order, and `config`'s own keys land on top. Returns a new object with no `extends` left in it.
|
|
13
|
-
*
|
|
14
|
-
* The point is a shared package - `extends: "@panates/rman-monorepo"` - so a repository declares
|
|
15
|
-
* its house rules once instead of restating them. `+key` is what makes that liveable (see
|
|
16
|
-
* `mergeConfig`): without it, adding one step to a base's list means copying the list.
|
|
17
|
-
*
|
|
18
|
-
* `from` is the file the `extends` was written in, and everything resolves relative to **it**: a
|
|
19
|
-
* bare specifier through that file's own `node_modules`, a relative path against its directory.
|
|
20
|
-
* Resolving from rman's own location instead would look in rman's dependencies, where a
|
|
21
|
-
* repository's shared config has no reason to be.
|
|
22
|
-
*
|
|
23
|
-
* `seen` carries the chain being resolved, so a config that extends its way back to itself is
|
|
24
|
-
* reported rather than recursed into forever.
|
|
25
|
-
*/
|
|
26
|
-
export async function resolveExtends(config, from, seen = []) {
|
|
27
|
-
const declared = config[EXTENDS_KEY];
|
|
28
|
-
if (declared === undefined)
|
|
29
|
-
return config;
|
|
30
|
-
const targets = Array.isArray(declared) ? declared : [declared];
|
|
31
|
-
for (const target of targets) {
|
|
32
|
-
if (typeof target !== 'string' || !target.trim()) {
|
|
33
|
-
throw new Error(`"extends" in "${from}" must be a config name or path, or an array of them`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const base = {};
|
|
37
|
-
for (const target of targets) {
|
|
38
|
-
const file = resolveConfigTarget(target, from, EXTENDS_KEY);
|
|
39
|
-
if (seen.includes(file)) {
|
|
40
|
-
throw new Error(`"extends" forms a cycle: ${[...seen, file].map(f => path.basename(f)).join(' -> ')}`);
|
|
41
|
-
}
|
|
42
|
-
const loaded = await loadConfigFile(file);
|
|
43
|
-
assertNoSelectorExtends(loaded, file);
|
|
44
|
-
// Recursive: a shared config may itself be built on another.
|
|
45
|
-
mergeConfig(base, await resolveExtends(loaded, file, [...seen, file]), file);
|
|
46
|
-
}
|
|
47
|
-
const own = { ...config };
|
|
48
|
-
delete own[EXTENDS_KEY];
|
|
49
|
-
return mergeConfig(base, own);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Refuses `extends` inside a `"[selector]"` block. A selector block is typed as a whole
|
|
53
|
-
* `RmanConfig`, so writing one there looks valid and would simply never be resolved - and a config
|
|
54
|
-
* that quietly does nothing is worse than one that won't load. Inheritance is a statement about
|
|
55
|
-
* the file, not about the packages it happens to name.
|
|
56
|
-
*/
|
|
57
|
-
export function assertNoSelectorExtends(config, file) {
|
|
58
|
-
for (const [key, value] of Object.entries(config)) {
|
|
59
|
-
if (!isSelectorKey(key) || !value || typeof value !== 'object')
|
|
60
|
-
continue;
|
|
61
|
-
if (EXTENDS_KEY in value) {
|
|
62
|
-
throw new Error(`"${key}" in "${file}" cannot use "extends" - it belongs at the top level, where it is a ` +
|
|
63
|
-
`statement about this config rather than about the packages the selector names.`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/** Loads one resolved target. YAML and JSON are read directly; anything else goes through the
|
|
68
|
-
* module loader, so a shared config can be a `defineConfig` module with real logic in it. */
|
|
69
|
-
async function loadConfigFile(file) {
|
|
70
|
-
const ext = path.extname(file);
|
|
71
|
-
if (ext === '.yml' || ext === '.yaml') {
|
|
72
|
-
const obj = yaml.load(fs.readFileSync(file, 'utf-8'));
|
|
73
|
-
return asConfig(obj, file);
|
|
74
|
-
}
|
|
75
|
-
if (ext === '.json')
|
|
76
|
-
return asConfig(JSON.parse(fs.readFileSync(file, 'utf-8')), file);
|
|
77
|
-
const mod = await import(pathToFileURL(file).href);
|
|
78
|
-
return asConfig(mod?.default ?? mod, file);
|
|
79
|
-
}
|
|
80
|
-
function asConfig(value, file) {
|
|
81
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
82
|
-
throw new Error(`"${file}" does not export an rman config object`);
|
|
83
|
-
}
|
|
84
|
-
const config = { ...value };
|
|
85
|
-
// Editor tooling only - meaningless once merged, and `additionalProperties` would reject it
|
|
86
|
-
// wherever it ended up.
|
|
87
|
-
delete config.$schema;
|
|
88
|
-
return config;
|
|
89
|
-
}
|
package/core/manifest.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { semverScheme } from './version-scheme.js';
|
|
3
|
-
/**
|
|
4
|
-
* The registry, merged onto the `Manifest` interface so one name carries both the shape and the
|
|
5
|
-
* operations - `Manifest.read(dir)` returns a `Manifest`.
|
|
6
|
-
*
|
|
7
|
-
* A namespace rather than loose `addManifestProvider`/`readManifest` functions because a namespace
|
|
8
|
-
* is what a plugin can *augment*: `declare module 'rman' { namespace Manifest { ... } }` is how
|
|
9
|
-
* `rman-node` already adds to `SystemInfo`, and anything this seam grows later can arrive the
|
|
10
|
-
* same way instead of as another top-level export.
|
|
11
|
-
*/
|
|
12
|
-
export var Manifest;
|
|
13
|
-
(function (Manifest) {
|
|
14
|
-
/** Registers a provider. Called by `loadPlugins` for each plugin's `manifest`, in `plugins`
|
|
15
|
-
* declaration order - so which one answers is a function of the repository's own config. */
|
|
16
|
-
function addProvider(provider) {
|
|
17
|
-
if (providers.includes(provider))
|
|
18
|
-
return;
|
|
19
|
-
providers.push(provider);
|
|
20
|
-
}
|
|
21
|
-
Manifest.addProvider = addProvider;
|
|
22
|
-
/** For tests, which would otherwise leak a provider into every later case in the process. */
|
|
23
|
-
function clearProviders() {
|
|
24
|
-
providers.length = 0;
|
|
25
|
-
}
|
|
26
|
-
Manifest.clearProviders = clearProviders;
|
|
27
|
-
/**
|
|
28
|
-
* Reads `dir`'s manifest through the first provider that recognizes it, with the scheme that
|
|
29
|
-
* provider brings.
|
|
30
|
-
*
|
|
31
|
-
* **With no provider registered, or none recognizing the directory**, the fallback is a package
|
|
32
|
-
* named after its own directory at version `0.0.0`. That is deliberately the least it can claim:
|
|
33
|
-
* the directory name is a fact, and `0.0.0` is the version a thing has when nothing says
|
|
34
|
-
* otherwise. The alternative - refusing to construct a package at all - would make `rman info` and
|
|
35
|
-
* `rman list` fail in a repository whose `.rmanrc` simply names no plugin yet, which is exactly
|
|
36
|
-
* when someone needs to run them.
|
|
37
|
-
*/
|
|
38
|
-
function read(dir) {
|
|
39
|
-
for (const provider of providers) {
|
|
40
|
-
const manifest = provider.read(dir);
|
|
41
|
-
if (manifest) {
|
|
42
|
-
return {
|
|
43
|
-
manifest,
|
|
44
|
-
versionScheme: provider.versionScheme ?? semverScheme,
|
|
45
|
-
fileName: provider.fileName,
|
|
46
|
-
provider: provider.name,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
manifest: { name: path.basename(dir), version: '0.0.0', raw: {} },
|
|
52
|
-
versionScheme: semverScheme,
|
|
53
|
-
/** Nothing was read, so nothing can be named - a caller listing "the file I changed" has no
|
|
54
|
-
* file to list, which is correct rather than a placeholder that does not exist. */
|
|
55
|
-
fileName: '',
|
|
56
|
-
/** Same reasoning: no provider claimed this directory, so it belongs to no ecosystem. Empty
|
|
57
|
-
* rather than a sentinel like `'unknown'`, which would read as an ecosystem's name and could
|
|
58
|
-
* collide with a real provider's. */
|
|
59
|
-
provider: '',
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
Manifest.read = read;
|
|
63
|
-
/** Writes through whichever provider recognizes `dir`. Throws when none does: a write that lands
|
|
64
|
-
* nowhere is worse than one that fails, since the caller has already decided the new version. */
|
|
65
|
-
function write(dir, manifest) {
|
|
66
|
-
for (const provider of providers) {
|
|
67
|
-
if (provider.read(dir)) {
|
|
68
|
-
provider.write(dir, manifest);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
throw new Error(`No manifest provider recognizes "${dir}", so there is nowhere to write its version.\n` +
|
|
73
|
-
` A repository's ".rmanrc" names its providers - see "plugins" (e.g. ['rman-node']).`);
|
|
74
|
-
}
|
|
75
|
-
Manifest.write = write;
|
|
76
|
-
/**
|
|
77
|
-
* The packages `pkg` declares a dependency on, via whichever provider recognizes it.
|
|
78
|
-
*
|
|
79
|
-
* No provider, or one with no opinion, means no declared dependencies - `.rmanrc "dependencies"`
|
|
80
|
-
* is then the only source, which is exactly right: a repository rman cannot read the manifests
|
|
81
|
-
* of can still describe its own graph by hand.
|
|
82
|
-
*/
|
|
83
|
-
function dependenciesOf(pkg, candidates) {
|
|
84
|
-
return providerOf(pkg)?.dependencies?.(pkg.manifest, candidates) ?? [];
|
|
85
|
-
}
|
|
86
|
-
Manifest.dependenciesOf = dependenciesOf;
|
|
87
|
-
/** `${{ pkg.scope }}`/`${{ pkg.unscopedName }}`, by whichever provider recognizes `dir` - and
|
|
88
|
-
* "no scope, the name is its own unscoped form" when none has an opinion. */
|
|
89
|
-
function splitName(dir, name) {
|
|
90
|
-
for (const provider of providers) {
|
|
91
|
-
if (!provider.read(dir))
|
|
92
|
-
continue;
|
|
93
|
-
return provider.splitName?.(name) ?? { unscopedName: name };
|
|
94
|
-
}
|
|
95
|
-
return { unscopedName: name };
|
|
96
|
-
}
|
|
97
|
-
Manifest.splitName = splitName;
|
|
98
|
-
/**
|
|
99
|
-
* Refreshes `pkg`'s references to the packages in `bumped`, via whichever provider recognizes it.
|
|
100
|
-
*
|
|
101
|
-
* No provider, or one without an opinion, means nothing to rewrite - a repository whose packages
|
|
102
|
-
* reference each other by path has nothing here to go stale.
|
|
103
|
-
*/
|
|
104
|
-
function updateDependencyVersions(pkg, bumped) {
|
|
105
|
-
providerOf(pkg)?.updateDependencyVersions?.(pkg.manifest, bumped);
|
|
106
|
-
}
|
|
107
|
-
Manifest.updateDependencyVersions = updateDependencyVersions;
|
|
108
|
-
/**
|
|
109
|
-
* Rewrites a hard-coded version in `content` through `pkg`'s own ecosystem - see
|
|
110
|
-
* `ManifestProvider.stampVersion`.
|
|
111
|
-
*
|
|
112
|
-
* `undefined` covers three cases the caller has to tell apart from each other, and cannot: no
|
|
113
|
-
* provider claimed the package, the provider has no opinion about stamping, or it looked and found
|
|
114
|
-
* nothing to change. All three mean "this file was not stamped", which is what `version` reports.
|
|
115
|
-
*/
|
|
116
|
-
function stampVersion(pkg, file, content, version, options) {
|
|
117
|
-
return providerOf(pkg)?.stampVersion?.(file, content, version, options);
|
|
118
|
-
}
|
|
119
|
-
Manifest.stampVersion = stampVersion;
|
|
120
|
-
/**
|
|
121
|
-
* What `pkg`'s own ecosystem's registry says its current version is - see
|
|
122
|
-
* `ManifestProvider.publishedVersion` for the one thing this is for and the one thing it must
|
|
123
|
-
* never be used for.
|
|
124
|
-
*
|
|
125
|
-
* `undefined` when the provider has no opinion, which includes every repository that names no
|
|
126
|
-
* plugin: git tags then answer the boundary question alone.
|
|
127
|
-
*/
|
|
128
|
-
async function publishedVersion(pkg) {
|
|
129
|
-
return providerOf(pkg)?.publishedVersion?.(pkg);
|
|
130
|
-
}
|
|
131
|
-
Manifest.publishedVersion = publishedVersion;
|
|
132
|
-
/** The file names providers look for, for an error message that can say what was expected. */
|
|
133
|
-
function fileNames() {
|
|
134
|
-
return providers.map(p => p.fileName);
|
|
135
|
-
}
|
|
136
|
-
Manifest.fileNames = fileNames;
|
|
137
|
-
const providers = [];
|
|
138
|
-
/**
|
|
139
|
-
* The provider that claimed `pkg`, found by the name it reported as `pkg.provider` - exact, and
|
|
140
|
-
* without re-reading the manifest off disk to work it out again.
|
|
141
|
-
*
|
|
142
|
-
* The probe is the fallback, not the rule: it covers a package constructed *before* its provider
|
|
143
|
-
* was registered, which `Repository.create` cannot produce (plugins load first) but a test
|
|
144
|
-
* arranging providers by hand can.
|
|
145
|
-
*/
|
|
146
|
-
function providerOf(pkg) {
|
|
147
|
-
const byName = pkg.provider ? providers.find(p => p.name === pkg.provider) : undefined;
|
|
148
|
-
return byName ?? providers.find(p => p.read(pkg.dirname));
|
|
149
|
-
}
|
|
150
|
-
})(Manifest || (Manifest = {}));
|
package/core/merge-config.js
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
/** The prefix that turns a key into an append instead of a replacement: `+before` adds to whatever
|
|
2
|
-
* `before` already resolved to, rather than taking its place. */
|
|
3
|
-
export const APPEND_PREFIX = '+';
|
|
4
|
-
/**
|
|
5
|
-
* Keys that **append whether or not you ask** - `+plugins` is accepted and means nothing extra.
|
|
6
|
-
*
|
|
7
|
-
* `plugins` is the whole list because it is the one key where replacing is never what anyone meant:
|
|
8
|
-
* every other setting has a value a closer layer can sensibly overrule, while a plugin *adds
|
|
9
|
-
* commands and seams*, and a repository naming one has no wish to lose the ones its shared config
|
|
10
|
-
* brought. Replacing was the silent failure - `extends`-ing a toolchain config and then adding a
|
|
11
|
-
* plugin of your own dropped the toolchain's, and what you noticed was `Unknown argument: publish`.
|
|
12
|
-
*
|
|
13
|
-
* Do not extend this list casually: a key that always appends can never be *un*-said by a closer
|
|
14
|
-
* layer, which is only acceptable where the value is a set of contributions rather than a decision.
|
|
15
|
-
*/
|
|
16
|
-
export const ALWAYS_APPEND = ['plugins'];
|
|
17
|
-
/**
|
|
18
|
-
* Where a key keeps what it is replacing, so the replacement can be handed it back as `value`.
|
|
19
|
-
*
|
|
20
|
-
* A **symbol-keyed chain on the containing object**, one entry per key, rather than something
|
|
21
|
-
* attached to the value itself. It started as a wrapper around a *function*, which is the only kind
|
|
22
|
-
* of value you can hang a property on - and that is exactly why it had to change: `value` belongs to
|
|
23
|
-
* an expression (`"${{ [...value, 'x'] }}"`) just as much as to a function, and a string cannot
|
|
24
|
-
* carry one.
|
|
25
|
-
*
|
|
26
|
-
* A symbol is invisible to `Object.entries`, `JSON.stringify` and js-yaml, so the chain travels
|
|
27
|
-
* through `mergeConfig`, `finalizeConfig` and `rman config` without any of them having to know it
|
|
28
|
-
* is there.
|
|
29
|
-
*
|
|
30
|
-
* Each entry is a link, not a single slot: three layers each deriving from the one below need
|
|
31
|
-
* `A <- expr2 <- expr3`, and one slot would have lost `A` the moment `expr3` arrived.
|
|
32
|
-
*/
|
|
33
|
-
export const PREVIOUS_VALUES = Symbol('rman.previousValues');
|
|
34
|
-
/**
|
|
35
|
-
* Which file each key came from, so an error can name it.
|
|
36
|
-
*
|
|
37
|
-
* A config is merged from several files before anything reads it - a directory's own four forms, an
|
|
38
|
-
* `extends` base, every `"[selector]"` block, and one layer per directory from the root down - so by
|
|
39
|
-
* the time an expression fails, `version.commitMessage` could have been written in any of them.
|
|
40
|
-
* Saying only the key sends the reader looking through all of them.
|
|
41
|
-
*
|
|
42
|
-
* Recorded the same way `PREVIOUS_VALUES` is, for the same reason: a symbol on the containing
|
|
43
|
-
* object, invisible to `Object.entries`, `JSON.stringify` and js-yaml, so it travels with the config
|
|
44
|
-
* without any reader having to know it is there.
|
|
45
|
-
*/
|
|
46
|
-
export const ORIGINS = Symbol('rman.origins');
|
|
47
|
-
/** Only these two can ask for `value`, so only these two are worth remembering a previous for. */
|
|
48
|
-
export function carriesPreviousValue(value) {
|
|
49
|
-
return typeof value === 'function' || (typeof value === 'string' && value.includes('${{'));
|
|
50
|
-
}
|
|
51
|
-
/** `"+before"` -> `"before"`, or `undefined` for a key that isn't an append. */
|
|
52
|
-
export function appendTarget(key) {
|
|
53
|
-
return key.length > APPEND_PREFIX.length && key.startsWith(APPEND_PREFIX)
|
|
54
|
-
? key.slice(APPEND_PREFIX.length)
|
|
55
|
-
: undefined;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Merges `source` onto `target` in place, the way every layer of rman config is combined - a
|
|
59
|
-
* directory's own file forms, the directory chain, `"[selector]"` blocks, and an `extends` base.
|
|
60
|
-
* One implementation for all of them, so they cannot disagree about what an append means.
|
|
61
|
-
*
|
|
62
|
-
* Plain keys replace, and nested objects merge recursively, as a deep merge always has. What this
|
|
63
|
-
* adds is `+key`:
|
|
64
|
-
*
|
|
65
|
-
* ```yaml
|
|
66
|
-
* # the root says before: "rm ./build"
|
|
67
|
-
* # a package adds +before: "rm ./cache"
|
|
68
|
-
* # it resolves to before: ["rm ./build", "rm ./cache"]
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* Appending is the half a shared config can't live without: a base that declares
|
|
72
|
-
* `before: ["rm ./build"]` otherwise forces every repository wanting one more step to restate the
|
|
73
|
-
* whole list - which is a copy of the base, silently frozen at the version it was copied from.
|
|
74
|
-
*
|
|
75
|
-
* A scalar is promoted to a list on the way, so neither side has to be written as an array. With
|
|
76
|
-
* nothing inherited, `+key` simply produces a one-element list.
|
|
77
|
-
*
|
|
78
|
-
* On a value that isn't a list, the prefix is **ignored** rather than an error, because there the
|
|
79
|
-
* two spellings already mean the same thing: objects merge whether or not you asked them to, and a
|
|
80
|
-
* scalar has nothing to append to, so `+key` behaves exactly as `key` would. Nothing is silently
|
|
81
|
-
* dropped - the value still lands.
|
|
82
|
-
*
|
|
83
|
-
* `key` and `+key` in the same object are both honored, in that order: the replacement happens
|
|
84
|
-
* first, then the append lands on top of it.
|
|
85
|
-
*/
|
|
86
|
-
export function mergeConfig(target, source,
|
|
87
|
-
/** The file `source` was read from, recorded per key - see `ORIGINS`. A caller merging a value it
|
|
88
|
-
* built rather than read (a selector block already carrying its own origins) passes nothing. */
|
|
89
|
-
origin) {
|
|
90
|
-
// Plain keys first, so a `+key` alongside its own `key` appends to that replacement rather than
|
|
91
|
-
// to whatever the previous layer had.
|
|
92
|
-
for (const [key, value] of Object.entries(source)) {
|
|
93
|
-
if (appendTarget(key))
|
|
94
|
-
continue;
|
|
95
|
-
/** `plugins`: additive at every layer, so the closer one adds rather than takes over. */
|
|
96
|
-
if (ALWAYS_APPEND.includes(key)) {
|
|
97
|
-
appendList(target, key, value);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
assignMerged(target, key, value, source, origin);
|
|
101
|
-
}
|
|
102
|
-
for (const [key, value] of Object.entries(source)) {
|
|
103
|
-
const plain = appendTarget(key);
|
|
104
|
-
if (!plain)
|
|
105
|
-
continue;
|
|
106
|
-
// An object merges either way, so the prefix asks for nothing extra - resolve it now and let
|
|
107
|
-
// the two spellings coincide.
|
|
108
|
-
if (isPlainObject(value) || isPlainObject(target[plain])) {
|
|
109
|
-
assignMerged(target, plain, value, source, origin);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (plain in target) {
|
|
113
|
-
appendList(target, plain, value);
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
/** Nothing to append to *yet*. Kept as an append rather than collapsed into the plain key,
|
|
117
|
-
* because the layer that provides it may still be coming: a directory's own file forms are
|
|
118
|
-
* merged into an empty object long before the selector blocks and parent directories they
|
|
119
|
-
* append to are. Collapsing here lost both of those - measured. `finalizeConfig` turns
|
|
120
|
-
* whatever is still outstanding at the end into a plain list. */
|
|
121
|
-
target[key] = [...toList(target[key] ?? []), ...toList(value)];
|
|
122
|
-
}
|
|
123
|
-
return target;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Turns any `+key` still outstanding into its plain key, as a list - the "nothing was inherited"
|
|
127
|
-
* case, where an append simply is the whole value.
|
|
128
|
-
*
|
|
129
|
-
* Called once on a fully resolved config, and only there: until then an outstanding append may
|
|
130
|
-
* still find the layer it belongs to, and a config handed to a command must carry no `+key` at all
|
|
131
|
-
* or every reader would have to know about them.
|
|
132
|
-
*/
|
|
133
|
-
export function finalizeConfig(config) {
|
|
134
|
-
if (Array.isArray(config))
|
|
135
|
-
return config.map(finalizeConfig);
|
|
136
|
-
if (!isPlainObject(config))
|
|
137
|
-
return config;
|
|
138
|
-
const result = {};
|
|
139
|
-
for (const [key, value] of Object.entries(config)) {
|
|
140
|
-
const plain = appendTarget(key);
|
|
141
|
-
if (!plain) {
|
|
142
|
-
result[key] = finalizeConfig(value);
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
const pending = finalizeConfig(value);
|
|
146
|
-
result[plain] = plain in result ? [...toList(result[plain]), ...toList(pending)] : toList(pending);
|
|
147
|
-
}
|
|
148
|
-
/** Carried across by hand: this rebuilds the object from `Object.entries`, which does not see a
|
|
149
|
-
* symbol - and dropping it here would lose every `value` chain the merge just recorded. */
|
|
150
|
-
for (const carried of [PREVIOUS_VALUES, ORIGINS]) {
|
|
151
|
-
const value = config[carried];
|
|
152
|
-
if (value)
|
|
153
|
-
Object.defineProperty(result, carried, { value });
|
|
154
|
-
}
|
|
155
|
-
return result;
|
|
156
|
-
}
|
|
157
|
-
function assignMerged(target, key, value, source, origin) {
|
|
158
|
-
/** A source that already carries origins wins over the caller's: an `extends` base keeps the file
|
|
159
|
-
* its own keys were written in, rather than being attributed to the file that named it. */
|
|
160
|
-
const from = source[ORIGINS]?.[key] ?? origin;
|
|
161
|
-
if (from !== undefined) {
|
|
162
|
-
const carrier = target;
|
|
163
|
-
/** **Non-enumerable**, like `PREVIOUS_VALUES`: `expect`'s `toEqual` compares symbol properties,
|
|
164
|
-
* so a plain assignment turned every config-shape assertion in the suite into a diff about
|
|
165
|
-
* bookkeeping (measured, five specs at once). Nothing should see this but the error messages. */
|
|
166
|
-
if (!carrier[ORIGINS])
|
|
167
|
-
Object.defineProperty(target, ORIGINS, { value: {}, writable: true });
|
|
168
|
-
carrier[ORIGINS][key] = from;
|
|
169
|
-
}
|
|
170
|
-
if (isPlainObject(value)) {
|
|
171
|
-
if (!isPlainObject(target[key]))
|
|
172
|
-
target[key] = {};
|
|
173
|
-
mergeConfig(target[key], value, from);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* A value that can ask for `value` **replaces** like any other - and remembers what it replaced:
|
|
178
|
-
*
|
|
179
|
-
* ```js
|
|
180
|
-
* '[*]': { clean: { include: ({ vars }) => [vars.buildDir] } }
|
|
181
|
-
* '[*]': { clean: { include: "${{ [...value, pkg.basename + '.log'] }}" } }
|
|
182
|
-
* ```
|
|
183
|
-
*
|
|
184
|
-
* Chained here rather than at resolution time because only the merge knows the order of the
|
|
185
|
-
* layers - by the time `interpolateConfig` sees the config they have collapsed into one object,
|
|
186
|
-
* and whatever a closer layer said has already taken the place of what it was derived from.
|
|
187
|
-
*/
|
|
188
|
-
if (carriesPreviousValue(value) && key in target) {
|
|
189
|
-
const carrier = target;
|
|
190
|
-
const chain = (carrier[PREVIOUS_VALUES] ??= {});
|
|
191
|
-
chain[key] = { value: target[key], previous: chain[key] };
|
|
192
|
-
}
|
|
193
|
-
target[key] = Array.isArray(value) ? [...value] : value;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Appends `value` to `target[key]`, de-duplicating **only** an `ALWAYS_APPEND` key.
|
|
197
|
-
*
|
|
198
|
-
* That asymmetry is the point. `plugins` appends without being asked, so a repository and the config
|
|
199
|
-
* it extends both naming `'rman-node'` is the ordinary case rather than a mistake, and the list is
|
|
200
|
-
* also what `rman config` prints. An explicit `+before`, by contrast, was *written* - repeating a
|
|
201
|
-
* step that is already there is a strange thing to ask for, but it is what was asked for, and
|
|
202
|
-
* silently collapsing it would make one layer's list depend on another's contents.
|
|
203
|
-
*
|
|
204
|
-
* By identity (`===`), which covers a string by value and a plugin object by reference; two
|
|
205
|
-
* *different* objects claiming one plugin name are caught where it counts, at registration - see
|
|
206
|
-
* `loadPlugins`.
|
|
207
|
-
*/
|
|
208
|
-
function appendList(target, key, value) {
|
|
209
|
-
const merged = [...toList(target[key] ?? []), ...toList(value)];
|
|
210
|
-
target[key] = ALWAYS_APPEND.includes(key) ? merged.filter((entry, at) => merged.indexOf(entry) === at) : merged;
|
|
211
|
-
}
|
|
212
|
-
function toList(value) {
|
|
213
|
-
return Array.isArray(value) ? value : [value];
|
|
214
|
-
}
|
|
215
|
-
/** A config object, as opposed to an array or anything with its own prototype - only the former
|
|
216
|
-
* merges key by key. */
|
|
217
|
-
function isPlainObject(value) {
|
|
218
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
219
|
-
}
|
package/core/package.d.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { RmanConfig } from '../interfaces/rman-config.interface.js';
|
|
2
|
-
import { Manifest } from './manifest.js';
|
|
3
|
-
import type { Repository } from './repository.js';
|
|
4
|
-
import { type VersionScheme } from './version-scheme.js';
|
|
5
|
-
export declare class Package {
|
|
6
|
-
readonly dirname: string;
|
|
7
|
-
/**
|
|
8
|
-
* This package's identity, read through whichever `ManifestProvider` recognizes its directory.
|
|
9
|
-
*
|
|
10
|
-
* There is no `json` here any more, and that is the point: `package.json` is npm's answer to
|
|
11
|
-
* "where is a package's name and version written", not rman's. `manifest.raw` is still the whole
|
|
12
|
-
* document for a command that knows its own ecosystem - `rman-node` reads `scripts` and
|
|
13
|
-
* `publishConfig` off it - but the core only ever touches `name`, `version` and `private`.
|
|
14
|
-
*/
|
|
15
|
-
manifest: Manifest;
|
|
16
|
-
/**
|
|
17
|
-
* In-repo packages this one depends on - **the full transitive closure**, not just its direct
|
|
18
|
-
* dependencies (see `Repository._updateDependencies`).
|
|
19
|
-
*
|
|
20
|
-
* References, not names: a name is only an identifier if the ecosystem guarantees uniqueness,
|
|
21
|
-
* which npm does and others do not - Go identifies a module by import path, and nothing stops a
|
|
22
|
-
* repository from holding two packages whose short names collide. A `Package` is unambiguous
|
|
23
|
-
* whatever the ecosystem, and comparing by identity removes a lookup from every consumer.
|
|
24
|
-
*/
|
|
25
|
-
dependencies: Package[];
|
|
26
|
-
/** Effective rman config for this package, cascaded from the repository root, with every
|
|
27
|
-
* `${{ ... }}` expression already evaluated. */
|
|
28
|
-
config: RmanConfig;
|
|
29
|
-
/**
|
|
30
|
-
* The repository this package belongs to - so anything holding a package can reach the whole
|
|
31
|
-
* picture (its siblings, the root's config, git) without being handed it separately.
|
|
32
|
-
*
|
|
33
|
-
* Assigned by `Repository.create` rather than taken as a constructor argument, and it has to be:
|
|
34
|
-
* `Repository extends Package`, so a repository constructing itself runs this constructor before
|
|
35
|
-
* it exists to be passed in. A repository's own is itself.
|
|
36
|
-
*/
|
|
37
|
-
repository: Repository;
|
|
38
|
-
/**
|
|
39
|
-
* The package whose directory contains this one - the repository root for an ordinary member, and
|
|
40
|
-
* a genuine enclosing package for one nested inside another (which `Repository.currentPackage`
|
|
41
|
-
* already has to reason about). `undefined` for the root itself, which nothing contains.
|
|
42
|
-
*/
|
|
43
|
-
parent?: Package;
|
|
44
|
-
/**
|
|
45
|
-
* How this package's versions are numbered - `pkg.versionScheme.next(pkg.version, 'minor')`.
|
|
46
|
-
*
|
|
47
|
-
* Comes from the same provider that read the manifest, because the ecosystem that decides where
|
|
48
|
-
* a version is written is the one that decides how it is numbered - a `pyproject.toml` read with
|
|
49
|
-
* semver's arithmetic would be a pair that never occurs in reality.
|
|
50
|
-
*
|
|
51
|
-
* Defaults to semver, so this seam existing changes nothing. **A group must not mix schemes** -
|
|
52
|
-
* a group is one version line, and comparing across schemes is meaningless; `assertOneScheme`
|
|
53
|
-
* refuses it rather than acting on whatever falls out.
|
|
54
|
-
*/
|
|
55
|
-
versionScheme: VersionScheme;
|
|
56
|
-
/** The file the manifest was read from, absolute - for a command that has to say which file it
|
|
57
|
-
* changed (a commit's path list). Empty when no provider recognized this directory. */
|
|
58
|
-
manifestFileName: string;
|
|
59
|
-
/**
|
|
60
|
-
* **Which ecosystem this package belongs to** - `'node'` for one read by `rman-node`, from the
|
|
61
|
-
* `ManifestProvider.name` that claimed the directory. Empty when no provider did.
|
|
62
|
-
*
|
|
63
|
-
* The escape hatch for code that legitimately knows one technology: `if (pkg.provider === 'node')`
|
|
64
|
-
* before reaching into `manifest.raw` for something only npm has. Per *package*, not per
|
|
65
|
-
* repository, because `Manifest.read` asks per directory - a polyglot monorepo can hold a `node`
|
|
66
|
-
* package beside a `cargo` one, and a command sweeping over `getPackages()` has to be able to
|
|
67
|
-
* tell.
|
|
68
|
-
*
|
|
69
|
-
* Not a union type on purpose: the set of ecosystems is whatever the repository's `plugins`
|
|
70
|
-
* contribute, so narrowing it here would mean the core listing plugins it cannot know about.
|
|
71
|
-
*/
|
|
72
|
-
provider: string;
|
|
73
|
-
constructor(dirname: string);
|
|
74
|
-
get basename(): string;
|
|
75
|
-
get name(): string;
|
|
76
|
-
get version(): string;
|
|
77
|
-
get isPrivate(): boolean;
|
|
78
|
-
/** Re-reads from disk - for a command that has just written the manifest itself and wants the
|
|
79
|
-
* package to agree with the file again. */
|
|
80
|
-
reloadManifest(): Manifest;
|
|
81
|
-
/** Writes the current manifest back through its provider. */
|
|
82
|
-
writeManifest(): void;
|
|
83
|
-
}
|