watskeburt 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,45 +1,88 @@
1
1
  # watskeburt
2
2
 
3
- get git changed files & their statuses since _any reference_
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 reference it got passed.
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
11
  - :warning: in the process of getting 'production ready'. It's automatically
11
12
  tested + it's using itself - but a bunch of static analysis and a bit of
12
13
  automation still needs be added.
13
- - Interface is stable-ish, but can can change until 1.0.0 is published
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
14
16
 
15
- ## sample return value
17
+ ## why?
16
18
 
17
- ```json
18
- [
19
- { "name": "doc/cli.md", "changeType": "modified" },
20
- {
21
- "name": "test/report/mermaid/mermaid.spec.mjs",
22
- "changeType": "renamed",
23
- "oldName": "test/configs/plugins/mermaid-reporter-plugin/module-level/index.spec.mjs",
24
- "similarity": 66
25
- },
26
- { "name": "src/not-tracked-yet.mjs", "changeType": "untracked" }
27
- // ...
28
- ]
29
- ```
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
30
27
 
31
- ## cli
28
+ ### :shell: cli
32
29
 
33
30
  For now there's also a simple command line interface
34
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)$
35
36
  ```
36
- Usage: watskeburt [options] <reference>
37
37
 
38
- lists files since <reference>
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>
39
46
 
40
47
  Options:
41
- -T, --output-type <type> json,regex (default: "regex")
42
- --tracked-only only take tracked files into account (default: false)
43
- -h, --help display help for command
48
+ -V, --version output the version number
49
+ -T, --output-type <type> json,regex (default: "regex")
50
+ --tracked-only only take tracked files into account (default: false)
51
+ -h, --help display help for command
52
+
53
+ ```
44
54
 
55
+ ### :scroll: API
56
+
57
+ ```javascript
58
+ // const { list } = require('watskeburt'); // will work in commonjs contexts as well
59
+ import { list, getSHA } from "watskeburt";
60
+
61
+ // print the SHA1 of the current HEAD
62
+ console.log(getSHA());
63
+
64
+ // list all files that differ between 'main' and
65
+ /** @type {import('watskeburt').IChange[]} */
66
+ const lChangedFiles = list("main");
45
67
  ```
