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/package.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { Manifest } from './manifest.js';
|
|
3
|
-
import { semverScheme } from './version-scheme.js';
|
|
4
|
-
export class Package {
|
|
5
|
-
dirname;
|
|
6
|
-
/**
|
|
7
|
-
* This package's identity, read through whichever `ManifestProvider` recognizes its directory.
|
|
8
|
-
*
|
|
9
|
-
* There is no `json` here any more, and that is the point: `package.json` is npm's answer to
|
|
10
|
-
* "where is a package's name and version written", not rman's. `manifest.raw` is still the whole
|
|
11
|
-
* document for a command that knows its own ecosystem - `rman-node` reads `scripts` and
|
|
12
|
-
* `publishConfig` off it - but the core only ever touches `name`, `version` and `private`.
|
|
13
|
-
*/
|
|
14
|
-
manifest;
|
|
15
|
-
/**
|
|
16
|
-
* In-repo packages this one depends on - **the full transitive closure**, not just its direct
|
|
17
|
-
* dependencies (see `Repository._updateDependencies`).
|
|
18
|
-
*
|
|
19
|
-
* References, not names: a name is only an identifier if the ecosystem guarantees uniqueness,
|
|
20
|
-
* which npm does and others do not - Go identifies a module by import path, and nothing stops a
|
|
21
|
-
* repository from holding two packages whose short names collide. A `Package` is unambiguous
|
|
22
|
-
* whatever the ecosystem, and comparing by identity removes a lookup from every consumer.
|
|
23
|
-
*/
|
|
24
|
-
dependencies = [];
|
|
25
|
-
/** Effective rman config for this package, cascaded from the repository root, with every
|
|
26
|
-
* `${{ ... }}` expression already evaluated. */
|
|
27
|
-
config = {};
|
|
28
|
-
/**
|
|
29
|
-
* The repository this package belongs to - so anything holding a package can reach the whole
|
|
30
|
-
* picture (its siblings, the root's config, git) without being handed it separately.
|
|
31
|
-
*
|
|
32
|
-
* Assigned by `Repository.create` rather than taken as a constructor argument, and it has to be:
|
|
33
|
-
* `Repository extends Package`, so a repository constructing itself runs this constructor before
|
|
34
|
-
* it exists to be passed in. A repository's own is itself.
|
|
35
|
-
*/
|
|
36
|
-
repository;
|
|
37
|
-
/**
|
|
38
|
-
* The package whose directory contains this one - the repository root for an ordinary member, and
|
|
39
|
-
* a genuine enclosing package for one nested inside another (which `Repository.currentPackage`
|
|
40
|
-
* already has to reason about). `undefined` for the root itself, which nothing contains.
|
|
41
|
-
*/
|
|
42
|
-
parent;
|
|
43
|
-
/**
|
|
44
|
-
* How this package's versions are numbered - `pkg.versionScheme.next(pkg.version, 'minor')`.
|
|
45
|
-
*
|
|
46
|
-
* Comes from the same provider that read the manifest, because the ecosystem that decides where
|
|
47
|
-
* a version is written is the one that decides how it is numbered - a `pyproject.toml` read with
|
|
48
|
-
* semver's arithmetic would be a pair that never occurs in reality.
|
|
49
|
-
*
|
|
50
|
-
* Defaults to semver, so this seam existing changes nothing. **A group must not mix schemes** -
|
|
51
|
-
* a group is one version line, and comparing across schemes is meaningless; `assertOneScheme`
|
|
52
|
-
* refuses it rather than acting on whatever falls out.
|
|
53
|
-
*/
|
|
54
|
-
versionScheme = semverScheme;
|
|
55
|
-
/** The file the manifest was read from, absolute - for a command that has to say which file it
|
|
56
|
-
* changed (a commit's path list). Empty when no provider recognized this directory. */
|
|
57
|
-
manifestFileName;
|
|
58
|
-
/**
|
|
59
|
-
* **Which ecosystem this package belongs to** - `'node'` for one read by `rman-node`, from the
|
|
60
|
-
* `ManifestProvider.name` that claimed the directory. Empty when no provider did.
|
|
61
|
-
*
|
|
62
|
-
* The escape hatch for code that legitimately knows one technology: `if (pkg.provider === 'node')`
|
|
63
|
-
* before reaching into `manifest.raw` for something only npm has. Per *package*, not per
|
|
64
|
-
* repository, because `Manifest.read` asks per directory - a polyglot monorepo can hold a `node`
|
|
65
|
-
* package beside a `cargo` one, and a command sweeping over `getPackages()` has to be able to
|
|
66
|
-
* tell.
|
|
67
|
-
*
|
|
68
|
-
* Not a union type on purpose: the set of ecosystems is whatever the repository's `plugins`
|
|
69
|
-
* contribute, so narrowing it here would mean the core listing plugins it cannot know about.
|
|
70
|
-
*/
|
|
71
|
-
provider;
|
|
72
|
-
constructor(dirname) {
|
|
73
|
-
this.dirname = dirname;
|
|
74
|
-
const { manifest, versionScheme, fileName, provider } = Manifest.read(dirname);
|
|
75
|
-
this.manifest = manifest;
|
|
76
|
-
this.versionScheme = versionScheme;
|
|
77
|
-
this.manifestFileName = fileName ? path.join(dirname, fileName) : '';
|
|
78
|
-
this.provider = provider;
|
|
79
|
-
}
|
|
80
|
-
get basename() {
|
|
81
|
-
return path.basename(this.dirname);
|
|
82
|
-
}
|
|
83
|
-
get name() {
|
|
84
|
-
return this.manifest.name;
|
|
85
|
-
}
|
|
86
|
-
get version() {
|
|
87
|
-
return this.manifest.version;
|
|
88
|
-
}
|
|
89
|
-
get isPrivate() {
|
|
90
|
-
return !!this.manifest.private;
|
|
91
|
-
}
|
|
92
|
-
/** Re-reads from disk - for a command that has just written the manifest itself and wants the
|
|
93
|
-
* package to agree with the file again. */
|
|
94
|
-
reloadManifest() {
|
|
95
|
-
const { manifest, versionScheme, fileName, provider } = Manifest.read(this.dirname);
|
|
96
|
-
this.manifest = manifest;
|
|
97
|
-
this.versionScheme = versionScheme;
|
|
98
|
-
this.manifestFileName = fileName ? path.join(this.dirname, fileName) : '';
|
|
99
|
-
this.provider = provider;
|
|
100
|
-
return this.manifest;
|
|
101
|
-
}
|
|
102
|
-
/** Writes the current manifest back through its provider. */
|
|
103
|
-
writeManifest() {
|
|
104
|
-
Manifest.write(this.dirname, this.manifest);
|
|
105
|
-
}
|
|
106
|
-
}
|
package/core/plugin.d.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { RmanConfig } from '../interfaces/rman-config.interface.js';
|
|
2
|
-
import { RunService } from '../services/run.service.js';
|
|
3
|
-
import { VersionPlanService } from '../services/version-plan.service.js';
|
|
4
|
-
import { BinPath } from '../utils/bin-path.js';
|
|
5
|
-
import type { CustomCommand, LoadedCommand } from './custom-command.js';
|
|
6
|
-
import { type ManifestProvider } from './manifest.js';
|
|
7
|
-
import { Workspace } from './workspace.js';
|
|
8
|
-
/** The `.rmanrc` key naming plugin packages to load. */
|
|
9
|
-
export declare const PLUGINS_KEY = "plugins";
|
|
10
|
-
/**
|
|
11
|
-
* What a plugin package hands rman.
|
|
12
|
-
*
|
|
13
|
-
* An object rather than a bare array of commands, for the reason `CommandContext` is one: a plugin
|
|
14
|
-
* will eventually contribute more than commands (config defaults, publish targets, a package
|
|
15
|
-
* provider for a non-Node repository), and nothing written against this should have to change when
|
|
16
|
-
* it does.
|
|
17
|
-
*/
|
|
18
|
-
export interface RmanPlugin {
|
|
19
|
-
/** For error messages and `--help` grouping. Conventionally the package's own name. */
|
|
20
|
-
name: string;
|
|
21
|
-
commands?: CustomCommand[];
|
|
22
|
-
/**
|
|
23
|
-
* Where `run` can find a package's steps besides its `.rmanrc` - `rman-node` contributes
|
|
24
|
-
* `package.json#scripts` here, with npm's `pre`/`post` lifecycle.
|
|
25
|
-
*
|
|
26
|
-
* Registered in `plugins` declaration order, and only for plugins the repository actually named:
|
|
27
|
-
* what a script resolves to is then a function of the config rather than of what happened to be
|
|
28
|
-
* imported.
|
|
29
|
-
*/
|
|
30
|
-
runSteps?: RunService.StepSource;
|
|
31
|
-
/**
|
|
32
|
-
* How this ecosystem's repositories are laid out - `rman-node` reads `workspaces` from the root
|
|
33
|
-
* `package.json` here.
|
|
34
|
-
*
|
|
35
|
-
* Loaded **before any package is known**, since this is what finds them: `Repository.create`
|
|
36
|
-
* reads the root config, loads the plugins it names, and only then asks. A repository naming no
|
|
37
|
-
* plugin therefore has no packages beyond itself.
|
|
38
|
-
*/
|
|
39
|
-
workspace?: Workspace.Provider;
|
|
40
|
-
/**
|
|
41
|
-
* Where a package's name and version are written, and how it is numbered - `rman-node`
|
|
42
|
-
* contributes `package.json` here.
|
|
43
|
-
*
|
|
44
|
-
* Registered before any package is constructed, since `Package` reads through it. A repository
|
|
45
|
-
* naming no plugin therefore gets packages named after their own directories at version
|
|
46
|
-
* `0.0.0` - see `readManifest`.
|
|
47
|
-
*/
|
|
48
|
-
manifest?: ManifestProvider;
|
|
49
|
-
/**
|
|
50
|
-
* How a release is planned - which packages have changed since their last release and what
|
|
51
|
-
* version each gets. `rman-node` contributes `NodeVersionPlanService` here.
|
|
52
|
-
*
|
|
53
|
-
* **`VersionPlanService` is abstract, so `version`/`changed` do not work without one** (they fail
|
|
54
|
-
* naming this key). Unlike `manifest` and `workspace`, which degrade to honest defaults, a plan is
|
|
55
|
-
* either right or it quietly releases the wrong set of packages - see `VersionPlanService`.
|
|
56
|
-
*
|
|
57
|
-
* Consulted when a command asks, not at load time, so this is declared and nothing else has to
|
|
58
|
-
* happen as the plugin's module is imported.
|
|
59
|
-
*/
|
|
60
|
-
versionPlanner?: VersionPlanService;
|
|
61
|
-
/**
|
|
62
|
-
* Where this ecosystem keeps a repository's locally installed executables - `rman-node`
|
|
63
|
-
* contributes npm's `node_modules/.bin`, walked up the directory chain.
|
|
64
|
-
*
|
|
65
|
-
* Prepended to PATH for every `exec`/`runBin` child process, so a command an author wrote runs
|
|
66
|
-
* against the repository's own pinned tools. **Every plugin's entries are used**, not just the
|
|
67
|
-
* first - see `BinPath`.
|
|
68
|
-
*/
|
|
69
|
-
binPaths?: BinPath.Provider;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Identity helper for authoring a plugin with full type-checking - the `defineConfig`/
|
|
73
|
-
* `defineCommand` pattern again, for the same reason. Returns `plugin` unchanged.
|
|
74
|
-
*
|
|
75
|
-
* **A plugin package's entry point exports a *config*, not this**, so that a package is an
|
|
76
|
-
* `.rmanrc` like any other and can carry a second plugin later without changing shape:
|
|
77
|
-
*
|
|
78
|
-
* ```js
|
|
79
|
-
* // rman-node's entry point
|
|
80
|
-
* import { defineConfig, definePlugin } from 'rman';
|
|
81
|
-
* import publishCommand from './commands/publish.js';
|
|
82
|
-
*
|
|
83
|
-
* export const nodePlugin = definePlugin({ name: 'rman-node', commands: [publishCommand] });
|
|
84
|
-
* export default defineConfig({ plugins: [nodePlugin] });
|
|
85
|
-
* ```
|
|
86
|
-
*
|
|
87
|
-
* **A module exporting the plugin itself is refused**, with a message saying so. Accepting both
|
|
88
|
-
* would mean telling a plugin from a config at runtime, and `name` is a key either may have - the
|
|
89
|
-
* test would be a guess, and guessing "plugin" registers nothing while reporting success.
|
|
90
|
-
*/
|
|
91
|
-
export declare function definePlugin(plugin: RmanPlugin): RmanPlugin;
|
|
92
|
-
/**
|
|
93
|
-
* Loads every plugin the repository's `.rmanrc "plugins"` declares, in declaration order.
|
|
94
|
-
*
|
|
95
|
-
* This is what lets a *package* contribute commands. `.rman/*.mjs` covers one repository's own
|
|
96
|
-
* commands (see `loadCustomCommands`); a plugin covers a whole class of repository - `rman-node`
|
|
97
|
-
* carrying everything that only means something because the repository is a Node one, so rman's
|
|
98
|
-
* core does not have to.
|
|
99
|
-
*
|
|
100
|
-
* An entry is a package name, a path, or a plugin object - and a named package's entry point
|
|
101
|
-
* exports a **config**, whose own `plugins` are then loaded the same way (see `loadInto`).
|
|
102
|
-
*
|
|
103
|
-
* Names resolve relative to the repository root's own `node_modules`, the way `extends` resolves -
|
|
104
|
-
* a plugin is the repository's dependency, not rman's.
|
|
105
|
-
*
|
|
106
|
-
* **A plugin that cannot be loaded throws**, unlike a broken `.rman/*.mjs` file, which is warned
|
|
107
|
-
* about and skipped. The two differ because the consequence does: a skipped local command affects
|
|
108
|
-
* only itself, while a missing plugin silently removes commands the repository is built around -
|
|
109
|
-
* `rman publish` would simply not exist, and "not a known command" sends the reader looking in the
|
|
110
|
-
* wrong place entirely.
|
|
111
|
-
*/
|
|
112
|
-
export declare function loadPlugins(rootDir: string, rootConfig: RmanConfig): Promise<LoadedCommand[]>;
|
package/core/plugin.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
-
import { RunService } from '../services/run.service.js';
|
|
4
|
-
import { VersionPlanService } from '../services/version-plan.service.js';
|
|
5
|
-
import { BinPath } from '../utils/bin-path.js';
|
|
6
|
-
import { Manifest } from './manifest.js';
|
|
7
|
-
import { resolveConfigTarget } from './resolve-target.js';
|
|
8
|
-
import { Workspace } from './workspace.js';
|
|
9
|
-
/** The `.rmanrc` key naming plugin packages to load. */
|
|
10
|
-
export const PLUGINS_KEY = 'plugins';
|
|
11
|
-
/**
|
|
12
|
-
* Identity helper for authoring a plugin with full type-checking - the `defineConfig`/
|
|
13
|
-
* `defineCommand` pattern again, for the same reason. Returns `plugin` unchanged.
|
|
14
|
-
*
|
|
15
|
-
* **A plugin package's entry point exports a *config*, not this**, so that a package is an
|
|
16
|
-
* `.rmanrc` like any other and can carry a second plugin later without changing shape:
|
|
17
|
-
*
|
|
18
|
-
* ```js
|
|
19
|
-
* // rman-node's entry point
|
|
20
|
-
* import { defineConfig, definePlugin } from 'rman';
|
|
21
|
-
* import publishCommand from './commands/publish.js';
|
|
22
|
-
*
|
|
23
|
-
* export const nodePlugin = definePlugin({ name: 'rman-node', commands: [publishCommand] });
|
|
24
|
-
* export default defineConfig({ plugins: [nodePlugin] });
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* **A module exporting the plugin itself is refused**, with a message saying so. Accepting both
|
|
28
|
-
* would mean telling a plugin from a config at runtime, and `name` is a key either may have - the
|
|
29
|
-
* test would be a guess, and guessing "plugin" registers nothing while reporting success.
|
|
30
|
-
*/
|
|
31
|
-
export function definePlugin(plugin) {
|
|
32
|
-
return plugin;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Loads every plugin the repository's `.rmanrc "plugins"` declares, in declaration order.
|
|
36
|
-
*
|
|
37
|
-
* This is what lets a *package* contribute commands. `.rman/*.mjs` covers one repository's own
|
|
38
|
-
* commands (see `loadCustomCommands`); a plugin covers a whole class of repository - `rman-node`
|
|
39
|
-
* carrying everything that only means something because the repository is a Node one, so rman's
|
|
40
|
-
* core does not have to.
|
|
41
|
-
*
|
|
42
|
-
* An entry is a package name, a path, or a plugin object - and a named package's entry point
|
|
43
|
-
* exports a **config**, whose own `plugins` are then loaded the same way (see `loadInto`).
|
|
44
|
-
*
|
|
45
|
-
* Names resolve relative to the repository root's own `node_modules`, the way `extends` resolves -
|
|
46
|
-
* a plugin is the repository's dependency, not rman's.
|
|
47
|
-
*
|
|
48
|
-
* **A plugin that cannot be loaded throws**, unlike a broken `.rman/*.mjs` file, which is warned
|
|
49
|
-
* about and skipped. The two differ because the consequence does: a skipped local command affects
|
|
50
|
-
* only itself, while a missing plugin silently removes commands the repository is built around -
|
|
51
|
-
* `rman publish` would simply not exist, and "not a known command" sends the reader looking in the
|
|
52
|
-
* wrong place entirely.
|
|
53
|
-
*/
|
|
54
|
-
export async function loadPlugins(rootDir, rootConfig) {
|
|
55
|
-
const commands = [];
|
|
56
|
-
/** Resolved against the repository root, where the `.rmanrc` declaring them lives. */
|
|
57
|
-
await loadInto(commands, rootConfig, path.join(rootDir, '.rmanrc'), { files: new Set(), names: new Set() });
|
|
58
|
-
return commands;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* One config's `plugins`, in declaration order.
|
|
62
|
-
*
|
|
63
|
-
* Recursive because a plugin package **exports a config**, not a plugin: `rman-node`'s entry point
|
|
64
|
-
* is `export default defineConfig({ plugins: [ ... ] })`, so resolving a name lands on another
|
|
65
|
-
* config whose own `plugins` are the ones to register. That also means a plugin package can name a
|
|
66
|
-
* plugin of its own and it simply works.
|
|
67
|
-
*
|
|
68
|
-
* `from` is the file the entries are resolved against, and it changes as it descends - an entry in
|
|
69
|
-
* `rman-node`'s config resolves through *its* `node_modules`, not the repository's, the same rule
|
|
70
|
-
* `extends` follows.
|
|
71
|
-
*
|
|
72
|
-
* **Only `plugins` is read out of an imported config.** Its other keys are not merged: a config's
|
|
73
|
-
* way into a repository is `extends`, which is the key that says "merge this underneath mine".
|
|
74
|
-
* Reading them here would make a plugin able to configure a repository by being installed.
|
|
75
|
-
*/
|
|
76
|
-
async function loadInto(commands, config, from, seen) {
|
|
77
|
-
const declared = config?.[PLUGINS_KEY];
|
|
78
|
-
if (declared === undefined)
|
|
79
|
-
return;
|
|
80
|
-
for (const entry of Array.isArray(declared) ? declared : [declared]) {
|
|
81
|
-
/**
|
|
82
|
-
* The object form: a JS config handing a plugin over directly, and what a plugin package's own
|
|
83
|
-
* config holds. Nothing to resolve or import.
|
|
84
|
-
*
|
|
85
|
-
* An object here **is** a plugin - it is not guessed at. The one thing checked is that it has a
|
|
86
|
-
* `name`, because everything downstream (the registration guard, `--help` grouping, every error
|
|
87
|
-
* message) is keyed by it.
|
|
88
|
-
*/
|
|
89
|
-
if (isPlainObject(entry)) {
|
|
90
|
-
if (typeof entry.name !== 'string' || !entry.name) {
|
|
91
|
-
throw new Error(`A plugin object in "${PLUGINS_KEY}" has no "name" - every other message is keyed by it.`);
|
|
92
|
-
}
|
|
93
|
-
register(commands, entry, entry.name, from, seen);
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (typeof entry !== 'string' || !entry.trim()) {
|
|
97
|
-
throw new Error(`"${PLUGINS_KEY}" takes a package name, a path, or a plugin object - not ${JSON.stringify(entry)}`);
|
|
98
|
-
}
|
|
99
|
-
const file = resolveConfigTarget(entry, from, PLUGINS_KEY);
|
|
100
|
-
/** A config naming itself, or two naming each other, would otherwise recurse forever. Keyed by
|
|
101
|
-
* resolved file, so the same package reached by two names is still loaded once. */
|
|
102
|
-
if (seen.files.has(file))
|
|
103
|
-
continue;
|
|
104
|
-
seen.files.add(file);
|
|
105
|
-
const mod = await import(pathToFileURL(file).href);
|
|
106
|
-
const exported = mod?.default ?? mod?.plugin;
|
|
107
|
-
/**
|
|
108
|
-
* **A module exports one thing: an rman config.** Not a plugin, and not either-or.
|
|
109
|
-
*
|
|
110
|
-
* Accepting both meant having to *tell them apart*, and there is no reliable way to - `name` is
|
|
111
|
-
* a key a config may have as well, so the test came down to "a name plus at least one of the
|
|
112
|
-
* things a plugin contributes", which is a guess. Guess wrong in the direction of "plugin" and
|
|
113
|
-
* nothing is registered while the command reports success, which is the worst outcome on offer.
|
|
114
|
-
* One shape, one rule, one error.
|
|
115
|
-
*/
|
|
116
|
-
if (!isPlainObject(exported) || exported.plugins === undefined) {
|
|
117
|
-
throw new Error(`Plugin "${entry}" must export an rman config - \`export default defineConfig({ plugins: [ ... ] })\`. ` +
|
|
118
|
-
describeExport(exported));
|
|
119
|
-
}
|
|
120
|
-
await loadInto(commands, exported, file, seen);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Everything a plugin contributes, in one place - so the object and the imported forms cannot drift
|
|
125
|
-
* apart in what they support.
|
|
126
|
-
*
|
|
127
|
-
* **One registration per plugin name.** `plugins` appends at every layer now, so the same plugin
|
|
128
|
-
* arriving twice is an ordinary consequence of `extends` rather than a mistake to report - and
|
|
129
|
-
* registering it twice would define its commands twice, which yargs does not survive. The config
|
|
130
|
-
* merge already drops an identical entry; this catches the rest, including two objects claiming one
|
|
131
|
-
* name and an object that duplicates a named package.
|
|
132
|
-
*/
|
|
133
|
-
function register(commands, plugin, label, specifier, seen) {
|
|
134
|
-
if (seen.names.has(plugin.name))
|
|
135
|
-
return;
|
|
136
|
-
seen.names.add(plugin.name);
|
|
137
|
-
if (plugin.runSteps)
|
|
138
|
-
RunService.addStepSource(plugin.runSteps);
|
|
139
|
-
if (plugin.manifest)
|
|
140
|
-
Manifest.addProvider(plugin.manifest);
|
|
141
|
-
if (plugin.workspace)
|
|
142
|
-
Workspace.addProvider(plugin.workspace);
|
|
143
|
-
if (plugin.binPaths)
|
|
144
|
-
BinPath.addProvider(plugin.binPaths);
|
|
145
|
-
if (plugin.versionPlanner)
|
|
146
|
-
VersionPlanService.setPlanner(plugin.versionPlanner);
|
|
147
|
-
for (const command of plugin.commands ?? []) {
|
|
148
|
-
commands.push(toLoadedCommand(command, label || specifier, specifier));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* The second half of the error above - what the module *did* export, so the author can see how far
|
|
153
|
-
* off it was.
|
|
154
|
-
*
|
|
155
|
-
* It recognizes a plugin object only to **say so in a message**. That is the one safe use for this
|
|
156
|
-
* shape test: it decides nothing, so a wrong guess costs a slightly less helpful sentence rather
|
|
157
|
-
* than a plugin that silently does not load.
|
|
158
|
-
*/
|
|
159
|
-
function describeExport(exported) {
|
|
160
|
-
if (exported === undefined)
|
|
161
|
-
return 'It has no default export.';
|
|
162
|
-
if (!isPlainObject(exported))
|
|
163
|
-
return `Its default export is a ${typeof exported}.`;
|
|
164
|
-
const looksLikePlugin = PLUGIN_SEAMS.some(seam => exported[seam] !== undefined);
|
|
165
|
-
return looksLikePlugin
|
|
166
|
-
? `Its default export looks like the plugin itself - put it in a config's "${PLUGINS_KEY}".`
|
|
167
|
-
: `Its default export has no "${PLUGINS_KEY}".`;
|
|
168
|
-
}
|
|
169
|
-
const PLUGIN_SEAMS = ['commands', 'runSteps', 'workspace', 'manifest', 'versionPlanner', 'binPaths'];
|
|
170
|
-
/** A config object, as opposed to an array or anything with its own prototype. */
|
|
171
|
-
function isPlainObject(value) {
|
|
172
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
173
|
-
}
|
|
174
|
-
/** A plugin's command, checked the same way a `.rman/*.mjs` one is - the name it answers to comes
|
|
175
|
-
* from its own `command` string, since a plugin has no file name to fall back on. */
|
|
176
|
-
function toLoadedCommand(command, pluginName, specifier) {
|
|
177
|
-
const declared = command?.command?.trim();
|
|
178
|
-
if (!declared) {
|
|
179
|
-
throw new Error(`Plugin "${pluginName}" has a command with no "command" name - it cannot be registered.`);
|
|
180
|
-
}
|
|
181
|
-
if (typeof command.handler !== 'function') {
|
|
182
|
-
throw new Error(`Plugin "${pluginName}" command "${declared}" has no "handler" function.`);
|
|
183
|
-
}
|
|
184
|
-
if (typeof command.describe !== 'string' || !command.describe) {
|
|
185
|
-
throw new Error(`Plugin "${pluginName}" command "${declared}" has no "describe" - \`rman --help\` would have ` +
|
|
186
|
-
`nothing to list it by.`);
|
|
187
|
-
}
|
|
188
|
-
return { ...command, command: declared, name: declared.split(/\s+/)[0], file: specifier };
|
|
189
|
-
}
|
package/core/workspace.d.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How a repository is laid out, and who decides.
|
|
3
|
-
*
|
|
4
|
-
* A namespace for the same reason `Manifest` is one: it is what a plugin can *augment*
|
|
5
|
-
* (`declare module 'rman' { namespace Workspace { ... } }`), so whatever this seam grows later
|
|
6
|
-
* arrives as a member here rather than as another top-level export.
|
|
7
|
-
*/
|
|
8
|
-
export declare namespace Workspace {
|
|
9
|
-
/** What a repository looks like: where its root is, and which directories hold its packages. */
|
|
10
|
-
interface Layout {
|
|
11
|
-
/** Absolute path to the repository root. */
|
|
12
|
-
root: string;
|
|
13
|
-
/**
|
|
14
|
-
* Absolute paths to the package **directories**, outside the root. Empty for a repository that
|
|
15
|
-
* is itself the one package.
|
|
16
|
-
*
|
|
17
|
-
* Directories rather than `Package` objects, and not for want of trying: a provider runs before
|
|
18
|
-
* any package exists - `Repository.create` constructs them from these paths afterwards, through
|
|
19
|
-
* the *manifest* provider. A discovery provider returning packages would have to construct them
|
|
20
|
-
* itself, making discovery the owner of identity too.
|
|
21
|
-
*
|
|
22
|
-
* Unlike `Package.dependencies`, there is no identity question here: these are paths, and a
|
|
23
|
-
* path is unique by construction where a name is only unique if the ecosystem says so.
|
|
24
|
-
*/
|
|
25
|
-
packageDirs: string[];
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* How a repository's packages are found.
|
|
29
|
-
*
|
|
30
|
-
* **The core has no provider**, and that is the point: "packages are the `workspaces` globs in
|
|
31
|
-
* the root `package.json`, and a package is a directory with a `package.json` in it" is true of
|
|
32
|
-
* npm and of nothing else. `rman-node` contributes that one; a plugin for another ecosystem
|
|
33
|
-
* contributes its own (a Cargo workspace, a `go.work`, a `pyproject.toml`).
|
|
34
|
-
*
|
|
35
|
-
* Returns `undefined` for "this is not a repository I recognize", so the next provider gets a
|
|
36
|
-
* turn and a repository nobody recognizes falls back to being a single package.
|
|
37
|
-
*/
|
|
38
|
-
type Provider = (root: string) => Layout | undefined;
|
|
39
|
-
/**
|
|
40
|
-
* Registers a provider. Called by `loadPlugins` for each plugin's `workspace`, in `plugins`
|
|
41
|
-
* declaration order - so which provider answers is a function of the repository's own config.
|
|
42
|
-
*/
|
|
43
|
-
function addProvider(provider: Provider): void;
|
|
44
|
-
/** For tests, which would otherwise leak a provider into every later case in the process. */
|
|
45
|
-
function clearProviders(): void;
|
|
46
|
-
/** The first provider that recognizes `root`, in declaration order. */
|
|
47
|
-
function resolve(root: string): Layout | undefined;
|
|
48
|
-
/**
|
|
49
|
-
* Where the repository starts, decided **without knowing anything about any ecosystem** - it has
|
|
50
|
-
* to be, because the plugins that do know are named in the config file this walk is looking for.
|
|
51
|
-
*
|
|
52
|
-
* Walking up from `from`, stopping after a directory holding `.git`, the root is:
|
|
53
|
-
*
|
|
54
|
-
* 1. the **outermost** directory in that chain holding an `.rmanrc*` - outermost, because a
|
|
55
|
-
* *package* may have its own `.rmanrc` (that is a supported thing), and from inside such a
|
|
56
|
-
* package the nearest one is the package's, not the repository's;
|
|
57
|
-
* 2. otherwise the `.git` directory itself, the ordinary meaning of "repository root";
|
|
58
|
-
* 3. otherwise `from`, which is all that is left to go on.
|
|
59
|
-
*
|
|
60
|
-
* **What "outermost" costs**, since the two cases genuinely conflict and only one can win: a
|
|
61
|
-
* self-contained project nested inside a larger git repository *and sharing its `.git`* resolves
|
|
62
|
-
* to the outer root, not to itself (measured). That is the same repository by any definition git
|
|
63
|
-
* recognizes, so it is the defensible answer - and a nested project with a `.git` of its own is
|
|
64
|
-
* found correctly, because the walk stops there before the outer `.rmanrc` is ever seen (also
|
|
65
|
-
* measured). A per-package `.rmanrc` is the common case and it is the one served.
|
|
66
|
-
*/
|
|
67
|
-
function findRoot(from: string, deep?: number): string;
|
|
68
|
-
}
|
package/core/workspace.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
/**
|
|
4
|
-
* How a repository is laid out, and who decides.
|
|
5
|
-
*
|
|
6
|
-
* A namespace for the same reason `Manifest` is one: it is what a plugin can *augment*
|
|
7
|
-
* (`declare module 'rman' { namespace Workspace { ... } }`), so whatever this seam grows later
|
|
8
|
-
* arrives as a member here rather than as another top-level export.
|
|
9
|
-
*/
|
|
10
|
-
export var Workspace;
|
|
11
|
-
(function (Workspace) {
|
|
12
|
-
/**
|
|
13
|
-
* Registers a provider. Called by `loadPlugins` for each plugin's `workspace`, in `plugins`
|
|
14
|
-
* declaration order - so which provider answers is a function of the repository's own config.
|
|
15
|
-
*/
|
|
16
|
-
function addProvider(provider) {
|
|
17
|
-
if (providers.includes(provider))
|
|
18
|
-
return;
|
|
19
|
-
providers.push(provider);
|
|
20
|
-
}
|
|
21
|
-
Workspace.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
|
-
Workspace.clearProviders = clearProviders;
|
|
27
|
-
/** The first provider that recognizes `root`, in declaration order. */
|
|
28
|
-
function resolve(root) {
|
|
29
|
-
for (const provider of providers) {
|
|
30
|
-
const layout = provider(root);
|
|
31
|
-
if (layout)
|
|
32
|
-
return layout;
|
|
33
|
-
}
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
Workspace.resolve = resolve;
|
|
37
|
-
/**
|
|
38
|
-
* Where the repository starts, decided **without knowing anything about any ecosystem** - it has
|
|
39
|
-
* to be, because the plugins that do know are named in the config file this walk is looking for.
|
|
40
|
-
*
|
|
41
|
-
* Walking up from `from`, stopping after a directory holding `.git`, the root is:
|
|
42
|
-
*
|
|
43
|
-
* 1. the **outermost** directory in that chain holding an `.rmanrc*` - outermost, because a
|
|
44
|
-
* *package* may have its own `.rmanrc` (that is a supported thing), and from inside such a
|
|
45
|
-
* package the nearest one is the package's, not the repository's;
|
|
46
|
-
* 2. otherwise the `.git` directory itself, the ordinary meaning of "repository root";
|
|
47
|
-
* 3. otherwise `from`, which is all that is left to go on.
|
|
48
|
-
*
|
|
49
|
-
* **What "outermost" costs**, since the two cases genuinely conflict and only one can win: a
|
|
50
|
-
* self-contained project nested inside a larger git repository *and sharing its `.git`* resolves
|
|
51
|
-
* to the outer root, not to itself (measured). That is the same repository by any definition git
|
|
52
|
-
* recognizes, so it is the defensible answer - and a nested project with a `.git` of its own is
|
|
53
|
-
* found correctly, because the walk stops there before the outer `.rmanrc` is ever seen (also
|
|
54
|
-
* measured). A per-package `.rmanrc` is the common case and it is the one served.
|
|
55
|
-
*/
|
|
56
|
-
function findRoot(from, deep = 10) {
|
|
57
|
-
const chain = [];
|
|
58
|
-
let dir = path.resolve(from);
|
|
59
|
-
let remaining = deep;
|
|
60
|
-
while (remaining-- >= 0 && fs.existsSync(dir)) {
|
|
61
|
-
chain.push(dir);
|
|
62
|
-
if (fs.existsSync(path.join(dir, '.git')))
|
|
63
|
-
break;
|
|
64
|
-
const parent = path.resolve(dir, '..');
|
|
65
|
-
if (parent === dir)
|
|
66
|
-
break;
|
|
67
|
-
dir = parent;
|
|
68
|
-
}
|
|
69
|
-
const withConfig = chain.filter(hasRmanConfig);
|
|
70
|
-
if (withConfig.length)
|
|
71
|
-
return withConfig[withConfig.length - 1];
|
|
72
|
-
const gitRoot = chain.find(d => fs.existsSync(path.join(d, '.git')));
|
|
73
|
-
return gitRoot ?? path.resolve(from);
|
|
74
|
-
}
|
|
75
|
-
Workspace.findRoot = findRoot;
|
|
76
|
-
/** Every file form a `.rmanrc` comes in - `package.json#rman` is deliberately *not* one of them
|
|
77
|
-
* here: it would make the root question npm-shaped again. */
|
|
78
|
-
const CONFIG_FILES = ['.rmanrc', '.rmanrc.yml', '.rmanrc.cjs', '.rmanrc.mjs', '.rmanrc.js'];
|
|
79
|
-
function hasRmanConfig(dir) {
|
|
80
|
-
return CONFIG_FILES.some(name => fs.existsSync(path.join(dir, name)));
|
|
81
|
-
}
|
|
82
|
-
const providers = [];
|
|
83
|
-
})(Workspace || (Workspace = {}));
|
|
File without changes
|