proper-changelog 0.1.0

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 ADDED
@@ -0,0 +1,63 @@
1
+ # proper-changelog
2
+
3
+ GitHub releases are useful in some ways, but they're horrible as changelogs if you need to look at changes across multiple versions or figure out when a specific change was introduced. This tool reads GitHub releases and generates a single markdown changelog.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ # By GitHub repository
9
+ npx proper-changelog --repo <owner>/<repo>
10
+
11
+ # By npm package name (the GitHub repository is read from the latest published version)
12
+ npx proper-changelog --package <package-name>
13
+ ```
14
+
15
+ Exactly one of `--repo` or `--package` is required.
16
+
17
+ Releases are listed newest-first by published date. Draft releases are always excluded, and prereleases are excluded unless `--include-prereleases` is passed.
18
+
19
+ By default this writes the changelog to `CHANGELOG-<package-or-repo>.md` in the current directory (using the package name when `--package` is given, otherwise the repo name). Use `--stdout` to print it instead, or `--out` to choose a different file name.
20
+
21
+ ```bash
22
+ # Write to a custom file
23
+ npx proper-changelog --repo microsoft/beachball --out CHANGELOG.md
24
+
25
+ # Print to stdout
26
+ npx proper-changelog --repo microsoft/beachball --stdout
27
+
28
+ # Resolve the repository from an npm package
29
+ npx proper-changelog --package @fluentui/react --stdout
30
+ ```
31
+
32
+ ## Options
33
+
34
+ Either `--package` or `--repo` is required, and they're mutually exclusive.
35
+
36
+ <!-- prettier-ignore -->
37
+ | Option | Description |
38
+ | ------ | ----------- |
39
+ | `--repo <owner/repo>` | GitHub repository to read releases from. |
40
+ | `--package <name>` | npm package whose GitHub repository should be used (read from the latest published version on npmjs.com; only supports github.com repos). Note that for a monorepo, this does **not** do any filtering of releases by package. |
41
+ | `-o, --out <file>` | Output file name (default: `CHANGELOG-<package-or-repo>.md`). Mutually exclusive with `--stdout`. |
42
+ | `--stdout` | Write the changelog to stdout instead of a file. Mutually exclusive with `--out`. |
43
+ | `--token <token>` | GitHub token (see [Authentication](#authentication)). |
44
+ | `--include-prereleases` | Include prerelease releases. Draft releases are always excluded. |
45
+ | `--from <tag>` | Include releases up to and including this tag (based on date, **not** semver). |
46
+ | `--to <tag>` | Include releases down to and including this tag (based on date, **not** semver). |
47
+ | `--limit <n>` | Maximum number of releases to include. |
48
+ | `--filter <pattern>` | Only include releases whose **tag** matches `<pattern>`. A plain string matches tags containing it (case-insensitive); wrap the value in slashes (e.g. `/^v1\./i`) to match with a regular expression. Useful for monorepos that tag releases per package. (Warning: this is _not_ sanitized, so ReDOS yourself at will.) |
49
+ | `--since <date>` | Only include releases published after this date. Accepts any value parseable by `new Date()`, such as `2024-01-01`. |
50
+
51
+ ## Authentication
52
+
53
+ The GitHub API is rate-limited for unauthenticated requests. To use a token, the tool checks the following in order:
54
+
55
+ 1. The `--token` option
56
+ 2. The `GITHUB_TOKEN` or `GH_TOKEN` environment variables
57
+ 3. The output of `gh auth token` (if the [GitHub CLI](https://cli.github.com/) is installed and authenticated)
58
+
59
+ If no token is found, the tool prints a warning and continues unauthenticated.
60
+
61
+ ## API
62
+
63
+ The package currently does not have an importable API. If you want this, please open a feature request describing your use case.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { cli } from '../lib/cli.js';
3
+
4
+ cli();
package/lib/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { type CliContext, type ProperChangelogOptions } from './types.ts';
2
+ /** Generate the changelog and write it to a file or stdout. */
3
+ export declare function _generateChangelog(options: ProperChangelogOptions, context: CliContext): Promise<void>;
4
+ /** Run the CLI and handle top-level errors. Intended to be called from the bin script. */
5
+ export declare function cli(): void;
6
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAG1F,+DAA+D;AAC/D,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB5G;AAED,0FAA0F;AAC1F,wBAAgB,GAAG,IAAI,IAAI,CAoB1B"}
package/lib/cli.js ADDED
@@ -0,0 +1,49 @@
1
+ import fs from 'fs';
2
+ import { CommanderError } from 'commander';
3
+ import { fetchReleases } from "./fetchReleases.js";
4
+ import { renderChangelog } from "./renderChangelog.js";
5
+ import { ChangelogError } from "./types.js";
6
+ import { parseArgs } from "./parseArgs.js";
7
+ /** Generate the changelog and write it to a file or stdout. */
8
+ export async function _generateChangelog(options, context) {
9
+ const { repo } = options;
10
+ const releases = await fetchReleases(repo, options.token);
11
+ if (!releases.length) {
12
+ context.warn(`No releases found for ${repo.owner}/${repo.repo}`);
13
+ return;
14
+ }
15
+ const changelog = renderChangelog(releases, options);
16
+ if (options.stdout) {
17
+ context.log(changelog);
18
+ return;
19
+ }
20
+ // Strip a leading npm scope and replace path separators so the result is a safe single filename.
21
+ const changelogName = (options.package ?? repo.repo).replace(/^@/, '').replace(/\//g, '-');
22
+ const outFile = options.out ?? `CHANGELOG-${changelogName}.md`;
23
+ context.writeFile(outFile, changelog);
24
+ context.log(`Wrote changelog to ${outFile}`);
25
+ }
26
+ /** Run the CLI and handle top-level errors. Intended to be called from the bin script. */
27
+ export function cli() {
28
+ (async () => {
29
+ const context = {
30
+ argv: process.argv,
31
+ env: process.env,
32
+ log: message => console.log(message),
33
+ warn: message => console.warn(message),
34
+ writeFile: (file, content) => fs.writeFileSync(file, content, 'utf8'),
35
+ };
36
+ const options = await parseArgs(context);
37
+ await _generateChangelog(options, context);
38
+ })().catch((err) => {
39
+ if (err instanceof CommanderError || err instanceof ChangelogError) {
40
+ console.error(err.message);
41
+ }
42
+ else {
43
+ console.error(err instanceof Error ? err.stack || err.message : String(err));
44
+ }
45
+ // eslint-disable-next-line no-restricted-properties -- central handler
46
+ process.exit(1);
47
+ });
48
+ }
49
+ //# sourceMappingURL=cli.js.map
package/lib/cli.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAgD,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAA+B,EAAE,OAAmB;IAC3F,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAEzB,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACjE,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAErD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO;IACT,CAAC;IAED,iGAAiG;IACjG,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,IAAI,aAAa,aAAa,KAAK,CAAC;IAC/D,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,GAAG;IACjB,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,OAAO,GAAe;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;YACpC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YACtC,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC;SACtE,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QAC1B,IAAI,GAAG,YAAY,cAAc,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YACnE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/E,CAAC;QACD,uEAAuE;QACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type GitHubRelease, type RepoId } from './types.ts';
2
+ /**
3
+ * Fetch all releases for a repository from the GitHub REST API, following pagination.
4
+ *
5
+ * If `token` is provided, it is sent as a bearer token; otherwise requests are made
6
+ * unauthenticated (and are subject to stricter rate limits).
7
+ */
8
+ export declare function fetchReleases(repo: RepoId, token?: string): Promise<GitHubRelease[]>;
9
+ //# sourceMappingURL=fetchReleases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchReleases.d.ts","sourceRoot":"","sources":["../src/fetchReleases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAK7E;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA8B1F"}
@@ -0,0 +1,47 @@
1
+ import { ChangelogError } from "./types.js";
2
+ const apiBase = 'https://api.github.com';
3
+ const perPage = 100;
4
+ /**
5
+ * Fetch all releases for a repository from the GitHub REST API, following pagination.
6
+ *
7
+ * If `token` is provided, it is sent as a bearer token; otherwise requests are made
8
+ * unauthenticated (and are subject to stricter rate limits).
9
+ */
10
+ export async function fetchReleases(repo, token) {
11
+ const headers = {
12
+ Accept: 'application/vnd.github+json',
13
+ 'X-GitHub-Api-Version': '2026-03-10',
14
+ 'User-Agent': 'proper-changelog',
15
+ };
16
+ if (token) {
17
+ headers.Authorization = `Bearer ${token}`;
18
+ }
19
+ const releases = [];
20
+ let url = `${apiBase}/repos/${repo.owner}/${repo.repo}/releases?per_page=${perPage}`;
21
+ while (url) {
22
+ const response = await fetch(url, { headers });
23
+ if (!response.ok) {
24
+ const body = await response.text().catch(() => '');
25
+ throw new ChangelogError(`Failed to fetch releases for ${repo.owner}/${repo.repo}: ${response.status} ${response.statusText}` +
26
+ (body ? `\n${body}` : ''));
27
+ }
28
+ const page = (await response.json());
29
+ releases.push(...page);
30
+ url = getNextLink(response.headers.get('link'));
31
+ }
32
+ return releases;
33
+ }
34
+ /** Parse the `Link` response header and return the URL with `rel="next"`, if any. */
35
+ function getNextLink(linkHeader) {
36
+ if (!linkHeader) {
37
+ return undefined;
38
+ }
39
+ for (const part of linkHeader.split(',')) {
40
+ const match = part.match(/<([^>]+)>;\s*rel="([^"]+)"/);
41
+ if (match?.[2] === 'next') {
42
+ return match[1];
43
+ }
44
+ }
45
+ return undefined;
46
+ }
47
+ //# sourceMappingURL=fetchReleases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchReleases.js","sourceRoot":"","sources":["../src/fetchReleases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmC,MAAM,YAAY,CAAC;AAE7E,MAAM,OAAO,GAAG,wBAAwB,CAAC;AACzC,MAAM,OAAO,GAAG,GAAG,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,KAAc;IAC9D,MAAM,OAAO,GAA2B;QACtC,MAAM,EAAE,6BAA6B;QACrC,sBAAsB,EAAE,YAAY;QACpC,YAAY,EAAE,kBAAkB;KACjC,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,aAAa,GAAG,UAAU,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,IAAI,GAAG,GAAuB,GAAG,OAAO,UAAU,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,sBAAsB,OAAO,EAAE,CAAC;IAEzG,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,QAAQ,GAAa,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,cAAc,CACtB,gCAAgC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE;gBAClG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC5B,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAoB,CAAC;QACxD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAEvB,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,qFAAqF;AACrF,SAAS,WAAW,CAAC,UAAyB;IAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACvD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type CliContext, type ProperChangelogOptions } from './types.ts';
2
+ /**
3
+ * Parse the CLI arguments (`process.argv` by default), fetch the repo from `--package` if needed,
4
+ * and get the default token if needed.
5
+ *
6
+ * By default this will exit the program if an argument is invalid.
7
+ */
8
+ export declare function parseArgs(context: Pick<CliContext, 'argv' | 'env' | 'exitOverride' | 'writeErr' | 'warn'>): Promise<ProperChangelogOptions>;
9
+ //# sourceMappingURL=parseArgs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseArgs.d.ts","sourceRoot":"","sources":["../src/parseArgs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,UAAU,EAAmB,KAAK,sBAAsB,EAA+B,MAAM,YAAY,CAAC;AAExH;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,CAAC,GAC/E,OAAO,CAAC,sBAAsB,CAAC,CAiDjC"}
@@ -0,0 +1,87 @@
1
+ import { Command, InvalidArgumentError, Option } from 'commander';
2
+ import { resolveRepoFromPackage } from "./resolveRepoFromPackage.js";
3
+ import { resolveToken } from "./resolveToken.js";
4
+ import { ChangelogError } from "./types.js";
5
+ /**
6
+ * Parse the CLI arguments (`process.argv` by default), fetch the repo from `--package` if needed,
7
+ * and get the default token if needed.
8
+ *
9
+ * By default this will exit the program if an argument is invalid.
10
+ */
11
+ export async function parseArgs(context) {
12
+ const program = new Command()
13
+ .name('proper-changelog')
14
+ .description("Generate a single markdown changelog from a GitHub repository's releases.")
15
+ .addOption(new Option('--repo <owner/repo>', 'GitHub repository to read releases from (use this OR --package)')
16
+ .argParser(parseRepo)
17
+ .conflicts('package'))
18
+ .addOption(new Option('--package <name>', 'npm package whose GitHub repository should be used (use this OR --repo)').conflicts('repo'))
19
+ .addOption(new Option('-o, --out <file>', 'output file name (default: CHANGELOG-<package-or-repo>.md)').conflicts('stdout'))
20
+ .addOption(new Option('--stdout', 'write the changelog to stdout instead of a file').conflicts('out'))
21
+ .option('--token <token>', 'GitHub token (falls back to GITHUB_TOKEN/GH_TOKEN, then `gh auth token`)')
22
+ .option('--include-prereleases', 'include prerelease releases (drafts are always excluded)')
23
+ .option('--from <tag>', 'include releases up to and including this tag (based on date, not semver)')
24
+ .option('--to <tag>', 'include releases down to and including this tag (based on date, not semver)')
25
+ .option('--limit <n>', 'maximum number of releases to include', parseLimit)
26
+ .option('--filter <pattern>', 'only include releases whose tag matches this substring or /regex/', parseFilter)
27
+ .option('--since <date>', 'only include releases published after this date (e.g. 2024-01-01)', parseSince)
28
+ .allowExcessArguments(false);
29
+ context.exitOverride && program.exitOverride(context.exitOverride);
30
+ context.writeErr && program.configureOutput({ writeErr: context.writeErr });
31
+ const rawOptions = program.parse(context.argv ?? process.argv).opts();
32
+ let repo = rawOptions.repo;
33
+ if (rawOptions.package) {
34
+ repo = await resolveRepoFromPackage(rawOptions.package);
35
+ }
36
+ else if (!repo) {
37
+ throw new ChangelogError('Exactly one of --repo or --package is required.');
38
+ }
39
+ const token = await resolveToken(rawOptions.token, context.env);
40
+ if (!token) {
41
+ context.warn('Warning: no GitHub token found (checked --token, GITHUB_TOKEN/GH_TOKEN, and `gh auth token`). ' +
42
+ 'Requests will be unauthenticated and may be rate-limited.');
43
+ }
44
+ return { ...rawOptions, repo, token };
45
+ }
46
+ /** Parse a `--repo` value in `owner/repo` form. */
47
+ function parseRepo(value) {
48
+ const match = value.match(/^([^/\s]+)\/([^/\s]+)$/);
49
+ if (!match) {
50
+ throw new InvalidArgumentError(`Expected "owner/repo" but got "${value}".`);
51
+ }
52
+ return { owner: match[1], repo: match[2] };
53
+ }
54
+ /** Parse a `--limit` value as a positive integer. */
55
+ function parseLimit(value) {
56
+ const parsed = Number(value);
57
+ if (!Number.isInteger(parsed) || parsed <= 0) {
58
+ throw new InvalidArgumentError(`Expected a positive integer but got "${value}".`);
59
+ }
60
+ return parsed;
61
+ }
62
+ /** Parse a `--since` value as a date. */
63
+ function parseSince(value) {
64
+ const date = new Date(value);
65
+ if (Number.isNaN(date.getTime())) {
66
+ throw new InvalidArgumentError(`Expected a date but got "${value}".`);
67
+ }
68
+ return date;
69
+ }
70
+ /**
71
+ * Parse a `--filter` value. A value wrapped in slashes (optionally with trailing regex flags,
72
+ * e.g. `/^v1\./i`) is converted to a `RegExp`; any other value is returned as-is for a
73
+ * case-insensitive substring match.
74
+ */
75
+ function parseFilter(value) {
76
+ const regexMatch = value.match(/^\/(.*)\/([a-z]*)$/s);
77
+ if (!regexMatch) {
78
+ return value;
79
+ }
80
+ try {
81
+ return new RegExp(regexMatch[1], regexMatch[2]);
82
+ }
83
+ catch (error) {
84
+ throw new InvalidArgumentError(`Invalid regular expression "${value}": ${error.message}`);
85
+ }
86
+ }
87
+ //# sourceMappingURL=parseArgs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseArgs.js","sourceRoot":"","sources":["../src/parseArgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAA8E,cAAc,EAAE,MAAM,YAAY,CAAC;AAExH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAgF;IAEhF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;SAC1B,IAAI,CAAC,kBAAkB,CAAC;SACxB,WAAW,CAAC,2EAA2E,CAAC;SACxF,SAAS,CACR,IAAI,MAAM,CAAC,qBAAqB,EAAE,iEAAiE,CAAC;SACjG,SAAS,CAAC,SAAS,CAAC;SACpB,SAAS,CAAC,SAAS,CAAC,CACxB;SACA,SAAS,CACR,IAAI,MAAM,CACR,kBAAkB,EAClB,yEAAyE,CAC1E,CAAC,SAAS,CAAC,MAAM,CAAC,CACpB;SACA,SAAS,CACR,IAAI,MAAM,CAAC,kBAAkB,EAAE,4DAA4D,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CACjH;SACA,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,iDAAiD,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;SACrG,MAAM,CAAC,iBAAiB,EAAE,0EAA0E,CAAC;SACrG,MAAM,CAAC,uBAAuB,EAAE,0DAA0D,CAAC;SAC3F,MAAM,CAAC,cAAc,EAAE,2EAA2E,CAAC;SACnG,MAAM,CAAC,YAAY,EAAE,6EAA6E,CAAC;SACnG,MAAM,CAAC,aAAa,EAAE,uCAAuC,EAAE,UAAU,CAAC;SAC1E,MAAM,CAAC,oBAAoB,EAAE,mEAAmE,EAAE,WAAW,CAAC;SAC9G,MAAM,CAAC,gBAAgB,EAAE,mEAAmE,EAAE,UAAU,CAAC;SACzG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE/B,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACnE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE5E,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAc,CAAC;IAElF,IAAI,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;IAC3B,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,IAAI,GAAG,MAAM,sBAAsB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;SAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,cAAc,CAAC,iDAAiD,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CACV,gGAAgG;YAC9F,2DAA2D,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC;AAED,mDAAmD;AACnD,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,KAAK,IAAI,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED,qDAAqD;AACrD,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,oBAAoB,CAAC,wCAAwC,KAAK,IAAI,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yCAAyC;AACzC,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,oBAAoB,CAAC,4BAA4B,KAAK,IAAI,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACtD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,oBAAoB,CAAC,+BAA+B,KAAK,MAAO,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IACvG,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type SelectReleasesOptions } from './selectReleases.ts';
2
+ import type { GitHubRelease, ProperChangelogOptions } from './types.ts';
3
+ export type RenderChangelogOptions = SelectReleasesOptions & Pick<ProperChangelogOptions, 'package' | 'repo'>;
4
+ /**
5
+ * Render a full markdown changelog from GitHub releases, applying the given options.
6
+ */
7
+ export declare function renderChangelog(releases: GitHubRelease[], options: RenderChangelogOptions): string;
8
+ //# sourceMappingURL=renderChangelog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderChangelog.d.ts","sourceRoot":"","sources":["../src/renderChangelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAcxE,MAAM,MAAM,sBAAsB,GAAG,qBAAqB,GAAG,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC;AAE9G;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAUlG"}
@@ -0,0 +1,119 @@
1
+ import { selectReleases } from "./selectReleases.js";
2
+ const maxHeadingLevel = 6;
3
+ /**
4
+ * Render a full markdown changelog from GitHub releases, applying the given options.
5
+ */
6
+ export function renderChangelog(releases, options) {
7
+ const selected = selectReleases(releases, options);
8
+ const heading = `# Changelog - ${options.package || options.repo.repo}`;
9
+ if (selected.length === 0) {
10
+ return `${heading}\n\nNo releases found.\n`;
11
+ }
12
+ const sections = selected.map(renderRelease);
13
+ return `${heading}\n\n${sections.join('\n\n')}\n`;
14
+ }
15
+ /** Render a single release as a markdown section. */
16
+ function renderRelease(release) {
17
+ const releaseName = release.name?.trim() || release.tag_name;
18
+ const bodyLines = (release.body ?? '').replace(/\r\n/g, '\n').split('\n');
19
+ const headings = parseHeadings(bodyLines);
20
+ // If the body has any h1, demote every heading by one level so the highest is h2.
21
+ const baseDemotion = headings.some(heading => heading.level === 1) ? 1 : 0;
22
+ // Headings that sit at level 2 after applying the base demotion.
23
+ const h2Headings = headings.filter(heading => heading.level + baseDemotion === 2);
24
+ let sectionHeading;
25
+ let demotion;
26
+ let promotedLineIndex;
27
+ if (h2Headings.length === 1) {
28
+ // Use the single h2 as the section heading (don't demote any other headings).
29
+ sectionHeading = `## ${formatSectionHeading(h2Headings[0].text, release.tag_name)}`;
30
+ demotion = baseDemotion;
31
+ promotedLineIndex = h2Headings[0].lineIndex;
32
+ }
33
+ else {
34
+ // No h2 (don't demote) or multiple h2s (demote everything one more level): use the
35
+ // release name as the section heading.
36
+ sectionHeading = `## ${formatSectionHeading(releaseName, release.tag_name)}`;
37
+ demotion = h2Headings.length > 1 ? baseDemotion + 1 : baseDemotion;
38
+ }
39
+ const lines = [sectionHeading, ''];
40
+ const date = formatDate(release.published_at);
41
+ const meta = [`Tag [\`${release.tag_name}\`](${release.html_url})`];
42
+ if (date) {
43
+ meta.push(`released ${date}`);
44
+ }
45
+ lines.push(`_${meta.join(' • ')}_`, '');
46
+ const body = transformBody(bodyLines, demotion, promotedLineIndex);
47
+ if (body) {
48
+ lines.push(body, '');
49
+ }
50
+ return lines.join('\n').trimEnd();
51
+ }
52
+ /**
53
+ * Build the section heading text from a candidate title (either a single h2 heading or the release
54
+ * name). If the title already references the version (the tag without a leading `v`), use it as-is;
55
+ * otherwise prefix it with the tag.
56
+ */
57
+ function formatSectionHeading(title, tag) {
58
+ const tagWithoutV = tag.replace(/^v/, '');
59
+ return title.includes(tagWithoutV) ? title : `${tag} - ${title}`;
60
+ }
61
+ /** Format an ISO timestamp as `YYYY-MM-DD`, or return undefined if missing/invalid. */
62
+ function formatDate(published) {
63
+ if (!published) {
64
+ return undefined;
65
+ }
66
+ const date = new Date(published);
67
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString().slice(0, 10);
68
+ }
69
+ /** Collect the ATX headings from body lines, ignoring headings inside fenced code blocks. */
70
+ function parseHeadings(lines) {
71
+ const headings = [];
72
+ let inFence = false;
73
+ lines.forEach((line, lineIndex) => {
74
+ if (/^\s*(```|~~~)/.test(line)) {
75
+ inFence = !inFence;
76
+ return;
77
+ }
78
+ if (inFence) {
79
+ return;
80
+ }
81
+ const match = line.match(/^(#{1,6})\s+(.*\S)\s*$/);
82
+ if (match) {
83
+ headings.push({ lineIndex, level: match[1].length, text: match[2] });
84
+ }
85
+ });
86
+ return headings;
87
+ }
88
+ /**
89
+ * Rebuild a release body, demoting ATX headings by `demotion` levels (headings inside fenced
90
+ * code blocks are left alone) and optionally removing the heading promoted to the section heading.
91
+ */
92
+ function transformBody(lines, demotion, promotedLineIndex) {
93
+ let inFence = false;
94
+ const out = [];
95
+ lines.forEach((line, lineIndex) => {
96
+ if (lineIndex === promotedLineIndex) {
97
+ return;
98
+ }
99
+ if (/^\s*(```|~~~)/.test(line)) {
100
+ inFence = !inFence;
101
+ out.push(line);
102
+ return;
103
+ }
104
+ if (!inFence && demotion > 0) {
105
+ const match = line.match(/^(#{1,6})(\s.*)$/);
106
+ if (match) {
107
+ const level = Math.min(match[1].length + demotion, maxHeadingLevel);
108
+ out.push('#'.repeat(level) + match[2]);
109
+ return;
110
+ }
111
+ }
112
+ out.push(line);
113
+ });
114
+ return out
115
+ .join('\n')
116
+ .replace(/\n{3,}/g, '\n\n')
117
+ .trim();
118
+ }
119
+ //# sourceMappingURL=renderChangelog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderChangelog.js","sourceRoot":"","sources":["../src/renderChangelog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type RepoId } from './types.ts';
2
+ /** The `repository` field of an npm package manifest, in object form. */
3
+ interface NpmRepository {
4
+ type?: string;
5
+ url?: string;
6
+ directory?: string;
7
+ }
8
+ /** Resolve the GitHub repository for an npm package from its latest published version. */
9
+ export declare function resolveRepoFromPackage(packageName: string): Promise<RepoId>;
10
+ /**
11
+ * Parse a GitHub `owner/repo` from an npm `repository` field. Only github.com is supported:
12
+ * github.com URLs (`git+https`, `https`, `git://`, `git@github.com:`) and the `github:`
13
+ * shorthand. Throws if the repository refers to any other host or can't be parsed.
14
+ * @internal Exported for testing
15
+ */
16
+ export declare function _parseGitHubRepo(repository: NpmRepository | string | undefined, packageName: string): RepoId;
17
+ export {};
18
+ //# sourceMappingURL=resolveRepoFromPackage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveRepoFromPackage.d.ts","sourceRoot":"","sources":["../src/resolveRepoFromPackage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzD,yEAAyE;AACzE,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD,0FAA0F;AAC1F,wBAAsB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBjF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkC5G"}
@@ -0,0 +1,50 @@
1
+ import { ChangelogError } from "./types.js";
2
+ const registryBase = 'https://registry.npmjs.org';
3
+ /** Resolve the GitHub repository for an npm package from its latest published version. */
4
+ export async function resolveRepoFromPackage(packageName) {
5
+ // Encode the package name for the URL path. The leading `@` in scoped names is allowed
6
+ // unencoded, but the `/` separator must be encoded.
7
+ const encodedName = packageName.startsWith('@')
8
+ ? `@${encodeURIComponent(packageName.slice(1))}`
9
+ : encodeURIComponent(packageName);
10
+ const url = `${registryBase}/${encodedName}/latest`;
11
+ const response = await fetch(url, { headers: { Accept: 'application/json' } });
12
+ if (!response.ok) {
13
+ throw new ChangelogError(`Failed to look up npm package "${packageName}": ${response.status} ${response.statusText}`);
14
+ }
15
+ const manifest = (await response.json());
16
+ return _parseGitHubRepo(manifest.repository, packageName);
17
+ }
18
+ /**
19
+ * Parse a GitHub `owner/repo` from an npm `repository` field. Only github.com is supported:
20
+ * github.com URLs (`git+https`, `https`, `git://`, `git@github.com:`) and the `github:`
21
+ * shorthand. Throws if the repository refers to any other host or can't be parsed.
22
+ * @internal Exported for testing
23
+ */
24
+ export function _parseGitHubRepo(repository, packageName) {
25
+ const raw = typeof repository === 'string' ? repository : repository?.url;
26
+ if (!raw) {
27
+ throw new ChangelogError(`npm package "${packageName}" does not specify a repository.`);
28
+ }
29
+ // `github:owner/repo` shorthand always refers to github.com.
30
+ const shorthandMatch = raw.match(/^github:([^/#]+)\/([^/#]+?)(?:\.git)?(?:#.*)?$/);
31
+ if (shorthandMatch) {
32
+ return { owner: shorthandMatch[1], repo: shorthandMatch[2] };
33
+ }
34
+ // A bare `owner/repo` string shorthand also defaults to github.com.
35
+ const bareMatch = raw.match(/^([^/#:]+)\/([^/#]+?)(?:\.git)?(?:#.*)?$/);
36
+ if (bareMatch) {
37
+ return { owner: bareMatch[1], repo: bareMatch[2] };
38
+ }
39
+ // Any other host shorthand (e.g. `gitlab:`/`bitbucket:`) is unsupported.
40
+ if (/^\w+:[^/]+\/[^/]+$/i.test(raw) && !raw.startsWith('github:')) {
41
+ throw new ChangelogError(`npm package "${packageName}" repository is "${raw}" which does not appear to be on github.com`);
42
+ }
43
+ // URL forms: https, git+https, git://, ssh (git@github.com:owner/repo).
44
+ const urlMatch = raw.match(/github\.com[/:]([^/#]+)\/([^/#]+?)(?:\.git)?(?:#.*)?$/);
45
+ if (urlMatch) {
46
+ return { owner: urlMatch[1], repo: urlMatch[2] };
47
+ }
48
+ throw new ChangelogError(`npm package "${packageName}" repository is "${raw}" which does not appear to be on github.com`);
49
+ }
50
+ //# sourceMappingURL=resolveRepoFromPackage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveRepoFromPackage.js","sourceRoot":"","sources":["../src/resolveRepoFromPackage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAe,MAAM,YAAY,CAAC;AAczD,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAElD,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,WAAmB;IAC9D,uFAAuF;IACvF,oDAAoD;IACpD,MAAM,WAAW,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC;QAC7C,CAAC,CAAC,IAAI,kBAAkB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;QAChD,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAEpC,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,WAAW,SAAS,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAC/E,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,cAAc,CACtB,kCAAkC,WAAW,MAAM,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC5F,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAgB,CAAC;IACxD,OAAO,gBAAgB,CAAC,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAA8C,EAAE,WAAmB;IAClG,MAAM,GAAG,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC;IAC1E,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC,gBAAgB,WAAW,kCAAkC,CAAC,CAAC;IAC1F,CAAC;IAED,6DAA6D;IAC7D,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACnF,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,CAAC;IAED,oEAAoE;IACpE,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IACxE,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,CAAC;IAED,yEAAyE;IACzE,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,cAAc,CACtB,gBAAgB,WAAW,oBAAoB,GAAG,6CAA6C,CAChG,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACpF,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,CAAC;IAED,MAAM,IAAI,cAAc,CACtB,gBAAgB,WAAW,oBAAoB,GAAG,6CAA6C,CAChG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Resolve a GitHub auth token, in priority order:
3
+ * 1. The explicitly provided token (e.g. from `--token`).
4
+ * 2. The `GITHUB_TOKEN` or `GH_TOKEN` environment variables.
5
+ * 3. The output of `gh auth token` (if the GitHub CLI is installed and authenticated).
6
+ *
7
+ * Returns `undefined` if no token could be resolved.
8
+ */
9
+ export declare function resolveToken(explicitToken: string | undefined, env: NodeJS.ProcessEnv): Promise<string | undefined>;
10
+ //# sourceMappingURL=resolveToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveToken.d.ts","sourceRoot":"","sources":["../src/resolveToken.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiB7B"}
@@ -0,0 +1,27 @@
1
+ import spawn from 'nano-spawn';
2
+ /**
3
+ * Resolve a GitHub auth token, in priority order:
4
+ * 1. The explicitly provided token (e.g. from `--token`).
5
+ * 2. The `GITHUB_TOKEN` or `GH_TOKEN` environment variables.
6
+ * 3. The output of `gh auth token` (if the GitHub CLI is installed and authenticated).
7
+ *
8
+ * Returns `undefined` if no token could be resolved.
9
+ */
10
+ export async function resolveToken(explicitToken, env) {
11
+ if (explicitToken) {
12
+ return explicitToken;
13
+ }
14
+ const envToken = env.GITHUB_TOKEN || env.GH_TOKEN;
15
+ if (envToken) {
16
+ return envToken;
17
+ }
18
+ try {
19
+ const { stdout } = await spawn('gh', ['auth', 'token']);
20
+ return stdout.trim() || undefined;
21
+ }
22
+ catch {
23
+ // gh not installed or not authenticated; fall through to unauthenticated.
24
+ return undefined;
25
+ }
26
+ }
27
+ //# sourceMappingURL=resolveToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveToken.js","sourceRoot":"","sources":["../src/resolveToken.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,aAAiC,EACjC,GAAsB;IAEtB,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,QAAQ,CAAC;IAClD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type GitHubRelease, type ProperChangelogOptions } from './types.ts';
2
+ export type SelectReleasesOptions = Pick<ProperChangelogOptions, 'includePrereleases' | 'filter' | 'since' | 'from' | 'to' | 'limit'>;
3
+ /**
4
+ * Filter, sort, and slice releases according to the provided options.
5
+ * Draft releases are always excluded; prereleases are excluded unless `includePrereleases`.
6
+ * Releases are returned newest-first by published date.
7
+ */
8
+ export declare function selectReleases(releases: GitHubRelease[], options: SelectReleasesOptions): GitHubRelease[];
9
+ //# sourceMappingURL=selectReleases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectReleases.d.ts","sourceRoot":"","sources":["../src/selectReleases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE7F,MAAM,MAAM,qBAAqB,GAAG,IAAI,CACtC,sBAAsB,EACtB,oBAAoB,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CACpE,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,aAAa,EAAE,CA0CzG"}
@@ -0,0 +1,62 @@
1
+ import { ChangelogError } from "./types.js";
2
+ /**
3
+ * Filter, sort, and slice releases according to the provided options.
4
+ * Draft releases are always excluded; prereleases are excluded unless `includePrereleases`.
5
+ * Releases are returned newest-first by published date.
6
+ */
7
+ export function selectReleases(releases, options) {
8
+ let selected = releases.filter(release => !release.draft);
9
+ if (!options.includePrereleases) {
10
+ selected = selected.filter(release => !release.prerelease);
11
+ }
12
+ if (options.filter) {
13
+ const matchesTag = makeTagMatcher(options.filter);
14
+ selected = selected.filter(release => matchesTag(release.tag_name));
15
+ }
16
+ if (options.since) {
17
+ const sinceTime = options.since.getTime();
18
+ selected = selected.filter(release => {
19
+ const time = release.published_at ? new Date(release.published_at).getTime() : NaN;
20
+ return !Number.isNaN(time) && time > sinceTime;
21
+ });
22
+ }
23
+ selected.sort((a, b) => {
24
+ const aTime = (a.published_at && Date.parse(a.published_at)) || 0;
25
+ const bTime = (b.published_at && Date.parse(b.published_at)) || 0;
26
+ return bTime - aTime;
27
+ });
28
+ // Apply the `from`/`to` tag range (inclusive, order-independent).
29
+ if (options.from || options.to) {
30
+ const bounds = [
31
+ options.from !== undefined ? indexOfTag(selected, options.from) : 0,
32
+ options.to !== undefined ? indexOfTag(selected, options.to) : selected.length - 1,
33
+ ];
34
+ const start = Math.min(...bounds);
35
+ const end = Math.max(...bounds);
36
+ selected = selected.slice(start, end + 1);
37
+ }
38
+ if (options.limit !== undefined && options.limit >= 0) {
39
+ selected = selected.slice(0, options.limit);
40
+ }
41
+ return selected;
42
+ }
43
+ /** Find the index of a release by tag name, throwing a helpful error if not found. */
44
+ function indexOfTag(releases, tag) {
45
+ const index = releases.findIndex(release => release.tag_name === tag);
46
+ if (index === -1) {
47
+ throw new ChangelogError(`No release found with tag "${tag}".`);
48
+ }
49
+ return index;
50
+ }
51
+ /**
52
+ * Build a tag-matching predicate from a filter. A `RegExp` matches tags that satisfy it;
53
+ * a string matches tags that contain it (case-insensitive).
54
+ */
55
+ function makeTagMatcher(filter) {
56
+ if (filter instanceof RegExp) {
57
+ return tag => filter.test(tag);
58
+ }
59
+ const needle = filter.toLowerCase();
60
+ return tag => tag.toLowerCase().includes(needle);
61
+ }
62
+ //# sourceMappingURL=selectReleases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectReleases.js","sourceRoot":"","sources":["../src/selectReleases.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ import type { components } from '@octokit/openapi-types';
2
+ import type { CommanderError, OutputConfiguration } from 'commander';
3
+ /** A GitHub release as returned by the REST API (`GET /repos/{owner}/{repo}/releases`). */
4
+ export type GitHubRelease = components['schemas']['release'];
5
+ /** Parsed `owner/repo` identifier. */
6
+ export interface RepoId {
7
+ owner: string;
8
+ repo: string;
9
+ }
10
+ /** Options as returned by `program.parse().opts()`. */
11
+ export interface CliOptions {
12
+ /** Repository to read releases from. */
13
+ repo?: RepoId;
14
+ /** npm package name the repo was resolved from, if any (used for the changelog heading/filename). */
15
+ package?: string;
16
+ /** Auth token for the GitHub API (optional; requests are rate-limited without one). */
17
+ token?: string;
18
+ /** Include prerelease releases (default: false). Draft releases are always excluded. */
19
+ includePrereleases?: boolean;
20
+ /** Only include releases up to and including this tag (most recent bound). */
21
+ from?: string;
22
+ /** Only include releases down to and including this tag (oldest bound). */
23
+ to?: string;
24
+ /** Maximum number of releases to include. */
25
+ limit?: number;
26
+ /**
27
+ * Filter releases by tag name. A string matches tags that contain it (case-insensitive);
28
+ * a `RegExp` matches tags that satisfy it. (The CLI converts a value wrapped in slashes,
29
+ * e.g. `/^v1\./i`, into a `RegExp`.)
30
+ */
31
+ filter?: string | RegExp;
32
+ /** Only include releases published after this date. */
33
+ since?: Date;
34
+ /** Write output to this file */
35
+ out?: string;
36
+ /** If true, write to stdout instead of a file */
37
+ stdout?: boolean;
38
+ }
39
+ /** Options controlling changelog generation. */
40
+ export type ProperChangelogOptions = Required<Pick<CliOptions, 'repo'>> & CliOptions;
41
+ export interface CliContext {
42
+ argv: string[];
43
+ env: NodeJS.ProcessEnv;
44
+ /** Commander error handler */
45
+ exitOverride?: (err: CommanderError) => never | void;
46
+ /** Commander error logging handler */
47
+ writeErr?: OutputConfiguration['writeErr'];
48
+ log: (message: string) => void;
49
+ warn: (message: string) => void;
50
+ writeFile: (file: string, content: string) => void;
51
+ }
52
+ /** Throw this to indicate an expected error (stack won't be logged) */
53
+ export declare class ChangelogError extends Error {
54
+ name: string;
55
+ }
56
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErE,2FAA2F;AAC3F,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC;AAE7D,sCAAsC;AACtC,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,uDAAuD;IACvD,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,gDAAgD;AAChD,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,GAAG,UAAU,CAAC;AAErF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,8BAA8B;IAC9B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,KAAK,GAAG,IAAI,CAAC;IACrD,sCAAsC;IACtC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC3C,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AAED,uEAAuE;AACvE,qBAAa,cAAe,SAAQ,KAAK;IACvC,IAAI,SAAoB;CACzB"}
package/lib/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /** Throw this to indicate an expected error (stack won't be logged) */
2
+ export class ChangelogError extends Error {
3
+ name = 'ChangelogError';
4
+ }
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAyDA,uEAAuE;AACvE,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,IAAI,GAAG,gBAAgB,CAAC;CACzB"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "proper-changelog",
3
+ "version": "0.1.0",
4
+ "description": "Make a readable changelog from GitHub releases",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/microsoft/beachball",
9
+ "directory": "packages/proper-changelog"
10
+ },
11
+ "license": "MIT",
12
+ "exports": {
13
+ ".": null
14
+ },
15
+ "bin": "./bin/proper-changelog.js",
16
+ "engines": {
17
+ "node": ">=22.18.0"
18
+ },
19
+ "files": [
20
+ "bin",
21
+ "lib/!(__*)",
22
+ "lib/!(__*)/**/*"
23
+ ],
24
+ "scripts": {
25
+ "build": "yarn run -T tsc --pretty",
26
+ "depcheck": "yarn run -T depcheck .",
27
+ "lint": "yarn run -T eslint --color --max-warnings=0 src",
28
+ "test": "cross-env NODE_OPTIONS='--experimental-vm-modules' yarn run -T jest",
29
+ "update-snapshots": "yarn test -u"
30
+ },
31
+ "devDependencies": {
32
+ "@microsoft/beachball-scripts": "workspace:^",
33
+ "@octokit/openapi-types": "^27.0.0",
34
+ "cross-env": "^10.1.0"
35
+ },
36
+ "dependencies": {
37
+ "commander": "^15.0.0",
38
+ "nano-spawn": "^2.1.0"
39
+ }
40
+ }