@ljharb/coauthors 1.0.0 → 2.0.1
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/CHANGELOG.md +32 -0
- package/README.md +1 -1
- package/bin.mjs +9 -34
- package/package.json +18 -29
- package/results.mjs +4 -5
- package/validateRemote.mjs +7 -7
- package/help.txt +0 -4
- package/pargs.mjs +0 -119
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [v2.0.1](https://github.com/ljharb/coauthors/compare/v2.0.0...v2.0.1) - 2026-06-18
|
|
9
|
+
|
|
10
|
+
### Commits
|
|
11
|
+
|
|
12
|
+
- [types] clean up some annotations [`5c56964`](https://github.com/ljharb/coauthors/commit/5c56964c94ec43d0c593b8e82a3759267c940e22)
|
|
13
|
+
- [Dev Deps] update `eslint` [`e39c67a`](https://github.com/ljharb/coauthors/commit/e39c67ad57b9de9c9d627ca8ac729d1472d152b7)
|
|
14
|
+
- [Tests] extract coverage config to file [`b13990e`](https://github.com/ljharb/coauthors/commit/b13990e8049a070dee35d6d9b3c3ce4d242f53ba)
|
|
15
|
+
- [actions] update workflows [`a4bb6ef`](https://github.com/ljharb/coauthors/commit/a4bb6ef475b80587431ed03400a850bf22c15709)
|
|
16
|
+
- [Tests] increase coverage [`b980162`](https://github.com/ljharb/coauthors/commit/b9801621dc9bcf2d2d0c00f2a252f89bf89a8791)
|
|
17
|
+
- [Dev Deps] update `@ljharb/eslint-config`, `@types/node`, `auto-changelog`, `esmock`, `tape` [`7562264`](https://github.com/ljharb/coauthors/commit/7562264c14baf865411f03b5f3a31d8451bdf0c5)
|
|
18
|
+
- [Deps] update `pargs` [`9be2a35`](https://github.com/ljharb/coauthors/commit/9be2a35b70f1ad771cc1bfb743cde7c48702ca4c)
|
|
19
|
+
- [Dev Deps] update `@ljharb/eslint-config`, `@types/node`, `eslint`, `npmignore` [`78926c8`](https://github.com/ljharb/coauthors/commit/78926c848b5644ab3a08693b8a28d85171bd7e06)
|
|
20
|
+
- [Dev Deps] update `@ljharb/eslint-config`, `@types/node` [`4316fe4`](https://github.com/ljharb/coauthors/commit/4316fe4f0975e1472af8a857666e82b5c6f4fc9a)
|
|
21
|
+
- [Deps] update `pargs` [`0bd17fb`](https://github.com/ljharb/coauthors/commit/0bd17fb53c0b1b8058f8d5d47d3b53ffe9c55ff8)
|
|
22
|
+
- [Deps] update `pargs` [`9cbfea5`](https://github.com/ljharb/coauthors/commit/9cbfea509bd39b6dfa020818169bfd7499fbf761)
|
|
23
|
+
- [Deps] update `pargs` [`f036877`](https://github.com/ljharb/coauthors/commit/f0368774d195dfaf96356e8ce6e8ac4173a55485)
|
|
24
|
+
- [Dev Deps] update `c8` [`ce1d62e`](https://github.com/ljharb/coauthors/commit/ce1d62e3613fc31ed8ee6a0bb4a213264b976c75)
|
|
25
|
+
- [Deps] update `pargs` [`edb1de1`](https://github.com/ljharb/coauthors/commit/edb1de1ffb208b503e1ac06a1a2c55ef289a8bd5)
|
|
26
|
+
|
|
27
|
+
## [v2.0.0](https://github.com/ljharb/coauthors/compare/v1.0.0...v2.0.0) - 2025-10-29
|
|
28
|
+
|
|
29
|
+
### Commits
|
|
30
|
+
|
|
31
|
+
- [Refactor] use `pargs` package [`4b46ee9`](https://github.com/ljharb/coauthors/commit/4b46ee9f6ddefe5b936190d32bff54bd46eb45f2)
|
|
32
|
+
- [Dev Deps] update `@ljharb/eslint-config`, `@ljharb/tsconfig@latest`, `@types/node`, `@types/tape`, `auto-changelog`, `c8`, `esmock`, `tape` [`caede0a`](https://github.com/ljharb/coauthors/commit/caede0a44cc1ce0076e8ac01d5a5d75c92e43234)
|
|
33
|
+
- [Dev Deps] update `@types/node`, `esmock` [`09becac`](https://github.com/ljharb/coauthors/commit/09becacad05b12e30206f82f95a2f052789a05d7)
|
|
34
|
+
- [meta] fix binary name [`9db7fac`](https://github.com/ljharb/coauthors/commit/9db7fac087158cc3b5fe9476c670e04aa9546f76)
|
|
35
|
+
- [Tests] replace `aud` with `npm audit` [`59ad63a`](https://github.com/ljharb/coauthors/commit/59ad63aaeafb10492ba1ea010809b314e3942a8a)
|
|
36
|
+
- [Dev Deps] update `@types/node` [`00ffe27`](https://github.com/ljharb/coauthors/commit/00ffe270c1c21fceecb79424eeedc122a8ade21b)
|
|
37
|
+
- [Breaking] tighten engines requirements [`e33b6b0`](https://github.com/ljharb/coauthors/commit/e33b6b0e188aff796d7bc75ec7e62d804b759811)
|
|
38
|
+
- [Dev Deps] add missing peer dep [`5c5483b`](https://github.com/ljharb/coauthors/commit/5c5483b245b47e39beb5ff8e6799039442ba0a28)
|
|
39
|
+
|
|
8
40
|
## v1.0.0 - 2024-07-16
|
|
9
41
|
|
|
10
42
|
### Commits
|
package/README.md
CHANGED
package/bin.mjs
CHANGED
|
@@ -1,52 +1,27 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFile } from 'fs/promises';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
3
|
import validateRemote from './validateRemote.mjs';
|
|
7
4
|
import getResults from './results.mjs';
|
|
8
5
|
|
|
9
|
-
import pargs from '
|
|
10
|
-
|
|
11
|
-
async function getHelpText() {
|
|
12
|
-
return `${await readFile(path.join(import.meta.dirname, './help.txt'), 'utf-8')}`;
|
|
13
|
-
}
|
|
6
|
+
import pargs from 'pargs';
|
|
14
7
|
|
|
15
8
|
const {
|
|
16
|
-
|
|
9
|
+
help,
|
|
17
10
|
positionals,
|
|
18
11
|
errors,
|
|
19
|
-
} = await pargs(
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
help: { type: 'boolean' },
|
|
24
|
-
},
|
|
25
|
-
allowPositionals: 1,
|
|
26
|
-
},
|
|
27
|
-
);
|
|
12
|
+
} = await pargs(import.meta.filename, {
|
|
13
|
+
allowPositionals: 1,
|
|
14
|
+
positionals: [{ description: 'the git remote to read co-authors from (default: origin)', name: 'remote' }],
|
|
15
|
+
});
|
|
28
16
|
|
|
29
17
|
const remote = validateRemote(positionals[0] ?? 'origin');
|
|
30
18
|
|
|
31
19
|
if (typeof remote !== 'string') {
|
|
32
|
-
errors.
|
|
20
|
+
errors[errors.length] = `${remote}`;
|
|
33
21
|
}
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
const helpText = await getHelpText();
|
|
37
|
-
if (errors.length === 0) {
|
|
38
|
-
console.log(helpText);
|
|
39
|
-
} else {
|
|
40
|
-
console.error(`${helpText}${errors.length === 0 ? '' : '\n'}`);
|
|
41
|
-
|
|
42
|
-
process.exitCode ||= parseInt('1'.repeat(errors.length), 2);
|
|
43
|
-
errors.forEach((error) => console.error(error));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
process.exit();
|
|
47
|
-
}
|
|
23
|
+
await help();
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
const results = Array.from(getResults(/** @type {string} */ (remote)), (x) => `Co-authored-by: ${x}`);
|
|
25
|
+
const results = Array.from(getResults(`${remote}`), (x) => `Co-authored-by: ${x}`);
|
|
51
26
|
|
|
52
27
|
console.log(results.join('\n'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ljharb/coauthors",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "A cli to generate a complete git co-authors list, including existing co-authors, for use in a commit message.",
|
|
5
5
|
"bin": "./bin.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"prepack": "npmignore --auto --commentLines=autogenerated",
|
|
12
12
|
"prepublish": "not-in-publish || npm run prepublishOnly",
|
|
13
13
|
"prepublishOnly": "safe-publish-latest",
|
|
14
|
-
"lint": "eslint
|
|
14
|
+
"lint": "eslint .",
|
|
15
15
|
"postlint": "tsc",
|
|
16
16
|
"pretest": "npm run lint",
|
|
17
17
|
"tests-only": "c8 tape 'test/**/*.*js'",
|
|
18
18
|
"test": "npm run tests-only",
|
|
19
|
-
"posttest": "
|
|
19
|
+
"posttest": "npx npm@\">= 10.2\" audit --production",
|
|
20
20
|
"version": "auto-changelog && git add CHANGELOG.md",
|
|
21
21
|
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
|
|
22
22
|
},
|
|
@@ -38,39 +38,27 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/ljharb/coauthors#readme",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"commit-to-co-authors": "^0.1.0"
|
|
41
|
+
"commit-to-co-authors": "^0.1.0",
|
|
42
|
+
"pargs": "^1.4.2"
|
|
42
43
|
},
|
|
43
44
|
"engines": {
|
|
44
|
-
"node": ">=
|
|
45
|
+
"node": "^22.20 || ^24.10 || >= 25"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@ljharb/eslint-config": "^
|
|
48
|
-
"@ljharb/tsconfig": "^0.2
|
|
49
|
-
"@types/node": "^
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"eslint": "^8.8.0",
|
|
55
|
-
"esmock": "^2.6.6",
|
|
48
|
+
"@ljharb/eslint-config": "^22.2.3",
|
|
49
|
+
"@ljharb/tsconfig": "^0.3.2",
|
|
50
|
+
"@types/node": "^22.19.21",
|
|
51
|
+
"auto-changelog": "^2.6.0",
|
|
52
|
+
"c8": "^11.0.0",
|
|
53
|
+
"eslint": "^10.5.0",
|
|
54
|
+
"esmock": "^2.7.6",
|
|
56
55
|
"in-publish": "^2.0.1",
|
|
57
|
-
"
|
|
56
|
+
"jiti": "^0.0.0",
|
|
57
|
+
"npmignore": "^0.3.5",
|
|
58
58
|
"safe-publish-latest": "^2.0.0",
|
|
59
|
-
"tape": "^5.
|
|
59
|
+
"tape": "^5.10.2",
|
|
60
60
|
"typescript": "next"
|
|
61
61
|
},
|
|
62
|
-
"c8": {
|
|
63
|
-
"all": true,
|
|
64
|
-
"reporters": [
|
|
65
|
-
"html",
|
|
66
|
-
"text",
|
|
67
|
-
"lcov"
|
|
68
|
-
],
|
|
69
|
-
"exclude": [
|
|
70
|
-
"coverage",
|
|
71
|
-
"./pargs.mjs"
|
|
72
|
-
]
|
|
73
|
-
},
|
|
74
62
|
"auto-changelog": {
|
|
75
63
|
"output": "CHANGELOG.md",
|
|
76
64
|
"template": "keepachangelog",
|
|
@@ -82,7 +70,8 @@
|
|
|
82
70
|
"publishConfig": {
|
|
83
71
|
"ignore": [
|
|
84
72
|
".github/workflows",
|
|
85
|
-
".
|
|
73
|
+
"eslint.config.mjs",
|
|
74
|
+
".nycrc",
|
|
86
75
|
"test"
|
|
87
76
|
]
|
|
88
77
|
}
|
package/results.mjs
CHANGED
|
@@ -4,19 +4,18 @@ import { commitToCoAuthors } from 'commit-to-co-authors';
|
|
|
4
4
|
|
|
5
5
|
import getDefaultBranch from './getDefaultBranch.mjs';
|
|
6
6
|
|
|
7
|
-
/** @
|
|
7
|
+
/** @param {string} remote */
|
|
8
8
|
export default function getResults(remote) {
|
|
9
9
|
const defaultBranch = getDefaultBranch(remote);
|
|
10
10
|
|
|
11
11
|
const logCommitters = `${execSync(`git shortlog -sne ${remote}/${defaultBranch}..HEAD`)}`;
|
|
12
12
|
const mappedLogCommitters = logCommitters.matchAll(/\t(?<author>.*)$/gm)
|
|
13
|
-
|
|
14
|
-
.map(
|
|
15
|
-
|
|
13
|
+
// @ts-expect-error TODO: file TS bug about `groups` always being present when a group is in the regex
|
|
14
|
+
.map((x) => x.groups.author);
|
|
15
|
+
|
|
16
16
|
const fromLogs = new Set(mappedLogCommitters);
|
|
17
17
|
|
|
18
18
|
const logText = `${execSync(`git log --no-expand-tabs --pretty=full ${remote}/${defaultBranch}..HEAD`)}`;
|
|
19
|
-
/** @type {Set<string>} */
|
|
20
19
|
const fromMsgs = new Set(commitToCoAuthors(logText).map(({ name, email }) => `${name} <${email}>`));
|
|
21
20
|
|
|
22
21
|
return fromMsgs.union(fromLogs);
|
package/validateRemote.mjs
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
|
|
3
|
-
/** @type {(remote: string, cwd?: string) => string | {
|
|
3
|
+
/** @type {(remote: string, cwd?: string) => string | { toString(): string }} */
|
|
4
4
|
export default function validateRemote(remote, cwd = process.cwd()) {
|
|
5
5
|
if (remote === '' || remote.includes(' ')) {
|
|
6
|
-
return {
|
|
6
|
+
return /** @type {{ toString(): string }} */ ({
|
|
7
7
|
__proto__: null,
|
|
8
|
-
|
|
9
|
-
};
|
|
8
|
+
toString() { return 'Remote name must not be empty, nor contain spaces.'; },
|
|
9
|
+
});
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const allRemotes = `${execSync('git remote', { cwd })}`.split('\n');
|
|
13
13
|
|
|
14
14
|
if (!allRemotes.includes(remote)) {
|
|
15
|
-
return {
|
|
15
|
+
return /** @type {{ toString(): string }} */ ({
|
|
16
16
|
__proto__: null,
|
|
17
|
-
|
|
18
|
-
};
|
|
17
|
+
toString() { return `Remote \`${remote}\` does not exist; check \`git remote\` output`; },
|
|
18
|
+
});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
return remote;
|
package/help.txt
DELETED
package/pargs.mjs
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { parseArgs } from 'util';
|
|
2
|
-
import { realpathSync } from 'fs';
|
|
3
|
-
|
|
4
|
-
/** @typedef {import('util').ParseArgsConfig} ParseArgsConfig */
|
|
5
|
-
|
|
6
|
-
/** @typedef {(Error | TypeError) & { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' | 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE' | 'ERR_INVALID_ARG_TYPE' | 'ERR_INVALID_ARG_VALUE' | 'ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL'}} ParseArgsError */
|
|
7
|
-
|
|
8
|
-
/** @type {(e: unknown) => e is ParseArgsError} */
|
|
9
|
-
function isParseArgsError(e) {
|
|
10
|
-
return !!e
|
|
11
|
-
&& typeof e === 'object'
|
|
12
|
-
&& 'code' in e
|
|
13
|
-
&& (
|
|
14
|
-
e.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION'
|
|
15
|
-
|| e.code === 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE'
|
|
16
|
-
|| e.code === 'ERR_INVALID_ARG_TYPE'
|
|
17
|
-
|| e.code === 'ERR_INVALID_ARG_VALUE'
|
|
18
|
-
|| e.code === 'ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL'
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
/** @typedef {Omit<ParseArgsConfig, 'args' | 'strict' | 'allowPositionals'> & { allowPositionals?: boolean | number }} PargsConfig */
|
|
22
|
-
|
|
23
|
-
/** @type {(entrypointPath: ImportMeta['filename'], obj: PargsConfig) => Promise<{ errors: string[] } & ReturnType<typeof parseArgs>>} */
|
|
24
|
-
export default async function pargs(entrypointPath, obj) {
|
|
25
|
-
const argv = process.argv.flatMap((arg) => {
|
|
26
|
-
try {
|
|
27
|
-
const realpathedArg = realpathSync(arg);
|
|
28
|
-
if (
|
|
29
|
-
realpathedArg === process.execPath
|
|
30
|
-
|| realpathedArg === entrypointPath
|
|
31
|
-
) {
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
34
|
-
} catch (e) { /**/ }
|
|
35
|
-
return arg;
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
if ('help' in obj) {
|
|
39
|
-
throw new TypeError('The "help" option is reserved');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** @type {ParseArgsConfig & { tokens: true }} */
|
|
43
|
-
const newObj = {
|
|
44
|
-
args: argv,
|
|
45
|
-
...obj,
|
|
46
|
-
options: {
|
|
47
|
-
...obj.options,
|
|
48
|
-
help: {
|
|
49
|
-
default: false,
|
|
50
|
-
type: 'boolean',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
tokens: true,
|
|
54
|
-
// @ts-expect-error blocked on @types/node v22
|
|
55
|
-
allowNegative: true,
|
|
56
|
-
allowPositionals: typeof obj.allowPositionals !== 'undefined',
|
|
57
|
-
strict: true,
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const errors = [];
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
const { tokens, ...results } = parseArgs(newObj);
|
|
64
|
-
|
|
65
|
-
const posCount = typeof obj.allowPositionals === 'number' ? obj.allowPositionals : obj.allowPositionals ? Infinity : 0;
|
|
66
|
-
if (results.positionals.length > posCount) {
|
|
67
|
-
errors.push(`Only ${posCount} positional arguments allowed; got ${results.positionals.length}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** @typedef {Extract<typeof tokens[number], { kind: 'option' }>} OptionToken */
|
|
71
|
-
const optionTokens = tokens.filter(/** @type {(token: typeof tokens[number]) => token is OptionToken} */ (token) => token.kind === 'option');
|
|
72
|
-
|
|
73
|
-
const bools = obj.options ? Object.entries(obj.options).filter(([, { type }]) => type === 'boolean') : [];
|
|
74
|
-
const boolMap = new Map(bools);
|
|
75
|
-
for (let i = 0; i < optionTokens.length; i += 1) {
|
|
76
|
-
const { name, value } = optionTokens[i];
|
|
77
|
-
if (boolMap.has(name) && typeof value !== 'boolean' && typeof value !== 'undefined') {
|
|
78
|
-
errors.push(`Error: Argument --${name} must be a boolean`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const passedArgs = new Set(optionTokens.map(({ name, rawName }) => (rawName === '--no-help' ? rawName : name)));
|
|
83
|
-
|
|
84
|
-
const groups = Object.groupBy(passedArgs, (x) => x.replace(/^no-/, ''));
|
|
85
|
-
for (let i = 0; i < bools.length; i++) {
|
|
86
|
-
const [key] = bools[i];
|
|
87
|
-
if ((groups[key]?.length ?? 0) > 1) {
|
|
88
|
-
errors.push(`Error: Arguments \`--${key}\` and \`--no-${key}\` are mutually exclusive`);
|
|
89
|
-
}
|
|
90
|
-
if (passedArgs.has(`no-${key}`)) {
|
|
91
|
-
// @ts-expect-error
|
|
92
|
-
results.values[key] = !results.values[`no-${key}`];
|
|
93
|
-
}
|
|
94
|
-
// @ts-expect-error
|
|
95
|
-
delete results.values[`no-${key}`];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const knownOptions = obj.options ? Object.keys(obj.options) : [];
|
|
99
|
-
const unknownArgs = knownOptions.length > 0 ? passedArgs.difference(new Set(knownOptions)) : passedArgs;
|
|
100
|
-
if (unknownArgs.size > 0) {
|
|
101
|
-
errors.push(`Error: Unknown option(s): ${Array.from(unknownArgs, (x) => `\`${x}\``).join(', ')}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
errors,
|
|
106
|
-
...results,
|
|
107
|
-
...obj.tokens && { tokens },
|
|
108
|
-
};
|
|
109
|
-
} catch (e) {
|
|
110
|
-
if (isParseArgsError(e)) {
|
|
111
|
-
return {
|
|
112
|
-
values: {},
|
|
113
|
-
positionals: [],
|
|
114
|
-
errors: [`Error: ${e.message}`],
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
throw e;
|
|
118
|
-
}
|
|
119
|
-
}
|