From c10820c116fc65b07d513bf5734c828cd4d63c77 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Fri, 30 Aug 2024 02:05:51 +0100 Subject: [PATCH 01/16] install typescript --- .eslintrc.js | 18 -- .eslintrc.json | 28 +++ .gitignore | 1 + package-lock.json | 430 +++++++++++++++++++++++++++++++++++++++++----- package.json | 9 +- tsconfig.json | 18 ++ 6 files changed, 438 insertions(+), 66 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 .eslintrc.json create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 7f75c88..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - env: { - browser: true, - commonjs: true, - es2021: true, - jest: true, - }, - extends: [ - 'airbnb-base', - ], - parserOptions: { - ecmaVersion: 12, - }, - rules: { - 'no-use-before-define': 'off', - 'no-restricted-syntax': 'off', - }, -}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..165cbc7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,28 @@ +{ + "parser": "@typescript-eslint/parser", + "env": { + "browser": true, + "commonjs": true, + "es2021": true, + "jest": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/strict" + ], + "parserOptions": { + "project": "./tsconfig.json", + "ecmaVersion": 2020, + "sourceType": "module" + }, + "rules": { + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/explicit-module-boundary-types": "error", + "@typescript-eslint/strict-boolean-expressions": "error", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "no-nested-ternary": "error", + "no-use-before-define": "off", + "no-restricted-syntax": "off" + } +} diff --git a/.gitignore b/.gitignore index c2658d7..d35bbf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +.idea/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4aded49..43af0bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pr-commenter-action", - "version": "1.5.0", + "version": "1.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pr-commenter-action", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", @@ -16,11 +16,16 @@ "mustache": "^4.2.0" }, "devDependencies": { + "@types/mustache": "^4.2.5", + "@types/node": "^22.5.1", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", "@vercel/ncc": "^0.38.0", - "eslint": "^8.49.0 ", + "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.28.1", - "jest": "^29.7.0" + "jest": "^29.7.0", + "typescript": "^5.5.4" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -726,18 +731,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz", - "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -780,22 +785,23 @@ } }, "node_modules/@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -838,9 +844,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1517,12 +1524,21 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/node": { - "version": "20.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", - "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==", + "node_modules/@types/mustache": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.2.5.tgz", + "integrity": "sha512-PLwiVvTBg59tGFL/8VpcGvqOu3L4OuveNvPi0EYbWchRdEVP++yRUXJPFl+CApKEq13017/4Nf7aQ5lTtHUNsA==", "dev": true }, + "node_modules/@types/node": { + "version": "22.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", + "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -1544,6 +1560,209 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vercel/ncc": { "version": "0.38.0", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.0.tgz", @@ -1554,9 +1773,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1691,6 +1910,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.findlastindex": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", @@ -2261,6 +2489,18 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2421,18 +2661,19 @@ } }, "node_modules/eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2789,6 +3030,34 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3066,9 +3335,9 @@ } }, "node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3095,6 +3364,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -3216,9 +3505,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -4477,6 +4766,15 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -4500,9 +4798,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4851,6 +5149,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -4991,9 +5298,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -5535,6 +5842,18 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -5674,6 +5993,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -5689,6 +6021,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, "node_modules/universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", diff --git a/package.json b/package.json index 45ec198..17f0122 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,15 @@ "mustache": "^4.2.0" }, "devDependencies": { + "@types/mustache": "^4.2.5", + "@types/node": "^22.5.1", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", "@vercel/ncc": "^0.38.0", - "eslint": "^8.49.0 ", + "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.28.1", - "jest": "^29.7.0" + "jest": "^29.7.0", + "typescript": "^5.5.4" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3464e06 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "sourceMap": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "noImplicitThis": true, + "alwaysStrict": true + }, + "include": ["lib/**/*.ts"], + "exclude": [ + "node_modules" + ], +} From a9b9b96a2a08efe9e2a1826d63746a30bf865c8b Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Fri, 30 Aug 2024 02:06:25 +0100 Subject: [PATCH 02/16] [js -> ts] lib/config.js --- lib/{config.js => config.ts} | 103 ++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 43 deletions(-) rename lib/{config.js => config.ts} (59%) diff --git a/lib/config.js b/lib/config.ts similarity index 59% rename from lib/config.js rename to lib/config.ts index 16308d8..1ee679d 100644 --- a/lib/config.js +++ b/lib/config.ts @@ -1,7 +1,30 @@ -const Mustache = require('mustache'); +import * as Mustache from 'mustache'; +import { Context } from 'mustache'; + +type ConfigObject = { + comment: { + header?: string | null; + 'on-create'?: string | null; + 'on-update'?: string | null; + 'glob-options'?: object; + footer?: string | null; + snippets?: SnippetObject[]; + } +}; + +type SnippetObject = { + id: string; + body: string; + files: (string | FileMatcher)[]; +}; + +type FileMatcher = { + any?: string[]; + all?: string[]; +}; -function validateCommentConfig(configObject, templateVariables) { - const configMap = new Map(); +function validateCommentConfig(configObject: ConfigObject, templateVariables: Context): Map { + const configMap = new Map(); if (typeof configObject.comment !== 'object') { throw Error( @@ -9,7 +32,7 @@ function validateCommentConfig(configObject, templateVariables) { ); } - if (configObject.comment.header === undefined || configObject.comment.header === null || typeof configObject.comment.header === 'string') { + if (configObject.comment.header === undefined || configObject.comment.header === null) { configMap.set('header', configObject.comment.header); } else { throw Error( @@ -23,7 +46,7 @@ function validateCommentConfig(configObject, templateVariables) { } else if (typeof configObject.comment['on-create'] === 'string') { const onCreate = Mustache.render(configObject.comment['on-create'], templateVariables); - if (allowedOnCreateValues.includes(onCreate)) { + if (allowedOnCreateValues.includes(onCreate as typeof allowedOnCreateValues[number])) { configMap.set('onCreate', onCreate); } else { throw Error( @@ -36,13 +59,13 @@ function validateCommentConfig(configObject, templateVariables) { ); } - const allowedOnUpdateValues = ['recreate', 'edit', 'nothing']; + const allowedOnUpdateValues = ['recreate', 'edit', 'nothing'] as const; if (configObject.comment['on-update'] === undefined || configObject.comment['on-update'] === null) { configMap.set('onUpdate', allowedOnUpdateValues[0]); } else if (typeof configObject.comment['on-update'] === 'string') { const onUpdate = Mustache.render(configObject.comment['on-update'], templateVariables); - if (allowedOnUpdateValues.includes(onUpdate)) { + if (allowedOnUpdateValues.includes(onUpdate as typeof allowedOnUpdateValues[number])) { configMap.set('onUpdate', onUpdate); } else { throw Error( @@ -69,44 +92,33 @@ function validateCommentConfig(configObject, templateVariables) { if (Array.isArray(configObject.comment.snippets) && configObject.comment.snippets.length > 0) { configMap.set('snippets', configObject.comment.snippets.map((snippetObject, index) => { - const snippetMap = new Map(); - - if (typeof snippetObject.id === 'string') { - const id = Mustache.render(snippetObject.id, templateVariables); - const regex = /^[A-Za-z0-9\-_,]*$/; - if (regex.exec(id)) { - snippetMap.set('id', id); - } else { - throw Error( - `found invalid snippet id '${id}' (snippet ids must contain only letters, numbers, dashes, and underscores)`, - ); - } - } else { - throw Error( - `found unexpected value type '${typeof snippetObject.id}' under key '.comment.snippets.${index}.id' (should be a string)`, - ); - } + const snippetMap = new Map(); - if (typeof snippetObject.body === 'string') { - snippetMap.set('body', snippetObject.body); + const id = Mustache.render(snippetObject.id, templateVariables); + const regex = /^[A-Za-z0-9\-_,]*$/; + if (regex.exec(id)) { + snippetMap.set('id', id); } else { throw Error( - `found unexpected value type '${typeof snippetObject.body}' under key '.comment.snippets.${index}.body' (should be a string)`, + `found invalid snippet id '${id}' (snippet ids must contain only letters, numbers, dashes, and underscores)`, ); } - const isValidMatcher = (matcher) => { - const isAnyValid = !matcher.any - || (Array.isArray(matcher.any) && matcher.any.length > 0 && matcher.any.every((f) => typeof f === 'string')); + snippetMap.set('body', snippetObject.body); + + const isValidMatcher = (matcher: string | FileMatcher): boolean => { + if (typeof matcher === 'string') return true; + if (typeof matcher !== 'object' || matcher === null) return false; + + const isAnyValid = !matcher.any || (Array.isArray(matcher.any) && matcher.any.length > 0); - const isAllValid = !matcher.all - || (Array.isArray(matcher.all) && matcher.all.length > 0 && matcher.all.every((f) => typeof f === 'string')); + const isAllValid = !matcher.all || (Array.isArray(matcher.all) && matcher.all.length > 0); const isAtLeastOnePresent = (!!matcher.any || !!matcher.all); - return typeof matcher === 'string' || (matcher && isAnyValid && isAllValid && isAtLeastOnePresent); + return isAnyValid && isAllValid && isAtLeastOnePresent; }; - const isValidFileList = (list) => Array.isArray(list) && list.length > 0; + const isValidFileList = (list: (string | FileMatcher)[]): boolean => Array.isArray(list) && list.length > 0; if (isValidFileList(snippetObject.files)) { const list = snippetObject.files.map((matcher, matcherIndex) => { @@ -114,14 +126,19 @@ function validateCommentConfig(configObject, templateVariables) { if (typeof matcher === 'string') { return matcher; } - const obj = {}; - if (matcher.any) { obj.any = matcher.any; } - if (matcher.all) { obj.all = matcher.all; } + const obj: FileMatcher = {}; + if (matcher.any) { + obj.any = matcher.any; + } + if (matcher.all) { + obj.all = matcher.all; + } return obj; + } else { + throw Error( + `found unexpected value type under key '.comment.snippets.${index}.files.${matcherIndex}' (should be a string or an object with keys 'all' and/or 'any')`, + ); } - throw Error( - `found unexpected value type under key '.comment.snippets.${index}.files.${matcherIndex}' (should be a string or an object with keys 'all' and/or 'any')`, - ); }); snippetMap.set('files', list); } else { @@ -133,8 +150,8 @@ function validateCommentConfig(configObject, templateVariables) { return snippetMap; })); - const snippetIds = configMap.get('snippets').map((s) => s.get('id')); - snippetIds.forEach((value, index, self) => { + const snippetIds = (configMap.get('snippets') as Map[]).map((s: Map) => s.get('id') as string); + snippetIds.forEach((value: string, index: number, self: string[]) => { if (self.indexOf(value) !== index) { throw Error( `found duplicate snippet id '${value}'`, @@ -150,4 +167,4 @@ function validateCommentConfig(configObject, templateVariables) { return configMap; } -module.exports = { validateCommentConfig }; +export { validateCommentConfig }; From 936f3a471b38baf459b7a827366610574d3a3ff3 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Sat, 31 Aug 2024 23:39:31 +0100 Subject: [PATCH 03/16] update config --- .eslintrc.json | 1 + package-lock.json | 7 +++++++ package.json | 1 + tsconfig.json | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 165cbc7..2235fbb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,6 +21,7 @@ "@typescript-eslint/explicit-module-boundary-types": "error", "@typescript-eslint/strict-boolean-expressions": "error", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/explicit-function-return-type": "error", "no-nested-ternary": "error", "no-use-before-define": "off", "no-restricted-syntax": "off" diff --git a/package-lock.json b/package-lock.json index 43af0bb..384a505 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "mustache": "^4.2.0" }, "devDependencies": { + "@types/js-yaml": "^4.0.9", "@types/mustache": "^4.2.5", "@types/node": "^22.5.1", "@typescript-eslint/eslint-plugin": "^7.18.0", @@ -1518,6 +1519,12 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", diff --git a/package.json b/package.json index 17f0122..045518d 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "mustache": "^4.2.0" }, "devDependencies": { + "@types/js-yaml": "^4.0.9", "@types/mustache": "^4.2.5", "@types/node": "^22.5.1", "@typescript-eslint/eslint-plugin": "^7.18.0", diff --git a/tsconfig.json b/tsconfig.json index 3464e06..804f262 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "noImplicitThis": true, "alwaysStrict": true }, - "include": ["lib/**/*.ts"], + "include": ["**/*.ts"], "exclude": [ "node_modules" ], From b91e2019a85069c80b583c6eed30a70b12d95dc7 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Sat, 31 Aug 2024 23:39:48 +0100 Subject: [PATCH 04/16] Convert lib files --- lib/{comment.js => comment.ts} | 51 ++++++++++++------- lib/config.ts | 73 ++++++++++----------------- lib/{github.js => github.ts} | 46 +++++++++++------ lib/index.js | 11 ----- lib/index.ts | 13 +++++ lib/{run.js => run.ts} | 84 ++++++++++++++++++++------------ lib/{snippets.js => snippets.ts} | 29 +++++------ 7 files changed, 171 insertions(+), 136 deletions(-) rename lib/{comment.js => comment.ts} (50%) rename lib/{github.js => github.ts} (53%) delete mode 100644 lib/index.js create mode 100644 lib/index.ts rename lib/{run.js => run.ts} (51%) rename lib/{snippets.js => snippets.ts} (64%) diff --git a/lib/comment.js b/lib/comment.ts similarity index 50% rename from lib/comment.js rename to lib/comment.ts index 2fc1df9..842524f 100644 --- a/lib/comment.js +++ b/lib/comment.ts @@ -1,26 +1,30 @@ -const Mustache = require('mustache'); +import * as Mustache from 'mustache'; +import { Comment } from '../types/global.type'; -function commentMetadata(snippetIds) { +function commentMetadata(snippetIds: string[]): string { return ``; } -function extractCommentMetadata(commentBody) { +function extractCommentMetadata(commentBody: string): string[] | null { // snippet id regex plus a comma const regex = //; const match = regex.exec(commentBody); if (match) { - return match[1].split(',').map((s) => s.trim()).filter((s) => s !== ''); + return match[1].split(',').map((s: string) => s.trim()).filter((s) => s !== ''); } return null; } -function assembleCommentBody(snippetIds, commentConfig, templateVariables = {}) { +function assembleCommentBody( + snippetIds: string[], + commentConfig: Map, + templateVariables: Record = {}): string { let strings = [ - commentConfig.get('header'), - ...commentConfig.get('snippets').map((snippet) => { - if (snippetIds.includes(snippet.get('id'))) { - return snippet.get('body'); + commentConfig.get('header') as string | undefined, + ...(commentConfig.get('snippets') as Map[]).map((snippet) => { + if (snippetIds.includes(snippet.get('id') as string)) { + return snippet.get('body') as string; } return null; }), @@ -28,24 +32,28 @@ function assembleCommentBody(snippetIds, commentConfig, templateVariables = {}) commentMetadata(snippetIds), ]; - strings = strings.filter((s) => !!s); + strings = strings.filter((s) => Boolean(s)); const rawCommentBody = strings.join('\n\n'); return Mustache.render(rawCommentBody, templateVariables); } -function newCommentDifferentThanPreviousComment(previousComment, snippetIds) { +function newCommentDifferentThanPreviousComment(previousComment: Comment, snippetIds: string[]): boolean { const previousSnippetIds = extractCommentMetadata(previousComment.body); - return previousSnippetIds.join(',') !== snippetIds.join(','); + return previousSnippetIds !== null && previousSnippetIds.join(',') !== snippetIds.join(','); } -function newCommentWouldHaveContent(snippetIds) { +function newCommentWouldHaveContent(snippetIds: string[]): boolean { return snippetIds.length > 0; } -function shouldPostNewComment(previousComment, snippetIds, commentConfig) { +function shouldPostNewComment( + previousComment: Comment | null, + snippetIds: string[], + commentConfig: Map +): boolean { const isNotEmpty = newCommentWouldHaveContent(snippetIds); const isCreating = !previousComment && commentConfig.get('onCreate') === 'create'; const isUpdating = !!previousComment @@ -55,14 +63,22 @@ function shouldPostNewComment(previousComment, snippetIds, commentConfig) { return isNotEmpty && (isCreating || isUpdating); } -function shouldDeletePreviousComment(previousComment, snippetIds, commentConfig) { +function shouldDeletePreviousComment( + previousComment: Comment | null, + snippetIds: string[], + commentConfig: Map +): boolean { return !!previousComment && ( shouldPostNewComment(previousComment, snippetIds, commentConfig) || (!newCommentWouldHaveContent(snippetIds) && commentConfig.get('onUpdate') !== 'nothing') ); } -function shouldEditPreviousComment(previousComment, snippetIds, commentConfig) { +function shouldEditPreviousComment( + previousComment: Comment | null, + snippetIds: string[], + commentConfig: Map +): boolean { return newCommentWouldHaveContent(snippetIds) && ( !!previousComment && commentConfig.get('onUpdate') === 'edit' @@ -70,8 +86,7 @@ function shouldEditPreviousComment(previousComment, snippetIds, commentConfig) { ); } -module.exports = { - commentMetadata, +export { assembleCommentBody, extractCommentMetadata, shouldPostNewComment, diff --git a/lib/config.ts b/lib/config.ts index 1ee679d..6af2198 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,50 +1,29 @@ import * as Mustache from 'mustache'; -import { Context } from 'mustache'; - -type ConfigObject = { - comment: { - header?: string | null; - 'on-create'?: string | null; - 'on-update'?: string | null; - 'glob-options'?: object; - footer?: string | null; - snippets?: SnippetObject[]; - } -}; - -type SnippetObject = { - id: string; - body: string; - files: (string | FileMatcher)[]; -}; - -type FileMatcher = { - any?: string[]; - all?: string[]; -}; +import { CommentObject, ConfigObject, MatchConfig } from '../types/global.type'; -function validateCommentConfig(configObject: ConfigObject, templateVariables: Context): Map { +function validateCommentConfig(configObject: ConfigObject, templateVariables: Record): Map { const configMap = new Map(); + const comment: CommentObject = configObject.comment; - if (typeof configObject.comment !== 'object') { + if (typeof comment !== 'object') { throw Error( - `found unexpected value type '${typeof configObject.comment}' under key '.comment' (should be an object)`, + `found unexpected value type '${typeof comment}' under key '.comment' (should be an object)`, ); } - if (configObject.comment.header === undefined || configObject.comment.header === null) { - configMap.set('header', configObject.comment.header); + if (comment.header === undefined || comment.header === null) { + configMap.set('header', comment.header); } else { throw Error( - `found unexpected value type '${typeof configObject.comment.header}' under key '.comment.header' (should be a string)`, + `found unexpected value type '${typeof comment.header}' under key '.comment.header' (should be a string)`, ); } const allowedOnCreateValues = ['create', 'nothing']; - if (configObject.comment['on-create'] === undefined || configObject.comment['on-create'] === null) { + if (comment['on-create'] === undefined || comment['on-create'] === null) { configMap.set('onCreate', allowedOnCreateValues[0]); - } else if (typeof configObject.comment['on-create'] === 'string') { - const onCreate = Mustache.render(configObject.comment['on-create'], templateVariables); + } else if (typeof comment['on-create'] === 'string') { + const onCreate = Mustache.render(comment['on-create'], templateVariables); if (allowedOnCreateValues.includes(onCreate as typeof allowedOnCreateValues[number])) { configMap.set('onCreate', onCreate); @@ -55,15 +34,15 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Co } } else { throw Error( - `found unexpected value type '${typeof configObject.comment['on-create']}' under key '.comment.on-create' (should be a string)`, + `found unexpected value type '${typeof comment['on-create']}' under key '.comment.on-create' (should be a string)`, ); } const allowedOnUpdateValues = ['recreate', 'edit', 'nothing'] as const; - if (configObject.comment['on-update'] === undefined || configObject.comment['on-update'] === null) { + if (comment['on-update'] === undefined || comment['on-update'] === null) { configMap.set('onUpdate', allowedOnUpdateValues[0]); - } else if (typeof configObject.comment['on-update'] === 'string') { - const onUpdate = Mustache.render(configObject.comment['on-update'], templateVariables); + } else if (typeof comment['on-update'] === 'string') { + const onUpdate = Mustache.render(comment['on-update'], templateVariables); if (allowedOnUpdateValues.includes(onUpdate as typeof allowedOnUpdateValues[number])) { configMap.set('onUpdate', onUpdate); @@ -74,24 +53,24 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Co } } else { throw Error( - `found unexpected value type '${typeof configObject.comment['on-update']}' under key '.comment.on-update' (should be a string)`, + `found unexpected value type '${typeof comment['on-update']}' under key '.comment.on-update' (should be a string)`, ); } - if (configObject.comment['glob-options'] && typeof configObject.comment['glob-options'] === 'object') { - configMap.set('globOptions', configObject.comment['glob-options']); + if (comment['glob-options'] && typeof comment['glob-options'] === 'object') { + configMap.set('globOptions', comment['glob-options']); } - if (configObject.comment.footer === undefined || configObject.comment.footer === null || typeof configObject.comment.footer === 'string') { - configMap.set('footer', configObject.comment.footer); + if (comment.footer === undefined || comment.footer === null || typeof comment.footer === 'string') { + configMap.set('footer', comment.footer); } else { throw Error( - `found unexpected value type '${typeof configObject.comment.footer}' under key '.comment.footer' (should be a string)`, + `found unexpected value type '${typeof comment.footer}' under key '.comment.footer' (should be a string)`, ); } - if (Array.isArray(configObject.comment.snippets) && configObject.comment.snippets.length > 0) { - configMap.set('snippets', configObject.comment.snippets.map((snippetObject, index) => { + if (Array.isArray(comment.snippets) && comment.snippets.length > 0) { + configMap.set('snippets', comment.snippets.map((snippetObject, index) => { const snippetMap = new Map(); const id = Mustache.render(snippetObject.id, templateVariables); @@ -106,7 +85,7 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Co snippetMap.set('body', snippetObject.body); - const isValidMatcher = (matcher: string | FileMatcher): boolean => { + const isValidMatcher = (matcher: string | MatchConfig): boolean => { if (typeof matcher === 'string') return true; if (typeof matcher !== 'object' || matcher === null) return false; @@ -118,7 +97,7 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Co return isAnyValid && isAllValid && isAtLeastOnePresent; }; - const isValidFileList = (list: (string | FileMatcher)[]): boolean => Array.isArray(list) && list.length > 0; + const isValidFileList = (list: (string | MatchConfig)[]): boolean => Array.isArray(list) && list.length > 0; if (isValidFileList(snippetObject.files)) { const list = snippetObject.files.map((matcher, matcherIndex) => { @@ -126,7 +105,7 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Co if (typeof matcher === 'string') { return matcher; } - const obj: FileMatcher = {}; + const obj: MatchConfig = {}; if (matcher.any) { obj.any = matcher.any; } diff --git a/lib/github.js b/lib/github.ts similarity index 53% rename from lib/github.js rename to lib/github.ts index 15c6ae0..8e133cf 100644 --- a/lib/github.js +++ b/lib/github.ts @@ -1,16 +1,26 @@ -const github = require('@actions/github'); -const core = require('@actions/core'); +import * as github from '@actions/github'; +import * as core from '@actions/core'; +import { Comment } from '../types/global.type'; -async function deleteComment(client, comment) { - return client.rest.issues.deleteComment({ +type OctokitClient = ReturnType; + +type RemoteDefinition = { + owner: string; + repo: string; + path: string; + ref: string; +} + +async function deleteComment(client: OctokitClient, comment: Comment): Promise { + await client.rest.issues.deleteComment({ owner: github.context.repo.owner, repo: github.context.repo.repo, comment_id: comment.id, }); } -async function editComment(client, comment, newBody) { - return client.rest.issues.updateComment({ +async function editComment(client: OctokitClient, comment: Comment, newBody: string): Promise { + await client.rest.issues.updateComment({ owner: github.context.repo.owner, repo: github.context.repo.repo, comment_id: comment.id, @@ -18,8 +28,8 @@ async function editComment(client, comment, newBody) { }); } -async function createComment(client, prNumber, body) { - return client.rest.issues.createComment({ +async function createComment(client: OctokitClient, prNumber: number, body: string): Promise { + await client.rest.issues.createComment({ owner: github.context.repo.owner, repo: github.context.repo.repo, issue_number: prNumber, @@ -27,14 +37,14 @@ async function createComment(client, prNumber, body) { }); } -async function getChangedFiles(client, prNumber) { +async function getChangedFiles(client: OctokitClient, prNumber: number): Promise { const listFilesOptions = client.rest.pulls.listFiles.endpoint.merge({ owner: github.context.repo.owner, repo: github.context.repo.repo, pull_number: prNumber, }); - const listFilesResponse = await client.paginate(listFilesOptions); + const listFilesResponse = await client.paginate<{filename: string}>(listFilesOptions); const changedFiles = listFilesResponse.map((f) => f.filename); core.debug('found changed files:'); @@ -45,8 +55,8 @@ async function getChangedFiles(client, prNumber) { return changedFiles; } -async function getFileContent(client, repoPath) { - let remoteDefn = { +async function getFileContent(client: OctokitClient, repoPath: string): Promise { + let remoteDefn: RemoteDefinition = { owner: github.context.repo.owner, repo: github.context.repo.repo, path: repoPath, @@ -59,7 +69,7 @@ async function getFileContent(client, repoPath) { if (match) { // eslint-disable-next-line no-unused-vars - const [_, org, repo, ref, path] = match; + const [, org, repo, ref, path] = match; remoteDefn = { owner: org, repo, @@ -73,10 +83,14 @@ async function getFileContent(client, repoPath) { const response = await client.rest.repos.getContent(remoteDefn); - return Buffer.from(response.data.content, response.data.encoding).toString(); + if ('content' in response.data && 'encoding' in response.data) { + return Buffer.from(response.data.content, response.data.encoding as BufferEncoding).toString(); + } + + throw new Error('Unexpected response format from getContent'); } -async function getComments(client, prNumber) { +async function getComments(client: OctokitClient, prNumber: number): Promise { const { data: comments } = await client.rest.issues.listComments({ owner: github.context.repo.owner, repo: github.context.repo.repo, @@ -86,7 +100,7 @@ async function getComments(client, prNumber) { return comments; } -module.exports = { +export { deleteComment, editComment, createComment, diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 97271b2..0000000 --- a/lib/index.js +++ /dev/null @@ -1,11 +0,0 @@ -const core = require('@actions/core'); -const { run } = require('./run'); - -(async () => { - try { - await run(); - } catch (error) { - core.error(error); - core.setFailed(error.message); - } -})(); diff --git a/lib/index.ts b/lib/index.ts new file mode 100644 index 0000000..de2c67c --- /dev/null +++ b/lib/index.ts @@ -0,0 +1,13 @@ +import * as core from '@actions/core'; +import { run } from './run'; + +(async (): Promise => { + try { + await run(); + } catch (error: unknown) { + if (error instanceof Error) { + core.error(error); + core.setFailed(error.message); + } + } +})(); diff --git a/lib/run.js b/lib/run.ts similarity index 51% rename from lib/run.js rename to lib/run.ts index a717cf4..a601591 100644 --- a/lib/run.js +++ b/lib/run.ts @@ -1,46 +1,45 @@ -const core = require('@actions/core'); -const github = require('@actions/github'); -const yaml = require('js-yaml'); -const { validateCommentConfig } = require('./config'); -const { getMatchingSnippetIds } = require('./snippets'); - -const { +import * as core from '@actions/core'; +import * as github from '@actions/github'; +import * as yaml from 'js-yaml'; +import { Comment, CommentConfig, ConfigObject } from '../types/global.type'; +import { validateCommentConfig } from './config'; +import { getMatchingSnippetIds } from './snippets'; +import { assembleCommentBody, extractCommentMetadata, shouldPostNewComment, shouldDeletePreviousComment, shouldEditPreviousComment, -} = require('./comment'); +} from './comment'; -const { +import { deleteComment, editComment, createComment, getChangedFiles, getFileContent, getComments, -} = require('./github'); +} from './github'; -async function run() { +async function run(): Promise { const token = core.getInput('github-token', { required: true }); const configPath = core.getInput('config-file', { required: true }); const templateVariablesJSONString = core.getInput('template-variables', { required: false }); const prNumber = getPrNumber(); - if (!prNumber) { - // eslint-disable-next-line no-console + if (prNumber === undefined) { console.log('Could not get pull request number from context, exiting'); return; } // eslint-disable-next-line new-cap - const client = new github.getOctokit(token); + const client = github.getOctokit(token); core.debug(`fetching changed files for pr #${prNumber}`); const changedFiles = await getChangedFiles(client, prNumber); const previousComment = await getPreviousPRComment(client, prNumber); - let templateVariables = {}; + let templateVariables: Record = {}; if (templateVariablesJSONString) { core.debug('Input template-variables was passed'); core.debug(templateVariablesJSONString); @@ -54,17 +53,17 @@ async function run() { core.debug('Input template-variables was not passed'); } - const commentConfig = await getCommentConfig(client, configPath, templateVariables); + const commentConfig = (await getCommentConfig(client, configPath, templateVariables)) as CommentConfig; const snippetIds = getMatchingSnippetIds(changedFiles, commentConfig); - if (shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) { + if (previousComment && shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) { core.info('removing previous comment'); await deleteComment(client, previousComment); } const commentBody = assembleCommentBody(snippetIds, commentConfig, templateVariables); - if (shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) { + if (previousComment && shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) { core.info('updating previous comment'); await editComment(client, previousComment, commentBody); } @@ -75,7 +74,7 @@ async function run() { } } -function getPrNumber() { +function getPrNumber(): number | undefined { const pullRequest = github.context.payload.pull_request; if (!pullRequest) { return undefined; @@ -84,33 +83,58 @@ function getPrNumber() { return pullRequest.number; } -async function getCommentConfig(client, configurationPath, templateVariables) { +function isConfigObject(obj: unknown): obj is ConfigObject { + if (typeof obj !== 'object' || obj === null) { + return false; + } + + const config = obj as Record; + + if (typeof config.comment !== 'object' || config.comment === null) { + return false; + } + + const comment = config.comment as Record; + + if (!Array.isArray(comment.snippets) || comment.snippets.length === 0) { + return false; + } + + return true; +} + +async function getCommentConfig(client: ReturnType, configurationPath: string, templateVariables: Record): Promise { const configurationContent = await getFileContent(client, configurationPath); const configObject = yaml.load(configurationContent); + if (!isConfigObject(configObject)) { + throw new Error('Invalid configuration object structure'); + } + // transform object to a map or throw if yaml is malformed: - const configMap = validateCommentConfig(configObject, templateVariables); - return configMap; + return validateCommentConfig(configObject, templateVariables) as CommentConfig; } -async function getPreviousPRComment(client, prNumber) { +async function getPreviousPRComment(client: ReturnType, prNumber: number): Promise { const comments = await getComments(client, prNumber); core.debug(`there are ${comments.length} comments on the PR #${prNumber}`); - const newestFirst = (c1, c2) => c2.created_at.localeCompare(c1.created_at); + const newestFirst = (a: Comment, b: Comment): number => b.created_at.localeCompare(a.created_at); const sortedComments = comments.sort(newestFirst); - const previousComment = sortedComments.find((c) => extractCommentMetadata(c.body) !== null); + const previousComment = sortedComments.find((c) => c.body != null && extractCommentMetadata(c.body) !== null); - if (previousComment) { + if (previousComment?.body != null) { const previousSnippetIds = extractCommentMetadata(previousComment.body); - core.info(`found previous comment made by pr-commenter: ${previousComment.url}`); - core.info(`extracted snippet ids from previous comment: ${previousSnippetIds.join(', ')}`); + if (previousSnippetIds) { + core.info(`found previous comment made by pr-commenter: ${previousComment.url}`); + core.info(`extracted snippet ids from previous comment: ${previousSnippetIds.join(', ')}`); + } - return previousComment; + await previousComment; } return null; } -module.exports = { run }; +export { run }; diff --git a/lib/snippets.js b/lib/snippets.ts similarity index 64% rename from lib/snippets.js rename to lib/snippets.ts index 1ca0235..3df8a2f 100644 --- a/lib/snippets.js +++ b/lib/snippets.ts @@ -1,12 +1,13 @@ -const core = require('@actions/core'); -const { Minimatch } = require('minimatch'); +import * as core from '@actions/core'; +import { Minimatch, MinimatchOptions } from "minimatch"; +import { CommentConfig, MatchConfig } from "../types/global.type"; -function getMatchingSnippetIds(changedFiles, commentConfig) { - const snippetIds = commentConfig.get('snippets').reduce((acc, snippet) => { +function getMatchingSnippetIds(changedFiles: string[], commentConfig: CommentConfig): string[] { + const snippetIds = commentConfig.get('snippets').reduce((acc, snippet): string[] => { core.debug(`processing snippet ${snippet.get('id')}`); - if (checkGlobs(changedFiles, snippet.get('files'), commentConfig.get('globOptions') || {})) { - return [...acc, snippet.get('id')]; + if (checkGlobs(changedFiles, snippet.get('files') as [], commentConfig.get('globOptions') || {})) { + return [...acc, snippet.get('id') as string]; } return acc; }, []); @@ -16,7 +17,7 @@ function getMatchingSnippetIds(changedFiles, commentConfig) { return snippetIds; } -function toMatchConfig(config) { +function toMatchConfig(config: string | MatchConfig): MatchConfig { if (typeof config === 'string') { return { any: [config], @@ -26,11 +27,11 @@ function toMatchConfig(config) { return config; } -function printPattern(matcher) { +function printPattern(matcher: Minimatch): string { return (matcher.negate ? '!' : '') + matcher.pattern; } -function checkGlobs(changedFiles, globs, opts) { +function checkGlobs(changedFiles: string[], globs: (string | MatchConfig)[], opts: MinimatchOptions): boolean { for (const glob of globs) { core.debug(` checking pattern ${JSON.stringify(glob)}`); const matchConfig = toMatchConfig(glob); @@ -41,7 +42,7 @@ function checkGlobs(changedFiles, globs, opts) { return false; } -function isMatch(changedFile, matchers) { +function isMatch(changedFile: string, matchers: Minimatch[]): boolean { core.debug(` matching patterns against file ${changedFile}`); for (const matcher of matchers) { core.debug(` - ${printPattern(matcher)}`); @@ -56,7 +57,7 @@ function isMatch(changedFile, matchers) { } // equivalent to "Array.some()" but expanded for debugging and clarity -function checkAny(changedFiles, globs, opts) { +function checkAny(changedFiles: string[], globs: string[], opts: MinimatchOptions): boolean { const matchers = globs.map((g) => new Minimatch(g, opts)); core.debug(' checking "any" patterns'); for (const changedFile of changedFiles) { @@ -71,7 +72,7 @@ function checkAny(changedFiles, globs, opts) { } // equivalent to "Array.every()" but expanded for debugging and clarity -function checkAll(changedFiles, globs, opts) { +function checkAll(changedFiles: string[], globs: string[], opts: MinimatchOptions): boolean { const matchers = globs.map((g) => new Minimatch(g, opts)); core.debug(' checking "all" patterns'); for (const changedFile of changedFiles) { @@ -85,7 +86,7 @@ function checkAll(changedFiles, globs, opts) { return true; } -function checkMatch(changedFiles, matchConfig, opts) { +function checkMatch(changedFiles: string[], matchConfig: MatchConfig, opts: MinimatchOptions): boolean { if (matchConfig.all !== undefined) { if (!checkAll(changedFiles, matchConfig.all, opts)) { return false; @@ -101,4 +102,4 @@ function checkMatch(changedFiles, matchConfig, opts) { return true; } -module.exports = { getMatchingSnippetIds }; +export { getMatchingSnippetIds }; From 5d789ba7d000b0fd07decb1e280bb72e5b06fbd8 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Sat, 31 Aug 2024 23:40:35 +0100 Subject: [PATCH 05/16] Convert lib files --- lib/run.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/run.ts b/lib/run.ts index a601591..21feb48 100644 --- a/lib/run.ts +++ b/lib/run.ts @@ -96,11 +96,7 @@ function isConfigObject(obj: unknown): obj is ConfigObject { const comment = config.comment as Record; - if (!Array.isArray(comment.snippets) || comment.snippets.length === 0) { - return false; - } - - return true; + return !(!Array.isArray(comment.snippets) || comment.snippets.length === 0); } async function getCommentConfig(client: ReturnType, configurationPath: string, templateVariables: Record): Promise { From e5a7dc7f1ac359c1b1036f11736446119dec95b6 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Sat, 31 Aug 2024 23:40:48 +0100 Subject: [PATCH 06/16] add types --- types/global.type.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 types/global.type.ts diff --git a/types/global.type.ts b/types/global.type.ts new file mode 100644 index 0000000..92d20b5 --- /dev/null +++ b/types/global.type.ts @@ -0,0 +1,44 @@ +import { MinimatchOptions } from "minimatch"; + +export type ConfigObject = { + comment: CommentObject; +}; + +export type CommentObject = { + header: string | null; + footer: string | null; + snippets: SnippetObject[]; + 'on-create'?: string | null; + 'on-update'?: string | null; + 'glob-options'?: object; +} + +export type SnippetObject = { + id: string; + body: string; + files: (string | MatchConfig)[]; +}; + +export type Comment = { + id: number; + url: string; + created_at: string; + body?: string; + body_text?: string; + body_html?: string; +} + +export type MatchConfig = { + any?: string[]; + all?: string[]; +}; + +export interface CommentConfig extends Map { + get(key: 'on-create' | 'on-update' | 'globOptions' | 'snippets'): Map[]; + get(key: 'globOptions'): MinimatchOptions | undefined; +} + +export interface Snippet extends Map { + get(key: 'id' | 'body'): string; + get(key: 'files'): (string | MatchConfig)[]; +} \ No newline at end of file From 9074f2052a16903319695adf7af01a5af49620b8 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Wed, 4 Sep 2024 01:51:16 +0100 Subject: [PATCH 07/16] refactor to add runtime type checks --- lib/comment.ts | 3 ++- lib/config.ts | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/comment.ts b/lib/comment.ts index 842524f..85274c5 100644 --- a/lib/comment.ts +++ b/lib/comment.ts @@ -40,7 +40,7 @@ function assembleCommentBody( } function newCommentDifferentThanPreviousComment(previousComment: Comment, snippetIds: string[]): boolean { - const previousSnippetIds = extractCommentMetadata(previousComment.body); + const previousSnippetIds = (previousComment.body != null) ? extractCommentMetadata(previousComment.body) : null; return previousSnippetIds !== null && previousSnippetIds.join(',') !== snippetIds.join(','); } @@ -87,6 +87,7 @@ function shouldEditPreviousComment( } export { + commentMetadata, assembleCommentBody, extractCommentMetadata, shouldPostNewComment, diff --git a/lib/config.ts b/lib/config.ts index 6af2198..18c5b9a 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,7 +1,7 @@ import * as Mustache from 'mustache'; import { CommentObject, ConfigObject, MatchConfig } from '../types/global.type'; -function validateCommentConfig(configObject: ConfigObject, templateVariables: Record): Map { +function validateCommentConfig(configObject: ConfigObject, templateVariables?: Record): Map { const configMap = new Map(); const comment: CommentObject = configObject.comment; @@ -11,7 +11,7 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Re ); } - if (comment.header === undefined || comment.header === null) { + if (comment.header === undefined || comment.header === null || typeof comment.header === 'string') { configMap.set('header', comment.header); } else { throw Error( @@ -73,29 +73,42 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables: Re configMap.set('snippets', comment.snippets.map((snippetObject, index) => { const snippetMap = new Map(); - const id = Mustache.render(snippetObject.id, templateVariables); - const regex = /^[A-Za-z0-9\-_,]*$/; - if (regex.exec(id)) { - snippetMap.set('id', id); + if (typeof snippetObject.id === 'string') { + const id = Mustache.render(snippetObject.id, templateVariables); + const regex = /^[A-Za-z0-9\-_,]*$/; + if (regex.exec(id)) { + snippetMap.set('id', id); + } else { + throw Error( + `found invalid snippet id '${id}' (snippet ids must contain only letters, numbers, dashes, and underscores)`, + ); + } } else { throw Error( - `found invalid snippet id '${id}' (snippet ids must contain only letters, numbers, dashes, and underscores)`, + `found unexpected value type '${typeof snippetObject.id}' under key '.comment.snippets.${index}.id' (should be a string)` ); } - snippetMap.set('body', snippetObject.body); + if (typeof snippetObject.body === 'string') { + snippetMap.set('body', snippetObject.body); + } else { + throw Error( + `found unexpected value type '${typeof snippetObject.body}' under key '.comment.snippets.${index}.body' (should be a string)`, + ); + } const isValidMatcher = (matcher: string | MatchConfig): boolean => { - if (typeof matcher === 'string') return true; - if (typeof matcher !== 'object' || matcher === null) return false; + if (typeof matcher !== "string") { + const isAnyValid = !(matcher.any) || (Array.isArray(matcher.any) && matcher.any.length > 0 && matcher.any.every((f) => typeof f === 'string')); - const isAnyValid = !matcher.any || (Array.isArray(matcher.any) && matcher.any.length > 0); + const isAllValid = !(matcher.all) || (Array.isArray(matcher.all) && matcher.all.length > 0 && matcher.all.every((f) => typeof f === 'string')); - const isAllValid = !matcher.all || (Array.isArray(matcher.all) && matcher.all.length > 0); + const isAtLeastOnePresent = ((Boolean(matcher.any)) || (Boolean(matcher.all))); - const isAtLeastOnePresent = (!!matcher.any || !!matcher.all); + return isAnyValid && isAllValid && isAtLeastOnePresent; + } - return isAnyValid && isAllValid && isAtLeastOnePresent; + return true; }; const isValidFileList = (list: (string | MatchConfig)[]): boolean => Array.isArray(list) && list.length > 0; From 0467f2552417cee4586824b15db5e42c3e5fba1b Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Wed, 4 Sep 2024 01:51:35 +0100 Subject: [PATCH 08/16] add jest config for TS --- jest.config.json | 11 ++ package-lock.json | 476 ++++++++++++++++++++++++++-------------------- package.json | 2 + 3 files changed, 283 insertions(+), 206 deletions(-) create mode 100644 jest.config.json diff --git a/jest.config.json b/jest.config.json new file mode 100644 index 0000000..b979fb1 --- /dev/null +++ b/jest.config.json @@ -0,0 +1,11 @@ +{ + "preset": "ts-jest", + "testEnvironment": "node", + "transform": { + "^.+\\.ts$": "ts-jest" + }, + "transformIgnorePatterns": [ + "/node_modules/" + ], + "moduleFileExtensions": ["ts", "js", "json", "node"] +} diff --git a/package-lock.json b/package-lock.json index 384a505..f65fe57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,8 @@ "mustache": "^4.2.0" }, "devDependencies": { + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.9", "@types/mustache": "^4.2.5", "@types/node": "^22.5.1", @@ -26,6 +28,7 @@ "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.28.1", "jest": "^29.7.0", + "ts-jest": "^29.2.5", "typescript": "^5.5.4" } }, @@ -80,89 +83,18 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", @@ -209,14 +141,14 @@ "dev": true }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.25.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -239,63 +171,29 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", - "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.15" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -305,60 +203,49 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", - "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" @@ -379,14 +266,15 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -464,10 +352,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.6" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -536,12 +427,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -638,12 +529,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -653,34 +544,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", - "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.17", - "debug": "^4.1.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -697,13 +585,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", - "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.15", + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1238,14 +1126,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -1261,9 +1149,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1276,9 +1164,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1519,6 +1407,16 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -2002,6 +1900,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -2183,6 +2087,18 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -2520,6 +2436,21 @@ "node": ">=6.0.0" } }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.519", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.519.tgz", @@ -3107,6 +3038,27 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3981,6 +3933,46 @@ "node": ">=8" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -4695,6 +4687,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4758,6 +4756,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -5861,6 +5865,66 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-jest": { + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", diff --git a/package.json b/package.json index 045518d..152ebe8 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "mustache": "^4.2.0" }, "devDependencies": { + "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.9", "@types/mustache": "^4.2.5", "@types/node": "^22.5.1", @@ -37,6 +38,7 @@ "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.28.1", "jest": "^29.7.0", + "ts-jest": "^29.2.5", "typescript": "^5.5.4" } } From 3adda77c6e4688395545e21b9e2a9f30f557e5f2 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Wed, 4 Sep 2024 02:02:02 +0100 Subject: [PATCH 09/16] add TemplateVariables interface --- lib/comment.ts | 4 ++-- lib/config.ts | 4 ++-- types/global.type.ts | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/comment.ts b/lib/comment.ts index 85274c5..ed54cf5 100644 --- a/lib/comment.ts +++ b/lib/comment.ts @@ -1,5 +1,5 @@ import * as Mustache from 'mustache'; -import { Comment } from '../types/global.type'; +import {Comment, TemplateVariables} from '../types/global.type'; function commentMetadata(snippetIds: string[]): string { return ``; @@ -19,7 +19,7 @@ function extractCommentMetadata(commentBody: string): string[] | null { function assembleCommentBody( snippetIds: string[], commentConfig: Map, - templateVariables: Record = {}): string { + templateVariables: TemplateVariables = {}): string { let strings = [ commentConfig.get('header') as string | undefined, ...(commentConfig.get('snippets') as Map[]).map((snippet) => { diff --git a/lib/config.ts b/lib/config.ts index 18c5b9a..4a3adcc 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,7 +1,7 @@ import * as Mustache from 'mustache'; -import { CommentObject, ConfigObject, MatchConfig } from '../types/global.type'; +import {CommentObject, ConfigObject, MatchConfig, TemplateVariables} from '../types/global.type'; -function validateCommentConfig(configObject: ConfigObject, templateVariables?: Record): Map { +function validateCommentConfig(configObject: ConfigObject, templateVariables?: TemplateVariables): Map { const configMap = new Map(); const comment: CommentObject = configObject.comment; diff --git a/types/global.type.ts b/types/global.type.ts index 92d20b5..93872fa 100644 --- a/types/global.type.ts +++ b/types/global.type.ts @@ -41,4 +41,8 @@ export interface CommentConfig extends Map { export interface Snippet extends Map { get(key: 'id' | 'body'): string; get(key: 'files'): (string | MatchConfig)[]; -} \ No newline at end of file +} + +export interface TemplateVariables { + [key: string]: unknown; +} From 1712ba41d093c9764f4cb5fa6b315ae531fbeadb Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Wed, 4 Sep 2024 02:34:14 +0100 Subject: [PATCH 10/16] refactor tests to TS --- test/{comment.test.js => comment.test.ts} | 138 ++++++++++++++------ test/{config.test.js => config.test.ts} | 59 ++++++--- test/{run.test.js => run.test.ts} | 28 ++-- test/{snippets.test.js => snippets.test.ts} | 105 +++++++-------- 4 files changed, 211 insertions(+), 119 deletions(-) rename test/{comment.test.js => comment.test.ts} (66%) rename test/{config.test.js => config.test.ts} (86%) rename test/{run.test.js => run.test.ts} (76%) rename test/{snippets.test.js => snippets.test.ts} (85%) diff --git a/test/comment.test.js b/test/comment.test.ts similarity index 66% rename from test/comment.test.js rename to test/comment.test.ts index e39bce1..73d36ca 100644 --- a/test/comment.test.js +++ b/test/comment.test.ts @@ -1,17 +1,17 @@ -const comment = require('../lib/comment'); +import * as comment from '../lib/comment'; describe('comment', () => { describe('assembleCommentBody', () => { test('header, footer, and many snippets', () => { - const commentConfig = new Map([ + const commentConfig = new Map([ ['header', 'hello'], ['footer', 'bye'], ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['body', 'A list:\n- one\n- two\n- three'], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['body', 'Do not forget to be awesome!'], ]), @@ -35,15 +35,15 @@ describe('comment', () => { }); test('no header', () => { - const commentConfig = new Map([ + const commentConfig = new Map([ ['header', undefined], ['footer', 'bye'], ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['body', 'A list:\n- one\n- two\n- three'], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['body', 'Do not forget to be awesome!'], ]), @@ -59,15 +59,15 @@ describe('comment', () => { }); test('no footer', () => { - const commentConfig = new Map([ + const commentConfig = new Map([ ['header', 'hello'], ['footer', undefined], ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['body', 'A list:\n- one\n- two\n- three'], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['body', 'Do not forget to be awesome!'], ]), @@ -83,15 +83,15 @@ describe('comment', () => { }); test('no header and no footer', () => { - const commentConfig = new Map([ + const commentConfig = new Map([ ['header', undefined], ['footer', undefined], ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['body', 'A list:\n- one\n- two\n- three'], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['body', 'Do not forget to be awesome!'], ]), @@ -106,11 +106,11 @@ describe('comment', () => { }); test('supports Mustache templates', () => { - const commentConfig = new Map([ + const commentConfig = new Map([ ['header', 'hello {{user.firstName}}'], ['footer', 'bye {{user.firstName}}'], ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['body', 'A list:\n- one\n- two\n{{#shouldIncludeThirdItem}}- three\n{{/shouldIncludeThirdItem}}'], ]), @@ -143,7 +143,7 @@ describe('comment', () => { test('finds an empty array in the middle of a comment', () => { const commentBody = 'hello\nthere\n\nblabla'; - const expectedResult = []; + const expectedResult: unknown = []; expect(comment.extractCommentMetadata(commentBody)).toEqual(expectedResult); }); @@ -186,42 +186,54 @@ describe('comment', () => { describe('comment create/delete/edit logic', () => { test('no previous comment, no snippets detected', () => { - const previousComment = undefined; - const snippetIds = []; + const previousComment: unknown = undefined; + const snippetIds: string[] = []; - let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); }); @@ -230,39 +242,51 @@ describe('comment', () => { const previousComment = undefined; const snippetIds = ['snippet1', 'snippet2']; - let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); }); @@ -271,39 +295,51 @@ describe('comment', () => { const previousComment = { body: comment.commentMetadata(['snippet1', 'snippet2']) }; const snippetIds = ['snippet1', 'snippet2']; - let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'recreate']]); + commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); }); @@ -312,80 +348,104 @@ describe('comment', () => { const previousComment = { body: comment.commentMetadata(['snippet2']) }; const snippetIds = ['snippet1', 'snippet2']; - let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(true); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'recreate']]); + commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); }); test('a previous comment, new comment would have no snippets', () => { const previousComment = { body: comment.commentMetadata(['snippet2']) }; - const snippetIds = []; + const snippetIds: string[] = []; - let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + let commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'recreate']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'edit']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + commentConfig = new Map([['onCreate', 'create'], ['onUpdate', 'nothing']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); - commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'edit']]); + commentConfig = new Map([['onCreate', 'nothing'], ['onUpdate', 'edit']]); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldPostNewComment(previousComment, snippetIds, commentConfig)) .toEqual(false); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(true); + // @ts-expect-error: Intentional type violation for testing expect(comment.shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) .toEqual(false); }); diff --git a/test/config.test.js b/test/config.test.ts similarity index 86% rename from test/config.test.js rename to test/config.test.ts index 128179f..a7084e2 100644 --- a/test/config.test.js +++ b/test/config.test.ts @@ -1,4 +1,4 @@ -const config = require('../lib/config'); +import * as config from '../lib/config'; describe('validateCommentConfig', () => { const snippet1Object = { @@ -43,19 +43,19 @@ describe('validateCommentConfig', () => { }], }; - const snippet1Map = new Map([ + const snippet1Map = new Map([ ['id', 'snippet1'], ['body', 'let me tell you something...'], ['files', ['/foo/bar.txt', '/foo/baz/*']], ]); - const snippet2Map = new Map([ + const snippet2Map = new Map([ ['id', 'snippet2'], ['body', 'once upon a time...'], ['files', ['/pizzazz/**/*.html']], ]); - const snippet3Map = new Map([ + const snippet3Map = new Map([ ['id', 'snippet3'], ['body', 'there was a wolf...'], ['files', [{ @@ -63,7 +63,7 @@ describe('validateCommentConfig', () => { }]], ]); - const snippet4Map = new Map([ + const snippet4Map = new Map([ ['id', 'snippet4'], ['body', 'something something...'], ['files', [{ @@ -71,7 +71,7 @@ describe('validateCommentConfig', () => { }]], ]); - const snippet5Map = new Map([ + const snippet5Map = new Map([ ['id', 'snippet5'], ['body', 'something something...'], ['files', [{ @@ -91,7 +91,7 @@ describe('validateCommentConfig', () => { }, }; - const output = new Map([ + const output = new Map([ ['onCreate', 'nothing'], ['onUpdate', 'nothing'], ['header', 'hello'], @@ -114,7 +114,7 @@ describe('validateCommentConfig', () => { }, }; - const output = new Map([ + const output = new Map([ ['onCreate', 'create'], ['onUpdate', 'nothing'], ['header', 'hello'], @@ -135,7 +135,7 @@ describe('validateCommentConfig', () => { }, }; - const output = new Map([ + const output = new Map([ ['onCreate', 'create'], ['onUpdate', 'edit'], ['header', 'hi'], @@ -155,7 +155,7 @@ describe('validateCommentConfig', () => { }, }; - const output = new Map([ + const output = new Map([ ['onCreate', 'create'], ['onUpdate', 'edit'], ['header', undefined], @@ -163,6 +163,7 @@ describe('validateCommentConfig', () => { ['snippets', [snippet2Map]], ]); + // @ts-expect-error: Intentional type violation for testing expect(config.validateCommentConfig(input)).toEqual(output); }); @@ -175,7 +176,7 @@ describe('validateCommentConfig', () => { }, }; - const output = new Map([ + const output = new Map([ ['onCreate', 'create'], ['onUpdate', 'edit'], ['header', 'hello'], @@ -183,6 +184,7 @@ describe('validateCommentConfig', () => { ['snippets', [snippet2Map]], ]); + // @ts-expect-error: Intentional type violation for testing expect(config.validateCommentConfig(input)).toEqual(output); }); @@ -193,6 +195,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'number' under key '\.comment\.header' \(should be a string\)/); }); @@ -203,6 +206,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'number' under key '\.comment\.footer' \(should be a string\)/); }); @@ -213,6 +217,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value 'sup' under key '\.comment\.on-create' \(should be one of: create, nothing\)/); }); @@ -223,6 +228,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'object' under key '\.comment\.on-create' \(should be a string\)/); }); @@ -238,7 +244,7 @@ describe('validateCommentConfig', () => { const templateVariables = { onCreate: 'nothing' }; - const output = new Map([ + const output = new Map([ ['onCreate', 'nothing'], ['onUpdate', 'recreate'], ['header', 'hi'], @@ -258,6 +264,7 @@ describe('validateCommentConfig', () => { const templateVariables = { onCreate: '1234' }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input, templateVariables)).toThrow(/found unexpected value '1234' under key '\.comment\.on-create' \(should be one of: create, nothing\)/); }); @@ -268,6 +275,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value 'whatever' under key '\.comment\.on-update' \(should be one of: recreate, edit, nothing\)/); }); @@ -278,6 +286,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'number' under key '\.comment\.on-update' \(should be a string\)/); }); @@ -293,7 +302,7 @@ describe('validateCommentConfig', () => { const templateVariables = { onUpdate: 'nothing' }; - const output = new Map([ + const output = new Map([ ['onCreate', 'create'], ['onUpdate', 'nothing'], ['header', 'hi'], @@ -313,6 +322,7 @@ describe('validateCommentConfig', () => { const templateVariables = { onUpdate: 'cat' }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input, templateVariables)).toThrow(/found unexpected value 'cat' under key '\.comment\.on-update' \(should be one of: recreate, edit, nothing\)/); }); @@ -327,7 +337,7 @@ describe('validateCommentConfig', () => { }, }; - const output = new Map([ + const output = new Map([ ['onCreate', 'create'], ['onUpdate', 'recreate'], ['header', undefined], @@ -339,6 +349,7 @@ describe('validateCommentConfig', () => { ['snippets', [snippet1Map]], ]); + // @ts-expect-error: Intentional type violation for testing expect(config.validateCommentConfig(input)).toEqual(output); }); @@ -347,6 +358,7 @@ describe('validateCommentConfig', () => { comment: {}, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets' \(should be a non-empty array\)/); }); @@ -357,6 +369,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets' \(should be a non-empty array\)/); }); @@ -367,6 +380,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets' \(should be a non-empty array\)/); }); @@ -382,6 +396,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'undefined' under key '\.comment\.snippets\.0\.id' \(should be a string\)/); }); @@ -398,6 +413,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found invalid snippet id 'can this have spaces\?' \(snippet ids must contain only letters, numbers, dashes, and underscores\)/); }); @@ -416,13 +432,13 @@ describe('validateCommentConfig', () => { const templateVariables = { date: '2021-08-27' }; - const snippets = [new Map([ + const snippets = [new Map([ ['id', 'snippet-2021-08-27'], ['body', 'something something...'], ['files', ['foo.txt']], ])]; - const expected = new Map([ + const expected = new Map([ ['footer', undefined], ['header', undefined], ['onCreate', 'create'], @@ -430,6 +446,7 @@ describe('validateCommentConfig', () => { ['snippets', snippets], ]); + // @ts-expect-error: Intentional type violation for testing expect(config.validateCommentConfig(input, templateVariables)).toEqual(expected); }); @@ -448,6 +465,7 @@ describe('validateCommentConfig', () => { const templateVariables = { date: '2021 08 27' }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input, templateVariables)).toThrow(/found invalid snippet id 'snippet-2021 08 27' \(snippet ids must contain only letters, numbers, dashes, and underscores\)/); }); @@ -473,6 +491,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'undefined' under key '\.comment\.snippets\.1\.id' \(should be a string\)/); }); @@ -488,6 +507,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type 'undefined' under key '\.comment\.snippets\.0\.body' \(should be a string\)/); }); @@ -503,6 +523,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets\.0\.files' \(should be a non-empty array\)/); }); @@ -519,6 +540,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets\.0\.files' \(should be a non-empty array\)/); }); @@ -535,6 +557,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets\.0\.files\.1' \(should be a string or an object with keys 'all' and\/or 'any'\)/); }); @@ -553,6 +576,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets\.0\.files\.0' \(should be a string or an object with keys 'all' and\/or 'any'\)/); }); @@ -573,6 +597,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found unexpected value type under key '\.comment\.snippets\.0\.files\.2' \(should be a string or an object with keys 'all' and\/or 'any'\)/); }); @@ -594,6 +619,7 @@ describe('validateCommentConfig', () => { }, }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input)).toThrow(/found duplicate snippet id 'foo'/); }); @@ -617,6 +643,7 @@ describe('validateCommentConfig', () => { const variableTemplates = { number: 5, id: 5 }; + // @ts-expect-error: Intentional type violation for testing expect(() => config.validateCommentConfig(input, variableTemplates)).toThrow(/found duplicate snippet id 'snippet-5'/); }); }); diff --git a/test/run.test.js b/test/run.test.ts similarity index 76% rename from test/run.test.js rename to test/run.test.ts index 6d71407..405ed55 100644 --- a/test/run.test.js +++ b/test/run.test.ts @@ -1,9 +1,9 @@ -const core = require('@actions/core'); -const github = require('@actions/github'); -const localGithub = require('../lib/github'); -const comment = require('../lib/comment'); +import * as core from '@actions/core'; +import * as github from '@actions/github'; +import * as localGithub from '../lib/github'; +import * as comment from '../lib/comment'; -const { run } = require('../lib/run'); +import { run } from '../lib/run'; const fakePRNumber = 432; @@ -16,8 +16,12 @@ jest.mock('@actions/github', () => ({ jest.mock('../lib/github'); -describe('run', () => { - test('fully-mocked recreating a comment happy path', async () => { +describe('run', (): void => { + beforeEach(() => { + github.context.payload = { pull_request: { number: fakePRNumber } }; // Ensure the mock context is correct + }); + + test('fully-mocked recreating a comment happy path', async (): Promise => { const fakeToken = 'github-token-123456'; const fakeConfigPath = 'foo/config-file.yml'; const fakeConfig = 'comment:\n' @@ -40,7 +44,7 @@ describe('run', () => { + ' files:\n' + ' - static/**.css\n'; - core.getInput.mockImplementation((argument) => { + (core.getInput as jest.Mock).mockImplementation((argument: string): string | null => { if (argument === 'github-token') { return fakeToken; } @@ -74,14 +78,14 @@ describe('run', () => { + 'Bye!\n\n'}${ comment.commentMetadata(['snippet1', 'snippet3'])}`; - localGithub.getChangedFiles.mockResolvedValue(['static/foo.html', 'README.md', 'static/foo.css']); - localGithub.getFileContent.mockResolvedValue(fakeConfig); - localGithub.getComments.mockResolvedValue(existingPRComments); + (localGithub.getChangedFiles as jest.Mock).mockResolvedValue(['static/foo.html', 'README.md', 'static/foo.css']); + (localGithub.getFileContent as jest.Mock).mockResolvedValue(fakeConfig); + (localGithub.getComments as jest.Mock).mockResolvedValue(existingPRComments); await run(); expect(github.getOctokit).toHaveBeenCalledTimes(1); - expect(github.getOctokit.mock.calls[0][0]).toEqual(fakeToken); + expect((github.getOctokit as jest.Mock).mock.calls[0][0]).toEqual(fakeToken); expect(localGithub.getChangedFiles).toHaveBeenCalledTimes(1); expect(localGithub.getChangedFiles).toHaveBeenCalledWith({ name: 'fake-client' }, fakePRNumber); diff --git a/test/snippets.test.js b/test/snippets.test.ts similarity index 85% rename from test/snippets.test.js rename to test/snippets.test.ts index 511154b..c9e90e9 100644 --- a/test/snippets.test.js +++ b/test/snippets.test.ts @@ -1,4 +1,5 @@ -const snippets = require('../lib/snippets'); +import * as snippets from '../lib/snippets'; +import {CommentConfig} from "../types/global.type"; jest.mock('@actions/core'); @@ -6,14 +7,14 @@ describe('getMatchingSnippetIds', () => { test('no matches', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['foo/**/*.txt']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['foo/bar/baz.html', 'foo/bar.html', 'notfoo/foo/banana.txt']; - const expectedResult = []; + const expectedResult: unknown = []; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -22,12 +23,12 @@ describe('getMatchingSnippetIds', () => { test('a match, one pattern', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['foo/**/*.txt']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['foo/bar/baz.txt', 'foo/bar.html', 'notfoo/foo/banana.txt']; const expectedResult = ['snippet1']; @@ -38,12 +39,12 @@ describe('getMatchingSnippetIds', () => { test('negated pattern - at least one changed file must not match', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['!**/*.txt']], ]), ]], - ]); + ]) as CommentConfig; let actualResult = snippets.getMatchingSnippetIds(['foo/bar.txt', 'foo/bar/baz.txt'], config); expect(actualResult).toEqual([]); @@ -55,14 +56,14 @@ describe('getMatchingSnippetIds', () => { test('single * does not match nested dirs', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['*.txt']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['foo/bar.txt', 'foo/bar/baz.txt']; - const expectedResult = []; + const expectedResult: unknown = []; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -71,14 +72,14 @@ describe('getMatchingSnippetIds', () => { test('double ** matches files in root dir', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['**/*.txt']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['bar.txt']; - const expectedResult = ['snippet1']; + const expectedResult: unknown = ['snippet1']; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -87,14 +88,14 @@ describe('getMatchingSnippetIds', () => { test('* does not match hidden files by default, unless an option is passed', () => { let config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['*.txt']], ]), ]], - ]); + ]) as CommentConfig; let changedFiles = ['.hidden.txt']; - let expectedResult = []; + let expectedResult: unknown = []; let actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -111,14 +112,14 @@ describe('getMatchingSnippetIds', () => { test('a match, many patterns', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['README.md', 'foo/*.html', 'foo/**/*.txt']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['foo/bar/baz.txt', 'foo/bar.html', 'notfoo/foo/banana.txt']; - const expectedResult = ['snippet1']; + const expectedResult: unknown = ['snippet1']; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -127,22 +128,22 @@ describe('getMatchingSnippetIds', () => { test('a match, many patterns, many snippets', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['README.md']], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['files', ['**/*.html', '**/*.css']], ]), - new Map([ + new Map([ ['id', 'snippet3'], ['files', ['foo/**/*']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['bar/1.txt', 'bar/2.txt', 'bar/index.html']; - const expectedResult = ['snippet2']; + const expectedResult: unknown = ['snippet2']; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -151,22 +152,22 @@ describe('getMatchingSnippetIds', () => { test('a match, many patterns, many snippets - the match matches multiple patterns', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['README.md']], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['files', ['**/*.html', '**/*.css']], ]), - new Map([ + new Map([ ['id', 'snippet3'], ['files', ['foo/**/*']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['foo/index.html']; - const expectedResult = ['snippet2', 'snippet3']; + const expectedResult: unknown = ['snippet2', 'snippet3']; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -175,22 +176,22 @@ describe('getMatchingSnippetIds', () => { test('many matches, many patterns, many snippets', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['README.md']], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['files', ['**/*.html', '**/*.css']], ]), - new Map([ + new Map([ ['id', 'snippet3'], ['files', ['foo/**/*']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['README.md', 'bar/1.txt', 'bar/2.txt', 'foo/HELLO.md']; - const expectedResult = ['snippet1', 'snippet3']; + const expectedResult: unknown = ['snippet1', 'snippet3']; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -199,22 +200,22 @@ describe('getMatchingSnippetIds', () => { test('snippets are always returned in the same order as they are in the config', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', ['README.md']], ]), - new Map([ + new Map([ ['id', 'snippet2'], ['files', ['**/*.md']], ]), - new Map([ + new Map([ ['id', 'snippet3'], ['files', ['foo/**/*']], ]), ]], - ]); + ]) as CommentConfig; const changedFiles = ['foo/HELLO.txt', 'README.md']; - const expectedResult = ['snippet1', 'snippet2', 'snippet3']; + const expectedResult: unknown = ['snippet1', 'snippet2', 'snippet3']; const actualResult = snippets.getMatchingSnippetIds(changedFiles, config); expect(actualResult).toEqual(expectedResult); @@ -223,12 +224,12 @@ describe('getMatchingSnippetIds', () => { test('patterns using the "all" option - ALL changed files must match ALL of the patterns', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', [{ all: ['**/*.html', 'static/*'] }]], ]), ]], - ]); + ]) as CommentConfig; expect(snippets.getMatchingSnippetIds(['static/index.html'], config)).toEqual(['snippet1']); expect(snippets.getMatchingSnippetIds(['static/about.html'], config)).toEqual(['snippet1']); @@ -240,12 +241,12 @@ describe('getMatchingSnippetIds', () => { test('negated pattern using the "all" option - NONE changed files can match', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', [{ all: ['!**/*.css'] }]], ]), ]], - ]); + ]) as CommentConfig; expect(snippets.getMatchingSnippetIds(['static/index.html'], config)).toEqual(['snippet1']); expect(snippets.getMatchingSnippetIds(['static/about.html'], config)).toEqual(['snippet1']); @@ -257,12 +258,12 @@ describe('getMatchingSnippetIds', () => { test('patterns using the "any" option - ONE of the changed files must match ALL of the patterns', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', [{ any: ['**/*.html', 'static/*'] }]], ]), ]], - ]); + ]) as CommentConfig; expect(snippets.getMatchingSnippetIds(['static/index.html'], config)).toEqual(['snippet1']); expect(snippets.getMatchingSnippetIds(['static/index.css'], config)).toEqual([]); @@ -274,7 +275,7 @@ describe('getMatchingSnippetIds', () => { test('patterns using both the "all" and "any" option', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', [ { @@ -284,7 +285,7 @@ describe('getMatchingSnippetIds', () => { ]], ]), ]], - ]); + ]) as CommentConfig; expect(snippets.getMatchingSnippetIds(['static/foo/index.html'], config)).toEqual(['snippet1']); expect(snippets.getMatchingSnippetIds(['static/foo/bar/index.html'], config)).toEqual(['snippet1']); @@ -296,7 +297,7 @@ describe('getMatchingSnippetIds', () => { test('patterns using both the "all" and "any" option or a simple matcher', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', [ 'README.md', @@ -307,7 +308,7 @@ describe('getMatchingSnippetIds', () => { ]], ]), ]], - ]); + ]) as CommentConfig; expect(snippets.getMatchingSnippetIds(['static/foo/index.html'], config)).toEqual(['snippet1']); expect(snippets.getMatchingSnippetIds(['static/foo/bar/index.html'], config)).toEqual(['snippet1']); @@ -325,7 +326,7 @@ describe('getMatchingSnippetIds', () => { test('patterns using the "all" and "any" option can be modified with globOptions', () => { const config = new Map([ ['snippets', [ - new Map([ + new Map([ ['id', 'snippet1'], ['files', [ { @@ -336,7 +337,7 @@ describe('getMatchingSnippetIds', () => { ]), ]], ['globOptions', { nocase: true }], - ]); + ]) as CommentConfig; expect(snippets.getMatchingSnippetIds(['static/foo/bar/index.html'], config)).toEqual(['snippet1']); expect(snippets.getMatchingSnippetIds(['static/FOO/bar/index.html'], config)).toEqual(['snippet1']); From eee971d0adb7605e48cfcab32afe60834ebd9a2e Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Wed, 4 Sep 2024 02:34:57 +0100 Subject: [PATCH 11/16] update package.json scripts for TS files --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 152ebe8..5d728aa 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "build": "ncc build lib/index.js --license licenses.txt", - "lint": "eslint lib/**/*.js test/**/*.js", + "build": "ncc build lib/index.ts --license licenses.txt", + "lint": "eslint lib/**/*.ts test/**/*.ts", "test": "jest" }, "repository": { From 98fe87165ff0b9f879d337efdd1c6da07958fed1 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Wed, 4 Sep 2024 02:35:39 +0100 Subject: [PATCH 12/16] refactor run to fix failing test --- lib/run.ts | 99 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/lib/run.ts b/lib/run.ts index 21feb48..af4eaf4 100644 --- a/lib/run.ts +++ b/lib/run.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core'; import * as github from '@actions/github'; import * as yaml from 'js-yaml'; -import { Comment, CommentConfig, ConfigObject } from '../types/global.type'; +import {Comment, CommentConfig, ConfigObject, TemplateVariables} from '../types/global.type'; import { validateCommentConfig } from './config'; import { getMatchingSnippetIds } from './snippets'; import { @@ -22,55 +22,62 @@ import { } from './github'; async function run(): Promise { - const token = core.getInput('github-token', { required: true }); - const configPath = core.getInput('config-file', { required: true }); - const templateVariablesJSONString = core.getInput('template-variables', { required: false }); - - const prNumber = getPrNumber(); - if (prNumber === undefined) { - console.log('Could not get pull request number from context, exiting'); - return; - } - - // eslint-disable-next-line new-cap - const client = github.getOctokit(token); - - core.debug(`fetching changed files for pr #${prNumber}`); - const changedFiles = await getChangedFiles(client, prNumber); - const previousComment = await getPreviousPRComment(client, prNumber); - - let templateVariables: Record = {}; - if (templateVariablesJSONString) { - core.debug('Input template-variables was passed'); - core.debug(templateVariablesJSONString); + try { + const token = core.getInput('github-token', {required: true}); + const configPath = core.getInput('config-file', {required: true}); + const templateVariablesJSONString = core.getInput('template-variables', {required: false}); + + const prNumber = getPrNumber(); + if (prNumber === undefined) { + console.log('Could not get pull request number from context, exiting'); + return; + } - try { - templateVariables = JSON.parse(templateVariablesJSONString); - } catch (error) { - core.warning('Failed to parse template-variables input as JSON. Continuing without template variables.'); + // eslint-disable-next-line new-cap + const client = github.getOctokit(token); + + core.debug(`fetching changed files for pr #${prNumber}`); + const changedFiles = await getChangedFiles(client, prNumber); + const previousComment = await getPreviousPRComment(client, prNumber); + + let templateVariables: TemplateVariables = {}; + if (templateVariablesJSONString) { + core.debug('Input template-variables was passed'); + core.debug(templateVariablesJSONString); + + try { + templateVariables = JSON.parse(templateVariablesJSONString); + } catch (error) { + core.warning('Failed to parse template-variables input as JSON. Continuing without template variables.'); + } + } else { + core.debug('Input template-variables was not passed'); } - } else { - core.debug('Input template-variables was not passed'); - } - const commentConfig = (await getCommentConfig(client, configPath, templateVariables)) as CommentConfig; - const snippetIds = getMatchingSnippetIds(changedFiles, commentConfig); + const commentConfig = (await getCommentConfig(client, configPath, templateVariables)); + const snippetIds = getMatchingSnippetIds(changedFiles, commentConfig); - if (previousComment && shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) { - core.info('removing previous comment'); - await deleteComment(client, previousComment); - } + if (previousComment && shouldDeletePreviousComment(previousComment, snippetIds, commentConfig)) { + core.info('removing previous comment'); + await deleteComment(client, previousComment); + } - const commentBody = assembleCommentBody(snippetIds, commentConfig, templateVariables); + const commentBody = assembleCommentBody(snippetIds, commentConfig, templateVariables); - if (previousComment && shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) { - core.info('updating previous comment'); - await editComment(client, previousComment, commentBody); - } + if (previousComment && shouldEditPreviousComment(previousComment, snippetIds, commentConfig)) { + core.info('updating previous comment'); + await editComment(client, previousComment, commentBody); + } - if (shouldPostNewComment(previousComment, snippetIds, commentConfig)) { - core.info('creating a new comment'); - await createComment(client, prNumber, commentBody); + if (shouldPostNewComment(previousComment, snippetIds, commentConfig)) { + core.info('creating a new comment'); + await createComment(client, prNumber, commentBody); + } + } catch (error: unknown) { + if (error instanceof Error) { + core.error(error); + core.setFailed((error as Error).message); + } } } @@ -99,7 +106,7 @@ function isConfigObject(obj: unknown): obj is ConfigObject { return !(!Array.isArray(comment.snippets) || comment.snippets.length === 0); } -async function getCommentConfig(client: ReturnType, configurationPath: string, templateVariables: Record): Promise { +async function getCommentConfig(client: ReturnType, configurationPath: string, templateVariables: TemplateVariables): Promise { const configurationContent = await getFileContent(client, configurationPath); const configObject = yaml.load(configurationContent); @@ -126,11 +133,9 @@ async function getPreviousPRComment(client: ReturnType core.info(`found previous comment made by pr-commenter: ${previousComment.url}`); core.info(`extracted snippet ids from previous comment: ${previousSnippetIds.join(', ')}`); } - - await previousComment; } - return null; + return previousComment ?? null; } export { run }; From b6dcc20f59527675cf05ebee5d8085c584f64944 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Thu, 3 Oct 2024 17:10:42 +0100 Subject: [PATCH 13/16] [WIP] refactor with PR comments --- .eslintrc.json | 7 ++----- .gitignore | 3 +-- lib/comment.ts | 36 ++++++++++++++++++++++++--------- lib/config.ts | 39 ++++++++++++++++++++++++++++-------- lib/github.ts | 4 ++-- lib/run.ts | 53 +++++++++++++++++++++++++------------------------ lib/snippets.ts | 17 +++++++++++++--- 7 files changed, 104 insertions(+), 55 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2235fbb..3f0bacb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,7 @@ { "parser": "@typescript-eslint/parser", "env": { - "browser": true, - "commonjs": true, - "es2021": true, + "node": true, "jest": true }, "extends": [ @@ -13,8 +11,7 @@ ], "parserOptions": { "project": "./tsconfig.json", - "ecmaVersion": 2020, - "sourceType": "module" + "ecmaVersion": 2015 }, "rules": { "@typescript-eslint/no-explicit-any": "error", diff --git a/.gitignore b/.gitignore index d35bbf7..40b878d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -node_modules/ -.idea/ \ No newline at end of file +node_modules/ \ No newline at end of file diff --git a/lib/comment.ts b/lib/comment.ts index ed54cf5..865c31f 100644 --- a/lib/comment.ts +++ b/lib/comment.ts @@ -1,5 +1,23 @@ import * as Mustache from 'mustache'; -import {Comment, TemplateVariables} from '../types/global.type'; +import {MinimatchOptions} from 'minimatch/dist/esm'; +import { Snippet } from './snippets'; +import { TemplateVariables } from "./config"; + +export type Comment = { + id: number; + url: string; + created_at: string; + body?: string; + body_text?: string; + body_html?: string; +} + +export interface CommentObject extends Map{ + get(key: 'header' | 'footer' | 'onCreate' | 'onUpdate'): string; + get(key: 'snippets'): Snippet[], + get(key: 'on-create' | 'on-update' | 'globOptions'): Map[]; + get(key: 'globOptions'): MinimatchOptions | undefined; +} function commentMetadata(snippetIds: string[]): string { return ``; @@ -18,13 +36,13 @@ function extractCommentMetadata(commentBody: string): string[] | null { function assembleCommentBody( snippetIds: string[], - commentConfig: Map, + commentConfig: CommentObject, templateVariables: TemplateVariables = {}): string { let strings = [ - commentConfig.get('header') as string | undefined, - ...(commentConfig.get('snippets') as Map[]).map((snippet) => { - if (snippetIds.includes(snippet.get('id') as string)) { - return snippet.get('body') as string; + commentConfig.get('header'), + ...(commentConfig.get('snippets')).map(snippet => { + if (snippetIds.includes(snippet.get('id'))) { + return snippet.get('body'); } return null; }), @@ -52,7 +70,7 @@ function newCommentWouldHaveContent(snippetIds: string[]): boolean { function shouldPostNewComment( previousComment: Comment | null, snippetIds: string[], - commentConfig: Map + commentConfig: CommentObject ): boolean { const isNotEmpty = newCommentWouldHaveContent(snippetIds); const isCreating = !previousComment && commentConfig.get('onCreate') === 'create'; @@ -66,7 +84,7 @@ function shouldPostNewComment( function shouldDeletePreviousComment( previousComment: Comment | null, snippetIds: string[], - commentConfig: Map + commentConfig: CommentObject ): boolean { return !!previousComment && ( shouldPostNewComment(previousComment, snippetIds, commentConfig) @@ -77,7 +95,7 @@ function shouldDeletePreviousComment( function shouldEditPreviousComment( previousComment: Comment | null, snippetIds: string[], - commentConfig: Map + commentConfig: CommentObject ): boolean { return newCommentWouldHaveContent(snippetIds) && ( !!previousComment diff --git a/lib/config.ts b/lib/config.ts index 4a3adcc..9ee3aac 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,9 +1,32 @@ import * as Mustache from 'mustache'; -import {CommentObject, ConfigObject, MatchConfig, TemplateVariables} from '../types/global.type'; +import { CommentObject } from './comment'; +import { Snippet } from "./snippets"; -function validateCommentConfig(configObject: ConfigObject, templateVariables?: TemplateVariables): Map { - const configMap = new Map(); - const comment: CommentObject = configObject.comment; +export type MatchConfig = { + any?: string[]; + all?: string[]; +}; + +export type TemplateVariables = { + [key: string]: unknown; +} + +export type Config = { + comment: CommentConfig; +}; + +interface CommentConfig extends CommentObject { + header: string | null; + footer: string | null; + snippets: Snippet[]; + 'on-create'?: string | null; + 'on-update'?: string | null; + 'glob-options'?: object; +} + +function validateCommentConfig(configObject: Config, templateVariables?: TemplateVariables): Map { + const configMap: CommentObject = new Map(); + const comment: CommentConfig = configObject.comment; if (typeof comment !== 'object') { throw Error( @@ -25,7 +48,7 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables?: T } else if (typeof comment['on-create'] === 'string') { const onCreate = Mustache.render(comment['on-create'], templateVariables); - if (allowedOnCreateValues.includes(onCreate as typeof allowedOnCreateValues[number])) { + if (allowedOnCreateValues.includes(onCreate)) { configMap.set('onCreate', onCreate); } else { throw Error( @@ -38,13 +61,13 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables?: T ); } - const allowedOnUpdateValues = ['recreate', 'edit', 'nothing'] as const; + const allowedOnUpdateValues = ['recreate', 'edit', 'nothing']; if (comment['on-update'] === undefined || comment['on-update'] === null) { configMap.set('onUpdate', allowedOnUpdateValues[0]); } else if (typeof comment['on-update'] === 'string') { const onUpdate = Mustache.render(comment['on-update'], templateVariables); - if (allowedOnUpdateValues.includes(onUpdate as typeof allowedOnUpdateValues[number])) { + if (allowedOnUpdateValues.includes(onUpdate)) { configMap.set('onUpdate', onUpdate); } else { throw Error( @@ -142,7 +165,7 @@ function validateCommentConfig(configObject: ConfigObject, templateVariables?: T return snippetMap; })); - const snippetIds = (configMap.get('snippets') as Map[]).map((s: Map) => s.get('id') as string); + const snippetIds = (configMap.get('snippets')).map((s) => s.get('id')); snippetIds.forEach((value: string, index: number, self: string[]) => { if (self.indexOf(value) !== index) { throw Error( diff --git a/lib/github.ts b/lib/github.ts index 8e133cf..27ec267 100644 --- a/lib/github.ts +++ b/lib/github.ts @@ -1,8 +1,8 @@ import * as github from '@actions/github'; import * as core from '@actions/core'; -import { Comment } from '../types/global.type'; +import { Comment } from './comment'; -type OctokitClient = ReturnType; +export type OctokitClient = ReturnType; type RemoteDefinition = { owner: string; diff --git a/lib/run.ts b/lib/run.ts index af4eaf4..bed58e4 100644 --- a/lib/run.ts +++ b/lib/run.ts @@ -1,9 +1,10 @@ import * as core from '@actions/core'; import * as github from '@actions/github'; import * as yaml from 'js-yaml'; -import {Comment, CommentConfig, ConfigObject, TemplateVariables} from '../types/global.type'; -import { validateCommentConfig } from './config'; +import { Comment, CommentObject } from './comment'; +import { Config, TemplateVariables, validateCommentConfig } from './config'; import { getMatchingSnippetIds } from './snippets'; +import { OctokitClient } from './github'; import { assembleCommentBody, extractCommentMetadata, @@ -76,7 +77,7 @@ async function run(): Promise { } catch (error: unknown) { if (error instanceof Error) { core.error(error); - core.setFailed((error as Error).message); + core.setFailed((error).message); } } } @@ -90,35 +91,35 @@ function getPrNumber(): number | undefined { return pullRequest.number; } -function isConfigObject(obj: unknown): obj is ConfigObject { - if (typeof obj !== 'object' || obj === null) { - return false; - } - - const config = obj as Record; - - if (typeof config.comment !== 'object' || config.comment === null) { - return false; - } - - const comment = config.comment as Record; - - return !(!Array.isArray(comment.snippets) || comment.snippets.length === 0); -} - -async function getCommentConfig(client: ReturnType, configurationPath: string, templateVariables: TemplateVariables): Promise { +// function isConfigObject(obj: unknown): obj is Config { +// if (typeof obj !== 'object' || obj === null) { +// return false; +// } +// +// const config = obj as Record; +// +// if (typeof config.comment !== 'object' || config.comment === null) { +// return false; +// } +// +// const comment = config.comment as Record; +// +// return !(!Array.isArray(comment.snippets) || comment.snippets.length === 0); +// } + +async function getCommentConfig(client: OctokitClient, configurationPath: string, templateVariables: TemplateVariables): Promise { const configurationContent = await getFileContent(client, configurationPath); - const configObject = yaml.load(configurationContent); + const configObject = yaml.load(configurationContent) as Config; - if (!isConfigObject(configObject)) { - throw new Error('Invalid configuration object structure'); - } + // if (!isConfigObject(configObject)) { + // throw new Error('Invalid configuration object structure'); + // } // transform object to a map or throw if yaml is malformed: - return validateCommentConfig(configObject, templateVariables) as CommentConfig; + return validateCommentConfig(configObject, templateVariables); } -async function getPreviousPRComment(client: ReturnType, prNumber: number): Promise { +async function getPreviousPRComment(client: OctokitClient, prNumber: number): Promise { const comments = await getComments(client, prNumber); core.debug(`there are ${comments.length} comments on the PR #${prNumber}`); diff --git a/lib/snippets.ts b/lib/snippets.ts index 3df8a2f..fa0f32f 100644 --- a/lib/snippets.ts +++ b/lib/snippets.ts @@ -1,12 +1,23 @@ import * as core from '@actions/core'; import { Minimatch, MinimatchOptions } from "minimatch"; -import { CommentConfig, MatchConfig } from "../types/global.type"; +import { CommentObject } from "./comment"; +import { MatchConfig } from "./config"; -function getMatchingSnippetIds(changedFiles: string[], commentConfig: CommentConfig): string[] { +type files = (string | MatchConfig)[]; + +export type Snippet = { + id: string; + body: string; + get(key: 'id' | 'body'): string; + files: files; + get(key: 'files'): files; +}; + +function getMatchingSnippetIds(changedFiles: string[], commentConfig: CommentObject): string[] { const snippetIds = commentConfig.get('snippets').reduce((acc, snippet): string[] => { core.debug(`processing snippet ${snippet.get('id')}`); - if (checkGlobs(changedFiles, snippet.get('files') as [], commentConfig.get('globOptions') || {})) { + if (checkGlobs(changedFiles, snippet.get('files'), commentConfig.get('globOptions') || {})) { return [...acc, snippet.get('id') as string]; } return acc; From dc601e327a87334ea92bfe98730880787c935234 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Fri, 4 Oct 2024 01:44:38 +0100 Subject: [PATCH 14/16] make final updates: tighten type definitions --- .gitignore | 2 +- lib/config.ts | 4 ++-- lib/run.ts | 20 ------------------ lib/snippets.ts | 2 +- types/global.type.ts | 48 -------------------------------------------- 5 files changed, 4 insertions(+), 72 deletions(-) delete mode 100644 types/global.type.ts diff --git a/.gitignore b/.gitignore index 40b878d..c2658d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -node_modules/ \ No newline at end of file +node_modules/ diff --git a/lib/config.ts b/lib/config.ts index 9ee3aac..7728689 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -15,7 +15,7 @@ export type Config = { comment: CommentConfig; }; -interface CommentConfig extends CommentObject { +export interface CommentConfig extends CommentObject { header: string | null; footer: string | null; snippets: Snippet[]; @@ -24,7 +24,7 @@ interface CommentConfig extends CommentObject { 'glob-options'?: object; } -function validateCommentConfig(configObject: Config, templateVariables?: TemplateVariables): Map { +function validateCommentConfig(configObject: Config, templateVariables?: TemplateVariables): CommentObject { const configMap: CommentObject = new Map(); const comment: CommentConfig = configObject.comment; diff --git a/lib/run.ts b/lib/run.ts index bed58e4..f5bb888 100644 --- a/lib/run.ts +++ b/lib/run.ts @@ -91,30 +91,10 @@ function getPrNumber(): number | undefined { return pullRequest.number; } -// function isConfigObject(obj: unknown): obj is Config { -// if (typeof obj !== 'object' || obj === null) { -// return false; -// } -// -// const config = obj as Record; -// -// if (typeof config.comment !== 'object' || config.comment === null) { -// return false; -// } -// -// const comment = config.comment as Record; -// -// return !(!Array.isArray(comment.snippets) || comment.snippets.length === 0); -// } - async function getCommentConfig(client: OctokitClient, configurationPath: string, templateVariables: TemplateVariables): Promise { const configurationContent = await getFileContent(client, configurationPath); const configObject = yaml.load(configurationContent) as Config; - // if (!isConfigObject(configObject)) { - // throw new Error('Invalid configuration object structure'); - // } - // transform object to a map or throw if yaml is malformed: return validateCommentConfig(configObject, templateVariables); } diff --git a/lib/snippets.ts b/lib/snippets.ts index fa0f32f..244b24b 100644 --- a/lib/snippets.ts +++ b/lib/snippets.ts @@ -18,7 +18,7 @@ function getMatchingSnippetIds(changedFiles: string[], commentConfig: CommentObj core.debug(`processing snippet ${snippet.get('id')}`); if (checkGlobs(changedFiles, snippet.get('files'), commentConfig.get('globOptions') || {})) { - return [...acc, snippet.get('id') as string]; + return [...acc, snippet.get('id')]; } return acc; }, []); diff --git a/types/global.type.ts b/types/global.type.ts deleted file mode 100644 index 93872fa..0000000 --- a/types/global.type.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { MinimatchOptions } from "minimatch"; - -export type ConfigObject = { - comment: CommentObject; -}; - -export type CommentObject = { - header: string | null; - footer: string | null; - snippets: SnippetObject[]; - 'on-create'?: string | null; - 'on-update'?: string | null; - 'glob-options'?: object; -} - -export type SnippetObject = { - id: string; - body: string; - files: (string | MatchConfig)[]; -}; - -export type Comment = { - id: number; - url: string; - created_at: string; - body?: string; - body_text?: string; - body_html?: string; -} - -export type MatchConfig = { - any?: string[]; - all?: string[]; -}; - -export interface CommentConfig extends Map { - get(key: 'on-create' | 'on-update' | 'globOptions' | 'snippets'): Map[]; - get(key: 'globOptions'): MinimatchOptions | undefined; -} - -export interface Snippet extends Map { - get(key: 'id' | 'body'): string; - get(key: 'files'): (string | MatchConfig)[]; -} - -export interface TemplateVariables { - [key: string]: unknown; -} From 25125c504f68cd6d3e68911a1b16201f967c16c6 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Fri, 4 Oct 2024 01:45:06 +0100 Subject: [PATCH 15/16] update tests --- test/comment.test.ts | 11 ++++++----- test/config.test.ts | 12 +++++++----- test/snippets.test.ts | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/test/comment.test.ts b/test/comment.test.ts index 73d36ca..3144699 100644 --- a/test/comment.test.ts +++ b/test/comment.test.ts @@ -1,4 +1,5 @@ import * as comment from '../lib/comment'; +import {CommentObject} from "../lib/comment"; describe('comment', () => { describe('assembleCommentBody', () => { @@ -16,7 +17,7 @@ describe('comment', () => { ['body', 'Do not forget to be awesome!'], ]), ]], - ]); + ]) as CommentObject; expect(comment.assembleCommentBody(['snippet1'], commentConfig)).toEqual( 'hello\n\n' @@ -48,7 +49,7 @@ describe('comment', () => { ['body', 'Do not forget to be awesome!'], ]), ]], - ]); + ]) as CommentObject; expect(comment.assembleCommentBody(['snippet1', 'snippet2'], commentConfig)).toEqual( 'A list:\n- one\n- two\n- three\n\n' @@ -72,7 +73,7 @@ describe('comment', () => { ['body', 'Do not forget to be awesome!'], ]), ]], - ]); + ]) as CommentObject; expect(comment.assembleCommentBody(['snippet1', 'snippet2'], commentConfig)).toEqual( 'hello\n\n' @@ -96,7 +97,7 @@ describe('comment', () => { ['body', 'Do not forget to be awesome!'], ]), ]], - ]); + ]) as CommentObject; expect(comment.assembleCommentBody(['snippet1', 'snippet2'], commentConfig)).toEqual( 'A list:\n- one\n- two\n- three\n\n' @@ -115,7 +116,7 @@ describe('comment', () => { ['body', 'A list:\n- one\n- two\n{{#shouldIncludeThirdItem}}- three\n{{/shouldIncludeThirdItem}}'], ]), ]], - ]); + ]) as CommentObject; const templateVariables = { user: { diff --git a/test/config.test.ts b/test/config.test.ts index a7084e2..b24dd48 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -1,4 +1,5 @@ import * as config from '../lib/config'; +import {Config} from "../lib/config"; describe('validateCommentConfig', () => { const snippet1Object = { @@ -89,7 +90,7 @@ describe('validateCommentConfig', () => { footer: 'bye', snippets: [snippet1Object, snippet2Object, snippet3Object, snippet4Object, snippet5Object], }, - }; + } as Config; const output = new Map([ ['onCreate', 'nothing'], @@ -103,6 +104,7 @@ describe('validateCommentConfig', () => { }); test('removes unknown keys', () => { + // @ts-expect-error: Intentional type violation for testing const input = { comment: { 'on-update': 'nothing', @@ -112,7 +114,7 @@ describe('validateCommentConfig', () => { id: '342', snippets: [snippet1Object, snippet2Object], }, - }; + } as Config; const output = new Map([ ['onCreate', 'create'], @@ -133,7 +135,7 @@ describe('validateCommentConfig', () => { footer: 'bye', snippets: [snippet2Object], }, - }; + } as Config; const output = new Map([ ['onCreate', 'create'], @@ -240,7 +242,7 @@ describe('validateCommentConfig', () => { footer: 'bye', snippets: [snippet2Object], }, - }; + } as Config; const templateVariables = { onCreate: 'nothing' }; @@ -298,7 +300,7 @@ describe('validateCommentConfig', () => { footer: 'bye', snippets: [snippet2Object], }, - }; + } as Config; const templateVariables = { onUpdate: 'nothing' }; diff --git a/test/snippets.test.ts b/test/snippets.test.ts index c9e90e9..c454c76 100644 --- a/test/snippets.test.ts +++ b/test/snippets.test.ts @@ -1,5 +1,5 @@ import * as snippets from '../lib/snippets'; -import {CommentConfig} from "../types/global.type"; +import {CommentConfig} from "../lib/config"; jest.mock('@actions/core'); From 73ff4f4afdbf8a347bc8f9586e64961c781acfe5 Mon Sep 17 00:00:00 2001 From: Delaney Sylvans Date: Fri, 4 Oct 2024 02:10:50 +0100 Subject: [PATCH 16/16] update tsconfig --- tsconfig.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 804f262..e20f466 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { - "module": "commonjs", - "target": "es5", + "module": "node16", + "moduleResolution": "node16", + "target": "es2015", "sourceMap": true, "strict": true, "noImplicitAny": true,