watskeburt 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +65 -24
- package/dist/cjs-bundle.js +1 -0
- package/package.json +72 -21
- package/src/.eslintrc.json +47 -0
- package/src/cli.mjs +13 -11
- package/src/convert-to-change-object.mjs +10 -12
- package/src/formatters/format.mjs +1 -1
- package/src/formatters/json.mjs +2 -1
- package/src/formatters/regex.mjs +13 -2
- package/src/git-primitives.mjs +88 -0
- package/src/main.mjs +13 -18
- package/src/version.mjs +1 -0
- package/types/watskeburt.d.ts +41 -1
- package/src/get-diff-lines.mjs +0 -51
package/README.md
CHANGED
@@ -1,47 +1,88 @@
|
|
1
1
|
# watskeburt
|
2
2
|
|
3
|
-
get git changed files & their
|
3
|
+
get git changed files & their statuses since _any revision_
|
4
4
|
|
5
5
|
## what's this do?
|
6
6
|
|
7
7
|
A micro-lib to retrieve an array of file names that were changed (added,
|
8
|
-
modified, renamed, deleted, ...) since the
|
8
|
+
modified, renamed, deleted, ...) since the revision it got passed. Also
|
9
|
+
sports a cli for use outside of JavaScript c.s.
|
9
10
|
|
10
|
-
- :warning: NOT yet published on npm
|
11
11
|
- :warning: in the process of getting 'production ready'. It's automatically
|
12
|
-
tested + it's using itself
|
13
|
-
|
14
|
-
- :warning:
|
15
|
-
|
12
|
+
tested + it's using itself - but a bunch of static analysis and a bit of
|
13
|
+
automation still needs be added.
|
14
|
+
- :warning: Interface is stable-ish, but can can change until 1.0.0 is published
|
15
|
+
- :warning: expect some rough edges for e.g. error scenarios
|
16
16
|
|
17
|
-
##
|
17
|
+
## why?
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
},
|
28
|
-
{ "name": "src/not-tracked-yet.mjs", "changeType": "untracked" }
|
29
|
-
// ...
|
30
|
-
]
|
31
|
-
```
|
19
|
+
Useful primitives to support e.g. auto-documenting pull requests, or to save
|
20
|
+
processing power by only doing static analysis only over stuff that is changed.
|
21
|
+
|
22
|
+
There are a few packages like these like this on npm, but it seems they've
|
23
|
+
fallen out of maintenance. More generic packages don't get plagued by this
|
24
|
+
but for just this simple usage they're a bit overkill.
|
25
|
+
|
26
|
+
## usage
|
32
27
|
|
33
|
-
|
28
|
+
### :shell: cli
|
34
29
|
|
35
30
|
For now there's also a simple command line interface
|
36
31
|
|
32
|
+
```shell
|
33
|
+
# list all JavaScript-ish files changed since main in a regular expression
|
34
|
+
$ npx watskeburt main
|
35
|
+
^(src/cli.mjs|src/formatters/regex.mjs|src/version.mjs)$
|
37
36
|
```
|
38
|
-
Usage: watskeburt [options] <reference>
|
39
37
|
|
40
|
-
|
38
|
+
By default this returns a regex that contains all changed files that could be
|
39
|
+
source files in the JavaScript ecosystem (.js, .mjs, .ts, .tsx ...) that can
|
40
|
+
be used in e.g. the `--focus` filter of dependency-cruiser:
|
41
|
+
|
42
|
+
```
|
43
|
+
Usage: cli [options] [revision]
|
44
|
+
|
45
|
+
lists files & their statuses since [revision].
|
46
|
+
|
47
|
+
-> When you don't pass a revision the revision defaults to the current one.
|
41
48
|
|
42
49
|
Options:
|
50
|
+
-V, --version output the version number
|
43
51
|
-T, --output-type <type> json,regex (default: "regex")
|
44
52
|
--tracked-only only take tracked files into account (default: false)
|
45
53
|
-h, --help display help for command
|
54
|
+
```
|
46
55
|
|
56
|
+
### :scroll: API
|
57
|
+
|
58
|
+
```javascript
|
59
|
+
// const { list } = require('watskeburt'); // will work in commonjs contexts as well
|
60
|
+
import { list, getSHA } from "watskeburt";
|
61
|
+
|
62
|
+
// print the SHA1 of the current HEAD
|
63
|
+
console.log(getSHA());
|
64
|
+
|
65
|
+
// list all files that differ between 'main' and
|
66
|
+
/** @type {import('watskeburt').IChange[]} */
|
67
|
+
const lChangedFiles = list("main");
|
47
68
|
```
|
69
|
+
|
70
|
+
An array of changes looks something like this:
|
71
|
+
|
72
|
+
```javascript
|
73
|
+
[
|
74
|
+
{ name: "doc/cli.md", changeType: "modified" },
|
75
|
+
{
|
76
|
+
name: "test/thing.spec.mjs",
|
77
|
+
changeType: "renamed",
|
78
|
+
oldName: "test/old-thing.spec.mjs",
|
79
|
+
},
|
80
|
+
{ name: "src/not-tracked-yet.mjs", changeType: "untracked" },
|
81
|
+
];
|
82
|
+
```
|
83
|
+
|
84
|
+
## 🇳🇱 'watskeburt'??
|
85
|
+
|
86
|
+
_watskeburt_ is a fast pronunciation of the Dutch sentence "Wat is er gebeurd?"
|
87
|
+
(What has happened?), as well as the title of a song by the Dutch hip hop group
|
88
|
+
"De Jeugd van Tegenwoordig".
|
@@ -0,0 +1 @@
|
|
1
|
+
var s=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var A=(e,t)=>{for(var n in t)s(e,n,{get:t[n],enumerable:!0})},_=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of E(t))!C.call(e,o)&&o!==n&&s(e,o,{get:()=>t[o],enumerable:!(r=x(t,o))||r.enumerable});return e};var R=e=>_(s({},"__esModule",{value:!0}),e);var H={};A(H,{getSHA:()=>w,list:()=>M});module.exports=R(H);var u=require("os"),D=/^(?<changeType>[ACDMRTUXB])(?<similarity>[0-9]{3})?[ \t]+(?<name>[^ \t]+)[ \t]*(?<newName>[^ \t]+)?$/,L=/^(?<stagedChangeType>[ ACDMRTUXB?!])(?<unStagedChangeType>[ ACDMRTUXB?!])[ \t]+(?<name>[^ \t]+)(( -> )(?<newName>[^ \t]+))?$/,O={A:"added",C:"copied",D:"deleted",M:"modified",R:"renamed",T:"type changed",U:"unmerged",B:"pairing broken"," ":"unmodified","?":"untracked","!":"ignored"};function i(e){return O[e]||"unknown"}function U(e){let t=e.match(L),n={};if(t){let r=i(t.groups.stagedChangeType),o=i(t.groups.unStagedChangeType);n.changeType=r==="unmodified"?o:r,t.groups.newName?(n.name=t.groups.newName,n.oldName=t.groups.name):n.name=t.groups.name}return n}function v(e){let t=e.match(D),n={};return t&&(n.changeType=i(t.groups.changeType),t.groups.newName?(n.name=t.groups.newName,n.oldName=t.groups.name):n.name=t.groups.name),n}function m(e){return e.split(u.EOL).filter(Boolean).map(U).filter(({changeType:t})=>Boolean(t))}function g(e){return e.split(u.EOL).filter(Boolean).map(v).filter(({changeType:t})=>Boolean(t))}var a=require("child_process");function d(e){return e instanceof Buffer?e.toString("utf8"):e}function $(e){throw e.code==="ENOENT"?new Error("git executable not found"):new Error(`internal spawn error: ${e}`)}function c(e,t,n){let r=n("git",e,{cwd:process.cwd(),env:process.env});if(r.error&&$(r.error),r.status===0)return d(r.stdout);throw new Error(t[r.status]||`internal git error: ${r.status} (${d(r.stderr)})`)}function T(e=a.spawnSync){let t={129:`'${process.cwd()}' does not seem to be a git repository`};return c(["status","--porcelain"],t,e)}function h(e,t=a.spawnSync){let n={128:`revision '${e}' unknown `,129:`'${process.cwd()}' does not seem to be a git repository`};return c(["diff",e,"--name-status"],n,t)}function S(e=a.spawnSync){return c(["rev-parse","HEAD"],{},e).slice(0,40)}var N=require("path");function f(e,t=[".js",".jsx",".mjs",".cjs",".ts",".tsx",".vue",".vuex",".json"],n=["modified","added","renamed","copied","untracked"]){return`^(${e.filter(o=>n.includes(o.changeType)).map(({name:o})=>o).filter(o=>t.includes((0,N.extname)(o))).join("|")})$`}function p(e){return JSON.stringify(e,null,2)}var j={regex:f,json:p,object:e=>e},B="object";function l(e,t){return j[t||B](e)}function w(){return S()}function M(e,t){let n=e||w(),r=g(h(n)),o=t||{};return o.trackedOnly||(r=r.concat(m(T()).filter(({changeType:y})=>y==="untracked"))),l(r,o.outputType)}0&&(module.exports={getSHA,list});
|
package/package.json
CHANGED
@@ -1,20 +1,41 @@
|
|
1
1
|
{
|
2
2
|
"name": "watskeburt",
|
3
|
-
"version": "0.
|
4
|
-
"description": "
|
3
|
+
"version": "0.4.0",
|
4
|
+
"description": "List files changed since a git revision",
|
5
|
+
"keywords": [
|
6
|
+
"git",
|
7
|
+
"diff"
|
8
|
+
],
|
9
|
+
"homepage": "https://github.com/sverweij/watskeburt",
|
10
|
+
"repository": {
|
11
|
+
"type": "git",
|
12
|
+
"url": "git+https://github.com/sverweij/watskeburt"
|
13
|
+
},
|
14
|
+
"bugs": {
|
15
|
+
"url": "https://github.com/sverweij/watskeburt/issues"
|
16
|
+
},
|
17
|
+
"author": {
|
18
|
+
"name": "Sander Verweij",
|
19
|
+
"url": "https://sverweij.github.io"
|
20
|
+
},
|
21
|
+
"license": "MIT",
|
5
22
|
"bin": "src/cli.mjs",
|
6
|
-
"main": "
|
23
|
+
"main": "dist/cjs-bundle.js",
|
7
24
|
"module": "src/main.mjs",
|
25
|
+
"sideEffects": false,
|
8
26
|
"exports": {
|
9
27
|
".": [
|
10
28
|
{
|
11
|
-
"import": "./src/main.mjs"
|
12
|
-
|
29
|
+
"import": "./src/main.mjs",
|
30
|
+
"require": "./dist/cjs-bundle.js"
|
31
|
+
},
|
32
|
+
"./dist/cjs-bundle.js"
|
13
33
|
]
|
14
34
|
},
|
15
35
|
"types": "types/watskeburt.d.ts",
|
16
36
|
"files": [
|
17
37
|
"src",
|
38
|
+
"dist",
|
18
39
|
"!src/**/*.spec.mjs",
|
19
40
|
"!**/*.DS_Store",
|
20
41
|
"types",
|
@@ -22,31 +43,61 @@
|
|
22
43
|
"package.json",
|
23
44
|
"README.md"
|
24
45
|
],
|
46
|
+
"dependencies": {
|
47
|
+
"commander": "^9.3.0"
|
48
|
+
},
|
49
|
+
"devDependencies": {
|
50
|
+
"@typescript-eslint/eslint-plugin": "^5.30.5",
|
51
|
+
"c8": "^7.11.3",
|
52
|
+
"dependency-cruiser": "^11.11.0",
|
53
|
+
"esbuild": "^0.14.48",
|
54
|
+
"eslint": "^8.19.0",
|
55
|
+
"eslint-config-moving-meadow": "^3.0.0",
|
56
|
+
"eslint-config-prettier": "^8.5.0",
|
57
|
+
"eslint-plugin-budapestian": "^4.0.0",
|
58
|
+
"eslint-plugin-import": "^2.26.0",
|
59
|
+
"eslint-plugin-mocha": "^10.0.5",
|
60
|
+
"eslint-plugin-node": "^11.1.0",
|
61
|
+
"eslint-plugin-security": "^1.5.0",
|
62
|
+
"eslint-plugin-unicorn": "^43.0.1",
|
63
|
+
"mocha": "^10.0.0",
|
64
|
+
"npm-run-all": "^4.1.5",
|
65
|
+
"prettier": "^2.7.1",
|
66
|
+
"typescript": "^4.7.4"
|
67
|
+
},
|
68
|
+
"engines": {
|
69
|
+
"node": "^12.20||^14||>=16"
|
70
|
+
},
|
25
71
|
"scripts": {
|
72
|
+
"build": "npm-run-all --sequential build:version build:dist",
|
73
|
+
"build:version": "node tools/get-version.mjs > src/version.mjs",
|
74
|
+
"build:dist": "esbuild src/main.mjs --format=cjs --target=node12 --platform=node --bundle --global-name=wkbtcjs --minify --outfile=dist/cjs-bundle.js",
|
75
|
+
"check": "npm-run-all --parallel --aggregate-output lint depcruise test:cover",
|
76
|
+
"clean": "rm -rf dist",
|
26
77
|
"test": "mocha \"src/**/*.spec.mjs\"",
|
27
78
|
"test:cover": "c8 --check-coverage --statements 100 --branches 100 --functions 100 --lines 100 --exclude \"**/*.spec.mjs\" --reporter text-summary --reporter html --reporter json-summary npm test",
|
28
79
|
"depcruise": "depcruise src --config",
|
29
|
-
"depcruise:graph": "depcruise src --include-only '^(src)' --config --output-type dot | dot -T svg | depcruise-wrap-stream-in-html > docs/dependency-graph.html",
|
80
|
+
"depcruise:graph": "depcruise src --include-only '^(src)' --config --output-type dot | dot -T svg | tee docs/dependency-graph.svg | depcruise-wrap-stream-in-html > docs/dependency-graph.html",
|
30
81
|
"depcruise:graph:archi": "depcruise src --include-only '^(src)' --config --output-type archi | dot -T svg | depcruise-wrap-stream-in-html > docs/high-level-dependency-graph.html",
|
31
82
|
"depcruise:graph:dev": "depcruise src --include-only '^(src)' --prefix vscode://file/$(pwd)/ --config --output-type dot | dot -T svg | depcruise-wrap-stream-in-html | browser",
|
32
83
|
"depcruise:graph:diff:dev": "depcruise src --include-only '^(src)' --focus \"$(node src/cli.mjs main -T regex)\" --prefix vscode://file/$(pwd)/ --config --output-type dot | dot -T svg | depcruise-wrap-stream-in-html | browser",
|
33
84
|
"depcruise:graph:diff:mermaid": "depcruise src --include-only '^(src)' --config --output-type mermaid --output-to - --focus \"$(node src/cli.mjs $SHA -T regex)\"",
|
34
85
|
"depcruise:html": "depcruise src --progress --config --output-type err-html --output-to dependency-violation-report.html",
|
35
86
|
"depcruise:text": "depcruise src --progress --config --output-type text",
|
36
|
-
"depcruise:focus": "depcruise src --progress --config --output-type text --focus"
|
87
|
+
"depcruise:focus": "depcruise src --progress --config --output-type text --focus",
|
88
|
+
"lint": "npm-run-all --parallel --aggregate-output lint:format lint:eslint",
|
89
|
+
"lint:fix": "npm-run-all --parallel --aggregate-output lint:format:fix lint:eslint:fix",
|
90
|
+
"lint:eslint": "eslint src --cache --cache-location node_modules/.cache/eslint/",
|
91
|
+
"lint:eslint:fix": "eslint src tools --fix --cache --cache-location node_modules/.cache/eslint/",
|
92
|
+
"lint:format": "prettier --check \"{src,tools}/**/*.mjs\" \"types/**/*.ts\" \"*.{json,yml,md,js}\"",
|
93
|
+
"lint:format:fix": "prettier --loglevel warn --write \"{src,tools}/**/*.mjs\" \"types/**/*.ts\" \"*.{json,yml,md,js}\"",
|
94
|
+
"scm:stage": "git add .",
|
95
|
+
"version": "npm-run-all --sequential clean build lint depcruise:graph scm:stage"
|
37
96
|
},
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
"devDependencies": {
|
45
|
-
"c8": "^7.11.3",
|
46
|
-
"dependency-cruiser": "^11.11.0",
|
47
|
-
"mocha": "^10.0.0"
|
48
|
-
},
|
49
|
-
"dependencies": {
|
50
|
-
"commander": "^9.3.0"
|
51
|
-
}
|
97
|
+
"eslintIgnore": [
|
98
|
+
"coverage",
|
99
|
+
"docs",
|
100
|
+
"dist",
|
101
|
+
"node_modules"
|
102
|
+
]
|
52
103
|
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
{
|
2
|
+
"root": true,
|
3
|
+
"extends": ["moving-meadow"],
|
4
|
+
"overrides": [
|
5
|
+
{
|
6
|
+
"files": ["**/*.mjs"],
|
7
|
+
"rules": {
|
8
|
+
"unicorn/no-null": "off",
|
9
|
+
"unicorn/prefer-spread": "off",
|
10
|
+
"node/no-unsupported-features/es-syntax": "off",
|
11
|
+
"import/no-relative-parent-imports": "off",
|
12
|
+
"sort-imports": "off",
|
13
|
+
"unicorn/prefer-node-protocol": "error",
|
14
|
+
"unicorn/prefer-module": "error"
|
15
|
+
},
|
16
|
+
"parserOptions": {
|
17
|
+
"ecmaVersion": "latest"
|
18
|
+
}
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"files": ["**/*.spec.mjs"],
|
22
|
+
"env": {
|
23
|
+
"mocha": true
|
24
|
+
},
|
25
|
+
"rules": {
|
26
|
+
"no-magic-numbers": "off",
|
27
|
+
"security/detect-non-literal-require": "off",
|
28
|
+
"security/detect-non-literal-fs-filename": "off",
|
29
|
+
"max-lines-per-function": "off",
|
30
|
+
"max-lines": "off",
|
31
|
+
"no-null": "off"
|
32
|
+
}
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"files": ["types/*.ts"],
|
36
|
+
"plugins": ["@typescript-eslint"],
|
37
|
+
"extends": ["plugin:@typescript-eslint/recommended"],
|
38
|
+
"parser": "@typescript-eslint/parser",
|
39
|
+
"parserOptions": {
|
40
|
+
"ecmaVersion": "latest"
|
41
|
+
},
|
42
|
+
"rules": {
|
43
|
+
"node/no-unsupported-features/es-syntax": "off"
|
44
|
+
}
|
45
|
+
}
|
46
|
+
]
|
47
|
+
}
|
package/src/cli.mjs
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
+
/* eslint-disable no-console */
|
2
3
|
|
3
4
|
import { program } from "commander";
|
4
|
-
import {
|
5
|
+
import { list } from "./main.mjs";
|
6
|
+
import { VERSION } from "./version.mjs";
|
5
7
|
|
6
8
|
program
|
7
|
-
.description(
|
9
|
+
.description(
|
10
|
+
"lists files & their statuses since [revision].\n\n" +
|
11
|
+
"-> When you don't pass a revision the revision defaults to the current one."
|
12
|
+
)
|
13
|
+
.version(VERSION)
|
8
14
|
.option("-T, --output-type <type>", "json,regex", "regex")
|
9
15
|
.option("--tracked-only", "only take tracked files into account", false)
|
10
|
-
.arguments("
|
16
|
+
.arguments("[revision]")
|
11
17
|
.parse(process.argv);
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
console.error(pError);
|
18
|
-
}
|
19
|
-
} else {
|
20
|
-
program.help();
|
19
|
+
try {
|
20
|
+
console.log(list(program.args[0], program.opts()));
|
21
|
+
} catch (pError) {
|
22
|
+
console.error(`ERROR: ${pError.message}`);
|
21
23
|
}
|
@@ -1,10 +1,13 @@
|
|
1
|
+
// the security (and unicorn) plugins don't seem to detect named caption
|
2
|
+
// groups very well - false-flagging below regular expressions to be susceptible
|
3
|
+
// to redos attacks.
|
4
|
+
/* eslint-disable unicorn/no-unsafe-regex, security/detect-unsafe-regex */
|
1
5
|
import { EOL } from "node:os";
|
2
|
-
|
3
|
-
|
4
|
-
)
|
5
|
-
const DIFF_SHORT_STATUS_LINE_PATTERN =
|
6
|
-
|
7
|
-
);
|
6
|
+
|
7
|
+
const DIFF_NAME_STATUS_LINE_PATTERN =
|
8
|
+
/^(?<changeType>[ACDMRTUXB])(?<similarity>[0-9]{3})?[ \t]+(?<name>[^ \t]+)[ \t]*(?<newName>[^ \t]+)?$/;
|
9
|
+
const DIFF_SHORT_STATUS_LINE_PATTERN =
|
10
|
+
/^(?<stagedChangeType>[ ACDMRTUXB?!])(?<unStagedChangeType>[ ACDMRTUXB?!])[ \t]+(?<name>[^ \t]+)(( -> )(?<newName>[^ \t]+))?$/;
|
8
11
|
|
9
12
|
const CHANGE_CHAR_2_CHANGE_TYPE = {
|
10
13
|
A: "added",
|
@@ -22,6 +25,7 @@ const CHANGE_CHAR_2_CHANGE_TYPE = {
|
|
22
25
|
};
|
23
26
|
|
24
27
|
function changeChar2ChangeType(pChar) {
|
28
|
+
// eslint-disable-next-line security/detect-object-injection
|
25
29
|
return CHANGE_CHAR_2_CHANGE_TYPE[pChar] || "unknown";
|
26
30
|
}
|
27
31
|
|
@@ -72,12 +76,6 @@ export function convertDiffLine(pString) {
|
|
72
76
|
lReturnValue.changeType = changeChar2ChangeType(
|
73
77
|
lMatchResult.groups.changeType
|
74
78
|
);
|
75
|
-
if (lMatchResult.groups.similarity) {
|
76
|
-
lReturnValue.similarity = Number.parseInt(
|
77
|
-
lMatchResult.groups.similarity,
|
78
|
-
10
|
79
|
-
);
|
80
|
-
}
|
81
79
|
if (lMatchResult.groups.newName) {
|
82
80
|
lReturnValue.name = lMatchResult.groups.newName;
|
83
81
|
lReturnValue.oldName = lMatchResult.groups.name;
|
package/src/formatters/json.mjs
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
const INDENT = 2;
|
1
2
|
/**
|
2
3
|
*
|
3
4
|
* @param {import('../types/watskeburt').IChange[]} pChanges
|
4
5
|
* @return {string}
|
5
6
|
*/
|
6
7
|
export default function formatToJSON(pChanges) {
|
7
|
-
return JSON.stringify(pChanges, null,
|
8
|
+
return JSON.stringify(pChanges, null, INDENT);
|
8
9
|
}
|
package/src/formatters/regex.mjs
CHANGED
@@ -9,13 +9,24 @@ import { extname } from "node:path";
|
|
9
9
|
*/
|
10
10
|
export default function formatToRegex(
|
11
11
|
pChanges,
|
12
|
-
pExtensions = [
|
12
|
+
pExtensions = [
|
13
|
+
".js",
|
14
|
+
".jsx",
|
15
|
+
".mjs",
|
16
|
+
".cjs",
|
17
|
+
".ts",
|
18
|
+
".tsx",
|
19
|
+
".vue",
|
20
|
+
".vuex",
|
21
|
+
".json",
|
22
|
+
],
|
13
23
|
pChangeTypes = ["modified", "added", "renamed", "copied", "untracked"]
|
14
24
|
) {
|
15
25
|
const lChanges = pChanges
|
16
26
|
.filter((pChange) => pChangeTypes.includes(pChange.changeType))
|
17
27
|
.map(
|
18
|
-
|
28
|
+
// .replace(/\./g, "\\\\.")
|
29
|
+
({ name }) => name
|
19
30
|
)
|
20
31
|
.filter((pName) => pExtensions.includes(extname(pName)))
|
21
32
|
.join("|");
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
2
|
+
|
3
|
+
function stringifyOutStream(pError) {
|
4
|
+
if (pError instanceof Buffer) {
|
5
|
+
return pError.toString("utf8");
|
6
|
+
} else {
|
7
|
+
return pError;
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
function throwSpawnError(pError) {
|
12
|
+
if (pError.code === "ENOENT") {
|
13
|
+
throw new Error("git executable not found");
|
14
|
+
} else {
|
15
|
+
throw new Error(`internal spawn error: ${pError}`);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
*
|
21
|
+
* @param {string[]} pArguments
|
22
|
+
* @return {string}
|
23
|
+
* @throws {Error}
|
24
|
+
*/
|
25
|
+
function getGitResult(pArguments, pErrorMap, pSpawnFunction) {
|
26
|
+
const lGitResult = pSpawnFunction("git", pArguments, {
|
27
|
+
cwd: process.cwd(),
|
28
|
+
// eslint-disable-next-line node/no-process-env
|
29
|
+
env: process.env,
|
30
|
+
});
|
31
|
+
|
32
|
+
if (lGitResult.error) {
|
33
|
+
throwSpawnError(lGitResult.error);
|
34
|
+
}
|
35
|
+
|
36
|
+
if (lGitResult.status === 0) {
|
37
|
+
return stringifyOutStream(lGitResult.stdout);
|
38
|
+
} else {
|
39
|
+
throw new Error(
|
40
|
+
pErrorMap[lGitResult.status] ||
|
41
|
+
`internal git error: ${lGitResult.status} (${stringifyOutStream(
|
42
|
+
lGitResult.stderr
|
43
|
+
)})`
|
44
|
+
);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
*
|
50
|
+
* @returns {string}
|
51
|
+
* @throws {Error}
|
52
|
+
*/
|
53
|
+
export function getStatusShort(pSpawnFunction = spawnSync) {
|
54
|
+
const lErrorMap = {
|
55
|
+
129: `'${process.cwd()}' does not seem to be a git repository`,
|
56
|
+
};
|
57
|
+
return getGitResult(["status", "--porcelain"], lErrorMap, pSpawnFunction);
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
*
|
62
|
+
* @param {string} pOldRevision the target to compare against (e.g. branch name, commit, tag etc)
|
63
|
+
* @return {string}
|
64
|
+
* @throws {Error}
|
65
|
+
*/
|
66
|
+
export function getDiffLines(pOldRevision, pSpawnFunction = spawnSync) {
|
67
|
+
const lErrorMap = {
|
68
|
+
128: `revision '${pOldRevision}' unknown `,
|
69
|
+
129: `'${process.cwd()}' does not seem to be a git repository`,
|
70
|
+
};
|
71
|
+
return getGitResult(
|
72
|
+
["diff", pOldRevision, "--name-status"],
|
73
|
+
lErrorMap,
|
74
|
+
pSpawnFunction
|
75
|
+
);
|
76
|
+
}
|
77
|
+
/**
|
78
|
+
*
|
79
|
+
* @returns {string}
|
80
|
+
*/
|
81
|
+
export function getSHA1(pSpawnFunction = spawnSync) {
|
82
|
+
const lSha1Length = 40;
|
83
|
+
|
84
|
+
return getGitResult(["rev-parse", "HEAD"], {}, pSpawnFunction).slice(
|
85
|
+
0,
|
86
|
+
lSha1Length
|
87
|
+
);
|
88
|
+
}
|
package/src/main.mjs
CHANGED
@@ -2,31 +2,26 @@ import {
|
|
2
2
|
convertDiffLines,
|
3
3
|
convertStatusLines,
|
4
4
|
} from "./convert-to-change-object.mjs";
|
5
|
-
import { getDiffLines, getStatusShort } from "./
|
5
|
+
import { getDiffLines, getSHA1, getStatusShort } from "./git-primitives.mjs";
|
6
6
|
import format from "./formatters/format.mjs";
|
7
7
|
|
8
|
-
/**
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
* @param {import("../types/watskeburt.js").IOptions} pOptions
|
19
|
-
* @returns {string|import("../types/watskeburt.js").IChange[]}
|
20
|
-
*/
|
21
|
-
export function convert(pOldThing, pOptions) {
|
22
|
-
let lChanges = convertDiffLines(getDiffLines(pOldThing));
|
8
|
+
/** @type {import("../types/watskeburt.js").getSHA} */
|
9
|
+
export function getSHA() {
|
10
|
+
return getSHA1();
|
11
|
+
}
|
12
|
+
|
13
|
+
/** @type {import("../types/watskeburt.js").list} */
|
14
|
+
export function list(pOldRevision, pOptions) {
|
15
|
+
const lOldRevision = pOldRevision || getSHA();
|
16
|
+
let lChanges = convertDiffLines(getDiffLines(lOldRevision));
|
17
|
+
const lOptions = pOptions || {};
|
23
18
|
|
24
|
-
if (!
|
19
|
+
if (!lOptions.trackedOnly) {
|
25
20
|
lChanges = lChanges.concat(
|
26
21
|
convertStatusLines(getStatusShort()).filter(
|
27
22
|
({ changeType }) => changeType === "untracked"
|
28
23
|
)
|
29
24
|
);
|
30
25
|
}
|
31
|
-
return format(lChanges,
|
26
|
+
return format(lChanges, lOptions.outputType);
|
32
27
|
}
|
package/src/version.mjs
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export const VERSION = "0.4.0";
|
package/types/watskeburt.d.ts
CHANGED
@@ -13,15 +13,55 @@ export type changeTypeType =
|
|
13
13
|
| "ignored";
|
14
14
|
|
15
15
|
export interface IChange {
|
16
|
+
/**
|
17
|
+
* name of the file
|
18
|
+
*/
|
16
19
|
name: string;
|
20
|
+
/**
|
21
|
+
* how the file was changed
|
22
|
+
*/
|
17
23
|
changeType: changeTypeType;
|
18
|
-
|
24
|
+
/**
|
25
|
+
* if the file was renamed: what the old file's name was
|
26
|
+
*/
|
19
27
|
oldName?: string;
|
20
28
|
}
|
21
29
|
|
22
30
|
export type outputTypeType = "regex" | "json" | "object";
|
23
31
|
|
24
32
|
export interface IOptions {
|
33
|
+
/**
|
34
|
+
* The type of output to deliver. Defaults to "object" - in which case
|
35
|
+
* the list function returns an IChange[] object
|
36
|
+
*/
|
25
37
|
outputType: outputTypeType;
|
38
|
+
/**
|
39
|
+
* When true _only_ takes already tracked files into account.
|
40
|
+
* When true also takes untracked files into account.
|
41
|
+
*
|
42
|
+
* Defaults to true.
|
43
|
+
*/
|
26
44
|
trackedOnly: boolean;
|
27
45
|
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* returns a list of files changed since pOldRevision.
|
49
|
+
*
|
50
|
+
* @param pOldRevision the revision against which to compare. E.g. a commit-hash,
|
51
|
+
* a branch or a tag. When not passed defaults to the _current_
|
52
|
+
* commit hash (if there's any)
|
53
|
+
* @param pOptions Options that influence how the changes are returned and that
|
54
|
+
* filter what is returned and
|
55
|
+
* @throws {Error}
|
56
|
+
*/
|
57
|
+
export function list(
|
58
|
+
pOldRevision?: string,
|
59
|
+
pOptions?: IOptions
|
60
|
+
): IChange[] | string;
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Returns the SHA1 of the current HEAD
|
64
|
+
*
|
65
|
+
* @throws {Error}
|
66
|
+
*/
|
67
|
+
export function getSHA(): string;
|
package/src/get-diff-lines.mjs
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
2
|
-
|
3
|
-
function stringifyOutStream(pError) {
|
4
|
-
if (pError instanceof Buffer) {
|
5
|
-
return pError.toString("utf8");
|
6
|
-
} else {
|
7
|
-
return pError;
|
8
|
-
}
|
9
|
-
}
|
10
|
-
|
11
|
-
/**
|
12
|
-
*
|
13
|
-
* @param {string[]} pArguments
|
14
|
-
* @return {string}
|
15
|
-
* @throws {Error}
|
16
|
-
*/
|
17
|
-
function getGitResult(pArguments, pSpawnFunction) {
|
18
|
-
const lGitResult = pSpawnFunction("git", pArguments, {
|
19
|
-
cwd: process.cwd(),
|
20
|
-
env: process.env,
|
21
|
-
});
|
22
|
-
|
23
|
-
if (lGitResult.error) {
|
24
|
-
throw new Error(stringifyOutStream(lGitResult.error));
|
25
|
-
}
|
26
|
-
|
27
|
-
if (lGitResult.status === 0) {
|
28
|
-
return stringifyOutStream(lGitResult.stdout);
|
29
|
-
} else {
|
30
|
-
throw new Error(lGitResult.output);
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
/**
|
35
|
-
*
|
36
|
-
* @returns {string}
|
37
|
-
* @throws {Error}
|
38
|
-
*/
|
39
|
-
export function getStatusShort(pSpawnFunction = spawnSync) {
|
40
|
-
return getGitResult(["status", "--porcelain"], pSpawnFunction);
|
41
|
-
}
|
42
|
-
|
43
|
-
/**
|
44
|
-
*
|
45
|
-
* @param {string} pOldThing the target to compare against (e.g. branch name, commit, tag etc)
|
46
|
-
* @return {string}
|
47
|
-
* @throws {Error}
|
48
|
-
*/
|
49
|
-
export function getDiffLines(pOldThing, pSpawnFunction = spawnSync) {
|
50
|
-
return getGitResult(["diff", pOldThing, "--name-status"], pSpawnFunction);
|
51
|
-
}
|