watskeburt 0.2.0 → 0.3.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 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
- }