68
+
69
+ An array of changes looks something like this:
70
+
71
+ ```javascript
72
+ [
73
+ { name: "doc/cli.md", changeType: "modified" },
74
+ {
75
+ name: "test/thing.spec.mjs",
76
+ changeType: "renamed",
77
+ oldName: "test/old-thing.spec.mjs",
78
+ similarity: 66,
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".
@@ -1 +1 @@
1
- var a=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var R=(t,e)=>{for(var n in e)a(t,n,{get:e[n],enumerable:!0})},_=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of N(e))!C.call(t,r)&&r!==n&&a(t,r,{get:()=>e[r],enumerable:!(o=S(e,r))||o.enumerable});return t};var w=t=>_(a({},"__esModule",{value:!0}),t);var B={};R(B,{convert:()=>v});module.exports=w(B);var u=require("os"),x=new RegExp("^(?<changeType>[ACDMRTUXB])(?<similarity>[0-9]{3})?[ ]+(?<name>[^ ]+)[ ]*(?<newName>[^ ]+)?$"),E=new RegExp("^(?<stagedChangeType>[ ACDMRTUXB?!])(?<unStagedChangeType>[ ACDMRTUXB?!])[ ]+(?<name>[^ ]+)(( -> )(?<newName>[^ ]+))?$"),A={A:"added",C:"copied",D:"deleted",M:"modified",R:"renamed",T:"type changed",U:"unmerged",B:"pairing broken"," ":"unmodified","?":"untracked","!":"ignored"};function i(t){return A[t]||"unknown"}function U(t){let e=t.match(E),n={};if(e){let o=i(e.groups.stagedChangeType),r=i(e.groups.unStagedChangeType);n.changeType=o==="unmodified"?r:o,e.groups.newName?(n.name=e.groups.newName,n.oldName=e.groups.name):n.name=e.groups.name}return n}function D(t){let e=t.match(x),n={};return e&&(n.changeType=i(e.groups.changeType),e.groups.similarity&&(n.similarity=Number.parseInt(e.groups.similarity,10)),e.groups.newName?(n.name=e.groups.newName,n.oldName=e.groups.name):n.name=e.groups.name),n}function p(t){return t.split(u.EOL).filter(Boolean).map(U).filter(({changeType:e})=>Boolean(e))}function g(t){return t.split(u.EOL).filter(Boolean).map(D).filter(({changeType:e})=>Boolean(e))}var s=require("child_process");function l(t){return t instanceof Buffer?t.toString("utf8"):t}function d(t,e){let n=e("git",t,{cwd:process.cwd(),env:process.env});if(n.error)throw new Error(l(n.error));if(n.status===0)return l(n.stdout);throw new Error(n.output)}function T(t=s.spawnSync){return d(["status","--porcelain"],t)}function h(t,e=s.spawnSync){return d(["diff",t,"--name-status"],e)}var y=require("path");function f(t,e=[".js",".ts",".mjs",".cjs"],n=["modified","added","renamed","copied","untracked"]){return`^(${t.filter(r=>n.includes(r.changeType)).map(({name:r})=>r).filter(r=>e.includes((0,y.extname)(r))).join("|")})$`}function c(t){return JSON.stringify(t,null,2)}var L={regex:f,json:c,object:t=>t},O="object";function m(t,e){return L[e||O](t)}function v(t,e){let n=g(h(t)),o=e||{};return o.trackedOnly||(n=n.concat(p(T()).filter(({changeType:r})=>r==="untracked"))),m(n,o.outputType)}0&&(module.exports={convert});
1
+ var i=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var E=(e,t)=>{for(var n in t)i(e,n,{get:t[n],enumerable:!0})},C=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of w(t))!x.call(e,o)&&o!==n&&i(e,o,{get:()=>t[o],enumerable:!(r=y(t,o))||r.enumerable});return e};var A=e=>C(i({},"__esModule",{value:!0}),e);var M={};E(M,{getSHA:()=>B,list:()=>j});module.exports=A(M);var u=require("os"),_=/^(?<changeType>[ACDMRTUXB])(?<similarity>[0-9]{3})?[ \t]+(?<name>[^ \t]+)[ \t]*(?<newName>[^ \t]+)?$/,D=/^(?<stagedChangeType>[ ACDMRTUXB?!])(?<unStagedChangeType>[ ACDMRTUXB?!])[ \t]+(?<name>[^ \t]+)(( -> )(?<newName>[^ \t]+))?$/,R={A:"added",C:"copied",D:"deleted",M:"modified",R:"renamed",T:"type changed",U:"unmerged",B:"pairing broken"," ":"unmodified","?":"untracked","!":"ignored"};function s(e){return R[e]||"unknown"}function L(e){let t=e.match(D),n={};if(t){let r=s(t.groups.stagedChangeType),o=s(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 U(e){let t=e.match(_),n={};return t&&(n.changeType=s(t.groups.changeType),t.groups.similarity&&(n.similarity=Number.parseInt(t.groups.similarity,10)),t.groups.newName?(n.name=t.groups.newName,n.oldName=t.groups.name):n.name=t.groups.name),n}function l(e){return e.split(u.EOL).filter(Boolean).map(L).filter(({changeType:t})=>Boolean(t))}function g(e){return e.split(u.EOL).filter(Boolean).map(U).filter(({changeType:t})=>Boolean(t))}var a=require("child_process");function d(e){return e instanceof Buffer?e.toString("utf8"):e}function O(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&&O(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 v={regex:f,json:p,object:e=>e},$="object";function m(e,t){return v[t||$](e)}function j(e,t){let n=g(h(e)),r=t||{};return r.trackedOnly||(n=n.concat(l(T()).filter(({changeType:o})=>o==="untracked"))),m(n,r.outputType)}function B(){return S()}0&&(module.exports={getSHA,list});
package/package.json CHANGED
@@ -1,10 +1,28 @@
1
1
  {
2
2
  "name": "watskeburt",
3
- "version": "0.2.0",
4
- "description": "list files changed since a git reference",
3
+ "version": "0.3.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
23
  "main": "dist/cjs-bundle.js",
7
24
  "module": "src/main.mjs",
25
+ "sideEffects": false,
8
26
  "exports": {
9
27
  ".": [
10
28
  {
@@ -25,8 +43,34 @@
25
43
  "package.json",
26
44
  "README.md"
27
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
+ },
67
+ "engines": {
68
+ "node": "^12.20||^14||>=16"
69
+ },
28
70
  "scripts": {
29
- "build": "esbuild src/main.mjs --format=cjs --target=node12 --platform=node --bundle --global-name=wkbtcjs --minify --outfile=dist/cjs-bundle.js",
71
+ "build": "npm-run-all --sequential build:version build:dist",
72
+ "build:version": "node tools/get-version.mjs > src/version.mjs",
73
+ "build:dist": "esbuild src/main.mjs --format=cjs --target=node12 --platform=node --bundle --global-name=wkbtcjs --minify --outfile=dist/cjs-bundle.js",
30
74
  "clean": "rm -rf dist",
31
75
  "test": "mocha \"src/**/*.spec.mjs\"",
32
76
  "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",
@@ -39,23 +83,19 @@
39
83
  "depcruise:html": "depcruise src --progress --config --output-type err-html --output-to dependency-violation-report.html",
40
84
  "depcruise:text": "depcruise src --progress --config --output-type text",
41
85
  "depcruise:focus": "depcruise src --progress --config --output-type text --focus",
86
+ "lint": "npm-run-all --parallel --aggregate-output lint:format lint:eslint",
87
+ "lint:fix": "npm-run-all --parallel --aggregate-output lint:format:fix lint:eslint:fix",
88
+ "lint:eslint": "eslint src --cache --cache-location node_modules/.cache/eslint/",
89
+ "lint:eslint:fix": "eslint src tools --fix --cache --cache-location node_modules/.cache/eslint/",
90
+ "lint:format": "prettier --check \"{src,tools}/**/*.mjs\" \"types/**/*.ts\" \"*.{json,yml,md,js}\"",
91
+ "lint:format:fix": "prettier --loglevel warn --write \"{src,tools}/**/*.mjs\" \"types/**/*.ts\" \"*.{json,yml,md,js}\"",
42
92
  "scm:stage": "git add .",
43
- "version": "run-s clean build depcruise:graph scm:stage"
44
- },
45
- "keywords": [
46
- "git",
47
- "diff"
48
- ],
49
- "author": "Sander Verweij (https://sverweij.github.io/)",
50
- "license": "MIT",
51
- "devDependencies": {
52
- "c8": "^7.11.3",
53
- "dependency-cruiser": "^11.11.0",
54
- "esbuild": "^0.14.48",
55
- "mocha": "^10.0.0",
56
- "npm-run-all": "^4.1.5"
93
+ "version": "npm-run-all --sequential clean build lint depcruise:graph scm:stage"
57
94
  },
58
- "dependencies": {
59
- "commander": "^9.3.0"
60
- }
95
+ "eslintIgnore": [
96
+ "coverage",
97
+ "docs",
98
+ "dist",
99
+ "node_modules"
100
+ ]
61
101
  }
@@ -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,20 +1,23 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-disable no-console */
2
3
 
3
4
  import { program } from "commander";
4
- import { convert } from "./main.mjs";
5
+ import { list } from "./main.mjs";
6
+ import { VERSION } from "./version.mjs";
5
7
 
6
8
  program
7
- .description("lists files since <reference>")
9
+ .description("lists files & their statuses since <revision>")
10
+ .version(VERSION)
8
11
  .option("-T, --output-type <type>", "json,regex", "regex")
9
12
  .option("--tracked-only", "only take tracked files into account", false)
10
- .arguments("<reference>")
13
+ .arguments("<revision>")
11
14
  .parse(process.argv);
12
15
 
13
16
  if (program.args[0]) {
14
17
  try {
15
- console.log(convert(program.args[0], program.opts()));
18
+ console.log(list(program.args[0], program.opts()));
16
19
  } catch (pError) {
17
- console.error(pError);
20
+ console.error(`ERROR: ${pError.message}`);
18
21
  }
19
22
  } else {
20
23
  program.help();
@@ -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
- 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
- );
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
 
@@ -4,7 +4,7 @@ import formatToJSON from "./json.mjs";
4
4
  const OUTPUT_TYPE_TO_FUNCTION = {
5
5
  regex: formatToRegex,
6
6
  json: formatToJSON,
7
- object: (x) => x,
7
+ object: (pX) => pX,
8
8
  };
9
9
  const DEFAULT_OUTPUT_TYPE = "object";
10
10
 
@@ -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, 2);
8
+ return JSON.stringify(pChanges, null, INDENT);
8
9
  }
@@ -9,13 +9,24 @@ import { extname } from "node:path";
9
9
  */
10
10
  export default function formatToRegex(
11
11
  pChanges,
12
- pExtensions = [".js", ".ts", ".mjs", ".cjs"],
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
- ({ name }) => name //.replace(/\./g, "\\\\.")
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,24 +2,12 @@ import {
2
2
  convertDiffLines,
3
3
  convertStatusLines,
4
4
  } from "./convert-to-change-object.mjs";
5
- import { getDiffLines, getStatusShort } from "./get-diff-lines.mjs";
5
+ import { getDiffLines, getSHA1, getStatusShort } from "./git-primitives.mjs";
6
6
  import format from "./formatters/format.mjs";
7
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));
8
+ /** @type {import("../types/watskeburt.js").list} */
9
+ export function list(pOldRevision, pOptions) {
10
+ let lChanges = convertDiffLines(getDiffLines(pOldRevision));
23
11
  const lOptions = pOptions || {};
24
12
 
25
13
  if (!lOptions.trackedOnly) {
@@ -31,3 +19,8 @@ export function convert(pOldThing, pOptions) {
31
19
  }
32
20
  return format(lChanges, lOptions.outputType);
33
21
  }
22
+
23
+ /** @type {import("../types/watskeburt.js").getSHA} */
24
+ export function getSHA() {
25
+ return getSHA1();
26
+ }
@@ -0,0 +1 @@
1
+ export const VERSION = "0.3.0";
@@ -13,15 +13,58 @@ 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;
24
+ /**
25
+ * if the file was renamed: the % of similarity (range: 0 - 100)
26
+ */
18
27
  similarity?: Number;
28
+ /**
29
+ * if the file was renamed: what the old file's name was
30
+ */
19
31
  oldName?: string;
20
32
  }
21
33
 
22
34
  export type outputTypeType = "regex" | "json" | "object";
23
35
 
24
36
  export interface IOptions {
37
+ /**
38
+ * The type of output to deliver. Defaults to "object" - in which case
39
+ * the list function returns an IChange[] object
40
+ */
25
41
  outputType: outputTypeType;
42
+ /**
43
+ * When true _only_ takes already tracked files into account.
44
+ * When true also takes untracked files into account.
45
+ *
46
+ * Defaults to true.
47
+ */
26
48
  trackedOnly: boolean;
27
49
  }
50
+
51
+ /**
52
+ * returns a list of files changed since pOldRevision.
53
+ *
54
+ * @param pOldRevision the revision against which to compare. E.g. a commit-hash,
55
+ * a branch or a tag.
56
+ * @param pOptions Options that influence how the changes are returned and that
57
+ * filter what is returned and
58
+ * @throws {Error}
59
+ */
60
+ export function list(
61
+ pOldRevision: string,
62
+ pOptions: IOptions
63
+ ): IChange[] | string;
64
+
65
+ /**
66
+ * Returns the SHA1 of the current HEAD
67
+ *
68
+ * @throws {Error}
69
+ */
70
+ export function getSHA(): string;
@@ -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
- }