From 142ebcbad4ee06b965baa080b35e7762b045f7a4 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 8 Nov 2021 12:30:11 +0100 Subject: [PATCH 01/54] Added setup for TypeScript-based ESLint rules. --- package-lock.json | 2875 +++++++++++++++++++++++++++++++++++++---- package.json | 21 +- sources/.eslintrc | 7 + sources/tsconfig.json | 103 ++ 4 files changed, 2776 insertions(+), 230 deletions(-) create mode 100644 sources/.eslintrc create mode 100644 sources/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 1fc0aa1..ab267fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,69 +1,2483 @@ { "name": "eslint-plugin-highcharts", "version": "1.0.10", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "eslint-plugin-highcharts", + "version": "1.0.10", + "license": "ISC", + "dependencies": { + "eslint": "^7.27.0", + "requireindex": "~1.2.0" + }, + "devDependencies": { + "@types/eslint": "7.28.2", + "@types/node": "12.20.36", + "@typescript-eslint/eslint-plugin": "^5.3.0", + "@typescript-eslint/parser": "^5.3.0", + "husky": "7.0.4", + "mocha": "^9.1.2", + "typescript": "^4.4.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/eslint": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", + "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.20.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.36.tgz", + "integrity": "sha512-+5haRZ9uzI7rYqzDznXgkuacqb6LJhAti8mzZKWxIXn/WEtvB+GHVJ7AuMwcN1HMvXOSJcrvA6PPoYHYOYYebA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.0.tgz", + "integrity": "sha512-ARUEJHJrq85aaiCqez7SANeahDsJTD3AEua34EoQN9pHS6S5Bq9emcIaGGySt/4X2zSi+vF5hAH52sEen7IO7g==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.3.0", + "@typescript-eslint/scope-manager": "5.3.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/experimental-utils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.0.tgz", + "integrity": "sha512-NFVxYTjKj69qB0FM+piah1x3G/63WB8vCBMnlnEHUsiLzXSTWb9FmFn36FD9Zb4APKBLY3xRArOGSMQkuzTF1w==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.3.0", + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/typescript-estree": "5.3.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.0.tgz", + "integrity": "sha512-rKu/yAReip7ovx8UwOAszJVO5MgBquo8WjIQcp1gx4pYQCwYzag+I5nVNHO4MqyMkAo0gWt2gWUi+36gWAVKcw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.3.0", + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/typescript-estree": "5.3.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.0.tgz", + "integrity": "sha512-22Uic9oRlTsPppy5Tcwfj+QET5RWEnZ5414Prby465XxQrQFZ6nnm5KnXgnsAJefG4hEgMnaxTB3kNEyjdjj6A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/visitor-keys": "5.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.0.tgz", + "integrity": "sha512-fce5pG41/w8O6ahQEhXmMV+xuh4+GayzqEogN24EK+vECA3I6pUwKuLi5QbXO721EMitpQne5VKXofPonYlAQg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.0.tgz", + "integrity": "sha512-FJ0nqcaUOpn/6Z4Jwbtf+o0valjBLkqc3MWkMvrhA2TvzFXtcclIM8F4MBEmYa2kgcI8EZeSAzwoSrIC8JYkug==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/visitor-keys": "5.3.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.0.tgz", + "integrity": "sha512-oVIAfIQuq0x2TFDNLVavUn548WL+7hdhxYn+9j3YdJJXB7mH9dAmZNJsPDa7Jc+B9WGqoiex7GUDbyMxV0a/aw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.3.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", + "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "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/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "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", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "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" + } + }, + "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", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", + "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "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/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dependencies": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } }, - "@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==" + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + } + } + }, + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/eslint": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", + "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@types/node": { + "version": "12.20.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.36.tgz", + "integrity": "sha512-+5haRZ9uzI7rYqzDznXgkuacqb6LJhAti8mzZKWxIXn/WEtvB+GHVJ7AuMwcN1HMvXOSJcrvA6PPoYHYOYYebA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.0.tgz", + "integrity": "sha512-ARUEJHJrq85aaiCqez7SANeahDsJTD3AEua34EoQN9pHS6S5Bq9emcIaGGySt/4X2zSi+vF5hAH52sEen7IO7g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@typescript-eslint/experimental-utils": "5.3.0", + "@typescript-eslint/scope-manager": "5.3.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "@typescript-eslint/experimental-utils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.0.tgz", + "integrity": "sha512-NFVxYTjKj69qB0FM+piah1x3G/63WB8vCBMnlnEHUsiLzXSTWb9FmFn36FD9Zb4APKBLY3xRArOGSMQkuzTF1w==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.3.0", + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/typescript-estree": "5.3.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + } } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true } } }, - "@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "@typescript-eslint/parser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.0.tgz", + "integrity": "sha512-rKu/yAReip7ovx8UwOAszJVO5MgBquo8WjIQcp1gx4pYQCwYzag+I5nVNHO4MqyMkAo0gWt2gWUi+36gWAVKcw==", "dev": true, "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "@typescript-eslint/scope-manager": "5.3.0", + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/typescript-estree": "5.3.0", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.0.tgz", + "integrity": "sha512-22Uic9oRlTsPppy5Tcwfj+QET5RWEnZ5414Prby465XxQrQFZ6nnm5KnXgnsAJefG4hEgMnaxTB3kNEyjdjj6A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/visitor-keys": "5.3.0" + } + }, + "@typescript-eslint/types": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.0.tgz", + "integrity": "sha512-fce5pG41/w8O6ahQEhXmMV+xuh4+GayzqEogN24EK+vECA3I6pUwKuLi5QbXO721EMitpQne5VKXofPonYlAQg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.0.tgz", + "integrity": "sha512-FJ0nqcaUOpn/6Z4Jwbtf+o0valjBLkqc3MWkMvrhA2TvzFXtcclIM8F4MBEmYa2kgcI8EZeSAzwoSrIC8JYkug==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.0", + "@typescript-eslint/visitor-keys": "5.3.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.0.tgz", + "integrity": "sha512-oVIAfIQuq0x2TFDNLVavUn548WL+7hdhxYn+9j3YdJJXB7mH9dAmZNJsPDa7Jc+B9WGqoiex7GUDbyMxV0a/aw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.0", + "eslint-visitor-keys": "^3.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", + "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "dev": true + } } }, "@ungap/promise-all-settled": { @@ -75,20 +2489,18 @@ "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" }, "acorn-jsx": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "requires": {} }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -99,20 +2511,17 @@ "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "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, "requires": { "color-convert": "^1.9.0" } @@ -131,22 +2540,25 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, + "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 + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", @@ -158,7 +2570,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -182,8 +2593,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "camelcase": { "version": "6.2.0", @@ -195,7 +2605,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -205,7 +2614,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -214,7 +2622,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -222,20 +2629,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -273,7 +2677,6 @@ "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, "requires": { "color-name": "1.1.3" } @@ -281,20 +2684,17 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -302,10 +2702,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "requires": { "ms": "2.1.2" } @@ -319,8 +2718,7 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "diff": { "version": "5.0.0", @@ -328,11 +2726,19 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "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, + "requires": { + "path-type": "^4.0.0" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, "requires": { "esutils": "^2.0.2" } @@ -340,14 +2746,12 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, "requires": { "ansi-colors": "^4.1.1" } @@ -361,14 +2765,12 @@ "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", - "dev": true, "requires": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.2", @@ -415,7 +2817,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -425,7 +2826,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" }, @@ -433,22 +2833,19 @@ "eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" } } }, "eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" }, "espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, "requires": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", @@ -458,22 +2855,19 @@ "eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" } } }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, "requires": { "estraverse": "^5.1.0" }, @@ -481,8 +2875,7 @@ "estraverse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" } } }, @@ -490,7 +2883,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "requires": { "estraverse": "^5.2.0" }, @@ -498,46 +2890,61 @@ "estraverse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" } } }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@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" + } }, "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", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, "requires": { "flat-cache": "^3.0.4" } @@ -571,7 +2978,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -580,14 +2986,12 @@ "flatted": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.3.2", @@ -599,8 +3003,7 @@ "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "get-caller-file": { "version": "2.0.5", @@ -612,7 +3015,6 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -626,7 +3028,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -635,11 +3036,32 @@ "version": "13.9.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true + } + } + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -649,8 +3071,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "he": { "version": "1.2.0", @@ -658,17 +3079,21 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -677,14 +3102,12 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -693,8 +3116,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-binary-path": { "version": "2.1.0", @@ -708,20 +3130,17 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "requires": { "is-extglob": "^2.1.1" } @@ -747,20 +3166,17 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -769,20 +3185,17 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -800,20 +3213,17 @@ "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" }, "log-symbols": { "version": "4.1.0", @@ -829,16 +3239,30 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -881,23 +3305,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -933,8 +3340,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nanoid": { "version": "3.1.25", @@ -945,8 +3351,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, "normalize-path": { "version": "3.0.0", @@ -958,7 +3363,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -967,7 +3371,6 @@ "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -999,7 +3402,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" } @@ -1013,13 +3415,17 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "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 }, "picomatch": { @@ -1031,19 +3437,22 @@ "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "randombytes": { @@ -1067,8 +3476,7 @@ "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" }, "require-directory": { "version": "2.1.1", @@ -1079,8 +3487,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "requireindex": { "version": "1.2.0", @@ -1090,18 +3497,31 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1112,7 +3532,6 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -1130,7 +3549,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -1138,14 +3556,18 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -1156,7 +3578,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -1165,7 +3586,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -1173,22 +3593,19 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1199,7 +3616,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -1207,14 +3623,12 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "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, "requires": { "has-flag": "^3.0.0" } @@ -1223,7 +3637,6 @@ "version": "6.7.1", "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, "requires": { "ajv": "^8.0.1", "lodash.clonedeep": "^4.5.0", @@ -1237,7 +3650,6 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -1248,16 +3660,14 @@ "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" } } }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, "to-regex-range": { "version": "5.0.1", @@ -1268,11 +3678,25 @@ "is-number": "^7.0.0" } }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "requires": { "prelude-ls": "^1.2.1" } @@ -1280,14 +3704,18 @@ "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "typescript": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -1295,14 +3723,12 @@ "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -1310,8 +3736,7 @@ "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, "workerpool": { "version": "6.1.5", @@ -1359,8 +3784,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "y18n": { "version": "5.0.8", @@ -1371,8 +3795,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { "version": "16.2.0", diff --git a/package.json b/package.json index b876c75..ce3717e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "eslint-plugin-highcharts", + "license": "ISC", "version": "1.0.10", "description": "ESLint rules for the Highcharts project", "keywords": [ @@ -14,17 +15,29 @@ }, "main": "lib/index.js", "scripts": { + "build": "npx tsc -b sources", + "lint": "npx eslint sources", "test": "mocha tests --recursive" }, "dependencies": { + "eslint": "^7.27.0", "requireindex": "~1.2.0" }, "devDependencies": { - "eslint": "^7.27.0", - "mocha": "^9.1.2" + "@types/eslint": "7.28.2", + "@types/node": "12.20.36", + "@typescript-eslint/eslint-plugin": "^5.3.0", + "@typescript-eslint/parser": "^5.3.0", + "husky": "7.0.4", + "mocha": "^9.1.2", + "typescript": "^4.4.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" }, - "license": "ISC" + "husky": { + "hooks": { + "pre-commit": "npm run lint && npm run build && npm test" + } + } } diff --git a/sources/.eslintrc b/sources/.eslintrc new file mode 100644 index 0000000..d798b8f --- /dev/null +++ b/sources/.eslintrc @@ -0,0 +1,7 @@ +{ + "extends": [ + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser" +} diff --git a/sources/tsconfig.json b/sources/tsconfig.json new file mode 100644 index 0000000..7408c36 --- /dev/null +++ b/sources/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + // "target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es6", + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "rootDir": "./", + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + "outDir": "../lib/", + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} From c8a29a0fe61af4dea10df8f8d751a73bdff794bb Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 8 Nov 2021 20:48:16 +0100 Subject: [PATCH 02/54] Added pretty-length rule with doclet AST, fix mode not working. --- lib/rules/pretty-length.js | 168 +++++++++++++++++ package-lock.json | 4 +- package.json | 5 +- sources/rules/pretty-length.ts | 324 +++++++++++++++++++++++++++++++++ 4 files changed, 497 insertions(+), 4 deletions(-) create mode 100644 lib/rules/pretty-length.js create mode 100644 sources/rules/pretty-length.ts diff --git a/lib/rules/pretty-length.js b/lib/rules/pretty-length.js new file mode 100644 index 0000000..5fdf5c8 --- /dev/null +++ b/lib/rules/pretty-length.js @@ -0,0 +1,168 @@ +/** + * @fileoverview Limit and autofix max length. + * @author Sophie Bremer + */ +'use strict'; +const FS = require("fs"); +const Path = require("path"); +/* * + * + * Constants + * + * */ +const messageTemplate = 'Code line exceeds limit of {0} characters.'; +const optionsDefault = { + maximalLength: 80 +}; +const optionsSchema = { + 'ignorePattern': { + 'type': 'string' + }, + 'maximalLength': { + 'type': 'integer' + } +}; +const regExps = { + breakSplit: /\n/gu, + docletTag: /^(@[A-Za-z][\w-]+)\s+(?:(\{[^\}]+\})\s+)?([\s\S]*)$/u, + docletTagSplit: /(?<=\s)(?=@[A-Za-z])/gu, + docletTagTrim: /^\s*\*\s+/mu, + spaceSplit: /\s+/gsu +}; +/* * + * + * Functions + * + * */ +function create(ruleContext) { + const cwd = ruleContext.getCwd(), filePath = Path.relative(cwd, ruleContext.getFilename()), options = createOptions(ruleContext.options), message = messageTemplate.replace('{0}', `${options.maximalLength}`), sourceCode = ruleContext.getSourceCode(), + // Read source file as sourceCode.getText is build from flawed AST + sourceText = FS.readFileSync(filePath).toString(), sourceTextLines = sourceCode.getText().split(regExps.breakSplit); + return { + Program: (node) => { + const context = { + cwd, + filePath, + node, + options, + ruleContext, + sourceCode, + sourceText, + sourceTextLines, + report: (line, column) => { + ruleContext.report({ + fix: (fixer) => programFix(context, fixer), + loc: { + column, + line + }, + message, + node + }); + } + }; + return program(context); + } + }; +} +function createOptions(ruleOptions) { + const [options] = ruleOptions; + return { + ignorePattern: options.ignorePattern || optionsDefault.ignorePattern, + indentSize: options.indentSize || optionsDefault.indentSize, + maximalLength: (options.maximalLength || + optionsDefault.maximalLength) + }; +} +/** + * + * @return {Array} + */ +function extractDoclets(context) { + const comments = context.sourceCode.getAllComments(), doclets = [], indentSize = context.options.indentSize || 4; + for (let i = 0, iEnd = comments.length, comment; i < iEnd; ++i) { + comment = comments[i]; + if (!comment.loc || + !comment.range || + comment.type !== 'Block' || + comment.value[0] !== '*') { + continue; + } + doclets.push({ + indentLevel: Math.floor((comment.loc.start.column || 0) / indentSize), + loc: comment.loc, + range: comment.range, + tags: extractDocletTags(comment.value) + }); + } + return doclets; +} +function extractDocletTags(doclet) { + const segments = doclet.split(regExps.docletTagSplit), tags = []; + for (let i = 0, iEnd = segments.length, segment; i < iEnd; ++i) { + segment = segments[i]; + const tag = segment.match(regExps.docletTag) || [], name = tag[1], meta = (tag[2] || '').trim() || void 0; + let param, value = tag[3] || tag[0] || segment; + if (name === '@param') { + param = value.split(regExps.spaceSplit)[0]; + value = value.substr(param.length).trim(); + } + if (name !== '@example') { + value = value.replace(regExps.docletTagTrim, ''); + } + tags.push({ + meta, + name, + param, + value + }); + } + return tags; +} +function program(context) { + const sourceTextLines = context.sourceTextLines, { ignorePattern, maximalLength } = context.options, ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); + console.log('Checking', context.filePath); + for (let line = 0, lineEnd = sourceTextLines.length, sourceTextLength, sourceTextLine; line < lineEnd; ++line) { + sourceTextLine = sourceTextLines[line]; + sourceTextLength = sourceTextLine.length; + if (ignoreRegExp && + ignoreRegExp.test(sourceTextLine)) { + continue; + } + if (sourceTextLength > maximalLength) { + context.report(line, maximalLength - 1); + } + } +} +function programFix(context, fixer) { + const docletASTs = extractDoclets(context), fixes = []; + for (let i = 0, iEnd = docletASTs.length; i < iEnd; ++i) { + fixes.push(fixer.replaceTextRange(docletASTs[i].range, '/**\n' + + docletASTs[i].tags + .map(tag => ((tag.name ? + ` ${tag.name} ` : + '') + + (tag.meta ? + `${tag.meta} ` : + '') + + tag.value)) + .join('\n *') + + ' */')); + } + FS.writeFileSync(context.filePath + '.json', + // eslint-disable-next-line @typescript-eslint/no-var-requires + JSON.stringify(docletASTs, void 0, ' ')); + return fixes; +} +module.exports = { + meta: { + 'type': 'layout', + 'fixable': 'code', + 'schema': [{ + 'type': 'object', + 'properties': optionsSchema, + 'additionalProperties': false + }] + }, + create +}; diff --git a/package-lock.json b/package-lock.json index ab267fa..51dd58b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "eslint-plugin-highcharts", - "version": "1.0.10", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "eslint-plugin-highcharts", - "version": "1.0.10", + "version": "2.0.0", "license": "ISC", "dependencies": { "eslint": "^7.27.0", diff --git a/package.json b/package.json index ce3717e..792adcf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "eslint-plugin-highcharts", "license": "ISC", - "version": "1.0.10", + "version": "2.0.0", "description": "ESLint rules for the Highcharts project", "keywords": [ "eslint", @@ -17,7 +17,8 @@ "scripts": { "build": "npx tsc -b sources", "lint": "npx eslint sources", - "test": "mocha tests --recursive" + "test": "mocha tests --recursive", + "watch": "npm run build -- --watch" }, "dependencies": { "eslint": "^7.27.0", diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts new file mode 100644 index 0000000..b5b2360 --- /dev/null +++ b/sources/rules/pretty-length.ts @@ -0,0 +1,324 @@ +/** + * @fileoverview Limit and autofix max length. + * @author Sophie Bremer + */ + +'use strict'; + +import type { AST, Rule, SourceCode } from 'eslint'; +import type { Comment, Node, SourceLocation } from 'estree'; + +import * as FS from 'fs'; +import * as Path from 'path'; +import * as Util from 'util'; + +/* * + * + * Declarations + * + * */ + +interface Context { + cwd: string; + filePath: string; + node: Node; + options: Options; + ruleContext: Rule.RuleContext; + sourceCode: SourceCode; + sourceText: string; + sourceTextLines: Array; + report: ( + line: number, + column: number + ) => void; +} + +interface DocletAST { + indentLevel: number; + loc: SourceLocation; + range: AST.Range + tags: Array; +} + +interface DocletTag { + meta?: string; + name?: string; + param?: string; + value: string; +} + +interface Options { + ignorePattern?: string; + indentSize?: number; + maximalLength: number; +} + +/* * + * + * Constants + * + * */ + +const messageTemplate = 'Code line exceeds limit of {0} characters.'; + +const optionsDefault: Options = { + maximalLength: 80 +}; + +const optionsSchema = { + 'ignorePattern': { + 'type': 'string' + }, + 'maximalLength': { + 'type': 'integer' + } +} + +const regExps = { + breakSplit: /\n/gu, + docletTag: /^(@[A-Za-z][\w-]+)\s+(?:(\{[^\}]+\})\s+)?([\s\S]*)$/u, + docletTagSplit: /(?<=\s)(?=@[A-Za-z])/gu, + docletTagTrim: /^\s*\*\s+/mu, + spaceSplit: /\s+/gsu +} + +/* * + * + * Functions + * + * */ + +function create( + ruleContext: Rule.RuleContext +): Rule.RuleListener { + const cwd = ruleContext.getCwd(), + filePath = Path.relative(cwd, ruleContext.getFilename()), + options = createOptions(ruleContext.options), + message = messageTemplate.replace('{0}', `${options.maximalLength}`), + sourceCode = ruleContext.getSourceCode(), + // Read source file as sourceCode.getText is build from flawed AST + sourceText = FS.readFileSync(filePath).toString(), + sourceTextLines = sourceCode.getText().split(regExps.breakSplit); + + return { + Program: (node: Node) => { + const context: Context = { + cwd, + filePath, + node, + options, + ruleContext, + sourceCode, + sourceText, + sourceTextLines, + report: ( + line: number, + column: number + ) => { + ruleContext.report({ + fix: (fixer) => programFix(context, fixer), + loc: { + column, + line + }, + message, + node + }); + } + }; + + return program(context); + } + }; +} + +function createOptions( + ruleOptions: Rule.RuleContext['options'] +): Options { + const [ options ] = ruleOptions as [ Partial ]; + + return { + ignorePattern: options.ignorePattern || optionsDefault.ignorePattern, + indentSize: options.indentSize || optionsDefault.indentSize, + maximalLength: ( + options.maximalLength || + optionsDefault.maximalLength + ) + }; +} + +/** + * + * @return {Array} + */ +function extractDoclets(context: Context): Array { + const comments = context.sourceCode.getAllComments(), + doclets: Array = [], + indentSize = context.options.indentSize || 4; + + for ( + let i = 0, + iEnd = comments.length, + comment: Comment; + i < iEnd; + ++i + ) { + comment = comments[i]; + + if ( + !comment.loc || + !comment.range || + comment.type !== 'Block' || + comment.value[0] !== '*' + ) { + continue; + } + + doclets.push({ + indentLevel: Math.floor((comment.loc.start.column || 0) / indentSize), + loc: comment.loc, + range: comment.range, + tags: extractDocletTags(comment.value) + }); + } + + return doclets; +} + +function extractDocletTags( + doclet: string +): Array { + const segments = doclet.split(regExps.docletTagSplit), + tags: Array = []; + + for ( + let i = 0, + iEnd = segments.length, + segment: string; + i < iEnd; + ++i + ) { + segment = segments[i]; + + const tag = segment.match(regExps.docletTag) || [], + name = tag[1], + meta = (tag[2] || '').trim() || void 0; + + let param: (string|undefined), + value = tag[3] || tag[0] || segment; + + + if (name === '@param') { + param = value.split(regExps.spaceSplit)[0]; + value = value.substr(param.length).trim(); + } + + if (name !== '@example') { + value = value.replace(regExps.docletTagTrim, ''); + } + + tags.push({ + meta, + name, + param, + value + }); + } + + return tags; +} + +function program(context: Context) { + const sourceTextLines = context.sourceTextLines, + { + ignorePattern, + maximalLength + } = context.options, + ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); + + console.log('Checking', context.filePath); + + for ( + let line = 0, + lineEnd = sourceTextLines.length, + sourceTextLength: number, + sourceTextLine: string; + line < lineEnd; + ++line + ) { + sourceTextLine = sourceTextLines[line]; + sourceTextLength = sourceTextLine.length; + + if ( + ignoreRegExp && + ignoreRegExp.test(sourceTextLine) + ) { + continue; + } + + if (sourceTextLength > maximalLength) { + context.report( + line, + maximalLength - 1 + ); + } + } +} + +function programFix( + context: Context, + fixer: Rule.RuleFixer +): (Array|null) { + const docletASTs = extractDoclets(context), + fixes = []; + + for (let i = 0, iEnd = docletASTs.length; i < iEnd; ++i) { + fixes.push(fixer.replaceTextRange( + docletASTs[i].range, + '/**\n' + + docletASTs[i].tags + .map(tag => ( + ( + tag.name ? + ` ${tag.name} ` : + '' + ) + + ( + tag.meta ? + `${tag.meta} ` : + '' + ) + + tag.value + )) + .join('\n *') + + ' */' + )); + } + + FS.writeFileSync( + context.filePath + '.json', + // eslint-disable-next-line @typescript-eslint/no-var-requires + JSON.stringify(docletASTs, void 0, ' ') + ); + + return fixes; +} + +/* * + * + * Default Export + * + * */ + +export = { + meta: { + 'type': 'layout', + 'fixable': 'code', + 'schema': [{ + 'type': 'object', + 'properties': optionsSchema, + 'additionalProperties': false + }] + }, + create +} as Rule.RuleModule; From 702994e73bc2f74204a08257ece2d52b1c1e62ad Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Wed, 10 Nov 2021 16:31:39 +0100 Subject: [PATCH 03/54] Migrated pretty-length to TypeScript AST. --- .eslintrc | 2 +- lib/RuleContext.js | 108 ++++++++++++ lib/rules/pretty-length.js | 149 ++++------------ package-lock.json | 4 +- package.json | 4 +- sources/FixFunction.d.ts | 36 ++++ sources/LintFunction.d.ts | 36 ++++ sources/RuleContext.ts | 240 ++++++++++++++++++++++++++ sources/RuleOptions.d.ts | 23 +++ sources/RuleType.d.ts | 27 +++ sources/TransformerFunction.d.ts | 36 ++++ sources/rules/pretty-length.ts | 283 +++++++------------------------ 12 files changed, 607 insertions(+), 341 deletions(-) create mode 100644 lib/RuleContext.js create mode 100644 sources/FixFunction.d.ts create mode 100644 sources/LintFunction.d.ts create mode 100644 sources/RuleContext.ts create mode 100644 sources/RuleOptions.d.ts create mode 100644 sources/RuleType.d.ts create mode 100644 sources/TransformerFunction.d.ts diff --git a/.eslintrc b/.eslintrc index 76cd5cf..48a59f5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,7 +7,7 @@ "func-style": 0, "quotes": 0, "max-len": [ - "error", + "warn", { "code": 120, "comments": 80, diff --git a/lib/RuleContext.js b/lib/RuleContext.js new file mode 100644 index 0000000..3f7c7e7 --- /dev/null +++ b/lib/RuleContext.js @@ -0,0 +1,108 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RuleContext = void 0; +const FS = require("fs"); +const Path = require("path"); +const TS = require("typescript"); +/* * + * + * Class + * + * */ +class RuleContext { + /* * + * + * Constructor + * + * */ + constructor(esLintContext, fixFunction) { + this.cwd = esLintContext.getCwd(); + this.esLintContext = esLintContext; + this.fixes = []; + this.options = (esLintContext.options[1] || {}); + this.settings = ((esLintContext.settings || {}).highcharts || {}); + this.fixFunction = fixFunction; + const filePath = Path.relative(this.cwd, esLintContext.getFilename()); + this.sourceFile = TS.createSourceFile(filePath, FS.readFileSync(filePath).toString(), TS.ScriptTarget.Latest, true, TS.ScriptKind.Unknown); + } + /* * + * + * Static Properties + * + * */ + static setupRuleExport(ruleType, ruleOptionsSchema, lintProgram, fixProgram) { + return { + meta: { + fixable: fixProgram ? 'code' : void 0, + type: ruleType, + }, + create: (esLintRuleContext) => ({ + Program: () => { + const context = new RuleContext(esLintRuleContext, fixProgram); + return lintProgram(context); + } + }) + }; + } + /* * + * + * Functions + * + * */ + fix(transformer) { + const factory = (transformationContext) => (sourceFile) => { + const internalVisitor = (node) => { + const visitedNode = transformer(transformationContext, node); + if (!visitedNode) { + return; + } + if (visitedNode === node) { + const subNodes = visitedNode.getChildren(), visitedSubNodes = TS.visitNodes(transformationContext.factory.createNodeArray(visitedNode.getChildren()), internalVisitor); + // @todo replacements not working :( + subNodes.splice(0, subNodes.length, ...visitedSubNodes); + } + return visitedNode; + }; + internalVisitor(sourceFile); + return sourceFile; + }; + const sourceFile = TS + .transform(this.sourceFile, [factory], Object.assign(Object.assign({}, TS.getDefaultCompilerOptions()), { removeComments: false, target: TS.ScriptTarget.ES2021 })) + .transformed[0]; + this.fixes.push({ + range: [sourceFile.pos, sourceFile.end], + text: sourceFile.text + }); + } + report(line, column, message) { + const report = { + loc: { + column, + line + }, + message + }; + if (this.fixFunction) { + const fixFunction = this.fixFunction; + report.fix = () => { + fixFunction(this); + return this.fixes.splice(0, this.fixes.length); + }; + } + this.esLintContext.report(report); + } + replace(node, replacement) { + const sourceFile = this.sourceFile, text = sourceFile.getFullText(), leadingText = text.substr(0, node.pos), trailingText = text.substr(node.end); + sourceFile.text = leadingText + replacement + trailingText; + } +} +exports.RuleContext = RuleContext; +/* * + * + * Default Export + * + * */ +exports.default = RuleContext; diff --git a/lib/rules/pretty-length.js b/lib/rules/pretty-length.js index 5fdf5c8..83adb05 100644 --- a/lib/rules/pretty-length.js +++ b/lib/rules/pretty-length.js @@ -3,15 +3,21 @@ * @author Sophie Bremer */ 'use strict'; +/* * + * + * Imports + * + * */ const FS = require("fs"); -const Path = require("path"); +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); /* * * * Constants * * */ const messageTemplate = 'Code line exceeds limit of {0} characters.'; -const optionsDefault = { +const optionsDefaults = { maximalLength: 80 }; const optionsSchema = { @@ -34,94 +40,20 @@ const regExps = { * Functions * * */ -function create(ruleContext) { - const cwd = ruleContext.getCwd(), filePath = Path.relative(cwd, ruleContext.getFilename()), options = createOptions(ruleContext.options), message = messageTemplate.replace('{0}', `${options.maximalLength}`), sourceCode = ruleContext.getSourceCode(), - // Read source file as sourceCode.getText is build from flawed AST - sourceText = FS.readFileSync(filePath).toString(), sourceTextLines = sourceCode.getText().split(regExps.breakSplit); - return { - Program: (node) => { - const context = { - cwd, - filePath, - node, - options, - ruleContext, - sourceCode, - sourceText, - sourceTextLines, - report: (line, column) => { - ruleContext.report({ - fix: (fixer) => programFix(context, fixer), - loc: { - column, - line - }, - message, - node - }); - } - }; - return program(context); +function fix(context) { + const docletASTs = []; + context.fix((transformationConext, node) => { + if (TS.isJSDoc(node)) { + process.stdout.write('.'); + return transformationConext.factory.createJSDocComment((TS.getTextOfJSDocComment(node.comment) || '').toUpperCase(), node.tags); } - }; -} -function createOptions(ruleOptions) { - const [options] = ruleOptions; - return { - ignorePattern: options.ignorePattern || optionsDefault.ignorePattern, - indentSize: options.indentSize || optionsDefault.indentSize, - maximalLength: (options.maximalLength || - optionsDefault.maximalLength) - }; + return node; + }); + FS.writeFileSync(context.sourceFile.fileName + '.json', JSON.stringify(docletASTs, ['comment', 'name', 'tags', 'text'], ' ')); } -/** - * - * @return {Array} - */ -function extractDoclets(context) { - const comments = context.sourceCode.getAllComments(), doclets = [], indentSize = context.options.indentSize || 4; - for (let i = 0, iEnd = comments.length, comment; i < iEnd; ++i) { - comment = comments[i]; - if (!comment.loc || - !comment.range || - comment.type !== 'Block' || - comment.value[0] !== '*') { - continue; - } - doclets.push({ - indentLevel: Math.floor((comment.loc.start.column || 0) / indentSize), - loc: comment.loc, - range: comment.range, - tags: extractDocletTags(comment.value) - }); - } - return doclets; -} -function extractDocletTags(doclet) { - const segments = doclet.split(regExps.docletTagSplit), tags = []; - for (let i = 0, iEnd = segments.length, segment; i < iEnd; ++i) { - segment = segments[i]; - const tag = segment.match(regExps.docletTag) || [], name = tag[1], meta = (tag[2] || '').trim() || void 0; - let param, value = tag[3] || tag[0] || segment; - if (name === '@param') { - param = value.split(regExps.spaceSplit)[0]; - value = value.substr(param.length).trim(); - } - if (name !== '@example') { - value = value.replace(regExps.docletTagTrim, ''); - } - tags.push({ - meta, - name, - param, - value - }); - } - return tags; -} -function program(context) { - const sourceTextLines = context.sourceTextLines, { ignorePattern, maximalLength } = context.options, ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); - console.log('Checking', context.filePath); +function lint(context) { + const sourceTextLines = context.sourceFile.getFullText().split(regExps.breakSplit), { ignorePattern, maximalLength } = setupOptions(context.options), ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); + console.log('Checking', context.sourceFile.fileName); for (let line = 0, lineEnd = sourceTextLines.length, sourceTextLength, sourceTextLine; line < lineEnd; ++line) { sourceTextLine = sourceTextLines[line]; sourceTextLength = sourceTextLine.length; @@ -130,39 +62,16 @@ function program(context) { continue; } if (sourceTextLength > maximalLength) { - context.report(line, maximalLength - 1); + context.report(line, maximalLength - 1, messageTemplate.replace('{0}', `${maximalLength}`)); } } } -function programFix(context, fixer) { - const docletASTs = extractDoclets(context), fixes = []; - for (let i = 0, iEnd = docletASTs.length; i < iEnd; ++i) { - fixes.push(fixer.replaceTextRange(docletASTs[i].range, '/**\n' + - docletASTs[i].tags - .map(tag => ((tag.name ? - ` ${tag.name} ` : - '') + - (tag.meta ? - `${tag.meta} ` : - '') + - tag.value)) - .join('\n *') + - ' */')); - } - FS.writeFileSync(context.filePath + '.json', - // eslint-disable-next-line @typescript-eslint/no-var-requires - JSON.stringify(docletASTs, void 0, ' ')); - return fixes; +function setupOptions(options) { + return { + ignorePattern: options.ignorePattern || optionsDefaults.ignorePattern, + indentSize: options.indentSize || optionsDefaults.indentSize, + maximalLength: (options.maximalLength || + optionsDefaults.maximalLength) + }; } -module.exports = { - meta: { - 'type': 'layout', - 'fixable': 'code', - 'schema': [{ - 'type': 'object', - 'properties': optionsSchema, - 'additionalProperties': false - }] - }, - create -}; +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, lint, fix); diff --git a/package-lock.json b/package-lock.json index 51dd58b..7875b71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@types/eslint": "7.28.2", + "@types/json-schema": "7.0.9", "@types/node": "12.20.36", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", @@ -22,7 +23,8 @@ "typescript": "^4.4.4" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.0.0", + "typescript": ">=4.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 792adcf..c807e17 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "devDependencies": { "@types/eslint": "7.28.2", + "@types/json-schema": "7.0.9", "@types/node": "12.20.36", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", @@ -34,7 +35,8 @@ "typescript": "^4.4.4" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.0.0", + "typescript": ">=4.0.0" }, "husky": { "hooks": { diff --git a/sources/FixFunction.d.ts b/sources/FixFunction.d.ts new file mode 100644 index 0000000..cf58f3a --- /dev/null +++ b/sources/FixFunction.d.ts @@ -0,0 +1,36 @@ +/** + * @author Sophie Bremer + */ + + +/* * + * + * Imports + * + * */ + + +import type RuleContext from './RuleContext'; +import type RuleOptions from './RuleOptions'; + + +/* * + * + * Declarations + * + * */ + + +export interface FixFunction { + (ruleContext: RuleContext): void; +} + + +/* * + * + * Default Export + * + * */ + + +export default FixFunction; diff --git a/sources/LintFunction.d.ts b/sources/LintFunction.d.ts new file mode 100644 index 0000000..3061cad --- /dev/null +++ b/sources/LintFunction.d.ts @@ -0,0 +1,36 @@ +/** + * @author Sophie Bremer + */ + + +/* * + * + * Imports + * + * */ + + +import type RuleContext from './RuleContext'; +import type RuleOptions from './RuleOptions'; + + +/* * + * + * Declarations + * + * */ + + +export interface LintFunction { + (ruleContext: RuleContext): void; +} + + +/* * + * + * Default Export + * + * */ + + +export default LintFunction; diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts new file mode 100644 index 0000000..3b54a8b --- /dev/null +++ b/sources/RuleContext.ts @@ -0,0 +1,240 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import type * as ESLint from 'eslint'; +import type * as JSONSchema from 'json-schema'; +import type FixFunction from './FixFunction'; +import type LintFunction from './LintFunction'; +import type RuleOptions from './RuleOptions'; +import type TransformerFunction from './TransformerFunction'; + +import * as FS from 'fs'; +import * as Path from 'path'; +import * as TS from 'typescript'; +import RuleType from './RuleType'; + + +/* * + * + * Class + * + * */ + + +export class RuleContext { + + + /* * + * + * Static Properties + * + * */ + + + public static setupRuleExport ( + ruleType: RuleType, + ruleOptionsSchema: JSONSchema.JSONSchema4, + lintProgram: LintFunction, + fixProgram?: FixFunction + ): ESLint.Rule.RuleModule { + return { + meta: { + fixable: fixProgram ? 'code' : void 0, + type: ruleType, + }, + create: (esLintRuleContext: ESLint.Rule.RuleContext) => ( + { + Program: () => { + const context = new RuleContext( + esLintRuleContext, + fixProgram + ); + + return lintProgram(context); + } + } + ) + }; + } + + + /* * + * + * Constructor + * + * */ + + + private constructor ( + esLintContext: ESLint.Rule.RuleContext, + fixFunction?: FixFunction + ) { + this.cwd = esLintContext.getCwd(); + this.esLintContext = esLintContext; + this.fixes = []; + this.options = (esLintContext.options[1] || {}); + this.settings = ((esLintContext.settings || {}).highcharts || {}); + this.fixFunction = fixFunction; + + const filePath = Path.relative(this.cwd, esLintContext.getFilename()) + + this.sourceFile = TS.createSourceFile( + filePath, + FS.readFileSync(filePath).toString(), + TS.ScriptTarget.Latest, + true, + TS.ScriptKind.Unknown + ); + } + + + /* * + * + * Properties + * + * */ + + + private cwd: string; + + + private esLintContext: ESLint.Rule.RuleContext; + + + private fixes: Array; + + + private fixFunction?: FixFunction; + + + public options: Partial; + + + public settings: ESLint.Rule.RuleContext['settings']; + + + public sourceFile: TS.SourceFile; + + + /* * + * + * Functions + * + * */ + + + public fix ( + transformer: TransformerFunction + ) { + const factory: TS.TransformerFactory = ( + transformationContext: TS.TransformationContext + ) => ( + sourceFile: TS.SourceFile + ) => { + const internalVisitor: TS.Visitor = (node) => { + const visitedNode = transformer(transformationContext, node); + + if (!visitedNode) { + return; + } + + if (visitedNode === node) { + const subNodes = visitedNode.getChildren(), + visitedSubNodes = TS.visitNodes( + transformationContext.factory.createNodeArray( + visitedNode.getChildren() + ), + internalVisitor + ); + // @todo replacements not working :( + subNodes.splice(0, subNodes.length, ...visitedSubNodes); + } + + return visitedNode; + }; + + internalVisitor(sourceFile); + + return sourceFile; + }; + + const sourceFile = TS + .transform( + this.sourceFile, + [factory], + { + ...TS.getDefaultCompilerOptions(), + removeComments: false, + target: TS.ScriptTarget.ES2021 + } + ) + .transformed[0]; + + this.fixes.push({ + range: [sourceFile.pos, sourceFile.end], + text: sourceFile.text + }); + } + + + public report ( + line: number, + column: number, + message: string + ): void { + const report: ESLint.Rule.ReportDescriptor = { + loc: { + column, + line + }, + message + }; + + if (this.fixFunction) { + const fixFunction = this.fixFunction; + report.fix = () => { + fixFunction(this); + return this.fixes.splice(0, this.fixes.length); + } + } + + this.esLintContext.report(report); + } + + + public replace ( + node: TS.Node, + replacement: string + ) { + const sourceFile = this.sourceFile, + text = sourceFile.getFullText(), + leadingText = text.substr(0, node.pos), + trailingText = text.substr(node.end); + + sourceFile.text = leadingText + replacement + trailingText; + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default RuleContext; diff --git a/sources/RuleOptions.d.ts b/sources/RuleOptions.d.ts new file mode 100644 index 0000000..24d070b --- /dev/null +++ b/sources/RuleOptions.d.ts @@ -0,0 +1,23 @@ +/** + * @author Sophie Bremer + */ + + +/* * + * + * Declarations + * + * */ + + +export type RuleOptions = Record; + + +/* * + * + * Default Export + * + * */ + + +export default RuleOptions; \ No newline at end of file diff --git a/sources/RuleType.d.ts b/sources/RuleType.d.ts new file mode 100644 index 0000000..cd41ae9 --- /dev/null +++ b/sources/RuleType.d.ts @@ -0,0 +1,27 @@ +/** + * @author Sophie Bremer + */ + + +/* * + * + * Declarations + * + * */ + + +export type RuleType = ( + 'layout' | + 'problem' | + 'suggestion' +); + + +/* * + * + * Default Export + * + * */ + + +export default RuleType; diff --git a/sources/TransformerFunction.d.ts b/sources/TransformerFunction.d.ts new file mode 100644 index 0000000..d2a5cb2 --- /dev/null +++ b/sources/TransformerFunction.d.ts @@ -0,0 +1,36 @@ +/** + * @author Sophie Bremer + */ + + +/* * + * + * Imports + * + * */ + + +import type * as TS from 'typescript'; + + +/* * + * + * Declarations + * + * */ + + +export type TransformerFunction = ( + transformationContext: TS.TransformationContext, + node: TS.Node +) => TS.Node; + + +/* * + * + * Default Export + * + * */ + + +export default TransformerFunction; diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index b5b2360..9887c6e 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -3,14 +3,22 @@ * @author Sophie Bremer */ + 'use strict'; -import type { AST, Rule, SourceCode } from 'eslint'; -import type { Comment, Node, SourceLocation } from 'estree'; + +/* * + * + * Imports + * + * */ + import * as FS from 'fs'; -import * as Path from 'path'; -import * as Util from 'util'; +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; + /* * * @@ -18,53 +26,32 @@ import * as Util from 'util'; * * */ -interface Context { - cwd: string; - filePath: string; - node: Node; - options: Options; - ruleContext: Rule.RuleContext; - sourceCode: SourceCode; - sourceText: string; - sourceTextLines: Array; - report: ( - line: number, - column: number - ) => void; -} -interface DocletAST { - indentLevel: number; - loc: SourceLocation; - range: AST.Range - tags: Array; -} +type PrettyLengthContext = RuleContext; -interface DocletTag { - meta?: string; - name?: string; - param?: string; - value: string; -} -interface Options { +interface PrettyLengthOptions extends RuleOptions { ignorePattern?: string; indentSize?: number; maximalLength: number; } + /* * * * Constants * * */ + const messageTemplate = 'Code line exceeds limit of {0} characters.'; -const optionsDefault: Options = { + +const optionsDefaults: PrettyLengthOptions = { maximalLength: 80 }; + const optionsSchema = { 'ignorePattern': { 'type': 'string' @@ -74,6 +61,7 @@ const optionsSchema = { } } + const regExps = { breakSplit: /\n/gu, docletTag: /^(@[A-Za-z][\w-]+)\s+(?:(\{[^\}]+\})\s+)?([\s\S]*)$/u, @@ -82,161 +70,48 @@ const regExps = { spaceSplit: /\s+/gsu } + /* * * * Functions * * */ -function create( - ruleContext: Rule.RuleContext -): Rule.RuleListener { - const cwd = ruleContext.getCwd(), - filePath = Path.relative(cwd, ruleContext.getFilename()), - options = createOptions(ruleContext.options), - message = messageTemplate.replace('{0}', `${options.maximalLength}`), - sourceCode = ruleContext.getSourceCode(), - // Read source file as sourceCode.getText is build from flawed AST - sourceText = FS.readFileSync(filePath).toString(), - sourceTextLines = sourceCode.getText().split(regExps.breakSplit); - return { - Program: (node: Node) => { - const context: Context = { - cwd, - filePath, - node, - options, - ruleContext, - sourceCode, - sourceText, - sourceTextLines, - report: ( - line: number, - column: number - ) => { - ruleContext.report({ - fix: (fixer) => programFix(context, fixer), - loc: { - column, - line - }, - message, - node - }); - } - }; - - return program(context); - } - }; -} - -function createOptions( - ruleOptions: Rule.RuleContext['options'] -): Options { - const [ options ] = ruleOptions as [ Partial ]; - - return { - ignorePattern: options.ignorePattern || optionsDefault.ignorePattern, - indentSize: options.indentSize || optionsDefault.indentSize, - maximalLength: ( - options.maximalLength || - optionsDefault.maximalLength - ) - }; -} - -/** - * - * @return {Array} - */ -function extractDoclets(context: Context): Array { - const comments = context.sourceCode.getAllComments(), - doclets: Array = [], - indentSize = context.options.indentSize || 4; - - for ( - let i = 0, - iEnd = comments.length, - comment: Comment; - i < iEnd; - ++i - ) { - comment = comments[i]; +function fix( + context: PrettyLengthContext +): void { + const docletASTs: Array = []; - if ( - !comment.loc || - !comment.range || - comment.type !== 'Block' || - comment.value[0] !== '*' - ) { - continue; + context.fix((transformationConext, node) => { + if (TS.isJSDoc(node)) { + process.stdout.write('.'); + return transformationConext.factory.createJSDocComment( + (TS.getTextOfJSDocComment(node.comment) || '').toUpperCase(), + node.tags + ); } + return node; + }); - doclets.push({ - indentLevel: Math.floor((comment.loc.start.column || 0) / indentSize), - loc: comment.loc, - range: comment.range, - tags: extractDocletTags(comment.value) - }); - } - - return doclets; + FS.writeFileSync( + context.sourceFile.fileName + '.json', + JSON.stringify(docletASTs, ['comment', 'name', 'tags', 'text'], ' ') + ); } -function extractDocletTags( - doclet: string -): Array { - const segments = doclet.split(regExps.docletTagSplit), - tags: Array = []; - - for ( - let i = 0, - iEnd = segments.length, - segment: string; - i < iEnd; - ++i - ) { - segment = segments[i]; - - const tag = segment.match(regExps.docletTag) || [], - name = tag[1], - meta = (tag[2] || '').trim() || void 0; - - let param: (string|undefined), - value = tag[3] || tag[0] || segment; - - - if (name === '@param') { - param = value.split(regExps.spaceSplit)[0]; - value = value.substr(param.length).trim(); - } - - if (name !== '@example') { - value = value.replace(regExps.docletTagTrim, ''); - } - - tags.push({ - meta, - name, - param, - value - }); - } - - return tags; -} -function program(context: Context) { - const sourceTextLines = context.sourceTextLines, +function lint ( + context: PrettyLengthContext +) { + const sourceTextLines = context.sourceFile.getFullText().split(regExps.breakSplit), { ignorePattern, maximalLength - } = context.options, + } = setupOptions(context.options), ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); - console.log('Checking', context.filePath); + console.log('Checking', context.sourceFile.fileName); for ( let line = 0, @@ -259,66 +134,38 @@ function program(context: Context) { if (sourceTextLength > maximalLength) { context.report( line, - maximalLength - 1 + maximalLength - 1, + messageTemplate.replace('{0}', `${maximalLength}`) ); } } } -function programFix( - context: Context, - fixer: Rule.RuleFixer -): (Array|null) { - const docletASTs = extractDoclets(context), - fixes = []; - - for (let i = 0, iEnd = docletASTs.length; i < iEnd; ++i) { - fixes.push(fixer.replaceTextRange( - docletASTs[i].range, - '/**\n' + - docletASTs[i].tags - .map(tag => ( - ( - tag.name ? - ` ${tag.name} ` : - '' - ) + - ( - tag.meta ? - `${tag.meta} ` : - '' - ) + - tag.value - )) - .join('\n *') + - ' */' - )); - } - - FS.writeFileSync( - context.filePath + '.json', - // eslint-disable-next-line @typescript-eslint/no-var-requires - JSON.stringify(docletASTs, void 0, ' ') - ); - return fixes; +function setupOptions( + options: Partial +): PrettyLengthOptions { + return { + ignorePattern: options.ignorePattern || optionsDefaults.ignorePattern, + indentSize: options.indentSize || optionsDefaults.indentSize, + maximalLength: ( + options.maximalLength || + optionsDefaults.maximalLength + ) + }; } + /* * * * Default Export * * */ -export = { - meta: { - 'type': 'layout', - 'fixable': 'code', - 'schema': [{ - 'type': 'object', - 'properties': optionsSchema, - 'additionalProperties': false - }] - }, - create -} as Rule.RuleModule; + +export = RuleContext.setupRuleExport( + 'layout', + optionsSchema, + lint, + fix +); From 7aa38f8be1fdb76313be47017d80e30601d28a84 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 11 Nov 2021 20:42:06 +0100 Subject: [PATCH 04/54] Added TypeScript scanner and JSDoc extractor. --- lib/RuleContext.js | 56 ++++-------- lib/SourceCode.js | 75 ++++++++++++++++ lib/SourceLine.js | 49 ++++++++++ lib/Utilities.js | 93 +++++++++++++++++++ lib/rules/pretty-length.js | 59 ++++++------ sources/HighchartsSettings.d.ts | 21 +++++ sources/RuleContext.ts | 127 ++++++++------------------ sources/SourceCode.ts | 131 +++++++++++++++++++++++++++ sources/SourceLine.ts | 82 +++++++++++++++++ sources/SourceToken.d.ts | 31 +++++++ sources/TransformerFunction.d.ts | 36 -------- sources/Utilities.ts | 149 +++++++++++++++++++++++++++++++ sources/rules/pretty-length.ts | 88 +++++++++--------- 13 files changed, 750 insertions(+), 247 deletions(-) create mode 100644 lib/SourceCode.js create mode 100644 lib/SourceLine.js create mode 100644 lib/Utilities.js create mode 100644 sources/HighchartsSettings.d.ts create mode 100644 sources/SourceCode.ts create mode 100644 sources/SourceLine.ts create mode 100644 sources/SourceToken.d.ts delete mode 100644 sources/TransformerFunction.d.ts create mode 100644 sources/Utilities.ts diff --git a/lib/RuleContext.js b/lib/RuleContext.js index 3f7c7e7..1ceba45 100644 --- a/lib/RuleContext.js +++ b/lib/RuleContext.js @@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.RuleContext = void 0; const FS = require("fs"); const Path = require("path"); -const TS = require("typescript"); +const SourceCode_1 = require("./SourceCode"); /* * * * Class @@ -18,32 +18,35 @@ class RuleContext { * Constructor * * */ - constructor(esLintContext, fixFunction) { + constructor(esLintContext, ruleOptionsDefault, fixFunction) { + this.changes = []; this.cwd = esLintContext.getCwd(); this.esLintContext = esLintContext; this.fixes = []; - this.options = (esLintContext.options[1] || {}); this.settings = ((esLintContext.settings || {}).highcharts || {}); + this.options = Object.assign(Object.assign(Object.assign({}, ruleOptionsDefault), this.settings), (esLintContext.options[1] || {})); this.fixFunction = fixFunction; - const filePath = Path.relative(this.cwd, esLintContext.getFilename()); - this.sourceFile = TS.createSourceFile(filePath, FS.readFileSync(filePath).toString(), TS.ScriptTarget.Latest, true, TS.ScriptKind.Unknown); + this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()), + this.sourceCode = new SourceCode_1.default(this.sourcePath, FS.readFileSync(this.sourcePath).toString()); } /* * * * Static Properties * * */ - static setupRuleExport(ruleType, ruleOptionsSchema, lintProgram, fixProgram) { + static setupRuleExport(ruleType, ruleOptionsSchema, ruleOptionsDefault, lintFunction, fixFunction) { return { meta: { - fixable: fixProgram ? 'code' : void 0, - type: ruleType, + fixable: fixFunction ? 'code' : void 0, + schema: [{ + additionalProperties: false, + properties: ruleOptionsSchema, + type: 'object' + }], + type: ruleType }, create: (esLintRuleContext) => ({ - Program: () => { - const context = new RuleContext(esLintRuleContext, fixProgram); - return lintProgram(context); - } + Program: () => lintFunction(new RuleContext(esLintRuleContext, ruleOptionsDefault, fixFunction)) }) }; } @@ -52,31 +55,6 @@ class RuleContext { * Functions * * */ - fix(transformer) { - const factory = (transformationContext) => (sourceFile) => { - const internalVisitor = (node) => { - const visitedNode = transformer(transformationContext, node); - if (!visitedNode) { - return; - } - if (visitedNode === node) { - const subNodes = visitedNode.getChildren(), visitedSubNodes = TS.visitNodes(transformationContext.factory.createNodeArray(visitedNode.getChildren()), internalVisitor); - // @todo replacements not working :( - subNodes.splice(0, subNodes.length, ...visitedSubNodes); - } - return visitedNode; - }; - internalVisitor(sourceFile); - return sourceFile; - }; - const sourceFile = TS - .transform(this.sourceFile, [factory], Object.assign(Object.assign({}, TS.getDefaultCompilerOptions()), { removeComments: false, target: TS.ScriptTarget.ES2021 })) - .transformed[0]; - this.fixes.push({ - range: [sourceFile.pos, sourceFile.end], - text: sourceFile.text - }); - } report(line, column, message) { const report = { loc: { @@ -94,10 +72,6 @@ class RuleContext { } this.esLintContext.report(report); } - replace(node, replacement) { - const sourceFile = this.sourceFile, text = sourceFile.getFullText(), leadingText = text.substr(0, node.pos), trailingText = text.substr(node.end); - sourceFile.text = leadingText + replacement + trailingText; - } } exports.RuleContext = RuleContext; /* * diff --git a/lib/SourceCode.js b/lib/SourceCode.js new file mode 100644 index 0000000..9acd47b --- /dev/null +++ b/lib/SourceCode.js @@ -0,0 +1,75 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SourceCode = void 0; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const SourceLine_1 = require("./SourceLine"); +/* * + * + * Class + * + * */ +class SourceCode { + /* * + * + * Constructor + * + * */ + constructor(fileName, sourceCode) { + this.fileName = fileName; + this.lines = []; + this.parse(sourceCode); + } + /* * + * + * Functions + * + * */ + parse(sourceCode, replace = false) { + const lines = this.lines; + if (replace) { + lines.length = 0; + } + if (!sourceCode) { + return; + } + const scanner = TS.createScanner(TS.ScriptTarget.Latest, false); + let kind, line = new SourceLine_1.default(), text; + scanner.setText(sourceCode); + do { + kind = scanner.scan(); + text = scanner.getTokenText(); + line.tokens.push({ + kind, + text + }); + if (kind === TS.SyntaxKind.NewLineTrivia || + kind === TS.SyntaxKind.EndOfFileToken) { + lines.push(line); + line = new SourceLine_1.default(); + } + } while (kind !== TS.SyntaxKind.EndOfFileToken); + } + toString() { + const lines = this.lines; + let text = ''; + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + text += lines[i].toString(); + } + return text; + } +} +exports.SourceCode = SourceCode; +/* * + * + * Default Export + * + * */ +exports.default = SourceCode; diff --git a/lib/SourceLine.js b/lib/SourceLine.js new file mode 100644 index 0000000..429f1ca --- /dev/null +++ b/lib/SourceLine.js @@ -0,0 +1,49 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SourceLine = void 0; +/* * + * + * Class + * + * */ +class SourceLine { + constructor() { + /* * + * + * Properties + * + * */ + this.tokens = []; + } + /* * + * + * Functions + * + * */ + getLength() { + const tokens = this.tokens; + let length = 0; + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + length += tokens[i].text.length; + } + return length; + } + toString() { + const tokens = this.tokens; + let text = ''; + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + text += tokens[i].text; + } + return text; + } +} +exports.SourceLine = SourceLine; +/* * + * + * Default Export + * + * */ +exports.default = SourceLine; diff --git a/lib/Utilities.js b/lib/Utilities.js new file mode 100644 index 0000000..f7ebd8d --- /dev/null +++ b/lib/Utilities.js @@ -0,0 +1,93 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.trimAll = exports.isDocumentedNode = exports.indent = exports.getJSDocs = void 0; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +/* * + * + * Functions + * + * */ +function getJSDocs(sourceLine) { + const code = sourceLine.tokens + .filter(token => token.kind === TS.SyntaxKind.MultiLineCommentTrivia) + .map(token => token.text) + .join(''); + if (!code.length) { + return []; + } + const source = TS.createSourceFile('', code, TS.ScriptTarget.Latest), jsDocs = [], extract = (node) => { + if (isDocumentedNode(node)) { + const jsDoc = node.jsDoc; + for (let i = 0, iEnd = jsDoc.length; i < iEnd; ++i) { + if (!jsDocs.includes(jsDoc[i])) { + jsDocs.push(jsDoc[i]); + } + } + } + node.getChildren(source).forEach(extract); + }; + extract(source); + return jsDocs; +} +exports.getJSDocs = getJSDocs; +/** + * Returns a indented string, that fits into a specific width and spans over + * several lines. + * + * @param text + * The string to pad. + * + * @param linePrefix + * The prefix for each line. + * + * @param wrap + * The maximum width of the padded string. + */ +function indent(text, linePrefix = '', wrap = 80) { + const fragments = text.split(/\s/gmu); + let newLine = true, line = '', paddedStr = ''; + fragments.forEach(fragment => { + if (!newLine && fragment === '') { + paddedStr += (line.trimRight() + '\n' + + linePrefix.trimRight() + '\n'); + newLine = true; + return; + } + if (!newLine && line.length + fragment.length + 1 > wrap) { + paddedStr += line.trimRight() + '\n'; + newLine = true; + } + if (newLine) { + line = linePrefix + fragment; + newLine = false; + } + else { + line += ' ' + fragment; + } + }); + return (newLine ? paddedStr : paddedStr + line.trimRight() + '\n'); +} +exports.indent = indent; +function isDocumentedNode(node) { + return (typeof node.jsDoc === 'object'); +} +exports.isDocumentedNode = isDocumentedNode; +function trimAll(text, keepParagraphs = false) { + if (keepParagraphs) { + const fragments = text.split(/\n\s*\n/gu), trimmed = []; + for (let i = 0, iEnd = fragments.length; i < iEnd; ++i) { + trimmed.push(trimAll(fragments[i])); + } + return trimmed.join('\n\n'); + } + return text.replace(/\s+/gu, ' ').trim(); +} +exports.trimAll = trimAll; diff --git a/lib/rules/pretty-length.js b/lib/rules/pretty-length.js index 83adb05..d11be38 100644 --- a/lib/rules/pretty-length.js +++ b/lib/rules/pretty-length.js @@ -9,14 +9,14 @@ * * */ const FS = require("fs"); -const TS = require("typescript"); +const U = require("../Utilities"); const RuleContext_1 = require("../RuleContext"); /* * * * Constants * * */ -const messageTemplate = 'Code line exceeds limit of {0} characters.'; +const messageTemplate = 'Line exceeds limit of {0} characters.'; const optionsDefaults = { maximalLength: 80 }; @@ -28,13 +28,6 @@ const optionsSchema = { 'type': 'integer' } }; -const regExps = { - breakSplit: /\n/gu, - docletTag: /^(@[A-Za-z][\w-]+)\s+(?:(\{[^\}]+\})\s+)?([\s\S]*)$/u, - docletTagSplit: /(?<=\s)(?=@[A-Za-z])/gu, - docletTagTrim: /^\s*\*\s+/mu, - spaceSplit: /\s+/gsu -}; /* * * * Functions @@ -42,36 +35,36 @@ const regExps = { * */ function fix(context) { const docletASTs = []; - context.fix((transformationConext, node) => { - if (TS.isJSDoc(node)) { - process.stdout.write('.'); - return transformationConext.factory.createJSDocComment((TS.getTextOfJSDocComment(node.comment) || '').toUpperCase(), node.tags); - } - return node; - }); - FS.writeFileSync(context.sourceFile.fileName + '.json', JSON.stringify(docletASTs, ['comment', 'name', 'tags', 'text'], ' ')); + context.sourceCode.lines.forEach(sourceLine => docletASTs.push(...U.getJSDocs(sourceLine))); + FS.writeFileSync(context.sourceCode.fileName + '.json', JSON.stringify(docletASTs, [ + 'name', + 'comment', + 'text', + 'tags', + 'kind', + 'flags', + 'modifierFlagsCache', + 'transformFlags', + 'end', + 'pos', + ], ' ')); +} +function fixJSDoc(context) { + // @todo } function lint(context) { - const sourceTextLines = context.sourceFile.getFullText().split(regExps.breakSplit), { ignorePattern, maximalLength } = setupOptions(context.options), ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); - console.log('Checking', context.sourceFile.fileName); - for (let line = 0, lineEnd = sourceTextLines.length, sourceTextLength, sourceTextLine; line < lineEnd; ++line) { - sourceTextLine = sourceTextLines[line]; - sourceTextLength = sourceTextLine.length; + const sourceLines = context.sourceCode.lines, { ignorePattern, maximalLength } = context.options, ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); + console.log('Checking', context.sourceCode.fileName); + for (let line = 0, lineEnd = sourceLines.length, sourceLine, sourceLineLength; line < lineEnd; ++line) { + sourceLine = sourceLines[line]; if (ignoreRegExp && - ignoreRegExp.test(sourceTextLine)) { + ignoreRegExp.test(sourceLine.toString())) { continue; } - if (sourceTextLength > maximalLength) { + sourceLineLength = sourceLine.getLength(); + if (sourceLineLength > maximalLength) { context.report(line, maximalLength - 1, messageTemplate.replace('{0}', `${maximalLength}`)); } } } -function setupOptions(options) { - return { - ignorePattern: options.ignorePattern || optionsDefaults.ignorePattern, - indentSize: options.indentSize || optionsDefaults.indentSize, - maximalLength: (options.maximalLength || - optionsDefaults.maximalLength) - }; -} -module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, lint, fix); +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, fix); diff --git a/sources/HighchartsSettings.d.ts b/sources/HighchartsSettings.d.ts new file mode 100644 index 0000000..19e45c3 --- /dev/null +++ b/sources/HighchartsSettings.d.ts @@ -0,0 +1,21 @@ +/* * + * + * Declarations + * + * */ + + +export interface HighchartsSetttings { + indentSize: number; + indentWithTabs: boolean; +} + + +/* * + * + * Default Export + * + * */ + + +export default HighchartsSettings; diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index 3b54a8b..92fc484 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -16,14 +16,15 @@ import type * as ESLint from 'eslint'; import type * as JSONSchema from 'json-schema'; import type FixFunction from './FixFunction'; +import type LineSegment from './SourceLine'; import type LintFunction from './LintFunction'; import type RuleOptions from './RuleOptions'; -import type TransformerFunction from './TransformerFunction'; import * as FS from 'fs'; import * as Path from 'path'; import * as TS from 'typescript'; import RuleType from './RuleType'; +import SourceCode from './SourceCode'; /* * @@ -46,24 +47,27 @@ export class RuleContext { public static setupRuleExport ( ruleType: RuleType, ruleOptionsSchema: JSONSchema.JSONSchema4, - lintProgram: LintFunction, - fixProgram?: FixFunction + ruleOptionsDefault: T, + lintFunction: LintFunction, + fixFunction?: FixFunction ): ESLint.Rule.RuleModule { return { meta: { - fixable: fixProgram ? 'code' : void 0, - type: ruleType, + fixable: fixFunction ? 'code' : void 0, + schema: [{ + additionalProperties: false, + properties: ruleOptionsSchema, + type: 'object' + }], + type: ruleType }, create: (esLintRuleContext: ESLint.Rule.RuleContext) => ( { - Program: () => { - const context = new RuleContext( - esLintRuleContext, - fixProgram - ); - - return lintProgram(context); - } + Program: () => lintFunction(new RuleContext( + esLintRuleContext, + ruleOptionsDefault, + fixFunction + )) } ) }; @@ -79,23 +83,25 @@ export class RuleContext { private constructor ( esLintContext: ESLint.Rule.RuleContext, + ruleOptionsDefault: T, fixFunction?: FixFunction ) { + this.changes = []; this.cwd = esLintContext.getCwd(); this.esLintContext = esLintContext; this.fixes = []; - this.options = (esLintContext.options[1] || {}); this.settings = ((esLintContext.settings || {}).highcharts || {}); + this.options = { + ...ruleOptionsDefault, + ...this.settings, + ...(esLintContext.options[1] || {}) + }; this.fixFunction = fixFunction; - const filePath = Path.relative(this.cwd, esLintContext.getFilename()) - - this.sourceFile = TS.createSourceFile( - filePath, - FS.readFileSync(filePath).toString(), - TS.ScriptTarget.Latest, - true, - TS.ScriptKind.Unknown + this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()), + this.sourceCode = new SourceCode( + this.sourcePath, + FS.readFileSync(this.sourcePath).toString() ); } @@ -107,6 +113,9 @@ export class RuleContext { * */ + private changes: Array; + + private cwd: string; @@ -119,13 +128,16 @@ export class RuleContext { private fixFunction?: FixFunction; - public options: Partial; + public options: T; public settings: ESLint.Rule.RuleContext['settings']; - public sourceFile: TS.SourceFile; + public sourceCode: SourceCode; + + + public sourcePath: string; /* * @@ -135,60 +147,6 @@ export class RuleContext { * */ - public fix ( - transformer: TransformerFunction - ) { - const factory: TS.TransformerFactory = ( - transformationContext: TS.TransformationContext - ) => ( - sourceFile: TS.SourceFile - ) => { - const internalVisitor: TS.Visitor = (node) => { - const visitedNode = transformer(transformationContext, node); - - if (!visitedNode) { - return; - } - - if (visitedNode === node) { - const subNodes = visitedNode.getChildren(), - visitedSubNodes = TS.visitNodes( - transformationContext.factory.createNodeArray( - visitedNode.getChildren() - ), - internalVisitor - ); - // @todo replacements not working :( - subNodes.splice(0, subNodes.length, ...visitedSubNodes); - } - - return visitedNode; - }; - - internalVisitor(sourceFile); - - return sourceFile; - }; - - const sourceFile = TS - .transform( - this.sourceFile, - [factory], - { - ...TS.getDefaultCompilerOptions(), - removeComments: false, - target: TS.ScriptTarget.ES2021 - } - ) - .transformed[0]; - - this.fixes.push({ - range: [sourceFile.pos, sourceFile.end], - text: sourceFile.text - }); - } - - public report ( line: number, column: number, @@ -214,19 +172,6 @@ export class RuleContext { } - public replace ( - node: TS.Node, - replacement: string - ) { - const sourceFile = this.sourceFile, - text = sourceFile.getFullText(), - leadingText = text.substr(0, node.pos), - trailingText = text.substr(node.end); - - sourceFile.text = leadingText + replacement + trailingText; - } - - } diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts new file mode 100644 index 0000000..3f4ba5b --- /dev/null +++ b/sources/SourceCode.ts @@ -0,0 +1,131 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import SourceLine from './SourceLine'; + + +/* * + * + * Class + * + * */ + + +export class SourceCode { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + fileName: string, + sourceCode: string + ) { + this.fileName = fileName; + this.lines = []; + this.parse(sourceCode); + } + + + /* * + * + * Properties + * + * */ + + + public readonly fileName: string; + + + public readonly lines: Array; + + + /* * + * + * Functions + * + * */ + + + public parse ( + sourceCode: string, + replace = false + ) { + const lines = this.lines; + + if (replace) { + lines.length = 0; + } + + if (!sourceCode) { + return; + } + + const scanner = TS.createScanner(TS.ScriptTarget.Latest, false); + + let kind: TS.SyntaxKind, + line = new SourceLine(), + text: string; + + scanner.setText(sourceCode); + + do { + kind = scanner.scan(); + text = scanner.getTokenText(); + + line.tokens.push({ + kind, + text + }); + + if ( + kind === TS.SyntaxKind.NewLineTrivia || + kind === TS.SyntaxKind.EndOfFileToken + ) { + lines.push(line); + line = new SourceLine(); + } + + } while (kind !== TS.SyntaxKind.EndOfFileToken) + } + + + public toString (): string { + const lines = this.lines; + + let text = ''; + + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + text += lines[i].toString(); + } + + return text; + } +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceCode; diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts new file mode 100644 index 0000000..4cb7d80 --- /dev/null +++ b/sources/SourceLine.ts @@ -0,0 +1,82 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import type SourceToken from './SourceToken'; + + +/* * + * + * Class + * + * */ + + +export class SourceLine { + + + /* * + * + * Properties + * + * */ + + + public readonly tokens: Array = []; + + + /* * + * + * Functions + * + * */ + + + public getLength(): number { + const tokens = this.tokens; + + let length = 0; + + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + length += tokens[i].text.length; + } + + return length; + } + + + public toString(): string { + const tokens = this.tokens; + + let text = ''; + + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + text += tokens[i].text; + } + + return text; + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceLine; diff --git a/sources/SourceToken.d.ts b/sources/SourceToken.d.ts new file mode 100644 index 0000000..28cd5c9 --- /dev/null +++ b/sources/SourceToken.d.ts @@ -0,0 +1,31 @@ +/* * + * + * Imports + * + * */ + + +import type * as TS from 'typescript'; + + +/* * + * + * Declarations + * + * */ + + +export interface SourceToken { + kind: TS.SyntaxKind; + text: string; +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceToken; diff --git a/sources/TransformerFunction.d.ts b/sources/TransformerFunction.d.ts deleted file mode 100644 index d2a5cb2..0000000 --- a/sources/TransformerFunction.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @author Sophie Bremer - */ - - -/* * - * - * Imports - * - * */ - - -import type * as TS from 'typescript'; - - -/* * - * - * Declarations - * - * */ - - -export type TransformerFunction = ( - transformationContext: TS.TransformationContext, - node: TS.Node -) => TS.Node; - - -/* * - * - * Default Export - * - * */ - - -export default TransformerFunction; diff --git a/sources/Utilities.ts b/sources/Utilities.ts new file mode 100644 index 0000000..9b6af21 --- /dev/null +++ b/sources/Utilities.ts @@ -0,0 +1,149 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import SourceLine from './SourceLine'; + + +/* * + * + * Declarations + * + * */ + + +export interface DocumentedNode extends TS.Node { + jsDoc: Array; +} + + +/* * + * + * Functions + * + * */ + + +export function getJSDocs( + sourceLine: SourceLine +): Array { + const code = sourceLine.tokens + .filter(token => token.kind === TS.SyntaxKind.MultiLineCommentTrivia) + .map(token => token.text) + .join(''); + + if (!code.length) { + return []; + } + + const source = TS.createSourceFile('', code, TS.ScriptTarget.Latest), + jsDocs: Array = [], + extract = (node: TS.Node) => { + if (isDocumentedNode(node)) { + const jsDoc = node.jsDoc; + for (let i = 0, iEnd = jsDoc.length; i < iEnd; ++i) { + if (!jsDocs.includes(jsDoc[i])) { + jsDocs.push(jsDoc[i]); + } + } + } + node.getChildren(source).forEach(extract); + }; + + extract(source); + + return jsDocs; +} + + +/** + * Returns a indented string, that fits into a specific width and spans over + * several lines. + * + * @param text + * The string to pad. + * + * @param linePrefix + * The prefix for each line. + * + * @param wrap + * The maximum width of the padded string. + */ +export function indent ( + text: string, + linePrefix = '', + wrap = 80 +): string { + const fragments = text.split(/\s/gmu); + + let newLine = true, + line = '', + paddedStr = ''; + + fragments.forEach(fragment => { + + if (!newLine && fragment === '') { + paddedStr += ( + line.trimRight() + '\n' + + linePrefix.trimRight() + '\n' + ); + newLine = true; + return; + } + + if (!newLine && line.length + fragment.length + 1 > wrap) { + paddedStr += line.trimRight() + '\n'; + newLine = true; + } + + if (newLine) { + line = linePrefix + fragment; + newLine = false; + } + else { + line += ' ' + fragment; + } + }); + + return (newLine ? paddedStr : paddedStr + line.trimRight() + '\n'); +} + + +export function isDocumentedNode( + node: T +): node is (T&DocumentedNode) { + return ( + typeof (node as unknown as Record).jsDoc === 'object' + ) +} + + +export function trimAll ( + text: string, + keepParagraphs = false +): string { + if (keepParagraphs) { + const fragments = text.split(/\n\s*\n/gu), + trimmed: Array = []; + + for (let i = 0, iEnd = fragments.length; i < iEnd; ++i) { + trimmed.push(trimAll(fragments[i])); + } + + return trimmed.join('\n\n'); + } + + return text.replace(/\s+/gu, ' ').trim(); +} diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index 9887c6e..7f108cd 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -16,8 +16,10 @@ import * as FS from 'fs'; import * as TS from 'typescript'; +import * as U from '../Utilities'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; +import SourceLine from '../SourceLine'; /* * @@ -44,7 +46,7 @@ interface PrettyLengthOptions extends RuleOptions { * */ -const messageTemplate = 'Code line exceeds limit of {0} characters.'; +const messageTemplate = 'Line exceeds limit of {0} characters.'; const optionsDefaults: PrettyLengthOptions = { @@ -62,15 +64,6 @@ const optionsSchema = { } -const regExps = { - breakSplit: /\n/gu, - docletTag: /^(@[A-Za-z][\w-]+)\s+(?:(\{[^\}]+\})\s+)?([\s\S]*)$/u, - docletTagSplit: /(?<=\s)(?=@[A-Za-z])/gu, - docletTagTrim: /^\s*\*\s+/mu, - spaceSplit: /\s+/gsu -} - - /* * * * Functions @@ -83,55 +76,71 @@ function fix( ): void { const docletASTs: Array = []; - context.fix((transformationConext, node) => { - if (TS.isJSDoc(node)) { - process.stdout.write('.'); - return transformationConext.factory.createJSDocComment( - (TS.getTextOfJSDocComment(node.comment) || '').toUpperCase(), - node.tags - ); - } - return node; - }); + context.sourceCode.lines.forEach( + sourceLine => docletASTs.push(...U.getJSDocs(sourceLine)) + ); FS.writeFileSync( - context.sourceFile.fileName + '.json', - JSON.stringify(docletASTs, ['comment', 'name', 'tags', 'text'], ' ') + context.sourceCode.fileName + '.json', + JSON.stringify( + docletASTs, + [ + 'name', + 'comment', + 'text', + 'tags', + 'kind', + 'flags', + 'modifierFlagsCache', + 'transformFlags', + 'end', + 'pos', + ], + ' ' + ) ); } +function fixJSDoc( + context: PrettyLengthContext +): void { + // @todo +} + + function lint ( context: PrettyLengthContext ) { - const sourceTextLines = context.sourceFile.getFullText().split(regExps.breakSplit), + const sourceLines = context.sourceCode.lines, { ignorePattern, maximalLength - } = setupOptions(context.options), + } = context.options, ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); - console.log('Checking', context.sourceFile.fileName); + console.log('Checking', context.sourceCode.fileName); for ( let line = 0, - lineEnd = sourceTextLines.length, - sourceTextLength: number, - sourceTextLine: string; + lineEnd = sourceLines.length, + sourceLine: SourceLine, + sourceLineLength: number; line < lineEnd; ++line ) { - sourceTextLine = sourceTextLines[line]; - sourceTextLength = sourceTextLine.length; + sourceLine = sourceLines[line]; if ( ignoreRegExp && - ignoreRegExp.test(sourceTextLine) + ignoreRegExp.test(sourceLine.toString()) ) { continue; } - if (sourceTextLength > maximalLength) { + sourceLineLength = sourceLine.getLength(); + + if (sourceLineLength > maximalLength) { context.report( line, maximalLength - 1, @@ -142,20 +151,6 @@ function lint ( } -function setupOptions( - options: Partial -): PrettyLengthOptions { - return { - ignorePattern: options.ignorePattern || optionsDefaults.ignorePattern, - indentSize: options.indentSize || optionsDefaults.indentSize, - maximalLength: ( - options.maximalLength || - optionsDefaults.maximalLength - ) - }; -} - - /* * * * Default Export @@ -166,6 +161,7 @@ function setupOptions( export = RuleContext.setupRuleExport( 'layout', optionsSchema, + optionsDefaults, lint, fix ); From b800155fea0645cb30c95fb334242e021e175cda Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 21 Jan 2022 14:30:37 +0100 Subject: [PATCH 05/54] Added source styling detection. --- sources/SourceCode.ts | 56 +++++++++++++++++++++++++++++++++++++++++++ sources/SourceLine.ts | 13 ++++++++++ 2 files changed, 69 insertions(+) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 3f4ba5b..1630460 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -13,6 +13,8 @@ * */ +import type SourceToken from './SourceToken'; + import * as TS from 'typescript'; import SourceLine from './SourceLine'; @@ -64,6 +66,60 @@ export class SourceCode { * */ + public getLinePos( + sourceLine: SourceLine + ): number { + const lines = this.lines, + lineIndex = lines.indexOf(sourceLine); + + if (lineIndex === -1) { + return -1; + } + + let pos = 0; + + for (let i = 0, iEnd = lineIndex; i < iEnd; ++i) { + pos += lines[i].getLength() + 1; // + line break + } + + return pos; + } + + + public getTokenPos( + sourceToken: SourceToken + ): number { + const lines = this.lines; + + let line: SourceLine, + pos = 0, + tokenIndex = -1; + + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + line = lines[i]; + tokenIndex = line.tokens.indexOf(sourceToken); + + if (tokenIndex >= 0) { + const tokens = line.tokens; + + for (let j = 0, jEnd = tokenIndex; j < jEnd; ++j) { + pos += tokens[j].text.length; + } + + break; + } + + pos += lines[i].getLength() + 1; // + line break + } + + if (tokenIndex === -1) { + return -1; + } + + return pos; + } + + public parse ( sourceCode: string, replace = false diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 4cb7d80..0699c42 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -15,6 +15,8 @@ import type SourceToken from './SourceToken'; +import * as TS from 'typescript'; + /* * * @@ -43,6 +45,17 @@ export class SourceLine { * */ + public getIndent(): number { + const firstToken = this.tokens[0]; + + if (firstToken.kind === TS.SyntaxKind.WhitespaceTrivia) { + return firstToken.text.length; + } + + return 0; + } + + public getLength(): number { const tokens = this.tokens; From ac50d4148ed58810be6cb9d683153f77373f6bf0 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 11 Apr 2022 10:47:08 +0200 Subject: [PATCH 06/54] Improved source doclet parsing. --- sources/SourceDoclet.ts | 224 +++++++++++++++++++++++++++++++++++ sources/SourceDocletTag.d.ts | 34 ++++++ sources/Utilities.ts | 63 +++++----- 3 files changed, 287 insertions(+), 34 deletions(-) create mode 100644 sources/SourceDoclet.ts create mode 100644 sources/SourceDocletTag.d.ts diff --git a/sources/SourceDoclet.ts b/sources/SourceDoclet.ts new file mode 100644 index 0000000..cfd375e --- /dev/null +++ b/sources/SourceDoclet.ts @@ -0,0 +1,224 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import type SourceDocletTag from './SourceDocletTag'; +import type SourceToken from './SourceToken'; + +import * as TS from 'typescript'; +import * as U from './Utilities'; +import SourceLine from './SourceLine'; + + +/* * + * + * Class + * + * */ + + +export class SourceDoclet extends SourceLine implements SourceToken { + + + /* * + * + * Static Properties + * + * */ + + public static parse ( + sourceToken: SourceToken + ): (SourceDoclet|null) { + if ( + sourceToken.kind === TS.SyntaxKind.MultiLineCommentTrivia && + sourceToken.text.startsWith('/**') + ) { + return new SourceDoclet(sourceToken); + } + + return null; + } + + + /* * + * + * Constructor + * + * */ + + + private constructor ( + sourceToken: SourceToken + ) { + super(); + + this.comment = ''; + this.text = sourceToken.text; + this.tokens = []; + + this.parseDoclet(sourceToken); + } + + + /* * + * + * Properties + * + * */ + + + public comment: string; + + + public readonly kind = TS.SyntaxKind.JSDocComment; + + + public readonly text: string; + + + public tokens: Array; + + + /* * + * + * Functions + * + * */ + + private parseDoclet( + sourceToken: SourceToken + ): Array { + const jsDocs: Array = []; + + if ( + sourceToken.kind === TS.SyntaxKind.MultiLineCommentTrivia && + sourceToken.text.startsWith('/**') + ) { + return jsDocs; + } + + const source = TS.createSourceFile( + '', + sourceToken.text, + TS.ScriptTarget.Latest + ); + // jsDocs: Array = [], + // extractDoclet = (node: TS.Node) => { + // if (U.isDocumentedNode(node)) { + // const jsDoc = node.jsDoc; + + // for (let i = 0, iEnd = jsDoc.length; i < iEnd; ++i) { + // if (!jsDocs.includes(jsDoc[i])) { + // jsDocs.push(jsDoc[i]); + // } + // } + // } + + // node.getChildren(source).forEach(extractDoclet); + // }; + + // extractDoclet(source); + + const sourceNodes = source.getChildren(); + + console.log(sourceNodes); + + const tags = (jsDocs[0].tags || []).map(tag => tag), + tokens = this.tokens; + + for ( + let i = 0, + iEnd = tags.length, + tag: TS.JSDocTag, + token: SourceDocletTag; + i < iEnd; + ++i + ) { + tag = tags[i]; + + if (TS.isJSDocSignature(tag)) { + const parameterTags = tag.parameters; + + for (let i = 0, iEnd = parameterTags.length; i < iEnd; ++i) { + const parameterTag = parameterTags[i], + parameterToken: SourceDocletTag = { + comment: TS.getTextOfJSDocComment(parameterTag.comment), + kind: parameterTag.kind, + paramName: parameterTag.name.getText(), + tagName: parameterTag.tagName.text, + text: parameterTag.getText() + }; + + if (parameterTag.typeExpression) { + parameterToken.tagType = parameterTag.typeExpression.getText(); + } + + tokens.push(parameterToken); + } + + if (tag.type) { + const returnTag = tag.type, + returnToken: SourceDocletTag = { + comment: TS.getTextOfJSDocComment(returnTag.comment), + kind: returnTag.kind, + tagName: returnTag.tagName.text, + text: returnTag.getText() + }; + + if (returnTag.typeExpression) { + returnToken.tagType = returnTag.typeExpression.getText(); + } + + tokens.push(returnToken); + } + + continue; + } + + token = { + comment: TS.getTextOfJSDocComment(tag.comment), + kind: tag.kind, + tagName: tag.tagName.text, + text: tag.getText() + }; + + if (TS.isJSDocFunctionType(tag)) { + token.tagType = tag.type?.getText(); + } + + if (TS.isJSDocParameterTag(tag)) { + token.paramName = tag.name.getText(); + + if (tag.typeExpression) { + token.tagType = tag.typeExpression.getText(); + } + } + + tokens.push(token); + } + + return jsDocs; + } + +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceDoclet; diff --git a/sources/SourceDocletTag.d.ts b/sources/SourceDocletTag.d.ts new file mode 100644 index 0000000..a7c2a26 --- /dev/null +++ b/sources/SourceDocletTag.d.ts @@ -0,0 +1,34 @@ +/* * + * + * Imports + * + * */ + + +import type * as U from './Utilities'; +import type SourceToken from './SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +export interface SourceDocletTag extends SourceToken { + comment?: string; + paramName?: string; + tagName: string; + tagType?: string; +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceDocletTag; diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 9b6af21..1406443 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -14,7 +14,6 @@ import * as TS from 'typescript'; -import SourceLine from './SourceLine'; /* * @@ -29,6 +28,34 @@ export interface DocumentedNode extends TS.Node { } +export type JSDocTagKind = ( + TS.SyntaxKind.JSDocAugmentsTag| + TS.SyntaxKind.JSDocAuthorTag| + TS.SyntaxKind.JSDocCallbackTag| + TS.SyntaxKind.JSDocClassTag| + TS.SyntaxKind.JSDocDeprecatedTag| + TS.SyntaxKind.JSDocEnumTag| + TS.SyntaxKind.JSDocImplementsTag| + TS.SyntaxKind.JSDocOverrideTag| + TS.SyntaxKind.JSDocParameterTag| + TS.SyntaxKind.JSDocPrivateTag| + TS.SyntaxKind.JSDocPropertyTag| + TS.SyntaxKind.JSDocProtectedTag| + TS.SyntaxKind.JSDocPublicTag| + TS.SyntaxKind.JSDocReadonlyTag| + TS.SyntaxKind.JSDocReturnTag| + TS.SyntaxKind.JSDocSeeTag| + TS.SyntaxKind.JSDocTag| + TS.SyntaxKind.JSDocTemplateTag| + TS.SyntaxKind.JSDocThisTag| + TS.SyntaxKind.JSDocTypeTag| + TS.SyntaxKind.JSDocTypedefTag +); + + +export type UnknownObject = Record; + + /* * * * Functions @@ -36,38 +63,6 @@ export interface DocumentedNode extends TS.Node { * */ -export function getJSDocs( - sourceLine: SourceLine -): Array { - const code = sourceLine.tokens - .filter(token => token.kind === TS.SyntaxKind.MultiLineCommentTrivia) - .map(token => token.text) - .join(''); - - if (!code.length) { - return []; - } - - const source = TS.createSourceFile('', code, TS.ScriptTarget.Latest), - jsDocs: Array = [], - extract = (node: TS.Node) => { - if (isDocumentedNode(node)) { - const jsDoc = node.jsDoc; - for (let i = 0, iEnd = jsDoc.length; i < iEnd; ++i) { - if (!jsDocs.includes(jsDoc[i])) { - jsDocs.push(jsDoc[i]); - } - } - } - node.getChildren(source).forEach(extract); - }; - - extract(source); - - return jsDocs; -} - - /** * Returns a indented string, that fits into a specific width and spans over * several lines. @@ -125,7 +120,7 @@ export function isDocumentedNode( node: T ): node is (T&DocumentedNode) { return ( - typeof (node as unknown as Record).jsDoc === 'object' + typeof (node as unknown as DocumentedNode).jsDoc === 'object' ) } From f9265d28bda82262f3d955f64e51728d978d7e0c Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 11 Apr 2022 14:52:16 +0200 Subject: [PATCH 07/54] Added SourceTree with SourceNodes. --- sources/RuleContext.ts | 37 ++++++++-- sources/SourceNode.ts | 152 +++++++++++++++++++++++++++++++++++++++++ sources/SourceTree.ts | 146 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+), 7 deletions(-) create mode 100644 sources/SourceNode.ts create mode 100644 sources/SourceTree.ts diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index 92fc484..faba76c 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -16,7 +16,6 @@ import type * as ESLint from 'eslint'; import type * as JSONSchema from 'json-schema'; import type FixFunction from './FixFunction'; -import type LineSegment from './SourceLine'; import type LintFunction from './LintFunction'; import type RuleOptions from './RuleOptions'; @@ -25,6 +24,7 @@ import * as Path from 'path'; import * as TS from 'typescript'; import RuleType from './RuleType'; import SourceCode from './SourceCode'; +import SourceTree from './SourceTree'; /* * @@ -98,11 +98,7 @@ export class RuleContext { }; this.fixFunction = fixFunction; - this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()), - this.sourceCode = new SourceCode( - this.sourcePath, - FS.readFileSync(this.sourcePath).toString() - ); + this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()); } @@ -113,6 +109,12 @@ export class RuleContext { * */ + private _sourceCode?: SourceCode; + + + private _sourceTree?: SourceTree; + + private changes: Array; @@ -134,12 +136,33 @@ export class RuleContext { public settings: ESLint.Rule.RuleContext['settings']; - public sourceCode: SourceCode; + public get sourceCode (): SourceCode { + if (!this._sourceCode) { + this._sourceCode = new SourceCode( + this.sourcePath, + FS.readFileSync(this.sourcePath).toString() + ); + } + + return this._sourceCode; + } public sourcePath: string; + public get sourceTree (): SourceTree { + if (!this._sourceTree) { + this._sourceTree = new SourceTree( + this.sourcePath, + FS.readFileSync(this.sourcePath).toString() + ); + } + + return this._sourceTree; + } + + /* * * * Functions diff --git a/sources/SourceNode.ts b/sources/SourceNode.ts new file mode 100644 index 0000000..be2c69a --- /dev/null +++ b/sources/SourceNode.ts @@ -0,0 +1,152 @@ +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import SourceToken from './SourceToken'; + + +/* * + * + * Class + * + * */ + + +export class SourceNode implements SourceToken { + + + /* * + * + * Static Properties + * + * */ + + + public static parse ( + sourceFile: TS.SourceFile, + node: TS.Node + ): SourceNode { + const nodeChildren = node.getChildren(sourceFile), + sourceNode = new SourceNode(node.kind); + + if (nodeChildren.length) { + const sourceChildren: Array = sourceNode.children = []; + + for ( + let i = 0, + iEnd = nodeChildren.length, + sourceChild: SourceNode; + i < iEnd; + ++i + ) { + sourceChild = SourceNode.parse(sourceFile, nodeChildren[i]); + sourceChild.parent = sourceNode; + sourceChildren.push(sourceChild); + } + } else { + sourceNode.text = node.getText(sourceFile); + } + + return sourceNode; + } + + + /* * + * + * Constructor + * + * */ + + public constructor( + kind: TS.SyntaxKind, + text: string = '' + ) { + this.kind = kind; + this.text = text; + } + + /* * + * + * Properties + * + * */ + + + public children?: Array; + + + public kind: TS.SyntaxKind; + + + public parent?: SourceNode; + + + public text: string; + + + /* * + * + * Functions + * + * */ + + + public toString(): string { + const children = this.children; + + let text = this.text; + + if (children) { + for (let i = 0, iEnd = children.length; i < iEnd; ++i) { + text += children.toString(); + } + } + + return text; + } + + + public toTokens(): Array { + const children = this.children, + tokens: Array = [this]; + + if (children) { + for ( + let i = 0, + iEnd = children.length, + childrensChildren: Array; + i < iEnd; + ++i + ) { + childrensChildren = children[i].toTokens(); + + for ( + let j = 0, + jEnd = childrensChildren.length; + j < jEnd; + ++j + ) { + tokens.push(childrensChildren[j]); + } + } + } + + return tokens; + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceNode; diff --git a/sources/SourceTree.ts b/sources/SourceTree.ts new file mode 100644 index 0000000..a1f8940 --- /dev/null +++ b/sources/SourceTree.ts @@ -0,0 +1,146 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import SourceNode from './SourceNode'; +import SourceToken from './SourceToken'; + + +/* * + * + * Class + * + * */ + + +export class SourceTree { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + fileName: string, + sourceCode: string + ) { + this.fileName = fileName; + this.nodes = []; + this.parse(sourceCode); + } + + + /* * + * + * Properties + * + * */ + + + public readonly fileName: string; + + + public readonly nodes: Array; + + + /* * + * + * Functions + * + * */ + + + public parse ( + sourceCode: string, + replace = false + ) { + const nodes = this.nodes; + + if (replace) { + nodes.length = 0; + } + + if (!sourceCode) { + return; + } + + const sourceFile = TS.createSourceFile( + '', + sourceCode, + TS.ScriptTarget.Latest + ), + tree = SourceNode.parse(sourceFile, sourceFile), + roots = tree.children?.[0].children; + + if (roots) { + for (let i = 0, iEnd = roots.length; i < iEnd; ++i) { + nodes.push(roots[i]); + } + } + } + + + public toTokens (): Array { + const nodes = this.nodes, + tokens: Array = []; + + for ( + let i = 0, + iEnd = nodes.length, + nodesChildren: Array; + i < iEnd; + ++i + ) { + nodesChildren = nodes[i].toTokens(); + + for ( + let j = 0, + jEnd = nodesChildren.length; + j < jEnd; + ++j + ) { + tokens.push(nodesChildren[j]); + } + } + + return tokens; + } + + + public toString (): string { + const nodes = this.nodes; + + let text = ''; + + for (let i = 0, iEnd = nodes.length; i < iEnd; ++i) { + text += nodes[i].toString(); + } + + return text; + } +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceTree; From bfcadc7355b3b5ced2aea8e236a5e8778d2dc841 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 11 Apr 2022 14:55:13 +0200 Subject: [PATCH 08/54] Added TypeScript debug rule. --- sources/rules/debug.ts | 106 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 sources/rules/debug.ts diff --git a/sources/rules/debug.ts b/sources/rules/debug.ts new file mode 100644 index 0000000..b50726c --- /dev/null +++ b/sources/rules/debug.ts @@ -0,0 +1,106 @@ +/** + * @fileoverview Debugs TypeScript tokens. + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; +import SourceToken from '../SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +type DebugContext = RuleContext; + + +interface DebugOptions extends RuleOptions { + onlyKindOf?: Array; +} + + +/* * + * + * Constants + * + * */ + + +const optionsDefaults: RuleOptions = {} + + +const optionsSchema = { + 'onlyKindOf': { + 'type': 'array' + } +} + + +/* * + * + * Functions + * + * */ + + +function lint ( + context: DebugContext +) { + const onlyKindOf = context.options.onlyKindOf, + sourceTree = context.sourceTree, + sourceTokens = sourceTree.toTokens(); + + for ( + let i = 0, + iEnd = sourceTokens.length, + sourcePosition: number = 0, + sourceToken: SourceToken; + i < iEnd; + ++i + ) { + sourceToken = sourceTokens[i]; + + if (onlyKindOf && !onlyKindOf.includes(sourceToken.kind)) { + continue; + } + + context.report( + 0, + 0, + `SyntaxKind: ${sourceToken.kind} ${TS.SyntaxKind[sourceToken.kind]} @ ${sourcePosition}` + ); + + sourcePosition += sourceToken.text.length; + } +} + + +/* * + * + * Default Export + * + * */ + + +export = RuleContext.setupRuleExport( + 'layout', + optionsSchema, + optionsDefaults, + lint +); From 5c7d435d60ef9da8af6eee8e3a0e50f1d9358cb7 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Tue, 12 Apr 2022 18:10:38 +0200 Subject: [PATCH 09/54] Added SourceParser. --- sources/SourceCode.ts | 3 +- sources/SourceDoclet.ts | 1 + sources/SourceNode.ts | 90 +++++---------- sources/SourceParser.ts | 249 ++++++++++++++++++++++++++++++++++++++++ sources/SourceTree.ts | 18 +-- sources/rules/debug.ts | 30 ++--- 6 files changed, 302 insertions(+), 89 deletions(-) create mode 100644 sources/SourceParser.ts diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 1630460..b324042 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -13,10 +13,9 @@ * */ -import type SourceToken from './SourceToken'; - import * as TS from 'typescript'; import SourceLine from './SourceLine'; +import SourceToken from './SourceToken'; /* * diff --git a/sources/SourceDoclet.ts b/sources/SourceDoclet.ts index cfd375e..aaa3653 100644 --- a/sources/SourceDoclet.ts +++ b/sources/SourceDoclet.ts @@ -37,6 +37,7 @@ export class SourceDoclet extends SourceLine implements SourceToken { * * */ + public static parse ( sourceToken: SourceToken ): (SourceDoclet|null) { diff --git a/sources/SourceNode.ts b/sources/SourceNode.ts index be2c69a..7a06d83 100644 --- a/sources/SourceNode.ts +++ b/sources/SourceNode.ts @@ -6,6 +6,7 @@ import * as TS from 'typescript'; +import SourceDoclet from './SourceDoclet'; import SourceToken from './SourceToken'; @@ -19,42 +20,6 @@ import SourceToken from './SourceToken'; export class SourceNode implements SourceToken { - /* * - * - * Static Properties - * - * */ - - - public static parse ( - sourceFile: TS.SourceFile, - node: TS.Node - ): SourceNode { - const nodeChildren = node.getChildren(sourceFile), - sourceNode = new SourceNode(node.kind); - - if (nodeChildren.length) { - const sourceChildren: Array = sourceNode.children = []; - - for ( - let i = 0, - iEnd = nodeChildren.length, - sourceChild: SourceNode; - i < iEnd; - ++i - ) { - sourceChild = SourceNode.parse(sourceFile, nodeChildren[i]); - sourceChild.parent = sourceNode; - sourceChildren.push(sourceChild); - } - } else { - sourceNode.text = node.getText(sourceFile); - } - - return sourceNode; - } - - /* * * * Constructor @@ -79,15 +44,18 @@ export class SourceNode implements SourceToken { public children?: Array; - public kind: TS.SyntaxKind; + public doclet?: string; - public parent?: SourceNode; + public kind: TS.SyntaxKind; public text: string; + public type?: string; + + /* * * * Functions @@ -95,34 +63,23 @@ export class SourceNode implements SourceToken { * */ - public toString(): string { - const children = this.children; - - let text = this.text; - - if (children) { - for (let i = 0, iEnd = children.length; i < iEnd; ++i) { - text += children.toString(); - } - } - - return text; - } - - - public toTokens(): Array { + public toArray(): Array { const children = this.children, - tokens: Array = [this]; + parent = new SourceNode(this.kind, this.text), + result: Array = [parent]; + + parent.doclet = this.doclet; + parent.type = this.type; if (children) { for ( let i = 0, iEnd = children.length, - childrensChildren: Array; + childrensChildren: Array; i < iEnd; ++i ) { - childrensChildren = children[i].toTokens(); + childrensChildren = children[i].toArray(); for ( let j = 0, @@ -130,12 +87,27 @@ export class SourceNode implements SourceToken { j < jEnd; ++j ) { - tokens.push(childrensChildren[j]); + result.push(childrensChildren[j]); } } } - return tokens; + return result; + } + + + public toString(): string { + const children = this.children; + + let text = this.text; + + if (children) { + for (let i = 0, iEnd = children.length; i < iEnd; ++i) { + text += children.toString(); + } + } + + return text; } diff --git a/sources/SourceParser.ts b/sources/SourceParser.ts new file mode 100644 index 0000000..b22bc00 --- /dev/null +++ b/sources/SourceParser.ts @@ -0,0 +1,249 @@ +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import * as U from './Utilities'; +import SourceDoclet from './SourceDoclet'; +import SourceNode from './SourceNode'; + + +/* * + * + * Functions + * + * */ + + +function ignoreChildrenOf( + _tsSourceFile: TS.SourceFile, + tsNode: TS.Node +): boolean { + return ( + tsNode.kind === TS.SyntaxKind.FirstStatement || + TS.isExpressionStatement(tsNode) || + TS.isImportDeclaration(tsNode) || + TS.isPropertySignature(tsNode) || + TS.isReturnStatement(tsNode) || + TS.isVariableDeclarationList(tsNode) + ); +} + + +function joinNodeArray( + tsSourceFile: TS.SourceFile, + tsNodes: ( + Array| + TS.NodeArray + ), + separator: string = ',' +): string { + const nodes: Array = []; + + for (const tsNode of tsNodes) { + nodes.push(tsNode.getText(tsSourceFile)); + } + + return nodes.join(separator); +} + + +export function parse( + tsSourceFile: TS.SourceFile, + tsNode: TS.Node +): SourceNode { + let sourceNode: SourceNode; + + if (TS.isForStatement(tsNode)) { + sourceNode = parseFor(tsSourceFile, tsNode); + } else if (TS.isFunctionDeclaration(tsNode)) { + sourceNode = parseFunction(tsSourceFile, tsNode); + } else if (TS.isIfStatement(tsNode)) { + sourceNode = parseIf(tsSourceFile, tsNode); + } else if (TS.isInterfaceDeclaration(tsNode)) { + sourceNode = parseInterface(tsSourceFile, tsNode); + } else if (TS.isModuleDeclaration(tsNode)) { + sourceNode = parseModule(tsSourceFile, tsNode); + } else if (ignoreChildrenOf(tsSourceFile, tsNode)) { + sourceNode = new SourceNode( + tsNode.kind, + tsNode.getText(tsSourceFile) + ); + } else { + const tsNodeChildren = tsNode.getChildren(tsSourceFile); + sourceNode = new SourceNode(tsNode.kind); + + if (tsNodeChildren.length) { + sourceNode.children = parseChildren(tsSourceFile, tsNodeChildren); + } else { + sourceNode.text = tsNode.getText(tsSourceFile); + } + } + + if (U.isDocumentedNode(tsNode)) { + const doclet = joinNodeArray(tsSourceFile, tsNode.jsDoc, '\n'); + + if (doclet) { + sourceNode.doclet = doclet; + } + } + + return sourceNode; +} + + +export function parseChildren( + tsSourceFile: TS.SourceFile, + tsNodeChildren: (Array|TS.NodeArray) +): Array { + const sourceChildren: Array = []; + + for (const tsChild of tsNodeChildren) { + if (tsChild.kind === TS.SyntaxKind.SyntaxList) { + return parseChildren( + tsSourceFile, + tsChild.getChildren(tsSourceFile) + ); + } else { + sourceChildren.push(parse(tsSourceFile, tsChild)); + } + } + + return sourceChildren; +} + + +function parseFor( + tsSourceFile: TS.SourceFile, + tsNode: TS.ForStatement +): SourceNode { + const sourceNode = new SourceNode(tsNode.kind); + + sourceNode.children = parseChildren( + tsSourceFile, + tsNode.statement.getChildren(tsSourceFile) + ); + + if (tsNode.initializer) { + sourceNode.text += `${tsNode.initializer.getText(tsSourceFile)};` + } + + if (tsNode.condition) { + sourceNode.text += `${tsNode.condition.getText(tsSourceFile)};` + } + + if (tsNode.incrementor) { + sourceNode.text += `${tsNode.incrementor.getText(tsSourceFile)};` + } + + return sourceNode; +} + + +function parseFunction( + tsSourceFile: TS.SourceFile, + tsNode: TS.FunctionDeclaration +): SourceNode { + const sourceNode = new SourceNode( + tsNode.kind, + tsNode.name ? tsNode.name.getText(tsSourceFile) : '' + ); + + if (tsNode.body) { + sourceNode.children = parseChildren( + tsSourceFile, + tsNode.body.getChildren(tsSourceFile) + ); + } + + return sourceNode; +} + + +function parseIf( + tsSourceFile: TS.SourceFile, + tsNode: TS.IfStatement +): SourceNode { + const sourceNode = new SourceNode( + tsNode.kind, + tsNode.expression.getText(tsSourceFile) + ); + + sourceNode.children = parseChildren( + tsSourceFile, + tsNode.thenStatement.getChildren(tsSourceFile) + ); + + if (tsNode.elseStatement) { + const firstChild = tsNode.elseStatement.getFirstToken(tsSourceFile); + + if ( + firstChild && + TS.isIfStatement(firstChild) && + tsNode.elseStatement.getChildCount(tsSourceFile) === 1 + ) { + const elseIfSourceNode = parseIf(tsSourceFile, firstChild); + + elseIfSourceNode.text = `else ${elseIfSourceNode.text}`; + + sourceNode.children.push(elseIfSourceNode); + } else { + const elseSourceNode = new SourceNode(tsNode.kind, 'else'); + + elseSourceNode.children = parseChildren( + tsSourceFile, + tsNode.elseStatement.getChildren(tsSourceFile) + ); + + sourceNode.children.push(elseSourceNode); + } + } + + return sourceNode; +} + + +function parseInterface( + tsSourceFile: TS.SourceFile, + tsNode: TS.InterfaceDeclaration +): SourceNode { + const sourceNode = new SourceNode( + tsNode.kind, + tsNode.name.getText(tsSourceFile) + ); + + sourceNode.children = parseChildren(tsSourceFile, tsNode.members); + + if (tsNode.typeParameters) { + sourceNode.text += `<${joinNodeArray(tsSourceFile, tsNode.typeParameters)}>`; + } + + if (tsNode.heritageClauses) { + sourceNode.type = joinNodeArray(tsSourceFile, tsNode.heritageClauses); + } + + return sourceNode; +} + + +function parseModule( + tsSourceFile: TS.SourceFile, + tsNode: TS.ModuleDeclaration +): SourceNode { + const sourceNode = new SourceNode( + tsNode.kind, + tsNode.name.getText(tsSourceFile) + ); + + if (tsNode.body) { + sourceNode.children = parseChildren( + tsSourceFile, + tsNode.body.getChildren(tsSourceFile) + ); + } + + return sourceNode; +} diff --git a/sources/SourceTree.ts b/sources/SourceTree.ts index a1f8940..c4abe69 100644 --- a/sources/SourceTree.ts +++ b/sources/SourceTree.ts @@ -14,6 +14,7 @@ import * as TS from 'typescript'; +import * as SP from './SourceParser'; import SourceNode from './SourceNode'; import SourceToken from './SourceToken'; @@ -79,13 +80,12 @@ export class SourceTree { return; } - const sourceFile = TS.createSourceFile( + const tsSourceFile = TS.createSourceFile( '', sourceCode, TS.ScriptTarget.Latest ), - tree = SourceNode.parse(sourceFile, sourceFile), - roots = tree.children?.[0].children; + roots = SP.parseChildren(tsSourceFile, tsSourceFile.getChildren()); if (roots) { for (let i = 0, iEnd = roots.length; i < iEnd; ++i) { @@ -95,18 +95,18 @@ export class SourceTree { } - public toTokens (): Array { + public toArray (): Array { const nodes = this.nodes, - tokens: Array = []; + result: Array = []; for ( let i = 0, iEnd = nodes.length, - nodesChildren: Array; + nodesChildren: Array; i < iEnd; ++i ) { - nodesChildren = nodes[i].toTokens(); + nodesChildren = nodes[i].toArray(); for ( let j = 0, @@ -114,11 +114,11 @@ export class SourceTree { j < jEnd; ++j ) { - tokens.push(nodesChildren[j]); + result.push(nodesChildren[j]); } } - return tokens; + return result; } diff --git a/sources/rules/debug.ts b/sources/rules/debug.ts index b50726c..d279f78 100644 --- a/sources/rules/debug.ts +++ b/sources/rules/debug.ts @@ -64,29 +64,21 @@ function lint ( ) { const onlyKindOf = context.options.onlyKindOf, sourceTree = context.sourceTree, - sourceTokens = sourceTree.toTokens(); - - for ( - let i = 0, - iEnd = sourceTokens.length, - sourcePosition: number = 0, - sourceToken: SourceToken; - i < iEnd; - ++i - ) { - sourceToken = sourceTokens[i]; - - if (onlyKindOf && !onlyKindOf.includes(sourceToken.kind)) { + sourceNodes = sourceTree.toArray(); + + for (const sourceNode of sourceNodes) { + + if (onlyKindOf && !onlyKindOf.includes(sourceNode.kind)) { continue; } - context.report( - 0, - 0, - `SyntaxKind: ${sourceToken.kind} ${TS.SyntaxKind[sourceToken.kind]} @ ${sourcePosition}` + console.log( + sourceNode.kind, + TS.SyntaxKind[sourceNode.kind], + sourceNode.text, + sourceNode.type, + sourceNode.doclet ); - - sourcePosition += sourceToken.text.length; } } From 1da7da631f97db9140d7caa35334c25f6d99e6a4 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Wed, 13 Apr 2022 12:17:19 +0200 Subject: [PATCH 10/54] Added types extraction. --- sources/SourceNode.ts | 4 +- sources/Utilities.ts | 85 ++++++++++++++++++++++++++++++++++++++++++ sources/rules/debug.ts | 2 +- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/sources/SourceNode.ts b/sources/SourceNode.ts index 7a06d83..823f552 100644 --- a/sources/SourceNode.ts +++ b/sources/SourceNode.ts @@ -53,7 +53,7 @@ export class SourceNode implements SourceToken { public text: string; - public type?: string; + public types?: Array; /* * @@ -69,7 +69,7 @@ export class SourceNode implements SourceToken { result: Array = [parent]; parent.doclet = this.doclet; - parent.type = this.type; + parent.types = this.types; if (children) { for ( diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 1406443..bd74b6d 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -28,6 +28,11 @@ export interface DocumentedNode extends TS.Node { } +export interface ExpressionNode extends TS.Node { + expression: TS.Expression; +} + + export type JSDocTagKind = ( TS.SyntaxKind.JSDocAugmentsTag| TS.SyntaxKind.JSDocAuthorTag| @@ -53,6 +58,27 @@ export type JSDocTagKind = ( ); +export type StatementKind = ( + TS.SyntaxKind.BreakStatement| + TS.SyntaxKind.ContinueStatement| + TS.SyntaxKind.DebuggerStatement| + TS.SyntaxKind.DoStatement| + TS.SyntaxKind.EmptyStatement| + TS.SyntaxKind.ExpressionStatement| + TS.SyntaxKind.ForStatement| + TS.SyntaxKind.ForInStatement| + TS.SyntaxKind.ForOfStatement| + TS.SyntaxKind.IfStatement| + TS.SyntaxKind.LabeledStatement| + TS.SyntaxKind.ReturnStatement| + TS.SyntaxKind.SwitchStatement| + TS.SyntaxKind.ThrowStatement| + TS.SyntaxKind.TryStatement| + TS.SyntaxKind.VariableStatement| + TS.SyntaxKind.WhileStatement +); + + export type UnknownObject = Record; @@ -63,6 +89,23 @@ export type UnknownObject = Record; * */ +export function extractTypes( + tsSourceFile: TS.SourceFile, + tsNode: TS.Node +): Array { + const tsChildren = tsNode.getChildren(tsSourceFile), + types: Array = []; + + for (const tsChild of tsChildren) { + if (TS.isTypeReferenceNode(tsChild)) { + types.push(tsChild.getText(tsSourceFile)); + } + } + + return types; +} + + /** * Returns a indented string, that fits into a specific width and spans over * several lines. @@ -125,6 +168,48 @@ export function isDocumentedNode( } +export function isExpressionNode( + node: T +): node is (T&ExpressionNode) { + return ( + typeof (node as unknown as ExpressionNode).expression === 'object' + ) +} + + +export function isNodeClass( + node: T, + nodeClass: ('Assignment'|'Declaration'|'Expression'|'Signature'|'Statement') +): boolean { + const kindClass: (string|undefined) = TS.SyntaxKind[node.kind]; + return !!kindClass && kindClass.endsWith(nodeClass); +} + + +export function isNodeStatement( + node: T +): node is (T&StatementKind) { + return ( + TS.isBreakOrContinueStatement(node) || + TS.isDebuggerStatement(node) || + TS.isDoStatement(node) || + TS.isEmptyStatement(node) || + TS.isExpressionStatement(node) || + TS.isForInStatement(node) || + TS.isForOfStatement(node) || + TS.isForStatement(node) || + TS.isIfStatement(node) || + TS.isLabeledStatement(node) || + TS.isReturnStatement(node) || + TS.isSwitchStatement(node) || + TS.isThrowStatement(node) || + TS.isTryStatement(node) || + TS.isVariableStatement(node) || + TS.isWhileStatement(node) + ); +} + + export function trimAll ( text: string, keepParagraphs = false diff --git a/sources/rules/debug.ts b/sources/rules/debug.ts index d279f78..154b844 100644 --- a/sources/rules/debug.ts +++ b/sources/rules/debug.ts @@ -76,7 +76,7 @@ function lint ( sourceNode.kind, TS.SyntaxKind[sourceNode.kind], sourceNode.text, - sourceNode.type, + sourceNode.types, sourceNode.doclet ); } From 361e115cb003b06c87869425e520c808f6681c12 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Wed, 13 Apr 2022 12:19:26 +0200 Subject: [PATCH 11/54] Improved variable parsing. --- sources/SourceParser.ts | 64 +++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/sources/SourceParser.ts b/sources/SourceParser.ts index b22bc00..493c508 100644 --- a/sources/SourceParser.ts +++ b/sources/SourceParser.ts @@ -19,16 +19,14 @@ import SourceNode from './SourceNode'; function ignoreChildrenOf( - _tsSourceFile: TS.SourceFile, tsNode: TS.Node ): boolean { return ( - tsNode.kind === TS.SyntaxKind.FirstStatement || TS.isExpressionStatement(tsNode) || TS.isImportDeclaration(tsNode) || TS.isPropertySignature(tsNode) || TS.isReturnStatement(tsNode) || - TS.isVariableDeclarationList(tsNode) + TS.isVariableDeclaration(tsNode) ); } @@ -67,11 +65,17 @@ export function parse( sourceNode = parseInterface(tsSourceFile, tsNode); } else if (TS.isModuleDeclaration(tsNode)) { sourceNode = parseModule(tsSourceFile, tsNode); - } else if (ignoreChildrenOf(tsSourceFile, tsNode)) { + } else if (TS.isVariableStatement(tsNode)) { + sourceNode = parseVariables(tsSourceFile, tsNode); + } else if (ignoreChildrenOf(tsNode)) { + const types = U.extractTypes(tsSourceFile, tsNode); + sourceNode = new SourceNode( tsNode.kind, tsNode.getText(tsSourceFile) ); + + sourceNode.types = types; } else { const tsNodeChildren = tsNode.getChildren(tsSourceFile); sourceNode = new SourceNode(tsNode.kind); @@ -101,15 +105,19 @@ export function parseChildren( ): Array { const sourceChildren: Array = []; - for (const tsChild of tsNodeChildren) { - if (tsChild.kind === TS.SyntaxKind.SyntaxList) { + for (const tsNodeChild of tsNodeChildren) switch (tsNodeChild.kind) { + case TS.SyntaxKind.CommaToken: + continue; + + case TS.SyntaxKind.SyntaxList: return parseChildren( tsSourceFile, - tsChild.getChildren(tsSourceFile) + tsNodeChild.getChildren(tsSourceFile) ); - } else { - sourceChildren.push(parse(tsSourceFile, tsChild)); - } + + default: + sourceChildren.push(parse(tsSourceFile, tsNodeChild)); + continue; } return sourceChildren; @@ -178,14 +186,14 @@ function parseIf( ); if (tsNode.elseStatement) { - const firstChild = tsNode.elseStatement.getFirstToken(tsSourceFile); + const tsFirstChild = tsNode.elseStatement.getFirstToken(tsSourceFile); if ( - firstChild && - TS.isIfStatement(firstChild) && + tsFirstChild && + TS.isIfStatement(tsFirstChild) && tsNode.elseStatement.getChildCount(tsSourceFile) === 1 ) { - const elseIfSourceNode = parseIf(tsSourceFile, firstChild); + const elseIfSourceNode = parseIf(tsSourceFile, tsFirstChild); elseIfSourceNode.text = `else ${elseIfSourceNode.text}`; @@ -222,7 +230,9 @@ function parseInterface( } if (tsNode.heritageClauses) { - sourceNode.type = joinNodeArray(tsSourceFile, tsNode.heritageClauses); + sourceNode.types = [ + joinNodeArray(tsSourceFile, tsNode.heritageClauses) + ]; } return sourceNode; @@ -247,3 +257,27 @@ function parseModule( return sourceNode; } + + +function parseVariables( + tsSourceFile: TS.SourceFile, + tsNode: TS.VariableStatement +): SourceNode { + const tsFirstChild = tsNode.getFirstToken(tsSourceFile); + + if (!tsFirstChild) { + return new SourceNode(tsNode.kind); + } + + const sourceNode = new SourceNode( + tsNode.declarationList.kind, + tsFirstChild.getText(tsSourceFile) + ); + + sourceNode.children = parseChildren( + tsSourceFile, + tsNode.declarationList.getChildren(tsSourceFile) + ); + + return sourceNode; +} From 1e815e9369b70bee26cebc7e0536246f6b524535 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Wed, 20 Apr 2022 21:21:28 +0200 Subject: [PATCH 12/54] Added SourcePosition. --- sources/SourceLine.ts | 66 +++++++++++++++++++++++++++++++++---- sources/SourcePosition.d.ts | 23 +++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 sources/SourcePosition.d.ts diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 0699c42..aa57583 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -13,9 +13,10 @@ * */ -import type SourceToken from './SourceToken'; - import * as TS from 'typescript'; +import SourceCode from './SourceCode'; +import SourcePosition from './SourcePosition'; +import SourceToken from './SourceToken'; /* * @@ -69,13 +70,66 @@ export class SourceLine { } - public toString(): string { - const tokens = this.tokens; + public getPosition( + sourceCode: SourceCode, + token?: SourceToken + ): (SourcePosition|null) { + const lines = sourceCode.lines, + lineIndex = lines.indexOf(this), + position: SourcePosition = { + column: 1, + end: 0, + line: 1, + start: 0 + }; + + if (lineIndex === -1) { + return null; + } + + let tokens: Array; + + for (let i = 0, iLast = lineIndex, match: (RegExpMatchArray|null); i <= iLast; ++i) { + + position.column = 1; + tokens = lines[i].tokens; + + for (const lineToken of tokens) { + + if (i === iLast) { + if (!token) { + return position; + } else if (lineToken === token) { + position.end = position.start + lineToken.text.length; + console.log(position); + return position; + } + } + + if (lineToken.kind === TS.SyntaxKind.MultiLineCommentTrivia) { + match = lineToken.text.match(/\n|\r|\r\n/g); + if (match) { + position.line += match.length; + } + } + + position.column += lineToken.text.length; + position.start += lineToken.text.length; + } + + position.line += 1; + } + + return null; + } + + + public toString(): string { let text = ''; - for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { - text += tokens[i].text; + for (const token of this.tokens) { + text += token.text; } return text; diff --git a/sources/SourcePosition.d.ts b/sources/SourcePosition.d.ts new file mode 100644 index 0000000..a7387a5 --- /dev/null +++ b/sources/SourcePosition.d.ts @@ -0,0 +1,23 @@ +/* * + * + * Declarations + * + * */ + + +export interface SourcePosition { + column: number; + end: number; + line: number; + start: number; +} + + +/* * + * + * Default Export + * + * */ + + +export default SourcePosition; From 1f9f1552d572857d7745606352d63634ce22fe13 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 21 Apr 2022 12:17:32 +0200 Subject: [PATCH 13/54] Added generic-array-type rule with fix. --- sources/RuleContext.ts | 50 +++------ sources/rules/generic-array-type.ts | 153 ++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 36 deletions(-) create mode 100644 sources/rules/generic-array-type.ts diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index faba76c..efd350d 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -21,9 +21,9 @@ import type RuleOptions from './RuleOptions'; import * as FS from 'fs'; import * as Path from 'path'; -import * as TS from 'typescript'; import RuleType from './RuleType'; import SourceCode from './SourceCode'; +import SourcePosition from './SourcePosition'; import SourceTree from './SourceTree'; @@ -49,11 +49,11 @@ export class RuleContext { ruleOptionsSchema: JSONSchema.JSONSchema4, ruleOptionsDefault: T, lintFunction: LintFunction, - fixFunction?: FixFunction + reportWithFix?: boolean ): ESLint.Rule.RuleModule { return { meta: { - fixable: fixFunction ? 'code' : void 0, + fixable: reportWithFix ? 'code' : void 0, schema: [{ additionalProperties: false, properties: ruleOptionsSchema, @@ -65,8 +65,7 @@ export class RuleContext { { Program: () => lintFunction(new RuleContext( esLintRuleContext, - ruleOptionsDefault, - fixFunction + ruleOptionsDefault )) } ) @@ -83,20 +82,16 @@ export class RuleContext { private constructor ( esLintContext: ESLint.Rule.RuleContext, - ruleOptionsDefault: T, - fixFunction?: FixFunction + ruleOptionsDefault: T ) { - this.changes = []; this.cwd = esLintContext.getCwd(); this.esLintContext = esLintContext; - this.fixes = []; this.settings = ((esLintContext.settings || {}).highcharts || {}); this.options = { ...ruleOptionsDefault, ...this.settings, ...(esLintContext.options[1] || {}) }; - this.fixFunction = fixFunction; this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()); } @@ -115,21 +110,12 @@ export class RuleContext { private _sourceTree?: SourceTree; - private changes: Array; - - private cwd: string; private esLintContext: ESLint.Rule.RuleContext; - private fixes: Array; - - - private fixFunction?: FixFunction; - - public options: T; @@ -171,27 +157,19 @@ export class RuleContext { public report ( - line: number, - column: number, - message: string + position: SourcePosition, + message: string, + fix?: ESLint.Rule.ReportFixer ): void { - const report: ESLint.Rule.ReportDescriptor = { + this.esLintContext.report( { + fix, loc: { - column, - line + // ESLint needs column zero-based: + column: position.column - 1, + line: position.line }, message - }; - - if (this.fixFunction) { - const fixFunction = this.fixFunction; - report.fix = () => { - fixFunction(this); - return this.fixes.splice(0, this.fixes.length); - } - } - - this.esLintContext.report(report); + }); } diff --git a/sources/rules/generic-array-type.ts b/sources/rules/generic-array-type.ts new file mode 100644 index 0000000..18fa63d --- /dev/null +++ b/sources/rules/generic-array-type.ts @@ -0,0 +1,153 @@ +/** + * @fileoverview Array types should always be written in generic syntax to avoid + * any confusion with array assignments, array indexer, or type selectors. + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as ESLint from 'eslint'; +import * as FS from 'fs'; +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; +import SourceLine from '../SourceLine'; +import SourcePosition from '../SourcePosition'; +import SourceToken from '../SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +type GenericArrayTypeContext = RuleContext; + + +/* * + * + * Constants + * + * */ + + +const message = [ + 'Do not use the [] shortcut for the array type.', + 'Instead write the generic Array<...> to improve readability.' +].join(' '); + +const optionsDefaults: RuleOptions = {}; + +const optionsSchema = {}; + + +/* * + * + * Functions + * + * */ + + +function createFixer( + position: SourcePosition, + identifierToken: SourceToken, + openBracketToken: SourceToken, + closeBracketToken: SourceToken +): ESLint.Rule.ReportFixer { + return (): (ESLint.Rule.Fix|null) => { + const range: ESLint.AST.Range = [ + position.start, + ( + position.start + + identifierToken.text.length + + openBracketToken.text.length + + closeBracketToken.text.length + ) + ], + text = `Array<${identifierToken}>`; + + return { range, text }; + }; +} + +function lint ( + context: GenericArrayTypeContext +): void { + const sourceLines = context.sourceCode.lines; + + for ( + let line = 0, + lineEnd = sourceLines.length, + sourceLine: SourceLine, + sourceLineTokens: Array; + line < lineEnd; + ++line + ) { + sourceLine = sourceLines[line]; + sourceLineTokens = sourceLine.tokens; + + for ( + let index = 0, + indexEnd = sourceLineTokens.length - 2, + identifierToken: SourceToken, + openBracketToken: SourceToken, + closeBracketToken: SourceToken; + index < indexEnd; + ++index + ) { + identifierToken = sourceLineTokens[index]; + openBracketToken = sourceLineTokens[index+1]; + closeBracketToken = sourceLineTokens[index+2]; + + if ( + identifierToken.kind === TS.SyntaxKind.Identifier && + openBracketToken.kind === TS.SyntaxKind.OpenBracketToken && + closeBracketToken.kind === TS.SyntaxKind.CloseBracketToken + ) { + const position = sourceLine.getPosition( + context.sourceCode, + openBracketToken + ); + + if (position) { + context.report( + position, + message, + createFixer( + position, + identifierToken, + openBracketToken, + closeBracketToken + ) + ); + } + } + + } + } +} + +/* * + * + * Default Export + * + * */ + +export = RuleContext.setupRuleExport( + 'layout', + optionsSchema, + optionsDefaults, + lint, + true +); From 2fe190eed0b84b0704ca0c4f972554eca5b7ad3e Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:29:07 +0200 Subject: [PATCH 14/54] Added SourceComment tokens. --- sources/SourceCode.ts | 13 +- sources/SourceComment.ts | 191 +++++++++++++++ ...ceDocletTag.d.ts => SourceCommentTag.d.ts} | 1 - sources/SourceDoclet.ts | 225 ------------------ sources/SourceLine.ts | 1 - sources/SourceNode.ts | 1 - sources/SourceParser.ts | 1 - sources/SourceToken.d.ts | 4 +- 8 files changed, 202 insertions(+), 235 deletions(-) create mode 100644 sources/SourceComment.ts rename sources/{SourceDocletTag.d.ts => SourceCommentTag.d.ts} (94%) delete mode 100644 sources/SourceDoclet.ts diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index b324042..fc2c28d 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -14,6 +14,7 @@ import * as TS from 'typescript'; +import SourceComment from './SourceComment'; import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; @@ -145,10 +146,14 @@ export class SourceCode { kind = scanner.scan(); text = scanner.getTokenText(); - line.tokens.push({ - kind, - text - }); + if ( + kind === TS.SyntaxKind.MultiLineCommentTrivia || + kind === TS.SyntaxKind.SingleLineCommentTrivia + ) { + line.tokens.push(new SourceComment(kind, text)); + } else { + line.tokens.push({ kind, text }); + } if ( kind === TS.SyntaxKind.NewLineTrivia || diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts new file mode 100644 index 0000000..b8ba0e9 --- /dev/null +++ b/sources/SourceComment.ts @@ -0,0 +1,191 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import SourceCommentTag from './SourceCommentTag'; +import SourceLine from './SourceLine'; +import SourceToken from './SourceToken'; + + +/* * + * + * Class + * + * */ + + +export class SourceComment extends SourceLine implements SourceToken { + + + /* * + * + * Static Functions + * + * */ + + + private static isNodeWithJSDoc( + node: T + ): node is (T&{jsDoc:Array}) { + return typeof (node as {jsDoc?:TS.JSDoc}).jsDoc !== 'undefined'; + } + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + kind: ( + TS.SyntaxKind.MultiLineCommentTrivia | + TS.SyntaxKind.SingleLineCommentTrivia + ), + text: string + ) { + super(); + + this.kind = kind; + this.tokens = []; + + const tokens = this.tokens; + + if (kind === TS.SyntaxKind.SingleLineCommentTrivia) { + const words = text.split(/\s+/g); + + for (const word of words) { + tokens.push({ + kind: TS.SyntaxKind.Unknown, + text: word + }); + } + } else if (/^\/\*\*\s/.test(text) && this.parseJSDoc(text)) { + this.kind = TS.SyntaxKind.JSDocComment; + console.log(this.tokens); + } else { + const lines = text.split(/\n|\r|\r\n/g); + + for (const line of lines) { + tokens.push(new SourceComment(TS.SyntaxKind.SingleLineCommentTrivia, line)); + } + } + } + + + /* * + * + * Properties + * + * */ + + + public readonly kind: ( + TS.SyntaxKind.JSDocComment | + TS.SyntaxKind.MultiLineCommentTrivia | + TS.SyntaxKind.SingleLineCommentTrivia + ); + + + public get text (): string { + return this.toString(); + } + + public tokens: Array<(SourceToken|SourceCommentTag)>; + + /* * + * + * Functions + * + * */ + + + private parseJSDoc(text: string): boolean { + const tokens = this.tokens, + tsSource = TS.createSourceFile('', text, TS.ScriptTarget.Latest, true, TS.ScriptKind.JS), + tsChildren = tsSource.getChildren(tsSource); + + let success = false; + + for (const tsChild of tsChildren) { + if (!SourceComment.isNodeWithJSDoc(tsChild)) { + continue; + } + + const tsJSDocs = tsChild.jsDoc; + + for (const tsJSDoc of tsJSDocs) { + if (tsJSDoc.comment) { + tokens.push({ + kind: TS.SyntaxKind.JSDocText, + text: '' + tsJSDoc.comment + }); + } + if (tsJSDoc.tags) { + let kind: TS.SyntaxKind, + tagName: string, + text: string; + for (const tsTag of tsJSDoc.tags) { + kind = tsTag.kind; + tagName = tsTag.tagName.getText(tsSource); + + if (tsTag.comment) { + if (typeof tsTag.comment === 'string') { + text = tsTag.comment + } else { + text = tsTag.comment + .map(tsNode => tsNode.getText(tsSource)) + .join('\n'); + } + } else { + text = ''; + } + + if (TS.isJSDocParameterTag(tsTag)) { + tokens.push({ + kind, + paramName: tsTag.name.getText(tsSource), + tagName, + tagType: (tsTag.typeExpression?.type.getText(tsSource) || '*'), + text + }); + } else { + tokens.push({ kind, tagName, text }); + } + } + } + } + + success = true; + + break; + } + + return success; + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceComment; diff --git a/sources/SourceDocletTag.d.ts b/sources/SourceCommentTag.d.ts similarity index 94% rename from sources/SourceDocletTag.d.ts rename to sources/SourceCommentTag.d.ts index a7c2a26..c2fd84c 100644 --- a/sources/SourceDocletTag.d.ts +++ b/sources/SourceCommentTag.d.ts @@ -17,7 +17,6 @@ import type SourceToken from './SourceToken'; export interface SourceDocletTag extends SourceToken { - comment?: string; paramName?: string; tagName: string; tagType?: string; diff --git a/sources/SourceDoclet.ts b/sources/SourceDoclet.ts deleted file mode 100644 index aaa3653..0000000 --- a/sources/SourceDoclet.ts +++ /dev/null @@ -1,225 +0,0 @@ -/** - * @author Sophie Bremer - */ - - -'use strict'; - - -/* * - * - * Imports - * - * */ - - -import type SourceDocletTag from './SourceDocletTag'; -import type SourceToken from './SourceToken'; - -import * as TS from 'typescript'; -import * as U from './Utilities'; -import SourceLine from './SourceLine'; - - -/* * - * - * Class - * - * */ - - -export class SourceDoclet extends SourceLine implements SourceToken { - - - /* * - * - * Static Properties - * - * */ - - - public static parse ( - sourceToken: SourceToken - ): (SourceDoclet|null) { - if ( - sourceToken.kind === TS.SyntaxKind.MultiLineCommentTrivia && - sourceToken.text.startsWith('/**') - ) { - return new SourceDoclet(sourceToken); - } - - return null; - } - - - /* * - * - * Constructor - * - * */ - - - private constructor ( - sourceToken: SourceToken - ) { - super(); - - this.comment = ''; - this.text = sourceToken.text; - this.tokens = []; - - this.parseDoclet(sourceToken); - } - - - /* * - * - * Properties - * - * */ - - - public comment: string; - - - public readonly kind = TS.SyntaxKind.JSDocComment; - - - public readonly text: string; - - - public tokens: Array; - - - /* * - * - * Functions - * - * */ - - private parseDoclet( - sourceToken: SourceToken - ): Array { - const jsDocs: Array = []; - - if ( - sourceToken.kind === TS.SyntaxKind.MultiLineCommentTrivia && - sourceToken.text.startsWith('/**') - ) { - return jsDocs; - } - - const source = TS.createSourceFile( - '', - sourceToken.text, - TS.ScriptTarget.Latest - ); - // jsDocs: Array = [], - // extractDoclet = (node: TS.Node) => { - // if (U.isDocumentedNode(node)) { - // const jsDoc = node.jsDoc; - - // for (let i = 0, iEnd = jsDoc.length; i < iEnd; ++i) { - // if (!jsDocs.includes(jsDoc[i])) { - // jsDocs.push(jsDoc[i]); - // } - // } - // } - - // node.getChildren(source).forEach(extractDoclet); - // }; - - // extractDoclet(source); - - const sourceNodes = source.getChildren(); - - console.log(sourceNodes); - - const tags = (jsDocs[0].tags || []).map(tag => tag), - tokens = this.tokens; - - for ( - let i = 0, - iEnd = tags.length, - tag: TS.JSDocTag, - token: SourceDocletTag; - i < iEnd; - ++i - ) { - tag = tags[i]; - - if (TS.isJSDocSignature(tag)) { - const parameterTags = tag.parameters; - - for (let i = 0, iEnd = parameterTags.length; i < iEnd; ++i) { - const parameterTag = parameterTags[i], - parameterToken: SourceDocletTag = { - comment: TS.getTextOfJSDocComment(parameterTag.comment), - kind: parameterTag.kind, - paramName: parameterTag.name.getText(), - tagName: parameterTag.tagName.text, - text: parameterTag.getText() - }; - - if (parameterTag.typeExpression) { - parameterToken.tagType = parameterTag.typeExpression.getText(); - } - - tokens.push(parameterToken); - } - - if (tag.type) { - const returnTag = tag.type, - returnToken: SourceDocletTag = { - comment: TS.getTextOfJSDocComment(returnTag.comment), - kind: returnTag.kind, - tagName: returnTag.tagName.text, - text: returnTag.getText() - }; - - if (returnTag.typeExpression) { - returnToken.tagType = returnTag.typeExpression.getText(); - } - - tokens.push(returnToken); - } - - continue; - } - - token = { - comment: TS.getTextOfJSDocComment(tag.comment), - kind: tag.kind, - tagName: tag.tagName.text, - text: tag.getText() - }; - - if (TS.isJSDocFunctionType(tag)) { - token.tagType = tag.type?.getText(); - } - - if (TS.isJSDocParameterTag(tag)) { - token.paramName = tag.name.getText(); - - if (tag.typeExpression) { - token.tagType = tag.typeExpression.getText(); - } - } - - tokens.push(token); - } - - return jsDocs; - } - -} - - -/* * - * - * Default Export - * - * */ - - -export default SourceDoclet; diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index aa57583..ded1100 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -101,7 +101,6 @@ export class SourceLine { return position; } else if (lineToken === token) { position.end = position.start + lineToken.text.length; - console.log(position); return position; } } diff --git a/sources/SourceNode.ts b/sources/SourceNode.ts index 823f552..ef9f770 100644 --- a/sources/SourceNode.ts +++ b/sources/SourceNode.ts @@ -6,7 +6,6 @@ import * as TS from 'typescript'; -import SourceDoclet from './SourceDoclet'; import SourceToken from './SourceToken'; diff --git a/sources/SourceParser.ts b/sources/SourceParser.ts index 493c508..5bc2e56 100644 --- a/sources/SourceParser.ts +++ b/sources/SourceParser.ts @@ -7,7 +7,6 @@ import * as TS from 'typescript'; import * as U from './Utilities'; -import SourceDoclet from './SourceDoclet'; import SourceNode from './SourceNode'; diff --git a/sources/SourceToken.d.ts b/sources/SourceToken.d.ts index 28cd5c9..348f7dd 100644 --- a/sources/SourceToken.d.ts +++ b/sources/SourceToken.d.ts @@ -15,8 +15,8 @@ import type * as TS from 'typescript'; * */ -export interface SourceToken { - kind: TS.SyntaxKind; +export interface SourceToken { + kind: T; text: string; } From 1dac36664c9b44c0327ea08d8f294a22d2627a34 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 22 Apr 2022 14:04:13 +0200 Subject: [PATCH 15/54] Refactored positioning calculations. --- sources/SourceCode.ts | 114 ++++++++++++++--------- sources/SourceComment.ts | 135 +++++----------------------- sources/SourceCommentTag.d.ts | 33 ------- sources/SourceLine.ts | 115 ++++++++++++++---------- sources/Utilities.ts | 7 ++ sources/rules/generic-array-type.ts | 34 +++---- sources/rules/pretty-length.ts | 123 +++++++++++++------------ 7 files changed, 250 insertions(+), 311 deletions(-) delete mode 100644 sources/SourceCommentTag.d.ts diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index fc2c28d..211b3c8 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -14,8 +14,11 @@ import * as TS from 'typescript'; +import * as U from './Utilities'; import SourceComment from './SourceComment'; +import SourceDoc from './SourceDoc'; import SourceLine from './SourceLine'; +import SourcePosition from './SourcePosition'; import SourceToken from './SourceToken'; @@ -66,57 +69,83 @@ export class SourceCode { * */ - public getLinePos( - sourceLine: SourceLine - ): number { + public getLinePosition( + line: SourceLine + ): (SourcePosition|null) { const lines = this.lines, - lineIndex = lines.indexOf(sourceLine); - - if (lineIndex === -1) { - return -1; + lineIndex = lines.indexOf(line), + position: SourcePosition = { + column: 1, + end: 0, + line: 1, + start: 0 + }; + + if (lineIndex < 0) { + return null; } - let pos = 0; + for ( + let i = 0, + tokens: Array, + tokenLength: number, + tokenText: string; + i < lineIndex; + ++i + ) { + tokens = lines[i].tokens; + + for (const token of tokens) { + tokenText = token.text; + + if ( + token.kind === TS.SyntaxKind.JSDocComment || + token.kind === TS.SyntaxKind.MultiLineCommentTrivia + ) { + tokenLength = U.breakText(tokenText).length; + + if (tokenLength > 2) { + // count only the extra lines in-between + position.line += tokenLength - 2; + } + } - for (let i = 0, iEnd = lineIndex; i < iEnd; ++i) { - pos += lines[i].getLength() + 1; // + line break - } + position.start += tokenText.length; + } - return pos; - } + position.line += 1; + } + position.end = position.start + U.breakText(line.toString())[0].length; - public getTokenPos( - sourceToken: SourceToken - ): number { - const lines = this.lines; + return position; + } - let line: SourceLine, - pos = 0, - tokenIndex = -1; - for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { - line = lines[i]; - tokenIndex = line.tokens.indexOf(sourceToken); + /** + * Returns the token position relative to the code. + */ + public getTokenPosition( + line: SourceLine, + token: SourceToken + ): (SourcePosition|null) { - if (tokenIndex >= 0) { - const tokens = line.tokens; + const linePosition = this.getLinePosition(line), + tokenPosition = line.getTokenPosition(token); - for (let j = 0, jEnd = tokenIndex; j < jEnd; ++j) { - pos += tokens[j].text.length; - } + console.log(linePosition); + console.log(tokenPosition); - break; - } - - pos += lines[i].getLength() + 1; // + line break + if (!linePosition || !tokenPosition) { + return null; } - if (tokenIndex === -1) { - return -1; + return { + column: tokenPosition.column, + end: linePosition.start + tokenPosition.end, + line: linePosition.line + tokenPosition.line - 1, + start: linePosition.start + tokenPosition.start } - - return pos; } @@ -146,13 +175,12 @@ export class SourceCode { kind = scanner.scan(); text = scanner.getTokenText(); - if ( - kind === TS.SyntaxKind.MultiLineCommentTrivia || - kind === TS.SyntaxKind.SingleLineCommentTrivia - ) { - line.tokens.push(new SourceComment(kind, text)); - } else { + if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { line.tokens.push({ kind, text }); + // } else if (SourceDoc.isDocComment(text)) { + // line.tokens.push(new SourceDoc(text, Math.floor(line.getIndent() / 2))); + } else { + line.tokens.push(new SourceComment(text, Math.floor(line.getIndent() / 2))); } if ( @@ -178,6 +206,8 @@ export class SourceCode { return text; } + + } diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index b8ba0e9..ffdf360 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -14,7 +14,7 @@ import * as TS from 'typescript'; -import SourceCommentTag from './SourceCommentTag'; +import * as U from './Utilities'; import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; @@ -29,20 +29,6 @@ import SourceToken from './SourceToken'; export class SourceComment extends SourceLine implements SourceToken { - /* * - * - * Static Functions - * - * */ - - - private static isNodeWithJSDoc( - node: T - ): node is (T&{jsDoc:Array}) { - return typeof (node as {jsDoc?:TS.JSDoc}).jsDoc !== 'undefined'; - } - - /* * * * Constructor @@ -51,37 +37,26 @@ export class SourceComment extends SourceLine implements SourceToken { public constructor ( - kind: ( - TS.SyntaxKind.MultiLineCommentTrivia | - TS.SyntaxKind.SingleLineCommentTrivia - ), - text: string + text: string, + indent: number = 0 ) { super(); - this.kind = kind; - this.tokens = []; - - const tokens = this.tokens; - - if (kind === TS.SyntaxKind.SingleLineCommentTrivia) { - const words = text.split(/\s+/g); - - for (const word of words) { - tokens.push({ - kind: TS.SyntaxKind.Unknown, - text: word - }); - } - } else if (/^\/\*\*\s/.test(text) && this.parseJSDoc(text)) { - this.kind = TS.SyntaxKind.JSDocComment; - console.log(this.tokens); - } else { - const lines = text.split(/\n|\r|\r\n/g); - - for (const line of lines) { - tokens.push(new SourceComment(TS.SyntaxKind.SingleLineCommentTrivia, line)); - } + this.indent = indent; + this.kind = TS.SyntaxKind.MultiLineCommentTrivia; + + const lines = U.breakText(text), + lineBreak = text.includes('\r\n') ? '\r\n' : '\n', + tokens = this.tokens; + + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + tokens.push({ + kind: TS.SyntaxKind.SingleLineCommentTrivia, + text: (i ? lines[i].substr(indent) : lines[i]) + }, { + kind: TS.SyntaxKind.NewLineTrivia, + text: lineBreak + }); } } @@ -93,18 +68,16 @@ export class SourceComment extends SourceLine implements SourceToken { * */ - public readonly kind: ( - TS.SyntaxKind.JSDocComment | - TS.SyntaxKind.MultiLineCommentTrivia | - TS.SyntaxKind.SingleLineCommentTrivia - ); + private readonly indent: number; + + + public readonly kind: TS.SyntaxKind.MultiLineCommentTrivia; public get text (): string { return this.toString(); } - public tokens: Array<(SourceToken|SourceCommentTag)>; /* * * @@ -113,68 +86,8 @@ export class SourceComment extends SourceLine implements SourceToken { * */ - private parseJSDoc(text: string): boolean { - const tokens = this.tokens, - tsSource = TS.createSourceFile('', text, TS.ScriptTarget.Latest, true, TS.ScriptKind.JS), - tsChildren = tsSource.getChildren(tsSource); - - let success = false; - - for (const tsChild of tsChildren) { - if (!SourceComment.isNodeWithJSDoc(tsChild)) { - continue; - } - - const tsJSDocs = tsChild.jsDoc; - - for (const tsJSDoc of tsJSDocs) { - if (tsJSDoc.comment) { - tokens.push({ - kind: TS.SyntaxKind.JSDocText, - text: '' + tsJSDoc.comment - }); - } - if (tsJSDoc.tags) { - let kind: TS.SyntaxKind, - tagName: string, - text: string; - for (const tsTag of tsJSDoc.tags) { - kind = tsTag.kind; - tagName = tsTag.tagName.getText(tsSource); - - if (tsTag.comment) { - if (typeof tsTag.comment === 'string') { - text = tsTag.comment - } else { - text = tsTag.comment - .map(tsNode => tsNode.getText(tsSource)) - .join('\n'); - } - } else { - text = ''; - } - - if (TS.isJSDocParameterTag(tsTag)) { - tokens.push({ - kind, - paramName: tsTag.name.getText(tsSource), - tagName, - tagType: (tsTag.typeExpression?.type.getText(tsSource) || '*'), - text - }); - } else { - tokens.push({ kind, tagName, text }); - } - } - } - } - - success = true; - - break; - } - - return success; + public getIndent(): number { + return this.indent; } diff --git a/sources/SourceCommentTag.d.ts b/sources/SourceCommentTag.d.ts deleted file mode 100644 index c2fd84c..0000000 --- a/sources/SourceCommentTag.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* * - * - * Imports - * - * */ - - -import type * as U from './Utilities'; -import type SourceToken from './SourceToken'; - - -/* * - * - * Declarations - * - * */ - - -export interface SourceDocletTag extends SourceToken { - paramName?: string; - tagName: string; - tagType?: string; -} - - -/* * - * - * Default Export - * - * */ - - -export default SourceDocletTag; diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index ded1100..3ebe61f 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -14,7 +14,7 @@ import * as TS from 'typescript'; -import SourceCode from './SourceCode'; +import * as U from './Utilities'; import SourcePosition from './SourcePosition'; import SourceToken from './SourceToken'; @@ -49,7 +49,10 @@ export class SourceLine { public getIndent(): number { const firstToken = this.tokens[0]; - if (firstToken.kind === TS.SyntaxKind.WhitespaceTrivia) { + if ( + firstToken && + firstToken.kind === TS.SyntaxKind.WhitespaceTrivia + ) { return firstToken.text.length; } @@ -57,81 +60,103 @@ export class SourceLine { } - public getLength(): number { - const tokens = this.tokens; + public getLines(): Array { + return U.breakText(this.toString()); + } + - let length = 0; + public getMaximalLength(): number { + const lines = this.getLines(); - for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { - length += tokens[i].text.length; + let lineLength: number, + maximalLength = 0; + + for (const line of lines) { + lineLength = line.length; + if (lineLength > maximalLength) { + maximalLength = lineLength; + } } - return length; + return maximalLength; } - public getPosition( - sourceCode: SourceCode, - token?: SourceToken + /** + * Returns the token position relative to the line. + */ + public getTokenPosition( + token: SourceToken ): (SourcePosition|null) { - const lines = sourceCode.lines, - lineIndex = lines.indexOf(this), - position: SourcePosition = { + const tokens = this.tokens, + tokenIndex = tokens.indexOf(token), + position = { column: 1, end: 0, line: 1, start: 0 }; - if (lineIndex === -1) { + if (tokenIndex < 0) { return null; } - let tokens: Array; - - for (let i = 0, iLast = lineIndex, match: (RegExpMatchArray|null); i <= iLast; ++i) { + for (let i = 0, tokenText: string; i < tokenIndex; ++i) { + tokenText = tokens[i].text; + + if ( + token.kind === TS.SyntaxKind.JSDocComment || + token.kind === TS.SyntaxKind.MultiLineCommentTrivia + ) { + position.line += U.breakText(tokenText).length - 1; + } - position.column = 1; - tokens = lines[i].tokens; + position.start += tokenText.length; + } - for (const lineToken of tokens) { + position.end = token.text.length; - if (i === iLast) { - if (!token) { - return position; - } else if (lineToken === token) { - position.end = position.start + lineToken.text.length; - return position; - } - } + return position; + } - if (lineToken.kind === TS.SyntaxKind.MultiLineCommentTrivia) { - match = lineToken.text.match(/\n|\r|\r\n/g); - if (match) { - position.line += match.length; - } - } + public toString( + maxLength?: number + ): string { + if (!maxLength) { + let text = ''; - position.column += lineToken.text.length; - position.start += lineToken.text.length; + for (const token of this.tokens) { + text += token.text; } - position.line += 1; + return text; } - return null; - } + const lines: Array = [], + tokens = this.tokens; + + if (!tokens.length) { + return ''; + } + let line = '', + tokenText: string; - public toString(): string { - let text = ''; + for (const token of tokens) { + tokenText = token.text; - for (const token of this.tokens) { - text += token.text; + if ((line + tokenText).length > maxLength) { + lines.push(line); + line = ''; + } + + line += tokenText; } - return text; + lines.push(line); + + return lines.join('\n'); } diff --git a/sources/Utilities.ts b/sources/Utilities.ts index bd74b6d..1548e72 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -89,6 +89,13 @@ export type UnknownObject = Record; * */ +export function breakText( + text: string +): Array { + return text.split(/\r\n|\r|\n/g); +} + + export function extractTypes( tsSourceFile: TS.SourceFile, tsNode: TS.Node diff --git a/sources/rules/generic-array-type.ts b/sources/rules/generic-array-type.ts index 18fa63d..6c9b8f1 100644 --- a/sources/rules/generic-array-type.ts +++ b/sources/rules/generic-array-type.ts @@ -16,11 +16,9 @@ import * as ESLint from 'eslint'; -import * as FS from 'fs'; import * as TS from 'typescript'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; -import SourceLine from '../SourceLine'; import SourcePosition from '../SourcePosition'; import SourceToken from '../SourceToken'; @@ -84,41 +82,33 @@ function createFixer( function lint ( context: GenericArrayTypeContext ): void { - const sourceLines = context.sourceCode.lines; - - for ( - let line = 0, - lineEnd = sourceLines.length, - sourceLine: SourceLine, - sourceLineTokens: Array; - line < lineEnd; - ++line - ) { - sourceLine = sourceLines[line]; - sourceLineTokens = sourceLine.tokens; + const code = context.sourceCode, + lines = code.lines; + + let tokens: Array; + + for (const line of lines) { + tokens = line.tokens; for ( let index = 0, - indexEnd = sourceLineTokens.length - 2, + indexEnd = tokens.length - 2, identifierToken: SourceToken, openBracketToken: SourceToken, closeBracketToken: SourceToken; index < indexEnd; ++index ) { - identifierToken = sourceLineTokens[index]; - openBracketToken = sourceLineTokens[index+1]; - closeBracketToken = sourceLineTokens[index+2]; + identifierToken = tokens[index]; + openBracketToken = tokens[index+1]; + closeBracketToken = tokens[index+2]; if ( identifierToken.kind === TS.SyntaxKind.Identifier && openBracketToken.kind === TS.SyntaxKind.OpenBracketToken && closeBracketToken.kind === TS.SyntaxKind.CloseBracketToken ) { - const position = sourceLine.getPosition( - context.sourceCode, - openBracketToken - ); + const position = code.getTokenPosition(line, openBracketToken); if (position) { context.report( diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index 7f108cd..34ec115 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -14,12 +14,14 @@ * */ -import * as FS from 'fs'; +import * as ESLint from 'eslint'; import * as TS from 'typescript'; -import * as U from '../Utilities'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; +import SourceComment from '../SourceComment'; +import SourceDoc from '../SourceDoc'; import SourceLine from '../SourceLine'; +import SourcePosition from '../SourcePosition'; /* * @@ -71,81 +73,86 @@ const optionsSchema = { * */ -function fix( - context: PrettyLengthContext -): void { - const docletASTs: Array = []; - - context.sourceCode.lines.forEach( - sourceLine => docletASTs.push(...U.getJSDocs(sourceLine)) - ); - - FS.writeFileSync( - context.sourceCode.fileName + '.json', - JSON.stringify( - docletASTs, - [ - 'name', - 'comment', - 'text', - 'tags', - 'kind', - 'flags', - 'modifierFlagsCache', - 'transformFlags', - 'end', - 'pos', - ], - ' ' - ) - ); -} +function createFixer ( + line: SourceLine, + position: SourcePosition, + maximalLength: number +): ESLint.Rule.ReportFixer { + return (): ESLint.Rule.Fix => { + const range: ESLint.AST.Range = [ position.start, position.end ], + text: Array = [], + tokens = line.tokens; + + let indent: number, + lineText = '', + tokenText: string; + + for (const token of tokens) { + if (token.kind === TS.SyntaxKind.NewLineTrivia) { + continue; + } + + if ( + token instanceof SourceComment || + token instanceof SourceDoc + ) { + indent = token.getIndent(); + tokenText = token.toString(maximalLength - indent); + continue; + } + + tokenText = token.text; + + if ((lineText.length + tokenText.length) > maximalLength) { + text.push(lineText); + lineText = ''; + } + + lineText += tokenText; + } + text.push(lineText); -function fixJSDoc( - context: PrettyLengthContext -): void { - // @todo + return { range, text: text.join('\n') }; + }; } function lint ( context: PrettyLengthContext -) { - const sourceLines = context.sourceCode.lines, +): void { + const code = context.sourceCode, { ignorePattern, maximalLength } = context.options, - ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); + ignoreRegExp = (ignorePattern ? new RegExp(ignorePattern) : void 0), + lines = code.lines, + message = messageTemplate.replace('{0}', `${maximalLength}`); - console.log('Checking', context.sourceCode.fileName); - - for ( - let line = 0, - lineEnd = sourceLines.length, - sourceLine: SourceLine, - sourceLineLength: number; - line < lineEnd; - ++line - ) { - sourceLine = sourceLines[line]; + let lineLength: number; + for (const line of lines) { if ( ignoreRegExp && - ignoreRegExp.test(sourceLine.toString()) + ignoreRegExp.test(line.toString()) ) { continue; } - sourceLineLength = sourceLine.getLength(); + lineLength = line.getMaximalLength(); + + if (lineLength > maximalLength) { + const position = code.getLinePosition(line); - if (sourceLineLength > maximalLength) { - context.report( - line, - maximalLength - 1, - messageTemplate.replace('{0}', `${maximalLength}`) - ); + if (position) { + console.log(position); + context.report( + position, + message + ` ${lineLength}`, + createFixer(line, position, maximalLength) + ); + } } } } @@ -163,5 +170,5 @@ export = RuleContext.setupRuleExport( optionsSchema, optionsDefaults, lint, - fix + true ); From 7383be4e8a079c0d9a636a851ab3e8bc489d86d2 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 22 Apr 2022 14:20:29 +0200 Subject: [PATCH 16/54] Improved marker of pretty-length rule. --- sources/SourceLine.ts | 12 ++++++------ sources/rules/pretty-length.ts | 26 ++++++++++++++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 3ebe61f..8bd17c8 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -60,13 +60,8 @@ export class SourceLine { } - public getLines(): Array { - return U.breakText(this.toString()); - } - - public getMaximalLength(): number { - const lines = this.getLines(); + const lines = this.getWrappedLines(); let lineLength: number, maximalLength = 0; @@ -120,6 +115,11 @@ export class SourceLine { } + public getWrappedLines(): Array { + return U.breakText(this.toString()); + } + + public toString( maxLength?: number ): string { diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index 34ec115..efb1ace 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -146,12 +146,26 @@ function lint ( const position = code.getLinePosition(line); if (position) { - console.log(position); - context.report( - position, - message + ` ${lineLength}`, - createFixer(line, position, maximalLength) - ); + const wrappedLines = line.getWrappedLines(); + + let lineIndex = 0; + + for (const wrappedLine of wrappedLines) { + if (wrappedLine.length > maximalLength) { + context.report( + { + column: maximalLength + 1, + end: position.end, + line: position.line + lineIndex, + start: position.start + }, + message + ` ${lineLength}`, + createFixer(line, position, maximalLength) + ); + } + + ++lineIndex; + } } } } From 2713ccabe5ef642adb7dbb6206ac98d09dbb59b7 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Sat, 23 Apr 2022 20:36:55 +0200 Subject: [PATCH 17/54] Added doc parsing. --- sources/SourceCode.ts | 5 +- sources/SourceDoc.ts | 220 ++++++++++++++++++++++++++++++++++++++ sources/SourceDocTag.d.ts | 33 ++++++ sources/Utilities.ts | 11 +- 4 files changed, 265 insertions(+), 4 deletions(-) create mode 100644 sources/SourceDoc.ts create mode 100644 sources/SourceDocTag.d.ts diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 211b3c8..a3a8438 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -177,8 +177,9 @@ export class SourceCode { if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { line.tokens.push({ kind, text }); - // } else if (SourceDoc.isDocComment(text)) { - // line.tokens.push(new SourceDoc(text, Math.floor(line.getIndent() / 2))); + } else if (SourceDoc.isDocComment(text)) { + const doc = new SourceDoc(text, Math.floor(line.getIndent() / 2)); + line.tokens.push(doc); } else { line.tokens.push(new SourceComment(text, Math.floor(line.getIndent() / 2))); } diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts new file mode 100644 index 0000000..89f68a3 --- /dev/null +++ b/sources/SourceDoc.ts @@ -0,0 +1,220 @@ +/** + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import SourceDocTag from './SourceDocTag'; +import SourceLine from './SourceLine'; +import SourceToken from './SourceToken'; +import * as U from './Utilities'; + + +/* * + * + * Class + * + * */ + + +export class SourceDoc extends SourceLine implements SourceToken { + + + /* * + * + * Static Functions + * + * */ + + + public static isDocComment( + text: string + ): boolean { + return /^\/\*\*\s/.test(text); + } + + + private static isNodeWithJSDoc( + node: T + ): node is (T&{jsDoc:Array}) { + return typeof (node as {jsDoc?:TS.JSDoc}).jsDoc !== 'undefined'; + } + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + text: string, + indent: number = 0 + ) { + super(); + + this.kind = TS.SyntaxKind.JSDocComment; + this.indent = indent; + this.text = text; + this.tokens = []; + + const tags = this.tokens, + tsSource = TS.createSourceFile('', text, TS.ScriptTarget.Latest, void 0, TS.ScriptKind.JS), + tsJSDocs: Array = [], + tsChildren = tsSource.getChildren(tsSource); + + for (const tsChild of tsChildren) { + if (SourceDoc.isNodeWithJSDoc(tsChild)) { + tsJSDocs.push(...tsChild.jsDoc); + } + } + + for (const tsJSDoc of tsJSDocs) { + + if (tsJSDoc.comment) { + tags.push({ + kind: TS.SyntaxKind.JSDocText, + tagName: 'description', + text: U.trimBreaks('' + tsJSDoc.comment) + }); + } + + if (tsJSDoc.tags) { + let kind: TS.SyntaxKind, + tagName: string, + text: string; + + for (const tsTag of tsJSDoc.tags) { + kind = tsTag.kind; + tagName = tsTag.tagName.getText(tsSource); + + if (tsTag.comment) { + if (typeof tsTag.comment === 'string') { + text = tsTag.comment + } else { + text = tsTag.comment + .map(tsNode => tsNode.getText(tsSource)) + .join('\n'); + } + } else { + text = ''; + } + + text = U.trimBreaks(text); + + if (TS.isJSDocParameterTag(tsTag)) { + tags.push({ + kind, + paramName: tsTag.name.getText(tsSource), + tagName, + tagType: (tsTag.typeExpression?.type.getText(tsSource) || '*'), + text + }); + } else { + tags.push({ kind, tagName, text }); + } + } + } + } + } + + + /* * + * + * Properties + * + * */ + + + public readonly kind: TS.SyntaxKind.JSDocComment; + + + private readonly indent: number; + + + public readonly text: string; + + + public readonly tokens: Array; + + + /* * + * + * Functions + * + * */ + + + public getIndent(): number { + return this.indent; + } + + + public toString( + maximalLength?: number + ): string { + if (!maximalLength) { + return this.text; + } + + const tags = this.tokens, + firstTag = tags[0], + text: Array = []; + + if ( + firstTag && + firstTag.tagName === 'description' + ) { + text.push(U.indent(firstTag.text, ' * ', maximalLength)); + tags.shift(); + + if (tags.length) { + text.push(' *'); + } + } + + for (const tag of tags) { + if (tag.kind === TS.SyntaxKind.JSDocParameterTag) { + text.push(` * @${tag.tagName} {${tag.tagType}} ${tag.paramName}`); + if (tag.text) { + text.push(U.indent(tag.text, ' * ', maximalLength)); + } + } else if (tag.tagName === 'example') { + text.push(` * @${tag.tagName}`); + text.push(tag.text); + } else if (U.breakText(tag.text).length > 1) { + text.push(` * @${tag.tagName}`); + if (tag.text) { + text.push(U.indent(tag.text, ' * ', maximalLength)); + } + } else { + text.push(` * @${tag.tagName} ${tag.text}`.trimRight()); + } + } + + return `/**\n${text.join('\n')}\n */`; + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceDoc; diff --git a/sources/SourceDocTag.d.ts b/sources/SourceDocTag.d.ts new file mode 100644 index 0000000..12fe8e9 --- /dev/null +++ b/sources/SourceDocTag.d.ts @@ -0,0 +1,33 @@ +/* * + * + * Imports + * + * */ + + +import type * as U from './Utilities'; +import type SourceToken from './SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +export interface SourceDocTag extends SourceToken { + paramName?: string; + tagName: string; + tagType?: string; +} + + +/* * + * + * Default Export + * + * */ + + +export default SourceDocTag; diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 1548e72..c108ad0 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -92,7 +92,7 @@ export type UnknownObject = Record; export function breakText( text: string ): Array { - return text.split(/\r\n|\r|\n/g); + return text.split(/\r\n|\r|\n/gu); } @@ -162,7 +162,7 @@ export function indent ( } }); - return (newLine ? paddedStr : paddedStr + line.trimRight() + '\n'); + return (newLine ? paddedStr : paddedStr + line.trimRight()); } @@ -234,3 +234,10 @@ export function trimAll ( return text.replace(/\s+/gu, ' ').trim(); } + + +export function trimBreaks ( + text: string +): string { + return text.replace(/^\s+|\s+$/gu, '') +} From d7a7b1a38d364cd2427dbc2af1229264556e3abc Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 25 Apr 2022 19:59:54 +0200 Subject: [PATCH 18/54] Improved detection of advanced tags. --- sources/SourceCode.ts | 3 +- sources/SourceDoc.ts | 102 ++++++++++++++++++++++++-------------- sources/SourceDocTag.d.ts | 4 +- sources/Utilities.ts | 33 ++++++++---- 4 files changed, 90 insertions(+), 52 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index a3a8438..b4948ec 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -178,8 +178,7 @@ export class SourceCode { if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { line.tokens.push({ kind, text }); } else if (SourceDoc.isDocComment(text)) { - const doc = new SourceDoc(text, Math.floor(line.getIndent() / 2)); - line.tokens.push(doc); + line.tokens.push(new SourceDoc(text, Math.floor(line.getIndent() / 2))); } else { line.tokens.push(new SourceComment(text, Math.floor(line.getIndent() / 2))); } diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 89f68a3..8ed196c 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -51,6 +51,20 @@ export class SourceDoc extends SourceLine implements SourceToken { } + private static isTagWithName( + tag: T + ): tag is (T&{name:TS.EntityName}) { + return typeof (tag as {name?:TS.EntityName}).name !== 'undefined'; + } + + + private static isTagWithType( + tag: T + ): tag is (T&{typeExpression:TS.JSDocTypeExpression}) { + return typeof (tag as {typeExpression?:TS.JSDocTypeExpression}).typeExpression !== 'undefined'; + } + + /* * * * Constructor @@ -85,45 +99,40 @@ export class SourceDoc extends SourceLine implements SourceToken { if (tsJSDoc.comment) { tags.push({ kind: TS.SyntaxKind.JSDocText, - tagName: 'description', + tagKind: 'description', text: U.trimBreaks('' + tsJSDoc.comment) }); } if (tsJSDoc.tags) { - let kind: TS.SyntaxKind, - tagName: string, - text: string; + let tag: SourceDocTag; for (const tsTag of tsJSDoc.tags) { - kind = tsTag.kind; - tagName = tsTag.tagName.getText(tsSource); + tag = { + kind: tsTag.kind, + tagKind: tsTag.tagName.text, + text: '' + }; if (tsTag.comment) { if (typeof tsTag.comment === 'string') { - text = tsTag.comment + tag.text = tsTag.comment } else { - text = tsTag.comment + tag.text = tsTag.comment .map(tsNode => tsNode.getText(tsSource)) .join('\n'); } - } else { - text = ''; } - text = U.trimBreaks(text); - - if (TS.isJSDocParameterTag(tsTag)) { - tags.push({ - kind, - paramName: tsTag.name.getText(tsSource), - tagName, - tagType: (tsTag.typeExpression?.type.getText(tsSource) || '*'), - text - }); - } else { - tags.push({ kind, tagName, text }); + if (SourceDoc.isTagWithName(tsTag)) { + tag.tagName = tsTag.name.getText(tsSource); } + + if (SourceDoc.isTagWithType(tsTag)) { + tag.tagType = tsTag.typeExpression.type.getText(tsSource) + } + if (tag.tagKind === 'function') console.log(tag); + tags.push(tag); } } } @@ -174,7 +183,7 @@ export class SourceDoc extends SourceLine implements SourceToken { if ( firstTag && - firstTag.tagName === 'description' + firstTag.tagKind === 'description' ) { text.push(U.indent(firstTag.text, ' * ', maximalLength)); tags.shift(); @@ -184,22 +193,41 @@ export class SourceDoc extends SourceLine implements SourceToken { } } + let part1: string, + part2: string; + for (const tag of tags) { - if (tag.kind === TS.SyntaxKind.JSDocParameterTag) { - text.push(` * @${tag.tagName} {${tag.tagType}} ${tag.paramName}`); - if (tag.text) { - text.push(U.indent(tag.text, ' * ', maximalLength)); - } - } else if (tag.tagName === 'example') { - text.push(` * @${tag.tagName}`); - text.push(tag.text); - } else if (U.breakText(tag.text).length > 1) { - text.push(` * @${tag.tagName}`); - if (tag.text) { - text.push(U.indent(tag.text, ' * ', maximalLength)); - } + + if (tag.tagKind === 'example') { + text.push(' * @example'); + text.push(U.indent(tag.text, ' * ')); + continue; + } + + part1 = `@${tag.tagKind}`; + part2 = tag.text; + + if (tag.tagType) { + part1 += ` {${tag.tagType}}`; + } + + if (tag.tagName) { + part1 += ` ${tag.tagName}`; + } + + if ( + part2 && + ! tag.tagType && + ! tag.tagName && + U.breakText(part2).length === 1 + ) { + text.push(U.indent(`${part1} ${U.removeBreaks(part2)}`.trimRight(), ' * ', maximalLength)); } else { - text.push(` * @${tag.tagName} ${tag.text}`.trimRight()); + text.push(` * ${part1}`); + + if (part2) { + text.push(U.indent(part2, ' * ', maximalLength)); + } } } diff --git a/sources/SourceDocTag.d.ts b/sources/SourceDocTag.d.ts index 12fe8e9..b6c404b 100644 --- a/sources/SourceDocTag.d.ts +++ b/sources/SourceDocTag.d.ts @@ -17,8 +17,8 @@ import type SourceToken from './SourceToken'; export interface SourceDocTag extends SourceToken { - paramName?: string; - tagName: string; + tagKind: string; + tagName?: string; tagType?: string; } diff --git a/sources/Utilities.ts b/sources/Utilities.ts index c108ad0..35df344 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -128,24 +128,29 @@ export function extractTypes( */ export function indent ( text: string, - linePrefix = '', - wrap = 80 + linePrefix: string, + wrap?: number ): string { - const fragments = text.split(/\s/gmu); + + if (!wrap) { + return linePrefix + text.replace(/\r\n|\r|\n/gu, `$0${linePrefix}`); + } + + const fragments = text.replace(/(?:\r\n|\r|\n){2,}/gu, ' \0 ').split(/\s/gmu); let newLine = true, line = '', paddedStr = ''; - fragments.forEach(fragment => { + for (const fragment of fragments) { - if (!newLine && fragment === '') { + if (fragment === '\0') { paddedStr += ( line.trimRight() + '\n' + linePrefix.trimRight() + '\n' ); newLine = true; - return; + continue; } if (!newLine && line.length + fragment.length + 1 > wrap) { @@ -160,13 +165,13 @@ export function indent ( else { line += ' ' + fragment; } - }); + } return (newLine ? paddedStr : paddedStr + line.trimRight()); } -export function isDocumentedNode( +export function isDocumentedNode ( node: T ): node is (T&DocumentedNode) { return ( @@ -175,7 +180,7 @@ export function isDocumentedNode( } -export function isExpressionNode( +export function isExpressionNode ( node: T ): node is (T&ExpressionNode) { return ( @@ -184,7 +189,7 @@ export function isExpressionNode( } -export function isNodeClass( +export function isNodeClass ( node: T, nodeClass: ('Assignment'|'Declaration'|'Expression'|'Signature'|'Statement') ): boolean { @@ -193,7 +198,7 @@ export function isNodeClass( } -export function isNodeStatement( +export function isNodeStatement ( node: T ): node is (T&StatementKind) { return ( @@ -217,6 +222,12 @@ export function isNodeStatement( } +export function removeBreaks ( + text: string +): string { + return text.replace(/[\n\r]+/gu, ' ').trim(); +} + export function trimAll ( text: string, keepParagraphs = false From edafc32909cac6d0f970e8a9211f436dd191411c Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 25 Apr 2022 20:37:29 +0200 Subject: [PATCH 19/54] Fixed double line ending. --- sources/SourceCode.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index b4948ec..0ff95de 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -175,14 +175,6 @@ export class SourceCode { kind = scanner.scan(); text = scanner.getTokenText(); - if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { - line.tokens.push({ kind, text }); - } else if (SourceDoc.isDocComment(text)) { - line.tokens.push(new SourceDoc(text, Math.floor(line.getIndent() / 2))); - } else { - line.tokens.push(new SourceComment(text, Math.floor(line.getIndent() / 2))); - } - if ( kind === TS.SyntaxKind.NewLineTrivia || kind === TS.SyntaxKind.EndOfFileToken @@ -191,6 +183,14 @@ export class SourceCode { line = new SourceLine(); } + if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { + line.tokens.push({ kind, text }); + } else if (SourceDoc.isDocComment(text)) { + line.tokens.push(new SourceDoc(text, Math.floor(line.getIndent() / 2))); + } else { + line.tokens.push(new SourceComment(text, Math.floor(line.getIndent() / 2))); + } + } while (kind !== TS.SyntaxKind.EndOfFileToken) } From b6f7ccb47394610c9ac69db98e0d335cc07df0ce Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 28 Apr 2022 14:48:35 +0200 Subject: [PATCH 20/54] Removed line breaks from all logical line tokens. --- sources/SourceCode.ts | 1 + sources/SourceComment.ts | 39 +++++++++++++++++++++++++++++++++++---- sources/SourceDoc.ts | 5 +++-- sources/SourceLine.ts | 9 +++++---- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 0ff95de..f50a3b7 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -181,6 +181,7 @@ export class SourceCode { ) { lines.push(line); line = new SourceLine(); + continue; } if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index ffdf360..9fba41c 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -46,16 +46,12 @@ export class SourceComment extends SourceLine implements SourceToken { this.kind = TS.SyntaxKind.MultiLineCommentTrivia; const lines = U.breakText(text), - lineBreak = text.includes('\r\n') ? '\r\n' : '\n', tokens = this.tokens; for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { tokens.push({ kind: TS.SyntaxKind.SingleLineCommentTrivia, text: (i ? lines[i].substr(indent) : lines[i]) - }, { - kind: TS.SyntaxKind.NewLineTrivia, - text: lineBreak }); } } @@ -90,6 +86,41 @@ export class SourceComment extends SourceLine implements SourceToken { return this.indent; } + public toString( + maximalLength?: number + ): string { + const lines: Array = []; + + if (maximalLength) { + let line: string = '', + words: Array; + + for (const token of this.tokens) { + words = token.text.split(' '); + + for (const word of words) { + if ((line + word).length > maximalLength) { + lines.push(line); + + if (line.match(/^\s*\*\s*/)) { + line = ''; + } + } + + line += word; + } + + lines.push(line); + } + } else { + for (const token of this.tokens) { + lines.push(token.text); + } + } + + return lines.join('\n'); + } + } diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 8ed196c..fecca33 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -129,9 +129,9 @@ export class SourceDoc extends SourceLine implements SourceToken { } if (SourceDoc.isTagWithType(tsTag)) { - tag.tagType = tsTag.typeExpression.type.getText(tsSource) + tag.tagType = tsTag.typeExpression.getText(tsSource) } - if (tag.tagKind === 'function') console.log(tag); + tags.push(tag); } } @@ -173,6 +173,7 @@ export class SourceDoc extends SourceLine implements SourceToken { public toString( maximalLength?: number ): string { + if (!maximalLength) { return this.text; } diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 8bd17c8..bd4d65f 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -121,9 +121,10 @@ export class SourceLine { public toString( - maxLength?: number + maximalLength?: number ): string { - if (!maxLength) { + + if (!maximalLength) { let text = ''; for (const token of this.tokens) { @@ -140,13 +141,13 @@ export class SourceLine { return ''; } - let line = '', + let line: string = '', tokenText: string; for (const token of tokens) { tokenText = token.text; - if ((line + tokenText).length > maxLength) { + if ((line + tokenText).length > maximalLength) { lines.push(line); line = ''; } From 6fd6c7deadf8f9b27077b836d11a559c7251e4c0 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:35:50 +0200 Subject: [PATCH 21/54] Fixed positioning of multiline comments. --- sources/SourceCode.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index f50a3b7..53ac0de 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -104,9 +104,8 @@ export class SourceCode { ) { tokenLength = U.breakText(tokenText).length; - if (tokenLength > 2) { - // count only the extra lines in-between - position.line += tokenLength - 2; + if (tokenLength > 1) { + position.line += tokenLength - 1; } } @@ -133,9 +132,6 @@ export class SourceCode { const linePosition = this.getLinePosition(line), tokenPosition = line.getTokenPosition(token); - console.log(linePosition); - console.log(tokenPosition); - if (!linePosition || !tokenPosition) { return null; } From 3c57b5e132db6c47a85e275d68630a472d6506d4 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 28 Apr 2022 19:41:12 +0200 Subject: [PATCH 22/54] Added line extraction. --- sources/Utilities.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 35df344..a5a42f2 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -113,6 +113,21 @@ export function extractTypes( } +export function extractFirstLine( + text: string +): string { + return breakText(text)[0]; +} + + +export function extractLastLine( + text: string +): string { + const lines = breakText(text); + return lines[lines.length-1]; +} + + /** * Returns a indented string, that fits into a specific width and spans over * several lines. From 1e5c0033db42e3e244574d2cc82ac49c04db5795 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 29 Apr 2022 17:43:51 +0200 Subject: [PATCH 23/54] Added support for sublines. --- sources/SourceLine.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index bd4d65f..1703358 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -145,9 +145,17 @@ export class SourceLine { tokenText: string; for (const token of tokens) { - tokenText = token.text; - if ((line + tokenText).length > maximalLength) { + if ( + token instanceof SourceLine && + token.tokens.length > 1 + ) { + tokenText = token.toString(maximalLength - line.length); + } else { + tokenText = token.text; + } + + if ((U.extractLastLine(line) + U.extractFirstLine(tokenText)).length > maximalLength) { lines.push(line); line = ''; } From ecf89df3085c4679511e68d825b58b3b8892ad89 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 29 Apr 2022 17:44:53 +0200 Subject: [PATCH 24/54] Added string pad function. --- sources/Utilities.ts | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sources/Utilities.ts b/sources/Utilities.ts index a5a42f2..75ee565 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -135,20 +135,23 @@ export function extractLastLine( * @param text * The string to pad. * - * @param linePrefix + * @param indent * The prefix for each line. * * @param wrap * The maximum width of the padded string. */ export function indent ( + indent: number, + prefix: string, text: string, - linePrefix: string, wrap?: number ): string { + prefix = pad(indent, prefix); + if (!wrap) { - return linePrefix + text.replace(/\r\n|\r|\n/gu, `$0${linePrefix}`); + return text.replace(/\r\n|\r|\n/gu, `\n${prefix}`); } const fragments = text.replace(/(?:\r\n|\r|\n){2,}/gu, ' \0 ').split(/\s/gmu); @@ -162,7 +165,7 @@ export function indent ( if (fragment === '\0') { paddedStr += ( line.trimRight() + '\n' + - linePrefix.trimRight() + '\n' + prefix.trimRight() + '\n' ); newLine = true; continue; @@ -174,10 +177,9 @@ export function indent ( } if (newLine) { - line = linePrefix + fragment; + line = prefix + fragment; newLine = false; - } - else { + } else { line += ' ' + fragment; } } @@ -237,6 +239,15 @@ export function isNodeStatement ( } +export function pad(indent: number, suffix: string = ''): string { + if (indent > 0) { + return ' '.repeat(indent) + suffix; + } + + return suffix; +} + + export function removeBreaks ( text: string ): string { From c07039fbdebb0c42c49b27b6f0ca289efe1469ce Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 29 Apr 2022 17:48:42 +0200 Subject: [PATCH 25/54] Optimized token scanning. --- sources/SourceCode.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 53ac0de..2ea638c 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -161,9 +161,11 @@ export class SourceCode { const scanner = TS.createScanner(TS.ScriptTarget.Latest, false); - let kind: TS.SyntaxKind, + let indent: number, + kind: TS.SyntaxKind, line = new SourceLine(), - text: string; + text: string, + token: SourceToken; scanner.setText(sourceCode); @@ -180,14 +182,20 @@ export class SourceCode { continue; } - if (kind !== TS.SyntaxKind.MultiLineCommentTrivia) { - line.tokens.push({ kind, text }); - } else if (SourceDoc.isDocComment(text)) { - line.tokens.push(new SourceDoc(text, Math.floor(line.getIndent() / 2))); + if (kind === TS.SyntaxKind.MultiLineCommentTrivia) { + indent = Math.floor(line.getIndent() / 2) * 2; + + if (SourceDoc.isDocComment(text)) { + token = new SourceDoc(text, indent); + } else { + token = new SourceComment(text, indent); + } } else { - line.tokens.push(new SourceComment(text, Math.floor(line.getIndent() / 2))); + token = { kind, text }; } + line.tokens.push(token); + } while (kind !== TS.SyntaxKind.EndOfFileToken) } From bf6e87570efbb4ff0ef81d0e631ff42f61c4268f Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 10:14:26 +0200 Subject: [PATCH 26/54] Added some debug test code. --- tests/sources/index.ts | 7 +++++++ tests/sources/tsconfig.json | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/sources/index.ts create mode 100644 tests/sources/tsconfig.json diff --git a/tests/sources/index.ts b/tests/sources/index.ts new file mode 100644 index 0000000..cdaa7a9 --- /dev/null +++ b/tests/sources/index.ts @@ -0,0 +1,7 @@ +import * as U from '../../lib/Utilities'; + +const loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; +const loremIpsumWithBreaks = loremIpsum.replace(/\. /g, '.\n\n'); + +console.log(U.indent(4, ' * ', loremIpsum, 60)); +console.log(U.indent(4, ' * ', loremIpsumWithBreaks, 60)); diff --git a/tests/sources/tsconfig.json b/tests/sources/tsconfig.json new file mode 100644 index 0000000..13bbe5a --- /dev/null +++ b/tests/sources/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../sources/tsconfig.json", + "compilerOptions": { + "outDir": ".", + "rootDir": "." + }, + "files": [ + "index.ts" + ], + "references": [{ + "path": "../../sources/tsconfig.json" + }] +} From cb3e4d2f7f551cf7fcd2c0c7ae6679331bd82c5c Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 10:15:01 +0200 Subject: [PATCH 27/54] Fixed SourceComment. --- sources/SourceComment.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index 9fba41c..a772ef8 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -86,6 +86,7 @@ export class SourceComment extends SourceLine implements SourceToken { return this.indent; } + public toString( maximalLength?: number ): string { @@ -93,24 +94,32 @@ export class SourceComment extends SourceLine implements SourceToken { if (maximalLength) { let line: string = '', + match: (RegExpMatchArray|null), words: Array; for (const token of this.tokens) { + words = token.text.split(' '); + line = words.shift() || ''; for (const word of words) { + if ((line + word).length > maximalLength) { - lines.push(line); + lines.push(line.trimRight()); - if (line.match(/^\s*\*\s*/)) { - line = ''; + match = line.match(/^\s*\*+\s*/); + + if (match && match.groups) { + line = match[0] + word; + } else { + line = word; } + } else { + line += ` ${word}`; } - - line += word; } - lines.push(line); + lines.push(line.trimRight()); } } else { for (const token of this.tokens) { From 0cee6f3eeec34aa293443bc6767041f229f001ea Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 11:10:20 +0200 Subject: [PATCH 28/54] Improved tags parsing. --- sources/SourceCode.ts | 2 +- sources/SourceDoc.ts | 145 +++++++++++++++++++++++++++--------------- 2 files changed, 95 insertions(+), 52 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 2ea638c..04fc41b 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -185,7 +185,7 @@ export class SourceCode { if (kind === TS.SyntaxKind.MultiLineCommentTrivia) { indent = Math.floor(line.getIndent() / 2) * 2; - if (SourceDoc.isDocComment(text)) { + if (SourceDoc.isSourceDoc(text)) { token = new SourceDoc(text, indent); } else { token = new SourceComment(text, indent); diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index fecca33..5ab6ede 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -14,10 +14,27 @@ import * as TS from 'typescript'; +import * as U from './Utilities'; import SourceDocTag from './SourceDocTag'; import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; -import * as U from './Utilities'; + + +/* * + * + * Declarations + * + * */ + + +interface NamedTag extends TS.JSDocTag { + name: TS.EntityName; +} + + +interface TypedTag extends TS.JSDocTag { + typeExpression: TS.JSDocTypeExpression; +} /* * @@ -37,31 +54,59 @@ export class SourceDoc extends SourceLine implements SourceToken { * */ - public static isDocComment( - text: string - ): boolean { - return /^\/\*\*\s/.test(text); - } + private static decorateTag( + tag: SourceDocTag, + tsSource: TS.SourceFile, + tsNode: (TS.JSDoc|TS.JSDocTag) + ): SourceDocTag { + + if (tsNode.comment) { + if (typeof tsNode.comment === 'string') { + tag.text = tsNode.comment + } else { + tag.text = tsNode.comment + .map(tsComment => tsComment.getText(tsSource)) + .join('\n'); + } + } + if (typeof (tsNode as NamedTag).name !== 'undefined') { + tag.tagName = (tsNode as NamedTag).name.getText(tsSource); + } else { + const match = tag.text.match(/^{[\w.()<>]+} +([^\[\]\s]+|\[[^\[\]\s]+\])(?:\r\n|\r|\n)/); - private static isNodeWithJSDoc( - node: T - ): node is (T&{jsDoc:Array}) { - return typeof (node as {jsDoc?:TS.JSDoc}).jsDoc !== 'undefined'; + if (match) { + tag.tagName = match[1]; + tag.text = tag.text.substr(match[0].length); + } + } + + if (typeof (tsNode as TypedTag).typeExpression !== 'undefined') { + tag.tagType = (tsNode as TypedTag).typeExpression.getText(tsSource); + } else { + const match = tag.text.match(/^({[\w.()<>]+}) /); + + if (match) { + tag.tagType = match[1]; + tag.text = tag.text.substr(match[0].length); + } + } + + return tag; } - private static isTagWithName( - tag: T - ): tag is (T&{name:TS.EntityName}) { - return typeof (tag as {name?:TS.EntityName}).name !== 'undefined'; + public static isSourceDoc( + text: string + ): boolean { + return /^\/\*\*\s/.test(text); } - private static isTagWithType( - tag: T - ): tag is (T&{typeExpression:TS.JSDocTypeExpression}) { - return typeof (tag as {typeExpression?:TS.JSDocTypeExpression}).typeExpression !== 'undefined'; + private static isNodeWithJSDoc( + tsNode: T + ): tsNode is (T&{jsDoc:Array}) { + return typeof (tsNode as {jsDoc?:TS.JSDoc}).jsDoc !== 'undefined'; } @@ -88,6 +133,8 @@ export class SourceDoc extends SourceLine implements SourceToken { tsJSDocs: Array = [], tsChildren = tsSource.getChildren(tsSource); + let tag: SourceDocTag; + for (const tsChild of tsChildren) { if (SourceDoc.isNodeWithJSDoc(tsChild)) { tsJSDocs.push(...tsChild.jsDoc); @@ -97,16 +144,18 @@ export class SourceDoc extends SourceLine implements SourceToken { for (const tsJSDoc of tsJSDocs) { if (tsJSDoc.comment) { - tags.push({ + tag = { kind: TS.SyntaxKind.JSDocText, tagKind: 'description', - text: U.trimBreaks('' + tsJSDoc.comment) - }); + text: '' + }; + + SourceDoc.decorateTag(tag, tsSource, tsJSDoc); + + tags.push(tag); } if (tsJSDoc.tags) { - let tag: SourceDocTag; - for (const tsTag of tsJSDoc.tags) { tag = { kind: tsTag.kind, @@ -114,23 +163,7 @@ export class SourceDoc extends SourceLine implements SourceToken { text: '' }; - if (tsTag.comment) { - if (typeof tsTag.comment === 'string') { - tag.text = tsTag.comment - } else { - tag.text = tsTag.comment - .map(tsNode => tsNode.getText(tsSource)) - .join('\n'); - } - } - - if (SourceDoc.isTagWithName(tsTag)) { - tag.tagName = tsTag.name.getText(tsSource); - } - - if (SourceDoc.isTagWithType(tsTag)) { - tag.tagType = tsTag.typeExpression.getText(tsSource) - } + SourceDoc.decorateTag(tag, tsSource, tsTag); tags.push(tag); } @@ -178,19 +211,20 @@ export class SourceDoc extends SourceLine implements SourceToken { return this.text; } - const tags = this.tokens, + const indent = this.indent, + tags = this.tokens, firstTag = tags[0], - text: Array = []; + text: Array = ['/**']; if ( firstTag && firstTag.tagKind === 'description' ) { - text.push(U.indent(firstTag.text, ' * ', maximalLength)); + text.push(U.indent(indent, ' * ', firstTag.text, maximalLength)); tags.shift(); if (tags.length) { - text.push(' *'); + text.push(U.pad(indent, ' *')); } } @@ -200,16 +234,19 @@ export class SourceDoc extends SourceLine implements SourceToken { for (const tag of tags) { if (tag.tagKind === 'example') { - text.push(' * @example'); - text.push(U.indent(tag.text, ' * ')); + text.push(U.pad(indent, ' * @example')); + text.push(U.indent(indent, ' * ', tag.text)); continue; } + if (tag.tagKind === 'sample') { + console.log(tag); + } part1 = `@${tag.tagKind}`; part2 = tag.text; if (tag.tagType) { - part1 += ` {${tag.tagType}}`; + part1 += ` ${tag.tagType}`; } if (tag.tagName) { @@ -222,17 +259,23 @@ export class SourceDoc extends SourceLine implements SourceToken { ! tag.tagName && U.breakText(part2).length === 1 ) { - text.push(U.indent(`${part1} ${U.removeBreaks(part2)}`.trimRight(), ' * ', maximalLength)); + text.push(U.indent( + indent, + ' * ', + `${part1} ${U.removeBreaks(part2)}`.trimRight(), + maximalLength + )); } else { - text.push(` * ${part1}`); + text.push(U.pad(indent, ` * ${part1}`)); if (part2) { - text.push(U.indent(part2, ' * ', maximalLength)); + text.push(U.indent(indent, ' * ', part2, maximalLength)); } } } - return `/**\n${text.join('\n')}\n */`; + text.push(U.pad(indent, ' */')); + return text.join('\n'); } From 446c5f5e686ba1a028b106be9b9a592d793443cc Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 11:10:48 +0200 Subject: [PATCH 29/54] Fixed tsconfig.json. --- sources/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/tsconfig.json b/sources/tsconfig.json index 7408c36..28f46d5 100644 --- a/sources/tsconfig.json +++ b/sources/tsconfig.json @@ -4,7 +4,7 @@ /* Projects */ // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ From 59dfd61e0756bd88bca49cb52225ff2306559a43 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 11:11:10 +0200 Subject: [PATCH 30/54] Fixed indent function. --- sources/Utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 75ee565..b0b5d53 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -151,7 +151,7 @@ export function indent ( prefix = pad(indent, prefix); if (!wrap) { - return text.replace(/\r\n|\r|\n/gu, `\n${prefix}`); + return prefix + text.replace(/\r\n|\r|\n/gu, `\n${prefix}`); } const fragments = text.replace(/(?:\r\n|\r|\n){2,}/gu, ' \0 ').split(/\s/gmu); From f119e51fa7a81eb56ff43cd01f1bb584a00069f4 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 12:36:16 +0200 Subject: [PATCH 31/54] Fixed MD code blocks. --- sources/SourceDoc.ts | 46 +++++++++++++++++++++++--------------------- sources/Utilities.ts | 39 ++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 5ab6ede..20c7f5f 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -73,7 +73,9 @@ export class SourceDoc extends SourceLine implements SourceToken { if (typeof (tsNode as NamedTag).name !== 'undefined') { tag.tagName = (tsNode as NamedTag).name.getText(tsSource); } else { - const match = tag.text.match(/^{[\w.()<>]+} +([^\[\]\s]+|\[[^\[\]\s]+\])(?:\r\n|\r|\n)/); + const match = tag.text.match( + /^{[\w.()<>]+} +([^\[\]\s]+|\[[^\[\]\s]+\])(?:\r\n|\r|\n)/ + ); if (match) { tag.tagName = match[1]; @@ -214,36 +216,35 @@ export class SourceDoc extends SourceLine implements SourceToken { const indent = this.indent, tags = this.tokens, firstTag = tags[0], - text: Array = ['/**']; + lines: Array = ['/**']; + + let part1: string, + part2: string; if ( firstTag && firstTag.tagKind === 'description' ) { - text.push(U.indent(indent, ' * ', firstTag.text, maximalLength)); + part2 = U.trimBreaks(firstTag.text); + + lines.push(U.indent(indent, ' * ', part2, maximalLength)); tags.shift(); if (tags.length) { - text.push(U.pad(indent, ' *')); + lines.push(U.pad(indent, ' *')); } } - let part1: string, - part2: string; - for (const tag of tags) { + part1 = `@${tag.tagKind}`; + part2 = U.trimBreaks(tag.text); + if (tag.tagKind === 'example') { - text.push(U.pad(indent, ' * @example')); - text.push(U.indent(indent, ' * ', tag.text)); + lines.push(U.pad(indent, ` * ${part1}`)); + lines.push(U.indent(indent, ' * ', part2)); continue; } - if (tag.tagKind === 'sample') { - console.log(tag); - } - - part1 = `@${tag.tagKind}`; - part2 = tag.text; if (tag.tagType) { part1 += ` ${tag.tagType}`; @@ -257,25 +258,26 @@ export class SourceDoc extends SourceLine implements SourceToken { part2 && ! tag.tagType && ! tag.tagName && - U.breakText(part2).length === 1 + U.breakText(tag.text).length === 1 ) { - text.push(U.indent( + lines.push(U.indent( indent, ' * ', - `${part1} ${U.removeBreaks(part2)}`.trimRight(), + `${part1} ${part2}`.trimRight(), maximalLength )); } else { - text.push(U.pad(indent, ` * ${part1}`)); + lines.push(U.pad(indent, ` * ${part1}`)); if (part2) { - text.push(U.indent(indent, ' * ', part2, maximalLength)); + lines.push(U.indent(indent, ' * ', part2, maximalLength)); } } } - text.push(U.pad(indent, ' */')); - return text.join('\n'); + lines.push(U.pad(indent, ' */')); + + return lines.join('\n'); } diff --git a/sources/Utilities.ts b/sources/Utilities.ts index b0b5d53..567678f 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -154,37 +154,54 @@ export function indent ( return prefix + text.replace(/\r\n|\r|\n/gu, `\n${prefix}`); } - const fragments = text.replace(/(?:\r\n|\r|\n){2,}/gu, ' \0 ').split(/\s/gmu); + const fragments = text + .replace(/(?:\r\n|\r|\n){2,}/gu, ' \x00 ') // double break + .replace(/(?:\r\n|\r|\n)/gu, ' \x05 ') // single break + .split(/\s/gmu); - let newLine = true, - line = '', + let codeBlock = false, + line = prefix, paddedStr = ''; for (const fragment of fragments) { - if (fragment === '\0') { + if (fragment === '\x00') { paddedStr += ( line.trimRight() + '\n' + prefix.trimRight() + '\n' ); - newLine = true; + line = prefix; continue; } - if (!newLine && line.length + fragment.length + 1 > wrap) { - paddedStr += line.trimRight() + '\n'; - newLine = true; + if (fragment === '\x05') { + if (codeBlock) { + paddedStr += line.trimRight() + '\n'; + line = prefix; + } + continue; } - if (newLine) { + if (fragment.startsWith('```')) { + codeBlock = !codeBlock; + if (line !== prefix) { + paddedStr += line.trimRight() + '\n'; + } + line = prefix + fragment; + continue; + } + + if (!codeBlock && line.length + fragment.length + 1 > wrap) { + paddedStr += line.trimRight() + '\n'; line = prefix + fragment; - newLine = false; + } else if (line === prefix) { + line += (fragment || ' '); } else { line += ' ' + fragment; } } - return (newLine ? paddedStr : paddedStr + line.trimRight()); + return (line === prefix ? paddedStr : paddedStr + line.trimRight()); } From 9a189b557d2710acb39e628b13f77af391a50ef8 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 15:02:28 +0200 Subject: [PATCH 32/54] Fixed doclet indent and line breaks. --- sources/RuleContext.ts | 2 +- sources/SourceCode.ts | 39 ++++++++++------- sources/SourceComment.ts | 7 ++-- sources/SourceDoc.ts | 5 ++- sources/SourceLine.ts | 77 +++++++++++++++++++++------------- sources/Utilities.ts | 2 +- sources/rules/pretty-length.ts | 68 ++++++++++++------------------ 7 files changed, 108 insertions(+), 92 deletions(-) diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index efd350d..da555c2 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -161,7 +161,7 @@ export class RuleContext { message: string, fix?: ESLint.Rule.ReportFixer ): void { - this.esLintContext.report( { + this.esLintContext.report({ fix, loc: { // ESLint needs column zero-based: diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index 04fc41b..a5d743d 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -44,7 +44,9 @@ export class SourceCode { sourceCode: string ) { this.fileName = fileName; + this.lineBreak = sourceCode.match(/\r\n/) ? '\r\n' : '\n'; this.lines = []; + this.raw = sourceCode; this.parse(sourceCode); } @@ -59,9 +61,15 @@ export class SourceCode { public readonly fileName: string; + public readonly lineBreak: string; + + public readonly lines: Array; + public readonly raw: string; + + /* * * * Functions @@ -113,9 +121,10 @@ export class SourceCode { } position.line += 1; + position.start += 1; // line break } - position.end = position.start + U.breakText(line.toString())[0].length; + position.end = position.start + line.toString().length; return position; } @@ -141,7 +150,7 @@ export class SourceCode { end: linePosition.start + tokenPosition.end, line: linePosition.line + tokenPosition.line - 1, start: linePosition.start + tokenPosition.start - } + }; } @@ -149,7 +158,8 @@ export class SourceCode { sourceCode: string, replace = false ) { - const lines = this.lines; + const lineBreak = this.lineBreak, + lines = this.lines; if (replace) { lines.length = 0; @@ -163,7 +173,7 @@ export class SourceCode { let indent: number, kind: TS.SyntaxKind, - line = new SourceLine(), + line = new SourceLine(lineBreak), text: string, token: SourceToken; @@ -178,7 +188,7 @@ export class SourceCode { kind === TS.SyntaxKind.EndOfFileToken ) { lines.push(line); - line = new SourceLine(); + line = new SourceLine(lineBreak); continue; } @@ -186,9 +196,9 @@ export class SourceCode { indent = Math.floor(line.getIndent() / 2) * 2; if (SourceDoc.isSourceDoc(text)) { - token = new SourceDoc(text, indent); + token = new SourceDoc(text, lineBreak, indent); } else { - token = new SourceComment(text, indent); + token = new SourceComment(text, lineBreak, indent); } } else { token = { kind, text }; @@ -200,16 +210,17 @@ export class SourceCode { } - public toString (): string { - const lines = this.lines; - - let text = ''; + public toString ( + maximalLength?: number + ): string { + const lines = this.lines, + strings: Array = []; - for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { - text += lines[i].toString(); + for (const line of lines) { + strings.push(line.toString(maximalLength)); } - return text; + return strings.join(this.lineBreak); } diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index a772ef8..17d9d41 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -38,9 +38,10 @@ export class SourceComment extends SourceLine implements SourceToken { public constructor ( text: string, + lineBreak: string = '\n', indent: number = 0 ) { - super(); + super(lineBreak); this.indent = indent; this.kind = TS.SyntaxKind.MultiLineCommentTrivia; @@ -104,7 +105,7 @@ export class SourceComment extends SourceLine implements SourceToken { for (const word of words) { - if ((line + word).length > maximalLength) { + if (line.length + 1 + word.length > maximalLength) { lines.push(line.trimRight()); match = line.match(/^\s*\*+\s*/); @@ -127,7 +128,7 @@ export class SourceComment extends SourceLine implements SourceToken { } } - return lines.join('\n'); + return lines.join(this.lineBreak); } diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 20c7f5f..03c38bf 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -121,9 +121,10 @@ export class SourceDoc extends SourceLine implements SourceToken { public constructor ( text: string, + lineBreak: string = '\n', indent: number = 0 ) { - super(); + super(lineBreak); this.kind = TS.SyntaxKind.JSDocComment; this.indent = indent; @@ -277,7 +278,7 @@ export class SourceDoc extends SourceLine implements SourceToken { lines.push(U.pad(indent, ' */')); - return lines.join('\n'); + return lines.join(this.lineBreak); } diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 1703358..ad837fb 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -29,6 +29,21 @@ import SourceToken from './SourceToken'; export class SourceLine { + /* * + * + * Constructor + * + * */ + + + public constructor( + lineBreak: string = '\n' + ) { + this.lineBreak = lineBreak; + this.tokens = []; + } + + /* * * * Properties @@ -36,7 +51,10 @@ export class SourceLine { * */ - public readonly tokens: Array = []; + public readonly lineBreak: string; + + + public readonly tokens: Array; /* * @@ -123,49 +141,48 @@ export class SourceLine { public toString( maximalLength?: number ): string { - - if (!maximalLength) { - let text = ''; - - for (const token of this.tokens) { - text += token.text; - } - - return text; - } - const lines: Array = [], tokens = this.tokens; + let line: string = ''; + if (!tokens.length) { - return ''; + return line; } - let line: string = '', - tokenText: string; + if (maximalLength) { + let tokenText: string; - for (const token of tokens) { + for (const token of tokens) { - if ( - token instanceof SourceLine && - token.tokens.length > 1 - ) { - tokenText = token.toString(maximalLength - line.length); - } else { - tokenText = token.text; + if ( + token instanceof SourceLine && + token.tokens.length > 1 + ) { + tokenText = token.toString(maximalLength); + } else { + tokenText = token.text; + } + + if ((U.extractLastLine(line) + U.extractFirstLine(tokenText)).length > maximalLength) { + lines.push(line); + line = ''; + } + + line += tokenText; } - if ((U.extractLastLine(line) + U.extractFirstLine(tokenText)).length > maximalLength) { - lines.push(line); - line = ''; + lines.push(line); + } else { + + for (const token of tokens) { + line += token.text; } - line += tokenText; + lines.push(line); } - lines.push(line); - - return lines.join('\n'); + return lines.join(this.lineBreak); } diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 567678f..faaca39 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -191,7 +191,7 @@ export function indent ( continue; } - if (!codeBlock && line.length + fragment.length + 1 > wrap) { + if (!codeBlock && line.length + 1 + fragment.length > wrap) { paddedStr += line.trimRight() + '\n'; line = prefix + fragment; } else if (line === prefix) { diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index efb1ace..97b3a3d 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -15,13 +15,8 @@ import * as ESLint from 'eslint'; -import * as TS from 'typescript'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; -import SourceComment from '../SourceComment'; -import SourceDoc from '../SourceDoc'; -import SourceLine from '../SourceLine'; -import SourcePosition from '../SourcePosition'; /* * @@ -74,46 +69,32 @@ const optionsSchema = { function createFixer ( - line: SourceLine, - position: SourcePosition, - maximalLength: number + context: PrettyLengthContext ): ESLint.Rule.ReportFixer { return (): ESLint.Rule.Fix => { - const range: ESLint.AST.Range = [ position.start, position.end ], - text: Array = [], - tokens = line.tokens; - - let indent: number, - lineText = '', - tokenText: string; - - for (const token of tokens) { - if (token.kind === TS.SyntaxKind.NewLineTrivia) { - continue; - } + const code = context.sourceCode, + fix: Array = [], + range: ESLint.AST.Range = [ 0, code.raw.length ], + lines = code.lines, + { + ignorePattern, + maximalLength + } = context.options, + ignoreRegExp = (ignorePattern ? new RegExp(ignorePattern) : void 0); + + for (const l of lines) { if ( - token instanceof SourceComment || - token instanceof SourceDoc + ignoreRegExp && + ignoreRegExp.test(l.toString()) ) { - indent = token.getIndent(); - tokenText = token.toString(maximalLength - indent); continue; } - tokenText = token.text; - - if ((lineText.length + tokenText.length) > maximalLength) { - text.push(lineText); - lineText = ''; - } - - lineText += tokenText; + fix.push(l.toString(maximalLength)); } - text.push(lineText); - - return { range, text: text.join('\n') }; + return { range, text: fix.join(code.lineBreak) }; }; } @@ -130,9 +111,10 @@ function lint ( lines = code.lines, message = messageTemplate.replace('{0}', `${maximalLength}`); - let lineLength: number; + let maximalLineLength: number; for (const line of lines) { + if ( ignoreRegExp && ignoreRegExp.test(line.toString()) @@ -140,9 +122,9 @@ function lint ( continue; } - lineLength = line.getMaximalLength(); + maximalLineLength = line.getMaximalLength(); - if (lineLength > maximalLength) { + if (maximalLineLength > maximalLength) { const position = code.getLinePosition(line); if (position) { @@ -151,7 +133,11 @@ function lint ( let lineIndex = 0; for (const wrappedLine of wrappedLines) { - if (wrappedLine.length > maximalLength) { + + if ( + wrappedLine.length > maximalLength && + wrappedLine.split(/\s+/g).length > 1 + ) { context.report( { column: maximalLength + 1, @@ -159,8 +145,8 @@ function lint ( line: position.line + lineIndex, start: position.start }, - message + ` ${lineLength}`, - createFixer(line, position, maximalLength) + message + ` ${maximalLineLength}`, + createFixer(context) ); } From 51377ac9fa1805cace856b4740fc071f21d69741 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 2 May 2022 19:18:54 +0200 Subject: [PATCH 33/54] Improved handling of LB in ML comments. --- sources/SourceCode.ts | 4 +- sources/SourceComment.ts | 2 +- sources/SourceDoc.ts | 147 +++++++++++++++------------------------ sources/SourceLine.ts | 4 +- sources/Utilities.ts | 64 ++++++++++------- 5 files changed, 99 insertions(+), 122 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index a5d743d..e07062d 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -44,7 +44,7 @@ export class SourceCode { sourceCode: string ) { this.fileName = fileName; - this.lineBreak = sourceCode.match(/\r\n/) ? '\r\n' : '\n'; + this.lineBreak = U.detectLineBreak(sourceCode) || '\n'; this.lines = []; this.raw = sourceCode; this.parse(sourceCode); @@ -110,7 +110,7 @@ export class SourceCode { token.kind === TS.SyntaxKind.JSDocComment || token.kind === TS.SyntaxKind.MultiLineCommentTrivia ) { - tokenLength = U.breakText(tokenText).length; + tokenLength = tokenText.split(U.lineBreaks).length; if (tokenLength > 1) { position.line += tokenLength - 1; diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index 17d9d41..18b1319 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -46,7 +46,7 @@ export class SourceComment extends SourceLine implements SourceToken { this.indent = indent; this.kind = TS.SyntaxKind.MultiLineCommentTrivia; - const lines = U.breakText(text), + const lines = text.split(U.lineBreaks), tokens = this.tokens; for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 03c38bf..653a7f6 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -20,23 +20,6 @@ import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; -/* * - * - * Declarations - * - * */ - - -interface NamedTag extends TS.JSDocTag { - name: TS.EntityName; -} - - -interface TypedTag extends TS.JSDocTag { - typeExpression: TS.JSDocTypeExpression; -} - - /* * * * Class @@ -56,42 +39,29 @@ export class SourceDoc extends SourceLine implements SourceToken { private static decorateTag( tag: SourceDocTag, - tsSource: TS.SourceFile, - tsNode: (TS.JSDoc|TS.JSDocTag) + lineBreak: string ): SourceDocTag { - if (tsNode.comment) { - if (typeof tsNode.comment === 'string') { - tag.text = tsNode.comment - } else { - tag.text = tsNode.comment - .map(tsComment => tsComment.getText(tsSource)) - .join('\n'); - } - } + const match = tag.text.match( + /^@(\w+)( +\{[\w.()<>]+\})?( +[^\[\]\s]+|\[[^\[\]\s]+\])?(.*)$/su + ); - if (typeof (tsNode as NamedTag).name !== 'undefined') { - tag.tagName = (tsNode as NamedTag).name.getText(tsSource); - } else { - const match = tag.text.match( - /^{[\w.()<>]+} +([^\[\]\s]+|\[[^\[\]\s]+\])(?:\r\n|\r|\n)/ - ); + if (match) { + tag.text = match[4] || ''; - if (match) { - tag.tagName = match[1]; - tag.text = tag.text.substr(match[0].length); + if (match[1]) { + tag.tagKind = match[1]; + } + if (match[2]) { + tag.tagType = match[2] + } + if (match[3]) { + tag.tagName = match[3] } } - if (typeof (tsNode as TypedTag).typeExpression !== 'undefined') { - tag.tagType = (tsNode as TypedTag).typeExpression.getText(tsSource); - } else { - const match = tag.text.match(/^({[\w.()<>]+}) /); - - if (match) { - tag.tagType = match[1]; - tag.text = tag.text.substr(match[0].length); - } + if (tag.text.startsWith(lineBreak)) { + tag.text = tag.text.substr(lineBreak.length); } return tag; @@ -105,13 +75,6 @@ export class SourceDoc extends SourceLine implements SourceToken { } - private static isNodeWithJSDoc( - tsNode: T - ): tsNode is (T&{jsDoc:Array}) { - return typeof (tsNode as {jsDoc?:TS.JSDoc}).jsDoc !== 'undefined'; - } - - /* * * * Constructor @@ -132,45 +95,48 @@ export class SourceDoc extends SourceLine implements SourceToken { this.tokens = []; const tags = this.tokens, - tsSource = TS.createSourceFile('', text, TS.ScriptTarget.Latest, void 0, TS.ScriptKind.JS), - tsJSDocs: Array = [], - tsChildren = tsSource.getChildren(tsSource); - - let tag: SourceDocTag; - - for (const tsChild of tsChildren) { - if (SourceDoc.isNodeWithJSDoc(tsChild)) { - tsJSDocs.push(...tsChild.jsDoc); - } - } - - for (const tsJSDoc of tsJSDocs) { + lines = text + .substr(3, text.length - 5) + .split(U.lineBreaks) + .slice(1, -1); + + let line: string, + tag: SourceDocTag = { + kind: TS.SyntaxKind.JSDocTag, + tagKind: 'description', + text: '' + }; + + tags.push(tag); + + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + line = lines[i].substr(indent).replace(/^ \* ?/gu, ''); + + if (!line && tags.length > 1) { + tags.push({ + kind: TS.SyntaxKind.WhitespaceTrivia, + tagKind: '', + text: '' + }); + } else if (line.startsWith('@')) { + if (!i) { + tags.pop(); // remove empty description + } - if (tsJSDoc.comment) { tag = { - kind: TS.SyntaxKind.JSDocText, - tagKind: 'description', - text: '' + kind: TS.SyntaxKind.JSDocTag, + tagKind: '', + text: line }; - SourceDoc.decorateTag(tag, tsSource, tsJSDoc); - tags.push(tag); + } else { + tag.text += lineBreak + line; } + } - if (tsJSDoc.tags) { - for (const tsTag of tsJSDoc.tags) { - tag = { - kind: tsTag.kind, - tagKind: tsTag.tagName.text, - text: '' - }; - - SourceDoc.decorateTag(tag, tsSource, tsTag); - - tags.push(tag); - } - } + for (const tag of tags) { + SourceDoc.decorateTag(tag, lineBreak); } } @@ -230,14 +196,15 @@ export class SourceDoc extends SourceLine implements SourceToken { lines.push(U.indent(indent, ' * ', part2, maximalLength)); tags.shift(); - - if (tags.length) { - lines.push(U.pad(indent, ' *')); - } } for (const tag of tags) { + if (tag.tagKind === '') { + lines.push(U.pad(indent, ' *')); + continue; + } + part1 = `@${tag.tagKind}`; part2 = U.trimBreaks(tag.text); @@ -259,7 +226,7 @@ export class SourceDoc extends SourceLine implements SourceToken { part2 && ! tag.tagType && ! tag.tagName && - U.breakText(tag.text).length === 1 + tag.text.split(U.lineBreaks).length === 1 ) { lines.push(U.indent( indent, diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index ad837fb..90dfe94 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -121,7 +121,7 @@ export class SourceLine { token.kind === TS.SyntaxKind.JSDocComment || token.kind === TS.SyntaxKind.MultiLineCommentTrivia ) { - position.line += U.breakText(tokenText).length - 1; + position.line += tokenText.split(U.lineBreaks).length - 1; } position.start += tokenText.length; @@ -134,7 +134,7 @@ export class SourceLine { public getWrappedLines(): Array { - return U.breakText(this.toString()); + return this.toString().split(U.lineBreaks); } diff --git a/sources/Utilities.ts b/sources/Utilities.ts index faaca39..5b58599 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -84,16 +84,22 @@ export type UnknownObject = Record; /* * * - * Functions + * Constants * * */ -export function breakText( - text: string -): Array { - return text.split(/\r\n|\r|\n/gu); -} +export const lineBreaks = /\r\n|\r|\n/gu; + + +export const paragraph = new RegExp(`${lineBreaks.source}{2,}`, 'gu'); + + +/* * + * + * Functions + * + * */ export function extractTypes( @@ -116,18 +122,25 @@ export function extractTypes( export function extractFirstLine( text: string ): string { - return breakText(text)[0]; + return text.split(lineBreaks)[0]; } export function extractLastLine( text: string ): string { - const lines = breakText(text); + const lines = text.split(lineBreaks); return lines[lines.length-1]; } +export function detectLineBreak( + text: string +): (string|undefined) { + return text.match(new RegExp(lineBreaks.source, 'u'))?.[0] +} + + /** * Returns a indented string, that fits into a specific width and spans over * several lines. @@ -147,16 +160,17 @@ export function indent ( text: string, wrap?: number ): string { + const lb = detectLineBreak(text); prefix = pad(indent, prefix); if (!wrap) { - return prefix + text.replace(/\r\n|\r|\n/gu, `\n${prefix}`); + return prefix + text.replace(lineBreaks, `${lb}${prefix}`); } const fragments = text - .replace(/(?:\r\n|\r|\n){2,}/gu, ' \x00 ') // double break - .replace(/(?:\r\n|\r|\n)/gu, ' \x05 ') // single break + .replace(paragraph, ' \x00 ') // paragraphs + .replace(lineBreaks, ' \x05 ') // single break .split(/\s/gmu); let codeBlock = false, @@ -166,17 +180,14 @@ export function indent ( for (const fragment of fragments) { if (fragment === '\x00') { - paddedStr += ( - line.trimRight() + '\n' + - prefix.trimRight() + '\n' - ); + paddedStr += line.trimRight() + lb + prefix.trimRight() + lb; line = prefix; continue; } if (fragment === '\x05') { if (codeBlock) { - paddedStr += line.trimRight() + '\n'; + paddedStr += line.trimRight() + lb; line = prefix; } continue; @@ -185,14 +196,14 @@ export function indent ( if (fragment.startsWith('```')) { codeBlock = !codeBlock; if (line !== prefix) { - paddedStr += line.trimRight() + '\n'; + paddedStr += line.trimRight() + lb; } line = prefix + fragment; continue; } if (!codeBlock && line.length + 1 + fragment.length > wrap) { - paddedStr += line.trimRight() + '\n'; + paddedStr += line.trimRight() + lb; line = prefix + fragment; } else if (line === prefix) { line += (fragment || ' '); @@ -268,22 +279,21 @@ export function pad(indent: number, suffix: string = ''): string { export function removeBreaks ( text: string ): string { - return text.replace(/[\n\r]+/gu, ' ').trim(); + return text.replace(lineBreaks, ' ').trim(); } export function trimAll ( text: string, keepParagraphs = false ): string { - if (keepParagraphs) { - const fragments = text.split(/\n\s*\n/gu), - trimmed: Array = []; + const lb = detectLineBreak(text) || '\n'; - for (let i = 0, iEnd = fragments.length; i < iEnd; ++i) { - trimmed.push(trimAll(fragments[i])); - } - - return trimmed.join('\n\n'); + if (keepParagraphs) { + return text + .replace(paragraph, ' \x00 ') + .replace(/\s+/gu, ' ') + .trim() + .replace(/ ?\x00 ?/, `${lb}${lb}`); } return text.replace(/\s+/gu, ' ').trim(); From 13625b0d6ae45c8f57dddc869312e9ad808229f8 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Tue, 3 May 2022 19:30:03 +0200 Subject: [PATCH 34/54] Fixed edge cases. --- sources/SourceCode.ts | 2 +- sources/SourceComment.ts | 2 +- sources/SourceDoc.ts | 152 ++++++++++++++++++++++++++------------- sources/SourceLine.ts | 4 +- sources/SourceTree.ts | 1 - sources/Utilities.ts | 54 +++++++------- 6 files changed, 136 insertions(+), 79 deletions(-) diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index e07062d..e2d3b5d 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -110,7 +110,7 @@ export class SourceCode { token.kind === TS.SyntaxKind.JSDocComment || token.kind === TS.SyntaxKind.MultiLineCommentTrivia ) { - tokenLength = tokenText.split(U.lineBreaks).length; + tokenLength = tokenText.split(U.LINE_BREAKS).length; if (tokenLength > 1) { position.line += tokenLength - 1; diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index 18b1319..bde6b28 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -46,7 +46,7 @@ export class SourceComment extends SourceLine implements SourceToken { this.indent = indent; this.kind = TS.SyntaxKind.MultiLineCommentTrivia; - const lines = text.split(U.lineBreaks), + const lines = text.split(U.LINE_BREAKS), tokens = this.tokens; for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 653a7f6..f4156ac 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -38,30 +38,57 @@ export class SourceDoc extends SourceLine implements SourceToken { private static decorateTag( - tag: SourceDocTag, - lineBreak: string + tag: SourceDocTag ): SourceDocTag { const match = tag.text.match( - /^@(\w+)( +\{[\w.()<>]+\})?( +[^\[\]\s]+|\[[^\[\]\s]+\])?(.*)$/su + /^@(\w+)(\s+\{[\w.()<>]+\})?(\s+[^\[\]\s]+|\[[^\[\]\s]+\])?(.*)$/su ); if (match) { - tag.text = match[4] || ''; - - if (match[1]) { - tag.tagKind = match[1]; + const { + 1: tagKind, + 4: tagText + } = match; + + let { + 2: tagType, + 3: tagName + } = match; + + if (tagText[0] === ' ') { + tag.text = tagText.substr(1); + } else { + tag.text = tagText; } - if (match[2]) { - tag.tagType = match[2] + + if (tagName) { + tagName = tagName.replace(/^ /u, ''); + + if (tag.tagName) { + tag.text = `${tagName} ${tag.text}`; + } else { + tag.tagName = tagName; + } } - if (match[3]) { - tag.tagName = match[3] + + if (tagType) { + tagType = tagType.replace(/^ /u, ''); + + if (tag.tagType) { + tag.text = `${tagType} ${tag.text}`; + } else { + tag.tagType = tagType; + } } - } - if (tag.text.startsWith(lineBreak)) { - tag.text = tag.text.substr(lineBreak.length); + if (tagKind) { + if (tag.tagKind) { + tag.text = `${tagKind} ${tag.text}`; + } else { + tag.tagKind = tagKind; + } + } } return tag; @@ -75,6 +102,27 @@ export class SourceDoc extends SourceLine implements SourceToken { } + public static extractCommentLines( + text: string + ): Array { + let lines = text.split(U.LINE_BREAKS); + + if (lines.length === 1) { + // remove /** and */ + return [ text.substr(4, text.length - 7) ]; + } + + // remove /**\n and \n*/ + lines = lines.slice(1, -1); + + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + lines[i] = lines[i].replace(/^\s+\*\s?/u, ''); + } + + return lines; + } + + /* * * * Constructor @@ -95,32 +143,50 @@ export class SourceDoc extends SourceLine implements SourceToken { this.tokens = []; const tags = this.tokens, - lines = text - .substr(3, text.length - 5) - .split(U.lineBreaks) - .slice(1, -1); - - let line: string, - tag: SourceDocTag = { - kind: TS.SyntaxKind.JSDocTag, - tagKind: 'description', - text: '' - }; + lines = SourceDoc.extractCommentLines(text); - tags.push(tag); + // leading text without tag is @description: + let tag: SourceDocTag = { + kind: TS.SyntaxKind.JSDocTag, + tagKind: 'description', + text: '' + }; - for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { - line = lines[i].substr(indent).replace(/^ \* ?/gu, ''); + tags.push(tag); + for (const line of lines) { if (!line && tags.length > 1) { tags.push({ - kind: TS.SyntaxKind.WhitespaceTrivia, + kind: TS.SyntaxKind.NewLineTrivia, tagKind: '', text: '' }); } else if (line.startsWith('@')) { - if (!i) { - tags.pop(); // remove empty description + + if (tags.length === 1) { + if (!tag.text) { + // remove empty initial description + tags.pop(); + } else { + // add trailing new lines as tokens + const trail = tag.text.match(new RegExp( + `(?:${U.LINE_BREAKS.source})+$`, + 'su' + )) || ['']; + + for ( + let i = 1, + iEnd = trail[0].split(U.LINE_BREAKS).length; + i < iEnd; + ++i + ) { + tags.push({ + kind: TS.SyntaxKind.NewLineTrivia, + tagKind: '', + text: '' + }); + } + } } tag = { @@ -136,7 +202,7 @@ export class SourceDoc extends SourceLine implements SourceToken { } for (const tag of tags) { - SourceDoc.decorateTag(tag, lineBreak); + SourceDoc.decorateTag(tag); } } @@ -192,9 +258,7 @@ export class SourceDoc extends SourceLine implements SourceToken { firstTag && firstTag.tagKind === 'description' ) { - part2 = U.trimBreaks(firstTag.text); - - lines.push(U.indent(indent, ' * ', part2, maximalLength)); + lines.push(U.indent(indent, ' * ', firstTag.text, maximalLength)); tags.shift(); } @@ -206,7 +270,7 @@ export class SourceDoc extends SourceLine implements SourceToken { } part1 = `@${tag.tagKind}`; - part2 = U.trimBreaks(tag.text); + part2 = tag.text; if (tag.tagKind === 'example') { lines.push(U.pad(indent, ` * ${part1}`)); @@ -222,24 +286,16 @@ export class SourceDoc extends SourceLine implements SourceToken { part1 += ` ${tag.tagName}`; } - if ( - part2 && - ! tag.tagType && - ! tag.tagName && - tag.text.split(U.lineBreaks).length === 1 - ) { + if (part2 && part2.trim().split(/\s/g).length > 1) { + lines.push(U.pad(indent, ` * ${part1}`)); lines.push(U.indent( indent, ' * ', - `${part1} ${part2}`.trimRight(), + U.trimAll(part2), maximalLength )); } else { - lines.push(U.pad(indent, ` * ${part1}`)); - - if (part2) { - lines.push(U.indent(indent, ' * ', part2, maximalLength)); - } + lines.push(U.pad(indent, ` * ${part1} ${part2}`.trimRight())); } } diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 90dfe94..82ec27f 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -121,7 +121,7 @@ export class SourceLine { token.kind === TS.SyntaxKind.JSDocComment || token.kind === TS.SyntaxKind.MultiLineCommentTrivia ) { - position.line += tokenText.split(U.lineBreaks).length - 1; + position.line += tokenText.split(U.LINE_BREAKS).length - 1; } position.start += tokenText.length; @@ -134,7 +134,7 @@ export class SourceLine { public getWrappedLines(): Array { - return this.toString().split(U.lineBreaks); + return this.toString().split(U.LINE_BREAKS); } diff --git a/sources/SourceTree.ts b/sources/SourceTree.ts index c4abe69..c91cfe5 100644 --- a/sources/SourceTree.ts +++ b/sources/SourceTree.ts @@ -16,7 +16,6 @@ import * as TS from 'typescript'; import * as SP from './SourceParser'; import SourceNode from './SourceNode'; -import SourceToken from './SourceToken'; /* * diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 5b58599..9d76ea8 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -89,10 +89,10 @@ export type UnknownObject = Record; * */ -export const lineBreaks = /\r\n|\r|\n/gu; +export const LINE_BREAKS = /\r\n|\r|\n/gu; -export const paragraph = new RegExp(`${lineBreaks.source}{2,}`, 'gu'); +export const PARAGRAPHS = new RegExp(`${LINE_BREAKS.source}{2,}`, 'gu'); /* * @@ -122,14 +122,14 @@ export function extractTypes( export function extractFirstLine( text: string ): string { - return text.split(lineBreaks)[0]; + return text.split(LINE_BREAKS)[0]; } export function extractLastLine( text: string ): string { - const lines = text.split(lineBreaks); + const lines = text.split(LINE_BREAKS); return lines[lines.length-1]; } @@ -137,7 +137,7 @@ export function extractLastLine( export function detectLineBreak( text: string ): (string|undefined) { - return text.match(new RegExp(lineBreaks.source, 'u'))?.[0] + return text.match(new RegExp(LINE_BREAKS.source, 'u'))?.[0] } @@ -160,59 +160,65 @@ export function indent ( text: string, wrap?: number ): string { - const lb = detectLineBreak(text); + + const lb = detectLineBreak(text) || '\n'; prefix = pad(indent, prefix); if (!wrap) { - return prefix + text.replace(lineBreaks, `${lb}${prefix}`); + return prefix + text.replace(LINE_BREAKS, `${lb}${prefix}`); } const fragments = text - .replace(paragraph, ' \x00 ') // paragraphs - .replace(lineBreaks, ' \x05 ') // single break + .replace(PARAGRAPHS, ' \x00 ') // paragraphs + .replace(LINE_BREAKS, ' \x05 ') // single break + .trim() .split(/\s/gmu); let codeBlock = false, + newLine = true, line = prefix, paddedStr = ''; for (const fragment of fragments) { if (fragment === '\x00') { + newLine = true; paddedStr += line.trimRight() + lb + prefix.trimRight() + lb; - line = prefix; continue; } if (fragment === '\x05') { if (codeBlock) { - paddedStr += line.trimRight() + lb; - line = prefix; + newLine = true; + paddedStr += line + lb; } continue; } if (fragment.startsWith('```')) { codeBlock = !codeBlock; - if (line !== prefix) { + + if (!newLine) { + newLine = true; paddedStr += line.trimRight() + lb; } - line = prefix + fragment; - continue; } - if (!codeBlock && line.length + 1 + fragment.length > wrap) { + if (!codeBlock && !newLine && line.length + 1 + fragment.length > wrap) { + newLine = true; paddedStr += line.trimRight() + lb; + } + + if (newLine) { + newLine = false; line = prefix + fragment; - } else if (line === prefix) { - line += (fragment || ' '); } else { line += ' ' + fragment; } } - return (line === prefix ? paddedStr : paddedStr + line.trimRight()); + return newLine ? paddedStr : paddedStr + line.trimRight(); } @@ -268,18 +274,14 @@ export function isNodeStatement ( export function pad(indent: number, suffix: string = ''): string { - if (indent > 0) { - return ' '.repeat(indent) + suffix; - } - - return suffix; + return ' '.repeat(indent) + suffix; } export function removeBreaks ( text: string ): string { - return text.replace(lineBreaks, ' ').trim(); + return text.replace(LINE_BREAKS, ' ').trim(); } export function trimAll ( @@ -290,7 +292,7 @@ export function trimAll ( if (keepParagraphs) { return text - .replace(paragraph, ' \x00 ') + .replace(PARAGRAPHS, ' \x00 ') .replace(/\s+/gu, ' ') .trim() .replace(/ ?\x00 ?/, `${lb}${lb}`); From c1fa4feaacdfe5c59edb84b23a83bee162bef793 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Wed, 4 May 2022 13:32:25 +0200 Subject: [PATCH 35/54] Fixed tag decoration. --- sources/SourceDoc.ts | 104 +++++++++++++++++++++++++------------------ sources/Utilities.ts | 21 +++++++-- 2 files changed, 78 insertions(+), 47 deletions(-) diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index f4156ac..4bb9520 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -20,6 +20,17 @@ import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; +/* * + * + * Constants + * + * */ + + +// eslint-disable-next-line max-len +const tagPattern = /^@(\w+)([ \t]+(?:true|false|[\d./]+|'[^'\r\n]+'|\{[^\}\s]+\}))?([ \t]+[\w\-./]+)?([\s\S]*)?$/u; + + /* * * * Class @@ -41,53 +52,32 @@ export class SourceDoc extends SourceLine implements SourceToken { tag: SourceDocTag ): SourceDocTag { - const match = tag.text.match( - /^@(\w+)(\s+\{[\w.()<>]+\})?(\s+[^\[\]\s]+|\[[^\[\]\s]+\])?(.*)$/su - ); + const match = tag.text.match(tagPattern); if (match) { const { 1: tagKind, - 4: tagText - } = match; - - let { 2: tagType, - 3: tagName + 3: tagName, + 4: tagText } = match; - if (tagText[0] === ' ') { - tag.text = tagText.substr(1); + if (tagText) { + tag.text = tagText.trimLeft(); } else { - tag.text = tagText; + tag.text = ''; } - if (tagName) { - tagName = tagName.replace(/^ /u, ''); - - if (tag.tagName) { - tag.text = `${tagName} ${tag.text}`; - } else { - tag.tagName = tagName; - } + if (!tag.tagName && tagName) { + tag.tagName = tagName.replace(/^[ \t]/u, ''); } - if (tagType) { - tagType = tagType.replace(/^ /u, ''); - - if (tag.tagType) { - tag.text = `${tagType} ${tag.text}`; - } else { - tag.tagType = tagType; - } + if (!tag.tagType && tagType) { + tag.tagType = tagType.replace(/^[ \t]/u, ''); } - if (tagKind) { - if (tag.tagKind) { - tag.text = `${tagKind} ${tag.text}`; - } else { - tag.tagKind = tagKind; - } + if (!tag.tagKind && tagKind) { + tag.tagKind = tagKind; } } @@ -252,7 +242,8 @@ export class SourceDoc extends SourceLine implements SourceToken { lines: Array = ['/**']; let part1: string, - part2: string; + part2: string, + padded: string; if ( firstTag && @@ -274,7 +265,7 @@ export class SourceDoc extends SourceLine implements SourceToken { if (tag.tagKind === 'example') { lines.push(U.pad(indent, ` * ${part1}`)); - lines.push(U.indent(indent, ' * ', part2)); + lines.push(U.pad(indent, ` * ${U.trimBreaks(part2)}`)); continue; } @@ -286,16 +277,41 @@ export class SourceDoc extends SourceLine implements SourceToken { part1 += ` ${tag.tagName}`; } - if (part2 && part2.trim().split(/\s/g).length > 1) { - lines.push(U.pad(indent, ` * ${part1}`)); - lines.push(U.indent( - indent, - ' * ', - U.trimAll(part2), - maximalLength - )); + padded = U.pad(indent, ` * ${part1} ${part2}`.trimRight()); + + if ( + padded.length <= maximalLength && + padded.trim().split(/\s/gu).length <= 3 + ) { + lines.push(padded); } else { - lines.push(U.pad(indent, ` * ${part1} ${part2}`.trimRight())); + padded = U.pad(indent, ` * ${part1}`); + + if (padded.length <= maximalLength) { + lines.push(padded); + } else { + lines.push(U.pad(indent, ` * ${U.trimAll(part1)}`)); + } + + if (part2) { + padded = ' * '; + + if ( + tag.tagType && + tag.tagType[0] !== ' ' && + tag.tagName && + tag.tagName[0] !== ' ' + ) { + padded += U.pad(tag.tagKind.length + 2); + } + + lines.push(U.indent( + indent, + padded, + U.trimAll(part2), + maximalLength + )); + } } } diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 9d76ea8..72ce056 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -92,7 +92,10 @@ export type UnknownObject = Record; export const LINE_BREAKS = /\r\n|\r|\n/gu; -export const PARAGRAPHS = new RegExp(`${LINE_BREAKS.source}{2,}`, 'gu'); +export const PARAGRAPHS = new RegExp(`(?:${LINE_BREAKS.source}){2,2}`, 'gu'); + + +export const SPACES = /[ \t]/gu; /* * @@ -173,17 +176,19 @@ export function indent ( .replace(PARAGRAPHS, ' \x00 ') // paragraphs .replace(LINE_BREAKS, ' \x05 ') // single break .trim() - .split(/\s/gmu); + .split(SPACES); let codeBlock = false, newLine = true, line = prefix, + newParagraph = false, paddedStr = ''; for (const fragment of fragments) { if (fragment === '\x00') { newLine = true; + newParagraph = true; paddedStr += line.trimRight() + lb + prefix.trimRight() + lb; continue; } @@ -192,6 +197,9 @@ export function indent ( if (codeBlock) { newLine = true; paddedStr += line + lb; + } else if (newParagraph) { + newLine = true; + paddedStr += prefix.trimRight() + lb; } continue; } @@ -216,6 +224,10 @@ export function indent ( } else { line += ' ' + fragment; } + + if (fragment && newParagraph) { + newParagraph = false; + } } return newLine ? paddedStr : paddedStr + line.trimRight(); @@ -305,5 +317,8 @@ export function trimAll ( export function trimBreaks ( text: string ): string { - return text.replace(/^\s+|\s+$/gu, '') + return text.replace(new RegExp( + `^(?:${LINE_BREAKS.source}){1,}|(?:${LINE_BREAKS.source}){1,}$`, + 'gu' + ), ''); } From a9f0dd3beb89b8e10738db398380baf93f5bc834 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Wed, 4 May 2022 19:40:40 +0200 Subject: [PATCH 36/54] Added reports stack with finalized fix. --- sources/RuleContext.ts | 47 +++++++++++++++++++++++++++++++++++++++- sources/SourceComment.ts | 47 ++++++++++++++++++++++++++++++++++++++++ sources/Utilities.ts | 8 +++++-- 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index da555c2..5021b76 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -92,7 +92,7 @@ export class RuleContext { ...this.settings, ...(esLintContext.options[1] || {}) }; - + this.preparedReports = []; this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()); } @@ -119,6 +119,9 @@ export class RuleContext { public options: T; + public readonly preparedReports: Array; + + public settings: ESLint.Rule.RuleContext['settings']; @@ -156,6 +159,22 @@ export class RuleContext { * */ + public prepareReport( + position: SourcePosition, + message: string, + fix?: ESLint.Rule.ReportFixer + ): void { + this.preparedReports.push({ + fix, + loc: { + // ESLint needs column zero-based: + column: position.column - 1, + line: position.line + }, + message + }); + } + public report ( position: SourcePosition, message: string, @@ -173,6 +192,32 @@ export class RuleContext { } + public sendReports( + finalFix?: ESLint.Rule.ReportFixer + ): void { + const esLintContext = this.esLintContext, + reports = this.preparedReports.splice(0); + + if (finalFix) { + for (let i = 0, iEnd = reports.length - 1, report: ESLint.Rule.ReportDescriptor; i <= iEnd; ++i) { + report = reports[i]; + + if (i === iEnd) { + report.fix = finalFix; + } else { + report.fix = () => null; + } + + esLintContext.report(report); + } + } else { + for (const report of reports) { + esLintContext.report(report); + } + } + } + + } diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index bde6b28..c80ab1d 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -19,6 +19,16 @@ import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; +/* * + * + * Constants + * + * */ + + +const starPattern = /^[ \t]+\*[ \t]?/u + + /* * * * Class @@ -29,6 +39,43 @@ import SourceToken from './SourceToken'; export class SourceComment extends SourceLine implements SourceToken { + /* * + * + * Static Functions + * + * */ + + + public static extractCommentLines( + text: string, + indent: number = 0 + ): Array { + let lines = text.split(U.LINE_BREAKS); + + if (lines.length === 1) { + // remove /** and */ + return [ text.substr(4, text.length - 7) ]; + } + + // remove /**\n and \n*/ + lines = lines.slice(1, -1); + + for (let i = 0, iEnd = lines.length, line: string; i < iEnd; ++i) { + line = lines[i]; + + if (line.match(starPattern)) { + // remove * + lines[i] = line.replace(starPattern, ''); + } else if (indent) { + // remove indent + lines[i] = line.substr(indent); + } + } + + return lines; + } + + /* * * * Constructor diff --git a/sources/Utilities.ts b/sources/Utilities.ts index 72ce056..4a0d31e 100644 --- a/sources/Utilities.ts +++ b/sources/Utilities.ts @@ -196,7 +196,7 @@ export function indent ( if (fragment === '\x05') { if (codeBlock) { newLine = true; - paddedStr += line + lb; + paddedStr += line.trimRight() + lb; } else if (newParagraph) { newLine = true; paddedStr += prefix.trimRight() + lb; @@ -213,7 +213,11 @@ export function indent ( } } - if (!codeBlock && !newLine && line.length + 1 + fragment.length > wrap) { + if ( + !codeBlock && + !newLine && + line.trimRight().length + 1 + fragment.length > wrap + ) { newLine = true; paddedStr += line.trimRight() + lb; } From 22a71a22c044734e8f209dfc1944bc38f7786636 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 5 May 2022 09:57:50 +0200 Subject: [PATCH 37/54] Improved handling of custom tags. --- sources/SourceDoc.ts | 99 +++++++++++++++------------------- sources/SourceDocTag.d.ts | 2 +- sources/rules/pretty-length.ts | 37 +++++++++---- 3 files changed, 70 insertions(+), 68 deletions(-) diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 4bb9520..9fc649a 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -15,6 +15,7 @@ import * as TS from 'typescript'; import * as U from './Utilities'; +import SourceComment from './SourceComment'; import SourceDocTag from './SourceDocTag'; import SourceLine from './SourceLine'; import SourceToken from './SourceToken'; @@ -28,7 +29,7 @@ import SourceToken from './SourceToken'; // eslint-disable-next-line max-len -const tagPattern = /^@(\w+)([ \t]+(?:true|false|[\d./]+|'[^'\r\n]+'|\{[^\}\s]+\}))?([ \t]+[\w\-./]+)?([\s\S]*)?$/u; +const tagPattern = /^@(\w+)([ \t]+\{[^\}\s]+\})?([ \t]+\S+)?(\s[\s\S]*)?$/u; /* * @@ -58,18 +59,22 @@ export class SourceDoc extends SourceLine implements SourceToken { const { 1: tagKind, 2: tagType, - 3: tagName, + 3: tagArgument, 4: tagText } = match; if (tagText) { - tag.text = tagText.trimLeft(); + tag.text = U.trimBreaks(tagText).trim(); } else { tag.text = ''; } - if (!tag.tagName && tagName) { - tag.tagName = tagName.replace(/^[ \t]/u, ''); + if (!tag.tagArgument && tagArgument) { + if (!tagText || tagText.match(/^[\r\n]/u)) { + tag.tagArgument = tagArgument.replace(/^[ \t]/u, ''); + } else { + tag.text = `${tagArgument} ${tag.text}`.trimLeft() + } } if (!tag.tagType && tagType) { @@ -92,27 +97,6 @@ export class SourceDoc extends SourceLine implements SourceToken { } - public static extractCommentLines( - text: string - ): Array { - let lines = text.split(U.LINE_BREAKS); - - if (lines.length === 1) { - // remove /** and */ - return [ text.substr(4, text.length - 7) ]; - } - - // remove /**\n and \n*/ - lines = lines.slice(1, -1); - - for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { - lines[i] = lines[i].replace(/^\s+\*\s?/u, ''); - } - - return lines; - } - - /* * * * Constructor @@ -133,7 +117,7 @@ export class SourceDoc extends SourceLine implements SourceToken { this.tokens = []; const tags = this.tokens, - lines = SourceDoc.extractCommentLines(text); + lines = SourceComment.extractCommentLines(text); // leading text without tag is @description: let tag: SourceDocTag = { @@ -269,49 +253,50 @@ export class SourceDoc extends SourceLine implements SourceToken { continue; } - if (tag.tagType) { + if (tag.tagType && tag.tagArgument) { + part1 = `${part1} ${tag.tagType} ${tag.tagArgument}`.trim(); + } else if (tag.tagType) { part1 += ` ${tag.tagType}`; + } else if (tag.tagArgument) { + part1 += ` ${tag.tagArgument}`; } - if (tag.tagName) { - part1 += ` ${tag.tagName}`; - } - - padded = U.pad(indent, ` * ${part1} ${part2}`.trimRight()); - if ( - padded.length <= maximalLength && - padded.trim().split(/\s/gu).length <= 3 + (!part2 && tag.tagType && tag.tagArgument) || + !(part2 && tag.tagType && tag.tagArgument) ) { - lines.push(padded); - } else { - padded = U.pad(indent, ` * ${part1}`); + padded = U.pad(indent, ` * ${part1} ${part2}`.trimRight()); + // test for one line style if (padded.length <= maximalLength) { lines.push(padded); - } else { - lines.push(U.pad(indent, ` * ${U.trimAll(part1)}`)); + continue; } + } - if (part2) { - padded = ' * '; + padded = U.pad(indent, ` * ${part1}`); - if ( - tag.tagType && - tag.tagType[0] !== ' ' && - tag.tagName && - tag.tagName[0] !== ' ' - ) { - padded += U.pad(tag.tagKind.length + 2); - } + // test for spaced style + if (padded.length <= maximalLength) { + lines.push(padded); + } else { + lines.push(U.pad(indent, ` * ${U.trimAll(part1)}`)); + } + + if (part2) { + padded = ' * '; - lines.push(U.indent( - indent, - padded, - U.trimAll(part2), - maximalLength - )); + // extra indent for @param etc + if ( + tag.tagArgument && + tag.tagArgument[0] !== ' ' + ) { + padded += U.pad(tag.tagKind.length + 2); } + + lines.push( + U.indent(indent, padded, U.trimAll(part2), maximalLength) + ); } } diff --git a/sources/SourceDocTag.d.ts b/sources/SourceDocTag.d.ts index b6c404b..7789a7d 100644 --- a/sources/SourceDocTag.d.ts +++ b/sources/SourceDocTag.d.ts @@ -17,8 +17,8 @@ import type SourceToken from './SourceToken'; export interface SourceDocTag extends SourceToken { + tagArgument?: string; tagKind: string; - tagName?: string; tagType?: string; } diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index 97b3a3d..e06d91f 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -17,6 +17,7 @@ import * as ESLint from 'eslint'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; +import SourceLine from '../SourceLine'; /* * @@ -69,12 +70,13 @@ const optionsSchema = { function createFixer ( - context: PrettyLengthContext + context: PrettyLengthContext, + fixLines: Array ): ESLint.Rule.ReportFixer { return (): ESLint.Rule.Fix => { const code = context.sourceCode, fix: Array = [], - range: ESLint.AST.Range = [ 0, code.raw.length ], + range: ESLint.AST.Range = [ 0, code.raw.length + 256 ], lines = code.lines, { ignorePattern, @@ -82,16 +84,22 @@ function createFixer ( } = context.options, ignoreRegExp = (ignorePattern ? new RegExp(ignorePattern) : void 0); + let text: string; + for (const l of lines) { + text = l.toString(); if ( - ignoreRegExp && - ignoreRegExp.test(l.toString()) + fixLines.includes(l) && + ( + !ignoreRegExp || + !ignoreRegExp.test(text) + ) ) { - continue; + fix.push(l.toString(maximalLength)); + } else { + fix.push(text); } - - fix.push(l.toString(maximalLength)); } return { range, text: fix.join(code.lineBreak) }; @@ -109,6 +117,7 @@ function lint ( } = context.options, ignoreRegExp = (ignorePattern ? new RegExp(ignorePattern) : void 0), lines = code.lines, + fixLines: Array = [], message = messageTemplate.replace('{0}', `${maximalLength}`); let maximalLineLength: number; @@ -130,6 +139,11 @@ function lint ( if (position) { const wrappedLines = line.getWrappedLines(); + if (wrappedLines.length === 1) { + // only lines with multiline comments for now + continue; + } + let lineIndex = 0; for (const wrappedLine of wrappedLines) { @@ -138,23 +152,26 @@ function lint ( wrappedLine.length > maximalLength && wrappedLine.split(/\s+/g).length > 1 ) { - context.report( + context.prepareReport( { column: maximalLength + 1, end: position.end, line: position.line + lineIndex, start: position.start }, - message + ` ${maximalLineLength}`, - createFixer(context) + message + ` ${maximalLineLength}` ); } ++lineIndex; } + + fixLines.push(line); } } } + + context.sendReports(createFixer(context, fixLines)); } From a4f2a3bfc222acefb91c158fe41de1cbe6a187fc Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 5 May 2022 11:50:55 +0200 Subject: [PATCH 38/54] Updated transpiled rules. --- lib/RuleContext.js | 77 ++++++++---- lib/SourceCode.js | 84 ++++++++++--- lib/SourceComment.js | 120 +++++++++++++++++++ lib/SourceDoc.js | 201 ++++++++++++++++++++++++++++++++ lib/SourceLine.js | 106 ++++++++++++++--- lib/SourceNode.js | 60 ++++++++++ lib/SourceParser.js | 159 +++++++++++++++++++++++++ lib/SourceTree.js | 75 ++++++++++++ lib/Utilities.js | 164 +++++++++++++++++++------- lib/rules/debug.js | 38 ++++++ lib/rules/generic-array-type.js | 57 +++++++++ lib/rules/pretty-length.js | 81 +++++++------ package-lock.json | 12 +- package.json | 7 +- sources/.eslintrc | 6 +- 15 files changed, 1110 insertions(+), 137 deletions(-) create mode 100644 lib/SourceComment.js create mode 100644 lib/SourceDoc.js create mode 100644 lib/SourceNode.js create mode 100644 lib/SourceParser.js create mode 100644 lib/SourceTree.js create mode 100644 lib/rules/debug.js create mode 100644 lib/rules/generic-array-type.js diff --git a/lib/RuleContext.js b/lib/RuleContext.js index 1ceba45..5d4a4ab 100644 --- a/lib/RuleContext.js +++ b/lib/RuleContext.js @@ -7,6 +7,7 @@ exports.RuleContext = void 0; const FS = require("fs"); const Path = require("path"); const SourceCode_1 = require("./SourceCode"); +const SourceTree_1 = require("./SourceTree"); /* * * * Class @@ -18,26 +19,23 @@ class RuleContext { * Constructor * * */ - constructor(esLintContext, ruleOptionsDefault, fixFunction) { - this.changes = []; + constructor(esLintContext, ruleOptionsDefault) { this.cwd = esLintContext.getCwd(); this.esLintContext = esLintContext; - this.fixes = []; this.settings = ((esLintContext.settings || {}).highcharts || {}); this.options = Object.assign(Object.assign(Object.assign({}, ruleOptionsDefault), this.settings), (esLintContext.options[1] || {})); - this.fixFunction = fixFunction; - this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()), - this.sourceCode = new SourceCode_1.default(this.sourcePath, FS.readFileSync(this.sourcePath).toString()); + this.preparedReports = []; + this.sourcePath = Path.relative(this.cwd, esLintContext.getFilename()); } /* * * * Static Properties * * */ - static setupRuleExport(ruleType, ruleOptionsSchema, ruleOptionsDefault, lintFunction, fixFunction) { + static setupRuleExport(ruleType, ruleOptionsSchema, ruleOptionsDefault, lintFunction, reportWithFix) { return { meta: { - fixable: fixFunction ? 'code' : void 0, + fixable: reportWithFix ? 'code' : void 0, schema: [{ additionalProperties: false, properties: ruleOptionsSchema, @@ -46,31 +44,68 @@ class RuleContext { type: ruleType }, create: (esLintRuleContext) => ({ - Program: () => lintFunction(new RuleContext(esLintRuleContext, ruleOptionsDefault, fixFunction)) + Program: () => lintFunction(new RuleContext(esLintRuleContext, ruleOptionsDefault)) }) }; } + get sourceCode() { + if (!this._sourceCode) { + this._sourceCode = new SourceCode_1.default(this.sourcePath, FS.readFileSync(this.sourcePath).toString()); + } + return this._sourceCode; + } + get sourceTree() { + if (!this._sourceTree) { + this._sourceTree = new SourceTree_1.default(this.sourcePath, FS.readFileSync(this.sourcePath).toString()); + } + return this._sourceTree; + } /* * * * Functions * * */ - report(line, column, message) { - const report = { + prepareReport(position, message, fix) { + this.preparedReports.push({ + fix, loc: { - column, - line + // ESLint needs column zero-based: + column: position.column - 1, + line: position.line }, message - }; - if (this.fixFunction) { - const fixFunction = this.fixFunction; - report.fix = () => { - fixFunction(this); - return this.fixes.splice(0, this.fixes.length); - }; + }); + } + report(position, message, fix) { + this.esLintContext.report({ + fix, + loc: { + // ESLint needs column zero-based: + column: position.column - 1, + line: position.line + }, + message + }); + } + sendReports(finalFix) { + const esLintContext = this.esLintContext, reports = this.preparedReports.splice(0); + if (finalFix) { + for (let i = 0, iEnd = reports.length - 1, report; i <= iEnd; ++i) { + report = reports[i]; + if (i === iEnd) { + report.fix = finalFix; + } + else { + report.fix = () => null; + } + esLintContext.report(report); + } + } + else { + for (const report of reports) { + esLintContext.report(report); + } } - this.esLintContext.report(report); } } exports.RuleContext = RuleContext; diff --git a/lib/SourceCode.js b/lib/SourceCode.js index 9acd47b..5afff6a 100644 --- a/lib/SourceCode.js +++ b/lib/SourceCode.js @@ -10,6 +10,9 @@ exports.SourceCode = void 0; * * */ const TS = require("typescript"); +const U = require("./Utilities"); +const SourceComment_1 = require("./SourceComment"); +const SourceDoc_1 = require("./SourceDoc"); const SourceLine_1 = require("./SourceLine"); /* * * @@ -24,7 +27,9 @@ class SourceCode { * */ constructor(fileName, sourceCode) { this.fileName = fileName; + this.lineBreak = U.detectLineBreak(sourceCode) || '\n'; this.lines = []; + this.raw = sourceCode; this.parse(sourceCode); } /* * @@ -32,8 +37,52 @@ class SourceCode { * Functions * * */ + getLinePosition(line) { + const lines = this.lines, lineIndex = lines.indexOf(line), position = { + column: 1, + end: 0, + line: 1, + start: 0 + }; + if (lineIndex < 0) { + return null; + } + for (let i = 0, tokens, tokenLength, tokenText; i < lineIndex; ++i) { + tokens = lines[i].tokens; + for (const token of tokens) { + tokenText = token.text; + if (token.kind === TS.SyntaxKind.JSDocComment || + token.kind === TS.SyntaxKind.MultiLineCommentTrivia) { + tokenLength = tokenText.split(U.LINE_BREAKS).length; + if (tokenLength > 1) { + position.line += tokenLength - 1; + } + } + position.start += tokenText.length; + } + position.line += 1; + position.start += 1; // line break + } + position.end = position.start + line.toString().length; + return position; + } + /** + * Returns the token position relative to the code. + */ + getTokenPosition(line, token) { + const linePosition = this.getLinePosition(line), tokenPosition = line.getTokenPosition(token); + if (!linePosition || !tokenPosition) { + return null; + } + return { + column: tokenPosition.column, + end: linePosition.start + tokenPosition.end, + line: linePosition.line + tokenPosition.line - 1, + start: linePosition.start + tokenPosition.start + }; + } parse(sourceCode, replace = false) { - const lines = this.lines; + const lineBreak = this.lineBreak, lines = this.lines; if (replace) { lines.length = 0; } @@ -41,29 +90,38 @@ class SourceCode { return; } const scanner = TS.createScanner(TS.ScriptTarget.Latest, false); - let kind, line = new SourceLine_1.default(), text; + let indent, kind, line = new SourceLine_1.default(lineBreak), text, token; scanner.setText(sourceCode); do { kind = scanner.scan(); text = scanner.getTokenText(); - line.tokens.push({ - kind, - text - }); if (kind === TS.SyntaxKind.NewLineTrivia || kind === TS.SyntaxKind.EndOfFileToken) { lines.push(line); - line = new SourceLine_1.default(); + line = new SourceLine_1.default(lineBreak); + continue; + } + if (kind === TS.SyntaxKind.MultiLineCommentTrivia) { + indent = Math.floor(line.getIndent() / 2) * 2; + if (SourceDoc_1.default.isSourceDoc(text)) { + token = new SourceDoc_1.default(text, lineBreak, indent); + } + else { + token = new SourceComment_1.default(text, lineBreak, indent); + } + } + else { + token = { kind, text }; } + line.tokens.push(token); } while (kind !== TS.SyntaxKind.EndOfFileToken); } - toString() { - const lines = this.lines; - let text = ''; - for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { - text += lines[i].toString(); + toString(maximalLength) { + const lines = this.lines, strings = []; + for (const line of lines) { + strings.push(line.toString(maximalLength)); } - return text; + return strings.join(this.lineBreak); } } exports.SourceCode = SourceCode; diff --git a/lib/SourceComment.js b/lib/SourceComment.js new file mode 100644 index 0000000..f302dfa --- /dev/null +++ b/lib/SourceComment.js @@ -0,0 +1,120 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SourceComment = void 0; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const U = require("./Utilities"); +const SourceLine_1 = require("./SourceLine"); +/* * + * + * Constants + * + * */ +const starPattern = /^[ \t]+\*[ \t]?/u; +/* * + * + * Class + * + * */ +class SourceComment extends SourceLine_1.default { + /* * + * + * Constructor + * + * */ + constructor(text, lineBreak = '\n', indent = 0) { + super(lineBreak); + this.indent = indent; + this.kind = TS.SyntaxKind.MultiLineCommentTrivia; + const lines = text.split(U.LINE_BREAKS), tokens = this.tokens; + for (let i = 0, iEnd = lines.length; i < iEnd; ++i) { + tokens.push({ + kind: TS.SyntaxKind.SingleLineCommentTrivia, + text: (i ? lines[i].substr(indent) : lines[i]) + }); + } + } + /* * + * + * Static Functions + * + * */ + static extractCommentLines(text, indent = 0) { + let lines = text.split(U.LINE_BREAKS); + if (lines.length === 1) { + // remove /** and */ + return [text.substr(4, text.length - 7)]; + } + // remove /**\n and \n*/ + lines = lines.slice(1, -1); + for (let i = 0, iEnd = lines.length, line; i < iEnd; ++i) { + line = lines[i]; + if (line.match(starPattern)) { + // remove * + lines[i] = line.replace(starPattern, ''); + } + else if (indent) { + // remove indent + lines[i] = line.substr(indent); + } + } + return lines; + } + get text() { + return this.toString(); + } + /* * + * + * Functions + * + * */ + getIndent() { + return this.indent; + } + toString(maximalLength) { + const lines = []; + if (maximalLength) { + let line = '', match, words; + for (const token of this.tokens) { + words = token.text.split(' '); + line = words.shift() || ''; + for (const word of words) { + if (line.length + 1 + word.length > maximalLength) { + lines.push(line.trimRight()); + match = line.match(/^\s*\*+\s*/); + if (match && match.groups) { + line = match[0] + word; + } + else { + line = word; + } + } + else { + line += ` ${word}`; + } + } + lines.push(line.trimRight()); + } + } + else { + for (const token of this.tokens) { + lines.push(token.text); + } + } + return lines.join(this.lineBreak); + } +} +exports.SourceComment = SourceComment; +/* * + * + * Default Export + * + * */ +exports.default = SourceComment; diff --git a/lib/SourceDoc.js b/lib/SourceDoc.js new file mode 100644 index 0000000..9931e56 --- /dev/null +++ b/lib/SourceDoc.js @@ -0,0 +1,201 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SourceDoc = void 0; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const U = require("./Utilities"); +const SourceComment_1 = require("./SourceComment"); +const SourceLine_1 = require("./SourceLine"); +/* * + * + * Constants + * + * */ +// eslint-disable-next-line max-len +const tagPattern = /^@(\w+)([ \t]+\{[^\}\s]+\})?([ \t]+\S+)?(\s[\s\S]*)?$/u; +/* * + * + * Class + * + * */ +class SourceDoc extends SourceLine_1.default { + /* * + * + * Constructor + * + * */ + constructor(text, lineBreak = '\n', indent = 0) { + super(lineBreak); + this.kind = TS.SyntaxKind.JSDocComment; + this.indent = indent; + this.text = text; + this.tokens = []; + const tags = this.tokens, lines = SourceComment_1.default.extractCommentLines(text); + // leading text without tag is @description: + let tag = { + kind: TS.SyntaxKind.JSDocTag, + tagKind: 'description', + text: '' + }; + tags.push(tag); + for (const line of lines) { + if (!line && tags.length > 1) { + tags.push({ + kind: TS.SyntaxKind.NewLineTrivia, + tagKind: '', + text: '' + }); + } + else if (line.startsWith('@')) { + if (tags.length === 1) { + if (!tag.text) { + // remove empty initial description + tags.pop(); + } + else { + // add trailing new lines as tokens + const trail = tag.text.match(new RegExp(`(?:${U.LINE_BREAKS.source})+$`, 'su')) || ['']; + for (let i = 1, iEnd = trail[0].split(U.LINE_BREAKS).length; i < iEnd; ++i) { + tags.push({ + kind: TS.SyntaxKind.NewLineTrivia, + tagKind: '', + text: '' + }); + } + } + } + tag = { + kind: TS.SyntaxKind.JSDocTag, + tagKind: '', + text: line + }; + tags.push(tag); + } + else { + tag.text += lineBreak + line; + } + } + for (const tag of tags) { + SourceDoc.decorateTag(tag); + } + } + /* * + * + * Static Functions + * + * */ + static decorateTag(tag) { + const match = tag.text.match(tagPattern); + if (match) { + const { 1: tagKind, 2: tagType, 3: tagArgument, 4: tagText } = match; + if (tagText) { + tag.text = U.trimBreaks(tagText).trim(); + } + else { + tag.text = ''; + } + if (!tag.tagArgument && tagArgument) { + if (!tagText || tagText.match(/^[\r\n]/u)) { + tag.tagArgument = tagArgument.replace(/^[ \t]/u, ''); + } + else { + tag.text = `${tagArgument} ${tag.text}`.trimLeft(); + } + } + if (!tag.tagType && tagType) { + tag.tagType = tagType.replace(/^[ \t]/u, ''); + } + if (!tag.tagKind && tagKind) { + tag.tagKind = tagKind; + } + } + return tag; + } + static isSourceDoc(text) { + return /^\/\*\*\s/.test(text); + } + /* * + * + * Functions + * + * */ + getIndent() { + return this.indent; + } + toString(maximalLength) { + if (!maximalLength) { + return this.text; + } + const indent = this.indent, tags = this.tokens, firstTag = tags[0], lines = ['/**']; + let part1, part2, padded; + if (firstTag && + firstTag.tagKind === 'description') { + lines.push(U.indent(indent, ' * ', firstTag.text, maximalLength)); + tags.shift(); + } + for (const tag of tags) { + if (tag.tagKind === '') { + lines.push(U.pad(indent, ' *')); + continue; + } + part1 = `@${tag.tagKind}`; + part2 = tag.text; + if (tag.tagKind === 'example') { + lines.push(U.pad(indent, ` * ${part1}`)); + lines.push(U.pad(indent, ` * ${U.trimBreaks(part2)}`)); + continue; + } + if (tag.tagType && tag.tagArgument) { + part1 = `${part1} ${tag.tagType} ${tag.tagArgument}`.trim(); + } + else if (tag.tagType) { + part1 += ` ${tag.tagType}`; + } + else if (tag.tagArgument) { + part1 += ` ${tag.tagArgument}`; + } + if ((!part2 && tag.tagType && tag.tagArgument) || + !(part2 && tag.tagType && tag.tagArgument)) { + padded = U.pad(indent, ` * ${part1} ${part2}`.trimRight()); + // test for one line style + if (padded.length <= maximalLength) { + lines.push(padded); + continue; + } + } + padded = U.pad(indent, ` * ${part1}`); + // test for spaced style + if (padded.length <= maximalLength) { + lines.push(padded); + } + else { + lines.push(U.pad(indent, ` * ${U.trimAll(part1)}`)); + } + if (part2) { + padded = ' * '; + // extra indent for @param etc + if (tag.tagArgument && + tag.tagArgument[0] !== ' ') { + padded += U.pad(tag.tagKind.length + 2); + } + lines.push(U.indent(indent, padded, U.trimAll(part2), maximalLength)); + } + } + lines.push(U.pad(indent, ' */')); + return lines.join(this.lineBreak); + } +} +exports.SourceDoc = SourceDoc; +/* * + * + * Default Export + * + * */ +exports.default = SourceDoc; diff --git a/lib/SourceLine.js b/lib/SourceLine.js index 429f1ca..1b73266 100644 --- a/lib/SourceLine.js +++ b/lib/SourceLine.js @@ -4,18 +4,26 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceLine = void 0; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const U = require("./Utilities"); /* * * * Class * * */ class SourceLine { - constructor() { - /* * - * - * Properties - * - * */ + /* * + * + * Constructor + * + * */ + constructor(lineBreak = '\n') { + this.lineBreak = lineBreak; this.tokens = []; } /* * @@ -23,21 +31,83 @@ class SourceLine { * Functions * * */ - getLength() { - const tokens = this.tokens; - let length = 0; - for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { - length += tokens[i].text.length; + getIndent() { + const firstToken = this.tokens[0]; + if (firstToken && + firstToken.kind === TS.SyntaxKind.WhitespaceTrivia) { + return firstToken.text.length; } - return length; + return 0; } - toString() { - const tokens = this.tokens; - let text = ''; - for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { - text += tokens[i].text; + getMaximalLength() { + const lines = this.getWrappedLines(); + let lineLength, maximalLength = 0; + for (const line of lines) { + lineLength = line.length; + if (lineLength > maximalLength) { + maximalLength = lineLength; + } + } + return maximalLength; + } + /** + * Returns the token position relative to the line. + */ + getTokenPosition(token) { + const tokens = this.tokens, tokenIndex = tokens.indexOf(token), position = { + column: 1, + end: 0, + line: 1, + start: 0 + }; + if (tokenIndex < 0) { + return null; + } + for (let i = 0, tokenText; i < tokenIndex; ++i) { + tokenText = tokens[i].text; + if (token.kind === TS.SyntaxKind.JSDocComment || + token.kind === TS.SyntaxKind.MultiLineCommentTrivia) { + position.line += tokenText.split(U.LINE_BREAKS).length - 1; + } + position.start += tokenText.length; + } + position.end = token.text.length; + return position; + } + getWrappedLines() { + return this.toString().split(U.LINE_BREAKS); + } + toString(maximalLength) { + const lines = [], tokens = this.tokens; + let line = ''; + if (!tokens.length) { + return line; + } + if (maximalLength) { + let tokenText; + for (const token of tokens) { + if (token instanceof SourceLine && + token.tokens.length > 1) { + tokenText = token.toString(maximalLength); + } + else { + tokenText = token.text; + } + if ((U.extractLastLine(line) + U.extractFirstLine(tokenText)).length > maximalLength) { + lines.push(line); + line = ''; + } + line += tokenText; + } + lines.push(line); + } + else { + for (const token of tokens) { + line += token.text; + } + lines.push(line); } - return text; + return lines.join(this.lineBreak); } } exports.SourceLine = SourceLine; diff --git a/lib/SourceNode.js b/lib/SourceNode.js new file mode 100644 index 0000000..bf3d8ea --- /dev/null +++ b/lib/SourceNode.js @@ -0,0 +1,60 @@ +"use strict"; +/* * + * + * Imports + * + * */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SourceNode = void 0; +/* * + * + * Class + * + * */ +class SourceNode { + /* * + * + * Constructor + * + * */ + constructor(kind, text = '') { + this.kind = kind; + this.text = text; + } + /* * + * + * Functions + * + * */ + toArray() { + const children = this.children, parent = new SourceNode(this.kind, this.text), result = [parent]; + parent.doclet = this.doclet; + parent.types = this.types; + if (children) { + for (let i = 0, iEnd = children.length, childrensChildren; i < iEnd; ++i) { + childrensChildren = children[i].toArray(); + for (let j = 0, jEnd = childrensChildren.length; j < jEnd; ++j) { + result.push(childrensChildren[j]); + } + } + } + return result; + } + toString() { + const children = this.children; + let text = this.text; + if (children) { + for (let i = 0, iEnd = children.length; i < iEnd; ++i) { + text += children.toString(); + } + } + return text; + } +} +exports.SourceNode = SourceNode; +/* * + * + * Default Export + * + * */ +exports.default = SourceNode; diff --git a/lib/SourceParser.js b/lib/SourceParser.js new file mode 100644 index 0000000..149b5bf --- /dev/null +++ b/lib/SourceParser.js @@ -0,0 +1,159 @@ +"use strict"; +/* * + * + * Imports + * + * */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseChildren = exports.parse = void 0; +const TS = require("typescript"); +const U = require("./Utilities"); +const SourceNode_1 = require("./SourceNode"); +/* * + * + * Functions + * + * */ +function ignoreChildrenOf(tsNode) { + return (TS.isExpressionStatement(tsNode) || + TS.isImportDeclaration(tsNode) || + TS.isPropertySignature(tsNode) || + TS.isReturnStatement(tsNode) || + TS.isVariableDeclaration(tsNode)); +} +function joinNodeArray(tsSourceFile, tsNodes, separator = ',') { + const nodes = []; + for (const tsNode of tsNodes) { + nodes.push(tsNode.getText(tsSourceFile)); + } + return nodes.join(separator); +} +function parse(tsSourceFile, tsNode) { + let sourceNode; + if (TS.isForStatement(tsNode)) { + sourceNode = parseFor(tsSourceFile, tsNode); + } + else if (TS.isFunctionDeclaration(tsNode)) { + sourceNode = parseFunction(tsSourceFile, tsNode); + } + else if (TS.isIfStatement(tsNode)) { + sourceNode = parseIf(tsSourceFile, tsNode); + } + else if (TS.isInterfaceDeclaration(tsNode)) { + sourceNode = parseInterface(tsSourceFile, tsNode); + } + else if (TS.isModuleDeclaration(tsNode)) { + sourceNode = parseModule(tsSourceFile, tsNode); + } + else if (TS.isVariableStatement(tsNode)) { + sourceNode = parseVariables(tsSourceFile, tsNode); + } + else if (ignoreChildrenOf(tsNode)) { + const types = U.extractTypes(tsSourceFile, tsNode); + sourceNode = new SourceNode_1.default(tsNode.kind, tsNode.getText(tsSourceFile)); + sourceNode.types = types; + } + else { + const tsNodeChildren = tsNode.getChildren(tsSourceFile); + sourceNode = new SourceNode_1.default(tsNode.kind); + if (tsNodeChildren.length) { + sourceNode.children = parseChildren(tsSourceFile, tsNodeChildren); + } + else { + sourceNode.text = tsNode.getText(tsSourceFile); + } + } + if (U.isDocumentedNode(tsNode)) { + const doclet = joinNodeArray(tsSourceFile, tsNode.jsDoc, '\n'); + if (doclet) { + sourceNode.doclet = doclet; + } + } + return sourceNode; +} +exports.parse = parse; +function parseChildren(tsSourceFile, tsNodeChildren) { + const sourceChildren = []; + for (const tsNodeChild of tsNodeChildren) + switch (tsNodeChild.kind) { + case TS.SyntaxKind.CommaToken: + continue; + case TS.SyntaxKind.SyntaxList: + return parseChildren(tsSourceFile, tsNodeChild.getChildren(tsSourceFile)); + default: + sourceChildren.push(parse(tsSourceFile, tsNodeChild)); + continue; + } + return sourceChildren; +} +exports.parseChildren = parseChildren; +function parseFor(tsSourceFile, tsNode) { + const sourceNode = new SourceNode_1.default(tsNode.kind); + sourceNode.children = parseChildren(tsSourceFile, tsNode.statement.getChildren(tsSourceFile)); + if (tsNode.initializer) { + sourceNode.text += `${tsNode.initializer.getText(tsSourceFile)};`; + } + if (tsNode.condition) { + sourceNode.text += `${tsNode.condition.getText(tsSourceFile)};`; + } + if (tsNode.incrementor) { + sourceNode.text += `${tsNode.incrementor.getText(tsSourceFile)};`; + } + return sourceNode; +} +function parseFunction(tsSourceFile, tsNode) { + const sourceNode = new SourceNode_1.default(tsNode.kind, tsNode.name ? tsNode.name.getText(tsSourceFile) : ''); + if (tsNode.body) { + sourceNode.children = parseChildren(tsSourceFile, tsNode.body.getChildren(tsSourceFile)); + } + return sourceNode; +} +function parseIf(tsSourceFile, tsNode) { + const sourceNode = new SourceNode_1.default(tsNode.kind, tsNode.expression.getText(tsSourceFile)); + sourceNode.children = parseChildren(tsSourceFile, tsNode.thenStatement.getChildren(tsSourceFile)); + if (tsNode.elseStatement) { + const tsFirstChild = tsNode.elseStatement.getFirstToken(tsSourceFile); + if (tsFirstChild && + TS.isIfStatement(tsFirstChild) && + tsNode.elseStatement.getChildCount(tsSourceFile) === 1) { + const elseIfSourceNode = parseIf(tsSourceFile, tsFirstChild); + elseIfSourceNode.text = `else ${elseIfSourceNode.text}`; + sourceNode.children.push(elseIfSourceNode); + } + else { + const elseSourceNode = new SourceNode_1.default(tsNode.kind, 'else'); + elseSourceNode.children = parseChildren(tsSourceFile, tsNode.elseStatement.getChildren(tsSourceFile)); + sourceNode.children.push(elseSourceNode); + } + } + return sourceNode; +} +function parseInterface(tsSourceFile, tsNode) { + const sourceNode = new SourceNode_1.default(tsNode.kind, tsNode.name.getText(tsSourceFile)); + sourceNode.children = parseChildren(tsSourceFile, tsNode.members); + if (tsNode.typeParameters) { + sourceNode.text += `<${joinNodeArray(tsSourceFile, tsNode.typeParameters)}>`; + } + if (tsNode.heritageClauses) { + sourceNode.types = [ + joinNodeArray(tsSourceFile, tsNode.heritageClauses) + ]; + } + return sourceNode; +} +function parseModule(tsSourceFile, tsNode) { + const sourceNode = new SourceNode_1.default(tsNode.kind, tsNode.name.getText(tsSourceFile)); + if (tsNode.body) { + sourceNode.children = parseChildren(tsSourceFile, tsNode.body.getChildren(tsSourceFile)); + } + return sourceNode; +} +function parseVariables(tsSourceFile, tsNode) { + const tsFirstChild = tsNode.getFirstToken(tsSourceFile); + if (!tsFirstChild) { + return new SourceNode_1.default(tsNode.kind); + } + const sourceNode = new SourceNode_1.default(tsNode.declarationList.kind, tsFirstChild.getText(tsSourceFile)); + sourceNode.children = parseChildren(tsSourceFile, tsNode.declarationList.getChildren(tsSourceFile)); + return sourceNode; +} diff --git a/lib/SourceTree.js b/lib/SourceTree.js new file mode 100644 index 0000000..0a832ca --- /dev/null +++ b/lib/SourceTree.js @@ -0,0 +1,75 @@ +/** + * @author Sophie Bremer + */ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SourceTree = void 0; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const SP = require("./SourceParser"); +/* * + * + * Class + * + * */ +class SourceTree { + /* * + * + * Constructor + * + * */ + constructor(fileName, sourceCode) { + this.fileName = fileName; + this.nodes = []; + this.parse(sourceCode); + } + /* * + * + * Functions + * + * */ + parse(sourceCode, replace = false) { + const nodes = this.nodes; + if (replace) { + nodes.length = 0; + } + if (!sourceCode) { + return; + } + const tsSourceFile = TS.createSourceFile('', sourceCode, TS.ScriptTarget.Latest), roots = SP.parseChildren(tsSourceFile, tsSourceFile.getChildren()); + if (roots) { + for (let i = 0, iEnd = roots.length; i < iEnd; ++i) { + nodes.push(roots[i]); + } + } + } + toArray() { + const nodes = this.nodes, result = []; + for (let i = 0, iEnd = nodes.length, nodesChildren; i < iEnd; ++i) { + nodesChildren = nodes[i].toArray(); + for (let j = 0, jEnd = nodesChildren.length; j < jEnd; ++j) { + result.push(nodesChildren[j]); + } + } + return result; + } + toString() { + const nodes = this.nodes; + let text = ''; + for (let i = 0, iEnd = nodes.length; i < iEnd; ++i) { + text += nodes[i].toString(); + } + return text; + } +} +exports.SourceTree = SourceTree; +/* * + * + * Default Export + * + * */ +exports.default = SourceTree; diff --git a/lib/Utilities.js b/lib/Utilities.js index f7ebd8d..ddf9ceb 100644 --- a/lib/Utilities.js +++ b/lib/Utilities.js @@ -3,41 +3,50 @@ */ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.trimAll = exports.isDocumentedNode = exports.indent = exports.getJSDocs = void 0; +exports.trimBreaks = exports.trimAll = exports.removeBreaks = exports.pad = exports.isNodeStatement = exports.isNodeClass = exports.isExpressionNode = exports.isDocumentedNode = exports.indent = exports.detectLineBreak = exports.extractLastLine = exports.extractFirstLine = exports.extractTypes = exports.SPACES = exports.PARAGRAPHS = exports.LINE_BREAKS = void 0; /* * * * Imports * * */ const TS = require("typescript"); +/* * + * + * Constants + * + * */ +exports.LINE_BREAKS = /\r\n|\r|\n/gu; +exports.PARAGRAPHS = new RegExp(`(?:${exports.LINE_BREAKS.source}){2,2}`, 'gu'); +exports.SPACES = /[ \t]/gu; /* * * * Functions * * */ -function getJSDocs(sourceLine) { - const code = sourceLine.tokens - .filter(token => token.kind === TS.SyntaxKind.MultiLineCommentTrivia) - .map(token => token.text) - .join(''); - if (!code.length) { - return []; - } - const source = TS.createSourceFile('', code, TS.ScriptTarget.Latest), jsDocs = [], extract = (node) => { - if (isDocumentedNode(node)) { - const jsDoc = node.jsDoc; - for (let i = 0, iEnd = jsDoc.length; i < iEnd; ++i) { - if (!jsDocs.includes(jsDoc[i])) { - jsDocs.push(jsDoc[i]); - } - } +function extractTypes(tsSourceFile, tsNode) { + const tsChildren = tsNode.getChildren(tsSourceFile), types = []; + for (const tsChild of tsChildren) { + if (TS.isTypeReferenceNode(tsChild)) { + types.push(tsChild.getText(tsSourceFile)); } - node.getChildren(source).forEach(extract); - }; - extract(source); - return jsDocs; + } + return types; } -exports.getJSDocs = getJSDocs; +exports.extractTypes = extractTypes; +function extractFirstLine(text) { + return text.split(exports.LINE_BREAKS)[0]; +} +exports.extractFirstLine = extractFirstLine; +function extractLastLine(text) { + const lines = text.split(exports.LINE_BREAKS); + return lines[lines.length - 1]; +} +exports.extractLastLine = extractLastLine; +function detectLineBreak(text) { + var _a; + return (_a = text.match(new RegExp(exports.LINE_BREAKS.source, 'u'))) === null || _a === void 0 ? void 0 : _a[0]; +} +exports.detectLineBreak = detectLineBreak; /** * Returns a indented string, that fits into a specific width and spans over * several lines. @@ -45,49 +54,122 @@ exports.getJSDocs = getJSDocs; * @param text * The string to pad. * - * @param linePrefix + * @param indent * The prefix for each line. * * @param wrap * The maximum width of the padded string. */ -function indent(text, linePrefix = '', wrap = 80) { - const fragments = text.split(/\s/gmu); - let newLine = true, line = '', paddedStr = ''; - fragments.forEach(fragment => { - if (!newLine && fragment === '') { - paddedStr += (line.trimRight() + '\n' + - linePrefix.trimRight() + '\n'); +function indent(indent, prefix, text, wrap) { + const lb = detectLineBreak(text) || '\n'; + prefix = pad(indent, prefix); + if (!wrap) { + return prefix + text.replace(exports.LINE_BREAKS, `${lb}${prefix}`); + } + const fragments = text + .replace(exports.PARAGRAPHS, ' \x00 ') // paragraphs + .replace(exports.LINE_BREAKS, ' \x05 ') // single break + .trim() + .split(exports.SPACES); + let codeBlock = false, newLine = true, line = prefix, newParagraph = false, paddedStr = ''; + for (const fragment of fragments) { + if (fragment === '\x00') { newLine = true; - return; + newParagraph = true; + paddedStr += line.trimRight() + lb + prefix.trimRight() + lb; + continue; } - if (!newLine && line.length + fragment.length + 1 > wrap) { - paddedStr += line.trimRight() + '\n'; + if (fragment === '\x05') { + if (codeBlock) { + newLine = true; + paddedStr += line.trimRight() + lb; + } + else if (newParagraph) { + newLine = true; + paddedStr += prefix.trimRight() + lb; + } + continue; + } + if (fragment.startsWith('```')) { + codeBlock = !codeBlock; + if (!newLine) { + newLine = true; + paddedStr += line.trimRight() + lb; + } + } + if (!codeBlock && + !newLine && + line.trimRight().length + 1 + fragment.length > wrap) { newLine = true; + paddedStr += line.trimRight() + lb; } if (newLine) { - line = linePrefix + fragment; newLine = false; + line = prefix + fragment; } else { line += ' ' + fragment; } - }); - return (newLine ? paddedStr : paddedStr + line.trimRight() + '\n'); + if (fragment && newParagraph) { + newParagraph = false; + } + } + return newLine ? paddedStr : paddedStr + line.trimRight(); } exports.indent = indent; function isDocumentedNode(node) { return (typeof node.jsDoc === 'object'); } exports.isDocumentedNode = isDocumentedNode; +function isExpressionNode(node) { + return (typeof node.expression === 'object'); +} +exports.isExpressionNode = isExpressionNode; +function isNodeClass(node, nodeClass) { + const kindClass = TS.SyntaxKind[node.kind]; + return !!kindClass && kindClass.endsWith(nodeClass); +} +exports.isNodeClass = isNodeClass; +function isNodeStatement(node) { + return (TS.isBreakOrContinueStatement(node) || + TS.isDebuggerStatement(node) || + TS.isDoStatement(node) || + TS.isEmptyStatement(node) || + TS.isExpressionStatement(node) || + TS.isForInStatement(node) || + TS.isForOfStatement(node) || + TS.isForStatement(node) || + TS.isIfStatement(node) || + TS.isLabeledStatement(node) || + TS.isReturnStatement(node) || + TS.isSwitchStatement(node) || + TS.isThrowStatement(node) || + TS.isTryStatement(node) || + TS.isVariableStatement(node) || + TS.isWhileStatement(node)); +} +exports.isNodeStatement = isNodeStatement; +function pad(indent, suffix = '') { + return ' '.repeat(indent) + suffix; +} +exports.pad = pad; +function removeBreaks(text) { + return text.replace(exports.LINE_BREAKS, ' ').trim(); +} +exports.removeBreaks = removeBreaks; function trimAll(text, keepParagraphs = false) { + const lb = detectLineBreak(text) || '\n'; if (keepParagraphs) { - const fragments = text.split(/\n\s*\n/gu), trimmed = []; - for (let i = 0, iEnd = fragments.length; i < iEnd; ++i) { - trimmed.push(trimAll(fragments[i])); - } - return trimmed.join('\n\n'); + return text + .replace(exports.PARAGRAPHS, ' \x00 ') + .replace(/\s+/gu, ' ') + .trim() + .replace(/ ?\x00 ?/, `${lb}${lb}`); } return text.replace(/\s+/gu, ' ').trim(); } exports.trimAll = trimAll; +function trimBreaks(text) { + return text.replace(new RegExp(`^(?:${exports.LINE_BREAKS.source}){1,}|(?:${exports.LINE_BREAKS.source}){1,}$`, 'gu'), ''); +} +exports.trimBreaks = trimBreaks; diff --git a/lib/rules/debug.js b/lib/rules/debug.js new file mode 100644 index 0000000..ec6a688 --- /dev/null +++ b/lib/rules/debug.js @@ -0,0 +1,38 @@ +/** + * @fileoverview Debugs TypeScript tokens. + * @author Sophie Bremer + */ +'use strict'; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); +/* * + * + * Constants + * + * */ +const optionsDefaults = {}; +const optionsSchema = { + 'onlyKindOf': { + 'type': 'array' + } +}; +/* * + * + * Functions + * + * */ +function lint(context) { + const onlyKindOf = context.options.onlyKindOf, sourceTree = context.sourceTree, sourceNodes = sourceTree.toArray(); + for (const sourceNode of sourceNodes) { + if (onlyKindOf && !onlyKindOf.includes(sourceNode.kind)) { + continue; + } + console.log(sourceNode.kind, TS.SyntaxKind[sourceNode.kind], sourceNode.text, sourceNode.types, sourceNode.doclet); + } +} +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint); diff --git a/lib/rules/generic-array-type.js b/lib/rules/generic-array-type.js new file mode 100644 index 0000000..867d684 --- /dev/null +++ b/lib/rules/generic-array-type.js @@ -0,0 +1,57 @@ +/** + * @fileoverview Array types should always be written in generic syntax to avoid + * any confusion with array assignments, array indexer, or type selectors. + * @author Sophie Bremer + */ +'use strict'; +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); +/* * + * + * Constants + * + * */ +const message = [ + 'Do not use the [] shortcut for the array type.', + 'Instead write the generic Array<...> to improve readability.' +].join(' '); +const optionsDefaults = {}; +const optionsSchema = {}; +/* * + * + * Functions + * + * */ +function createFixer(position, identifierToken, openBracketToken, closeBracketToken) { + return () => { + const range = [ + position.start, + (position.start + + identifierToken.text.length + + openBracketToken.text.length + + closeBracketToken.text.length) + ], text = `Array<${identifierToken}>`; + return { range, text }; + }; +} +function lint(context) { + const code = context.sourceCode, lines = code.lines; + let tokens; + for (const line of lines) { + tokens = line.tokens; + for (let index = 0, indexEnd = tokens.length - 2, identifierToken, openBracketToken, closeBracketToken; index < indexEnd; ++index) { + identifierToken = tokens[index]; + openBracketToken = tokens[index + 1]; + closeBracketToken = tokens[index + 2]; + if (identifierToken.kind === TS.SyntaxKind.Identifier && + openBracketToken.kind === TS.SyntaxKind.OpenBracketToken && + closeBracketToken.kind === TS.SyntaxKind.CloseBracketToken) { + const position = code.getTokenPosition(line, openBracketToken); + if (position) { + context.report(position, message, createFixer(position, identifierToken, openBracketToken, closeBracketToken)); + } + } + } + } +} +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, true); diff --git a/lib/rules/pretty-length.js b/lib/rules/pretty-length.js index d11be38..0a2ae47 100644 --- a/lib/rules/pretty-length.js +++ b/lib/rules/pretty-length.js @@ -3,13 +3,6 @@ * @author Sophie Bremer */ 'use strict'; -/* * - * - * Imports - * - * */ -const FS = require("fs"); -const U = require("../Utilities"); const RuleContext_1 = require("../RuleContext"); /* * * @@ -33,38 +26,58 @@ const optionsSchema = { * Functions * * */ -function fix(context) { - const docletASTs = []; - context.sourceCode.lines.forEach(sourceLine => docletASTs.push(...U.getJSDocs(sourceLine))); - FS.writeFileSync(context.sourceCode.fileName + '.json', JSON.stringify(docletASTs, [ - 'name', - 'comment', - 'text', - 'tags', - 'kind', - 'flags', - 'modifierFlagsCache', - 'transformFlags', - 'end', - 'pos', - ], ' ')); -} -function fixJSDoc(context) { - // @todo +function createFixer(context, fixLines) { + return () => { + const code = context.sourceCode, fix = [], range = [0, code.raw.length + 256], lines = code.lines, { ignorePattern, maximalLength } = context.options, ignoreRegExp = (ignorePattern ? new RegExp(ignorePattern) : void 0); + let text; + for (const l of lines) { + text = l.toString(); + if (fixLines.includes(l) && + (!ignoreRegExp || + !ignoreRegExp.test(text))) { + fix.push(l.toString(maximalLength)); + } + else { + fix.push(text); + } + } + return { range, text: fix.join(code.lineBreak) }; + }; } function lint(context) { - const sourceLines = context.sourceCode.lines, { ignorePattern, maximalLength } = context.options, ignoreRegExp = (ignorePattern && new RegExp(ignorePattern)); - console.log('Checking', context.sourceCode.fileName); - for (let line = 0, lineEnd = sourceLines.length, sourceLine, sourceLineLength; line < lineEnd; ++line) { - sourceLine = sourceLines[line]; + const code = context.sourceCode, { ignorePattern, maximalLength } = context.options, ignoreRegExp = (ignorePattern ? new RegExp(ignorePattern) : void 0), lines = code.lines, fixLines = [], message = messageTemplate.replace('{0}', `${maximalLength}`); + let maximalLineLength; + for (const line of lines) { if (ignoreRegExp && - ignoreRegExp.test(sourceLine.toString())) { + ignoreRegExp.test(line.toString())) { continue; } - sourceLineLength = sourceLine.getLength(); - if (sourceLineLength > maximalLength) { - context.report(line, maximalLength - 1, messageTemplate.replace('{0}', `${maximalLength}`)); + maximalLineLength = line.getMaximalLength(); + if (maximalLineLength > maximalLength) { + const position = code.getLinePosition(line); + if (position) { + const wrappedLines = line.getWrappedLines(); + if (wrappedLines.length === 1) { + // only lines with multiline comments for now + continue; + } + let lineIndex = 0; + for (const wrappedLine of wrappedLines) { + if (wrappedLine.length > maximalLength && + wrappedLine.split(/\s+/g).length > 1) { + context.prepareReport({ + column: maximalLength + 1, + end: position.end, + line: position.line + lineIndex, + start: position.start + }, message + ` ${maximalLineLength}`); + } + ++lineIndex; + } + fixLines.push(line); + } } } + context.sendReports(createFixer(context, fixLines)); } -module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, fix); +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, true); diff --git a/package-lock.json b/package-lock.json index 7875b71..c8e15e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2084,9 +2084,9 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3709,9 +3709,9 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index c807e17..e78c3aa 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,11 @@ }, "main": "lib/index.js", "scripts": { - "build": "npx tsc -b sources", - "lint": "npx eslint sources", + "build": "tsc -b sources", + "build-watch": "npm run build -- --watch", + "lint": "eslint sources", "test": "mocha tests --recursive", - "watch": "npm run build -- --watch" + "test-sources": "tsc -b tests/sources && node tests/sources/index.js" }, "dependencies": { "eslint": "^7.27.0", diff --git a/sources/.eslintrc b/sources/.eslintrc index d798b8f..5ca1160 100644 --- a/sources/.eslintrc +++ b/sources/.eslintrc @@ -3,5 +3,9 @@ "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], - "parser": "@typescript-eslint/parser" + "parser": "@typescript-eslint/parser", + "rules": { + "@typescript-eslint/no-inferrable-types": 0, + "@typescript-eslint/pretty-length": 0 + } } From 26e13ae2cabc197755b0baff4459cca83ae5e49a Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 5 May 2022 20:12:10 +0200 Subject: [PATCH 39/54] Added SourceLine.getEssentialTokens. --- sources/SourceLine.ts | 19 +++++++++++++++++++ sources/rules/pretty-length.ts | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 82ec27f..ad0a083 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -64,6 +64,25 @@ export class SourceLine { * */ + public getEssentialTokens(): Array { + const essentials: Array = [], + tokens = this.tokens; + + for (const token of tokens) { + switch (token.kind) { + case TS.SyntaxKind.EndOfFileToken: + case TS.SyntaxKind.NewLineTrivia: + case TS.SyntaxKind.WhitespaceTrivia: + continue; + default: + essentials.push(token); + } + } + + return essentials; + } + + public getIndent(): number { const firstToken = this.tokens[0]; diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index e06d91f..232e5f6 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -59,7 +59,7 @@ const optionsSchema = { 'maximalLength': { 'type': 'integer' } -} +}; /* * From 121b02b896fba8766f16cc8283eabcd816d1e417 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 9 May 2022 14:26:08 +0200 Subject: [PATCH 40/54] Added kinds extractor to SourceLine. --- lib/SourceLine.js | 24 ++++++++++++++++++++++++ sources/SourceLine.ts | 22 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/lib/SourceLine.js b/lib/SourceLine.js index 1b73266..6fec01b 100644 --- a/lib/SourceLine.js +++ b/lib/SourceLine.js @@ -31,6 +31,20 @@ class SourceLine { * Functions * * */ + getEssentialTokens() { + const essentials = [], tokens = this.tokens; + for (const token of tokens) { + switch (token.kind) { + case TS.SyntaxKind.EndOfFileToken: + case TS.SyntaxKind.NewLineTrivia: + case TS.SyntaxKind.WhitespaceTrivia: + continue; + default: + essentials.push(token); + } + } + return essentials; + } getIndent() { const firstToken = this.tokens[0]; if (firstToken && @@ -50,6 +64,16 @@ class SourceLine { } return maximalLength; } + getTokenKinds(start, end) { + const tokenKinds = [], tokens = this.tokens, tokensLength = tokens.length; + if (start && start >= tokensLength) { + return []; + } + for (let i = Math.max(start || 0, 0), iEnd = Math.min(end || tokensLength, tokensLength); i < iEnd; ++i) { + tokenKinds.push(tokens[i].kind); + } + return tokenKinds; + } /** * Returns the token position relative to the line. */ diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index ad0a083..65e8f0a 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -114,6 +114,28 @@ export class SourceLine { } + public getTokenKinds(start?: number, end?: number): Array { + const tokenKinds: Array = [], + tokens = this.tokens, + tokensLength = tokens.length; + + if (start && start >= tokensLength) { + return []; + } + + for ( + let i = Math.max(start || 0, 0), + iEnd = Math.min(end || tokensLength, tokensLength); + i < iEnd; + ++i + ) { + tokenKinds.push(tokens[i].kind); + } + + return tokenKinds; + } + + /** * Returns the token position relative to the line. */ From 1792b286b6010294923a3ed693e3582ff4409eb0 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 9 May 2022 20:00:14 +0200 Subject: [PATCH 41/54] Added source cache to RuleContext. --- lib/RuleContext.js | 25 +++++++++++++++++++++-- sources/RuleContext.ts | 45 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/lib/RuleContext.js b/lib/RuleContext.js index 5d4a4ab..9da785a 100644 --- a/lib/RuleContext.js +++ b/lib/RuleContext.js @@ -8,6 +8,13 @@ const FS = require("fs"); const Path = require("path"); const SourceCode_1 = require("./SourceCode"); const SourceTree_1 = require("./SourceTree"); +/* * + * + * Constants + * + * */ +const sourceCodeCache = {}; +const sourceTreeCache = {}; /* * * * Class @@ -50,13 +57,27 @@ class RuleContext { } get sourceCode() { if (!this._sourceCode) { - this._sourceCode = new SourceCode_1.default(this.sourcePath, FS.readFileSync(this.sourcePath).toString()); + const sourcePath = this.sourcePath; + if (sourceCodeCache[sourcePath]) { + this._sourceCode = sourceCodeCache[sourcePath]; + } + else { + this._sourceCode = new SourceCode_1.default(sourcePath, FS.readFileSync(sourcePath).toString()); + sourceCodeCache[sourcePath] = this._sourceCode; + } } return this._sourceCode; } get sourceTree() { if (!this._sourceTree) { - this._sourceTree = new SourceTree_1.default(this.sourcePath, FS.readFileSync(this.sourcePath).toString()); + const sourcePath = this.sourcePath; + if (sourceTreeCache[sourcePath]) { + this._sourceTree = sourceTreeCache[sourcePath]; + } + else { + this._sourceTree = new SourceTree_1.default(sourcePath, FS.readFileSync(sourcePath).toString()); + sourceTreeCache[sourcePath] = this._sourceTree; + } } return this._sourceTree; } diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index 5021b76..760eed1 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -27,6 +27,19 @@ import SourcePosition from './SourcePosition'; import SourceTree from './SourceTree'; +/* * + * + * Constants + * + * */ + + +const sourceCodeCache: Record = {}; + + +const sourceTreeCache: Record = {}; + + /* * * * Class @@ -126,11 +139,19 @@ export class RuleContext { public get sourceCode (): SourceCode { + if (!this._sourceCode) { - this._sourceCode = new SourceCode( - this.sourcePath, - FS.readFileSync(this.sourcePath).toString() - ); + const sourcePath = this.sourcePath; + + if (sourceCodeCache[sourcePath]) { + this._sourceCode = sourceCodeCache[sourcePath]; + } else { + this._sourceCode = new SourceCode( + sourcePath, + FS.readFileSync(sourcePath).toString() + ); + sourceCodeCache[sourcePath] = this._sourceCode; + } } return this._sourceCode; @@ -141,11 +162,19 @@ export class RuleContext { public get sourceTree (): SourceTree { + if (!this._sourceTree) { - this._sourceTree = new SourceTree( - this.sourcePath, - FS.readFileSync(this.sourcePath).toString() - ); + const sourcePath = this.sourcePath; + + if (sourceTreeCache[sourcePath]) { + this._sourceTree = sourceTreeCache[sourcePath]; + } else { + this._sourceTree = new SourceTree( + sourcePath, + FS.readFileSync(sourcePath).toString() + ); + sourceTreeCache[sourcePath] = this._sourceTree; + } } return this._sourceTree; From 016a228d82308dfbda0ec02fb496766d736433e4 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Mon, 9 May 2022 20:01:43 +0200 Subject: [PATCH 42/54] Added no-import-modification rule. --- lib/rules/no-import-modification.js | 89 ++++++++++++ sources/rules/no-import-modification.ts | 173 ++++++++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100644 lib/rules/no-import-modification.js create mode 100644 sources/rules/no-import-modification.ts diff --git a/lib/rules/no-import-modification.js b/lib/rules/no-import-modification.js new file mode 100644 index 0000000..f324eaf --- /dev/null +++ b/lib/rules/no-import-modification.js @@ -0,0 +1,89 @@ +/** + * @fileoverview Imports should not be immediately modified as this would + * prevent async import. Provide a composer to allow modifications by consumers. + * @author Sophie Bremer + */ +'use strict'; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); +/* * + * + * Constants + * + * */ +const messageTemplate = 'Imports should not be immediately modified. Create a composer.'; +const optionsDefaults = { + maximalLength: 80 +}; +const optionsSchema = {}; +/* * + * + * Functions + * + * */ +function lint(context) { + const code = context.sourceCode, importsToCheck = [], tokensToCheck = []; + let firstToken, secondToken, tokens; + for (const line of code.lines) { + if (line.getIndent() !== 0) { + continue; + } + tokens = line.getEssentialTokens(); + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + firstToken = tokens[i]; + if (firstToken.kind === TS.SyntaxKind.DeleteKeyword) { + const identifierIndex = line + .getTokenKinds(i) + .indexOf(TS.SyntaxKind.Identifier) + i; + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } + else if (firstToken.kind === TS.SyntaxKind.EqualsToken) { + const identifierIndex = line + .getTokenKinds(0, i) + .indexOf(TS.SyntaxKind.Identifier); + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } + secondToken = tokens[i + 1]; + if (!secondToken) { + continue; + } + if (firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.Identifier) { + importsToCheck.push(secondToken.text); + } + else if (firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.OpenBraceToken) { + let previousToken; + for (const thisToken of tokens.slice(i + 2)) { + if (previousToken && + previousToken.kind === TS.SyntaxKind.Identifier && + (thisToken.kind === TS.SyntaxKind.CommaToken || + thisToken.kind === TS.SyntaxKind.CloseBraceToken || + thisToken.kind === TS.SyntaxKind.CloseBracketToken)) { + importsToCheck.push(previousToken.text); + } + previousToken = thisToken; + } + } + } + } + for (const token of tokensToCheck) { + if (importsToCheck.includes(token[1].text)) { + const position = code.getTokenPosition(token[0], token[1]); + if (position) { + context.prepareReport(position, messageTemplate); + } + } + } + context.sendReports(); +} +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, false); diff --git a/sources/rules/no-import-modification.ts b/sources/rules/no-import-modification.ts new file mode 100644 index 0000000..07a11be --- /dev/null +++ b/sources/rules/no-import-modification.ts @@ -0,0 +1,173 @@ +/** + * @fileoverview Imports should not be immediately modified as this would + * prevent async import. Provide a composer to allow modifications by consumers. + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; +import SourceLine from '../SourceLine'; +import SourceToken from '../SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +type NoImportModificationContext = RuleContext; + + +interface NoImportModificationOptions extends RuleOptions { + ignorePattern?: string; + indentSize?: number; + maximalLength: number; +} + + +/* * + * + * Constants + * + * */ + + +const messageTemplate = 'Imports should not be immediately modified. Create a composer.'; + + +const optionsDefaults: NoImportModificationOptions = { + maximalLength: 80 +}; + + +const optionsSchema = {}; + + +/* * + * + * Functions + * + * */ + + +function lint( + context: NoImportModificationContext +): void { + const code = context.sourceCode, + importsToCheck: Array = [], + tokensToCheck: Array<[SourceLine, SourceToken]> = []; + + let firstToken: SourceToken, + secondToken: SourceToken, + tokens: Array; + + for (const line of code.lines) { + + if (line.getIndent() !== 0) { + continue; + } + + tokens = line.getEssentialTokens(); + + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + firstToken = tokens[i]; + + if (firstToken.kind === TS.SyntaxKind.DeleteKeyword) { + const identifierIndex = line + .getTokenKinds(i) + .indexOf(TS.SyntaxKind.Identifier) + i; + + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } else if ( + firstToken.kind === TS.SyntaxKind.EqualsToken + ) { + const identifierIndex = line + .getTokenKinds(0, i) + .indexOf(TS.SyntaxKind.Identifier); + + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } + + secondToken = tokens[i+1]; + + if (!secondToken) { + continue; + } + + if ( + firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.Identifier + ) { + importsToCheck.push(secondToken.text); + } else if ( + firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.OpenBraceToken + ) { + let previousToken: (SourceToken|undefined); + + for (const thisToken of tokens.slice(i+2)) { + + if ( + previousToken && + previousToken.kind === TS.SyntaxKind.Identifier && + ( + thisToken.kind === TS.SyntaxKind.CommaToken || + thisToken.kind === TS.SyntaxKind.CloseBraceToken || + thisToken.kind === TS.SyntaxKind.CloseBracketToken + ) + ) { + importsToCheck.push(previousToken.text); + } + + previousToken = thisToken; + } + } + } + } + + for (const token of tokensToCheck) { + if (importsToCheck.includes(token[1].text)) { + const position = code.getTokenPosition(token[0], token[1]); + + if (position) { + context.prepareReport(position, messageTemplate); + } + } + } + + context.sendReports(); +} + + +/* * + * + * Default Export + * + * */ + + +export = RuleContext.setupRuleExport( + 'layout', + optionsSchema, + optionsDefaults, + lint, + false +); From 51fb7efa39c602d11285654d6f82eb8f44f47660 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Tue, 10 May 2022 20:05:15 +0200 Subject: [PATCH 43/54] Replaced source cache with global scanner. --- sources/FixFunction.d.ts | 36 ---------------------- sources/RuleContext.ts | 40 +++++-------------------- sources/SourceCode.ts | 21 +++++++++---- sources/rules/no-import-modification.ts | 8 ++--- 4 files changed, 25 insertions(+), 80 deletions(-) delete mode 100644 sources/FixFunction.d.ts diff --git a/sources/FixFunction.d.ts b/sources/FixFunction.d.ts deleted file mode 100644 index cf58f3a..0000000 --- a/sources/FixFunction.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @author Sophie Bremer - */ - - -/* * - * - * Imports - * - * */ - - -import type RuleContext from './RuleContext'; -import type RuleOptions from './RuleOptions'; - - -/* * - * - * Declarations - * - * */ - - -export interface FixFunction { - (ruleContext: RuleContext): void; -} - - -/* * - * - * Default Export - * - * */ - - -export default FixFunction; diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index 760eed1..a155457 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -15,7 +15,6 @@ import type * as ESLint from 'eslint'; import type * as JSONSchema from 'json-schema'; -import type FixFunction from './FixFunction'; import type LintFunction from './LintFunction'; import type RuleOptions from './RuleOptions'; @@ -27,19 +26,6 @@ import SourcePosition from './SourcePosition'; import SourceTree from './SourceTree'; -/* * - * - * Constants - * - * */ - - -const sourceCodeCache: Record = {}; - - -const sourceTreeCache: Record = {}; - - /* * * * Class @@ -143,15 +129,10 @@ export class RuleContext { if (!this._sourceCode) { const sourcePath = this.sourcePath; - if (sourceCodeCache[sourcePath]) { - this._sourceCode = sourceCodeCache[sourcePath]; - } else { - this._sourceCode = new SourceCode( - sourcePath, - FS.readFileSync(sourcePath).toString() - ); - sourceCodeCache[sourcePath] = this._sourceCode; - } + this._sourceCode = new SourceCode( + sourcePath, + FS.readFileSync(sourcePath).toString() + ); } return this._sourceCode; @@ -166,15 +147,10 @@ export class RuleContext { if (!this._sourceTree) { const sourcePath = this.sourcePath; - if (sourceTreeCache[sourcePath]) { - this._sourceTree = sourceTreeCache[sourcePath]; - } else { - this._sourceTree = new SourceTree( - sourcePath, - FS.readFileSync(sourcePath).toString() - ); - sourceTreeCache[sourcePath] = this._sourceTree; - } + this._sourceTree = new SourceTree( + sourcePath, + FS.readFileSync(sourcePath).toString() + ); } return this._sourceTree; diff --git a/sources/SourceCode.ts b/sources/SourceCode.ts index e2d3b5d..3893e7a 100644 --- a/sources/SourceCode.ts +++ b/sources/SourceCode.ts @@ -13,6 +13,7 @@ * */ +import * as Timers from 'timers'; import * as TS from 'typescript'; import * as U from './Utilities'; import SourceComment from './SourceComment'; @@ -22,6 +23,16 @@ import SourcePosition from './SourcePosition'; import SourceToken from './SourceToken'; +/* * + * + * Constant + * + * */ + + +const tsScanner = TS.createScanner(TS.ScriptTarget.Latest, false); + + /* * * * Class @@ -169,19 +180,17 @@ export class SourceCode { return; } - const scanner = TS.createScanner(TS.ScriptTarget.Latest, false); - let indent: number, kind: TS.SyntaxKind, line = new SourceLine(lineBreak), text: string, token: SourceToken; - scanner.setText(sourceCode); + tsScanner.setText(sourceCode); do { - kind = scanner.scan(); - text = scanner.getTokenText(); + kind = tsScanner.scan(); + text = tsScanner.getTokenText(); if ( kind === TS.SyntaxKind.NewLineTrivia || @@ -206,7 +215,7 @@ export class SourceCode { line.tokens.push(token); - } while (kind !== TS.SyntaxKind.EndOfFileToken) + } while (kind !== TS.SyntaxKind.EndOfFileToken); } diff --git a/sources/rules/no-import-modification.ts b/sources/rules/no-import-modification.ts index 07a11be..1a2d5fa 100644 --- a/sources/rules/no-import-modification.ts +++ b/sources/rules/no-import-modification.ts @@ -34,8 +34,6 @@ type NoImportModificationContext = RuleContext; interface NoImportModificationOptions extends RuleOptions { ignorePattern?: string; - indentSize?: number; - maximalLength: number; } @@ -49,9 +47,7 @@ interface NoImportModificationOptions extends RuleOptions { const messageTemplate = 'Imports should not be immediately modified. Create a composer.'; -const optionsDefaults: NoImportModificationOptions = { - maximalLength: 80 -}; +const optionsDefaults: NoImportModificationOptions = {}; const optionsSchema = {}; @@ -165,7 +161,7 @@ function lint( export = RuleContext.setupRuleExport( - 'layout', + 'problem', optionsSchema, optionsDefaults, lint, From 1a388c1b70c81fb30efb3c585308b39f89ea98ab Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 12:32:34 +0200 Subject: [PATCH 44/54] Optimized no-import-modification rule. --- lib/RuleContext.js | 23 ++--------------------- lib/SourceCode.js | 18 +++++++++--------- lib/rules/no-import-modification.js | 11 ++++++----- sources/rules/no-import-modification.ts | 14 ++++++-------- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/lib/RuleContext.js b/lib/RuleContext.js index 9da785a..d9ef61b 100644 --- a/lib/RuleContext.js +++ b/lib/RuleContext.js @@ -8,13 +8,6 @@ const FS = require("fs"); const Path = require("path"); const SourceCode_1 = require("./SourceCode"); const SourceTree_1 = require("./SourceTree"); -/* * - * - * Constants - * - * */ -const sourceCodeCache = {}; -const sourceTreeCache = {}; /* * * * Class @@ -58,26 +51,14 @@ class RuleContext { get sourceCode() { if (!this._sourceCode) { const sourcePath = this.sourcePath; - if (sourceCodeCache[sourcePath]) { - this._sourceCode = sourceCodeCache[sourcePath]; - } - else { - this._sourceCode = new SourceCode_1.default(sourcePath, FS.readFileSync(sourcePath).toString()); - sourceCodeCache[sourcePath] = this._sourceCode; - } + this._sourceCode = new SourceCode_1.default(sourcePath, FS.readFileSync(sourcePath).toString()); } return this._sourceCode; } get sourceTree() { if (!this._sourceTree) { const sourcePath = this.sourcePath; - if (sourceTreeCache[sourcePath]) { - this._sourceTree = sourceTreeCache[sourcePath]; - } - else { - this._sourceTree = new SourceTree_1.default(sourcePath, FS.readFileSync(sourcePath).toString()); - sourceTreeCache[sourcePath] = this._sourceTree; - } + this._sourceTree = new SourceTree_1.default(sourcePath, FS.readFileSync(sourcePath).toString()); } return this._sourceTree; } diff --git a/lib/SourceCode.js b/lib/SourceCode.js index 5afff6a..1db87ea 100644 --- a/lib/SourceCode.js +++ b/lib/SourceCode.js @@ -4,16 +4,17 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceCode = void 0; -/* * - * - * Imports - * - * */ const TS = require("typescript"); const U = require("./Utilities"); const SourceComment_1 = require("./SourceComment"); const SourceDoc_1 = require("./SourceDoc"); const SourceLine_1 = require("./SourceLine"); +/* * + * + * Constant + * + * */ +const tsScanner = TS.createScanner(TS.ScriptTarget.Latest, false); /* * * * Class @@ -89,12 +90,11 @@ class SourceCode { if (!sourceCode) { return; } - const scanner = TS.createScanner(TS.ScriptTarget.Latest, false); let indent, kind, line = new SourceLine_1.default(lineBreak), text, token; - scanner.setText(sourceCode); + tsScanner.setText(sourceCode); do { - kind = scanner.scan(); - text = scanner.getTokenText(); + kind = tsScanner.scan(); + text = tsScanner.getTokenText(); if (kind === TS.SyntaxKind.NewLineTrivia || kind === TS.SyntaxKind.EndOfFileToken) { lines.push(line); diff --git a/lib/rules/no-import-modification.js b/lib/rules/no-import-modification.js index f324eaf..f04c67f 100644 --- a/lib/rules/no-import-modification.js +++ b/lib/rules/no-import-modification.js @@ -16,10 +16,11 @@ const RuleContext_1 = require("../RuleContext"); * Constants * * */ -const messageTemplate = 'Imports should not be immediately modified. Create a composer.'; -const optionsDefaults = { - maximalLength: 80 -}; +const messageTemplate = [ + 'Imports should not be immediately modified.', + 'Create a composer.' +].join(' '); +const optionsDefaults = {}; const optionsSchema = {}; /* * * @@ -86,4 +87,4 @@ function lint(context) { } context.sendReports(); } -module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, false); +module.exports = RuleContext_1.default.setupRuleExport('problem', optionsSchema, optionsDefaults, lint, false); diff --git a/sources/rules/no-import-modification.ts b/sources/rules/no-import-modification.ts index 1a2d5fa..4cf58d0 100644 --- a/sources/rules/no-import-modification.ts +++ b/sources/rules/no-import-modification.ts @@ -29,12 +29,7 @@ import SourceToken from '../SourceToken'; * */ -type NoImportModificationContext = RuleContext; - - -interface NoImportModificationOptions extends RuleOptions { - ignorePattern?: string; -} +type NoImportModificationContext = RuleContext; /* * @@ -44,10 +39,13 @@ interface NoImportModificationOptions extends RuleOptions { * */ -const messageTemplate = 'Imports should not be immediately modified. Create a composer.'; +const messageTemplate = [ + 'Imports should not be immediately modified.', + 'Create a composer.' +].join(' '); -const optionsDefaults: NoImportModificationOptions = {}; +const optionsDefaults: RuleOptions = {}; const optionsSchema = {}; From 9e44421a6d1097ed3b5dfa67991cd83831c0a167 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 12:36:53 +0200 Subject: [PATCH 45/54] Migrated no-optional-chaining rule. --- lib/rules/no-optional-chaining.js | 104 +++++++++++--------------- sources/rules/no-optional-chaining.ts | 102 +++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 62 deletions(-) create mode 100644 sources/rules/no-optional-chaining.ts diff --git a/lib/rules/no-optional-chaining.js b/lib/rules/no-optional-chaining.js index bab9581..0426308 100644 --- a/lib/rules/no-optional-chaining.js +++ b/lib/rules/no-optional-chaining.js @@ -1,66 +1,46 @@ -/* - * @fileoverview The optional chaining should not be used, as it bloats - * transpiled ES5 code. Instead make use of the `pick` function or make inline - * chain with `&&` or `||`. +/** + * @fileoverview Do not use optional chaining because it bloats transpiled code. + * Instead use the `pick` function or `&&` conditions. + * @author Sophie Bremer */ 'use strict'; - -const message = 'Do not use optional chaining.'; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - docs: { - description: message, - category: 'Migration', - recommended: false - }, - fixable: null, // or "code" or "whitespace" - schema: [ - // fill in your schema - ] - }, - - create: function (context) { - - // variables should be defined here - const code = context.getSourceCode().lines.join('\n'); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - const program = (node) => { - - const pattern = /\?\.[\w\[\(]/g; - - let match; - - while (match = pattern.exec(code)) { - const codeBefore = code.substr(0, pattern.lastIndex - match[0].length); - const linesBefore = codeBefore.split('\n'); - const line = linesBefore[linesBefore.length - 1]; - - context.report({ - node: node, - loc: { - line: linesBefore.length, - column: line.length - }, - message: message - }); +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); +/* * + * + * Constants + * + * */ +const message = [ + 'Do not use optional chaining.', + 'Instead use the `pick` function or `&&` conditions.' +].join(' '); +const optionsDefaults = {}; +const optionsSchema = {}; +/* * + * + * Functions + * + * */ +function lint(context) { + const code = context.sourceCode, lines = code.lines; + let tokens; + for (const line of lines) { + tokens = line.tokens; + for (let index = 0, indexEnd = tokens.length - 2, firstToken; index < indexEnd; ++index) { + firstToken = tokens[index]; + if (firstToken.kind === TS.SyntaxKind.QuestionDotToken) { + const position = code.getTokenPosition(line, firstToken); + if (position) { + context.report(position, message); + } } - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - - Program: program - - }; + } } -}; +} +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, false); diff --git a/sources/rules/no-optional-chaining.ts b/sources/rules/no-optional-chaining.ts new file mode 100644 index 0000000..559e4ec --- /dev/null +++ b/sources/rules/no-optional-chaining.ts @@ -0,0 +1,102 @@ +/** + * @fileoverview Do not use optional chaining because it bloats transpiled code. + * Instead use the `pick` function or `&&` conditions. + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; +import SourceToken from '../SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +type GenericArrayTypeContext = RuleContext; + + +/* * + * + * Constants + * + * */ + + +const message = [ + 'Do not use optional chaining.', + 'Instead use the `pick` function or `&&` conditions.' +].join(' '); + +const optionsDefaults: RuleOptions = {}; + +const optionsSchema = {}; + + +/* * + * + * Functions + * + * */ + + +function lint ( + context: GenericArrayTypeContext +): void { + const code = context.sourceCode, + lines = code.lines; + + let tokens: Array; + + for (const line of lines) { + tokens = line.tokens; + + for ( + let index = 0, + indexEnd = tokens.length - 2, + firstToken: SourceToken; + index < indexEnd; + ++index + ) { + firstToken = tokens[index]; + + if (firstToken.kind === TS.SyntaxKind.QuestionDotToken) { + const position = code.getTokenPosition(line, firstToken); + + if (position) { + context.report(position, message); + } + } + + } + } +} + +/* * + * + * Default Export + * + * */ + +export = RuleContext.setupRuleExport( + 'layout', + optionsSchema, + optionsDefaults, + lint, + false +); From 5a41136a503b8c2ce99397237825ea1ee0e90ca5 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 12:37:12 +0200 Subject: [PATCH 46/54] Added no-import-type rule. --- lib/rules/no-import-type.js | 50 +++++++++++++ sources/rules/no-import-type.ts | 124 ++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 lib/rules/no-import-type.js create mode 100644 sources/rules/no-import-type.ts diff --git a/lib/rules/no-import-type.js b/lib/rules/no-import-type.js new file mode 100644 index 0000000..99a6583 --- /dev/null +++ b/lib/rules/no-import-type.js @@ -0,0 +1,50 @@ +/** + * @fileoverview Explicitly type imports are not necessary because TypeScript + * will automatically remove type-only used imports during transpilation. + * @author Sophie Bremer + */ +'use strict'; +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); +/* * + * + * Constants + * + * */ +const message = [ + 'Explicitly type imports are not necessary.', + 'Instead write the generic Array<...> to improve readability.' +].join(' '); +const optionsDefaults = {}; +const optionsSchema = {}; +/* * + * + * Functions + * + * */ +function createFixer(range) { + return () => ({ range, text: '' }); +} +function lint(context) { + const code = context.sourceCode, lines = code.lines; + let tokens; + for (const line of lines) { + tokens = line.getEssentialTokens(); + for (let index = 0, indexEnd = tokens.length - 2, firstToken, secondToken; index < indexEnd; ++index) { + firstToken = tokens[index]; + secondToken = tokens[index + 1]; + if (firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.TypeKeyword) { + const firstPosition = code.getTokenPosition(line, firstToken), secondPosition = code.getTokenPosition(line, secondToken); + if (firstPosition && secondPosition) { + const range = [ + firstPosition.end, + secondPosition.end + ]; + context.report(secondPosition, message, createFixer(range)); + } + } + } + } +} +module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, true); diff --git a/sources/rules/no-import-type.ts b/sources/rules/no-import-type.ts new file mode 100644 index 0000000..92ccc85 --- /dev/null +++ b/sources/rules/no-import-type.ts @@ -0,0 +1,124 @@ +/** + * @fileoverview Explicitly type imports are not necessary because TypeScript + * will automatically remove type-only used imports during transpilation. + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as ESLint from 'eslint'; +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; +import SourceToken from '../SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +type NoImportTypeTypeContext = RuleContext; + + +/* * + * + * Constants + * + * */ + + +const message = [ + 'Explicitly type imports are not necessary.', + 'Instead write the generic Array<...> to improve readability.' +].join(' '); + +const optionsDefaults: RuleOptions = {}; + +const optionsSchema = {}; + + +/* * + * + * Functions + * + * */ + + +function createFixer( + range: ESLint.AST.Range +): ESLint.Rule.ReportFixer { + return (): ESLint.Rule.Fix => ({ range, text: '' }); +} + +function lint ( + context: NoImportTypeTypeContext +): void { + const code = context.sourceCode, + lines = code.lines; + + let tokens: Array; + + for (const line of lines) { + tokens = line.getEssentialTokens(); + + for ( + let index = 0, + indexEnd = tokens.length - 2, + firstToken: SourceToken, + secondToken: SourceToken; + index < indexEnd; + ++index + ) { + firstToken = tokens[index]; + secondToken = tokens[index+1]; + + if ( + firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.TypeKeyword + ) { + const firstPosition = code.getTokenPosition(line, firstToken), + secondPosition = code.getTokenPosition(line, secondToken); + + if (firstPosition && secondPosition) { + const range: ESLint.AST.Range = [ + firstPosition.end, + secondPosition.end + ]; + + context.report( + secondPosition, + message, + createFixer(range) + ); + } + } + + } + } +} + +/* * + * + * Default Export + * + * */ + +export = RuleContext.setupRuleExport( + 'layout', + optionsSchema, + optionsDefaults, + lint, + true +); From 6ed49ebbf6a0e122052a5d781c005f03321b6be8 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 13:18:02 +0200 Subject: [PATCH 47/54] Improved typing of rules. --- .gitignore | 4 +- lib/rules/generic-array-type.js | 20 +-- lib/rules/no-import-effects.js | 90 ++++++++++++++ lib/rules/no-import-type.js | 21 ++-- lib/rules/no-optional-chaining.js | 2 +- lib/rules/pretty-length.js | 2 +- package-lock.json | 14 +-- package.json | 2 +- sources/.eslintrc | 1 + sources/rules/generic-array-type.ts | 35 +++--- sources/rules/no-import-effects.ts | 167 ++++++++++++++++++++++++++ sources/rules/no-import-type.ts | 33 +++-- sources/rules/no-optional-chaining.ts | 2 +- sources/rules/pretty-length.ts | 4 +- 14 files changed, 323 insertions(+), 74 deletions(-) create mode 100644 lib/rules/no-import-effects.js create mode 100644 sources/rules/no-import-effects.ts diff --git a/.gitignore b/.gitignore index b512c09..a548411 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules \ No newline at end of file +*.tsbuildinfo +lib/**/*.d.ts +node_modules/ \ No newline at end of file diff --git a/lib/rules/generic-array-type.js b/lib/rules/generic-array-type.js index 867d684..3e1fe5a 100644 --- a/lib/rules/generic-array-type.js +++ b/lib/rules/generic-array-type.js @@ -36,19 +36,19 @@ function createFixer(position, identifierToken, openBracketToken, closeBracketTo } function lint(context) { const code = context.sourceCode, lines = code.lines; - let tokens; + let firstToken, secondToken, thirdToken, tokens; for (const line of lines) { tokens = line.tokens; - for (let index = 0, indexEnd = tokens.length - 2, identifierToken, openBracketToken, closeBracketToken; index < indexEnd; ++index) { - identifierToken = tokens[index]; - openBracketToken = tokens[index + 1]; - closeBracketToken = tokens[index + 2]; - if (identifierToken.kind === TS.SyntaxKind.Identifier && - openBracketToken.kind === TS.SyntaxKind.OpenBracketToken && - closeBracketToken.kind === TS.SyntaxKind.CloseBracketToken) { - const position = code.getTokenPosition(line, openBracketToken); + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i + 1]; + thirdToken = tokens[i + 2]; + if (firstToken.kind === TS.SyntaxKind.Identifier && + secondToken.kind === TS.SyntaxKind.OpenBracketToken && + thirdToken.kind === TS.SyntaxKind.CloseBracketToken) { + const position = code.getTokenPosition(line, secondToken); if (position) { - context.report(position, message, createFixer(position, identifierToken, openBracketToken, closeBracketToken)); + context.report(position, message, createFixer(position, firstToken, secondToken, thirdToken)); } } } diff --git a/lib/rules/no-import-effects.js b/lib/rules/no-import-effects.js new file mode 100644 index 0000000..b6d0d8a --- /dev/null +++ b/lib/rules/no-import-effects.js @@ -0,0 +1,90 @@ +/** + * @fileoverview Imports should not be anonymous. Move desired side effects into + * compose functions and call these instead. + * @author Sophie Bremer + */ +'use strict'; +/* * + * + * Imports + * + * */ +const TS = require("typescript"); +const RuleContext_1 = require("../RuleContext"); +/* * + * + * Constants + * + * */ +const messageTemplate = [ + 'Imports should not be anonymous.', + 'Create and call a composer for side effects.' +].join(' '); +const optionsDefaults = {}; +const optionsSchema = {}; +/* * + * + * Functions + * + * */ +function lint(context) { + const code = context.sourceCode, importsToCheck = [], tokensToCheck = []; + let firstToken, secondToken, tokens; + for (const line of code.lines) { + if (line.getIndent() !== 0) { + continue; + } + tokens = line.getEssentialTokens(); + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + firstToken = tokens[i]; + if (firstToken.kind === TS.SyntaxKind.DeleteKeyword) { + const identifierIndex = line + .getTokenKinds(i) + .indexOf(TS.SyntaxKind.Identifier) + i; + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } + else if (firstToken.kind === TS.SyntaxKind.EqualsToken) { + const identifierIndex = line + .getTokenKinds(0, i) + .indexOf(TS.SyntaxKind.Identifier); + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } + secondToken = tokens[i + 1]; + if (!secondToken) { + continue; + } + if (firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.Identifier) { + importsToCheck.push(secondToken.text); + } + else if (firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.OpenBraceToken) { + let previousToken; + for (const thisToken of tokens.slice(i + 2)) { + if (previousToken && + previousToken.kind === TS.SyntaxKind.Identifier && + (thisToken.kind === TS.SyntaxKind.CommaToken || + thisToken.kind === TS.SyntaxKind.CloseBraceToken || + thisToken.kind === TS.SyntaxKind.CloseBracketToken)) { + importsToCheck.push(previousToken.text); + } + previousToken = thisToken; + } + } + } + } + for (const token of tokensToCheck) { + if (importsToCheck.includes(token[1].text)) { + const position = code.getTokenPosition(token[0], token[1]); + if (position) { + context.prepareReport(position, messageTemplate); + } + } + } + context.sendReports(); +} +module.exports = RuleContext_1.default.setupRuleExport('problem', optionsSchema, optionsDefaults, lint, false); diff --git a/lib/rules/no-import-type.js b/lib/rules/no-import-type.js index 99a6583..6f000b7 100644 --- a/lib/rules/no-import-type.js +++ b/lib/rules/no-import-type.js @@ -22,26 +22,25 @@ const optionsSchema = {}; * Functions * * */ -function createFixer(range) { - return () => ({ range, text: '' }); +function createFixer(firstPosition, secondPosition) { + return () => ({ + range: [firstPosition.end, secondPosition.end], + text: '' + }); } function lint(context) { const code = context.sourceCode, lines = code.lines; - let tokens; + let firstToken, secondToken, tokens; for (const line of lines) { tokens = line.getEssentialTokens(); - for (let index = 0, indexEnd = tokens.length - 2, firstToken, secondToken; index < indexEnd; ++index) { - firstToken = tokens[index]; - secondToken = tokens[index + 1]; + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i + 1]; if (firstToken.kind === TS.SyntaxKind.ImportKeyword && secondToken.kind === TS.SyntaxKind.TypeKeyword) { const firstPosition = code.getTokenPosition(line, firstToken), secondPosition = code.getTokenPosition(line, secondToken); if (firstPosition && secondPosition) { - const range = [ - firstPosition.end, - secondPosition.end - ]; - context.report(secondPosition, message, createFixer(range)); + context.report(secondPosition, message, createFixer(firstPosition, secondPosition)); } } } diff --git a/lib/rules/no-optional-chaining.js b/lib/rules/no-optional-chaining.js index 0426308..ddda060 100644 --- a/lib/rules/no-optional-chaining.js +++ b/lib/rules/no-optional-chaining.js @@ -43,4 +43,4 @@ function lint(context) { } } } -module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, false); +module.exports = RuleContext_1.default.setupRuleExport('problem', optionsSchema, optionsDefaults, lint, false); diff --git a/lib/rules/pretty-length.js b/lib/rules/pretty-length.js index 0a2ae47..de3f2e7 100644 --- a/lib/rules/pretty-length.js +++ b/lib/rules/pretty-length.js @@ -71,10 +71,10 @@ function lint(context) { line: position.line + lineIndex, start: position.start }, message + ` ${maximalLineLength}`); + fixLines.push(line); } ++lineIndex; } - fixLines.push(line); } } } diff --git a/package-lock.json b/package-lock.json index c8e15e1..b714726 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@typescript-eslint/parser": "^5.3.0", "husky": "7.0.4", "mocha": "^9.1.2", - "typescript": "^4.4.4" + "typescript": "^4.6.4" }, "engines": { "node": ">=12.0.0", @@ -2084,9 +2084,9 @@ } }, "node_modules/typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3709,9 +3709,9 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" }, "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index e78c3aa..1a07ad4 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@typescript-eslint/parser": "^5.3.0", "husky": "7.0.4", "mocha": "^9.1.2", - "typescript": "^4.4.4" + "typescript": "^4.6.4" }, "engines": { "node": ">=12.0.0", diff --git a/sources/.eslintrc b/sources/.eslintrc index 5ca1160..6bafc36 100644 --- a/sources/.eslintrc +++ b/sources/.eslintrc @@ -6,6 +6,7 @@ "parser": "@typescript-eslint/parser", "rules": { "@typescript-eslint/no-inferrable-types": 0, + "@typescript-eslint/no-namespace": 0, "@typescript-eslint/pretty-length": 0 } } diff --git a/sources/rules/generic-array-type.ts b/sources/rules/generic-array-type.ts index 6c9b8f1..529af3c 100644 --- a/sources/rules/generic-array-type.ts +++ b/sources/rules/generic-array-type.ts @@ -85,30 +85,25 @@ function lint ( const code = context.sourceCode, lines = code.lines; - let tokens: Array; + let firstToken: SourceToken, + secondToken: SourceToken, + thirdToken: SourceToken, + tokens: Array; for (const line of lines) { tokens = line.tokens; - for ( - let index = 0, - indexEnd = tokens.length - 2, - identifierToken: SourceToken, - openBracketToken: SourceToken, - closeBracketToken: SourceToken; - index < indexEnd; - ++index - ) { - identifierToken = tokens[index]; - openBracketToken = tokens[index+1]; - closeBracketToken = tokens[index+2]; + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i+1]; + thirdToken = tokens[i+2]; if ( - identifierToken.kind === TS.SyntaxKind.Identifier && - openBracketToken.kind === TS.SyntaxKind.OpenBracketToken && - closeBracketToken.kind === TS.SyntaxKind.CloseBracketToken + firstToken.kind === TS.SyntaxKind.Identifier && + secondToken.kind === TS.SyntaxKind.OpenBracketToken && + thirdToken.kind === TS.SyntaxKind.CloseBracketToken ) { - const position = code.getTokenPosition(line, openBracketToken); + const position = code.getTokenPosition(line, secondToken); if (position) { context.report( @@ -116,9 +111,9 @@ function lint ( message, createFixer( position, - identifierToken, - openBracketToken, - closeBracketToken + firstToken, + secondToken, + thirdToken ) ); } diff --git a/sources/rules/no-import-effects.ts b/sources/rules/no-import-effects.ts new file mode 100644 index 0000000..741b0bb --- /dev/null +++ b/sources/rules/no-import-effects.ts @@ -0,0 +1,167 @@ +/** + * @fileoverview Imports should not be anonymous. Move desired side effects into + * compose functions and call these instead. + * @author Sophie Bremer + */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as TS from 'typescript'; +import RuleContext from '../RuleContext'; +import RuleOptions from '../RuleOptions'; +import SourceLine from '../SourceLine'; +import SourceToken from '../SourceToken'; + + +/* * + * + * Declarations + * + * */ + + +type NoImportEffectsContext = RuleContext; + + +/* * + * + * Constants + * + * */ + + +const messageTemplate = [ + 'Imports should not be anonymous.', + 'Create and call a composer for side effects.' +].join(' '); + + +const optionsDefaults: RuleOptions = {}; + + +const optionsSchema = {}; + + +/* * + * + * Functions + * + * */ + + +function lint( + context: NoImportEffectsContext +): void { + const code = context.sourceCode, + importsToCheck: Array = [], + tokensToCheck: Array<[SourceLine, SourceToken]> = []; + + let firstToken: SourceToken, + secondToken: SourceToken, + tokens: Array; + + for (const line of code.lines) { + + if (line.getIndent() !== 0) { + continue; + } + + tokens = line.getEssentialTokens(); + + for (let i = 0, iEnd = tokens.length; i < iEnd; ++i) { + firstToken = tokens[i]; + + if (firstToken.kind === TS.SyntaxKind.DeleteKeyword) { + const identifierIndex = line + .getTokenKinds(i) + .indexOf(TS.SyntaxKind.Identifier) + i; + + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } else if ( + firstToken.kind === TS.SyntaxKind.EqualsToken + ) { + const identifierIndex = line + .getTokenKinds(0, i) + .indexOf(TS.SyntaxKind.Identifier); + + if (identifierIndex >= 0) { + tokensToCheck.push([line, line.tokens[identifierIndex]]); + } + } + + secondToken = tokens[i+1]; + + if (!secondToken) { + continue; + } + + if ( + firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.Identifier + ) { + importsToCheck.push(secondToken.text); + } else if ( + firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.OpenBraceToken + ) { + let previousToken: (SourceToken|undefined); + + for (const thisToken of tokens.slice(i+2)) { + + if ( + previousToken && + previousToken.kind === TS.SyntaxKind.Identifier && + ( + thisToken.kind === TS.SyntaxKind.CommaToken || + thisToken.kind === TS.SyntaxKind.CloseBraceToken || + thisToken.kind === TS.SyntaxKind.CloseBracketToken + ) + ) { + importsToCheck.push(previousToken.text); + } + + previousToken = thisToken; + } + } + } + } + + for (const token of tokensToCheck) { + if (importsToCheck.includes(token[1].text)) { + const position = code.getTokenPosition(token[0], token[1]); + + if (position) { + context.prepareReport(position, messageTemplate); + } + } + } + + context.sendReports(); +} + + +/* * + * + * Default Export + * + * */ + + +export = RuleContext.setupRuleExport( + 'problem', + optionsSchema, + optionsDefaults, + lint, + false +); diff --git a/sources/rules/no-import-type.ts b/sources/rules/no-import-type.ts index 92ccc85..d781ff9 100644 --- a/sources/rules/no-import-type.ts +++ b/sources/rules/no-import-type.ts @@ -19,6 +19,7 @@ import * as ESLint from 'eslint'; import * as TS from 'typescript'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; +import SourcePosition from '../SourcePosition'; import SourceToken from '../SourceToken'; @@ -57,9 +58,13 @@ const optionsSchema = {}; function createFixer( - range: ESLint.AST.Range + firstPosition: SourcePosition, + secondPosition: SourcePosition ): ESLint.Rule.ReportFixer { - return (): ESLint.Rule.Fix => ({ range, text: '' }); + return (): ESLint.Rule.Fix => ({ + range: [firstPosition.end, secondPosition.end], + text: '' + }); } function lint ( @@ -68,21 +73,16 @@ function lint ( const code = context.sourceCode, lines = code.lines; - let tokens: Array; + let firstToken: SourceToken, + secondToken: SourceToken, + tokens: Array; for (const line of lines) { tokens = line.getEssentialTokens(); - for ( - let index = 0, - indexEnd = tokens.length - 2, - firstToken: SourceToken, - secondToken: SourceToken; - index < indexEnd; - ++index - ) { - firstToken = tokens[index]; - secondToken = tokens[index+1]; + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i+1]; if ( firstToken.kind === TS.SyntaxKind.ImportKeyword && @@ -92,15 +92,10 @@ function lint ( secondPosition = code.getTokenPosition(line, secondToken); if (firstPosition && secondPosition) { - const range: ESLint.AST.Range = [ - firstPosition.end, - secondPosition.end - ]; - context.report( secondPosition, message, - createFixer(range) + createFixer(firstPosition, secondPosition) ); } } diff --git a/sources/rules/no-optional-chaining.ts b/sources/rules/no-optional-chaining.ts index 559e4ec..b2d3e5f 100644 --- a/sources/rules/no-optional-chaining.ts +++ b/sources/rules/no-optional-chaining.ts @@ -94,7 +94,7 @@ function lint ( * */ export = RuleContext.setupRuleExport( - 'layout', + 'problem', optionsSchema, optionsDefaults, lint, diff --git a/sources/rules/pretty-length.ts b/sources/rules/pretty-length.ts index 232e5f6..e8d29e3 100644 --- a/sources/rules/pretty-length.ts +++ b/sources/rules/pretty-length.ts @@ -161,12 +161,12 @@ function lint ( }, message + ` ${maximalLineLength}` ); + + fixLines.push(line); } ++lineIndex; } - - fixLines.push(line); } } } From 531eb6523b5d9535b3386d8421b5dd7dd08f0af8 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 14:02:12 +0200 Subject: [PATCH 48/54] Fixed calculation of column positions. --- lib/SourceLine.js | 1 + sources/SourceLine.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/SourceLine.js b/lib/SourceLine.js index 6fec01b..de90762 100644 --- a/lib/SourceLine.js +++ b/lib/SourceLine.js @@ -95,6 +95,7 @@ class SourceLine { } position.start += tokenText.length; } + position.column = position.start + 1; position.end = token.text.length; return position; } diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 65e8f0a..25f1e14 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -168,6 +168,7 @@ export class SourceLine { position.start += tokenText.length; } + position.column = position.start + 1; position.end = token.text.length; return position; From c6110ff9ce11d9f431d5918c63006c171440a647 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 17:55:46 +0200 Subject: [PATCH 49/54] Fixed handling of custom JSDoc tags. --- lib/SourceComment.js | 16 +++++----------- lib/SourceDoc.js | 2 +- sources/SourceComment.ts | 16 +++++----------- sources/SourceDoc.ts | 2 +- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/SourceComment.js b/lib/SourceComment.js index f302dfa..e2b2c23 100644 --- a/lib/SourceComment.js +++ b/lib/SourceComment.js @@ -79,22 +79,16 @@ class SourceComment extends SourceLine_1.default { return this.indent; } toString(maximalLength) { - const lines = []; + const indent = this.indent, lines = []; if (maximalLength) { - let line = '', match, words; + let line = '', words; for (const token of this.tokens) { words = token.text.split(' '); line = words.shift() || ''; for (const word of words) { if (line.length + 1 + word.length > maximalLength) { lines.push(line.trimRight()); - match = line.match(/^\s*\*+\s*/); - if (match && match.groups) { - line = match[0] + word; - } - else { - line = word; - } + line = U.pad(indent, word); } else { line += ` ${word}`; @@ -105,10 +99,10 @@ class SourceComment extends SourceLine_1.default { } else { for (const token of this.tokens) { - lines.push(token.text); + lines.push(U.pad(indent, token.text)); } } - return lines.join(this.lineBreak); + return lines.join(this.lineBreak).substr(indent); } } exports.SourceComment = SourceComment; diff --git a/lib/SourceDoc.js b/lib/SourceDoc.js index 9931e56..36a4eca 100644 --- a/lib/SourceDoc.js +++ b/lib/SourceDoc.js @@ -19,7 +19,7 @@ const SourceLine_1 = require("./SourceLine"); * * */ // eslint-disable-next-line max-len -const tagPattern = /^@(\w+)([ \t]+\{[^\}\s]+\})?([ \t]+\S+)?(\s[\s\S]*)?$/u; +const tagPattern = /^@(\S+)([ \t]+\{[^\}\s]+\})?([ \t]+\S+)?(\s[\s\S]*)?$/u; /* * * * Class diff --git a/sources/SourceComment.ts b/sources/SourceComment.ts index c80ab1d..84a7ab4 100644 --- a/sources/SourceComment.ts +++ b/sources/SourceComment.ts @@ -138,11 +138,11 @@ export class SourceComment extends SourceLine implements SourceToken { public toString( maximalLength?: number ): string { - const lines: Array = []; + const indent = this.indent, + lines: Array = []; if (maximalLength) { let line: string = '', - match: (RegExpMatchArray|null), words: Array; for (const token of this.tokens) { @@ -155,13 +155,7 @@ export class SourceComment extends SourceLine implements SourceToken { if (line.length + 1 + word.length > maximalLength) { lines.push(line.trimRight()); - match = line.match(/^\s*\*+\s*/); - - if (match && match.groups) { - line = match[0] + word; - } else { - line = word; - } + line = U.pad(indent, word); } else { line += ` ${word}`; } @@ -171,11 +165,11 @@ export class SourceComment extends SourceLine implements SourceToken { } } else { for (const token of this.tokens) { - lines.push(token.text); + lines.push(U.pad(indent, token.text)); } } - return lines.join(this.lineBreak); + return lines.join(this.lineBreak).substr(indent); } diff --git a/sources/SourceDoc.ts b/sources/SourceDoc.ts index 9fc649a..b5faac7 100644 --- a/sources/SourceDoc.ts +++ b/sources/SourceDoc.ts @@ -29,7 +29,7 @@ import SourceToken from './SourceToken'; // eslint-disable-next-line max-len -const tagPattern = /^@(\w+)([ \t]+\{[^\}\s]+\})?([ \t]+\S+)?(\s[\s\S]*)?$/u; +const tagPattern = /^@(\S+)([ \t]+\{[^\}\s]+\})?([ \t]+\S+)?(\s[\s\S]*)?$/u; /* * From 434d01d64918f6858bc9aecfbe15d6d79727a3c4 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 18:27:07 +0200 Subject: [PATCH 50/54] Fixed token positions in line. --- lib/SourceLine.js | 2 +- sources/SourceLine.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/SourceLine.js b/lib/SourceLine.js index de90762..1e44d8f 100644 --- a/lib/SourceLine.js +++ b/lib/SourceLine.js @@ -96,7 +96,7 @@ class SourceLine { position.start += tokenText.length; } position.column = position.start + 1; - position.end = token.text.length; + position.end = position.start + token.text.length; return position; } getWrappedLines() { diff --git a/sources/SourceLine.ts b/sources/SourceLine.ts index 25f1e14..e7ce98d 100644 --- a/sources/SourceLine.ts +++ b/sources/SourceLine.ts @@ -169,7 +169,7 @@ export class SourceLine { } position.column = position.start + 1; - position.end = token.text.length; + position.end = position.start + token.text.length; return position; } From f5f5e79255cbb49589dadfee6f0f6df095167d21 Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 19:30:02 +0200 Subject: [PATCH 51/54] Limited reports to avoid range mismatch. --- lib/RuleContext.js | 14 +--------- lib/rules/generic-array-type.js | 19 +++++--------- lib/rules/no-optional-chaining.js | 3 ++- sources/RuleContext.ts | 20 +------------- sources/rules/generic-array-type.ts | 38 ++++++++------------------- sources/rules/no-optional-chaining.ts | 6 ++++- 6 files changed, 26 insertions(+), 74 deletions(-) diff --git a/lib/RuleContext.js b/lib/RuleContext.js index d9ef61b..9a0b430 100644 --- a/lib/RuleContext.js +++ b/lib/RuleContext.js @@ -67,20 +67,8 @@ class RuleContext { * Functions * * */ - prepareReport(position, message, fix) { + prepareReport(position, message) { this.preparedReports.push({ - fix, - loc: { - // ESLint needs column zero-based: - column: position.column - 1, - line: position.line - }, - message - }); - } - report(position, message, fix) { - this.esLintContext.report({ - fix, loc: { // ESLint needs column zero-based: column: position.column - 1, diff --git a/lib/rules/generic-array-type.js b/lib/rules/generic-array-type.js index 3e1fe5a..bba62df 100644 --- a/lib/rules/generic-array-type.js +++ b/lib/rules/generic-array-type.js @@ -22,20 +22,11 @@ const optionsSchema = {}; * Functions * * */ -function createFixer(position, identifierToken, openBracketToken, closeBracketToken) { - return () => { - const range = [ - position.start, - (position.start - + identifierToken.text.length - + openBracketToken.text.length - + closeBracketToken.text.length) - ], text = `Array<${identifierToken}>`; - return { range, text }; - }; +function createFixer(context, linesToFix) { + return () => null; } function lint(context) { - const code = context.sourceCode, lines = code.lines; + const code = context.sourceCode, lines = code.lines, linesToFix = []; let firstToken, secondToken, thirdToken, tokens; for (const line of lines) { tokens = line.tokens; @@ -48,10 +39,12 @@ function lint(context) { thirdToken.kind === TS.SyntaxKind.CloseBracketToken) { const position = code.getTokenPosition(line, secondToken); if (position) { - context.report(position, message, createFixer(position, firstToken, secondToken, thirdToken)); + context.prepareReport(position, message); + linesToFix.push(line); } } } } + context.sendReports(createFixer(context, linesToFix)); } module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, true); diff --git a/lib/rules/no-optional-chaining.js b/lib/rules/no-optional-chaining.js index ddda060..0c70edc 100644 --- a/lib/rules/no-optional-chaining.js +++ b/lib/rules/no-optional-chaining.js @@ -37,10 +37,11 @@ function lint(context) { if (firstToken.kind === TS.SyntaxKind.QuestionDotToken) { const position = code.getTokenPosition(line, firstToken); if (position) { - context.report(position, message); + context.prepareReport(position, message); } } } } + context.sendReports(); } module.exports = RuleContext_1.default.setupRuleExport('problem', optionsSchema, optionsDefaults, lint, false); diff --git a/sources/RuleContext.ts b/sources/RuleContext.ts index a155457..38ab83f 100644 --- a/sources/RuleContext.ts +++ b/sources/RuleContext.ts @@ -166,27 +166,9 @@ export class RuleContext { public prepareReport( position: SourcePosition, - message: string, - fix?: ESLint.Rule.ReportFixer + message: string ): void { this.preparedReports.push({ - fix, - loc: { - // ESLint needs column zero-based: - column: position.column - 1, - line: position.line - }, - message - }); - } - - public report ( - position: SourcePosition, - message: string, - fix?: ESLint.Rule.ReportFixer - ): void { - this.esLintContext.report({ - fix, loc: { // ESLint needs column zero-based: column: position.column - 1, diff --git a/sources/rules/generic-array-type.ts b/sources/rules/generic-array-type.ts index 529af3c..26340fd 100644 --- a/sources/rules/generic-array-type.ts +++ b/sources/rules/generic-array-type.ts @@ -19,6 +19,7 @@ import * as ESLint from 'eslint'; import * as TS from 'typescript'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; +import SourceLine from '../SourceLine'; import SourcePosition from '../SourcePosition'; import SourceToken from '../SourceToken'; @@ -58,32 +59,18 @@ const optionsSchema = {}; function createFixer( - position: SourcePosition, - identifierToken: SourceToken, - openBracketToken: SourceToken, - closeBracketToken: SourceToken + context: GenericArrayTypeContext, + linesToFix: Array ): ESLint.Rule.ReportFixer { - return (): (ESLint.Rule.Fix|null) => { - const range: ESLint.AST.Range = [ - position.start, - ( - position.start - + identifierToken.text.length - + openBracketToken.text.length - + closeBracketToken.text.length - ) - ], - text = `Array<${identifierToken}>`; - - return { range, text }; - }; + return (): (ESLint.Rule.Fix|null) => null; } function lint ( context: GenericArrayTypeContext ): void { const code = context.sourceCode, - lines = code.lines; + lines = code.lines, + linesToFix: Array = []; let firstToken: SourceToken, secondToken: SourceToken, @@ -106,21 +93,18 @@ function lint ( const position = code.getTokenPosition(line, secondToken); if (position) { - context.report( + context.prepareReport( position, - message, - createFixer( - position, - firstToken, - secondToken, - thirdToken - ) + message ); + linesToFix.push(line); } } } } + + context.sendReports(createFixer(context, linesToFix)); } /* * diff --git a/sources/rules/no-optional-chaining.ts b/sources/rules/no-optional-chaining.ts index b2d3e5f..691b31a 100644 --- a/sources/rules/no-optional-chaining.ts +++ b/sources/rules/no-optional-chaining.ts @@ -43,8 +43,10 @@ const message = [ 'Instead use the `pick` function or `&&` conditions.' ].join(' '); + const optionsDefaults: RuleOptions = {}; + const optionsSchema = {}; @@ -79,12 +81,14 @@ function lint ( const position = code.getTokenPosition(line, firstToken); if (position) { - context.report(position, message); + context.prepareReport(position, message); } } } } + + context.sendReports(); } /* * From 22f9fe8c357ea271a98754792db3416037b31eae Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 19:30:11 +0200 Subject: [PATCH 52/54] Fixed no-import-type fix. --- lib/rules/no-import-type.js | 36 ++++++++++++++----- sources/rules/no-import-type.ts | 61 ++++++++++++++++++++++++++------- 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/lib/rules/no-import-type.js b/lib/rules/no-import-type.js index 6f000b7..efa8b84 100644 --- a/lib/rules/no-import-type.js +++ b/lib/rules/no-import-type.js @@ -22,28 +22,48 @@ const optionsSchema = {}; * Functions * * */ -function createFixer(firstPosition, secondPosition) { - return () => ({ - range: [firstPosition.end, secondPosition.end], - text: '' - }); +function createFixer(context, linesToFix) { + return () => { + const code = context.sourceCode, fix = [], range = [0, code.raw.length + 256], lines = code.lines; + let firstToken, secondToken, thirdToken, tokens; + for (const l of lines) { + if (linesToFix.includes(l)) { + tokens = l.tokens; + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i + 1]; + thirdToken = tokens[i + 2]; + if (firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.WhitespaceTrivia && + thirdToken.kind === TS.SyntaxKind.TypeKeyword) { + tokens.splice(i + 1, 2); + iEnd = tokens.length - 2; + } + } + } + fix.push(l.toString()); + } + return { range, text: fix.join(code.lineBreak) }; + }; } function lint(context) { - const code = context.sourceCode, lines = code.lines; + const code = context.sourceCode, lines = code.lines, linesToFix = []; let firstToken, secondToken, tokens; for (const line of lines) { tokens = line.getEssentialTokens(); - for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + for (let i = 0, iEnd = tokens.length - 1; i < iEnd; ++i) { firstToken = tokens[i]; secondToken = tokens[i + 1]; if (firstToken.kind === TS.SyntaxKind.ImportKeyword && secondToken.kind === TS.SyntaxKind.TypeKeyword) { const firstPosition = code.getTokenPosition(line, firstToken), secondPosition = code.getTokenPosition(line, secondToken); if (firstPosition && secondPosition) { - context.report(secondPosition, message, createFixer(firstPosition, secondPosition)); + context.prepareReport(secondPosition, message); + linesToFix.push(line); } } } } + context.sendReports(createFixer(context, linesToFix)); } module.exports = RuleContext_1.default.setupRuleExport('layout', optionsSchema, optionsDefaults, lint, true); diff --git a/sources/rules/no-import-type.ts b/sources/rules/no-import-type.ts index d781ff9..279d373 100644 --- a/sources/rules/no-import-type.ts +++ b/sources/rules/no-import-type.ts @@ -19,7 +19,7 @@ import * as ESLint from 'eslint'; import * as TS from 'typescript'; import RuleContext from '../RuleContext'; import RuleOptions from '../RuleOptions'; -import SourcePosition from '../SourcePosition'; +import SourceLine from '../SourceLine'; import SourceToken from '../SourceToken'; @@ -58,20 +58,54 @@ const optionsSchema = {}; function createFixer( - firstPosition: SourcePosition, - secondPosition: SourcePosition + context: NoImportTypeTypeContext, + linesToFix: Array ): ESLint.Rule.ReportFixer { - return (): ESLint.Rule.Fix => ({ - range: [firstPosition.end, secondPosition.end], - text: '' - }); + return (): ESLint.Rule.Fix => { + const code = context.sourceCode, + fix: Array = [], + range: ESLint.AST.Range = [ 0, code.raw.length + 256 ], + lines = code.lines; + + let firstToken: SourceToken, + secondToken: SourceToken, + thirdToken: SourceToken, + tokens: Array; + + for (const l of lines) { + + if (linesToFix.includes(l)) { + tokens = l.tokens; + + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i+1]; + thirdToken = tokens[i+2]; + + if ( + firstToken.kind === TS.SyntaxKind.ImportKeyword && + secondToken.kind === TS.SyntaxKind.WhitespaceTrivia && + thirdToken.kind === TS.SyntaxKind.TypeKeyword + ) { + tokens.splice(i + 1, 2); + iEnd = tokens.length - 2; + } + } + } + + fix.push(l.toString()); + } + + return { range, text: fix.join(code.lineBreak) }; + }; } function lint ( context: NoImportTypeTypeContext ): void { const code = context.sourceCode, - lines = code.lines; + lines = code.lines, + linesToFix: Array = []; let firstToken: SourceToken, secondToken: SourceToken, @@ -80,7 +114,7 @@ function lint ( for (const line of lines) { tokens = line.getEssentialTokens(); - for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + for (let i = 0, iEnd = tokens.length - 1; i < iEnd; ++i) { firstToken = tokens[i]; secondToken = tokens[i+1]; @@ -92,16 +126,17 @@ function lint ( secondPosition = code.getTokenPosition(line, secondToken); if (firstPosition && secondPosition) { - context.report( + context.prepareReport( secondPosition, - message, - createFixer(firstPosition, secondPosition) + message ); + linesToFix.push(line); } } - } } + + context.sendReports(createFixer(context, linesToFix)); } /* * From 2f32e16a2332176c8559cc367dbe2d50818d3f6b Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Thu, 12 May 2022 19:45:46 +0200 Subject: [PATCH 53/54] Fixed generic-array-type fix. --- lib/rules/generic-array-type.js | 53 +++++++++++++++++-- sources/rules/generic-array-type.ts | 81 ++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 10 deletions(-) diff --git a/lib/rules/generic-array-type.js b/lib/rules/generic-array-type.js index bba62df..dc176a1 100644 --- a/lib/rules/generic-array-type.js +++ b/lib/rules/generic-array-type.js @@ -23,7 +23,54 @@ const optionsSchema = {}; * * */ function createFixer(context, linesToFix) { - return () => null; + return () => { + const code = context.sourceCode, fix = [], range = [0, code.raw.length + 256], lines = code.lines; + let firstToken, secondToken, thirdToken, tokenReplacements, tokens; + for (const l of lines) { + if (linesToFix.includes(l)) { + tokens = l.tokens; + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i + 1]; + thirdToken = tokens[i + 2]; + if (isMatch(firstToken, secondToken, thirdToken)) { + tokenReplacements = [{ + kind: TS.SyntaxKind.Identifier, + text: 'Array' + }, { + kind: TS.SyntaxKind.LessThanToken, + text: '<' + }, + firstToken, + { + kind: TS.SyntaxKind.GreaterThanToken, + text: '>' + }]; + tokens.splice(i, 3, ...tokenReplacements); + iEnd = tokens.length - 2; + } + } + } + fix.push(l.toString()); + } + return { range, text: fix.join(code.lineBreak) }; + }; +} +function isMatch(firstToken, secondToken, thirdToken) { + return (secondToken.kind === TS.SyntaxKind.OpenBracketToken && + thirdToken.kind === TS.SyntaxKind.CloseBracketToken && + (firstToken.kind === TS.SyntaxKind.AnyKeyword || + firstToken.kind === TS.SyntaxKind.BooleanKeyword || + firstToken.kind === TS.SyntaxKind.BigIntKeyword || + firstToken.kind === TS.SyntaxKind.CloseBracketToken || + firstToken.kind === TS.SyntaxKind.NullKeyword || + firstToken.kind === TS.SyntaxKind.NumberKeyword || + firstToken.kind === TS.SyntaxKind.ObjectKeyword || + firstToken.kind === TS.SyntaxKind.StringKeyword || + firstToken.kind === TS.SyntaxKind.SymbolKeyword || + firstToken.kind === TS.SyntaxKind.UndefinedKeyword || + firstToken.kind === TS.SyntaxKind.UnknownKeyword || + firstToken.kind === TS.SyntaxKind.VoidKeyword)); } function lint(context) { const code = context.sourceCode, lines = code.lines, linesToFix = []; @@ -34,9 +81,7 @@ function lint(context) { firstToken = tokens[i]; secondToken = tokens[i + 1]; thirdToken = tokens[i + 2]; - if (firstToken.kind === TS.SyntaxKind.Identifier && - secondToken.kind === TS.SyntaxKind.OpenBracketToken && - thirdToken.kind === TS.SyntaxKind.CloseBracketToken) { + if (isMatch(firstToken, secondToken, thirdToken)) { const position = code.getTokenPosition(line, secondToken); if (position) { context.prepareReport(position, message); diff --git a/sources/rules/generic-array-type.ts b/sources/rules/generic-array-type.ts index 26340fd..a78a309 100644 --- a/sources/rules/generic-array-type.ts +++ b/sources/rules/generic-array-type.ts @@ -62,9 +62,82 @@ function createFixer( context: GenericArrayTypeContext, linesToFix: Array ): ESLint.Rule.ReportFixer { - return (): (ESLint.Rule.Fix|null) => null; + return (): (ESLint.Rule.Fix|null) => { + const code = context.sourceCode, + fix: Array = [], + range: ESLint.AST.Range = [ 0, code.raw.length + 256 ], + lines = code.lines; + + let firstToken: SourceToken, + secondToken: SourceToken, + thirdToken: SourceToken, + tokenReplacements: Array, + tokens: Array; + + for (const l of lines) { + + if (linesToFix.includes(l)) { + tokens = l.tokens; + + for (let i = 0, iEnd = tokens.length - 2; i < iEnd; ++i) { + firstToken = tokens[i]; + secondToken = tokens[i+1]; + thirdToken = tokens[i+2]; + + if (isMatch(firstToken, secondToken, thirdToken)) { + tokenReplacements = [{ + kind: TS.SyntaxKind.Identifier, + text: 'Array' + }, { + kind: TS.SyntaxKind.LessThanToken, + text: '<' + }, + firstToken, + { + kind: TS.SyntaxKind.GreaterThanToken, + text: '>' + }]; + tokens.splice(i, 3, ...tokenReplacements); + iEnd = tokens.length - 2; + } + } + } + + fix.push(l.toString()); + } + + return { range, text: fix.join(code.lineBreak) }; + }; } + +function isMatch( + firstToken: SourceToken, + secondToken: SourceToken, + thirdToken: SourceToken +): boolean { + return ( + secondToken.kind === TS.SyntaxKind.OpenBracketToken && + thirdToken.kind === TS.SyntaxKind.CloseBracketToken && + ( + firstToken.kind === TS.SyntaxKind.AnyKeyword || + firstToken.kind === TS.SyntaxKind.BooleanKeyword || + firstToken.kind === TS.SyntaxKind.BigIntKeyword || + firstToken.kind === TS.SyntaxKind.CloseBracketToken || + firstToken.kind === TS.SyntaxKind.NullKeyword || + firstToken.kind === TS.SyntaxKind.NumberKeyword || + firstToken.kind === TS.SyntaxKind.ObjectKeyword || + firstToken.kind === TS.SyntaxKind.StringKeyword || + firstToken.kind === TS.SyntaxKind.SymbolKeyword || + firstToken.kind === TS.SyntaxKind.UndefinedKeyword || + firstToken.kind === TS.SyntaxKind.UnknownKeyword || + firstToken.kind === TS.SyntaxKind.VoidKeyword + ) + ); +} + + + function lint ( context: GenericArrayTypeContext ): void { @@ -85,11 +158,7 @@ function lint ( secondToken = tokens[i+1]; thirdToken = tokens[i+2]; - if ( - firstToken.kind === TS.SyntaxKind.Identifier && - secondToken.kind === TS.SyntaxKind.OpenBracketToken && - thirdToken.kind === TS.SyntaxKind.CloseBracketToken - ) { + if (isMatch(firstToken, secondToken, thirdToken)) { const position = code.getTokenPosition(line, secondToken); if (position) { From 5431917db6bf5f78e5de5611df1aa5359746e31d Mon Sep 17 00:00:00 2001 From: bre1470 <40056287+bre1470@users.noreply.github.com> Date: Fri, 13 May 2022 12:34:48 +0200 Subject: [PATCH 54/54] Fixed handling of generic and union. --- lib/rules/generic-array-type.js | 2 ++ sources/rules/generic-array-type.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/rules/generic-array-type.js b/lib/rules/generic-array-type.js index dc176a1..e2375c4 100644 --- a/lib/rules/generic-array-type.js +++ b/lib/rules/generic-array-type.js @@ -63,6 +63,8 @@ function isMatch(firstToken, secondToken, thirdToken) { firstToken.kind === TS.SyntaxKind.BooleanKeyword || firstToken.kind === TS.SyntaxKind.BigIntKeyword || firstToken.kind === TS.SyntaxKind.CloseBracketToken || + firstToken.kind === TS.SyntaxKind.CloseParenToken || + firstToken.kind === TS.SyntaxKind.GreaterThanToken || firstToken.kind === TS.SyntaxKind.NullKeyword || firstToken.kind === TS.SyntaxKind.NumberKeyword || firstToken.kind === TS.SyntaxKind.ObjectKeyword || diff --git a/sources/rules/generic-array-type.ts b/sources/rules/generic-array-type.ts index a78a309..28335d8 100644 --- a/sources/rules/generic-array-type.ts +++ b/sources/rules/generic-array-type.ts @@ -124,6 +124,8 @@ function isMatch( firstToken.kind === TS.SyntaxKind.BooleanKeyword || firstToken.kind === TS.SyntaxKind.BigIntKeyword || firstToken.kind === TS.SyntaxKind.CloseBracketToken || + firstToken.kind === TS.SyntaxKind.CloseParenToken || + firstToken.kind === TS.SyntaxKind.GreaterThanToken || firstToken.kind === TS.SyntaxKind.NullKeyword || firstToken.kind === TS.SyntaxKind.NumberKeyword || firstToken.kind === TS.SyntaxKind.ObjectKeyword ||