watskeburt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/package.json +52 -0
- package/src/cli.mjs +21 -0
- package/src/convert-to-change-object.mjs +115 -0
- package/src/formatters/format.mjs +19 -0
- package/src/formatters/json.mjs +8 -0
- package/src/formatters/regex.mjs +23 -0
- package/src/get-diff-lines.mjs +51 -0
- package/src/main.mjs +32 -0
- package/types/watskeburt.d.ts +27 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016-2022 Sander Verweij
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# watskeburt
|
2
|
+
|
3
|
+
get git changed files & their statusses since _any reference_
|
4
|
+
|
5
|
+
## what's this do?
|
6
|
+
|
7
|
+
A micro-lib to retrieve an array of file names that were changed (added,
|
8
|
+
modified, renamed, deleted, ...) since the reference it got passed.
|
9
|
+
|
10
|
+
- :warning: NOT yet published on npm
|
11
|
+
- :warning: in the process of getting 'production ready'. It's automatically
|
12
|
+
tested + it's using itself + interface is stable - but a bunch of static
|
13
|
+
analysis still needs be added, as well as a commonjs version.
|
14
|
+
- :warning: currently esm only - commonjs compiledown will follow as part of the
|
15
|
+
previous point.
|
16
|
+
|
17
|
+
## sample return value
|
18
|
+
|
19
|
+
```json
|
20
|
+
[
|
21
|
+
{ "name": "doc/cli.md", "changeType": "modified" },
|
22
|
+
{
|
23
|
+
"name": "test/report/mermaid/mermaid.spec.mjs",
|
24
|
+
"changeType": "renamed",
|
25
|
+
"oldName": "test/configs/plugins/mermaid-reporter-plugin/module-level/index.spec.mjs",
|
26
|
+
"similarity": 66
|
27
|
+
},
|
28
|
+
{ "name": "src/not-tracked-yet.mjs", "changeType": "untracked" }
|
29
|
+
// ...
|
30
|
+
]
|
31
|
+
```
|
32
|
+
|
33
|
+
## cli
|
34
|
+
|
35
|
+
For now there's also a simple command line interface
|
36
|
+
|
37
|
+
```
|
38
|
+
Usage: watskeburt [options] <reference>
|
39
|
+
|
40
|
+
lists files since <reference>
|
41
|
+
|
42
|
+
Options:
|
43
|
+
-T, --output-type <type> json,regex (default: "regex")
|
44
|
+
--tracked-only only take tracked files into account (default: false)
|
45
|
+
-h, --help display help for command
|
46
|
+
|
47
|
+
```
|
package/package.json
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
{
|
2
|
+
"name": "watskeburt",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"description": "list files changed since a git reference",
|
5
|
+
"bin": "src/cli.mjs",
|
6
|
+
"main": "src/main.mjs",
|
7
|
+
"module": "src/main.mjs",
|
8
|
+
"exports": {
|
9
|
+
".": [
|
10
|
+
{
|
11
|
+
"import": "./src/main.mjs"
|
12
|
+
}
|
13
|
+
]
|
14
|
+
},
|
15
|
+
"types": "types/watskeburt.d.ts",
|
16
|
+
"files": [
|
17
|
+
"src",
|
18
|
+
"!src/**/*.spec.mjs",
|
19
|
+
"!**/*.DS_Store",
|
20
|
+
"types",
|
21
|
+
"LICENSE",
|
22
|
+
"package.json",
|
23
|
+
"README.md"
|
24
|
+
],
|
25
|
+
"scripts": {
|
26
|
+
"test": "mocha \"src/**/*.spec.mjs\"",
|
27
|
+
"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
|
+
"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",
|
30
|
+
"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
|
+
"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
|
+
"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
|
+
"depcruise:graph:diff:mermaid": "depcruise src --include-only '^(src)' --config --output-type mermaid --output-to - --focus \"$(node src/cli.mjs $SHA -T regex)\"",
|
34
|
+
"depcruise:html": "depcruise src --progress --config --output-type err-html --output-to dependency-violation-report.html",
|
35
|
+
"depcruise:text": "depcruise src --progress --config --output-type text",
|
36
|
+
"depcruise:focus": "depcruise src --progress --config --output-type text --focus"
|
37
|
+
},
|
38
|
+
"keywords": [
|
39
|
+
"git",
|
40
|
+
"diff"
|
41
|
+
],
|
42
|
+
"author": "Sander Verweij (https://sverweij.github.io/)",
|
43
|
+
"license": "MIT",
|
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
|
+
}
|
52
|
+
}
|
package/src/cli.mjs
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import { program } from "commander";
|
4
|
+
import { convert } from "./main.mjs";
|
5
|
+
|
6
|
+
program
|
7
|
+
.description("lists files since <reference>")
|
8
|
+
.option("-T, --output-type <type>", "json,regex", "regex")
|
9
|
+
.option("--tracked-only", "only take tracked files into account", false)
|
10
|
+
.arguments("<reference>")
|
11
|
+
.parse(process.argv);
|
12
|
+
|
13
|
+
if (program.args[0]) {
|
14
|
+
try {
|
15
|
+
console.log(convert(program.args[0], program.opts()));
|
16
|
+
} catch (pError) {
|
17
|
+
console.error(pError);
|
18
|
+
}
|
19
|
+
} else {
|
20
|
+
program.help();
|
21
|
+
}
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import { EOL } from "node:os";
|
2
|
+
const DIFF_NAME_STATUS_LINE_PATTERN = new RegExp(
|
3
|
+
"^(?<changeType>[ACDMRTUXB])(?<similarity>[0-9]{3})?[ \t]+(?<name>[^ \t]+)[ \t]*(?<newName>[^ \t]+)?$"
|
4
|
+
);
|
5
|
+
const DIFF_SHORT_STATUS_LINE_PATTERN = new RegExp(
|
6
|
+
"^(?<stagedChangeType>[ ACDMRTUXB?!])(?<unStagedChangeType>[ ACDMRTUXB?!])[ \t]+(?<name>[^ \t]+)(( -> )(?<newName>[^ \t]+))?$"
|
7
|
+
);
|
8
|
+
|
9
|
+
const CHANGE_CHAR_2_CHANGE_TYPE = {
|
10
|
+
A: "added",
|
11
|
+
C: "copied",
|
12
|
+
D: "deleted",
|
13
|
+
M: "modified",
|
14
|
+
R: "renamed",
|
15
|
+
T: "type changed",
|
16
|
+
U: "unmerged",
|
17
|
+
B: "pairing broken",
|
18
|
+
" ": "unmodified",
|
19
|
+
"?": "untracked",
|
20
|
+
"!": "ignored",
|
21
|
+
// X: "unknown"
|
22
|
+
};
|
23
|
+
|
24
|
+
function changeChar2ChangeType(pChar) {
|
25
|
+
return CHANGE_CHAR_2_CHANGE_TYPE[pChar] || "unknown";
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
*
|
30
|
+
* @param {string} pString
|
31
|
+
* @returns {import('../types/watskeburt').IChange}
|
32
|
+
*/
|
33
|
+
export function convertStatusLine(pString) {
|
34
|
+
const lMatchResult = pString.match(DIFF_SHORT_STATUS_LINE_PATTERN);
|
35
|
+
/** @type {import('../types/watskeburt').IChange} */
|
36
|
+
let lReturnValue = {};
|
37
|
+
|
38
|
+
if (lMatchResult) {
|
39
|
+
const lStagedChangeType = changeChar2ChangeType(
|
40
|
+
lMatchResult.groups.stagedChangeType
|
41
|
+
);
|
42
|
+
const lUnStagedChangeType = changeChar2ChangeType(
|
43
|
+
lMatchResult.groups.unStagedChangeType
|
44
|
+
);
|
45
|
+
|
46
|
+
lReturnValue.changeType =
|
47
|
+
lStagedChangeType === "unmodified"
|
48
|
+
? lUnStagedChangeType
|
49
|
+
: lStagedChangeType;
|
50
|
+
|
51
|
+
if (lMatchResult.groups.newName) {
|
52
|
+
lReturnValue.name = lMatchResult.groups.newName;
|
53
|
+
lReturnValue.oldName = lMatchResult.groups.name;
|
54
|
+
} else {
|
55
|
+
lReturnValue.name = lMatchResult.groups.name;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
return lReturnValue;
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
*
|
63
|
+
* @param {string} pString
|
64
|
+
* @returns {import('../types/watskeburt').IChange}
|
65
|
+
*/
|
66
|
+
export function convertDiffLine(pString) {
|
67
|
+
const lMatchResult = pString.match(DIFF_NAME_STATUS_LINE_PATTERN);
|
68
|
+
/** @type {import('../types/watskeburt').IChange} */
|
69
|
+
let lReturnValue = {};
|
70
|
+
|
71
|
+
if (lMatchResult) {
|
72
|
+
lReturnValue.changeType = changeChar2ChangeType(
|
73
|
+
lMatchResult.groups.changeType
|
74
|
+
);
|
75
|
+
if (lMatchResult.groups.similarity) {
|
76
|
+
lReturnValue.similarity = Number.parseInt(
|
77
|
+
lMatchResult.groups.similarity,
|
78
|
+
10
|
79
|
+
);
|
80
|
+
}
|
81
|
+
if (lMatchResult.groups.newName) {
|
82
|
+
lReturnValue.name = lMatchResult.groups.newName;
|
83
|
+
lReturnValue.oldName = lMatchResult.groups.name;
|
84
|
+
} else {
|
85
|
+
lReturnValue.name = lMatchResult.groups.name;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
return lReturnValue;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
*
|
93
|
+
* @param {string} pString
|
94
|
+
* @returns {import('../types/watskeburt').IChange[]}
|
95
|
+
*/
|
96
|
+
export function convertStatusLines(pString) {
|
97
|
+
return pString
|
98
|
+
.split(EOL)
|
99
|
+
.filter(Boolean)
|
100
|
+
.map(convertStatusLine)
|
101
|
+
.filter(({ changeType }) => Boolean(changeType));
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
*
|
106
|
+
* @param {string} pString
|
107
|
+
* @returns {import('../types/watskeburt').IChange[]}
|
108
|
+
*/
|
109
|
+
export function convertDiffLines(pString) {
|
110
|
+
return pString
|
111
|
+
.split(EOL)
|
112
|
+
.filter(Boolean)
|
113
|
+
.map(convertDiffLine)
|
114
|
+
.filter(({ changeType }) => Boolean(changeType));
|
115
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import formatToRegex from "./regex.mjs";
|
2
|
+
import formatToJSON from "./json.mjs";
|
3
|
+
|
4
|
+
const OUTPUT_TYPE_TO_FUNCTION = {
|
5
|
+
regex: formatToRegex,
|
6
|
+
json: formatToJSON,
|
7
|
+
object: (x) => x,
|
8
|
+
};
|
9
|
+
const DEFAULT_OUTPUT_TYPE = "object";
|
10
|
+
|
11
|
+
/**
|
12
|
+
*
|
13
|
+
* @param {import("../../types/watskeburt.js").IChange[]} pChanges
|
14
|
+
* @param {import("../../types/watskeburt.js").outputTypeType} pOutputType
|
15
|
+
* @returns {string|import("../../types/watskeburt.js").IChange[]}
|
16
|
+
*/
|
17
|
+
export default function format(pChanges, pOutputType) {
|
18
|
+
return OUTPUT_TYPE_TO_FUNCTION[pOutputType || DEFAULT_OUTPUT_TYPE](pChanges);
|
19
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { extname } from "node:path";
|
2
|
+
|
3
|
+
/**
|
4
|
+
*
|
5
|
+
* @param {import('../types/watskeburt').IChange[]} pChanges
|
6
|
+
* @param {string[]} pExtensions
|
7
|
+
* @param {import('../types/watskeburt').changeTypeType[]} pChangeTypes
|
8
|
+
* @return {string}
|
9
|
+
*/
|
10
|
+
export default function formatToRegex(
|
11
|
+
pChanges,
|
12
|
+
pExtensions = [".js", ".ts", ".mjs", ".cjs"],
|
13
|
+
pChangeTypes = ["modified", "added", "renamed", "copied", "untracked"]
|
14
|
+
) {
|
15
|
+
const lChanges = pChanges
|
16
|
+
.filter((pChange) => pChangeTypes.includes(pChange.changeType))
|
17
|
+
.map(
|
18
|
+
({ name }) => name //.replace(/\./g, "\\\\.")
|
19
|
+
)
|
20
|
+
.filter((pName) => pExtensions.includes(extname(pName)))
|
21
|
+
.join("|");
|
22
|
+
return `^(${lChanges})$`;
|
23
|
+
}
|
@@ -0,0 +1,51 @@
|
|
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
|
+
}
|
package/src/main.mjs
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
import {
|
2
|
+
convertDiffLines,
|
3
|
+
convertStatusLines,
|
4
|
+
} from "./convert-to-change-object.mjs";
|
5
|
+
import { getDiffLines, getStatusShort } from "./get-diff-lines.mjs";
|
6
|
+
import format from "./formatters/format.mjs";
|
7
|
+
|
8
|
+
/**
|
9
|
+
*
|
10
|
+
* returns a list of files changed since pOldThing. With pOptions
|
11
|
+
* you can
|
12
|
+
* - influence whether you want to have the output as an array of
|
13
|
+
* IChange-s or one of the other output formats (outputType)
|
14
|
+
* - tell whether you want to take untracked files into account as
|
15
|
+
* well (by setting trackedOnly to false)
|
16
|
+
*
|
17
|
+
* @param {string} pOldThing reference to a commit, branch, tag, ...
|
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));
|
23
|
+
|
24
|
+
if (!pOptions.trackedOnly) {
|
25
|
+
lChanges = lChanges.concat(
|
26
|
+
convertStatusLines(getStatusShort()).filter(
|
27
|
+
({ changeType }) => changeType === "untracked"
|
28
|
+
)
|
29
|
+
);
|
30
|
+
}
|
31
|
+
return format(lChanges, pOptions.outputType);
|
32
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
export type changeTypeType =
|
2
|
+
| "added"
|
3
|
+
| "copied"
|
4
|
+
| "deleted"
|
5
|
+
| "modified"
|
6
|
+
| "renamed"
|
7
|
+
| "type changed"
|
8
|
+
| "unmerged"
|
9
|
+
| "pairing broken"
|
10
|
+
| "unknown"
|
11
|
+
| "unmodified"
|
12
|
+
| "untracked"
|
13
|
+
| "ignored";
|
14
|
+
|
15
|
+
export interface IChange {
|
16
|
+
name: string;
|
17
|
+
changeType: changeTypeType;
|
18
|
+
similarity?: Number;
|
19
|
+
oldName?: string;
|
20
|
+
}
|
21
|
+
|
22
|
+
export type outputTypeType = "regex" | "json" | "object";
|
23
|
+
|
24
|
+
export interface IOptions {
|
25
|
+
outputType: outputTypeType;
|
26
|
+
trackedOnly: boolean;
|
27
|
+
}
|