diff --git a/.eslintignore b/.eslintignore index 51659e4..08e9ecc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ test test/reports +dist diff --git a/.eslintrc b/.eslintrc index 2165a31..41cf7d2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,9 +1,14 @@ { - "extends": ["eslint:recommended", "plugin:import/errors"], - "plugins": ["import"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/errors", + "plugin:import/typescript" + ], + "plugins": ["import", "@typescript-eslint"], "rules": { "no-console": ["error", { "allow": ["error"] }], - "no-unused-vars": [ + "@typescript-eslint/no-unused-vars": [ "error", { "argsIgnorePattern": "^_", "ignoreRestSiblings": true } ], @@ -12,5 +17,9 @@ "env": { "es2020": true, "node": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module" } } diff --git a/.gitignore b/.gitignore index dc89ffc..506ea0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules coverage test/reports +dist .DS_Store \ No newline at end of file diff --git a/@types/escodegen-wallaby/index.d.ts b/@types/escodegen-wallaby/index.d.ts new file mode 100644 index 0000000..8cdd8ac --- /dev/null +++ b/@types/escodegen-wallaby/index.d.ts @@ -0,0 +1 @@ +declare module "escodegen-wallaby"; diff --git a/bin/react-scanner b/bin/react-scanner index 52351e6..661300c 100755 --- a/bin/react-scanner +++ b/bin/react-scanner @@ -1,3 +1,3 @@ #!/usr/bin/env node -require('../src/index'); \ No newline at end of file +require("../dist/src/index"); diff --git a/package-lock.json b/package-lock.json index c2a7616..408d1dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.1.0", "license": "MIT", "dependencies": { + "@mihkeleidast/astray": "^1.2.0", "@typescript-eslint/typescript-estree": "5.20.0", "astray": "1.1.1", "dlv": "1.1.3", @@ -17,19 +18,24 @@ "is-plain-object": "5.0.0", "picomatch": "2.3.1", "sade": "1.8.1", - "typescript": "4.6.3" + "typescript": "^4.8.4" }, "bin": { "react-scanner": "bin/react-scanner" }, "devDependencies": { + "@types/dlv": "^1.1.2", + "@types/node": "^16.11.64", + "@typescript-eslint/eslint-plugin": "^5.39.0", + "@typescript-eslint/parser": "^5.39.0", "c8": "7.12.0", "escodegen-wallaby": "1.6.35", - "eslint": "8.23.1", + "eslint": "^8.23.1", "eslint-plugin-import": "2.26.0", "execa": "5.0.0", "husky": "7.0.4", "prettier": "2.7.1", + "ts-node": "^10.9.1", "uvu": "0.5.6", "watchlist": "0.3.1" }, @@ -43,6 +49,28 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", @@ -143,6 +171,24 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mihkeleidast/astray": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mihkeleidast/astray/-/astray-1.2.0.tgz", + "integrity": "sha512-MgPRNsRduS73MMiwi8RMsF8LNd7BqB099FnayLkKfb5QMQfl95DLGTuB1XSHns/ED63AmO8DNTwi0CZBbvg4Gg==", + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0" + } + }, + "node_modules/@mihkeleidast/astray/node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "optional": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -175,24 +221,322 @@ "node": ">= 8" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/dlv": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/dlv/-/dlv-1.1.2.tgz", + "integrity": "sha512-OyiZ3jEKu7RtGO1yp9oOdK0cTwZ/10oE9PDJ6fyN3r9T5wkyOcvr6awdugjYdqF6KVO5eUvt7jx7rk2Eylufow==", + "dev": true + }, "node_modules/@types/estree": { "version": "0.0.45", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", "optional": true }, + "node_modules/@types/estree-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.0.tgz", + "integrity": "sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==", + "optional": true, + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/node": { + "version": "16.11.64", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.64.tgz", + "integrity": "sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz", + "integrity": "sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/type-utils": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "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/parser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.39.0.tgz", + "integrity": "sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "debug": "^4.3.4" + }, + "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/parser/node_modules/@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "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/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.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/scope-manager": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz", + "integrity": "sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.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/scope-manager/node_modules/@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "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/scope-manager/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.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/type-utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz", + "integrity": "sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "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": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "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/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.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.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", @@ -231,6 +575,109 @@ } } }, + "node_modules/@typescript-eslint/utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.39.0.tgz", + "integrity": "sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.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": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "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/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.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/utils/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==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", @@ -268,6 +715,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -318,6 +774,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -515,6 +977,12 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1977,6 +2445,12 @@ "semver": "bin/semver.js" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2620,6 +3094,58 @@ "node": ">=8.0" } }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -2676,9 +3202,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.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2729,6 +3255,12 @@ "node": ">=8" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -2862,6 +3394,15 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -2882,6 +3423,27 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, "@eslint/eslintrc": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", @@ -2956,6 +3518,23 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@mihkeleidast/astray": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mihkeleidast/astray/-/astray-1.2.0.tgz", + "integrity": "sha512-MgPRNsRduS73MMiwi8RMsF8LNd7BqB099FnayLkKfb5QMQfl95DLGTuB1XSHns/ED63AmO8DNTwi0CZBbvg4Gg==", + "requires": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0" + }, + "dependencies": { + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "optional": true + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2979,24 +3558,209 @@ "fastq": "^1.6.0" } }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/dlv": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/dlv/-/dlv-1.1.2.tgz", + "integrity": "sha512-OyiZ3jEKu7RtGO1yp9oOdK0cTwZ/10oE9PDJ6fyN3r9T5wkyOcvr6awdugjYdqF6KVO5eUvt7jx7rk2Eylufow==", + "dev": true + }, "@types/estree": { "version": "0.0.45", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", "optional": true }, + "@types/estree-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.0.tgz", + "integrity": "sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==", + "optional": true, + "requires": { + "@types/estree": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/node": { + "version": "16.11.64", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.64.tgz", + "integrity": "sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz", + "integrity": "sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/type-utils": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.39.0.tgz", + "integrity": "sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.3.0" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz", + "integrity": "sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.3.0" + } + } + } + }, + "@typescript-eslint/type-utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz", + "integrity": "sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.3.0" + } + } + } + }, "@typescript-eslint/types": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", @@ -3016,6 +3780,69 @@ "tsutils": "^3.21.0" } }, + "@typescript-eslint/utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.39.0.tgz", + "integrity": "sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "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==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, "@typescript-eslint/visitor-keys": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", @@ -3038,6 +3865,12 @@ "dev": true, "requires": {} }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3072,6 +3905,12 @@ "color-convert": "^2.0.1" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3227,6 +4066,12 @@ "safe-buffer": "~5.1.1" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4307,6 +5152,12 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4738,6 +5589,35 @@ "is-number": "^7.0.0" } }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -4779,9 +5659,9 @@ "dev": true }, "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==" + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==" }, "unbox-primitive": { "version": "1.0.1", @@ -4816,6 +5696,12 @@ "sade": "^1.7.3" } }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -4913,6 +5799,12 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 72290e3..ab43942 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,23 @@ "version": "1.1.0", "description": "Extract React components and props usage from code.", "bin": "bin/react-scanner", - "main": "src/scanner.js", + "main": "dist/src/scanner.js", + "types": "dist/src/scanner.d.ts", "scripts": { "prepare": "husky install", - "build": "node scripts/processors.js", - "lint": "eslint --max-warnings 0 \"**/*.js\"", - "prettier": "prettier --write \"**/*.{js,json,md}\"", - "test": "uvu src test", + "prebuild": "rimraf dist", + "build:processors": "ts-node scripts/processors.ts", + "build:lib": "tsc --project tsconfig.build.json", + "build": "npm run build:processors && npm run build:lib", + "lint": "eslint --max-warnings 0 \"**/*.{ts,js}\"", + "prettier": "prettier --write \"**/*.{ts,js,json,md}\"", + "test": "uvu -r ts-node/register src test", "test:watch": "watchlist src -- npm t", - "test:coverage": "c8 --include=src/**/*.js -o coverage --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 npm t", + "test:coverage": "c8 --include=src/**/*.ts -o coverage --check-coverage --branches 40 --functions 100 --lines 70 --statements 70 npm t", "prepublishOnly": "npm run build" }, "dependencies": { + "@mihkeleidast/astray": "^1.2.0", "@typescript-eslint/typescript-estree": "5.20.0", "astray": "1.1.1", "dlv": "1.1.3", @@ -23,16 +28,21 @@ "is-plain-object": "5.0.0", "picomatch": "2.3.1", "sade": "1.8.1", - "typescript": "4.6.3" + "typescript": "^4.8.4" }, "devDependencies": { + "@types/dlv": "^1.1.2", + "@types/node": "^16.11.64", + "@typescript-eslint/eslint-plugin": "^5.39.0", + "@typescript-eslint/parser": "^5.39.0", "c8": "7.12.0", "escodegen-wallaby": "1.6.35", - "eslint": "8.23.1", + "eslint": "^8.23.1", "eslint-plugin-import": "2.26.0", "execa": "5.0.0", "husky": "7.0.4", "prettier": "2.7.1", + "ts-node": "^10.9.1", "uvu": "0.5.6", "watchlist": "0.3.1" }, diff --git a/scripts/processors.js b/scripts/processors.js deleted file mode 100644 index 69f0f31..0000000 --- a/scripts/processors.js +++ /dev/null @@ -1,19 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const { fdir } = require("fdir"); - -const processorsMap = new fdir() - .glob(["**/!(*.test).js"]) - .crawl(path.resolve(__dirname, "../src/processors")) - .sync() - .map((file) => path.parse(file).name) - .sort() - .reduce((acc, processor) => { - acc[processor] = true; - return acc; - }, {}); - -fs.writeFileSync( - path.resolve(__dirname, "../src/processors/processors.json"), - JSON.stringify(processorsMap, null, 2) + "\n" -); diff --git a/scripts/processors.ts b/scripts/processors.ts new file mode 100644 index 0000000..e423f79 --- /dev/null +++ b/scripts/processors.ts @@ -0,0 +1,25 @@ +import fs from "fs"; +import path from "path"; +import { fdir } from "fdir"; + +const processorFiles = new fdir() + .glob("**/!(*.test).ts") + .crawl(path.resolve(__dirname, "../src/processors")) + .sync(); + +if (Array.isArray(processorFiles)) { + const processorsMap = processorFiles + .map((file) => + typeof file === "string" ? path.parse(file).name : file.dir + ) + .sort() + .reduce>((acc, processor) => { + acc[processor] = true; + return acc; + }, {}); + + fs.writeFileSync( + path.resolve(__dirname, "../src/processors/processors.json"), + JSON.stringify(processorsMap, null, 2) + "\n" + ); +} diff --git a/src/index.test.js b/src/index.test.ts similarity index 95% rename from src/index.test.js rename to src/index.test.ts index 63158bc..a2327b0 100644 --- a/src/index.test.js +++ b/src/index.test.ts @@ -1,18 +1,18 @@ -const fs = require("fs"); -const path = require("path"); -const execa = require("execa"); -const { suite } = require("uvu"); -const assert = require("uvu/assert"); +import fs from "fs"; +import path from "path"; +import execa from "execa"; +import { suite } from "uvu"; +import assert from "uvu/assert"; const Index = suite("index"); -function parseStdout(stdout) { +function parseStdout(stdout: string) { const firstLineBreakIndex = stdout.indexOf("\n"); if (firstLineBreakIndex === -1) { return { firstLine: stdout, - restOutput: null, + restOutput: "", }; } diff --git a/src/index.js b/src/index.ts similarity index 69% rename from src/index.js rename to src/index.ts index e3e2396..14f4c4c 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,7 +1,7 @@ -const path = require("path"); -const sade = require("sade"); -const { run } = require("./scanner"); -const packageJson = require("../package.json"); +import path from "path"; +import sade from "sade"; +import { run } from "./scanner"; +import packageJson from "../package.json"; sade("react-scanner", true) .version(packageJson.version) @@ -11,6 +11,7 @@ sade("react-scanner", true) .action((options) => { const configPath = path.resolve(process.cwd(), options.config); const configDir = path.dirname(configPath); + // eslint-disable-next-line @typescript-eslint/no-var-requires const config = require(configPath); run(config, configDir, "cli"); }) diff --git a/src/processors/count-components-and-props.js b/src/processors/count-components-and-props.js deleted file mode 100644 index 5086fec..0000000 --- a/src/processors/count-components-and-props.js +++ /dev/null @@ -1,40 +0,0 @@ -const countComponentsAndPropsProcessor = - (options) => - ({ forEachComponent, sortObjectKeysByValue, output }) => { - let result = {}; - - forEachComponent(({ componentName, component }) => { - const { instances } = component; - - if (!instances) { - return; - } - - result[componentName] = { - instances: instances.length, - props: {}, - }; - - instances.forEach((instance) => { - for (const prop in instance.props) { - if (result[componentName].props[prop] === undefined) { - result[componentName].props[prop] = 0; - } - - result[componentName].props[prop] += 1; - } - }); - - result[componentName].props = sortObjectKeysByValue( - result[componentName].props - ); - }); - - result = sortObjectKeysByValue(result, (component) => component.instances); - - output(result, options && options.outputTo); - - return result; - }; - -module.exports = countComponentsAndPropsProcessor; diff --git a/src/processors/count-components-and-props.ts b/src/processors/count-components-and-props.ts new file mode 100644 index 0000000..8ad30f6 --- /dev/null +++ b/src/processors/count-components-and-props.ts @@ -0,0 +1,51 @@ +import type { ProcessorFunctionArgs, ProcessorOptions } from "../types"; + +type ResultData = { + instances: number; + props: Record; +}; + +const countComponentsAndPropsProcessor = + (options: ProcessorOptions) => + ({ + forEachComponent, + sortObjectKeysByValue, + output, + }: ProcessorFunctionArgs) => { + let result: Record = {}; + + forEachComponent(({ componentName, component }) => { + const { instances } = component; + + if (!instances) { + return; + } + + const componentResult: ResultData = { + instances: instances.length, + props: {}, + }; + + instances.forEach((instance) => { + for (const prop in instance.props) { + if (componentResult.props[prop] === undefined) { + componentResult.props[prop] = 0; + } + + componentResult.props[prop] += 1; + } + }); + + componentResult.props = sortObjectKeysByValue(componentResult.props); + + result[componentName] = componentResult; + }); + + result = sortObjectKeysByValue(result, (component) => component.instances); + + output(result, options && options.outputTo); + + return result; + }; + +export default countComponentsAndPropsProcessor; diff --git a/src/processors/count-components.js b/src/processors/count-components.ts similarity index 52% rename from src/processors/count-components.js rename to src/processors/count-components.ts index 4edfcbc..990af63 100644 --- a/src/processors/count-components.js +++ b/src/processors/count-components.ts @@ -1,7 +1,13 @@ +import type { ProcessorFunctionArgs, ProcessorOptions } from "../types"; + const countComponentsProcessor = - (options) => - ({ forEachComponent, sortObjectKeysByValue, output }) => { - let result = {}; + (options: ProcessorOptions | undefined) => + ({ + forEachComponent, + sortObjectKeysByValue, + output, + }: ProcessorFunctionArgs) => { + let result: Record = {}; forEachComponent(({ componentName, component }) => { const { instances } = component; @@ -18,4 +24,4 @@ const countComponentsProcessor = return result; }; -module.exports = countComponentsProcessor; +export default countComponentsProcessor; diff --git a/src/processors/raw-report.js b/src/processors/raw-report.js deleted file mode 100644 index 1040682..0000000 --- a/src/processors/raw-report.js +++ /dev/null @@ -1,9 +0,0 @@ -const rawReportProcessor = - (options) => - ({ report, output }) => { - output(report, options && options.outputTo); - - return report; - }; - -module.exports = rawReportProcessor; diff --git a/src/processors/raw-report.ts b/src/processors/raw-report.ts new file mode 100644 index 0000000..4737bec --- /dev/null +++ b/src/processors/raw-report.ts @@ -0,0 +1,11 @@ +import type { ProcessorFunctionArgs, ProcessorOptions } from "../types"; + +const rawReportProcessor = + (options: ProcessorOptions | undefined) => + ({ report, output }: ProcessorFunctionArgs) => { + output(report, options && options.outputTo); + + return report; + }; + +export default rawReportProcessor; diff --git a/src/run.js b/src/run.ts similarity index 59% rename from src/run.js rename to src/run.ts index 11133fb..bc4d6dd 100644 --- a/src/run.js +++ b/src/run.ts @@ -1,25 +1,35 @@ -const fs = require("fs"); -const path = require("path"); -const { fdir } = require("fdir"); -const { isPlainObject } = require("is-plain-object"); -const scan = require("./scan"); -const { +import fs from "fs"; +import path from "path"; +import { fdir } from "fdir"; +import { isPlainObject } from "is-plain-object"; +import scan from "./scan"; +import { pluralize, forEachComponent, sortObjectKeysByValue, getExcludeFn, -} = require("./utils"); +} from "./utils"; +import type { Config, ProcessorFunction, ProcessorName, Report } from "./types"; const DEFAULT_GLOBS = ["**/!(*.test|*.spec).@(js|ts)?(x)"]; -const DEFAULT_PROCESSORS = ["count-components-and-props"]; +const DEFAULT_PROCESSORS: ProcessorName[] = ["count-components-and-props"]; + +export type RunMethod = "cli" | "programmatic"; +type RunArgs = { + config: Config; + configDir: string | undefined; + crawlFrom: string; + startTime: bigint; + method: RunMethod; +}; async function run({ config, - configDir, + configDir = "", crawlFrom, startTime, method = "cli", -}) { +}: RunArgs) { const rootDir = config.rootDir || configDir; const globs = config.globs || DEFAULT_GLOBS; const files = new fdir() @@ -29,12 +39,17 @@ async function run({ .crawl(crawlFrom) .sync(); + if (!Array.isArray(files)) { + console.error(`Something went wrong.`); + process.exit(1); + } + if (files.length === 0) { console.error(`No files found to scan.`); process.exit(1); } - let report = {}; + const report: Report = {}; const { components, includeSubComponents, @@ -45,6 +60,7 @@ async function run({ for (let i = 0, len = files.length; i < len; i++) { const filePath = files[i]; + if (typeof filePath !== "string") continue; const code = fs.readFileSync(filePath, "utf8"); scan({ @@ -72,8 +88,8 @@ async function run({ config.processors && config.processors.length > 0 ? config.processors : DEFAULT_PROCESSORS; - const prevResults = []; - const output = (data, destination) => { + const prevResults: unknown[] = []; + const output = (data: unknown, destination?: string) => { const defaultDestination = method === "cli" ? "stdout" : "return"; const dest = destination || defaultDestination; const dataStr = isPlainObject(data) @@ -90,7 +106,7 @@ async function run({ break; } default: { - const filePath = path.resolve(rootDir, destination); + const filePath = path.resolve(rootDir, dest); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, dataStr); @@ -99,29 +115,33 @@ async function run({ }; for (const processor of processors) { - let processorFn; + let processorFn: ProcessorFunction | undefined = undefined; if (typeof processor === "string") { - processorFn = require(`./processors/${processor}`)(); + processorFn = (await import(`./processors/${processor}`)).default(); } else if (Array.isArray(processor)) { - processorFn = require(`./processors/${processor[0]}`)(processor[1]); + processorFn = (await import(`./processors/${processor[0]}`)).default( + processor[1] + ); } else if (typeof processor === "function") { processorFn = processor; } - const result = await processorFn({ - report, - prevResults, - prevResult: prevResults[prevResults.length - 1], - forEachComponent: forEachComponent(report), - sortObjectKeysByValue, - output, - }); - - prevResults.push(result); + if (processorFn) { + const result: unknown = await processorFn({ + report, + prevResults, + prevResult: prevResults[prevResults.length - 1], + forEachComponent: forEachComponent(report), + sortObjectKeysByValue: sortObjectKeysByValue, + output, + }); + + prevResults.push(result); + } } return prevResults[prevResults.length - 1]; } -module.exports = run; +export default run; diff --git a/src/scan.test.js b/src/scan.test.ts similarity index 93% rename from src/scan.test.js rename to src/scan.test.ts index be5423d..996a32e 100644 --- a/src/scan.test.js +++ b/src/scan.test.ts @@ -1,21 +1,28 @@ -const { suite } = require("uvu"); -const escodegen = require("escodegen-wallaby"); -const assert = require("uvu/assert"); -const scan = require("./scan"); +import { Context, suite } from "uvu"; +import escodegen from "escodegen-wallaby"; +import assert from "uvu/assert"; +import scan from "./scan"; +import type { Config } from "./types"; -const Scan = suite("scan"); +interface ScanContext extends Context { + getReport: + | ((filePath: string, code: string, config?: Config | undefined) => unknown) + | undefined; +} + +const Scan = suite("scan"); Scan.before((context) => { context.getReport = ( - filePath, - code, + filePath: string, + code: string, { components, includeSubComponents, importedFrom, getComponentName, getPropValue, - } = {} + }: Config = {} ) => { const report = {}; @@ -46,13 +53,13 @@ Scan.after((context) => { Scan("invalid code", ({ getReport }) => { const originalConsoleError = global.console.error; - let errors = []; + let errors: string[] = []; global.console.error = (...args) => { errors = errors.concat(args); }; - const report = getReport("invalid-code.js", ` { }); Scan("unknown components", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "unknown-components.js", `
@@ -75,13 +82,16 @@ Scan("unknown components", ({ getReport }) => { }); Scan("ignores comments", ({ getReport }) => { - const report = getReport("ignores-comments.js", `{/* Hello */}`); + const report = getReport?.( + "ignores-comments.js", + `{/* Hello */}` + ); assert.equal(report, {}); }); Scan("self closing", ({ getReport }) => { - const report = getReport("self-closing.js", `
`); + const report = getReport?.("self-closing.js", `
`); assert.equal(report, { Header: { @@ -103,7 +113,7 @@ Scan("self closing", ({ getReport }) => { }); Scan("no props", ({ getReport }) => { - const report = getReport("no-props.js", `Hello`); + const report = getReport?.("no-props.js", `Hello`); assert.equal(report, { Text: { @@ -125,7 +135,7 @@ Scan("no props", ({ getReport }) => { }); Scan("prop with no value", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "prop-with-no-value.js", `Hello` ); @@ -153,7 +163,7 @@ Scan("prop with no value", ({ getReport }) => { }); Scan("props with literal values", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "props-with-literal-values.js", `Hello` ); @@ -182,7 +192,7 @@ Scan("props with literal values", ({ getReport }) => { }); Scan("props with other values", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "props-with-other-values.js", `Hello` ); @@ -210,7 +220,7 @@ Scan("props with other values", ({ getReport }) => { }); Scan("props with custom value formatter", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "props-with-custom-value-formatter.js", `<> e.preventDefault()}/> @@ -223,7 +233,7 @@ Scan("props with custom value formatter", ({ getReport }) => { componentName, defaultGetPropValue, }) => { - if (componentName === "Input" && propName === "style") { + if (node && componentName === "Input" && propName === "style") { if (node.type === "JSXExpressionContainer") { return escodegen.generate(node.expression); } else { @@ -273,7 +283,7 @@ Scan("props with custom value formatter", ({ getReport }) => { }); Scan("with props spread", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "with-props-spread.js", `Hello` ); @@ -298,7 +308,7 @@ Scan("with props spread", ({ getReport }) => { }); Scan("no sub components by default", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "no-sub-components-by-default.js", `
@@ -327,7 +337,7 @@ Scan("no sub components by default", ({ getReport }) => { }); Scan("with sub components", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "with-sub-components.js", ` <> @@ -402,7 +412,7 @@ Scan("with sub components", ({ getReport }) => { }); Scan("deeply nested sub components", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "deeply-nested-sub-components.js", `
@@ -512,7 +522,7 @@ Scan("deeply nested sub components", ({ getReport }) => { }); Scan("ignores non-JSX stuff", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "ignores-non-jsx-stuff.js", ` import React from "react"; @@ -582,7 +592,7 @@ Scan("ignores non-JSX stuff", ({ getReport }) => { }); Scan("typescript", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "typescript.ts", ` /* @jsx jsx */ @@ -681,7 +691,7 @@ Scan("typescript", ({ getReport }) => { }); Scan("not importedFrom", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "not-imported-from.js", ` import Header from "other-design-system"; @@ -695,7 +705,7 @@ Scan("not importedFrom", ({ getReport }) => { }); Scan("importedFrom default export", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-default-export.js", ` import Header from "my-design-system"; @@ -733,7 +743,7 @@ Scan("importedFrom default export", ({ getReport }) => { }); Scan("importedFrom default export as", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-default-export.js", ` import { default as Header } from "my-design-system"; @@ -772,7 +782,7 @@ Scan("importedFrom default export as", ({ getReport }) => { }); Scan("importedFrom named export", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-named-export.js", ` import { Header } from "basis"; @@ -808,7 +818,7 @@ Scan("importedFrom named export", ({ getReport }) => { }); Scan("props with jsx expressions", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-in-prop-jsx.js", ` @@ -847,7 +857,7 @@ Scan("props with jsx expressions", ({ getReport }) => { }); Scan("importedFrom named export with alias", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-named-export-with-alias.js", ` import { Header as MyHeader } from "basis"; @@ -885,7 +895,7 @@ Scan("importedFrom named export with alias", ({ getReport }) => { Scan( "importedFrom named export with alias - sub component", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-named-export-with-alias-sub-component.js", ` import { Header as MyHeader } from "basis"; @@ -937,7 +947,7 @@ Scan( ); Scan("importedFrom entire module", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "imported-from-entire-module.js", ` import * as Basis from "basis"; @@ -982,7 +992,7 @@ Scan("importedFrom entire module", ({ getReport }) => { }); Scan("custom getComponentName", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "custom-get-component-name.js", ` import MyBox from "@my/design-system/Box"; @@ -995,9 +1005,12 @@ Scan("custom getComponentName", ({ getReport }) => { `, { getComponentName: ({ moduleName }) => { + if (typeof moduleName !== "string") return ""; const parts = moduleName.split("/"); - return parts[parts.length - 1]; + if (parts.length === 0) return ""; + + return parts[parts.length - 1] ?? ""; }, } ); @@ -1048,7 +1061,7 @@ Scan("custom getComponentName", ({ getReport }) => { }); Scan("importAlias", ({ getReport }) => { - const report = getReport( + const report = getReport?.( "import-alias.js", ` import Text from "basis"; diff --git a/src/scan.js b/src/scan.ts similarity index 62% rename from src/scan.js rename to src/scan.ts index b932e25..0eb2225 100644 --- a/src/scan.js +++ b/src/scan.ts @@ -1,14 +1,23 @@ -const { parse } = require("@typescript-eslint/typescript-estree"); -const astray = require("astray"); -const getObjectPath = require("dlv"); -const { dset } = require("dset"); +import { AST, parse } from "@typescript-eslint/typescript-estree"; +import astray from "@mihkeleidast/astray"; +import getObjectPath from "dlv"; +import { dset } from "dset"; +import type { JSXOpeningElement } from "estree-jsx"; +import type { + GetComponentNameFunction, + ImportInfo, + GetPropValueFunction, + Report, + PropNode, + InstanceInfo, +} from "./types"; const parseOptions = { loc: true, jsx: true, }; -function getComponentNameFromAST(nameObj) { +function getComponentNameFromAST(nameObj: JSXOpeningElement["name"]): string { switch (nameObj.type) { case "JSXIdentifier": { return nameObj.name; @@ -27,7 +36,7 @@ function getComponentNameFromAST(nameObj) { } } -function getPropValue(node) { +function getPropValue(node: PropNode) { if (node === null) { return null; } @@ -48,28 +57,36 @@ function getPropValue(node) { throw new Error(`Unknown node type: ${node.type}`); } +type GetInstanceInfoArgs = { + node: JSXOpeningElement; + filePath: string; + importInfo: ImportInfo | undefined; + getPropValue: GetPropValueFunction | undefined; + componentName: string; +}; + function getInstanceInfo({ node, filePath, importInfo, getPropValue: customGetPropValue, componentName, -}) { +}: GetInstanceInfoArgs): InstanceInfo { const { attributes } = node; - const result = { + const result: InstanceInfo = { ...(importInfo !== undefined && { importInfo }), props: {}, propsSpread: false, location: { file: filePath, - start: node.name.loc.start, + start: node.name.loc?.start, }, }; for (let i = 0, len = attributes.length; i < len; i++) { const attribute = attributes[i]; - if (attribute.type === "JSXAttribute") { + if (attribute && attribute.type === "JSXAttribute") { const { name, value } = attribute; const propName = name.name; const propValue = customGetPropValue @@ -81,8 +98,8 @@ function getInstanceInfo({ }) : getPropValue(value); - result.props[propName] = propValue; - } else if (attribute.type === "JSXSpreadAttribute") { + result.props[propName.toString()] = propValue; + } else if (attribute && attribute.type === "JSXSpreadAttribute") { result.propsSpread = true; } } @@ -90,6 +107,17 @@ function getInstanceInfo({ return result; } +type ScanArgs = { + code: string; + filePath: string; + components: Record | undefined; + includeSubComponents?: boolean | undefined; + importedFrom?: string | RegExp | undefined; + getComponentName?: GetComponentNameFunction | undefined; + getPropValue?: GetPropValueFunction | undefined; + report: Report; +}; + function scan({ code, filePath, @@ -100,8 +128,8 @@ function scan({ imported === "default" ? local : imported || local, report, getPropValue, -}) { - let ast; +}: ScanArgs) { + let ast: AST>; try { ast = parse(code, parseOptions); @@ -110,7 +138,7 @@ function scan({ return; } - const importsMap = {}; + const importsMap: Record = {}; astray.walk(ast, { ImportDeclaration(node) { @@ -119,29 +147,29 @@ function scan({ const specifiersCount = specifiers.length; for (let i = 0; i < specifiersCount; i++) { - switch (specifiers[i].type) { - case "ImportDefaultSpecifier": - case "ImportSpecifier": - case "ImportNamespaceSpecifier": { - const imported = specifiers[i].imported - ? specifiers[i].imported.name - : null; - const local = specifiers[i].local.name; - - importsMap[local] = { - ...(imported !== null && { imported }), - local, - moduleName, - importType: specifiers[i].type, - }; - break; - } + const spec = specifiers[i]; + if (spec) { + switch (spec.type) { + case "ImportDefaultSpecifier": + case "ImportSpecifier": + case "ImportNamespaceSpecifier": { + const imported = "imported" in spec ? spec.imported.name : null; + const local = spec.local.name; + + importsMap[local] = { + ...(imported !== null && { imported }), + local, + moduleName, + importType: spec.type, + }; + break; + } - /* c8 ignore next 5 */ - default: { - throw new Error( - `Unknown import specifier type: ${specifiers[i].type}` - ); + /* c8 ignore next 5 */ + default: { + // @ts-expect-error expected runtime error + throw new Error(`Unknown import specifier type: ${spec.type}`); + } } } } @@ -151,8 +179,12 @@ function scan({ const name = getComponentNameFromAST(node.name); const nameParts = name.split("."); const [firstPart, ...restParts] = nameParts; - const actualFirstPart = importsMap[firstPart] - ? getComponentName(importsMap[firstPart]) + if (!firstPart) return; + + const importsItem = importsMap[firstPart]; + + const actualFirstPart = importsItem + ? getComponentName(importsItem) : firstPart; const shouldReportComponent = () => { if (components) { @@ -181,13 +213,16 @@ function scan({ } if (importedFrom) { - if (!importsMap[firstPart]) { + if (!importsItem) { return false; } - const actualImportedFrom = importsMap[firstPart].moduleName; + const actualImportedFrom = importsItem.moduleName; - if (importedFrom instanceof RegExp) { + if ( + importedFrom instanceof RegExp && + typeof actualImportedFrom === "string" + ) { if (importedFrom.test(actualImportedFrom) === false) { return false; } @@ -227,9 +262,11 @@ function scan({ }); componentInfo.instances.push(info); + + return; }, }, }); } -module.exports = scan; +export default scan; diff --git a/src/scanner.js b/src/scanner.js deleted file mode 100644 index 328af81..0000000 --- a/src/scanner.js +++ /dev/null @@ -1,30 +0,0 @@ -const startTime = process.hrtime.bigint(); - -const { validateConfig } = require("./utils"); -const runScan = require("./run"); - -const scanner = { - run: async function run(config, configDir, method = "programmatic") { - const { crawlFrom, errors } = validateConfig(config, configDir); - - if (errors.length === 0) { - return await runScan({ - config, - configDir, - crawlFrom, - startTime, - method: method, - }); - } else { - console.error(`Config errors:`); - - errors.forEach((error) => { - console.error(`- ${error}`); - }); - - process.exit(1); - } - }, -}; - -module.exports = scanner; diff --git a/src/scanner.test.js b/src/scanner.test.ts similarity index 90% rename from src/scanner.test.js rename to src/scanner.test.ts index e69f748..f62e631 100644 --- a/src/scanner.test.js +++ b/src/scanner.test.ts @@ -1,8 +1,8 @@ -const path = require("path"); -const { suite } = require("uvu"); -const assert = require("uvu/assert"); +import path from "path"; +import { suite } from "uvu"; +import assert from "uvu/assert"; -const scanner = require("./scanner"); +import scanner from "./scanner"; const Scanner = suite("Scanner"); diff --git a/src/scanner.ts b/src/scanner.ts new file mode 100644 index 0000000..dff498d --- /dev/null +++ b/src/scanner.ts @@ -0,0 +1,38 @@ +const startTime = process.hrtime.bigint(); + +import { validateConfig } from "./utils"; +import runScan, { RunMethod } from "./run"; +import type { Config } from "./types"; + +const run = async function run( + config: Config, + configDir?: string, + method: RunMethod = "programmatic" +) { + const { crawlFrom, errors } = validateConfig(config, configDir); + + if (errors.length === 0) { + return await runScan({ + config, + configDir, + crawlFrom: crawlFrom as string, + startTime, + method: method, + }); + } else { + console.error(`Config errors:`); + + errors.forEach((error) => { + console.error(`- ${error}`); + }); + + process.exit(1); + } +}; + +const scanner = { + run, +}; + +export default scanner; +export { run }; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..1f02ab7 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,106 @@ +import type { Position } from "estree"; +import type { + Literal, + JSXExpressionContainer, + JSXElement, + JSXFragment, + JSXIdentifier, +} from "estree-jsx"; + +export type ProcessorName = + | "count-components" + | "count-components-and-props" + | "raw-report"; + +type ProcessorConfig = { + outputTo: string; +}; +type ProcessorWithConfig = [ProcessorName, ProcessorConfig]; +export type ProcessorOptions = { + outputTo: string; +}; +export type ProcessorFunctionArgs = { + report: Report; + prevResults: unknown[]; + prevResult: unknown; + forEachComponent: (callback: ComponentCallback) => void; + sortObjectKeysByValue: ( + obj: Record, + mapValue?: undefined | ((value: Value) => string | number) + ) => Record; + output: (data: unknown, destination?: string) => void; +}; +export type ProcessorFunction = ( + args: ProcessorFunctionArgs +) => Promise; +type Processor = ProcessorName | ProcessorWithConfig | ProcessorFunction; + +export type ImportInfo = { + local: string; + imported?: string; + moduleName: string | boolean | number | bigint | RegExp | null | undefined; + importType: + | "ImportDefaultSpecifier" + | "ImportSpecifier" + | "ImportNamespaceSpecifier"; +}; + +export type GetComponentNameFunction = (args: ImportInfo) => string; + +export type PropNode = + | Literal + | JSXExpressionContainer + | JSXElement + | JSXFragment + | null; + +export type PropValue = string | number | boolean | RegExp | null | undefined; + +export type GetPropValueFunction = (args: { + node: PropNode; + componentName: string; + propName: string | JSXIdentifier; + defaultGetPropValue: (node: PropNode) => PropValue; +}) => PropValue; + +export type Config = { + rootDir?: string; + crawlFrom?: string; + includeSubComponents?: boolean; + importedFrom?: string | RegExp; + processors?: Processor[]; + globs?: string[]; + exclude?: Array | ((dir: string) => boolean); + components?: Record; + getComponentName?: GetComponentNameFunction; + getPropValue?: GetPropValueFunction; +}; + +export type InstanceInfo = { + propsSpread: boolean; + props: Record; + location: { + file: string; + start: Position | undefined; + }; +}; + +export type Report = Record< + string, + { + id?: number; + components?: Report | undefined; + instances?: InstanceInfo[]; + } +>; + +export type ComponentCallbackArgs = { + componentName: string; + component: { + id?: number; + components?: Report | undefined; + instances?: InstanceInfo[]; + }; +}; + +export type ComponentCallback = (args: ComponentCallbackArgs) => void; diff --git a/src/utils.test.js b/src/utils.test.ts similarity index 85% rename from src/utils.test.js rename to src/utils.test.ts index 24d07db..ae4682e 100644 --- a/src/utils.test.js +++ b/src/utils.test.ts @@ -1,16 +1,23 @@ -const fs = require("fs"); -const path = require("path"); -const { suite } = require("uvu"); -const assert = require("uvu/assert"); -const { +import fs from "fs"; +import path from "path"; +import { Context, suite } from "uvu"; +import assert from "uvu/assert"; +import type { Report } from "./types"; +import { validateConfig, pluralize, forEachComponent, sortObjectKeysByValue, getExcludeFn, -} = require("./utils"); +} from "./utils"; -const ValidateConfig = suite("validateConfig"); +interface ValidateConfigContext extends Context { + originalPathResolve: typeof path.resolve; + originFsExistsSync: typeof fs.existsSync; + mock: ((fn: () => void) => void) | undefined; +} + +const ValidateConfig = suite("validateConfig"); const Pluralize = suite("pluralize"); const ForEachComponent = suite("forEachComponent"); const SortObjectKeysByValue = suite("sortObjectKeysByValue"); @@ -35,7 +42,7 @@ ValidateConfig.after.each((context) => { }); ValidateConfig("crawlFrom is missing", (context) => { - context.mock(() => { + context.mock?.(() => { path.resolve = () => ""; fs.existsSync = () => false; }); @@ -48,13 +55,14 @@ ValidateConfig("crawlFrom is missing", (context) => { }); ValidateConfig("crawlFrom should be a string", (context) => { - context.mock(() => { + context.mock?.(() => { path.resolve = () => ""; fs.existsSync = () => false; }); const result = validateConfig( { + // @ts-expect-error expected runtime error crawlFrom: true, }, "/Users/misha/oscar" @@ -66,7 +74,7 @@ ValidateConfig("crawlFrom should be a string", (context) => { }); ValidateConfig("crawlFrom path doesn't exist", (context) => { - context.mock(() => { + context.mock?.(() => { fs.existsSync = () => false; }); @@ -86,6 +94,7 @@ ValidateConfig("exclude is an array with invalid items", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error exclude: ["utils", /node_modules/, undefined], }, "/Users/misha/oscar" @@ -103,6 +112,7 @@ ValidateConfig("exclude is neither an array nor a function", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error exclude: "utils", }, "/Users/misha/oscar" @@ -118,6 +128,7 @@ ValidateConfig("globs is not an array", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error globs: "**/*.js", }, "/Users/misha/oscar" @@ -133,6 +144,7 @@ ValidateConfig("globs has a non string item", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error globs: ["**/*.js", 4], }, "/Users/misha/oscar" @@ -148,6 +160,7 @@ ValidateConfig("components is not an object", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error components: "Header", }, "/Users/misha/oscar" @@ -164,6 +177,7 @@ ValidateConfig("components has a non true value", () => { { crawlFrom: "./src", components: { + // @ts-expect-error expected runtime error Header: false, }, }, @@ -180,6 +194,7 @@ ValidateConfig("includeSubComponents is not a boolean", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error includeSubComponents: "yes", }, "/Users/misha/oscar" @@ -195,6 +210,7 @@ ValidateConfig("importedFrom is not a string or a RegExp", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error importedFrom: ["basis"], }, "/Users/misha/oscar" @@ -210,6 +226,7 @@ ValidateConfig("processors is not an array", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error processors: "count-components", }, "/Users/misha/oscar" @@ -225,6 +242,7 @@ ValidateConfig("string form - unknown processor", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error processors: ["foo"], }, "/Users/misha/oscar" @@ -232,13 +250,14 @@ ValidateConfig("string form - unknown processor", () => { assert.is(result.crawlFrom, "/Users/misha/oscar/src"); assert.is(result.errors.length, 1); - assert.ok(/^unknown processor: foo/.test(result.errors[0])); + assert.ok(/^unknown processor: foo/.test(result.errors[0] ?? "")); }); ValidateConfig("array form - not a tuple", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error processors: [["count-components"]], }, "/Users/misha/oscar" @@ -256,7 +275,8 @@ ValidateConfig("array form - processor name is not a string", () => { const result = validateConfig( { crawlFrom: "./src", - processors: [[() => {}, "count-components"]], + // @ts-expect-error expected runtime error + processors: [[() => ({}), "count-components"]], }, "/Users/misha/oscar" ); @@ -273,6 +293,7 @@ ValidateConfig("array form - unknown processor", () => { const result = validateConfig( { crawlFrom: "./src", + // @ts-expect-error expected runtime error processors: [["foo", {}]], }, "/Users/misha/oscar" @@ -280,14 +301,15 @@ ValidateConfig("array form - unknown processor", () => { assert.is(result.crawlFrom, "/Users/misha/oscar/src"); assert.is(result.errors.length, 1); - assert.ok(/^unknown processor: foo/.test(result.errors[0])); + assert.ok(/^unknown processor: foo/.test(result.errors[0] ?? "")); }); ValidateConfig("array form - processor options is not an object", () => { const result = validateConfig( { crawlFrom: "./src", - processors: [["count-components", () => {}]], + // @ts-expect-error expected runtime error + processors: [["count-components", () => ({})]], }, "/Users/misha/oscar" ); @@ -304,7 +326,8 @@ ValidateConfig("array form - processor name is unsupported type", () => { const result = validateConfig( { crawlFrom: "./src", - processors: [true, () => {}], + // @ts-expect-error expected runtime error + processors: [true, () => ({})], }, "/Users/misha/oscar" ); @@ -350,7 +373,7 @@ Pluralize("count > 1", () => { }); ForEachComponent("visits every component", () => { - const report = { + const report: Report = { Header: { id: 1, components: { @@ -377,7 +400,7 @@ ForEachComponent("visits every component", () => { }, }; - const visits = []; + const visits: { id: number | undefined; name: string }[] = []; forEachComponent(report)(({ componentName, component }) => { visits.push({ diff --git a/src/utils.js b/src/utils.ts similarity index 76% rename from src/utils.js rename to src/utils.ts index 26b9198..9bcbf76 100644 --- a/src/utils.js +++ b/src/utils.ts @@ -1,14 +1,23 @@ -const fs = require("fs"); -const path = require("path"); -const { isPlainObject } = require("is-plain-object"); -const processors = require("./processors/processors"); +import fs from "fs"; +import path from "path"; +import { isPlainObject } from "is-plain-object"; +import processors from "./processors/processors.json"; +import type { ComponentCallback, Config, Report } from "./types"; -function pluralize(count, word) { +function pluralize(count: number, word: string) { return count === 1 ? `1 ${word}` : `${count} ${word}s`; } -function validateConfig(config, configDir) { - const result = { +type ValidatorResult = { + errors: string[]; + crawlFrom?: string; +}; + +function validateConfig( + config: Config, + configDir: string | undefined +): ValidatorResult { + const result: ValidatorResult = { errors: [], }; @@ -18,7 +27,7 @@ function validateConfig(config, configDir) { result.errors.push(`crawlFrom should be a string`); } else { const crawlFrom = path.resolve( - config.rootDir || configDir, + config.rootDir || configDir || "", config.crawlFrom ); @@ -108,10 +117,11 @@ function validateConfig(config, configDir) { ); } } else if (Array.isArray(processor)) { - if (processor.length !== 2) { + const { length } = processor; + if (length !== 2) { result.errors.push( `processor is in a form of array should have exactly 2 items (${pluralize( - processor.length, + length, "item" )} found)` ); @@ -152,30 +162,42 @@ function validateConfig(config, configDir) { return result; } -const forEachComponent = (report) => (callback) => { +const forEachComponent = (report: Report) => (callback: ComponentCallback) => { const queue = [{ namePrefix: "", componentsMap: report }]; while (queue.length > 0) { - const { namePrefix, componentsMap } = queue.shift(); + const item = queue.shift(); + + if (item) { + const { namePrefix, componentsMap } = item; + + for (const componentName in componentsMap) { + const component = componentsMap[componentName]; - for (let componentName in componentsMap) { - const component = componentsMap[componentName]; - const { components } = component; - const fullComponentName = `${namePrefix}${componentName}`; + if (component) { + const { components } = component; + const fullComponentName = `${namePrefix}${componentName}`; - callback({ componentName: fullComponentName, component }); + callback({ componentName: fullComponentName, component }); - if (components) { - queue.push({ - namePrefix: `${fullComponentName}.`, - componentsMap: components, - }); + if (components) { + queue.push({ + namePrefix: `${fullComponentName}.`, + componentsMap: components, + }); + } + } } } } }; -function sortObjectKeysByValue(obj, mapValue = (value) => value) { +// + +function sortObjectKeysByValue( + obj: Record, + mapValue: (value: Value) => string | number = (value) => String(value) +): Record { const entries = Object.entries(obj); entries.sort(([key1, value1], [key2, value2]) => { @@ -188,15 +210,15 @@ function sortObjectKeysByValue(obj, mapValue = (value) => value) { : 1; }); - return entries.reduce((acc, [key, value]) => { + return entries.reduce>((acc, [key, value]) => { acc[key] = value; return acc; }, {}); } -function getExcludeFn(configExclude) { +function getExcludeFn(configExclude: Config["exclude"]) { if (Array.isArray(configExclude)) { - return (dir) => { + return (dir: string) => { for (let i = 0, len = configExclude.length; i < len; i++) { const item = configExclude[i]; @@ -219,7 +241,7 @@ function getExcludeFn(configExclude) { return () => false; } -module.exports = { +export { pluralize, validateConfig, forEachComponent, diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..fd9e237 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["**/*.test.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9f9b7cf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "outDir": "dist", + "lib": ["es2021"], + "module": "commonjs", + "target": "es2021", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "importsNotUsedAsValues": "error", + "checkJs": false, + "resolveJsonModule": true, + "typeRoots": ["./@types", "./node_modules/@types"] + } +}