rman 1.3.0 → 2.0.0-beta.2
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 +63 -19
- package/cli.d.ts +5 -0
- package/cli.js +214 -88
- package/commands/build.command.d.ts +177 -3
- package/commands/build.command.js +20 -10
- package/commands/changed.command.d.ts +80 -3
- package/commands/changed.command.js +19 -12
- package/commands/changelog.command.d.ts +192 -3
- package/commands/changelog.command.js +87 -43
- package/commands/config.command.d.ts +44 -3
- package/commands/config.command.js +30 -19
- 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/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 +317 -3
- package/commands/version.command.js +149 -69
- 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 +143 -0
- package/core/command-builder.d.ts +14 -0
- package/core/command-builder.js +78 -0
- package/core/config.d.ts +140 -19
- package/core/config.js +258 -74
- package/core/core-services.d.ts +14 -0
- package/core/core-services.js +30 -0
- package/core/core-targets.d.ts +14 -0
- package/core/core-targets.js +16 -0
- package/core/custom-command.d.ts +42 -6
- package/core/custom-command.js +44 -17
- package/core/extends-config.d.ts +13 -5
- package/core/extends-config.js +52 -12
- package/core/load-config-module.d.ts +28 -0
- package/core/load-config-module.js +42 -0
- package/core/manifest.d.ts +47 -25
- package/core/manifest.js +51 -69
- package/core/merge-config.d.ts +33 -34
- package/core/merge-config.js +137 -92
- package/core/package.d.ts +145 -17
- package/core/package.js +128 -36
- package/core/plugin-loader.d.ts +65 -0
- package/core/plugin-loader.js +234 -0
- package/core/plugin.d.ts +135 -90
- package/core/plugin.js +70 -173
- package/core/publish-target.d.ts +124 -0
- package/core/publish-target.js +30 -0
- package/core/registry.d.ts +30 -0
- package/core/registry.js +47 -0
- package/core/repository.d.ts +72 -9
- package/core/repository.js +293 -44
- package/core/resolve-target.d.ts +1 -1
- package/core/resolve-target.js +1 -1
- package/core/service.d.ts +49 -0
- package/core/service.js +40 -0
- package/core/version-scheme.d.ts +23 -1
- package/core/version-scheme.js +29 -1
- package/core/workspace.d.ts +84 -33
- package/core/workspace.js +63 -22
- package/index.d.ts +111 -14
- package/index.js +85 -9
- package/interfaces/rman-config.interface.d.ts +725 -202
- package/interfaces/rman-config.interface.js +61 -1
- package/package.json +2 -1
- package/plugins/builtins.d.ts +44 -0
- package/plugins/builtins.js +33 -0
- package/plugins/detect.d.ts +78 -0
- package/plugins/detect.js +70 -0
- package/plugins/node/augmentation/rman.augmentation.d.ts +84 -0
- package/plugins/node/augmentation/rman.augmentation.js +1 -0
- package/plugins/node/augmentation/system-info.augmentation.d.ts +26 -0
- package/plugins/node/augmentation/system-info.augmentation.js +79 -0
- package/plugins/node/commands/ci.command.d.ts +131 -0
- package/plugins/node/commands/ci.command.js +59 -0
- package/plugins/node/commands/clean.command.d.ts +183 -0
- package/plugins/node/commands/clean.command.js +73 -0
- package/plugins/node/index.d.ts +29 -0
- package/plugins/node/index.js +40 -0
- package/plugins/node/node-config.interface.d.ts +77 -0
- package/plugins/node/node-config.interface.js +7 -0
- package/plugins/node/node-manifest.provider.d.ts +68 -0
- package/plugins/node/node-manifest.provider.js +125 -0
- package/plugins/node/node.platform.d.ts +53 -0
- package/plugins/node/node.platform.js +134 -0
- package/plugins/node/npm-publish-target.d.ts +73 -0
- package/plugins/node/npm-publish-target.js +96 -0
- package/plugins/node/services/ci.service.d.ts +47 -0
- package/plugins/node/services/ci.service.js +213 -0
- package/plugins/node/services/clean.service.d.ts +53 -0
- package/plugins/node/services/clean.service.js +237 -0
- package/plugins/node/services/publish.service.d.ts +114 -0
- package/plugins/node/services/publish.service.js +371 -0
- package/plugins/node/services/version-plan.service.d.ts +44 -0
- package/plugins/node/services/version-plan.service.js +58 -0
- package/plugins/node/utils/npm-view.d.ts +48 -0
- package/plugins/node/utils/npm-view.js +71 -0
- package/plugins/node/utils/workspace-range.d.ts +26 -0
- package/plugins/node/utils/workspace-range.js +28 -0
- package/services/change-hash.service.d.ts +2 -2
- package/services/change-hash.service.js +2 -2
- package/services/changelog.service.d.ts +62 -51
- package/services/changelog.service.js +14 -11
- package/services/docker-publish.service.d.ts +50 -29
- package/services/docker-publish.service.js +43 -20
- package/services/exec.service.d.ts +23 -12
- package/services/exec.service.js +14 -9
- package/services/github-release.service.d.ts +44 -33
- package/services/github-release.service.js +13 -10
- package/services/import.service.d.ts +25 -14
- package/services/import.service.js +9 -5
- package/services/list.service.d.ts +62 -10
- package/services/list.service.js +62 -15
- package/services/run.service.d.ts +22 -13
- package/services/run.service.js +262 -223
- package/services/version-plan.service.d.ts +27 -4
- package/services/version-plan.service.js +42 -15
- package/services/version.service.d.ts +31 -11
- package/services/version.service.js +29 -13
- package/targets/docker.target.d.ts +53 -0
- package/targets/docker.target.js +40 -0
- package/utils/bin-path.d.ts +6 -7
- package/utils/bin-path.js +7 -18
- package/utils/branch-guard.d.ts +29 -0
- package/utils/branch-guard.js +31 -0
- package/utils/exec.d.ts +10 -0
- package/utils/exec.js +1 -1
- package/utils/logger.d.ts +1 -1
- package/utils/logger.js +1 -1
- package/utils/package-filter.d.ts +127 -7
- package/utils/package-filter.js +197 -16
- package/utils/printable-config.d.ts +1 -1
- package/utils/printable-config.js +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-stamp.d.ts +1 -1
- package/utils/version-stamp.js +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Repository } from '../../../core/repository.js';
|
|
2
|
+
import type { LogLevel } from '../../../utils/logger.js';
|
|
3
|
+
import type { PackageFilterOptions } from '../../../utils/package-filter.js';
|
|
4
|
+
export declare namespace CleanService {
|
|
5
|
+
interface Options extends PackageFilterOptions {
|
|
6
|
+
/** Show the live progress panel. Default true; auto-disabled when stdout isn't a TTY. */
|
|
7
|
+
progress?: boolean;
|
|
8
|
+
/** Report what would be removed without actually removing anything. Default false. */
|
|
9
|
+
dryRun?: boolean;
|
|
10
|
+
/** Clean the whole repository even when the current directory is inside a single package
|
|
11
|
+
* (which otherwise scopes cleaning to just that package - see `Repository.currentPackage`).
|
|
12
|
+
* Has no effect when already at the repository root, or outside any known package. */
|
|
13
|
+
fromRoot?: boolean;
|
|
14
|
+
/** Verbosity of the classic per-item log (only applies when the live panel is off). Falls back
|
|
15
|
+
* to the root's `.rmanrc logLevel`, then 'info' - see `resolveRootLogLevel`. */
|
|
16
|
+
logLevel?: LogLevel;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `clean`: removes build output across every package (root included) - the replacement for
|
|
20
|
+
* `ts-cleanup`, plus whatever else `.rmanrc clean.include`/`clean.exclude` says to remove.
|
|
21
|
+
*
|
|
22
|
+
* **The `node` built-in's rather than always present, because what it knows how to delete is not
|
|
23
|
+
* repository-shaped knowledge but TypeScript-shaped**: a compiled `.js`/`.js.map`/`.d.ts` beside
|
|
24
|
+
* its `.ts` source, a `*.tsbuildinfo`, and a `node_modules` to skip while looking. A Cargo or Go
|
|
25
|
+
* repository has `cargo clean` and `go clean` and nothing here would fire for it, so a core
|
|
26
|
+
* `clean` was a command that only appeared to be general.
|
|
27
|
+
*
|
|
28
|
+
* The `clean.include`/`clean.exclude` globs move with it, and that is the one cost worth naming:
|
|
29
|
+
* they are ecosystem-neutral, so a repository wanting only those now has to name this plugin (or
|
|
30
|
+
* write the two `rm` lines as a `run` script). Keeping a stub `clean` in core for them would mean
|
|
31
|
+
* two commands with one name and a precedence rule between them - worse than the honest move.
|
|
32
|
+
*
|
|
33
|
+
* For every package not opted out via its own (cascaded) `clean.skip: true`:
|
|
34
|
+
* - deletes compiled `.js`/`.js.map`/`.d.ts` files under its `src`/`test` (see `cleanTsArtifacts`);
|
|
35
|
+
* - deletes any `*.tsbuildinfo` incremental-build cache file anywhere in it;
|
|
36
|
+
* - deletes anything matching its own (cascaded) `clean.include` glob(s), minus `clean.exclude`.
|
|
37
|
+
*
|
|
38
|
+
* `include`/`exclude` are resolved relative to *that* package's own directory - a root-level
|
|
39
|
+
* `.rmanrc` pattern like `packages/*\/build` is naturally evaluated from the repository root
|
|
40
|
+
* (spanning every package in one pass), while a package's own override in its own `.rmanrc`
|
|
41
|
+
* (e.g. `include: ./cache`) only ever reaches that one package, since a package normally
|
|
42
|
+
* overrides rather than merges with the root's value for the same key.
|
|
43
|
+
*
|
|
44
|
+
* Never touches `node_modules` - that's `ci`'s job, not this one.
|
|
45
|
+
*
|
|
46
|
+
* Run from inside a single package's own directory, it only cleans that package unless
|
|
47
|
+
* `options.fromRoot` says otherwise (see `Repository.currentPackage`).
|
|
48
|
+
*
|
|
49
|
+
* Uses the same live progress panel as `run`/`build` (see `../utils/progress-panel.ts`) - unlike
|
|
50
|
+
* `ci`, cleaning genuinely is independent per-package work, so the final per-package tally stays.
|
|
51
|
+
*/
|
|
52
|
+
function clean(repository: Repository, options?: Options): Promise<void>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import colors from 'ansi-colors';
|
|
4
|
+
import fg from 'fast-glob';
|
|
5
|
+
import { Logger, resolveRootLogLevel } from '../../../utils/logger.js';
|
|
6
|
+
import { filterPackages } from '../../../utils/package-filter.js';
|
|
7
|
+
import { ProgressPanel } from '../../../utils/progress-panel.js';
|
|
8
|
+
export var CleanService;
|
|
9
|
+
(function (CleanService) {
|
|
10
|
+
/**
|
|
11
|
+
* `clean`: removes build output across every package (root included) - the replacement for
|
|
12
|
+
* `ts-cleanup`, plus whatever else `.rmanrc clean.include`/`clean.exclude` says to remove.
|
|
13
|
+
*
|
|
14
|
+
* **The `node` built-in's rather than always present, because what it knows how to delete is not
|
|
15
|
+
* repository-shaped knowledge but TypeScript-shaped**: a compiled `.js`/`.js.map`/`.d.ts` beside
|
|
16
|
+
* its `.ts` source, a `*.tsbuildinfo`, and a `node_modules` to skip while looking. A Cargo or Go
|
|
17
|
+
* repository has `cargo clean` and `go clean` and nothing here would fire for it, so a core
|
|
18
|
+
* `clean` was a command that only appeared to be general.
|
|
19
|
+
*
|
|
20
|
+
* The `clean.include`/`clean.exclude` globs move with it, and that is the one cost worth naming:
|
|
21
|
+
* they are ecosystem-neutral, so a repository wanting only those now has to name this plugin (or
|
|
22
|
+
* write the two `rm` lines as a `run` script). Keeping a stub `clean` in core for them would mean
|
|
23
|
+
* two commands with one name and a precedence rule between them - worse than the honest move.
|
|
24
|
+
*
|
|
25
|
+
* For every package not opted out via its own (cascaded) `clean.skip: true`:
|
|
26
|
+
* - deletes compiled `.js`/`.js.map`/`.d.ts` files under its `src`/`test` (see `cleanTsArtifacts`);
|
|
27
|
+
* - deletes any `*.tsbuildinfo` incremental-build cache file anywhere in it;
|
|
28
|
+
* - deletes anything matching its own (cascaded) `clean.include` glob(s), minus `clean.exclude`.
|
|
29
|
+
*
|
|
30
|
+
* `include`/`exclude` are resolved relative to *that* package's own directory - a root-level
|
|
31
|
+
* `.rmanrc` pattern like `packages/*\/build` is naturally evaluated from the repository root
|
|
32
|
+
* (spanning every package in one pass), while a package's own override in its own `.rmanrc`
|
|
33
|
+
* (e.g. `include: ./cache`) only ever reaches that one package, since a package normally
|
|
34
|
+
* overrides rather than merges with the root's value for the same key.
|
|
35
|
+
*
|
|
36
|
+
* Never touches `node_modules` - that's `ci`'s job, not this one.
|
|
37
|
+
*
|
|
38
|
+
* Run from inside a single package's own directory, it only cleans that package unless
|
|
39
|
+
* `options.fromRoot` says otherwise (see `Repository.currentPackage`).
|
|
40
|
+
*
|
|
41
|
+
* Uses the same live progress panel as `run`/`build` (see `../utils/progress-panel.ts`) - unlike
|
|
42
|
+
* `ci`, cleaning genuinely is independent per-package work, so the final per-package tally stays.
|
|
43
|
+
*/
|
|
44
|
+
async function clean(repository, options = {}) {
|
|
45
|
+
/** Standing inside a single package's own directory scopes cleaning to just that package
|
|
46
|
+
* (root's own artifacts included) unless `--from-root` asks for the whole repository anyway - a
|
|
47
|
+
* no-op when already at the root, or outside any known package. */
|
|
48
|
+
const cwdScope = options.fromRoot ? undefined : repository.currentPackage;
|
|
49
|
+
const packages = repository.getPackages().filter(p => p !== repository.rootPackage);
|
|
50
|
+
const allTargets = cwdScope ? [cwdScope] : [repository.rootPackage, ...packages];
|
|
51
|
+
const targets = filterPackages(allTargets, options).filter(pkg => !cleanConfig(pkg).skip);
|
|
52
|
+
const dryRun = options.dryRun ?? false;
|
|
53
|
+
const progress = options.progress ?? true;
|
|
54
|
+
const logger = new Logger(options.logLevel ?? resolveRootLogLevel(repository));
|
|
55
|
+
const panel = new ProgressPanel(dryRun ? 'CLEAN (dry-run)' : 'CLEAN', !!process.stdout.isTTY && progress);
|
|
56
|
+
panel.start();
|
|
57
|
+
let failed = false;
|
|
58
|
+
try {
|
|
59
|
+
const results = await Promise.allSettled(targets.map(pkg => {
|
|
60
|
+
const label = pkg === repository.rootPackage ? 'root' : pkg.name;
|
|
61
|
+
return cleanPackage(pkg, panel.addItem(label), panel.enabled, dryRun, logger);
|
|
62
|
+
}));
|
|
63
|
+
if (results.some(r => r.status === 'rejected'))
|
|
64
|
+
failed = true;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
panel.stop();
|
|
68
|
+
}
|
|
69
|
+
panel.printSummary();
|
|
70
|
+
if (failed) {
|
|
71
|
+
const err = new Error('"clean" failed');
|
|
72
|
+
err.logged = true;
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
CleanService.clean = clean;
|
|
77
|
+
})(CleanService || (CleanService = {}));
|
|
78
|
+
/** Directories a package's TypeScript output gets cleaned from, mirroring `ts-cleanup -s src`
|
|
79
|
+
* and `-s test` (the only way it was ever actually invoked in this project's own scripts). */
|
|
80
|
+
const TS_SOURCE_DIRS = ['src', 'test'];
|
|
81
|
+
/** A package's (cascaded) `.rmanrc clean.include`/`clean.exclude`/`clean.skip` - a package that
|
|
82
|
+
* declares its own `clean` block replaces the root's entirely for itself (same as any other
|
|
83
|
+
* rman config key), rather than combining with it. `include`/`exclude` accept a single glob
|
|
84
|
+
* string or an array of them. */
|
|
85
|
+
function cleanConfig(pkg) {
|
|
86
|
+
const cfg = pkg.config?.clean;
|
|
87
|
+
const normalize = (v) => {
|
|
88
|
+
if (Array.isArray(v))
|
|
89
|
+
return v.map(String);
|
|
90
|
+
return typeof v === 'string' && v ? [v] : [];
|
|
91
|
+
};
|
|
92
|
+
return { include: normalize(cfg?.include), exclude: normalize(cfg?.exclude), skip: cfg?.skip === true };
|
|
93
|
+
}
|
|
94
|
+
/** Deletes `file`, unless `dryRun` - either way the caller treats it as removed for reporting. */
|
|
95
|
+
async function remove(file, dryRun) {
|
|
96
|
+
if (!dryRun)
|
|
97
|
+
await fs.promises.rm(file, { force: true });
|
|
98
|
+
}
|
|
99
|
+
/** Removes now-empty directories under `dir`, deepest first (so a parent left empty once its
|
|
100
|
+
* only child is removed gets caught in the same pass). Also removes `dir` itself once empty
|
|
101
|
+
* when `includeSelf` is set - used when `dir` is itself something the user asked to delete
|
|
102
|
+
* (e.g. an `include: build` match), as opposed to a source root like `src`/`test` that should
|
|
103
|
+
* stay even if everything inside it was cleaned out. No-op in dry-run mode: nothing was actually
|
|
104
|
+
* deleted, so there's nothing that could have been left empty. */
|
|
105
|
+
function pruneEmptyDirs(dir, includeSelf, dryRun) {
|
|
106
|
+
if (dryRun || !fs.existsSync(dir))
|
|
107
|
+
return;
|
|
108
|
+
const entries = fg.sync('**', { cwd: dir, onlyDirectories: true, dot: true });
|
|
109
|
+
entries.sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
|
|
110
|
+
for (const rel of entries) {
|
|
111
|
+
const abs = path.join(dir, rel);
|
|
112
|
+
if (fs.existsSync(abs) && fs.readdirSync(abs).length === 0)
|
|
113
|
+
fs.rmdirSync(abs);
|
|
114
|
+
}
|
|
115
|
+
if (includeSelf && fs.readdirSync(dir).length === 0)
|
|
116
|
+
fs.rmdirSync(dir);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Deletes every path matched by `include` (minus `exclude`) under `dirname` - files and
|
|
120
|
+
* directories alike, gulp-`src()`-style. Returns what was actually removed (or, in dry-run mode,
|
|
121
|
+
* what *would* be), relative to `dirname`, so the caller only logs what really happened.
|
|
122
|
+
*
|
|
123
|
+
* `exclude` protects at two levels: a pattern matching a whole `include` result directly (e.g.
|
|
124
|
+
* `packages/pkg1/*` against a matched `packages/pkg1/build` directory) drops that result before
|
|
125
|
+
* anything under it is touched; a finer pattern (e.g. `build/*.json`) instead protects just the
|
|
126
|
+
* matching files *inside* an otherwise-deleted directory, leaving the rest of it gone and the
|
|
127
|
+
* protected files (and their now non-empty parent) in place.
|
|
128
|
+
*/
|
|
129
|
+
async function cleanGlobs(dirname, include, exclude, dryRun) {
|
|
130
|
+
if (!include.length)
|
|
131
|
+
return [];
|
|
132
|
+
const matches = await fg(include, { cwd: dirname, ignore: exclude, onlyFiles: false, dot: true, absolute: true });
|
|
133
|
+
const protectedFiles = exclude.length
|
|
134
|
+
? new Set(await fg(exclude, { cwd: dirname, onlyFiles: true, dot: true, absolute: true }))
|
|
135
|
+
: new Set();
|
|
136
|
+
const removed = [];
|
|
137
|
+
for (const m of matches) {
|
|
138
|
+
const stat = await fs.promises.stat(m).catch(() => undefined);
|
|
139
|
+
if (!stat)
|
|
140
|
+
continue;
|
|
141
|
+
if (stat.isDirectory()) {
|
|
142
|
+
const filesInside = await fg('**', { cwd: m, onlyFiles: true, dot: true, absolute: true });
|
|
143
|
+
for (const f of filesInside) {
|
|
144
|
+
if (protectedFiles.has(f))
|
|
145
|
+
continue;
|
|
146
|
+
await remove(f, dryRun);
|
|
147
|
+
removed.push(path.relative(dirname, f));
|
|
148
|
+
}
|
|
149
|
+
pruneEmptyDirs(m, true, dryRun);
|
|
150
|
+
}
|
|
151
|
+
else if (!protectedFiles.has(m)) {
|
|
152
|
+
await remove(m, dryRun);
|
|
153
|
+
removed.push(path.relative(dirname, m));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return removed;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Removes compiled TypeScript output (`.js`, `.js.map`, `.d.ts`) sitting next to its `.ts`
|
|
160
|
+
* source under `src`/`test` - the same job `ts-cleanup -s <dir> --all` did (the only mode this
|
|
161
|
+
* project ever actually used it in). A `.d.ts` with no matching `.ts`/`.tsx` is left alone - it's
|
|
162
|
+
* presumably a hand-written declaration file, not build output. Prunes directories left empty
|
|
163
|
+
* afterward.
|
|
164
|
+
*/
|
|
165
|
+
async function cleanTsArtifacts(dirname, dryRun) {
|
|
166
|
+
const removed = [];
|
|
167
|
+
for (const sub of TS_SOURCE_DIRS) {
|
|
168
|
+
const dir = path.join(dirname, sub);
|
|
169
|
+
if (!fs.existsSync(dir))
|
|
170
|
+
continue;
|
|
171
|
+
const files = await fg('**/*.{js,js.map,d.ts}', { cwd: dir, onlyFiles: true, dot: true, absolute: true });
|
|
172
|
+
for (const f of files) {
|
|
173
|
+
if (f.endsWith('.d.ts')) {
|
|
174
|
+
const base = f.slice(0, -'.d.ts'.length);
|
|
175
|
+
if (!fs.existsSync(base + '.ts') && !fs.existsSync(base + '.tsx'))
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
await remove(f, dryRun);
|
|
179
|
+
removed.push(path.relative(dirname, f));
|
|
180
|
+
}
|
|
181
|
+
pruneEmptyDirs(dir, false, dryRun);
|
|
182
|
+
}
|
|
183
|
+
return removed;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Removes TypeScript's incremental-build cache files (`*.tsbuildinfo`) anywhere under `dirname` -
|
|
187
|
+
* `tsc --build`/`composite`/`incremental` output that `ts-cleanup` never touched, but which can
|
|
188
|
+
* leave a project in a stale state if a clean rebuild is expected to start from nothing. Skips
|
|
189
|
+
* `node_modules` (dependencies may ship their own, irrelevant to this package's own build).
|
|
190
|
+
*/
|
|
191
|
+
async function cleanTsBuildInfo(dirname, dryRun) {
|
|
192
|
+
const files = await fg('**/*.tsbuildinfo', {
|
|
193
|
+
cwd: dirname,
|
|
194
|
+
ignore: ['**/node_modules/**'],
|
|
195
|
+
onlyFiles: true,
|
|
196
|
+
dot: true,
|
|
197
|
+
absolute: true,
|
|
198
|
+
});
|
|
199
|
+
for (const f of files)
|
|
200
|
+
await remove(f, dryRun);
|
|
201
|
+
return files.map(f => path.relative(dirname, f));
|
|
202
|
+
}
|
|
203
|
+
async function cleanPackage(pkg, item, panelEnabled, dryRun, logger) {
|
|
204
|
+
item.status = 'running';
|
|
205
|
+
item.startedAt = Date.now();
|
|
206
|
+
try {
|
|
207
|
+
item.currentStep = 'ts';
|
|
208
|
+
const tsRemoved = await cleanTsArtifacts(pkg.dirname, dryRun);
|
|
209
|
+
const buildInfoRemoved = await cleanTsBuildInfo(pkg.dirname, dryRun);
|
|
210
|
+
item.currentStep = 'glob';
|
|
211
|
+
const { include, exclude } = cleanConfig(pkg);
|
|
212
|
+
const globRemoved = await cleanGlobs(pkg.dirname, include, exclude, dryRun);
|
|
213
|
+
const removed = [...tsRemoved, ...buildInfoRemoved, ...globRemoved];
|
|
214
|
+
const verb = dryRun ? 'would rm' : 'rm';
|
|
215
|
+
if (panelEnabled) {
|
|
216
|
+
item.lastLine = removed.length
|
|
217
|
+
? `${dryRun ? 'would remove' : 'removed'} ${removed.length} item(s)`
|
|
218
|
+
: 'already clean';
|
|
219
|
+
}
|
|
220
|
+
else if (removed.length) {
|
|
221
|
+
for (const r of removed)
|
|
222
|
+
logger.info(colors.yellow(verb), colors.cyan(item.name), r);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
logger.info(colors.gray('clean'), colors.cyan(item.name));
|
|
226
|
+
}
|
|
227
|
+
item.status = 'success';
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
item.status = 'failed';
|
|
231
|
+
item.log.push(e?.message ?? String(e));
|
|
232
|
+
throw e;
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
item.finishedAt = Date.now();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Package } from '../../../core/package.js';
|
|
2
|
+
import type { Repository } from '../../../core/repository.js';
|
|
3
|
+
import type { PackageFilterOptions } from '../../../utils/package-filter.js';
|
|
4
|
+
import { type NpmPackageView } from '../utils/npm-view.js';
|
|
5
|
+
import { CiService } from './ci.service.js';
|
|
6
|
+
export declare namespace PublishService {
|
|
7
|
+
/** Injectable registry lookup - mainly for tests, so they don't depend on network access or a
|
|
8
|
+
* real published package. */
|
|
9
|
+
interface Deps {
|
|
10
|
+
npmViewPackage?: (name: string, cwd: string) => Promise<NpmPackageView | undefined>;
|
|
11
|
+
}
|
|
12
|
+
interface Options extends PackageFilterOptions {
|
|
13
|
+
/** A package with uncommitted local changes is excluded (status `'skip'`) instead of aborting
|
|
14
|
+
* the whole plan (status `'error'`) - same as `version`'s own option. Default false. */
|
|
15
|
+
ignoreDirty?: boolean;
|
|
16
|
+
/** Registry to check against (and, in `applyPlan`, publish to) - `.npmrc`'s own configured
|
|
17
|
+
* registry is used when omitted. */
|
|
18
|
+
registry?: string;
|
|
19
|
+
/** Path to a custom `.npmrc`, for both the registry check and the actual publish. */
|
|
20
|
+
userconfig?: string;
|
|
21
|
+
/**
|
|
22
|
+
* `npm publish --tag <tag>` - the dist-tag to publish under, overriding what the version
|
|
23
|
+
* itself implies.
|
|
24
|
+
*
|
|
25
|
+
* **Usually unnecessary**: a prerelease is published under its own identifier (`2.0.0-beta.1`
|
|
26
|
+
* -> `beta`) and a release under npm's `latest`, neither needing to be asked for. Pass this to
|
|
27
|
+
* send a release somewhere other than `latest`, or a prerelease to a tag that is not its
|
|
28
|
+
* identifier (`next` for every preview, say). See `distTagFor`.
|
|
29
|
+
*
|
|
30
|
+
* **A plan option rather than an apply-only one**, because the tag is decided in the plan: it
|
|
31
|
+
* is printed beside each package and recorded as `Entry.distTag`, so `--dry-run` and the JSON
|
|
32
|
+
* a release pipeline gates on both show where a version is going.
|
|
33
|
+
*/
|
|
34
|
+
tag?: string;
|
|
35
|
+
}
|
|
36
|
+
interface ApplyOptions extends Options {
|
|
37
|
+
packageManager?: CiService.PackageManager;
|
|
38
|
+
/** `npm publish --access <access>` - required by the registry for a *new* scoped package. */
|
|
39
|
+
access?: 'public' | 'restricted';
|
|
40
|
+
/** `npm publish --otp <otp>` - a 2FA one-time password, for registries that require it. */
|
|
41
|
+
otp?: string;
|
|
42
|
+
/** Subdirectory to publish from, relative to the package's own directory - only consulted when
|
|
43
|
+
* the package doesn't already declare its own `package.json` `publishConfig.directory` (npm's
|
|
44
|
+
* native mechanism for this, which always wins when present). */
|
|
45
|
+
contents?: string;
|
|
46
|
+
}
|
|
47
|
+
/** One package's outcome in a publish plan - see `getPlan`. */
|
|
48
|
+
interface Entry {
|
|
49
|
+
package: Package;
|
|
50
|
+
version: string;
|
|
51
|
+
status: 'publish' | 'skip' | 'up-to-date' | 'error';
|
|
52
|
+
/** What the registry's **`latest` dist-tag** points at, if anything - only set once a registry
|
|
53
|
+
* check actually ran. Reported rather than compared: whether *this* version is published is a
|
|
54
|
+
* different question, and `getPlan` answers it from the published `versions`. */
|
|
55
|
+
registryVersion?: string;
|
|
56
|
+
/**
|
|
57
|
+
* The dist-tag this entry will publish under - `--tag` when given, otherwise the version's own
|
|
58
|
+
* prerelease identifier (`2.0.0-beta.1` -> `beta`), and `undefined` for an ordinary release,
|
|
59
|
+
* which npm puts on `latest`.
|
|
60
|
+
*
|
|
61
|
+
* **Decided in the plan and read back by `applyPlan`, rather than recomputed there.** The plan
|
|
62
|
+
* is what a reader confirms and what a pipeline gates on, so the tag has to be part of what it
|
|
63
|
+
* says; recomputing at publish time would let the two disagree about where a package is going.
|
|
64
|
+
*/
|
|
65
|
+
distTag?: string;
|
|
66
|
+
/** `PublishTarget.Entry`'s own field - what the core's `publish` prints beside the package
|
|
67
|
+
* name. Carries the dist-tag, so where a version is going is visible in the plan and in
|
|
68
|
+
* `--dry-run --json` without the core knowing anything about npm. */
|
|
69
|
+
detail?: string;
|
|
70
|
+
reason?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Computes what `publish` *would* do, across every non-private package (topological order,
|
|
74
|
+
* dependencies before dependents) - never touches the registry to publish anything, just
|
|
75
|
+
* queries it to decide, so it's safe to call any time, including as the plan a bare `rman
|
|
76
|
+
* publish` shows before asking for confirmation.
|
|
77
|
+
*
|
|
78
|
+
* A `private: true` package is always `'skip'`ped outright. Each remaining package's **dist-tag**
|
|
79
|
+
* is settled next (`distTagFor`): usually derived from the version itself, `'error'` in the two
|
|
80
|
+
* cases with nothing to derive - that one is about the invocation rather than the package, so it
|
|
81
|
+
* is answered before anything touches the network. A package with uncommitted local changes is
|
|
82
|
+
* `'error'` too (aborts the whole plan) unless `options.ignoreDirty` downgrades it to `'skip'`
|
|
83
|
+
* instead - same rule `version` uses, since publishing untracked local edits is worse than a bad
|
|
84
|
+
* commit.
|
|
85
|
+
*
|
|
86
|
+
* Otherwise the registry decides, via one `npmViewPackage` per remaining package (run
|
|
87
|
+
* concurrently): **the local version being among the published `versions`** is `'up-to-date'`,
|
|
88
|
+
* anything else - including never having been published at all - is `'publish'`. Deliberately not
|
|
89
|
+
* a comparison against `latest`, which answers a different question and disagrees with this one
|
|
90
|
+
* the moment a prerelease ships under its own dist-tag; see `npmViewPackage`.
|
|
91
|
+
*
|
|
92
|
+
* Deliberately decoupled from `version`: this only ever looks at what's *currently* on disk and
|
|
93
|
+
* on the registry, never at whether `version` was just run - so it works equally well right
|
|
94
|
+
* after a version bump, or standing alone in a release pipeline that bumped days earlier.
|
|
95
|
+
*
|
|
96
|
+
* A monorepo's root package is never a candidate at all - `repository.getPackages()` already
|
|
97
|
+
* excludes it for a real monorepo (it only doubles as "the" package in a single-package repo,
|
|
98
|
+
* where it's a normal candidate like any other).
|
|
99
|
+
*/
|
|
100
|
+
function getPlan(repository: Repository, options?: Options, deps?: Deps): Promise<Entry[]>;
|
|
101
|
+
/**
|
|
102
|
+
* Publishes every `'publish'` entry in `plan`, topological order (already `plan`'s own order -
|
|
103
|
+
* see `getPlan`), via the configured `packageManager`'s own `publish` command. Sequential, not
|
|
104
|
+
* concurrent: unlike `run`/`build`, a package genuinely needs its own dependencies to have
|
|
105
|
+
* landed on the registry first, and publishing is rare enough (once per release) that the
|
|
106
|
+
* simplicity is worth more than the parallelism `run` gets from `power-tasks`.
|
|
107
|
+
*
|
|
108
|
+
* If a package fails, every other still-pending entry depending on it (transitively) is marked
|
|
109
|
+
* `'error'` too and never attempted - publishing a package whose own new dependency range points
|
|
110
|
+
* at a version that never actually reached the registry would hand consumers a broken install.
|
|
111
|
+
* A package's own failure doesn't stop unrelated packages elsewhere in the plan, though.
|
|
112
|
+
*/
|
|
113
|
+
function applyPlan(repository: Repository, plan: Entry[], options?: ApplyOptions): Promise<Entry[]>;
|
|
114
|
+
}
|