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 CHANGED
@@ -1,47 +1,88 @@
1
1
  # watskeburt
2
2
 
3
- get git changed files & their statusses 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
- - :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 + 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.
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
- ## sample return value
17
+ ## why?
18
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
- ```
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
- ## cli
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
- 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].
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.1.0",
4
- "description": "list files changed since a git reference",
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": "src/main.mjs",
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
- "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
- }
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 { 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(
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("<reference>")
16
+ .arguments("[revision]")
11
17
  .parse(process.argv);
12
18
 
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();
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
- 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
 
@@ -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;
@@ -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,31 +2,26 @@ 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").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 (!pOptions.trackedOnly) {
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, pOptions.outputType);
26
+ return format(lChanges, lOptions.outputType);
32
27
  }
@@ -0,0 +1 @@
1
+ export const VERSION = "0.4.0";
@@ -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
- similarity?: Number;
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;
@@ -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
- }