diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000000000000000000000000000000000000..cef8a674a38bb74bbd8a325a17424471703d2e46 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,29 @@ +env: + browser: true + es2020: true +extends: + - "eslint:recommended" + - "plugin:@typescript-eslint/recommended" +parser: "@typescript-eslint/parser" +parserOptions: + ecmaVersion: 11 + sourceType: module +plugins: + - "@typescript-eslint" +rules: + indent: + - error + - tab + - SwitchCase: 1 + linebreak-style: + - error + - unix + quotes: + - error + - double + semi: + - error + - never + "@typescript-eslint/no-unused-vars": + - error + - argsIgnorePattern: "^_" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b41917f3ee62b642a6b86e8e9b30879e0aa9bee8..03cdc5dc4b24b75a8535ed5358f0174f9b5aa042 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,15 @@ stages: - test - release +lint: + image: dbogatov/docker-sources:node--14.4-alpine3.12 + stage: test + script: + - npm -g install eslint + - eslint ./{src,test}/**/*.ts -c .eslintrc.yml + tags: + - docker + test: image: dbogatov/docker-sources:node--14.4-alpine3.12 stage: test diff --git a/package-lock.json b/package-lock.json index bbb42b3a12006638a7a0d17234494769be70c2cf..7e6230730bbf8253e3fcaf20ef2293516d66971b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "broken-links-inspector", - "version": "1.0.0", + "version": "1.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -314,46 +314,6 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, - "@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, - "@sinonjs/samsam": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz", - "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==", - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" - }, "@types/chai": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", @@ -365,11 +325,23 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/intercept-stdout": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@types/intercept-stdout/-/intercept-stdout-0.1.0.tgz", "integrity": "sha512-b4+N4+pHcUWaK75k4GDavB5ZS6aHdlsyxKaU82JEq7mzY0+kziTiT6nKm+OCkMlGvL6RNk2rera2InJKVP5OiQ==" }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "dev": true + }, "@types/mocha": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", @@ -396,6 +368,105 @@ "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.4.0.tgz", + "integrity": "sha512-wfkpiqaEVhZIuQRmudDszc01jC/YR7gMSxa6ulhggAe/Hs0KVIuo9wzvFiDbG3JD5pRFQoqnf4m7REDsUvBnMQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "3.4.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.4.0.tgz", + "integrity": "sha512-rHPOjL43lOH1Opte4+dhC0a/+ks+8gOBwxXnyrZ/K4OTAChpSjP76fbI8Cglj7V5GouwVAGaK+xVwzqTyE/TPw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "3.4.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.4.0.tgz", + "integrity": "sha512-ZUGI/de44L5x87uX5zM14UYcbn79HSXUR+kzcqU42gH0AgpdB/TjuJy3m4ezI7Q/jk3wTQd755mxSDLhQP79KA==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "3.4.0", + "@typescript-eslint/typescript-estree": "3.4.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.4.0.tgz", + "integrity": "sha512-zKwLiybtt4uJb4mkG5q2t6+W7BuYx2IISiDNV+IY68VfoGwErDx/RfVI7SWL4gnZ2t1A1ytQQwZ+YOJbHHJ2rw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -406,6 +477,18 @@ "indent-string": "^4.0.0" } }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -485,6 +568,12 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "axios": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", @@ -548,6 +637,12 @@ "write-file-atomic": "^3.0.0" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -730,6 +825,12 @@ "type-detect": "^4.0.0" } }, + "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 + }, "default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -751,7 +852,17 @@ "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "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" + } }, "dom-serializer": { "version": "0.2.2", @@ -791,6 +902,23 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "enquirer": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz", + "integrity": "sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==", + "dev": true, + "requires": { + "ansi-colors": "^3.2.1" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + } + } + }, "entities": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", @@ -859,12 +987,204 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.3.1.tgz", + "integrity": "sha512-cQC/xj9bhWUcyi/RuMbRtC3I0eW8MH0jhRELSvpKYkWep3C6YZ2OkvcvJVUeO6gcunABmzptbXBuDoXsjHmfTA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "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", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.0.0", + "eslint-visitor-keys": "^1.2.0", + "espree": "^7.1.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.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": "^4.17.14", + "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": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "strip-ansi": { + "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" + } + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "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" + } + }, + "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 + }, + "espree": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", + "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", + "dev": true, + "requires": { + "acorn": "^7.2.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.2.0" + } + }, "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 }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -904,6 +1224,34 @@ "is-buffer": "~2.0.3" } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -962,6 +1310,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "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 + }, "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", @@ -1080,6 +1434,30 @@ "entities": "^2.0.0" } }, + "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 + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1379,6 +1757,18 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "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 + }, + "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 + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -1388,10 +1778,15 @@ "minimist": "^1.2.5" } }, - "just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==" + "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" + } }, "locate-path": { "version": "5.0.0", @@ -1429,11 +1824,6 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -1667,17 +2057,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nise": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz", - "integrity": "sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg==", - "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^6.0.0", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } + "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 }, "node-preload": { "version": "0.2.1", @@ -1853,6 +2237,20 @@ "wrappy": "1" } }, + "optionator": { + "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", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -1898,6 +2296,15 @@ "release-zalgo": "^1.0.0" } }, + "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==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1922,21 +2329,6 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - } - } - }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -1958,6 +2350,12 @@ "find-up": "^4.0.0" } }, + "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 + }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -1967,6 +2365,12 @@ "fromentries": "^1.2.0" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "promise.allsettled": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", @@ -1980,6 +2384,12 @@ "iterate-value": "^1.0.0" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "readdirp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", @@ -1989,6 +2399,12 @@ "picomatch": "^2.0.7" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -2079,18 +2495,41 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "sinon": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.2.tgz", - "integrity": "sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==", + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, "requires": { - "@sinonjs/commons": "^1.7.2", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.0.3", - "diff": "^4.0.2", - "nise": "^4.0.1", - "supports-color": "^7.1.0" + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } } }, "source-map": { @@ -2196,6 +2635,46 @@ "has-flag": "^4.0.0" } }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -2207,6 +2686,12 @@ "minimatch": "^3.0.4" } }, + "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 + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2235,10 +2720,35 @@ "yn": "3.1.1" } }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "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" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true }, "type-fest": { "version": "0.8.1", @@ -2261,12 +2771,27 @@ "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", "dev": true }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2291,6 +2816,12 @@ "string-width": "^1.0.2 || 2" } }, + "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 + }, "workerpool": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", @@ -2366,6 +2897,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", diff --git a/package.json b/package.json index 54ac39a4dd7b99cc432ca9eef2f9af3aa88b3416..20b1d6265ed9d0469dc0517295f1a238ee41a7c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "broken-links-inspector", - "version": "1.1.1", + "version": "1.1.2", "description": "Extract and recursively check all URLs reporting broken ones", "main": "dist/inspector.js", "types": "dist/inspector.d.ts", @@ -59,7 +59,10 @@ "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/sinon": "^9.0.4", + "@typescript-eslint/eslint-plugin": "^3.4.0", + "@typescript-eslint/parser": "^3.4.0", "chai": "^4.2.0", + "eslint": "^7.3.1", "mocha": "^8.0.1", "mocha-junit-reporter": "^2.0.0", "nyc": "^15.1.0", diff --git a/src/index.ts b/src/index.ts index e815687dabe3d8f5b8c312c74eec8f1bef697949..b0a9d94b97bcb33497648afb2aa4b2b911ead6cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ import { Inspector, URLsMatchingSet } from "./inspector" import { ConsoleReporter, JUnitReporter } from "./report" commander - .version("1.1.1") + .version("1.1.2") .description("Extract and recursively check all URLs reporting broken ones") commander @@ -30,7 +30,7 @@ commander process.exit(1) } - let inspector = new Inspector(new URLsMatchingSet(), { + const inspector = new Inspector(new URLsMatchingSet(), { acceptedCodes: inspectObj.acceptCodes as number[], timeout: parseInt(inspectObj.timeout as string), ignoredPrefixes: inspectObj.ignorePrefixes as string[], @@ -41,7 +41,7 @@ commander disablePrint: false }) - let result = await inspector.processURL(new URL(url), inspectObj.recursive as boolean) + const result = await inspector.processURL(new URL(url), inspectObj.recursive as boolean) for (const reporter of inspectObj.reporters as string[]) { switch (reporter) { diff --git a/src/inspector.ts b/src/inspector.ts index 9a279960df417a0df5775a478f075ff388ff1ac9..965e84b76ea0baadc6680bb06cdd2a08ba44b944 100644 --- a/src/inspector.ts +++ b/src/inspector.ts @@ -1,6 +1,6 @@ import * as parser from "htmlparser2" import axios, { AxiosError } from "axios" -import { Result, CheckStatus } from "./result"; +import { Result, CheckStatus } from "./result" import { isMatch } from "matcher" export interface IHttpClient { @@ -21,7 +21,7 @@ export class AxiosHttpClient implements IHttpClient { readonly acceptedCodes: number[] ) { } - private async timeoutWrapper<T>(timeoutMs: number, promise: () => Promise<T>, failureMessage: string = "timeout"): Promise<T> { + private async timeoutWrapper<T>(timeoutMs: number, promise: () => Promise<T>, failureMessage = "timeout"): Promise<T> { let timeoutHandle: NodeJS.Timeout | undefined const timeoutPromise = new Promise<never>((_, reject) => { timeoutHandle = setTimeout(() => reject(new Error(failureMessage)), timeoutMs) @@ -30,9 +30,11 @@ export class AxiosHttpClient implements IHttpClient { const result = await Promise.race([ promise(), timeoutPromise - ]); - clearTimeout(timeoutHandle!); - return result; + ]) + if (timeoutHandle) { + clearTimeout(timeoutHandle) + } + return result } async request(get: boolean, url: string): Promise<string> { @@ -43,13 +45,14 @@ export class AxiosHttpClient implements IHttpClient { return (await this.timeoutWrapper(this.timeout, () => get ? instance.get(url) : instance.head(url))).data as string } catch (exception) { - const error: AxiosError = exception; + const error: AxiosError = exception if ((exception.message as string).includes("timeout")) { throw new HttpClientFailure(true, -1) } else if (!error.response) { throw new HttpClientFailure(false, -1) } else { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (this.acceptedCodes.some(code => code == error.response!.status)) { return "" } else { @@ -70,11 +73,11 @@ export class Inspector { async processURL(originalUrl: URL, recursive: boolean): Promise<Result> { - let result = new Result(this.config.ignoreSkipped, this.config.disablePrint); + const result = new Result(this.config.ignoreSkipped, this.config.disablePrint) // [url, GET, parent?] - let urlsToCheck: [string, boolean, string?][] = [[originalUrl.href, true, undefined]] + const urlsToCheck: [string, boolean, string?][] = [[originalUrl.href, true, undefined]] - let processingRoutine = async (url: string, useGet: boolean, parent?: string) => { + const processingRoutine = async (url: string, useGet: boolean, parent?: string) => { try { try { @@ -85,7 +88,7 @@ export class Inspector { if (url.includes("#")) { url = url.split("#")[0] } - let shouldParse = url == originalUrl.href || (recursive && originalUrl.origin == new URL(url).origin) + const shouldParse = url == originalUrl.href || (recursive && originalUrl.origin == new URL(url).origin) if ( result.isChecked(url) || @@ -94,13 +97,13 @@ export class Inspector { ) { result.add({ url: url, status: CheckStatus.Skipped }, parent) } else { - let urlToCheck = parent ? new URL(url, parent).href : url + const urlToCheck = parent ? new URL(url, parent).href : url - let html = await this.httpClient.request(useGet || shouldParse, urlToCheck) + const html = await this.httpClient.request(useGet || shouldParse, urlToCheck) if (shouldParse) { - let discoveredURLs = this.extractURLs(html) + const discoveredURLs = this.extractURLs(html) for (const discovered of discoveredURLs) { urlsToCheck.push([discovered, this.config.get, url]) @@ -111,7 +114,7 @@ export class Inspector { } } catch (exception) { - const error: HttpClientFailure = exception; + const error: HttpClientFailure = exception // if HEAD was used, retry with GET if (!useGet) { @@ -128,11 +131,12 @@ export class Inspector { } } - let promises: Promise<void>[] = [] + const promises: Promise<void>[] = [] while (urlsToCheck.length > 0) { - let [url, useGet, parent] = urlsToCheck.pop()! + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const [url, useGet, parent] = urlsToCheck.pop()! promises.push(processingRoutine(url, useGet, parent)) @@ -147,13 +151,13 @@ export class Inspector { extractURLs(html: string): Set<string> { - let urls = new Set<string>(); - let matcher = this.matcher + const urls = new Set<string>() + const matcher = this.matcher - let parserInstance = new parser.Parser( + const parserInstance = new parser.Parser( { onopentag(name, attributes) { - const match = matcher.match(name, attributes); + const match = matcher.match(name, attributes) if (match && match !== "" && !match.startsWith("#")) { urls.add(match) } @@ -171,13 +175,13 @@ export class Inspector { export class Config { acceptedCodes: number[] = [999] - timeout: number = 2000 + timeout = 2000 ignoredPrefixes: string[] = ["mailto", "tel"] skipURLs: string[] = [] - verbose: boolean = false - get: boolean = false - ignoreSkipped: boolean = false - disablePrint: boolean = false + verbose = false + get = false + ignoreSkipped = false + disablePrint = false } export enum URLMatchingRule { @@ -192,7 +196,7 @@ export class URLsMatchingSet { private rules: URLMatchingRule[] constructor(...rules: URLMatchingRule[]) { - this.rules = rules.length > 0 ? rules : Object.values(URLMatchingRule); + this.rules = rules.length > 0 ? rules : Object.values(URLMatchingRule) } public match(name: string, attributes: { [s: string]: string }): string | undefined { @@ -203,29 +207,29 @@ export class URLsMatchingSet { if (name === "a" && "href" in attributes) { return attributes.href } - break; + break case URLMatchingRule.ScriptSrc: if (name === "script" && "src" in attributes) { return attributes.src } - break; + break case URLMatchingRule.LinkHref: if (name === "link" && "href" in attributes) { return attributes.href } - break; + break case URLMatchingRule.ImgSrc: if (name === "img" && "src" in attributes) { return attributes.src } - break; + break case URLMatchingRule.IFrameSrc: if (name === "iframe" && "src" in attributes) { return attributes.src } - break; + break default: - throw new Error(`unknown rule: ${rule}`); + throw new Error(`unknown rule: ${rule}`) } } diff --git a/src/report.ts b/src/report.ts index 41dab69495526acd4ee596115e57722ef7cf5102..82fad42fd1c58dbd6df6d86b1698f2b2e63ea57b 100644 --- a/src/report.ts +++ b/src/report.ts @@ -1,10 +1,10 @@ import { ResultItem, CheckStatus } from "./result" import chalk from "chalk" import { parse } from "js2xmlparser" -import fs from "fs"; +import fs from "fs" export interface IReporter { - process(pages: Map<string, ResultItem[]>): any + process(pages: Map<string, ResultItem[]>): unknown } /** @@ -38,11 +38,35 @@ export class JUnitReporter implements IReporter { process(pages: Map<string, ResultItem[]>): void { - let junitObject: any[] = [] + type TestCase = { + "@": { + name: string, + classname: string, + time: string, + }, + failure?: { + "@": { + message?: string + } + }, + skipped?: unknown + } + type TestSuite = { + testcase: TestCase[], + "@"?: { + name: string, + tests: number, + failures: number, + skipped: number, + time: string + } + } + + const junitObject: TestSuite[] = [] for (const page of pages) { - let testsuite: any = { + const testsuite: TestSuite = { testcase: [] } @@ -52,7 +76,7 @@ export class JUnitReporter implements IReporter { for (const check of page[1]) { - let testcase: any = { + const testcase: TestCase = { "@": { name: check.url, classname: page[0], @@ -108,7 +132,7 @@ export class JUnitReporter implements IReporter { junitObject.push(testsuite) } - let junitXml = parse("testsuites", { testsuite: junitObject }) + const junitXml = parse("testsuites", { testsuite: junitObject }) if (this.toFile) { fs.writeFileSync("junit-report.xml", junitXml) } else { @@ -120,7 +144,7 @@ export class JUnitReporter implements IReporter { export class ConsoleReporter implements IReporter { - private printTotals(oks: number, skipped: number, broken: number, indent: boolean = true) { + private printTotals(oks: number, skipped: number, broken: number, indent = true) { console.log(`${indent ? "\t" : ""}${chalk.green(`OK: ${oks}`)}, ${chalk.grey(`skipped: ${skipped}`)}, ${chalk.red(`broken: ${broken}`)}`) } @@ -131,17 +155,17 @@ export class ConsoleReporter implements IReporter { switch (check.status) { case CheckStatus.OK: statusLabel = chalk.green("OK".padEnd(labelWidth)) - break; + break case CheckStatus.Skipped: statusLabel = chalk.gray("SKIP".padEnd(labelWidth)) - break; + break case CheckStatus.Timeout: statusLabel = chalk.yellow("TIMEOUT".padEnd(labelWidth)) - break; + break case CheckStatus.NonSuccessCode: case CheckStatus.GenericError: statusLabel = chalk.red("BROKEN".padEnd(labelWidth)) - break; + break } if (check.status != CheckStatus.Skipped) { @@ -149,7 +173,7 @@ export class ConsoleReporter implements IReporter { } } - process(pages: Map<string, ResultItem[]>) { + process(pages: Map<string, ResultItem[]>): void { let allSkipped = 0 let allOks = 0 diff --git a/src/result.ts b/src/result.ts index bad7d1c2ab036603968ea481a09f5793a8bd00cc..e7ed813c4ba41c9e2466920970b3a17ea44cd7d8 100644 --- a/src/result.ts +++ b/src/result.ts @@ -8,7 +8,7 @@ export class Result { constructor(readonly ignoreSkipped: boolean, readonly disablePrint: boolean) { } - public add(completedCheck: ResultItem, parent: string = "original request") { + public add(completedCheck: ResultItem, parent = "original request"): void { if (completedCheck.status == CheckStatus.Skipped && this.ignoreSkipped) { return } @@ -22,6 +22,7 @@ export class Result { } if (this.pages.has(parent)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.pages.get(parent)!.push(completedCheck) } else { this.pages.set(parent, [completedCheck]) @@ -49,15 +50,15 @@ export class Result { return count } - public report<ReporterT extends IReporter>(reporter: ReporterT): any { + public report<ReporterT extends IReporter>(reporter: ReporterT): unknown { return reporter.process(this.pages) } - public success() { + public success(): boolean { return !this.atLeastOneBroken } - public set(pages: Map<string, ResultItem[]>) { + public set(pages: Map<string, ResultItem[]>): void { this.pages = pages } } diff --git a/test/extract-urls.ts b/test/extract-urls.ts index 17d6f11ddfe763e8765e5952ea96bf6e3eca5647..38edb7a45f066a9d78eebb842b223db2ac9bef41 100644 --- a/test/extract-urls.ts +++ b/test/extract-urls.ts @@ -1,6 +1,6 @@ import { Inspector, URLsMatchingSet, URLMatchingRule, Config } from "../src/inspector" -import { expect, assert } from "chai"; -import "mocha"; +import { expect, assert } from "chai" +import "mocha" describe("extractURLs", () => { @@ -9,27 +9,27 @@ describe("extractURLs", () => { it("works for <a href=...>", () => { const result = new Inspector(new URLsMatchingSet(URLMatchingRule.AHRef), new Config()).extractURLs(`<html><a href="${url}">Text</a></html>`) expect(result).to.eql(new Set([url])) - }); + }) it("works for <script src=...>", () => { const result = new Inspector(new URLsMatchingSet(URLMatchingRule.ScriptSrc), new Config()).extractURLs(`<html><script src="${url}">Text</script></html>`) expect(result).to.eql(new Set([url])) - }); + }) it("works for <link href=...>", () => { const result = new Inspector(new URLsMatchingSet(URLMatchingRule.LinkHref), new Config()).extractURLs(`<html><link href="${url}"></link></html>`) expect(result).to.eql(new Set([url])) - }); + }) it("works for <img src=...>", () => { const result = new Inspector(new URLsMatchingSet(URLMatchingRule.ImgSrc), new Config()).extractURLs(`<html><img src="${url}">Text</img></html>`) expect(result).to.eql(new Set([url])) - }); + }) it("works for <iframe src=...>", () => { const result = new Inspector(new URLsMatchingSet(URLMatchingRule.IFrameSrc), new Config()).extractURLs(`<html><iframe src="${url}">Text</iframe></html>`) expect(result).to.eql(new Set([url])) - }); + }) it("works for many rules", () => { const result = new Inspector(new URLsMatchingSet(), new Config()) @@ -42,7 +42,7 @@ describe("extractURLs", () => { </html>` ) expect(result).to.eql(new Set(["1", "2", "3", "4"])) - }); + }) it("does not match unless rule supplied", () => { const result = new Inspector(new URLsMatchingSet(URLMatchingRule.ImgSrc), new Config()) @@ -53,7 +53,7 @@ describe("extractURLs", () => { </html>` ) expect(result).to.eql(new Set([url])) - }); + }) it("filters duplicates", () => { const result = new Inspector(new URLsMatchingSet(), new Config()) @@ -65,10 +65,10 @@ describe("extractURLs", () => { </html>` ) expect(result).to.eql(new Set([url, "another-url"])) - }); + }) it("fails for unknown rule", () => { assert.throws(() => new Inspector(new URLsMatchingSet("error" as URLMatchingRule), new Config()).extractURLs(`<html><img src="${url}">Text</img></html>`), /unknown/) - }); + }) -}); +}) diff --git a/test/process-url.ts b/test/process-url.ts index 282ad7778bf2aedd8ee875eeb2777eb103b47525..617e0aa557aaaa655e45c31488fa64e751fd6314 100644 --- a/test/process-url.ts +++ b/test/process-url.ts @@ -1,9 +1,9 @@ import { Inspector, URLsMatchingSet, Config, IHttpClient, HttpClientFailure, AxiosHttpClient } from "../src/inspector" -import { assert } from "chai"; -import "mocha"; -import { ConsoleReporter, JUnitReporter, IReporter } from "../src/report"; -import { ResultItem, CheckStatus, Result } from "../src/result"; -import intercept from "intercept-stdout"; +import { assert } from "chai" +import "mocha" +import { ConsoleReporter, JUnitReporter, IReporter } from "../src/report" +import { ResultItem, CheckStatus, Result } from "../src/result" +import intercept from "intercept-stdout" class MockHttpClient implements IHttpClient { @@ -11,6 +11,7 @@ class MockHttpClient implements IHttpClient { constructor(readonly map: Map<string, [string[], boolean, boolean, number]>) { } async request(get: boolean, url: string): Promise<string> { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const [urls, timeout, failure, code] = this.map.get(url)! if (timeout) { throw new HttpClientFailure(true, -1) @@ -27,16 +28,17 @@ class MockHttpClient implements IHttpClient { } class MockReporter implements IReporter { - process(pages: Map<string, ResultItem[]>): any { + process(pages: Map<string, ResultItem[]>): Map<string, ResultItem[]> { return pages } } -function toURL(url: string, path: string = "") { +function toURL(url: string, path = "") { return new URL(`https://${url}/${path}`).href } function stripEffects(text: string) { + // eslint-disable-next-line no-control-regex return text.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "") } @@ -44,12 +46,15 @@ function assertEqualResults(expected: Map<string, ResultItem[]>, actual: Map<str for (const [expectedURL, expectedChecks] of expected) { assert(actual.has(expectedURL)) - let actualChecks = actual.get(expectedURL)! + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const actualChecks = actual.get(expectedURL)! assert(expectedChecks.length == actualChecks.length) for (const expectedCheck of expectedChecks) { - let actualCheck = actualChecks.find(c => c.url === expectedCheck.url) + const actualCheck = actualChecks.find(c => c.url === expectedCheck.url) assert(actualCheck) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion assert(expectedCheck.status == actualCheck!.status) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion assert(expectedCheck.message == actualCheck!.message) } } @@ -102,7 +107,7 @@ describe("Axios web server", async () => { try { await new AxiosHttpClient(5, []).request(false, "https://dbogatov.org") } catch (exception) { - const error: HttpClientFailure = exception; + const error: HttpClientFailure = exception assert(error.timeout) } }) @@ -111,7 +116,7 @@ describe("Axios web server", async () => { try { await new AxiosHttpClient(2000, []).request(false, "https://dbogatov.org/not-found-123") } catch (exception) { - const error: HttpClientFailure = exception; + const error: HttpClientFailure = exception assert(error.code == 404) } }) @@ -120,7 +125,7 @@ describe("Axios web server", async () => { try { await new AxiosHttpClient(1000, []).request(false, "bad-url") } catch (exception) { - const error: HttpClientFailure = exception; + const error: HttpClientFailure = exception assert(!error.timeout) assert(error.code == -1) } @@ -135,7 +140,7 @@ describe("process mock URL", function () { ([true, false] as boolean[]).forEach(recursive => { it(`processes ${recursive ? "" : "non-"}recursive`, async () => { - let config = new Config() + const config = new Config() config.disablePrint = true config.skipURLs = ["to-skip"] const inspector = new Inspector( @@ -143,14 +148,14 @@ describe("process mock URL", function () { config, httpClient ) - let unhook_intercept = intercept(_ => { return "" }); + const unhook_intercept = intercept(_ => { return "" }) const result = await inspector.processURL(new URL("https://original.com"), recursive) - unhook_intercept(); + unhook_intercept() const actual = result.report(new MockReporter()) as Map<string, ResultItem[]> - let expected = new Map(expectedNonRecursive) + const expected = new Map(expectedNonRecursive) if (recursive) { expected.set( @@ -164,26 +169,26 @@ describe("process mock URL", function () { assertEqualResults(expected, actual) assert(!result.success()) - }); + }) }) describe("reporters", function () { it("console", () => { - let log: string = "" - let unhook_intercept = intercept(line => { + let log = "" + const unhook_intercept = intercept(line => { log += stripEffects(line) return "" - }); + }) - let result = new Result(true, true) + const result = new Result(true, true) result.set(expectedNonRecursive) result.report(new ConsoleReporter()) - unhook_intercept(); + unhook_intercept() - let lines = log.split(/\r?\n/) + const lines = log.split(/\r?\n/) for (const [expectedURL, expectedChecks] of expectedNonRecursive) { assert(lines.find(l => l.startsWith(expectedURL))) @@ -192,13 +197,15 @@ describe("process mock URL", function () { if (expectedCheck.status == CheckStatus.Skipped) { continue } - let check = lines.find(l => l.includes("\t") && l.includes(expectedCheck.url + " ")) + const check = lines.find(l => l.includes("\t") && l.includes(expectedCheck.url + " ")) assert(check, `${expectedCheck.url} not found`) assert( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion check!.includes(expectedCheck.status == CheckStatus.NonSuccessCode || expectedCheck.status == CheckStatus.GenericError ? "BROKEN" : expectedCheck.status), `${expectedCheck.url}: status (${expectedCheck.status}) not found in "${check}"` ) if (expectedCheck.message) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion assert(check!.includes(expectedCheck.message)) } } @@ -207,21 +214,21 @@ describe("process mock URL", function () { it("junit", () => { - let log: string = "" - let unhook_intercept = intercept(line => { + let log = "" + const unhook_intercept = intercept(line => { log += line return "" - }); + }) - let result = new Result(true, true) + const result = new Result(true, true) result.set(expectedNonRecursive) result.report(new JUnitReporter(false)) - unhook_intercept(); + unhook_intercept() result.report(new JUnitReporter()) - let lines = log.split(/\r?\n/) + const lines = log.split(/\r?\n/) for (const [expectedURL, expectedChecks] of expectedNonRecursive) { assert(lines.find(l => l.includes("testsuite") && l.includes(expectedURL))) @@ -235,7 +242,7 @@ describe("process mock URL", function () { }) describe("process real URL", async () => { - let config = new Config() + const config = new Config() config.disablePrint = true const inspector = new Inspector( new URLsMatchingSet(), @@ -248,29 +255,29 @@ describe("process real URL", async () => { describe("result", () => { it("ignores skipped", () => { - let result = new Result(true, true) + const result = new Result(true, true) result.add({ status: CheckStatus.Skipped, url: "skip" }) result.add(new ResultItem()) assert(result.count() == 1) }) it("print progress", () => { - let result = new Result(true, false) + const result = new Result(true, false) - let log: string = "" - let unhook_intercept = intercept(line => { + let log = "" + const unhook_intercept = intercept(line => { log += line return "" - }); + }) result.add({ status: CheckStatus.GenericError, url: "" }) for (let index = 0; index < 120; index++) { result.add({ status: CheckStatus.OK, url: `${index}` }) } - unhook_intercept(); + unhook_intercept() - let lines = log.split(/\r?\n/) + const lines = log.split(/\r?\n/) assert(result.count() == 121) assert(lines.length == 2)