rman 2.0.0-beta.2 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -12
- package/{plugins/node/augmentation/rman.augmentation.d.ts → builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts} +4 -5
- package/{plugins → builtins/platforms}/node/augmentation/system-info.augmentation.js +1 -1
- package/{plugins → builtins/platforms}/node/commands/ci.command.d.ts +5 -3
- package/{plugins → builtins/platforms}/node/commands/ci.command.js +6 -5
- package/{plugins → builtins/platforms}/node/commands/clean.command.d.ts +6 -4
- package/{plugins → builtins/platforms}/node/commands/clean.command.js +6 -5
- package/builtins/platforms/node/index.d.ts +8 -0
- package/builtins/platforms/node/index.js +8 -0
- package/{plugins → builtins/platforms}/node/node-config.interface.d.ts +1 -2
- package/{plugins → builtins/platforms}/node/node-manifest.provider.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/node-manifest.provider.js +3 -3
- package/{plugins → builtins/platforms}/node/node.platform.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/ci.service.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/ci.service.js +4 -4
- package/{plugins → builtins/platforms}/node/services/clean.service.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/clean.service.js +3 -3
- package/{plugins → builtins/platforms}/node/services/publish.service.d.ts +4 -4
- package/{plugins → builtins/platforms}/node/services/publish.service.js +13 -7
- package/{plugins → builtins/platforms}/node/services/version-plan.service.d.ts +3 -3
- package/{plugins → builtins/platforms}/node/services/version-plan.service.js +2 -2
- package/builtins/presets/index.d.ts +15 -0
- package/builtins/presets/index.js +57 -0
- package/builtins/presets/node.d.ts +16 -0
- package/builtins/presets/node.js +57 -0
- package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +6 -6
- package/{services → builtins/publish-targets/docker}/docker-publish.service.js +9 -9
- package/{targets → builtins/publish-targets/docker}/docker.target.d.ts +6 -5
- package/{targets → builtins/publish-targets/docker}/docker.target.js +1 -1
- package/builtins/publish-targets/docker/index.d.ts +2 -0
- package/builtins/publish-targets/docker/index.js +2 -0
- package/builtins/publish-targets/npm/index.d.ts +2 -0
- package/builtins/publish-targets/npm/index.js +2 -0
- package/{plugins/node → builtins/publish-targets/npm}/npm-publish-target.d.ts +4 -4
- package/{plugins/node → builtins/publish-targets/npm}/npm-publish-target.js +4 -5
- package/cli.d.ts +3 -0
- package/cli.js +16 -7
- package/commands/changelog.command.d.ts +106 -2
- package/commands/changelog.command.js +40 -0
- package/commands/config.command.js +23 -5
- package/commands/list.command.js +1 -1
- package/commands/publish.command.d.ts +1 -1
- package/commands/publish.command.js +1 -1
- package/commands/run.command.js +1 -1
- package/commands/version.command.d.ts +16 -3
- package/commands/version.command.js +143 -9
- package/commands.d.ts +0 -1
- package/commands.js +0 -1
- package/constants.js +1 -1
- package/core/application.d.ts +10 -10
- package/core/application.js +28 -11
- package/core/{package.d.ts → classes/package.d.ts} +12 -4
- package/core/{package.js → classes/package.js} +17 -1
- package/core/{repository.d.ts → classes/repository.d.ts} +22 -36
- package/core/{repository.js → classes/repository.js} +112 -241
- package/core/{service.d.ts → classes/service.d.ts} +2 -2
- package/core/classes/workspace.d.ts +277 -0
- package/core/classes/workspace.js +585 -0
- package/core/command-builder.js +17 -1
- package/core/config/config-file-scope.d.ts +107 -0
- package/core/config/config-file-scope.js +226 -0
- package/core/config/config-interpolator.d.ts +164 -0
- package/core/config/config-interpolator.js +577 -0
- package/core/config/config-paths.d.ts +65 -0
- package/core/config/config-paths.js +78 -0
- package/core/config/config-reader.d.ts +215 -0
- package/core/config/config-reader.js +851 -0
- package/core/{merge-config.js → config/merge-config.js} +4 -8
- package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +5 -5
- package/core/{manifest.d.ts → interfaces/manifest.d.ts} +3 -3
- package/core/{manifest.js → interfaces/manifest.js} +2 -2
- package/core/{plugin.d.ts → interfaces/plugin.d.ts} +44 -27
- package/core/{plugin.js → interfaces/plugin.js} +48 -10
- package/core/{publish-target.d.ts → interfaces/publish-target.d.ts} +24 -12
- package/core/{publish-target.js → interfaces/publish-target.js} +16 -3
- package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
- package/core/plugin-loader.d.ts +11 -51
- package/core/plugin-loader.js +21 -202
- package/index.d.ts +29 -28
- package/index.js +20 -46
- package/interfaces/config-scope.interface.d.ts +223 -0
- package/interfaces/config-scope.interface.js +1 -0
- package/interfaces/rman-config.interface.d.ts +100 -5
- package/interfaces/rman-config.interface.js +17 -0
- package/package.json +3 -2
- package/services/change-hash.service.d.ts +40 -6
- package/services/change-hash.service.js +90 -11
- package/services/changelog.service.d.ts +31 -3
- package/services/changelog.service.js +448 -65
- package/services/config-service.d.ts +8 -0
- package/services/config-service.js +3 -0
- package/services/exec.service.d.ts +2 -2
- package/services/exec.service.js +1 -1
- package/services/github-release.service.d.ts +3 -3
- package/services/github-release.service.js +3 -3
- package/services/import.service.d.ts +2 -2
- package/services/import.service.js +1 -1
- package/services/list.service.d.ts +9 -7
- package/services/list.service.js +2 -2
- package/services/run.service.d.ts +15 -11
- package/services/run.service.js +62 -18
- package/services/system-info.d.ts +1 -1
- package/services/version-plan.service.d.ts +8 -4
- package/services/version-plan.service.js +54 -21
- package/services/version.service.d.ts +5 -5
- package/services/version.service.js +7 -4
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/utils/branch-guard.d.ts +1 -1
- package/utils/color-yaml.d.ts +8 -0
- package/utils/color-yaml.js +86 -0
- package/utils/git.d.ts +26 -0
- package/utils/git.js +65 -0
- package/utils/logger.d.ts +1 -1
- package/utils/package-filter.d.ts +8 -1
- package/utils/package-filter.js +7 -0
- package/utils/printable-config.d.ts +8 -0
- package/utils/printable-config.js +45 -0
- package/utils/progress-panel.d.ts +9 -0
- package/utils/progress-panel.js +23 -1
- package/utils/release-version.d.ts +1 -1
- package/utils/run-options.js +1 -1
- package/utils/version-group.d.ts +31 -0
- package/utils/version-group.js +43 -0
- package/commands/changed.command.d.ts +0 -80
- package/commands/changed.command.js +0 -36
- package/core/config.d.ts +0 -530
- package/core/config.js +0 -1123
- package/core/core-services.d.ts +0 -14
- package/core/core-services.js +0 -30
- package/core/core-targets.d.ts +0 -14
- package/core/core-targets.js +0 -16
- package/core/extends-config.d.ts +0 -35
- package/core/extends-config.js +0 -129
- package/core/load-config-module.d.ts +0 -28
- package/core/load-config-module.js +0 -42
- package/core/workspace.d.ts +0 -119
- package/core/workspace.js +0 -124
- package/plugins/builtins.d.ts +0 -44
- package/plugins/builtins.js +0 -33
- package/plugins/detect.d.ts +0 -78
- package/plugins/detect.js +0 -70
- package/plugins/node/index.d.ts +0 -29
- package/plugins/node/index.js +0 -40
- /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
- /package/{plugins → builtins/platforms}/node/augmentation/system-info.augmentation.d.ts +0 -0
- /package/{plugins → builtins/platforms}/node/node-config.interface.js +0 -0
- /package/{plugins → builtins/platforms}/node/node.platform.js +0 -0
- /package/{plugins → builtins/platforms}/node/utils/workspace-range.d.ts +0 -0
- /package/{plugins → builtins/platforms}/node/utils/workspace-range.js +0 -0
- /package/{plugins/node/utils → builtins/publish-targets/npm}/npm-view.d.ts +0 -0
- /package/{plugins/node/utils → builtins/publish-targets/npm}/npm-view.js +0 -0
- /package/core/{registry.d.ts → classes/registry.d.ts} +0 -0
- /package/core/{registry.js → classes/registry.js} +0 -0
- /package/core/{service.js → classes/service.js} +0 -0
- /package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +0 -0
- /package/core/{version-scheme.js → classes/version-scheme.js} +0 -0
- /package/core/{merge-config.d.ts → config/merge-config.d.ts} +0 -0
- /package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +0 -0
- /package/core/{resolve-target.js → config/resolve-config-target.js} +0 -0
- /package/core/{custom-command.js → interfaces/custom-command.js} +0 -0
- /package/{plugins/node/augmentation/rman.augmentation.js → core/interfaces/run-step.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { Service } from '../core/service.js';
|
|
3
|
+
import { Service } from '../core/classes/service.js';
|
|
4
4
|
import { GitHelper } from '../utils/git.js';
|
|
5
5
|
import { filterPackages } from '../utils/package-filter.js';
|
|
6
6
|
import { ChangeHashService } from './change-hash.service.js';
|
|
@@ -18,9 +18,24 @@ export class ChangelogService extends Service {
|
|
|
18
18
|
* the CLI command - printing what happened is the command's job.
|
|
19
19
|
*/
|
|
20
20
|
async generateToFile(options = {}) {
|
|
21
|
-
const entries = await this.getEntries(options);
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const entries = await this.getEntries({ ...options, write: true });
|
|
22
|
+
/**
|
|
23
|
+
* **Where each file is documented up to, recorded in the file itself.** Read back by
|
|
24
|
+
* `resolveBoundary` on the next run, which is what makes a second `--write` append the commits
|
|
25
|
+
* since this one rather than re-emitting everything since the last tag.
|
|
26
|
+
*
|
|
27
|
+
* **Each entry's own last commit, not HEAD** - and that distinction is not bookkeeping.
|
|
28
|
+
* Measured with HEAD: under `changelog.unreleased: false` the run wrote the released history
|
|
29
|
+
* and then marked the file as documented up to HEAD, so the commits it had deliberately *not*
|
|
30
|
+
* written fell behind the boundary. The release that followed found nothing after the marker
|
|
31
|
+
* and produced no entry at all - the work silently gone from the changelog for good.
|
|
32
|
+
*
|
|
33
|
+
* Entries are ordered oldest first and each prepend rewrites the one marker, so the last write
|
|
34
|
+
* for a file leaves the newest documented commit behind it.
|
|
35
|
+
*/
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
prependToChangelogFile(entry.package, entry.filePath, entry.content, entry.documentedUpTo);
|
|
38
|
+
}
|
|
24
39
|
return entries;
|
|
25
40
|
}
|
|
26
41
|
/**
|
|
@@ -83,42 +98,71 @@ export class ChangelogService extends Service {
|
|
|
83
98
|
// No boundary at all means nothing has ever been released, so everything so far is
|
|
84
99
|
// unreleased - the same fallback `VersionService` makes. (Not "not yet pushed": that reads
|
|
85
100
|
// as empty the moment a first release is pushed, and for a repo with no remote at all.)
|
|
86
|
-
|
|
101
|
+
//
|
|
102
|
+
/**
|
|
103
|
+
* **Release markers are dropped per segment, not here.** A release tag sits on exactly the
|
|
104
|
+
* commit this filter removes, so dropping first leaves `splitByRelease` matching tags
|
|
105
|
+
* against shas that are no longer in the list - every cut silently missed, and the whole
|
|
106
|
+
* range rendered as one release. Measured while writing it.
|
|
107
|
+
*/
|
|
108
|
+
promise = hash ? git.listCommits({ hash }) : git.listAllCommits();
|
|
87
109
|
commitsByHash.set(key, promise);
|
|
88
110
|
}
|
|
89
111
|
return promise;
|
|
90
112
|
};
|
|
91
113
|
const commitsByTarget = await Promise.all(targets.map(async (pkg) => {
|
|
92
|
-
const
|
|
93
|
-
const from = await
|
|
94
|
-
from: options.from,
|
|
95
|
-
catchUpFile: fs.existsSync(catchUpFile) ? catchUpFile : undefined,
|
|
96
|
-
});
|
|
114
|
+
const changelogFile = path.join(pkg.dirname, resolveFilePath(pkg, options.filePath));
|
|
115
|
+
const from = await resolveBoundary(git, pkg, changelogFile, options);
|
|
97
116
|
return listCommitsCached(from);
|
|
98
117
|
}));
|
|
99
118
|
const entries = [];
|
|
100
119
|
for (let i = 0; i < targets.length; i++) {
|
|
101
120
|
const pkg = targets[i];
|
|
102
|
-
const ownCommits = commitsByTarget[i].filter(c => ownersOf(repository, c).has(pkg));
|
|
103
|
-
if (!ownCommits.length)
|
|
104
|
-
continue;
|
|
105
|
-
const grouped = groupCommits(ownCommits.map(c => c.subject), ignoreTypesConfig(pkg));
|
|
106
|
-
// every commit could have been dropped by ignoreTypes - skip this package's entry entirely
|
|
107
|
-
// rather than rendering a heading with nothing real underneath it.
|
|
108
|
-
if (!grouped.features.length && !grouped.fixes.length && !grouped.other.length)
|
|
109
|
-
continue;
|
|
110
121
|
const label = pkg === repository.rootPackage ? `${path.basename(repository.dirname)} repository` : pkg.name;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
/**
|
|
123
|
+
* **One entry per release in the range, not one entry per package.** The range is whatever
|
|
124
|
+
* the boundary opened up, and a release tag inside it ends a release: a changelog that does
|
|
125
|
+
* not cut there is not a changelog, it is a commit list. Measured on a real repository - a
|
|
126
|
+
* first `--write` with no changelog file reaches back through twelve tags, and every commit
|
|
127
|
+
* in all of them landed under a single `v2.1.6` heading.
|
|
128
|
+
*
|
|
129
|
+
* Ordinary runs are unaffected by construction: the boundary is the last release, so no tag
|
|
130
|
+
* falls inside the range and there is exactly one segment - which is what `version
|
|
131
|
+
* --changelog` and `github-release` see too, since both pass an explicit boundary.
|
|
132
|
+
*/
|
|
133
|
+
const floor = await resolveStartingPoint(git, pkg, options);
|
|
134
|
+
const floorIndex = floor?.sha ? commitsByTarget[i].findIndex(c => c.sha === floor.sha) : -1;
|
|
135
|
+
const withUnreleased = resolveUnreleased(pkg, options);
|
|
136
|
+
const titles = resolveTitles(pkg);
|
|
137
|
+
const order = resolveOrder(pkg, titles);
|
|
138
|
+
for (const segment of await splitByRelease(git, pkg, commitsByTarget[i])) {
|
|
139
|
+
if (!segment.tag && !withUnreleased)
|
|
140
|
+
continue;
|
|
141
|
+
if (await isBelowStartingPoint(git, pkg, segment, floor, floorIndex))
|
|
142
|
+
continue;
|
|
143
|
+
const ownCommits = dropVersionBumps(segment.commits).filter(c => ownersOf(repository, c).has(pkg));
|
|
144
|
+
if (!ownCommits.length)
|
|
145
|
+
continue;
|
|
146
|
+
const sections = groupCommits(ownCommits.map(c => c.subject), ignoreTypesConfig(pkg), titles, order);
|
|
147
|
+
// every commit could have been dropped by ignoreTypes - skip this package's entry entirely
|
|
148
|
+
// rather than rendering a heading with nothing real underneath it.
|
|
149
|
+
if (!sections.length)
|
|
150
|
+
continue;
|
|
151
|
+
const { version, content } = await renderEntry(repository, pkg, label, sections, titles, git, options.version, segment.tag);
|
|
152
|
+
entries.push({
|
|
153
|
+
package: pkg,
|
|
154
|
+
label,
|
|
155
|
+
version,
|
|
156
|
+
/** The last commit this entry covers - what the file's marker records. For a tagged
|
|
157
|
+
* segment that is the release commit itself, so everything up to and including the
|
|
158
|
+
* release is documented and nothing after it is claimed. */
|
|
159
|
+
documentedUpTo: segment.commits[segment.commits.length - 1].sha,
|
|
160
|
+
sections,
|
|
161
|
+
...legacyBuckets(sections, titles),
|
|
162
|
+
content,
|
|
163
|
+
filePath: resolveFilePath(pkg, options.filePath),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
122
166
|
}
|
|
123
167
|
return entries;
|
|
124
168
|
}
|
|
@@ -131,41 +175,142 @@ function ignoreTypesConfig(pkg) {
|
|
|
131
175
|
const v = pkg.config?.changelog?.ignoreTypes;
|
|
132
176
|
return new Set(Array.isArray(v) ? v.map(t => String(t).toLowerCase()) : []);
|
|
133
177
|
}
|
|
134
|
-
|
|
135
|
-
|
|
178
|
+
/**
|
|
179
|
+
* **The headings, and the order they come out in** - `.rmanrc changelog.titles`, merged **per key**
|
|
180
|
+
* over the defaults rather than replacing them.
|
|
181
|
+
*
|
|
182
|
+
* Merging is the `vars` rule rather than a new exception, and it is what the shape of the key asks
|
|
183
|
+
* for: naming `dev` adds a section, and should not silently cost a repository its `feat` and `fix`.
|
|
184
|
+
* Renaming `feat` leaves it where it was, so the running order survives a rename. The cost, stated:
|
|
185
|
+
* a default section cannot be *removed* by leaving it out - `changelog.ignoreTypes` is the key that
|
|
186
|
+
* drops a type entirely.
|
|
187
|
+
*
|
|
188
|
+
* `'*'` is the heading for every type nobody named, and is always last however it was declared -
|
|
189
|
+
* a catch-all in the middle of the order would silently swallow the sections after it.
|
|
190
|
+
*/
|
|
191
|
+
function resolveTitles(pkg) {
|
|
192
|
+
const titles = new Map(DEFAULT_TITLES);
|
|
193
|
+
const cfg = pkg.config?.changelog?.titles;
|
|
194
|
+
if (cfg && typeof cfg === 'object') {
|
|
195
|
+
for (const [type, title] of Object.entries(cfg)) {
|
|
196
|
+
if (typeof title === 'string' && title)
|
|
197
|
+
titles.set(type.toLowerCase(), title);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return titles;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* One list of lines per heading, in the heading order, empty ones dropped.
|
|
204
|
+
*
|
|
205
|
+
* **The type prefix is stripped for every section, not just two.** It used to be `push(line)` for
|
|
206
|
+
* `feat`/`fix` and `push(subject)` for everything else, so Features read `- the first feature`
|
|
207
|
+
* while Other Changes read `- chore: write changelog` - the heading naming the type and the bullet
|
|
208
|
+
* repeating it. With every type able to have a heading of its own that asymmetry has no defence
|
|
209
|
+
* left. A subject that is not Conventional Commits at all has no prefix to strip and is kept whole.
|
|
210
|
+
*/
|
|
211
|
+
function groupCommits(subjects, ignoreTypes, titles, order) {
|
|
212
|
+
const CATCH_ALL = '*';
|
|
213
|
+
const byTitle = new Map();
|
|
214
|
+
const titleFor = (type) => titles.get(type) ?? titles.get(CATCH_ALL) ?? DEFAULT_TITLES.get(CATCH_ALL);
|
|
215
|
+
const push = (title, line) => {
|
|
216
|
+
const lines = byTitle.get(title);
|
|
217
|
+
if (lines)
|
|
218
|
+
lines.push(line);
|
|
219
|
+
else
|
|
220
|
+
byTitle.set(title, [line]);
|
|
221
|
+
};
|
|
136
222
|
for (const subject of subjects) {
|
|
137
223
|
const parsed = ConventionalCommitsService.parseSubject(subject);
|
|
138
224
|
if (!parsed) {
|
|
139
|
-
|
|
225
|
+
push(titleFor(CATCH_ALL), subject);
|
|
140
226
|
continue;
|
|
141
227
|
}
|
|
142
228
|
const { type, scope, description } = parsed;
|
|
143
229
|
if (ignoreTypes.has(type))
|
|
144
230
|
continue;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
231
|
+
push(titleFor(type), scope ? `**${scope}:** ${description}` : description);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Only a type with a heading of its own takes a position. A type nobody named resolves to the
|
|
235
|
+
* catch-all, so ordering by it would drag the catch-all to wherever that type was listed -
|
|
236
|
+
* measured: `sortTitles: ['docs', 'fix', 'feat']` with no `docs` heading put "Other Changes"
|
|
237
|
+
* first and swallowed the sections after it.
|
|
238
|
+
*/
|
|
239
|
+
const ordered = [...new Set(order.filter(t => t !== CATCH_ALL && titles.has(t)).map(titleFor))];
|
|
240
|
+
ordered.push(titleFor(CATCH_ALL));
|
|
241
|
+
const sections = [];
|
|
242
|
+
for (const title of ordered) {
|
|
243
|
+
const lines = byTitle.get(title);
|
|
244
|
+
if (lines?.length)
|
|
245
|
+
sections.push({ title, lines });
|
|
246
|
+
byTitle.delete(title);
|
|
152
247
|
}
|
|
153
|
-
|
|
248
|
+
/** A heading a repository named for a type nobody used is not rendered; one it named that the
|
|
249
|
+
* ordering above somehow missed still is, rather than being dropped on the floor. */
|
|
250
|
+
for (const [title, lines] of byTitle)
|
|
251
|
+
if (lines.length)
|
|
252
|
+
sections.push({ title, lines });
|
|
253
|
+
return sections;
|
|
154
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* **The order the sections come out in** - `.rmanrc changelog.sortTitles`, a list of commit types.
|
|
257
|
+
*
|
|
258
|
+
* Separate from `titles` because they are separate decisions. Order used to fall out of the order
|
|
259
|
+
* `titles` was written in, which quietly meant a repository renaming `feat` was also re-deciding
|
|
260
|
+
* where it sits; a key that patches wording should not move things.
|
|
261
|
+
*
|
|
262
|
+
* Types it leaves out keep their place **after** the listed ones, in the order `titles` knows them
|
|
263
|
+
* (the defaults first, then anything the repository added) - it is a sort, not a filter, and
|
|
264
|
+
* `ignoreTypes` is what drops a type. `'*'` is appended by `groupCommits` whatever this says.
|
|
265
|
+
*/
|
|
266
|
+
function resolveOrder(pkg, titles) {
|
|
267
|
+
const cfg = pkg.config?.changelog?.sortTitles;
|
|
268
|
+
const listed = Array.isArray(cfg) ? cfg.map(t => String(t).toLowerCase()) : [];
|
|
269
|
+
return [...new Set([...listed, ...titles.keys()])];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* `features`/`fixes`/`other` derived back out of the sections, for `{{features}}`/`{{fixes}}`/
|
|
273
|
+
* `{{other}}` and for `Entry`'s own three fields.
|
|
274
|
+
*
|
|
275
|
+
* Both were the public surface before headings were configurable, so they keep meaning what they
|
|
276
|
+
* meant: whatever `feat` and `fix` are listed under, and everything else together. A repository
|
|
277
|
+
* that renames those headings still gets them here; one that adds `dev` finds it in `other`, which
|
|
278
|
+
* is the only honest place for it in a shape that has three slots.
|
|
279
|
+
*/
|
|
280
|
+
function legacyBuckets(sections, titles) {
|
|
281
|
+
const of = (type) => sections.find(s => s.title === titles.get(type))?.lines ?? [];
|
|
282
|
+
const features = of('feat');
|
|
283
|
+
const fixes = of('fix');
|
|
284
|
+
const other = sections.filter(s => s.lines !== features && s.lines !== fixes).flatMap(s => s.lines);
|
|
285
|
+
return { features, fixes, other };
|
|
286
|
+
}
|
|
287
|
+
/** `feat`/`fix`/everything else, which is what rman shipped before `changelog.titles` existed and
|
|
288
|
+
* so is what a repository that sets nothing still gets, wording and order alike. */
|
|
289
|
+
const DEFAULT_TITLES = new Map([
|
|
290
|
+
['feat', '✨ Features'],
|
|
291
|
+
['fix', '🐛 Bug Fixes'],
|
|
292
|
+
['*', '🔧 Other Changes'],
|
|
293
|
+
]);
|
|
155
294
|
function bulletList(lines) {
|
|
156
295
|
return lines.map(l => `- ${l}`).join('\n');
|
|
157
296
|
}
|
|
158
|
-
function renderCommitsBlock(
|
|
159
|
-
|
|
160
|
-
if (grouped.features.length)
|
|
161
|
-
sections.push(`### ✨ Features\n\n${bulletList(grouped.features)}`);
|
|
162
|
-
if (grouped.fixes.length)
|
|
163
|
-
sections.push(`### 🐛 Bug Fixes\n\n${bulletList(grouped.fixes)}`);
|
|
164
|
-
if (grouped.other.length)
|
|
165
|
-
sections.push(`### 🔧 Other Changes\n\n${bulletList(grouped.other)}`);
|
|
166
|
-
return sections.join('\n\n');
|
|
297
|
+
function renderCommitsBlock(sections) {
|
|
298
|
+
return sections.map(s => `### ${s.title}\n\n${bulletList(s.lines)}`).join('\n\n');
|
|
167
299
|
}
|
|
168
|
-
|
|
300
|
+
/**
|
|
301
|
+
* **`{{title}}`, not `{{package}} {{version}}`** - an entry that a release tag closes is headed by
|
|
302
|
+
* that tag, because the tag is what it describes.
|
|
303
|
+
*
|
|
304
|
+
* The old heading was assembled out of two things that do not always belong together, and the
|
|
305
|
+
* repository root is where that showed: `## panates-javascript repository 2.1.6 (2026-04-30)`
|
|
306
|
+
* states a version the repository does not have - its root package was `panates-style` at `0.0.5`,
|
|
307
|
+
* and `2.1.6` was read off the `v2.1.6` tag. It also reads as one package's notes when the tag
|
|
308
|
+
* covers the whole repository.
|
|
309
|
+
*
|
|
310
|
+
* `{{package}}`, `{{version}}` and `{{tag}}` are all still bound, so a repository that wants the
|
|
311
|
+
* old shape - or the package's name kept beside the tag - writes its own `changelog.template`.
|
|
312
|
+
*/
|
|
313
|
+
const DEFAULT_TEMPLATE = '## {{title}} ({{date}})\n\n{{commits}}\n';
|
|
169
314
|
/** `.rmanrc changelog.template` is a *path* to a template file (resolved relative to the
|
|
170
315
|
* repository root, regardless of which level's `.rmanrc` declared it), not the template text
|
|
171
316
|
* itself - keeping multi-line Markdown out of `.rmanrc` so it stays readable. Falls back to a
|
|
@@ -188,6 +333,103 @@ const DEFAULT_CHANGELOG_FILE = 'CHANGELOG.md';
|
|
|
188
333
|
* `optionsFilePath` (`Changelog.Options.filePath`, CLI `--file-path`) applies the same way to
|
|
189
334
|
* every package and wins over `.rmanrc changelog.filePath` (cascaded, per-package overridable),
|
|
190
335
|
* which in turn wins over the default `'CHANGELOG.md'`. */
|
|
336
|
+
/**
|
|
337
|
+
* Whether the commits that are not released yet get an entry of their own - `.rmanrc
|
|
338
|
+
* changelog.unreleased`, or the flag, **defaulting to `true`**.
|
|
339
|
+
*
|
|
340
|
+
* `auto-changelog` defaults the equivalent off, and the opposite default here is not an oversight:
|
|
341
|
+
* that tool documents a finished history, while `rman changelog` exists to answer what is *not*
|
|
342
|
+
* released yet, down to the message it prints when there is none. Off by default would make the
|
|
343
|
+
* common case need a flag.
|
|
344
|
+
*
|
|
345
|
+
* **A named release is never dropped by it.** `options.version` means the caller is describing the
|
|
346
|
+
* release it is about to cut - `version --changelog` writes that entry before it commits and tags,
|
|
347
|
+
* so the segment is only "unreleased" for the few seconds until it is. Without this, a repository
|
|
348
|
+
* setting `unreleased: false` would find its releases silently documenting nothing.
|
|
349
|
+
*/
|
|
350
|
+
function resolveUnreleased(pkg, options) {
|
|
351
|
+
if (options.version)
|
|
352
|
+
return true;
|
|
353
|
+
const cfg = pkg.config?.changelog?.unreleased;
|
|
354
|
+
return options.unreleased ?? (typeof cfg === 'boolean' ? cfg : true);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* **Where this package's changelog begins** - `.rmanrc changelog.startingAt`, or the CLI flag,
|
|
358
|
+
* which wins as everywhere.
|
|
359
|
+
*
|
|
360
|
+
* The need is a package whose early development is noise: a first `--write` reaches back through
|
|
361
|
+
* every release there has ever been, and for something that has shipped for years most of that is
|
|
362
|
+
* not what a changelog is for. `auto-changelog` spells this as two options, `--starting-version`
|
|
363
|
+
* and `--starting-date`; one key taking whichever form the answer has reads better, and admits a
|
|
364
|
+
* third that neither covers - a commit, for a package whose history does not begin at a tag.
|
|
365
|
+
*
|
|
366
|
+
* **Three forms, decided in this order**, because the shapes overlap and a rule nobody can see is
|
|
367
|
+
* a trap:
|
|
368
|
+
*
|
|
369
|
+
* 1. `YYYY-MM-DD` - a date. Checked first because that shape is unambiguous.
|
|
370
|
+
* 2. a **version or release tag** - `2.0.0`, `v2.0.0`, `@scope/pkg@2.0.0` all resolve to a version,
|
|
371
|
+
* so a repository does not have to know which spelling this key wants.
|
|
372
|
+
* 3. a **commit** anything else git can resolve.
|
|
373
|
+
*
|
|
374
|
+
* A string that is none of the three is a configuration mistake and says so, naming all three. The
|
|
375
|
+
* one genuine collision is a tag whose name is also hex (`deadbee`): the tag wins, because a
|
|
376
|
+
* repository that named a tag that has said what it means.
|
|
377
|
+
*
|
|
378
|
+
* **Not `--from`.** That is this run's boundary, applies identically to every package and overrides
|
|
379
|
+
* detection; this is a lasting fact about one package, cascaded like any other config key, and
|
|
380
|
+
* still holds on the run after next. They read alike for a commit and are not the same thing.
|
|
381
|
+
*/
|
|
382
|
+
async function resolveStartingPoint(git, pkg, options) {
|
|
383
|
+
const cfg = pkg.config?.changelog?.startingAt;
|
|
384
|
+
const value = options.startingAt ?? (typeof cfg === 'string' ? cfg : '');
|
|
385
|
+
if (!value)
|
|
386
|
+
return undefined;
|
|
387
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
388
|
+
return { date: value };
|
|
389
|
+
if (pkg.versionScheme.isValid(value))
|
|
390
|
+
return { version: value };
|
|
391
|
+
const asTagVersion = ChangeHashService.extractVersion(value, ChangeHashService.tagPattern(pkg).replace('{name}', pkg.name));
|
|
392
|
+
if (pkg.versionScheme.isValid(asTagVersion))
|
|
393
|
+
return { version: asTagVersion };
|
|
394
|
+
const sha = await git.resolveCommit(value);
|
|
395
|
+
if (sha)
|
|
396
|
+
return { sha };
|
|
397
|
+
throw new Error(`Invalid "changelog.startingAt" for "${pkg.name}": "${value}" is not a ${pkg.versionScheme.name} ` +
|
|
398
|
+
'version or release tag, a YYYY-MM-DD date, or a commit in this repository');
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Whether this segment's release falls below the floor, and so is left out.
|
|
402
|
+
*
|
|
403
|
+
* **Only a tagged segment can**, and that is the rule rather than an edge case: the unreleased
|
|
404
|
+
* segment is happening now, so no past floor is above it, and dropping it would hide the very
|
|
405
|
+
* commits the run was asked about.
|
|
406
|
+
*
|
|
407
|
+
* All three forms are **inclusive**, matching `auto-changelog`: naming 2.0.0 keeps 2.0.0, and
|
|
408
|
+
* naming a commit keeps the release that commit belongs to.
|
|
409
|
+
*/
|
|
410
|
+
async function isBelowStartingPoint(git, pkg, segment, floor, floorIndex) {
|
|
411
|
+
if (!floor || !segment.tag)
|
|
412
|
+
return false;
|
|
413
|
+
if (floor.version) {
|
|
414
|
+
const expanded = ChangeHashService.tagPattern(pkg).replace('{name}', pkg.name);
|
|
415
|
+
const version = ChangeHashService.extractVersion(segment.tag, expanded);
|
|
416
|
+
/** A tag carrying no version the scheme can read is kept - a floor is for leaving out history,
|
|
417
|
+
* never for losing what it cannot classify. */
|
|
418
|
+
if (pkg.versionScheme.isValid(version) && pkg.versionScheme.compare(version, floor.version) < 0)
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
if (floor.date) {
|
|
422
|
+
const date = await git.commitDate(segment.tag);
|
|
423
|
+
if (date && date < floor.date)
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
/** The commit's own place in the range decides, so no second git call: a release that ended
|
|
427
|
+
* before the floor commit is below it. `-1` means the floor is not in this range at all -
|
|
428
|
+
* older than everything here, so nothing is below it. */
|
|
429
|
+
if (floor.sha && floorIndex >= 0 && segment.endIndex < floorIndex)
|
|
430
|
+
return true;
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
191
433
|
function resolveFilePath(pkg, optionsFilePath) {
|
|
192
434
|
if (optionsFilePath)
|
|
193
435
|
return optionsFilePath;
|
|
@@ -199,11 +441,97 @@ function resolveFilePath(pkg, optionsFilePath) {
|
|
|
199
441
|
* `{{version}}` doc on `Changelog.getEntries`) package.json. Falls back to package.json's version
|
|
200
442
|
* if no matching tag exists at all (never tagged, or a fresh package) - see `findLatestTag`.
|
|
201
443
|
*/
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
444
|
+
/**
|
|
445
|
+
* The version an entry is headed with, **and the date that release actually happened**.
|
|
446
|
+
*
|
|
447
|
+
* The two used to come from different places: the version was read back from the package's latest
|
|
448
|
+
* tag while the date was `new Date()`. So regenerating notes for an already-tagged release headed
|
|
449
|
+
* them with that release's number and *today's* date - measured on a real repository,
|
|
450
|
+
* `## @panates/eslint-config v2.1.6 (2026-09-25)` for a v2.1.6 tagged days earlier. Two halves of
|
|
451
|
+
* one heading describing two different releases.
|
|
452
|
+
*
|
|
453
|
+
* Today's date is still right for the case it was written for: a caller that passes `version`
|
|
454
|
+
* explicitly is describing a release that **does not exist yet** (`version --changelog` writes the
|
|
455
|
+
* entry before it commits and tags), so there is no tag to read a date off and the clock is the
|
|
456
|
+
* only answer. That is why this is decided here, beside the version, rather than at the template.
|
|
457
|
+
*/
|
|
458
|
+
async function resolveHeading(git, pkg, label, versionOverride, segmentTag) {
|
|
459
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
460
|
+
const expanded = ChangeHashService.tagPattern(pkg).replace('{name}', pkg.name);
|
|
461
|
+
/** A segment that a release tag closes is headed by **that** release, whatever the newest one
|
|
462
|
+
* is - which is the whole point of splitting, and the reason an override cannot win here: a
|
|
463
|
+
* caller naming a version is naming the *unreleased* one, the segment with no tag. */
|
|
464
|
+
if (segmentTag) {
|
|
465
|
+
return {
|
|
466
|
+
version: ChangeHashService.extractVersion(segmentTag, expanded),
|
|
467
|
+
date: (await git.commitDate(segmentTag)) ?? today,
|
|
468
|
+
title: segmentTag,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* A caller naming the version is naming the one being **prepared** - `version --changelog` writes
|
|
473
|
+
* the entry before it commits and tags. So the heading is the tag that release is about to get,
|
|
474
|
+
* which keeps every heading in the file the same kind of thing, and the date is today because
|
|
475
|
+
* that is when it is happening.
|
|
476
|
+
*/
|
|
477
|
+
if (versionOverride) {
|
|
478
|
+
return { version: versionOverride, date: today, title: ChangeHashService.expandTag(pkg, versionOverride) };
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* **Nothing else is a release**, so nothing else gets a release's heading. This used to read the
|
|
482
|
+
* package's latest tag and use its version *and its date* - so the commits that are not released
|
|
483
|
+
* yet were headed with the number and the day of the release before them. In the repository root
|
|
484
|
+
* that produced `## panates-javascript repository 2.1.6 (2026-04-30)` sitting above the real
|
|
485
|
+
* `## v2.1.6 (2026-04-30)`: the same release named twice, once wrongly.
|
|
486
|
+
*
|
|
487
|
+
* **The label stays in it**, which `Unreleased` alone loses: `rman changelog` prints every
|
|
488
|
+
* package's entry to one stream, and three consecutive `## Unreleased` blocks say nothing about
|
|
489
|
+
* which package each belongs to. Measured - 22 specs caught exactly that.
|
|
490
|
+
*
|
|
491
|
+
* `version` is still what it always was, for a template that wants it - only the heading changed.
|
|
492
|
+
*/
|
|
493
|
+
return { version: pkg.version || '', date: today, title: `Unreleased — ${label}` };
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* The range cut into releases: each **release tag inside it** closes a segment, and whatever
|
|
497
|
+
* follows the last one is the unreleased segment (`tag: undefined`).
|
|
498
|
+
*
|
|
499
|
+
* Oldest first, because `generateToFile` prepends each entry in turn - so the last one written is
|
|
500
|
+
* the one that ends up at the top of the file.
|
|
501
|
+
*
|
|
502
|
+
* The tagged commit belongs to the segment it **closes**: a release tag sits on the release commit,
|
|
503
|
+
* which comes after the work it releases. That commit is then dropped by `dropVersionBumps` like
|
|
504
|
+
* any other release marker, so it heads the segment without appearing in it.
|
|
505
|
+
*
|
|
506
|
+
* A tag pointing at a commit outside the range simply never matches, which is what should happen -
|
|
507
|
+
* it belongs to a release this run is not describing.
|
|
508
|
+
*/
|
|
509
|
+
async function splitByRelease(git, pkg, commits) {
|
|
510
|
+
if (!commits.length)
|
|
511
|
+
return [];
|
|
512
|
+
const tagBySha = new Map();
|
|
513
|
+
for (const pattern of ChangeHashService.releaseTagPatterns(pkg)) {
|
|
514
|
+
/** Earlier patterns win: a package with tags under its own name is described by those, and the
|
|
515
|
+
* shared `v*` is only there to cover the releases it predates. */
|
|
516
|
+
for (const { tag, sha } of await git.listTagCommits(pattern))
|
|
517
|
+
if (!tagBySha.has(sha))
|
|
518
|
+
tagBySha.set(sha, tag);
|
|
519
|
+
}
|
|
520
|
+
if (!tagBySha.size)
|
|
521
|
+
return [{ commits, endIndex: commits.length - 1 }];
|
|
522
|
+
const segments = [];
|
|
523
|
+
let current = [];
|
|
524
|
+
for (const [index, commit] of commits.entries()) {
|
|
525
|
+
current.push(commit);
|
|
526
|
+
const tag = tagBySha.get(commit.sha);
|
|
527
|
+
if (tag) {
|
|
528
|
+
segments.push({ tag, commits: current, endIndex: index });
|
|
529
|
+
current = [];
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (current.length)
|
|
533
|
+
segments.push({ commits: current, endIndex: commits.length - 1 });
|
|
534
|
+
return segments;
|
|
207
535
|
}
|
|
208
536
|
/** The most specific package whose directory contains `file` - the repository root itself as the
|
|
209
537
|
* fallback for anything outside every package (e.g. root-level config files). Mirrors
|
|
@@ -249,29 +577,84 @@ function ownersOf(repository, commit) {
|
|
|
249
577
|
}
|
|
250
578
|
return owners;
|
|
251
579
|
}
|
|
252
|
-
async function renderEntry(repository, pkg, label,
|
|
580
|
+
async function renderEntry(repository, pkg, label, sections, titles, git, versionOverride, segmentTag) {
|
|
253
581
|
const template = resolveTemplate(repository, pkg);
|
|
254
|
-
const version
|
|
582
|
+
const { version, date, title } = await resolveHeading(git, pkg, label, versionOverride, segmentTag);
|
|
255
583
|
const content = render(template, {
|
|
256
584
|
package: label,
|
|
257
585
|
version,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
586
|
+
tag: segmentTag ?? '',
|
|
587
|
+
title,
|
|
588
|
+
date,
|
|
589
|
+
commits: renderCommitsBlock(sections),
|
|
590
|
+
...Object.fromEntries(Object.entries(legacyBuckets(sections, titles)).map(([k, v]) => [k, bulletList(v)])),
|
|
263
591
|
});
|
|
264
592
|
return { version, content };
|
|
265
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* **Where a changelog file says it is documented up to**, written by `--write` and read back by
|
|
596
|
+
* `resolveBoundary`. An HTML comment, so it renders as nothing at all.
|
|
597
|
+
*
|
|
598
|
+
* The alternative was parsing the topmost version heading back out of the file, and it fails on the
|
|
599
|
+
* one thing this has to survive: `changelog.template` is the *repository's*, so the heading is a
|
|
600
|
+
* shape rman did not choose and cannot reliably read. The other alternative - the file's own last
|
|
601
|
+
* modifying commit - is what `catchUpFile` already does, and it cannot be the boundary because any
|
|
602
|
+
* unrelated edit (a typo, a hand-written note) would move it forward and drop every commit in
|
|
603
|
+
* between, silently.
|
|
604
|
+
*/
|
|
605
|
+
const MARKER = /^<!-- rman:documented-up-to ([0-9a-f]{7,40}) -->[ \t]*\r?\n+/m;
|
|
606
|
+
/**
|
|
607
|
+
* The boundary this package's notes start from.
|
|
608
|
+
*
|
|
609
|
+
* `--from` wins outright, as everywhere. Otherwise, **for a `--write` run only**, the changelog
|
|
610
|
+
* file decides - it is the thing being appended to, so where it stopped is the question:
|
|
611
|
+
*
|
|
612
|
+
* - **A marker** - start there. Measured without it: a second `--write` re-listed every commit
|
|
613
|
+
* since the last tag on top of the entry that already held them, so one commit appeared twice
|
|
614
|
+
* under two headings carrying the same version number.
|
|
615
|
+
* - **No file at all** - the whole history, because nothing has been documented. Measured without
|
|
616
|
+
* it: the first `--write` in a repository with a `v1.0.0` tag documented only the commits *after*
|
|
617
|
+
* that tag, and the ones before it were never written anywhere and never would be.
|
|
618
|
+
* - **A file with no marker** - one rman did not write, or wrote before markers existed. Fall back
|
|
619
|
+
* to ordinary detection, which is where `catchUpFile` still earns its place: it widens the tag
|
|
620
|
+
* boundary backwards to the file's last commit, so a stale changelog catches up rather than
|
|
621
|
+
* skipping. The next write leaves a marker, so this is the one run that has to guess.
|
|
622
|
+
*
|
|
623
|
+
* A **print** run is deliberately none of this: nothing is being appended, and answering "the notes
|
|
624
|
+
* for this release" with "nothing, it is all documented" would be the wrong question answered.
|
|
625
|
+
*/
|
|
626
|
+
async function resolveBoundary(git, pkg, changelogFile, options) {
|
|
627
|
+
if (options.from && options.from !== ChangeHashService.AUTO)
|
|
628
|
+
return options.from;
|
|
629
|
+
if (options.write) {
|
|
630
|
+
const exists = fs.existsSync(changelogFile);
|
|
631
|
+
if (!exists)
|
|
632
|
+
return undefined;
|
|
633
|
+
const marker = MARKER.exec(fs.readFileSync(changelogFile, 'utf-8'));
|
|
634
|
+
if (marker)
|
|
635
|
+
return marker[1];
|
|
636
|
+
}
|
|
637
|
+
return ChangeHashService.detect(git, pkg, {
|
|
638
|
+
from: options.from,
|
|
639
|
+
catchUpFile: fs.existsSync(changelogFile) ? changelogFile : undefined,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
266
642
|
/** Prepends `content` right after the top-level "# Changelog" heading if the file already has
|
|
267
643
|
* one, otherwise creates the file (and any missing parent directory - `relFilePath` can nest one,
|
|
268
644
|
* e.g. `'docs/CHANGELOG.md'`) with one. Leaves everything already in the file untouched below it. */
|
|
269
|
-
function prependToChangelogFile(pkg, relFilePath, content) {
|
|
645
|
+
function prependToChangelogFile(pkg, relFilePath, content, documentedUpTo) {
|
|
270
646
|
const file = path.join(pkg.dirname, relFilePath);
|
|
271
647
|
const existing = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : '';
|
|
272
648
|
const headerMatch = /^# Changelog\r?\n+/.exec(existing);
|
|
273
649
|
const header = headerMatch ? headerMatch[0] : '# Changelog\n\n';
|
|
274
|
-
|
|
650
|
+
/** The previous marker goes with the header it sat under - there is one per file, rewritten
|
|
651
|
+
* each time, never accumulated. */
|
|
652
|
+
const rest = (headerMatch ? existing.slice(headerMatch[0].length) : existing).replace(MARKER, '');
|
|
653
|
+
const marker = documentedUpTo ? `<!-- rman:documented-up-to ${documentedUpTo} -->\n\n` : '';
|
|
654
|
+
/** A rule **between** releases, so it is the writer's and not the template's: an entry printed
|
|
655
|
+
* to stdout, or handed to `github-release` as a body, has nothing below it to be separated
|
|
656
|
+
* from. Only written when there is something below. */
|
|
657
|
+
const separator = rest.trim() ? '\n---\n\n' : '';
|
|
275
658
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
276
|
-
fs.writeFileSync(file, header + content.trimEnd() + '\n
|
|
659
|
+
fs.writeFileSync(file, header + marker + content.trimEnd() + '\n' + separator + rest);
|
|
277
660
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Service } from '../core/service.js';
|
|
1
|
+
import { Service } from '../core/classes/service.js';
|
|
2
2
|
import { type LogLevel } from '../utils/logger.js';
|
|
3
3
|
import { type PackageFilterOptions } from '../utils/package-filter.js';
|
|
4
4
|
/**
|
|
@@ -39,7 +39,7 @@ export declare namespace ExecService {
|
|
|
39
39
|
fromRoot?: boolean;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
declare module '../core/service.js' {
|
|
42
|
+
declare module '../core/classes/service.js' {
|
|
43
43
|
interface ServiceMap {
|
|
44
44
|
exec: ExecService;
|
|
45
45
|
}
|
package/services/exec.service.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import colors from 'ansi-colors';
|
|
3
3
|
import { Task } from 'power-tasks';
|
|
4
|
-
import { Service } from '../core/service.js';
|
|
4
|
+
import { Service } from '../core/classes/service.js';
|
|
5
5
|
import { exec as execCommand } from '../utils/exec.js';
|
|
6
6
|
import { Logger, resolveRootLogLevel } from '../utils/logger.js';
|
|
7
7
|
import { filterPackages } from '../utils/package-filter.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Package } from '../core/package.js';
|
|
2
|
-
import { Service } from '../core/service.js';
|
|
1
|
+
import { Package } from '../core/classes/package.js';
|
|
2
|
+
import { Service } from '../core/classes/service.js';
|
|
3
3
|
/**
|
|
4
4
|
* A service class - see `ListService` for the shape and `Service` for the three measured
|
|
5
5
|
* consequences a namespace had. `repository` left the signature because the application carries it.
|
|
@@ -73,7 +73,7 @@ export declare namespace GithubReleaseService {
|
|
|
73
73
|
reason?: string;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
declare module '../core/service.js' {
|
|
76
|
+
declare module '../core/classes/service.js' {
|
|
77
77
|
interface ServiceMap {
|
|
78
78
|
githubRelease: GithubReleaseService;
|
|
79
79
|
}
|