diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..36dff701ff --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +*.d.ts +dist/** +node_modules \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..8888c3d3ac --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,72 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "warnOnUnsupportedTypeScriptVersion": false, + "ecmaVersion": 6, + "sourceType": "module" + }, + "env": { + "browser": false, + "node": true, + "es6": true + }, + "extends": ["plugin:prettier/recommended"], + "plugins": [ + "@typescript-eslint", "jsdoc", "import" + ], + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "error", + + "camelcase": "off", + "@typescript-eslint/camelcase": ["error", { "properties": "never", "allow": ["^[A-Za-z][a-zA-Za-z]+_[A-Za-z]+$"] }], + + "@typescript-eslint/class-name-casing": "error", + "@typescript-eslint/consistent-type-definitions": ["error", "interface"], + "@typescript-eslint/interface-name-prefix": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-this-alias": "error", + + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": ["error", { "allowTernary": true }], + + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/triple-slash-reference": "error", + "@typescript-eslint/unified-signatures": "error", + + // eslint-plugin-import + "import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }], + + // eslint-plugin-jsdoc + "jsdoc/check-alignment": "error", + + // eslint + "constructor-super": "error", + "dot-notation": "error", + "eqeqeq": "error", + "no-caller": "error", + "no-duplicate-case": "error", + "no-duplicate-imports": "error", + "no-empty": "error", + "no-eval": "error", + "no-extra-bind": "error", + "no-fallthrough": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-return-await": "error", + "no-sparse-arrays": "error", + "no-template-curly-in-string": "error", + "no-throw-literal": "error", + "no-undef-init": "error", + "no-unsafe-finally": "error", + "no-unused-labels": "error", + "no-var": "error", + "object-shorthand": "error", + "prefer-const": "error", + "prefer-object-spread": "error", + "use-isnan": "error" + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..f06235c460 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000000..f4071dcc6e --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 120, + "tabWidth": 2 +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 40ff774b49..55fffe578e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2052,6 +2052,12 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@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/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -2104,6 +2110,12 @@ "pretty-format": "^25.1.0" } }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -2137,6 +2149,68 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.22.0.tgz", + "integrity": "sha512-BvxRLaTDVQ3N+Qq8BivLiE9akQLAOUfxNHIEhedOcg8B2+jY8Rc4/D+iVprvuMX1AdezFYautuGDwr9QxqSxBQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.22.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.22.0.tgz", + "integrity": "sha512-sJt1GYBe6yC0dWOQzXlp+tiuGglNhJC9eXZeC8GBVH98Zv9jtatccuhz0OF5kC/DwChqsNfghHx7OlIDQjNYAQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.22.0", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.22.0.tgz", + "integrity": "sha512-FaZKC1X+nvD7qMPqKFUYHz3H0TAioSVFGvG29f796Nc5tBluoqfHgLbSFKsh7mKjRoeTm8J9WX2Wo9EyZWjG7w==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.22.0", + "@typescript-eslint/typescript-estree": "2.22.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz", + "integrity": "sha512-2HFZW2FQc4MhIBB8WhDm9lVFaBDy6h9jGrJ4V2Uzxe/ON29HCHBTj3GkgcsgMWfsl2U5as+pTOr30Nibaw7qRQ==", + "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": "^6.3.0", + "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" + } + } + } + }, "@zkochan/cmd-shim": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", @@ -2194,6 +2268,12 @@ } } }, + "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 + }, "acorn-walk": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", @@ -2334,6 +2414,17 @@ "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "dev": true }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -2355,6 +2446,16 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -3030,6 +3131,12 @@ "dev": true, "optional": true }, + "comment-parser": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.2.tgz", + "integrity": "sha512-4Rjb1FnxtOcv9qsfuaNuVsmmVn4ooVoBHzYfyKteiXwIU84PClyGA5jASoFMwPV93+FPh9spwueXauxFJZkGAg==", + "dev": true + }, "compare-func": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", @@ -3091,6 +3198,12 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "conventional-changelog-angular": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz", @@ -3596,6 +3709,15 @@ "path-type": "^3.0.0" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -3666,104 +3788,737 @@ "once": "^1.4.0" } }, - "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", - "dev": true - }, - "envinfo": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.0.tgz", - "integrity": "sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==", - "dev": true - }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "envinfo": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.0.tgz", + "integrity": "sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", + "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "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", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "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 + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "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" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "globals": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", + "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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" + } + }, + "inquirer": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.6.tgz", + "integrity": "sha512-7SVO4h+QIdMq6XcqIqrNte3gS5MzCCKZdsq9DO4PJziBFNYzP3PGFbDjgadDb//MCahzgjCxvQ/O2wa7kx9o4w==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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" + } + } + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "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-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" + }, + "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 + } + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", + "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", + "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + } + } + }, + "eslint-plugin-jsdoc": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.0.0.tgz", + "integrity": "sha512-dLqUtIL6tvOoV+9IDdP3FqOnQ/sxklzs4hxZa91kt0TGI2o1fSqIuc6wa71oWtWKEyvaQj7m6CnzQxcBC9CEsg==", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "comment-parser": "^0.7.2", + "debug": "^4.1.1", + "jsdoctypeparser": "^6.1.0", + "lodash": "^4.17.15", + "regextras": "^0.7.0", + "semver": "^6.3.0", + "spdx-expression-parse": "^3.0.0" + }, + "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" + } + } } }, - "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "eslint-plugin-prettier": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", "dev": true, "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "prettier-linter-helpers": "^1.0.0" } }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", "dev": true, "requires": { - "es6-promise": "^4.0.3" + "eslint-visitor-keys": "^1.1.0" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", "dev": true }, - "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "espree": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.0.tgz", + "integrity": "sha512-Xs8airJ7RQolnDIbLtRutmfvSsAe0xqMMAantCN/GMoqf81TFbeI1T7Jpd56qYu1uuh32dOG5W/X9uO+ghPXzA==", "dev": true, "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } + "acorn": "^7.1.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" } }, "esprima": { @@ -3772,6 +4527,24 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", + "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "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", @@ -4023,6 +4796,12 @@ "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", @@ -4096,6 +4875,15 @@ "escape-string-regexp": "^1.0.5" } }, + "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": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -4128,6 +4916,34 @@ "locate-path": "^3.0.0" } }, + "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.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -4231,6 +5047,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 + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -5253,6 +6075,12 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -7138,6 +7966,12 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsdoctypeparser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz", + "integrity": "sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA==", + "dev": true + }, "jsdom": { "version": "15.2.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", @@ -7209,6 +8043,12 @@ "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 + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -8117,6 +8957,18 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "octokit-pagination-methods": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", @@ -8289,6 +9141,23 @@ "readable-stream": "^2.1.5" } }, + "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" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, "parse-github-repo-url": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", @@ -8455,6 +9324,21 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-format": { "version": "25.1.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", @@ -8506,6 +9390,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -8823,6 +9713,18 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, + "regextras": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.0.tgz", + "integrity": "sha512-ds+fL+Vhl918gbAUb0k2gVKbTZLsg84Re3DI6p85Et0U0tYME3hyW4nMK8Px4dtDaBA2qNjvG5uWyW7eK5gfmw==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -9170,6 +10072,25 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "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": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, "slide": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", @@ -9635,6 +10556,12 @@ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", "dev": true }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, "strong-log-transformer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", @@ -9696,6 +10623,52 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "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 + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "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" + } + } + } + }, "tar": { "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", @@ -9783,6 +10756,12 @@ "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true }, + "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 + }, "thenify": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", @@ -9959,6 +10938,15 @@ "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "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" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -10193,6 +11181,12 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, "v8-to-istanbul": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.2.tgz", @@ -10408,6 +11402,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": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", diff --git a/package.json b/package.json index d7cb2e48d2..43a0ebbd0a 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,24 @@ "private": true, "scripts": { "pretest": "npm run build", + "lint": "eslint . --ext .ts", + "format": "prettier --write 'packages/**/*.ts'", "test": "jest", "build": "tsc -b tsconfig.all.json" }, "devDependencies": { "@types/jest": "^25.1.3", "@types/node": "^12.12.29", + "@typescript-eslint/eslint-plugin": "^2.22.0", + "@typescript-eslint/parser": "^2.22.0", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.0", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-jsdoc": "^22.0.0", + "eslint-plugin-prettier": "^3.1.2", "jest": "^25.1.0", "lerna": "^3.20.2", + "prettier": "^1.19.1", "ts-jest": "^25.2.1", "typescript": "^3.8.3" } diff --git a/packages/definitions-parser/package-lock.json b/packages/definitions-parser/package-lock.json index e74b55c068..f729a35d3b 100644 --- a/packages/definitions-parser/package-lock.json +++ b/packages/definitions-parser/package-lock.json @@ -42,6 +42,11 @@ "graceful-fs": "^4.1.6" } }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/packages/definitions-parser/package.json b/packages/definitions-parser/package.json index eaea6f8d4a..b686e4dc32 100644 --- a/packages/definitions-parser/package.json +++ b/packages/definitions-parser/package.json @@ -22,7 +22,8 @@ "dependencies": { "@definitelytyped/header-parser": "^0.0.0", "@definitelytyped/utils": "^1.0.0", - "fs-extra": "^8.1.0" + "fs-extra": "^8.1.0", + "typescript": "^3.8.3" }, "devDependencies": { "@types/fs-extra": "^8.1.0" diff --git a/packages/definitions-parser/src/get-definitely-typed.ts b/packages/definitions-parser/src/get-definitely-typed.ts index 4a03834f74..24ba792ec5 100644 --- a/packages/definitions-parser/src/get-definitely-typed.ts +++ b/packages/definitions-parser/src/get-definitely-typed.ts @@ -1,43 +1,42 @@ import { ensureDir } from "fs-extra"; -import { - DiskFS, - downloadAndExtractFile, - LoggerWithErrors, - FS, - exec -} from "@definitelytyped/utils"; +import { DiskFS, downloadAndExtractFile, LoggerWithErrors, FS, exec } from "@definitelytyped/utils"; import { dataDirPath, definitelyTypedZipUrl } from "./lib/settings"; /** Settings that may be determined dynamically. */ export interface ParseDefinitionsOptions { - /** - * e.g. '../DefinitelyTyped' - * This is overridden to `cwd` when running the tester, as that is run from within DefinitelyTyped. - * If undefined, downloads instead. - */ - readonly definitelyTypedPath: string | undefined; - /** Whether to show progress bars. Good when running locally, bad when running in CI. */ - readonly progress: boolean; - /** Disabled in CI since it has problems logging errors from other processes. */ - readonly parseInParallel: boolean; - } + /** + * e.g. '../DefinitelyTyped' + * This is overridden to `cwd` when running the tester, as that is run from within DefinitelyTyped. + * If undefined, downloads instead. + */ + readonly definitelyTypedPath: string | undefined; + /** Whether to show progress bars. Good when running locally, bad when running in CI. */ + readonly progress: boolean; + /** Disabled in CI since it has problems logging errors from other processes. */ + readonly parseInParallel: boolean; +} export async function getDefinitelyTyped(options: ParseDefinitionsOptions, log: LoggerWithErrors): Promise { - if (options.definitelyTypedPath === undefined) { - log.info("Downloading Definitely Typed ..."); - await ensureDir(dataDirPath); - return downloadAndExtractFile(definitelyTypedZipUrl); - } - const { error, stderr, stdout } = await exec("git diff --name-only", options.definitelyTypedPath); - if (error) { throw error; } - if (stderr) { throw new Error(stderr); } - if (stdout) { throw new Error(`'git diff' should be empty. Following files changed:\n${stdout}`); } - log.info(`Using local Definitely Typed at ${options.definitelyTypedPath}.`); - return new DiskFS(`${options.definitelyTypedPath}/`); + if (options.definitelyTypedPath === undefined) { + log.info("Downloading Definitely Typed ..."); + await ensureDir(dataDirPath); + return downloadAndExtractFile(definitelyTypedZipUrl); + } + const { error, stderr, stdout } = await exec("git diff --name-only", options.definitelyTypedPath); + if (error) { + throw error; + } + if (stderr) { + throw new Error(stderr); + } + if (stdout) { + throw new Error(`'git diff' should be empty. Following files changed:\n${stdout}`); + } + log.info(`Using local Definitely Typed at ${options.definitelyTypedPath}.`); + return new DiskFS(`${options.definitelyTypedPath}/`); } export function getLocallyInstalledDefinitelyTyped(path: string): FS { - return new DiskFS(`${path}/`); + return new DiskFS(`${path}/`); } - diff --git a/packages/definitions-parser/src/lib/definition-parser-worker.ts b/packages/definitions-parser/src/lib/definition-parser-worker.ts index fcee26993b..c9516940d4 100644 --- a/packages/definitions-parser/src/lib/definition-parser-worker.ts +++ b/packages/definitions-parser/src/lib/definition-parser-worker.ts @@ -8,15 +8,15 @@ import { getTypingInfo } from "./definition-parser"; export const definitionParserWorkerFilename = __filename; if (!module.parent) { - process.on("message", message => { - assert(process.argv.length === 3); - const typesPath = process.argv[2]; - // tslint:disable-next-line no-async-without-await - logUncaughtErrors(async () => { - for (const packageName of message as ReadonlyArray) { - const data = getTypingInfo(packageName, getLocallyInstalledDefinitelyTyped(typesPath).subDir(packageName)); - process.send!({ data, packageName }); - } - }); + process.on("message", message => { + assert(process.argv.length === 3); + const typesPath = process.argv[2]; + // tslint:disable-next-line no-async-without-await + logUncaughtErrors(async () => { + for (const packageName of message as readonly string[]) { + const data = getTypingInfo(packageName, getLocallyInstalledDefinitelyTyped(typesPath).subDir(packageName)); + process.send!({ data, packageName }); + } }); + }); } diff --git a/packages/definitions-parser/src/lib/definition-parser.ts b/packages/definitions-parser/src/lib/definition-parser.ts index f7f95c24f2..2d2af23c68 100644 --- a/packages/definitions-parser/src/lib/definition-parser.ts +++ b/packages/definitions-parser/src/lib/definition-parser.ts @@ -2,106 +2,130 @@ import * as ts from "typescript"; import { isTypeScriptVersion, parseHeaderOrFail, TypeScriptVersion } from "@definitelytyped/header-parser"; import { allReferencedFiles, createSourceFile, getModuleInfo, getTestDependencies } from "./module-info"; import { - formatTypingVersion, - getLicenseFromPackageJson, - PackageId, - PackageJsonDependency, - PathMapping, - TypingsDataRaw, - TypingsVersionsRaw, - TypingVersion, + formatTypingVersion, + getLicenseFromPackageJson, + PackageId, + PackageJsonDependency, + PathMapping, + TypingsDataRaw, + TypingsVersionsRaw, + TypingVersion } from "../packages"; import { dependenciesWhitelist } from "./settings"; -import { FS, split, mapDefined, filter, sort, withoutStart, computeHash, hasWindowsSlashes, join, flatMap, unique, unmangleScopedPackage } from "@definitelytyped/utils"; +import { + FS, + split, + mapDefined, + filter, + sort, + withoutStart, + computeHash, + hasWindowsSlashes, + join, + flatMap, + unique, + unmangleScopedPackage +} from "@definitelytyped/utils"; function matchesVersion(typingsDataRaw: TypingsDataRaw, version: TypingVersion, considerLibraryMinorVersion: boolean) { - return typingsDataRaw.libraryMajorVersion === version.major - && (considerLibraryMinorVersion ? - (version.minor === undefined || typingsDataRaw.libraryMinorVersion === version.minor) - : true); + return ( + typingsDataRaw.libraryMajorVersion === version.major && + (considerLibraryMinorVersion + ? version.minor === undefined || typingsDataRaw.libraryMinorVersion === version.minor + : true) + ); } function formattedLibraryVersion(typingsDataRaw: TypingsDataRaw) { - return `${typingsDataRaw.libraryMajorVersion}.${typingsDataRaw.libraryMinorVersion}`; + return `${typingsDataRaw.libraryMajorVersion}.${typingsDataRaw.libraryMinorVersion}`; } /** @param fs Rooted at the package's directory, e.g. `DefinitelyTyped/types/abs` */ export function getTypingInfo(packageName: string, fs: FS): TypingsVersionsRaw { - if (packageName !== packageName.toLowerCase()) { - throw new Error(`Package name \`${packageName}\` should be strictly lowercase`); + if (packageName !== packageName.toLowerCase()) { + throw new Error(`Package name \`${packageName}\` should be strictly lowercase`); + } + interface OlderVersionDir { + readonly directoryName: string; + readonly version: TypingVersion; + } + const [rootDirectoryLs, olderVersionDirectories] = split( + fs.readdir(), + fileOrDirectoryName => { + const version = parseVersionFromDirectoryName(fileOrDirectoryName); + return version === undefined ? undefined : { directoryName: fileOrDirectoryName, version }; + } + ); + + const considerLibraryMinorVersion = olderVersionDirectories.some(({ version }) => version.minor !== undefined); + + const latestData: TypingsDataRaw = { + libraryVersionDirectoryName: undefined, + ...combineDataForAllTypesVersions(packageName, rootDirectoryLs, fs, undefined) + }; + + const older = olderVersionDirectories.map(({ directoryName, version: directoryVersion }) => { + if (matchesVersion(latestData, directoryVersion, considerLibraryMinorVersion)) { + const latest = `${latestData.libraryMajorVersion}.${latestData.libraryMinorVersion}`; + throw new Error( + `The latest version is ${latest}, so the subdirectory '${directoryName}' is not allowed` + + (`v${latest}` === directoryName + ? "." + : `; since it applies to any ${latestData.libraryMajorVersion}.* version, up to and including ${latest}.`) + ); } - interface OlderVersionDir { readonly directoryName: string; readonly version: TypingVersion; } - const [rootDirectoryLs, olderVersionDirectories] = split(fs.readdir(), fileOrDirectoryName => { - const version = parseVersionFromDirectoryName(fileOrDirectoryName); - return version === undefined ? undefined : { directoryName: fileOrDirectoryName, version }; - }); - - const considerLibraryMinorVersion = olderVersionDirectories.some(({ version }) => version.minor !== undefined); - const latestData: TypingsDataRaw = { - libraryVersionDirectoryName: undefined, - ...combineDataForAllTypesVersions(packageName, rootDirectoryLs, fs, undefined), + const ls = fs.readdir(directoryName); + const data: TypingsDataRaw = { + libraryVersionDirectoryName: formatTypingVersion(directoryVersion), + ...combineDataForAllTypesVersions(packageName, ls, fs.subDir(directoryName), directoryVersion) }; - const older = olderVersionDirectories.map(({ directoryName, version: directoryVersion }) => { - if (matchesVersion(latestData, directoryVersion, considerLibraryMinorVersion)) { - const latest = `${latestData.libraryMajorVersion}.${latestData.libraryMinorVersion}`; - throw new Error( - `The latest version is ${latest}, so the subdirectory '${directoryName}' is not allowed` + - (`v${latest}` === directoryName ? - "." : `; since it applies to any ${latestData.libraryMajorVersion}.* version, up to and including ${latest}.`), - ); - } - - const ls = fs.readdir(directoryName); - const data: TypingsDataRaw = { - libraryVersionDirectoryName: formatTypingVersion(directoryVersion), - ...combineDataForAllTypesVersions(packageName, ls, fs.subDir(directoryName), directoryVersion), - }; - - if (!matchesVersion(data, directoryVersion, considerLibraryMinorVersion)) { - if (considerLibraryMinorVersion) { - throw new Error( - `Directory ${directoryName} indicates major.minor version ${directoryVersion.major}.${directoryVersion.minor}, ` + - `but header indicates major.minor version ${data.libraryMajorVersion}.${data.libraryMinorVersion}`, - ); - } - throw new Error( - `Directory ${directoryName} indicates major version ${directoryVersion.major}, but header indicates major version ` + - data.libraryMajorVersion.toString(), - ); - } - return data; - }); - - const res: TypingsVersionsRaw = {}; - res[formattedLibraryVersion(latestData)] = latestData; - for (const o of older) { - res[formattedLibraryVersion(o)] = o; + if (!matchesVersion(data, directoryVersion, considerLibraryMinorVersion)) { + if (considerLibraryMinorVersion) { + throw new Error( + `Directory ${directoryName} indicates major.minor version ${directoryVersion.major}.${directoryVersion.minor}, ` + + `but header indicates major.minor version ${data.libraryMajorVersion}.${data.libraryMinorVersion}` + ); + } + throw new Error( + `Directory ${directoryName} indicates major version ${directoryVersion.major}, but header indicates major version ` + + data.libraryMajorVersion.toString() + ); } - return res; + return data; + }); + + const res: TypingsVersionsRaw = {}; + res[formattedLibraryVersion(latestData)] = latestData; + for (const o of older) { + res[formattedLibraryVersion(o)] = o; + } + return res; } const packageJsonName = "package.json"; interface LsMinusTypesVersionsAndPackageJson { - readonly remainingLs: ReadonlyArray; - readonly typesVersions: ReadonlyArray; - readonly hasPackageJson: boolean; + readonly remainingLs: readonly string[]; + readonly typesVersions: readonly TypeScriptVersion[]; + readonly hasPackageJson: boolean; } -function getTypesVersionsAndPackageJson(ls: ReadonlyArray): LsMinusTypesVersionsAndPackageJson { - const withoutPackageJson = ls.filter(name => name !== packageJsonName); - const [remainingLs, typesVersions] = split(withoutPackageJson, fileOrDirectoryName => { - const match = /^ts(\d+\.\d+)$/.exec(fileOrDirectoryName); - if (match === null) { return undefined; } - - const version = match[1]; - if (!isTypeScriptVersion(version)) { - throw new Error(`Directory name starting with 'ts' should be a valid TypeScript version. Got: ${version}`); - } - return version; - }); - return { remainingLs, typesVersions, hasPackageJson: withoutPackageJson.length !== ls.length }; +function getTypesVersionsAndPackageJson(ls: readonly string[]): LsMinusTypesVersionsAndPackageJson { + const withoutPackageJson = ls.filter(name => name !== packageJsonName); + const [remainingLs, typesVersions] = split(withoutPackageJson, fileOrDirectoryName => { + const match = /^ts(\d+\.\d+)$/.exec(fileOrDirectoryName); + if (match === null) { + return undefined; + } + + const version = match[1]; + if (!isTypeScriptVersion(version)) { + throw new Error(`Directory name starting with 'ts' should be a valid TypeScript version. Got: ${version}`); + } + return version; + }); + return { remainingLs, typesVersions, hasPackageJson: withoutPackageJson.length !== ls.length }; } /** @@ -115,79 +139,107 @@ function getTypesVersionsAndPackageJson(ls: ReadonlyArray): LsMinusTypes * ``` */ export function parseVersionFromDirectoryName(directoryName: string): TypingVersion | undefined { - const match = /^v(\d+)(\.(\d+))?$/.exec(directoryName); - if (match === null) { - return undefined; - } - return { - major: Number(match[1]), - minor: match[3] !== undefined ? Number(match[3]) : undefined, // tslint:disable-line strict-type-predicates (false positive) - }; + const match = /^v(\d+)(\.(\d+))?$/.exec(directoryName); + if (match === null) { + return undefined; + } + return { + major: Number(match[1]), + minor: match[3] !== undefined ? Number(match[3]) : undefined // tslint:disable-line strict-type-predicates (false positive) + }; } function combineDataForAllTypesVersions( - typingsPackageName: string, - ls: ReadonlyArray, - fs: FS, - directoryVersion: TypingVersion | undefined, + typingsPackageName: string, + ls: readonly string[], + fs: FS, + directoryVersion: TypingVersion | undefined ): Omit { - const { remainingLs, typesVersions, hasPackageJson } = getTypesVersionsAndPackageJson(ls); - - // Every typesVersion has an index.d.ts, but only the root index.d.ts should have a header. - const { contributors, libraryMajorVersion, libraryMinorVersion, typeScriptVersion: minTsVersion, libraryName, projects } = - parseHeaderOrFail(readFileAndThrowOnBOM("index.d.ts", fs)); - - const dataForRoot = getTypingDataForSingleTypesVersion(undefined, typingsPackageName, fs.debugPath(), remainingLs, fs, directoryVersion); - const dataForOtherTypesVersions = typesVersions.map(tsVersion => { - const subFs = fs.subDir(`ts${tsVersion}`); - return getTypingDataForSingleTypesVersion(tsVersion, typingsPackageName, fs.debugPath(), subFs.readdir(), subFs, directoryVersion); - }); - const allTypesVersions = [dataForRoot, ...dataForOtherTypesVersions]; - - const packageJson = hasPackageJson ? fs.readJson(packageJsonName) as { readonly license?: unknown, readonly dependencies?: unknown } : {}; - const license = getLicenseFromPackageJson(packageJson.license); - const packageJsonDependencies = checkPackageJsonDependencies(packageJson.dependencies, packageJsonName); - - const files = Array.from(flatMap(allTypesVersions, ({ typescriptVersion, declFiles }) => - declFiles.map(file => - typescriptVersion === undefined ? file : `ts${typescriptVersion}/${file}`))); - - return { - libraryName, - typingsPackageName, - projectName: projects[0], // TODO: collect multiple project names - contributors, - libraryMajorVersion, - libraryMinorVersion, - minTsVersion, - typesVersions, - files, - license, - // TODO: Explicit type arguments shouldn't be necessary. https://github.com/Microsoft/TypeScript/issues/27507 - dependencies: getAllUniqueValues<"dependencies", PackageId>(allTypesVersions, "dependencies"), - testDependencies: getAllUniqueValues<"testDependencies", string>(allTypesVersions, "testDependencies"), - pathMappings: getAllUniqueValues<"pathMappings", PathMapping>(allTypesVersions, "pathMappings"), - packageJsonDependencies, - contentHash: hash(hasPackageJson ? [...files, packageJsonName] : files, mapDefined(allTypesVersions, a => a.tsconfigPathsForHash), fs), - globals: getAllUniqueValues<"globals", string>(allTypesVersions, "globals"), - declaredModules: getAllUniqueValues<"declaredModules", string>(allTypesVersions, "declaredModules"), - }; + const { remainingLs, typesVersions, hasPackageJson } = getTypesVersionsAndPackageJson(ls); + + // Every typesVersion has an index.d.ts, but only the root index.d.ts should have a header. + const { + contributors, + libraryMajorVersion, + libraryMinorVersion, + typeScriptVersion: minTsVersion, + libraryName, + projects + } = parseHeaderOrFail(readFileAndThrowOnBOM("index.d.ts", fs)); + + const dataForRoot = getTypingDataForSingleTypesVersion( + undefined, + typingsPackageName, + fs.debugPath(), + remainingLs, + fs, + directoryVersion + ); + const dataForOtherTypesVersions = typesVersions.map(tsVersion => { + const subFs = fs.subDir(`ts${tsVersion}`); + return getTypingDataForSingleTypesVersion( + tsVersion, + typingsPackageName, + fs.debugPath(), + subFs.readdir(), + subFs, + directoryVersion + ); + }); + const allTypesVersions = [dataForRoot, ...dataForOtherTypesVersions]; + + const packageJson = hasPackageJson + ? (fs.readJson(packageJsonName) as { readonly license?: unknown; readonly dependencies?: unknown }) + : {}; + const license = getLicenseFromPackageJson(packageJson.license); + const packageJsonDependencies = checkPackageJsonDependencies(packageJson.dependencies, packageJsonName); + + const files = Array.from( + flatMap(allTypesVersions, ({ typescriptVersion, declFiles }) => + declFiles.map(file => (typescriptVersion === undefined ? file : `ts${typescriptVersion}/${file}`)) + ) + ); + + return { + libraryName, + typingsPackageName, + projectName: projects[0], // TODO: collect multiple project names + contributors, + libraryMajorVersion, + libraryMinorVersion, + minTsVersion, + typesVersions, + files, + license, + // TODO: Explicit type arguments shouldn't be necessary. https://github.com/Microsoft/TypeScript/issues/27507 + dependencies: getAllUniqueValues<"dependencies", PackageId>(allTypesVersions, "dependencies"), + testDependencies: getAllUniqueValues<"testDependencies", string>(allTypesVersions, "testDependencies"), + pathMappings: getAllUniqueValues<"pathMappings", PathMapping>(allTypesVersions, "pathMappings"), + packageJsonDependencies, + contentHash: hash( + hasPackageJson ? [...files, packageJsonName] : files, + mapDefined(allTypesVersions, a => a.tsconfigPathsForHash), + fs + ), + globals: getAllUniqueValues<"globals", string>(allTypesVersions, "globals"), + declaredModules: getAllUniqueValues<"declaredModules", string>(allTypesVersions, "declaredModules") + }; } -function getAllUniqueValues(records: ReadonlyArray>>, key: K): ReadonlyArray { - return unique(flatMap(records, x => x[key])); +function getAllUniqueValues(records: readonly Record[], key: K): readonly T[] { + return unique(flatMap(records, x => x[key])); } interface TypingDataFromIndividualTypeScriptVersion { - /** Undefined for root (which uses `// TypeScript Version: ` comment instead) */ - readonly typescriptVersion: TypeScriptVersion | undefined; - readonly dependencies: ReadonlyArray; - readonly testDependencies: ReadonlyArray; - readonly pathMappings: ReadonlyArray; - readonly declFiles: ReadonlyArray; - readonly tsconfigPathsForHash: string | undefined; - readonly globals: ReadonlyArray; - readonly declaredModules: ReadonlyArray; + /** Undefined for root (which uses `// TypeScript Version: ` comment instead) */ + readonly typescriptVersion: TypeScriptVersion | undefined; + readonly dependencies: readonly PackageId[]; + readonly testDependencies: readonly string[]; + readonly pathMappings: readonly PathMapping[]; + readonly declFiles: readonly string[]; + readonly tsconfigPathsForHash: string | undefined; + readonly globals: readonly string[]; + readonly declaredModules: readonly string[]; } /** @@ -197,302 +249,366 @@ interface TypingDataFromIndividualTypeScriptVersion { * @param fs FS rooted at the directory for this particular TS version, e.g. `types/abs/ts3.1` or `types/abs` when typescriptVersion is undefined. */ function getTypingDataForSingleTypesVersion( - typescriptVersion: TypeScriptVersion | undefined, - packageName: string, - packageDirectory: string, - ls: ReadonlyArray, - fs: FS, - directoryVersion: TypingVersion | undefined, + typescriptVersion: TypeScriptVersion | undefined, + packageName: string, + packageDirectory: string, + ls: readonly string[], + fs: FS, + directoryVersion: TypingVersion | undefined ): TypingDataFromIndividualTypeScriptVersion { - const tsconfig = fs.readJson("tsconfig.json") as TsConfig; - checkFilesFromTsConfig(packageName, tsconfig, fs.debugPath()); - const { types, tests } = allReferencedFiles(tsconfig.files!, fs, packageName, packageDirectory); - const usedFiles = new Set([...types.keys(), ...tests.keys(), "tsconfig.json", "tslint.json"]); - const otherFiles = ls.indexOf(unusedFilesName) > -1 ? (fs.readFile(unusedFilesName)).split(/\r?\n/g).filter(Boolean) : []; - checkAllFilesUsed(ls, usedFiles, otherFiles, packageName, fs); - for (const untestedTypeFile of filter(otherFiles, name => name.endsWith(".d.ts"))) { - // add d.ts files from OTHER_FILES.txt in order get their dependencies - types.set(untestedTypeFile, createSourceFile(untestedTypeFile, fs.readFile(untestedTypeFile))); - } - - const { dependencies: dependenciesWithDeclaredModules, globals, declaredModules } = getModuleInfo(packageName, types); - const declaredModulesSet = new Set(declaredModules); - // Don't count an import of "x" as a dependency if we saw `declare module "x"` somewhere. - const dependenciesSet = new Set(filter(dependenciesWithDeclaredModules, m => !declaredModulesSet.has(m))); - const testDependencies = Array.from( - filter( - getTestDependencies(packageName, types, tests.keys(), dependenciesSet, fs), - m => !declaredModulesSet.has(m), - ), - ); - - const { dependencies, pathMappings } = calculateDependencies(packageName, tsconfig, dependenciesSet, directoryVersion); - const tsconfigPathsForHash = JSON.stringify(tsconfig.compilerOptions.paths); - return { - typescriptVersion, - dependencies, - testDependencies, - pathMappings, - globals, - declaredModules, - declFiles: sort(types.keys()), - tsconfigPathsForHash, - }; + const tsconfig = fs.readJson("tsconfig.json") as TsConfig; + checkFilesFromTsConfig(packageName, tsconfig, fs.debugPath()); + const { types, tests } = allReferencedFiles(tsconfig.files!, fs, packageName, packageDirectory); + const usedFiles = new Set([...types.keys(), ...tests.keys(), "tsconfig.json", "tslint.json"]); + const otherFiles = + ls.indexOf(unusedFilesName) > -1 + ? fs + .readFile(unusedFilesName) + .split(/\r?\n/g) + .filter(Boolean) + : []; + checkAllFilesUsed(ls, usedFiles, otherFiles, packageName, fs); + for (const untestedTypeFile of filter(otherFiles, name => name.endsWith(".d.ts"))) { + // add d.ts files from OTHER_FILES.txt in order get their dependencies + types.set(untestedTypeFile, createSourceFile(untestedTypeFile, fs.readFile(untestedTypeFile))); + } + + const { dependencies: dependenciesWithDeclaredModules, globals, declaredModules } = getModuleInfo(packageName, types); + const declaredModulesSet = new Set(declaredModules); + // Don't count an import of "x" as a dependency if we saw `declare module "x"` somewhere. + const dependenciesSet = new Set(filter(dependenciesWithDeclaredModules, m => !declaredModulesSet.has(m))); + const testDependencies = Array.from( + filter(getTestDependencies(packageName, types, tests.keys(), dependenciesSet, fs), m => !declaredModulesSet.has(m)) + ); + + const { dependencies, pathMappings } = calculateDependencies( + packageName, + tsconfig, + dependenciesSet, + directoryVersion + ); + const tsconfigPathsForHash = JSON.stringify(tsconfig.compilerOptions.paths); + return { + typescriptVersion, + dependencies, + testDependencies, + pathMappings, + globals, + declaredModules, + declFiles: sort(types.keys()), + tsconfigPathsForHash + }; } -function checkPackageJsonDependencies(dependencies: unknown, path: string): ReadonlyArray { - if (dependencies === undefined) { // tslint:disable-line strict-type-predicates (false positive) - return []; - } - if (dependencies === null || typeof dependencies !== "object") { // tslint:disable-line strict-type-predicates - throw new Error(`${path} should contain "dependencies" or not exist.`); - } - - const deps: PackageJsonDependency[] = []; - - for (const dependencyName of Object.keys(dependencies!)) { // `dependencies` cannot be null because of check above. - if (!dependenciesWhitelist.has(dependencyName)) { - const msg = dependencyName.startsWith("@types/") - ? `Dependency ${dependencyName} not in whitelist. +function checkPackageJsonDependencies(dependencies: unknown, path: string): readonly PackageJsonDependency[] { + if (dependencies === undefined) { + // tslint:disable-line strict-type-predicates (false positive) + return []; + } + if (dependencies === null || typeof dependencies !== "object") { + // tslint:disable-line strict-type-predicates + throw new Error(`${path} should contain "dependencies" or not exist.`); + } + + const deps: PackageJsonDependency[] = []; + + for (const dependencyName of Object.keys(dependencies!)) { + // `dependencies` cannot be null because of check above. + if (!dependenciesWhitelist.has(dependencyName)) { + const msg = dependencyName.startsWith("@types/") + ? `Dependency ${dependencyName} not in whitelist. Don't use a 'package.json' for @types dependencies unless this package relies on an old version of types that have since been moved to the source repo. For example, if package *P* used to have types on Definitely Typed at @types/P, but now has its own types, a dependent package *D* will need to use package.json to refer to @types/P if it relies on old versions of P's types. In this case, please make a pull request to types-publisher adding @types/P to \`dependenciesWhitelist.txt\`.` - : `Dependency ${dependencyName} not in whitelist. + : `Dependency ${dependencyName} not in whitelist. If you are depending on another \`@types\` package, do *not* add it to a \`package.json\`. Path mapping should make the import work. For namespaced dependencies you then have to add a \`paths\` mapping from \`@namespace/library\` to \`namespace__library\` in \`tsconfig.json\`. If this is an external library that provides typings, please make a pull request to types-publisher adding it to \`dependenciesWhitelist.txt\`.`; - throw new Error(`In ${path}: ${msg}`); - } + throw new Error(`In ${path}: ${msg}`); + } - const version = (dependencies as { [key: string]: unknown })[dependencyName]; - if (typeof version !== "string") { // tslint:disable-line strict-type-predicates - throw new Error(`In ${path}: Dependency version for ${dependencyName} should be a string.`); - } - deps.push({ name: dependencyName, version }); + const version = (dependencies as { [key: string]: unknown })[dependencyName]; + if (typeof version !== "string") { + // tslint:disable-line strict-type-predicates + throw new Error(`In ${path}: Dependency version for ${dependencyName} should be a string.`); } + deps.push({ name: dependencyName, version }); + } - return deps; + return deps; } function checkFilesFromTsConfig(packageName: string, tsconfig: TsConfig, directoryPath: string): void { - const tsconfigPath = `${directoryPath}/tsconfig.json`; - if (tsconfig.include) { - throw new Error(`In tsconfig, don't use "include", must use "files"`); - } - - const files = tsconfig.files; - if (!files) { - throw new Error(`${tsconfigPath} needs to specify "files"`); + const tsconfigPath = `${directoryPath}/tsconfig.json`; + if (tsconfig.include) { + throw new Error(`In tsconfig, don't use "include", must use "files"`); + } + + const files = tsconfig.files; + if (!files) { + throw new Error(`${tsconfigPath} needs to specify "files"`); + } + for (const file of files) { + if (file.startsWith("./")) { + throw new Error(`In ${tsconfigPath}: Unnecessary "./" at the start of ${file}`); } - for (const file of files) { - if (file.startsWith("./")) { - throw new Error(`In ${tsconfigPath}: Unnecessary "./" at the start of ${file}`); - } - if (file.endsWith(".d.ts") && file !== "index.d.ts") { - throw new Error(`${packageName}: Only index.d.ts may be listed explicitly in tsconfig's "files" entry. + if (file.endsWith(".d.ts") && file !== "index.d.ts") { + throw new Error(`${packageName}: Only index.d.ts may be listed explicitly in tsconfig's "files" entry. Other d.ts files must either be referenced through index.d.ts, tests, or added to OTHER_FILES.txt.`); - } + } - if (!file.endsWith(".d.ts") && !file.startsWith("test/")) { - const expectedName = `${packageName}-tests.ts`; - if (file !== expectedName && file !== `${expectedName}x`) { - const message = file.endsWith(".ts") || file.endsWith(".tsx") - ? `Expected file '${file}' to be named '${expectedName}' or to be inside a '${directoryPath}/test/' directory` - : (`Unexpected file extension for '${file}' -- expected '.ts' or '.tsx' (maybe this should not be in "files", but ` + - "OTHER_FILES.txt)"); - throw new Error(message); - } - } + if (!file.endsWith(".d.ts") && !file.startsWith("test/")) { + const expectedName = `${packageName}-tests.ts`; + if (file !== expectedName && file !== `${expectedName}x`) { + const message = + file.endsWith(".ts") || file.endsWith(".tsx") + ? `Expected file '${file}' to be named '${expectedName}' or to be inside a '${directoryPath}/test/' directory` + : `Unexpected file extension for '${file}' -- expected '.ts' or '.tsx' (maybe this should not be in "files", but ` + + "OTHER_FILES.txt)"; + throw new Error(message); + } } + } } interface TsConfig { - include?: ReadonlyArray; - files?: ReadonlyArray; - compilerOptions: ts.CompilerOptions; + include?: readonly string[]; + files?: readonly string[]; + compilerOptions: ts.CompilerOptions; } /** In addition to dependencies found in source code, also get dependencies from tsconfig. */ -interface DependenciesAndPathMappings { readonly dependencies: ReadonlyArray; readonly pathMappings: ReadonlyArray; } +interface DependenciesAndPathMappings { + readonly dependencies: readonly PackageId[]; + readonly pathMappings: readonly PathMapping[]; +} function calculateDependencies( - packageName: string, - tsconfig: TsConfig, - dependencyNames: ReadonlySet, - directoryVersion: TypingVersion | undefined, + packageName: string, + tsconfig: TsConfig, + dependencyNames: ReadonlySet, + directoryVersion: TypingVersion | undefined ): DependenciesAndPathMappings { - const paths = tsconfig.compilerOptions && tsconfig.compilerOptions.paths || {}; - - const dependencies: PackageId[] = []; - const pathMappings: PathMapping[] = []; - - for (const dependencyName of Object.keys(paths)) { - // Might have a path mapping for "foo/*" to support subdirectories - const rootDirectory = withoutEnd(dependencyName, "/*"); - if (rootDirectory !== undefined) { - if (!(rootDirectory in paths)) { - throw new Error(`In ${packageName}: found path mapping for ${dependencyName} but not for ${rootDirectory}`); - } - continue; - } - - const pathMappingList = paths[dependencyName]; - if (pathMappingList.length !== 1) { - throw new Error(`In ${packageName}: Path mapping for ${dependencyName} may only have 1 entry.`); - } - const pathMapping = pathMappingList[0]; - - // Path mapping may be for "@foo/bar" -> "foo__bar". - const scopedPackageName = unmangleScopedPackage(pathMapping); - if (scopedPackageName !== undefined) { - if (dependencyName !== scopedPackageName) { - throw new Error(`Expected directory ${pathMapping} to be the path mapping for ${dependencyName}`); - } - continue; - } + const paths = (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) || {}; + + const dependencies: PackageId[] = []; + const pathMappings: PathMapping[] = []; + + for (const dependencyName of Object.keys(paths)) { + // Might have a path mapping for "foo/*" to support subdirectories + const rootDirectory = withoutEnd(dependencyName, "/*"); + if (rootDirectory !== undefined) { + if (!(rootDirectory in paths)) { + throw new Error(`In ${packageName}: found path mapping for ${dependencyName} but not for ${rootDirectory}`); + } + continue; + } - const pathMappingVersion = parseDependencyVersionFromPath(dependencyName, dependencyName, pathMapping); - if (dependencyName === packageName) { - if (directoryVersion === undefined) { - throw new Error(`In ${packageName}: Latest version of a package should not have a path mapping for itself.`); - } - if ( - directoryVersion.major !== pathMappingVersion.major - || directoryVersion.minor !== pathMappingVersion.minor - ) { - const correctPathMapping = [`${dependencyName}/v${formatTypingVersion(directoryVersion)}`]; - throw new Error(`In ${packageName}: Must have a "paths" entry of "${dependencyName}": ${JSON.stringify(correctPathMapping)}`); - } - } else { - if (dependencyNames.has(dependencyName)) { - dependencies.push({ name: dependencyName, version: pathMappingVersion }); - } - } - // Else, the path mapping may be necessary if it is for a transitive dependency. We will check this in check-parse-results. - pathMappings.push({ packageName: dependencyName, version: pathMappingVersion }); + const pathMappingList = paths[dependencyName]; + if (pathMappingList.length !== 1) { + throw new Error(`In ${packageName}: Path mapping for ${dependencyName} may only have 1 entry.`); + } + const pathMapping = pathMappingList[0]; + + // Path mapping may be for "@foo/bar" -> "foo__bar". + const scopedPackageName = unmangleScopedPackage(pathMapping); + if (scopedPackageName !== undefined) { + if (dependencyName !== scopedPackageName) { + throw new Error(`Expected directory ${pathMapping} to be the path mapping for ${dependencyName}`); + } + continue; } - if (directoryVersion !== undefined && !(paths && packageName in paths)) { - const mapping = JSON.stringify([`${packageName}/v${formatTypingVersion(directoryVersion)}`]); + const pathMappingVersion = parseDependencyVersionFromPath(dependencyName, dependencyName, pathMapping); + if (dependencyName === packageName) { + if (directoryVersion === undefined) { + throw new Error(`In ${packageName}: Latest version of a package should not have a path mapping for itself.`); + } + if (directoryVersion.major !== pathMappingVersion.major || directoryVersion.minor !== pathMappingVersion.minor) { + const correctPathMapping = [`${dependencyName}/v${formatTypingVersion(directoryVersion)}`]; throw new Error( - `${packageName}: Older version ${formatTypingVersion(directoryVersion)} must have a "paths" entry of "${packageName}": ${mapping}`, + `In ${packageName}: Must have a "paths" entry of "${dependencyName}": ${JSON.stringify(correctPathMapping)}` ); + } + } else { + if (dependencyNames.has(dependencyName)) { + dependencies.push({ name: dependencyName, version: pathMappingVersion }); + } } + // Else, the path mapping may be necessary if it is for a transitive dependency. We will check this in check-parse-results. + pathMappings.push({ packageName: dependencyName, version: pathMappingVersion }); + } + + if (directoryVersion !== undefined && !(paths && packageName in paths)) { + const mapping = JSON.stringify([`${packageName}/v${formatTypingVersion(directoryVersion)}`]); + throw new Error( + `${packageName}: Older version ${formatTypingVersion( + directoryVersion + )} must have a "paths" entry of "${packageName}": ${mapping}` + ); + } - for (const dependency of dependencyNames) { - if (!dependencies.some(d => d.name === dependency) && !nodeBuiltins.has(dependency)) { - dependencies.push({ name: dependency, version: "*" }); - } + for (const dependency of dependencyNames) { + if (!dependencies.some(d => d.name === dependency) && !nodeBuiltins.has(dependency)) { + dependencies.push({ name: dependency, version: "*" }); } + } - return { dependencies, pathMappings }; + return { dependencies, pathMappings }; } const nodeBuiltins: ReadonlySet = new Set([ - "assert", "async_hooks", "buffer", "child_process", "cluster", "console", "constants", "crypto", - "dgram", "dns", "domain", "events", "fs", "http", "http2", "https", "module", "net", "os", - "path", "perf_hooks", "process", "punycode", "querystring", "readline", "repl", "stream", - "string_decoder", "timers", "tls", "tty", "url", "util", "v8", "vm", "zlib", + "assert", + "async_hooks", + "buffer", + "child_process", + "cluster", + "console", + "constants", + "crypto", + "dgram", + "dns", + "domain", + "events", + "fs", + "http", + "http2", + "https", + "module", + "net", + "os", + "path", + "perf_hooks", + "process", + "punycode", + "querystring", + "readline", + "repl", + "stream", + "string_decoder", + "timers", + "tls", + "tty", + "url", + "util", + "v8", + "vm", + "zlib" ]); -function parseDependencyVersionFromPath(packageName: string, dependencyName: string, dependencyPath: string): TypingVersion { - const versionString = withoutStart(dependencyPath, `${dependencyName}/`); - const version = versionString === undefined ? undefined : parseVersionFromDirectoryName(versionString); - if (version === undefined) { - throw new Error(`In ${packageName}, unexpected path mapping for ${dependencyName}: '${dependencyPath}'`); - } - return version; +function parseDependencyVersionFromPath( + packageName: string, + dependencyName: string, + dependencyPath: string +): TypingVersion { + const versionString = withoutStart(dependencyPath, `${dependencyName}/`); + const version = versionString === undefined ? undefined : parseVersionFromDirectoryName(versionString); + if (version === undefined) { + throw new Error(`In ${packageName}, unexpected path mapping for ${dependencyName}: '${dependencyPath}'`); + } + return version; } function withoutEnd(s: string, end: string): string | undefined { - if (s.endsWith(end)) { - return s.slice(0, s.length - end.length); - } - return undefined; + if (s.endsWith(end)) { + return s.slice(0, s.length - end.length); + } + return undefined; } -function hash(files: ReadonlyArray, tsconfigPathsForHash: ReadonlyArray, fs: FS): string { - const fileContents = files.map(f => `${f}**${readFileAndThrowOnBOM(f, fs)}`); - let allContent = fileContents.join("||"); - for (const path of tsconfigPathsForHash) { - allContent += path; - } - return computeHash(allContent); +function hash(files: readonly string[], tsconfigPathsForHash: readonly string[], fs: FS): string { + const fileContents = files.map(f => `${f}**${readFileAndThrowOnBOM(f, fs)}`); + let allContent = fileContents.join("||"); + for (const path of tsconfigPathsForHash) { + allContent += path; + } + return computeHash(allContent); } export function readFileAndThrowOnBOM(fileName: string, fs: FS): string { - const text = fs.readFile(fileName); - if (text.charCodeAt(0) === 0xFEFF) { - const commands = [ - "npm install -g strip-bom-cli", - `strip-bom ${fileName} > fix`, - `mv fix ${fileName}`, - ]; - throw new Error(`File '${fileName}' has a BOM. Try using:\n${commands.join("\n")}`); - } - return text; + const text = fs.readFile(fileName); + if (text.charCodeAt(0) === 0xfeff) { + const commands = ["npm install -g strip-bom-cli", `strip-bom ${fileName} > fix`, `mv fix ${fileName}`]; + throw new Error(`File '${fileName}' has a BOM. Try using:\n${commands.join("\n")}`); + } + return text; } const unusedFilesName = "OTHER_FILES.txt"; -function checkAllFilesUsed(ls: ReadonlyArray, usedFiles: Set, otherFiles: string[], packageName: string, fs: FS): void { - // Double-check that no windows "\\" broke in. - for (const fileName of usedFiles) { - if (hasWindowsSlashes(fileName)) { - throw new Error(`In ${packageName}: windows slash detected in ${fileName}`); - } +function checkAllFilesUsed( + ls: readonly string[], + usedFiles: Set, + otherFiles: string[], + packageName: string, + fs: FS +): void { + // Double-check that no windows "\\" broke in. + for (const fileName of usedFiles) { + if (hasWindowsSlashes(fileName)) { + throw new Error(`In ${packageName}: windows slash detected in ${fileName}`); } - checkAllUsedRecur(new Set(ls), usedFiles, new Set(otherFiles), fs); + } + checkAllUsedRecur(new Set(ls), usedFiles, new Set(otherFiles), fs); } function checkAllUsedRecur(ls: Iterable, usedFiles: Set, unusedFiles: Set, fs: FS): void { - for (const lsEntry of ls) { - if (usedFiles.has(lsEntry)) { - continue; - } - if (unusedFiles.has(lsEntry)) { - unusedFiles.delete(lsEntry); - continue; - } + for (const lsEntry of ls) { + if (usedFiles.has(lsEntry)) { + continue; + } + if (unusedFiles.has(lsEntry)) { + unusedFiles.delete(lsEntry); + continue; + } - if (fs.isDirectory(lsEntry)) { - const subdir = fs.subDir(lsEntry); - // We allow a "scripts" directory to be used for scripts. - if (lsEntry === "node_modules" || lsEntry === "scripts") { - continue; - } - - const lssubdir = subdir.readdir(); - if (lssubdir.length === 0) { - // tslint:disable-next-line strict-string-expressions - throw new Error(`Empty directory ${subdir} (${join(usedFiles)})`); - } - - function takeSubdirectoryOutOfSet(originalSet: Set): Set { - const subdirSet = new Set(); - for (const file of originalSet) { - const sub = withoutStart(file, `${lsEntry}/`); - if (sub !== undefined) { - originalSet.delete(file); - subdirSet.add(sub); - } - } - return subdirSet; - } - checkAllUsedRecur(lssubdir, takeSubdirectoryOutOfSet(usedFiles), takeSubdirectoryOutOfSet(unusedFiles), subdir); - } else { - if (lsEntry.toLowerCase() !== "readme.md" && lsEntry !== "NOTICE" && lsEntry !== ".editorconfig" && lsEntry !== unusedFilesName) { - throw new Error(`Unused file ${fs.debugPath()}/${lsEntry} (used files: ${JSON.stringify(Array.from(usedFiles))})`); - } + if (fs.isDirectory(lsEntry)) { + const subdir = fs.subDir(lsEntry); + // We allow a "scripts" directory to be used for scripts. + if (lsEntry === "node_modules" || lsEntry === "scripts") { + continue; + } + + const lssubdir = subdir.readdir(); + if (lssubdir.length === 0) { + // tslint:disable-next-line strict-string-expressions + throw new Error(`Empty directory ${subdir} (${join(usedFiles)})`); + } + + function takeSubdirectoryOutOfSet(originalSet: Set): Set { + const subdirSet = new Set(); + for (const file of originalSet) { + const sub = withoutStart(file, `${lsEntry}/`); + if (sub !== undefined) { + originalSet.delete(file); + subdirSet.add(sub); + } } + return subdirSet; + } + checkAllUsedRecur(lssubdir, takeSubdirectoryOutOfSet(usedFiles), takeSubdirectoryOutOfSet(unusedFiles), subdir); + } else { + if ( + lsEntry.toLowerCase() !== "readme.md" && + lsEntry !== "NOTICE" && + lsEntry !== ".editorconfig" && + lsEntry !== unusedFilesName + ) { + throw new Error( + `Unused file ${fs.debugPath()}/${lsEntry} (used files: ${JSON.stringify(Array.from(usedFiles))})` + ); + } } + } - for (const unusedFile of unusedFiles) { - if (usedFiles.has(unusedFile)) { - throw new Error(`File ${fs.debugPath()}/${unusedFile} listed in ${unusedFilesName} is already reachable from tsconfig.json.`); - } - throw new Error(`File ${fs.debugPath()}/${unusedFile} listed in ${unusedFilesName} does not exist.`); + for (const unusedFile of unusedFiles) { + if (usedFiles.has(unusedFile)) { + throw new Error( + `File ${fs.debugPath()}/${unusedFile} listed in ${unusedFilesName} is already reachable from tsconfig.json.` + ); } + throw new Error(`File ${fs.debugPath()}/${unusedFile} listed in ${unusedFilesName} does not exist.`); + } } diff --git a/packages/definitions-parser/src/lib/module-info.ts b/packages/definitions-parser/src/lib/module-info.ts index d2d39d0cbe..d2fa93c4c8 100644 --- a/packages/definitions-parser/src/lib/module-info.ts +++ b/packages/definitions-parser/src/lib/module-info.ts @@ -6,77 +6,78 @@ import { sort, joinPaths, FS, normalizeSlashes, hasWindowsSlashes } from "@defin import { readFileAndThrowOnBOM } from "./definition-parser"; export function getModuleInfo(packageName: string, all: Map): ModuleInfo { + const dependencies = new Set(); + const declaredModules: string[] = []; + const globals = new Set(); - const dependencies = new Set(); - const declaredModules: string[] = []; - const globals = new Set(); - - function addDependency(ref: string): void { - if (ref.startsWith(".")) { return; } - const dependency = rootName(ref, all); - if (dependency !== packageName) { - dependencies.add(dependency); - } - // TODO: else throw new Error(`Package ${packageName} references itself. (via ${src.fileName})`); + function addDependency(ref: string): void { + if (ref.startsWith(".")) { + return; } + const dependency = rootName(ref, all); + if (dependency !== packageName) { + dependencies.add(dependency); + } + // TODO: else throw new Error(`Package ${packageName} references itself. (via ${src.fileName})`); + } - for (const sourceFile of all.values()) { - for (const ref of imports(sourceFile)) { - addDependency(ref); - } - for (const ref of sourceFile.typeReferenceDirectives) { - addDependency(ref.fileName); + for (const sourceFile of all.values()) { + for (const ref of imports(sourceFile)) { + addDependency(ref); + } + for (const ref of sourceFile.typeReferenceDirectives) { + addDependency(ref.fileName); + } + if (ts.isExternalModule(sourceFile)) { + if (sourceFileExportsSomething(sourceFile)) { + declaredModules.push(properModuleName(packageName, sourceFile.fileName)); + const namespaceExport = sourceFile.statements.find(ts.isNamespaceExportDeclaration); + if (namespaceExport) { + globals.add(namespaceExport.name.text); } - if (ts.isExternalModule(sourceFile)) { - if (sourceFileExportsSomething(sourceFile)) { - declaredModules.push(properModuleName(packageName, sourceFile.fileName)); - const namespaceExport = sourceFile.statements.find(ts.isNamespaceExportDeclaration); - if (namespaceExport) { - globals.add(namespaceExport.name.text); - } + } + } else { + for (const node of sourceFile.statements) { + switch (node.kind) { + case ts.SyntaxKind.ModuleDeclaration: { + const decl = node as ts.ModuleDeclaration; + const name = decl.name.text; + if (decl.name.kind === ts.SyntaxKind.StringLiteral) { + declaredModules.push(assertNoWindowsSlashes(packageName, name)); + } else if (isValueNamespace(decl)) { + globals.add(name); + } + break; + } + case ts.SyntaxKind.VariableStatement: + for (const decl of (node as ts.VariableStatement).declarationList.declarations) { + if (decl.name.kind === ts.SyntaxKind.Identifier) { + globals.add(decl.name.text); + } } - } else { - for (const node of sourceFile.statements) { - switch (node.kind) { - case ts.SyntaxKind.ModuleDeclaration: { - const decl = node as ts.ModuleDeclaration; - const name = decl.name.text; - if (decl.name.kind === ts.SyntaxKind.StringLiteral) { - declaredModules.push(assertNoWindowsSlashes(packageName, name)); - } else if (isValueNamespace(decl)) { - globals.add(name); - } - break; - } - case ts.SyntaxKind.VariableStatement: - for (const decl of (node as ts.VariableStatement).declarationList.declarations) { - if (decl.name.kind === ts.SyntaxKind.Identifier) { - globals.add(decl.name.text); - } - } - break; - case ts.SyntaxKind.EnumDeclaration: - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.FunctionDeclaration: { - // Deliberately not doing this for types, because those won't show up in JS code and can't be used for ATA - const nameNode = (node as ts.EnumDeclaration | ts.ClassDeclaration | ts.FunctionDeclaration).name; - if (nameNode) { - globals.add(nameNode.text); - } - break; - } - case ts.SyntaxKind.ImportEqualsDeclaration: - case ts.SyntaxKind.InterfaceDeclaration: - case ts.SyntaxKind.TypeAliasDeclaration: - break; - default: - throw new Error(`Unexpected node kind ${ts.SyntaxKind[node.kind]}`); - } + break; + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.FunctionDeclaration: { + // Deliberately not doing this for types, because those won't show up in JS code and can't be used for ATA + const nameNode = (node as ts.EnumDeclaration | ts.ClassDeclaration | ts.FunctionDeclaration).name; + if (nameNode) { + globals.add(nameNode.text); } + break; + } + case ts.SyntaxKind.ImportEqualsDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + break; + default: + throw new Error(`Unexpected node kind ${ts.SyntaxKind[node.kind]}`); } + } } + } - return { dependencies, declaredModules, globals: sort(globals) }; + return { dependencies, declaredModules, globals: sort(globals) }; } /** @@ -84,25 +85,25 @@ export function getModuleInfo(packageName: string, all: Map { - switch (statement.kind) { - case ts.SyntaxKind.ImportEqualsDeclaration: - case ts.SyntaxKind.ImportDeclaration: - return false; - case ts.SyntaxKind.ModuleDeclaration: - return (statement as ts.ModuleDeclaration).name.kind === ts.SyntaxKind.Identifier; - default: - return true; - } - }); + return statements.some(statement => { + switch (statement.kind) { + case ts.SyntaxKind.ImportEqualsDeclaration: + case ts.SyntaxKind.ImportDeclaration: + return false; + case ts.SyntaxKind.ModuleDeclaration: + return (statement as ts.ModuleDeclaration).name.kind === ts.SyntaxKind.Identifier; + default: + return true; + } + }); } interface ModuleInfo { - dependencies: Set; - // Anything from a `declare module "foo"` - declaredModules: string[]; - // Every global symbol - globals: string[]; + dependencies: Set; + // Anything from a `declare module "foo"` + declaredModules: string[]; + // Every global symbol + globals: string[]; } /** @@ -110,8 +111,8 @@ interface ModuleInfo { * `foo/index.d.ts` declares "foo", `foo/bar.d.ts` declares "foo/bar", "foo/bar/index.d.ts" declares "foo/bar" */ function properModuleName(folderName: string, fileName: string): string { - const part = path.basename(fileName) === "index.d.ts" ? path.dirname(fileName) : withoutExtension(fileName, ".d.ts"); - return part === "." ? folderName : joinPaths(folderName, part); + const part = path.basename(fileName) === "index.d.ts" ? path.dirname(fileName) : withoutExtension(fileName, ".d.ts"); + return part === "." ? folderName : joinPaths(folderName, part); } /** @@ -119,84 +120,87 @@ function properModuleName(folderName: string, fileName: string): string { * Note: Throws an error for references like */ function rootName(importText: string, typeFiles: Map): string { - let slash = importText.indexOf("/"); - // Root of `@foo/bar/baz` is `@foo/bar` - if (importText.startsWith("@")) { - // Use second "/" - slash = importText.indexOf("/", slash + 1); - } - const root = importText.slice(0, slash); - const postImport = importText.slice(slash + 1); - if (slash > -1 && postImport.match(/v\d+$/) && !typeFiles.has(postImport + ".d.ts")) { - throw new Error(`${importText}: do not directly import specific versions of another types package. + let slash = importText.indexOf("/"); + // Root of `@foo/bar/baz` is `@foo/bar` + if (importText.startsWith("@")) { + // Use second "/" + slash = importText.indexOf("/", slash + 1); + } + const root = importText.slice(0, slash); + const postImport = importText.slice(slash + 1); + if (slash > -1 && postImport.match(/v\d+$/) && !typeFiles.has(postImport + ".d.ts")) { + throw new Error(`${importText}: do not directly import specific versions of another types package. You should work with the latest version of ${root} instead.`); - } - return slash === -1 ? importText : root; + } + return slash === -1 ? importText : root; } function withoutExtension(str: string, ext: string): string { - assert(str.endsWith(ext)); - return str.slice(0, str.length - ext.length); + assert(str.endsWith(ext)); + return str.slice(0, str.length - ext.length); } /** Returns a map from filename (path relative to `directory`) to the SourceFile we parsed for it. */ export function allReferencedFiles( - entryFilenames: ReadonlyArray, fs: FS, packageName: string, baseDirectory: string, -): { types: Map, tests: Map } { - const seenReferences = new Set(); - const types = new Map(); - const tests = new Map(); - entryFilenames.forEach(text => recur({ text, exact: true })); - return { types, tests }; - - function recur({ text, exact }: Reference): void { - if (seenReferences.has(text)) { - return; - } - seenReferences.add(text); - - const resolvedFilename = exact ? text : resolveModule(text, fs); - if (fs.exists(resolvedFilename)) { - const src = createSourceFile(resolvedFilename, readFileAndThrowOnBOM(resolvedFilename, fs)); - if (resolvedFilename.endsWith(".d.ts")) { - types.set(resolvedFilename, src); - } else { - tests.set(resolvedFilename, src); - } - - const refs = findReferencedFiles( - src, - packageName, - path.dirname(resolvedFilename), - normalizeSlashes(path.relative(baseDirectory, fs.debugPath())), - ); - refs.forEach(recur); - } + entryFilenames: readonly string[], + fs: FS, + packageName: string, + baseDirectory: string +): { types: Map; tests: Map } { + const seenReferences = new Set(); + const types = new Map(); + const tests = new Map(); + entryFilenames.forEach(text => recur({ text, exact: true })); + return { types, tests }; + + function recur({ text, exact }: Reference): void { + if (seenReferences.has(text)) { + return; } - + seenReferences.add(text); + + const resolvedFilename = exact ? text : resolveModule(text, fs); + if (fs.exists(resolvedFilename)) { + const src = createSourceFile(resolvedFilename, readFileAndThrowOnBOM(resolvedFilename, fs)); + if (resolvedFilename.endsWith(".d.ts")) { + types.set(resolvedFilename, src); + } else { + tests.set(resolvedFilename, src); + } + + const refs = findReferencedFiles( + src, + packageName, + path.dirname(resolvedFilename), + normalizeSlashes(path.relative(baseDirectory, fs.debugPath())) + ); + refs.forEach(recur); + } + } } function resolveModule(importSpecifier: string, fs: FS): string { - importSpecifier = importSpecifier.endsWith("/") ? importSpecifier.slice(0, importSpecifier.length - 1) : importSpecifier; - if (importSpecifier !== "." && importSpecifier !== "..") { - if (fs.exists(importSpecifier + ".d.ts")) { - return importSpecifier + ".d.ts"; - } - if (fs.exists(importSpecifier + ".ts")) { - return importSpecifier + ".ts"; - } - if (fs.exists(importSpecifier + ".tsx")) { - return importSpecifier + ".tsx"; - } + importSpecifier = importSpecifier.endsWith("/") + ? importSpecifier.slice(0, importSpecifier.length - 1) + : importSpecifier; + if (importSpecifier !== "." && importSpecifier !== "..") { + if (fs.exists(importSpecifier + ".d.ts")) { + return importSpecifier + ".d.ts"; } - return importSpecifier === "." ? "index.d.ts" : joinPaths(importSpecifier, "index.d.ts"); - + if (fs.exists(importSpecifier + ".ts")) { + return importSpecifier + ".ts"; + } + if (fs.exists(importSpecifier + ".tsx")) { + return importSpecifier + ".tsx"; + } + } + return importSpecifier === "." ? "index.d.ts" : joinPaths(importSpecifier, "index.d.ts"); } interface Reference { - /** includes exact filename, so true. import "foo" may reference "foo.d.ts" or "foo/index.d.ts", so false. */ - readonly exact: boolean; - text: string; + /** includes exact filename, so true. import "foo" may reference "foo.d.ts" or "foo/index.d.ts", so false. */ + readonly exact: boolean; + text: string; } /** @@ -205,56 +209,61 @@ interface Reference { * versionsBaseDirectory may be "" when not in typesVersions or ".." when inside `react-router/ts3.1` */ function findReferencedFiles(src: ts.SourceFile, packageName: string, subDirectory: string, baseDirectory: string) { - const refs: Reference[] = []; - - for (const ref of src.referencedFiles) { - // Any is assumed to be local - addReference({ text: ref.fileName, exact: true }); - } - for (const ref of src.typeReferenceDirectives) { - // only references are local (or "packagename/x", though in 3.7 that doesn't work in DT). - if (ref.fileName.startsWith("../" + packageName + "/")) { - addReference({ text: ref.fileName, exact: false }); - } else if (ref.fileName.startsWith(packageName + "/")) { - addReference({ text: convertToRelativeReference(ref.fileName), exact: false }); - } + const refs: Reference[] = []; + + for (const ref of src.referencedFiles) { + // Any is assumed to be local + addReference({ text: ref.fileName, exact: true }); + } + for (const ref of src.typeReferenceDirectives) { + // only references are local (or "packagename/x", though in 3.7 that doesn't work in DT). + if (ref.fileName.startsWith("../" + packageName + "/")) { + addReference({ text: ref.fileName, exact: false }); + } else if (ref.fileName.startsWith(packageName + "/")) { + addReference({ text: convertToRelativeReference(ref.fileName), exact: false }); } + } - for (const ref of imports(src)) { - if (ref.startsWith(".")) { - addReference({ text: ref, exact: false }); - } - if (ref.startsWith(packageName + "/")) { - addReference({ text: convertToRelativeReference(ref), exact: false }); - } + for (const ref of imports(src)) { + if (ref.startsWith(".")) { + addReference({ text: ref, exact: false }); } - return refs; - - function addReference(ref: Reference): void { - // `path.normalize` may add windows slashes - const full = normalizeSlashes(path.normalize(joinPaths(subDirectory, assertNoWindowsSlashes(src.fileName, ref.text)))); - // allow files in typesVersions directories (i.e. 'ts3.1') to reference files in parent directory - if (full.startsWith("../" + packageName + "/")) { - ref.text = full.slice(packageName.length + 4); - refs.push(ref); - return; - } - if (full.startsWith("..") - && (baseDirectory === "" || path.normalize(joinPaths(baseDirectory, full)).startsWith(".."))) { - throw new Error( - `${src.fileName}: ` + - 'Definitions must use global references to other packages, not parent ("../xxx") references.' + - `(Based on reference '${ref.text}')`); - } - ref.text = full; - refs.push(ref); + if (ref.startsWith(packageName + "/")) { + addReference({ text: convertToRelativeReference(ref), exact: false }); } - - /** boring/foo -> ./foo when subDirectory === '.'; ../foo when it's === 'x'; ../../foo when it's 'x/y' */ - function convertToRelativeReference(name: string) { - const relative = "." + "/..".repeat(subDirectory === "." ? 0 : subDirectory.split("/").length); - return relative + name.slice(packageName.length); + } + return refs; + + function addReference(ref: Reference): void { + // `path.normalize` may add windows slashes + const full = normalizeSlashes( + path.normalize(joinPaths(subDirectory, assertNoWindowsSlashes(src.fileName, ref.text))) + ); + // allow files in typesVersions directories (i.e. 'ts3.1') to reference files in parent directory + if (full.startsWith("../" + packageName + "/")) { + ref.text = full.slice(packageName.length + 4); + refs.push(ref); + return; + } + if ( + full.startsWith("..") && + (baseDirectory === "" || path.normalize(joinPaths(baseDirectory, full)).startsWith("..")) + ) { + throw new Error( + `${src.fileName}: ` + + 'Definitions must use global references to other packages, not parent ("../xxx") references.' + + `(Based on reference '${ref.text}')` + ); } + ref.text = full; + refs.push(ref); + } + + /** boring/foo -> ./foo when subDirectory === '.'; ../foo when it's === 'x'; ../../foo when it's 'x/y' */ + function convertToRelativeReference(name: string) { + const relative = "." + "/..".repeat(subDirectory === "." ? 0 : subDirectory.split("/").length); + return relative + name.slice(packageName.length); + } } /** @@ -262,144 +271,144 @@ function findReferencedFiles(src: ts.SourceFile, packageName: string, subDirecto * Does *not* include directives. */ function* imports({ statements }: ts.SourceFile | ts.ModuleBlock): Iterable { - for (const node of statements) { - switch (node.kind) { - case ts.SyntaxKind.ImportDeclaration: - case ts.SyntaxKind.ExportDeclaration: { - const { moduleSpecifier } = node as ts.ImportDeclaration | ts.ExportDeclaration; - if (moduleSpecifier && moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) { - yield (moduleSpecifier as ts.StringLiteral).text; - } - break; - } - - case ts.SyntaxKind.ImportEqualsDeclaration: { - const { moduleReference } = node as ts.ImportEqualsDeclaration; - if (moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - yield parseRequire(moduleReference); - } - break; - } + for (const node of statements) { + switch (node.kind) { + case ts.SyntaxKind.ImportDeclaration: + case ts.SyntaxKind.ExportDeclaration: { + const { moduleSpecifier } = node as ts.ImportDeclaration | ts.ExportDeclaration; + if (moduleSpecifier && moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) { + yield (moduleSpecifier as ts.StringLiteral).text; + } + break; + } - case ts.SyntaxKind.ModuleDeclaration: { - const { name, body } = node as ts.ModuleDeclaration; - if (name.kind === ts.SyntaxKind.StringLiteral && body) { - yield* imports(body as ts.ModuleBlock); - } - break; - } + case ts.SyntaxKind.ImportEqualsDeclaration: { + const { moduleReference } = node as ts.ImportEqualsDeclaration; + if (moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + yield parseRequire(moduleReference); + } + break; + } - default: + case ts.SyntaxKind.ModuleDeclaration: { + const { name, body } = node as ts.ModuleDeclaration; + if (name.kind === ts.SyntaxKind.StringLiteral && body) { + yield* imports(body as ts.ModuleBlock); } + break; + } + + default: } + } } function parseRequire(reference: ts.ExternalModuleReference): string { - const { expression } = reference; - if (!expression || !ts.isStringLiteral(expression)) { - throw new Error(`Bad 'import =' reference: ${reference.getText()}`); - } - return expression.text; + const { expression } = reference; + if (!expression || !ts.isStringLiteral(expression)) { + throw new Error(`Bad 'import =' reference: ${reference.getText()}`); + } + return expression.text; } function isValueNamespace(ns: ts.ModuleDeclaration): boolean { - if (!ns.body) { - throw new Error("@types should not use shorthand ambient modules"); - } - return ns.body.kind === ts.SyntaxKind.ModuleDeclaration - ? isValueNamespace(ns.body as ts.ModuleDeclaration) - : (ns.body as ts.ModuleBlock).statements.some(statementDeclaresValue); + if (!ns.body) { + throw new Error("@types should not use shorthand ambient modules"); + } + return ns.body.kind === ts.SyntaxKind.ModuleDeclaration + ? isValueNamespace(ns.body as ts.ModuleDeclaration) + : (ns.body as ts.ModuleBlock).statements.some(statementDeclaresValue); } function statementDeclaresValue(statement: ts.Statement): boolean { - switch (statement.kind) { - case ts.SyntaxKind.VariableStatement: - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.EnumDeclaration: - return true; - - case ts.SyntaxKind.ModuleDeclaration: - return isValueNamespace(statement as ts.ModuleDeclaration); - - case ts.SyntaxKind.InterfaceDeclaration: - case ts.SyntaxKind.TypeAliasDeclaration: - case ts.SyntaxKind.ImportEqualsDeclaration: - return false; - - default: - throw new Error(`Forgot to implement ambient namespace statement ${ts.SyntaxKind[statement.kind]}`); - } + switch (statement.kind) { + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.EnumDeclaration: + return true; + + case ts.SyntaxKind.ModuleDeclaration: + return isValueNamespace(statement as ts.ModuleDeclaration); + + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.ImportEqualsDeclaration: + return false; + + default: + throw new Error(`Forgot to implement ambient namespace statement ${ts.SyntaxKind[statement.kind]}`); + } } function assertNoWindowsSlashes(packageName: string, fileName: string): string { - if (hasWindowsSlashes(fileName)) { - throw new Error(`In ${packageName}: Use forward slash instead when referencing ${fileName}`); - } - return fileName; + if (hasWindowsSlashes(fileName)) { + throw new Error(`In ${packageName}: Use forward slash instead when referencing ${fileName}`); + } + return fileName; } export function getTestDependencies( - packageName: string, - typeFiles: Map, - testFiles: Iterable, - dependencies: ReadonlySet, - fs: FS, + packageName: string, + typeFiles: Map, + testFiles: Iterable, + dependencies: ReadonlySet, + fs: FS ): Iterable { - const testDependencies = new Set(); - for (const filename of testFiles) { - const content = readFileAndThrowOnBOM(filename, fs); - const sourceFile = createSourceFile(filename, content); - const { fileName, referencedFiles, typeReferenceDirectives } = sourceFile; - const filePath = () => path.join(packageName, fileName); - let hasImports = false; - let isModule = false; - let referencesSelf = false; - - for (const { fileName: ref } of referencedFiles) { - throw new Error(`Test files should not use ''. '${filePath()}' references '${ref}'.`); - } - for (const { fileName: referencedPackage } of typeReferenceDirectives) { - if (dependencies.has(referencedPackage)) { - throw new Error( - `'${filePath()}' unnecessarily references '${referencedPackage}', which is already referenced in the type definition.`); - } - if (referencedPackage === packageName) { - referencesSelf = true; - } - testDependencies.add(referencedPackage); - } - for (const imported of imports(sourceFile)) { - hasImports = true; - if (!imported.startsWith(".")) { - const dep = rootName(imported, typeFiles); - if (!dependencies.has(dep) && dep !== packageName) { - testDependencies.add(dep); - } - } + const testDependencies = new Set(); + for (const filename of testFiles) { + const content = readFileAndThrowOnBOM(filename, fs); + const sourceFile = createSourceFile(filename, content); + const { fileName, referencedFiles, typeReferenceDirectives } = sourceFile; + const filePath = () => path.join(packageName, fileName); + let hasImports = false; + let isModule = false; + let referencesSelf = false; + + for (const { fileName: ref } of referencedFiles) { + throw new Error(`Test files should not use ''. '${filePath()}' references '${ref}'.`); + } + for (const { fileName: referencedPackage } of typeReferenceDirectives) { + if (dependencies.has(referencedPackage)) { + throw new Error( + `'${filePath()}' unnecessarily references '${referencedPackage}', which is already referenced in the type definition.` + ); + } + if (referencedPackage === packageName) { + referencesSelf = true; + } + testDependencies.add(referencedPackage); + } + for (const imported of imports(sourceFile)) { + hasImports = true; + if (!imported.startsWith(".")) { + const dep = rootName(imported, typeFiles); + if (!dependencies.has(dep) && dep !== packageName) { + testDependencies.add(dep); } + } + } - isModule = hasImports || (() => { - // FIXME: This results in files without imports to be walked twice, - // once in the `imports(...)` function, and once more here: - for (const node of sourceFile.statements) { - if ( - node.kind === ts.SyntaxKind.ExportAssignment || - node.kind === ts.SyntaxKind.ExportDeclaration - ) { - return true; - } - } - return false; - })(); - - if (isModule && referencesSelf) { - throw new Error(`'${filePath()}' unnecessarily references the package. This can be removed.`); + isModule = + hasImports || + (() => { + // FIXME: This results in files without imports to be walked twice, + // once in the `imports(...)` function, and once more here: + for (const node of sourceFile.statements) { + if (node.kind === ts.SyntaxKind.ExportAssignment || node.kind === ts.SyntaxKind.ExportDeclaration) { + return true; + } } + return false; + })(); + + if (isModule && referencesSelf) { + throw new Error(`'${filePath()}' unnecessarily references the package. This can be removed.`); } - return testDependencies; + } + return testDependencies; } export function createSourceFile(filename: string, content: string): ts.SourceFile { - return ts.createSourceFile(filename, content, ts.ScriptTarget.Latest, /*setParentNodes*/false); + return ts.createSourceFile(filename, content, ts.ScriptTarget.Latest, /*setParentNodes*/ false); } diff --git a/packages/definitions-parser/src/lib/settings.ts b/packages/definitions-parser/src/lib/settings.ts index f37d08c544..39a0bd360e 100644 --- a/packages/definitions-parser/src/lib/settings.ts +++ b/packages/definitions-parser/src/lib/settings.ts @@ -9,8 +9,9 @@ export const logDir = joinPaths(root, "logs"); /** URL to download the repository from. */ export const definitelyTypedZipUrl = "https://codeload.github.com/DefinitelyTyped/DefinitelyTyped/tar.gz/master"; -export const dependenciesWhitelist: ReadonlySet = - new Set(readFileSync(joinPaths(root, "dependenciesWhitelist.txt")).split(/\r?\n/)); +export const dependenciesWhitelist: ReadonlySet = new Set( + readFileSync(joinPaths(root, "dependenciesWhitelist.txt")).split(/\r?\n/) +); /** Note: this is 'types' and not '@types' */ -export const scopeName = "types"; \ No newline at end of file +export const scopeName = "types"; diff --git a/packages/definitions-parser/src/mocks.ts b/packages/definitions-parser/src/mocks.ts index 845e6bf00f..d4906e8750 100644 --- a/packages/definitions-parser/src/mocks.ts +++ b/packages/definitions-parser/src/mocks.ts @@ -2,88 +2,99 @@ import { parseHeaderOrFail } from "@definitelytyped/header-parser"; import { Dir, FS, InMemoryFS, Semver } from "@definitelytyped/utils"; class DTMock { - public readonly fs: FS; - private readonly root: Dir; + public readonly fs: FS; + private readonly root: Dir; - constructor() { - this.root = new Dir(undefined); - this.root.set("notNeededPackages.json", `{ + constructor() { + this.root = new Dir(undefined); + this.root.set( + "notNeededPackages.json", + `{ "packages": [{ "libraryName": "Angular 2", "typingsPackageName": "angular", "asOfVersion": "1.2.3", "sourceRepoURL": "https://github.com/angular/angular2" }] - }`); - this.fs = new InMemoryFS(this.root, "DefinitelyTyped"); - } - - public pkgDir(packageName: string): Dir { - return this.root.subdir("types").subdir(packageName); - } - - public pkgFS(packageName: string): FS { - return this.fs.subDir("types").subDir(packageName); - } - - /** - * Creates a shallow copy of a package, meaning all entries in the old version directory that will be created refer to the copied entry from the - * latest version. The only exceptions are the `index.d.ts` and `tsconfig.json` files. - * - * The directory name will exactly follow the given `olderVersion`. I.e. `2` will become `v2`, whereas `2.2` will become `v2.2`. - * - * @param packageName The package of which an old version is to be added. - * @param olderVersion The older version that's to be added. - */ - public addOldVersionOfPackage(packageName: string, olderVersion: string) { - const latestDir = this.pkgDir(packageName); - const index = latestDir.get("index.d.ts") as string; - const latestHeader = parseHeaderOrFail(index); - const latestVersion = `${latestHeader.libraryMajorVersion}.${latestHeader.libraryMinorVersion}`; - const olderVersionParsed = Semver.parse(olderVersion, true)!; - - const oldDir = latestDir.subdir(`v${olderVersion}`); - const tsconfig = JSON.parse(latestDir.get("tsconfig.json") as string); - - oldDir.set("index.d.ts", index.replace(latestVersion, `${olderVersionParsed.major}.${olderVersionParsed.minor}`)); - oldDir.set("tsconfig.json", JSON.stringify({ - ...tsconfig, - compilerOptions: { - ...tsconfig.compilerOptions, - paths: { - [packageName]: [`${packageName}/v${olderVersion}`], - }, - }, - })); - - latestDir.forEach((content, entry) => { - if ( - content !== oldDir - && entry !== "index.d.ts" - && entry !== "tsconfig.json" - && !(content instanceof Dir && /^v\d+(\.\d+)?$/.test(entry)) - ) { - oldDir.set(entry, content); - } - }); - - return oldDir; - } + }` + ); + this.fs = new InMemoryFS(this.root, "DefinitelyTyped"); + } + + public pkgDir(packageName: string): Dir { + return this.root.subdir("types").subdir(packageName); + } + + public pkgFS(packageName: string): FS { + return this.fs.subDir("types").subDir(packageName); + } + + /** + * Creates a shallow copy of a package, meaning all entries in the old version directory that will be created refer to the copied entry from the + * latest version. The only exceptions are the `index.d.ts` and `tsconfig.json` files. + * + * The directory name will exactly follow the given `olderVersion`. I.e. `2` will become `v2`, whereas `2.2` will become `v2.2`. + * + * @param packageName The package of which an old version is to be added. + * @param olderVersion The older version that's to be added. + */ + public addOldVersionOfPackage(packageName: string, olderVersion: string) { + const latestDir = this.pkgDir(packageName); + const index = latestDir.get("index.d.ts") as string; + const latestHeader = parseHeaderOrFail(index); + const latestVersion = `${latestHeader.libraryMajorVersion}.${latestHeader.libraryMinorVersion}`; + const olderVersionParsed = Semver.parse(olderVersion, true)!; + + const oldDir = latestDir.subdir(`v${olderVersion}`); + const tsconfig = JSON.parse(latestDir.get("tsconfig.json") as string); + + oldDir.set("index.d.ts", index.replace(latestVersion, `${olderVersionParsed.major}.${olderVersionParsed.minor}`)); + oldDir.set( + "tsconfig.json", + JSON.stringify({ + ...tsconfig, + compilerOptions: { + ...tsconfig.compilerOptions, + paths: { + [packageName]: [`${packageName}/v${olderVersion}`] + } + } + }) + ); + + latestDir.forEach((content, entry) => { + if ( + content !== oldDir && + entry !== "index.d.ts" && + entry !== "tsconfig.json" && + !(content instanceof Dir && /^v\d+(\.\d+)?$/.test(entry)) + ) { + oldDir.set(entry, content); + } + }); + + return oldDir; + } } export function createMockDT() { - const dt = new DTMock(); + const dt = new DTMock(); - const boring = dt.pkgDir("boring"); - boring.set("index.d.ts", `// Type definitions for boring 1.0 + const boring = dt.pkgDir("boring"); + boring.set( + "index.d.ts", + `// Type definitions for boring 1.0 // Project: https://boring.com // Definitions by: Some Guy From Space // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped import * as React from 'react'; export const drills: number; -`); - boring.set("secondary.d.ts", ` +` + ); + boring.set( + "secondary.d.ts", + ` import deffo from 'react-default'; import { mammoths } from 'boring/quaternary'; export const hovercars: unknown; @@ -93,37 +104,61 @@ declare module "boring/fake" { declare module "other" { export const augmented: true; } -`); - boring.set("tertiary.d.ts", ` +` + ); + boring.set( + "tertiary.d.ts", + ` import { stuff } from 'things'; export var stock: number; -`); - boring.set("quaternary.d.ts", ` +` + ); + boring.set( + "quaternary.d.ts", + ` export const mammoths: object; -`); - boring.set("commonjs.d.ts", ` +` + ); + boring.set( + "commonjs.d.ts", + ` import vortex = require('vorticon'); declare const australia: {}; export = australia; -`); - boring.set("v1.d.ts", ` +` + ); + boring.set( + "v1.d.ts", + ` export const inane: true | false; -`); - boring.set("untested.d.ts", ` +` + ); + boring.set( + "untested.d.ts", + ` import { help } from 'manual'; export const fungible: false; -`); - boring.set("boring-tests.ts", ` +` + ); + boring.set( + "boring-tests.ts", + ` import { superstor } from "super-big-fun-hus"; import { drills } from "boring"; import { hovercars } from "boring/secondary"; import australia = require('boring/commonjs'); import { inane } from "boring/v1"; -`); - boring.set("OTHER_FILES.txt", ` +` + ); + boring.set( + "OTHER_FILES.txt", + ` untested.d.ts -`); - boring.set("tsconfig.json", `{ +` + ); + boring.set( + "tsconfig.json", + `{ "compilerOptions": { "module": "commonjs", "lib": [ @@ -147,10 +182,13 @@ untested.d.ts "index.d.ts", "boring-tests.ts" ] -}`); +}` + ); - const globby = dt.pkgDir("globby"); - globby.set("index.d.ts", `// Type definitions for globby 0.2 + const globby = dt.pkgDir("globby"); + globby.set( + "index.d.ts", + `// Type definitions for globby 0.2 // Project: https://globby-gloopy.com // Definitions by: The Dragon Quest Slime // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -158,22 +196,37 @@ untested.d.ts /// /// declare var x: number -`); - globby.set("merges.d.ts", ` +` + ); + globby.set( + "merges.d.ts", + ` declare var y: number -`); - globby.set("sneaky.d.ts", ` +` + ); + globby.set( + "sneaky.d.ts", + ` declare var ka: number -`); - globby.set("globby-tests.ts", ` +` + ); + globby.set( + "globby-tests.ts", + ` var z = x; -`); - const tests = globby.subdir("test"); - tests.set("other-tests.ts", ` +` + ); + const tests = globby.subdir("test"); + tests.set( + "other-tests.ts", + ` /// var z = y; -`); - globby.set("tsconfig.json", `{ +` + ); + globby.set( + "tsconfig.json", + `{ "compilerOptions": { "module": "commonjs", "lib": [ @@ -198,12 +251,18 @@ var z = y; "globby-tests.ts", "test/other-tests.ts" ] -}`); - const jquery = dt.pkgDir("jquery"); - jquery.set("JQuery.d.ts", ` +}` + ); + const jquery = dt.pkgDir("jquery"); + jquery.set( + "JQuery.d.ts", + ` declare var jQuery: 1; -`); - jquery.set("index.d.ts", `// Type definitions for jquery 3.3 +` + ); + jquery.set( + "index.d.ts", + `// Type definitions for jquery 3.3 // Project: https://jquery.com // Definitions by: Leonard Thieu // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -212,11 +271,17 @@ declare var jQuery: 1; /// export = jQuery; -`); - jquery.set("jquery-tests.ts", ` +` + ); + jquery.set( + "jquery-tests.ts", + ` console.log(jQuery); -`); - jquery.set("tsconfig.json", `{ +` + ); + jquery.set( + "tsconfig.json", + `{ "compilerOptions": { "module": "commonjs", "lib": [ @@ -242,7 +307,8 @@ console.log(jQuery); ] } -`); +` + ); - return dt; + return dt; } diff --git a/packages/definitions-parser/src/packages.ts b/packages/definitions-parser/src/packages.ts index fd464f1f38..b3b7b6e038 100644 --- a/packages/definitions-parser/src/packages.ts +++ b/packages/definitions-parser/src/packages.ts @@ -5,538 +5,605 @@ import { readDataFile } from "./data-file"; import { outputDirPath, scopeName } from "./lib/settings"; export class AllPackages { - static async read(dt: FS): Promise { - return AllPackages.from(await readTypesDataFile(), readNotNeededPackages(dt)); - } - - static from(data: TypesDataFile, notNeeded: ReadonlyArray): AllPackages { - return new AllPackages(mapValues(new Map(Object.entries(data)), raw => new TypingsVersions(raw)), notNeeded); - } - - static async readTypings(): Promise> { - return AllPackages.from(await readTypesDataFile(), []).allTypings(); - } - static async readLatestTypings(): Promise> { - return AllPackages.from(await readTypesDataFile(), []).allLatestTypings(); - } - - /** Use for `--single` tasks only. Do *not* call this in a loop! */ - static async readSingle(name: string): Promise { - const data = await readTypesDataFile(); - const raw = data[name]; - if (!raw) { - throw new Error(`Can't find package ${name}`); - } - const versions = Object.keys(raw); - if (versions.length > 1) { - throw new Error(`Package ${name} has multiple versions.`); - } - return new TypingsData(raw[versions[0]], /*isLatest*/ true); - } - - static readSingleNotNeeded(name: string, dt: FS): NotNeededPackage { - const notNeeded = readNotNeededPackages(dt); - const pkg = notNeeded.find(p => p.name === name); - if (pkg === undefined) { - throw new Error(`Cannot find not-needed package ${name}`); - } - return pkg; - } - - private constructor( - private readonly data: ReadonlyMap, - private readonly notNeeded: ReadonlyArray) {} - - getNotNeededPackage(name: string): NotNeededPackage | undefined { - return this.notNeeded.find(p => p.name === name); - } - - hasTypingFor(dep: PackageId): boolean { - return this.tryGetTypingsData(dep) !== undefined; - } - - tryResolve(dep: PackageId): PackageId { - const versions = this.data.get(getMangledNameForScopedPackage(dep.name)); - return versions ? versions.get(dep.version).id : dep; - } - - /** Gets the latest version of a package. E.g. getLatest(node v6) was node v10 (before node v11 came out). */ - getLatest(pkg: TypingsData): TypingsData { - return pkg.isLatest ? pkg : this.getLatestVersion(pkg.name); - } - - private getLatestVersion(packageName: string): TypingsData { - const latest = this.tryGetLatestVersion(packageName); - if (!latest) { - throw new Error(`No such package ${packageName}.`); - } - return latest; - } - - tryGetLatestVersion(packageName: string): TypingsData | undefined { - const versions = this.data.get(getMangledNameForScopedPackage(packageName)); - return versions && versions.getLatest(); - } - - getTypingsData(id: PackageId): TypingsData { - const pkg = this.tryGetTypingsData(id); - if (!pkg) { - throw new Error(`No typings available for ${JSON.stringify(id)}`); - } - return pkg; - } - - tryGetTypingsData({ name, version }: PackageId): TypingsData | undefined { - const versions = this.data.get(getMangledNameForScopedPackage(name)); - return versions && versions.tryGet(version); - } - - allPackages(): ReadonlyArray { - return [ ...this.allTypings(), ...this.allNotNeeded() ]; - } - - /** Note: this includes older version directories (`foo/v0`) */ - allTypings(): ReadonlyArray { - return assertSorted(Array.from(flattenData(this.data)), t => t.name); - } - - allLatestTypings(): ReadonlyArray { - return assertSorted(Array.from(this.data.values()).map(versions => versions.getLatest()), t => t.name); - } - - allNotNeeded(): ReadonlyArray { - return this.notNeeded; - } - - /** Returns all of the dependences *that have typings*, ignoring others, and including test dependencies. */ - *allDependencyTypings(pkg: TypingsData): Iterable { - for (const { name, version } of pkg.dependencies) { - const versions = this.data.get(getMangledNameForScopedPackage(name)); - if (versions) { - yield versions.get(version); - } - } - - for (const name of pkg.testDependencies) { - const versions = this.data.get(getMangledNameForScopedPackage(name)); - if (versions) { - yield versions.getLatest(); - } - } - } + static async read(dt: FS): Promise { + return AllPackages.from(await readTypesDataFile(), readNotNeededPackages(dt)); + } + + static from(data: TypesDataFile, notNeeded: readonly NotNeededPackage[]): AllPackages { + return new AllPackages( + mapValues(new Map(Object.entries(data)), raw => new TypingsVersions(raw)), + notNeeded + ); + } + + static async readTypings(): Promise { + return AllPackages.from(await readTypesDataFile(), []).allTypings(); + } + static async readLatestTypings(): Promise { + return AllPackages.from(await readTypesDataFile(), []).allLatestTypings(); + } + + /** Use for `--single` tasks only. Do *not* call this in a loop! */ + static async readSingle(name: string): Promise { + const data = await readTypesDataFile(); + const raw = data[name]; + if (!raw) { + throw new Error(`Can't find package ${name}`); + } + const versions = Object.keys(raw); + if (versions.length > 1) { + throw new Error(`Package ${name} has multiple versions.`); + } + return new TypingsData(raw[versions[0]], /*isLatest*/ true); + } + + static readSingleNotNeeded(name: string, dt: FS): NotNeededPackage { + const notNeeded = readNotNeededPackages(dt); + const pkg = notNeeded.find(p => p.name === name); + if (pkg === undefined) { + throw new Error(`Cannot find not-needed package ${name}`); + } + return pkg; + } + + private constructor( + private readonly data: ReadonlyMap, + private readonly notNeeded: readonly NotNeededPackage[] + ) {} + + getNotNeededPackage(name: string): NotNeededPackage | undefined { + return this.notNeeded.find(p => p.name === name); + } + + hasTypingFor(dep: PackageId): boolean { + return this.tryGetTypingsData(dep) !== undefined; + } + + tryResolve(dep: PackageId): PackageId { + const versions = this.data.get(getMangledNameForScopedPackage(dep.name)); + return versions ? versions.get(dep.version).id : dep; + } + + /** Gets the latest version of a package. E.g. getLatest(node v6) was node v10 (before node v11 came out). */ + getLatest(pkg: TypingsData): TypingsData { + return pkg.isLatest ? pkg : this.getLatestVersion(pkg.name); + } + + private getLatestVersion(packageName: string): TypingsData { + const latest = this.tryGetLatestVersion(packageName); + if (!latest) { + throw new Error(`No such package ${packageName}.`); + } + return latest; + } + + tryGetLatestVersion(packageName: string): TypingsData | undefined { + const versions = this.data.get(getMangledNameForScopedPackage(packageName)); + return versions && versions.getLatest(); + } + + getTypingsData(id: PackageId): TypingsData { + const pkg = this.tryGetTypingsData(id); + if (!pkg) { + throw new Error(`No typings available for ${JSON.stringify(id)}`); + } + return pkg; + } + + tryGetTypingsData({ name, version }: PackageId): TypingsData | undefined { + const versions = this.data.get(getMangledNameForScopedPackage(name)); + return versions && versions.tryGet(version); + } + + allPackages(): readonly AnyPackage[] { + return [...this.allTypings(), ...this.allNotNeeded()]; + } + + /** Note: this includes older version directories (`foo/v0`) */ + allTypings(): readonly TypingsData[] { + return assertSorted(Array.from(flattenData(this.data)), t => t.name); + } + + allLatestTypings(): readonly TypingsData[] { + return assertSorted( + Array.from(this.data.values()).map(versions => versions.getLatest()), + t => t.name + ); + } + + allNotNeeded(): readonly NotNeededPackage[] { + return this.notNeeded; + } + + /** Returns all of the dependences *that have typings*, ignoring others, and including test dependencies. */ + *allDependencyTypings(pkg: TypingsData): Iterable { + for (const { name, version } of pkg.dependencies) { + const versions = this.data.get(getMangledNameForScopedPackage(name)); + if (versions) { + yield versions.get(version); + } + } + + for (const name of pkg.testDependencies) { + const versions = this.data.get(getMangledNameForScopedPackage(name)); + if (versions) { + yield versions.getLatest(); + } + } + } } // Same as the function in moduleNameResolver.ts in typescript export function getMangledNameForScopedPackage(packageName: string): string { - if (packageName.startsWith("@")) { - const replaceSlash = packageName.replace("/", "__"); - if (replaceSlash !== packageName) { - return replaceSlash.slice(1); // Take off the "@" - } + if (packageName.startsWith("@")) { + const replaceSlash = packageName.replace("/", "__"); + if (replaceSlash !== packageName) { + return replaceSlash.slice(1); // Take off the "@" } - return packageName; + } + return packageName; } export const typesDataFilename = "definitions.json"; function* flattenData(data: ReadonlyMap): Iterable { - for (const versions of data.values()) { - yield* versions.getAll(); - } + for (const versions of data.values()) { + yield* versions.getAll(); + } } export type AnyPackage = NotNeededPackage | TypingsData; interface BaseRaw { - /** - * The name of the library. - * - * A human readable version, e.g. it might be "Moment.js" even though `packageName` is "moment". - */ - readonly libraryName: string; - - /** - * The NPM name to publish this under, e.g. "jquery". - * - * This does not include "@types". - */ - readonly typingsPackageName: string; + /** + * The name of the library. + * + * A human readable version, e.g. it might be "Moment.js" even though `packageName` is "moment". + */ + readonly libraryName: string; + + /** + * The NPM name to publish this under, e.g. "jquery". + * + * This does not include "@types". + */ + readonly typingsPackageName: string; } /** Prefer to use `AnyPackage` instead of this. */ export abstract class PackageBase { - static compare(a: PackageBase, b: PackageBase): number { return a.name.localeCompare(b.name); } - - /** Note: for "foo__bar" this is still "foo__bar", not "@foo/bar". */ - readonly name: string; - readonly libraryName: string; - - get unescapedName(): string { - return unmangleScopedPackage(this.name) || this.name; - } - - /** Short description for debug output. */ - get desc(): string { - return this.isLatest ? this.name : `${this.name} v${this.major}.${this.minor}`; - } - - constructor(data: BaseRaw) { - this.name = data.typingsPackageName; - this.libraryName = data.libraryName; - } - - isNotNeeded(): this is NotNeededPackage { - return this instanceof NotNeededPackage; - } - - abstract readonly isLatest: boolean; - abstract readonly projectName: string; - abstract readonly declaredModules: ReadonlyArray; - abstract readonly globals: ReadonlyArray; - abstract readonly minTypeScriptVersion: TypeScriptVersion; - - /** '@types/foo' for a package 'foo'. */ - get fullNpmName(): string { - return getFullNpmName(this.name); - } - - /** '@types%2ffoo' for a package 'foo'. */ - get fullEscapedNpmName(): string { - return `@${scopeName}%2f${this.name}`; - } - - abstract readonly major: number; - abstract readonly minor: number; - - get id(): PackageId { - return { name: this.name, version: { major: this.major, minor: this.minor }}; - } - - get outputDirectory(): string { - return joinPaths(outputDirPath, this.desc); - } + static compare(a: PackageBase, b: PackageBase): number { + return a.name.localeCompare(b.name); + } + + /** Note: for "foo__bar" this is still "foo__bar", not "@foo/bar". */ + readonly name: string; + readonly libraryName: string; + + get unescapedName(): string { + return unmangleScopedPackage(this.name) || this.name; + } + + /** Short description for debug output. */ + get desc(): string { + return this.isLatest ? this.name : `${this.name} v${this.major}.${this.minor}`; + } + + constructor(data: BaseRaw) { + this.name = data.typingsPackageName; + this.libraryName = data.libraryName; + } + + isNotNeeded(): this is NotNeededPackage { + return this instanceof NotNeededPackage; + } + + abstract readonly isLatest: boolean; + abstract readonly projectName: string; + abstract readonly declaredModules: readonly string[]; + abstract readonly globals: readonly string[]; + abstract readonly minTypeScriptVersion: TypeScriptVersion; + + /** '@types/foo' for a package 'foo'. */ + get fullNpmName(): string { + return getFullNpmName(this.name); + } + + /** '@types%2ffoo' for a package 'foo'. */ + get fullEscapedNpmName(): string { + return `@${scopeName}%2f${this.name}`; + } + + abstract readonly major: number; + abstract readonly minor: number; + + get id(): PackageId { + return { name: this.name, version: { major: this.major, minor: this.minor } }; + } + + get outputDirectory(): string { + return joinPaths(outputDirPath, this.desc); + } } export function getFullNpmName(packageName: string): string { - return `@${scopeName}/${getMangledNameForScopedPackage(packageName)}`; + return `@${scopeName}/${getMangledNameForScopedPackage(packageName)}`; } interface NotNeededPackageRaw extends BaseRaw { - /** - * If this is available, @types typings are deprecated as of this version. - * This is useful for packages that previously had DefinitelyTyped definitions but which now provide their own. - */ - // This must be "major.minor.patch" - readonly asOfVersion: string; - /** The package's own url, *not* DefinitelyTyped's. */ - readonly sourceRepoURL: string; + /** + * If this is available, @types typings are deprecated as of this version. + * This is useful for packages that previously had DefinitelyTyped definitions but which now provide their own. + */ + // This must be "major.minor.patch" + readonly asOfVersion: string; + /** The package's own url, *not* DefinitelyTyped's. */ + readonly sourceRepoURL: string; } export class NotNeededPackage extends PackageBase { - readonly version: Semver; - - get license(): License.MIT { return License.MIT; } - - readonly sourceRepoURL: string; - - constructor(raw: NotNeededPackageRaw) { - super(raw); - this.sourceRepoURL = raw.sourceRepoURL; - - for (const key of Object.keys(raw)) { - if (!["libraryName", "typingsPackageName", "sourceRepoURL", "asOfVersion"].includes(key)) { - throw new Error(`Unexpected key in not-needed package: ${key}`); - } - } - assert(raw.libraryName && raw.typingsPackageName && raw.sourceRepoURL && raw.asOfVersion); - - this.version = Semver.parse(raw.asOfVersion); - } - - get major(): number { return this.version.major; } - get minor(): number { return this.version.minor; } - - // A not-needed package has no other versions. (TODO: allow that?) - get isLatest(): boolean { return true; } - get projectName(): string { return this.sourceRepoURL; } - get declaredModules(): ReadonlyArray { return []; } - get globals(): ReadonlyArray { return this.globals; } - get minTypeScriptVersion(): TypeScriptVersion { return TypeScriptVersion.lowest; } - - readme(): string { - return `This is a stub types definition for ${this.libraryName} (${this.sourceRepoURL}).\n + readonly version: Semver; + + get license(): License.MIT { + return License.MIT; + } + + readonly sourceRepoURL: string; + + constructor(raw: NotNeededPackageRaw) { + super(raw); + this.sourceRepoURL = raw.sourceRepoURL; + + for (const key of Object.keys(raw)) { + if (!["libraryName", "typingsPackageName", "sourceRepoURL", "asOfVersion"].includes(key)) { + throw new Error(`Unexpected key in not-needed package: ${key}`); + } + } + assert(raw.libraryName && raw.typingsPackageName && raw.sourceRepoURL && raw.asOfVersion); + + this.version = Semver.parse(raw.asOfVersion); + } + + get major(): number { + return this.version.major; + } + get minor(): number { + return this.version.minor; + } + + // A not-needed package has no other versions. (TODO: allow that?) + get isLatest(): boolean { + return true; + } + get projectName(): string { + return this.sourceRepoURL; + } + get declaredModules(): readonly string[] { + return []; + } + get globals(): readonly string[] { + return this.globals; + } + get minTypeScriptVersion(): TypeScriptVersion { + return TypeScriptVersion.lowest; + } + + readme(): string { + return `This is a stub types definition for ${this.libraryName} (${this.sourceRepoURL}).\n ${this.libraryName} provides its own type definitions, so you don't need ${getFullNpmName(this.name)} installed!`; - } + } - deprecatedMessage(): string { - return `This is a stub types definition. ${this.name} provides its own type definitions, so you do not need this installed.`; - } + deprecatedMessage(): string { + return `This is a stub types definition. ${this.name} provides its own type definitions, so you do not need this installed.`; + } } export interface TypingsVersionsRaw { - [version: string]: TypingsDataRaw; + [version: string]: TypingsDataRaw; } export interface TypingVersion { - major: number; - minor?: number; + major: number; + minor?: number; } export function formatTypingVersion(version: TypingVersion) { - return `${version.major}${version.minor === undefined ? "" : `.${version.minor}`}`; + return `${version.major}${version.minor === undefined ? "" : `.${version.minor}`}`; } /** If no version is specified, uses "*". */ export type DependencyVersion = TypingVersion | "*"; export function formatDependencyVersion(version: DependencyVersion) { - return version === "*" ? "*" : formatTypingVersion(version); + return version === "*" ? "*" : formatTypingVersion(version); } export interface PackageJsonDependency { - readonly name: string; - readonly version: string; + readonly name: string; + readonly version: string; } export interface TypingsDataRaw extends BaseRaw { - /** - * Other definitions, that exist in the same typings repo, that this package depends on. - * - * These will refer to *package names*, not *folder names*. - */ - readonly dependencies: ReadonlyArray; - - /** - * Other definitions, that exist in the same typings repo, that the tests, but not the types, of this package depend on. - * - * These are always the latest version and will not include anything already in `dependencies`. - */ - readonly testDependencies: ReadonlyArray; - - /** - * External packages, from outside the typings repo, that provide definitions that this package depends on. - */ - readonly packageJsonDependencies: ReadonlyArray; - - /** - * Represents that there was a path mapping to a package. - * - * Not all path mappings are direct dependencies, they may be necessary for transitive dependencies. However, where `dependencies` and - * `pathMappings` share a key, they *must* share the same value. - */ - readonly pathMappings: ReadonlyArray; - - /** - * List of people that have contributed to the definitions in this package. - * - * These people will be requested for issue/PR review in the https://github.com/DefinitelyTyped/DefinitelyTyped repo. - */ - readonly contributors: ReadonlyArray; - - /** - * The [older] version of the library that this definition package refers to, as represented *on-disk*. - * - * @note The latest version always exists in the root of the package tree and thus does not have a value for this property. - */ - readonly libraryVersionDirectoryName?: string; - - /** - * Major version of the library. - * - * This data is parsed from a header comment in the entry point `.d.ts` and will be `0` if the file did not specify a version. - */ - readonly libraryMajorVersion: number; - - /** - * Minor version of the library. - * - * This data is parsed from a header comment in the entry point `.d.ts` and will be `0` if the file did not specify a version. - */ - readonly libraryMinorVersion: number; - - /** - * Minimum required TypeScript version to consume the definitions from this package. - */ - readonly minTsVersion: AllTypeScriptVersion; - - /** - * List of TS versions that have their own directories, and corresponding "typesVersions" in package.json. - * Usually empty. - */ - readonly typesVersions: ReadonlyArray; - - /** - * Files that should be published with this definition, e.g. ["jquery.d.ts", "jquery-extras.d.ts"] - * - * Does *not* include a partial `package.json` because that will not be copied directly. - */ - readonly files: ReadonlyArray; - - /** - * The license that this definition package is released under. - * - * Can be either MIT or Apache v2, defaults to MIT when not explicitly defined in this package’s "package.json". - */ - readonly license: License; - - /** - * A hash of the names and contents of the `files` list, used for versioning. - */ - readonly contentHash: string; - - /** - * Name or URL of the project, e.g. "http://cordova.apache.org". - */ - readonly projectName: string; - - /** - * A list of *values* declared in the global namespace. - * - * @note This does not include *types* declared in the global namespace. - */ - readonly globals: ReadonlyArray; - - /** - * External modules declared by this package. Includes the containing folder name when applicable (e.g. proper module). - */ - readonly declaredModules: ReadonlyArray; + /** + * Other definitions, that exist in the same typings repo, that this package depends on. + * + * These will refer to *package names*, not *folder names*. + */ + readonly dependencies: readonly PackageId[]; + + /** + * Other definitions, that exist in the same typings repo, that the tests, but not the types, of this package depend on. + * + * These are always the latest version and will not include anything already in `dependencies`. + */ + readonly testDependencies: readonly string[]; + + /** + * External packages, from outside the typings repo, that provide definitions that this package depends on. + */ + readonly packageJsonDependencies: readonly PackageJsonDependency[]; + + /** + * Represents that there was a path mapping to a package. + * + * Not all path mappings are direct dependencies, they may be necessary for transitive dependencies. However, where `dependencies` and + * `pathMappings` share a key, they *must* share the same value. + */ + readonly pathMappings: readonly PathMapping[]; + + /** + * List of people that have contributed to the definitions in this package. + * + * These people will be requested for issue/PR review in the https://github.com/DefinitelyTyped/DefinitelyTyped repo. + */ + readonly contributors: readonly Author[]; + + /** + * The [older] version of the library that this definition package refers to, as represented *on-disk*. + * + * @note The latest version always exists in the root of the package tree and thus does not have a value for this property. + */ + readonly libraryVersionDirectoryName?: string; + + /** + * Major version of the library. + * + * This data is parsed from a header comment in the entry point `.d.ts` and will be `0` if the file did not specify a version. + */ + readonly libraryMajorVersion: number; + + /** + * Minor version of the library. + * + * This data is parsed from a header comment in the entry point `.d.ts` and will be `0` if the file did not specify a version. + */ + readonly libraryMinorVersion: number; + + /** + * Minimum required TypeScript version to consume the definitions from this package. + */ + readonly minTsVersion: AllTypeScriptVersion; + + /** + * List of TS versions that have their own directories, and corresponding "typesVersions" in package.json. + * Usually empty. + */ + readonly typesVersions: readonly TypeScriptVersion[]; + + /** + * Files that should be published with this definition, e.g. ["jquery.d.ts", "jquery-extras.d.ts"] + * + * Does *not* include a partial `package.json` because that will not be copied directly. + */ + readonly files: readonly string[]; + + /** + * The license that this definition package is released under. + * + * Can be either MIT or Apache v2, defaults to MIT when not explicitly defined in this package’s "package.json". + */ + readonly license: License; + + /** + * A hash of the names and contents of the `files` list, used for versioning. + */ + readonly contentHash: string; + + /** + * Name or URL of the project, e.g. "http://cordova.apache.org". + */ + readonly projectName: string; + + /** + * A list of *values* declared in the global namespace. + * + * @note This does not include *types* declared in the global namespace. + */ + readonly globals: readonly string[]; + + /** + * External modules declared by this package. Includes the containing folder name when applicable (e.g. proper module). + */ + readonly declaredModules: readonly string[]; } /** * @see {TypingsDataRaw.pathMappings} */ export interface PathMapping { - readonly packageName: string; - readonly version: TypingVersion; + readonly packageName: string; + readonly version: TypingVersion; } // TODO: support BSD -- but must choose a *particular* BSD license from the list at https://spdx.org/licenses/ -export const enum License { MIT = "MIT", Apache20 = "Apache-2.0" } +export const enum License { + MIT = "MIT", + Apache20 = "Apache-2.0" +} const allLicenses = [License.MIT, License.Apache20]; export function getLicenseFromPackageJson(packageJsonLicense: unknown): License { - if (packageJsonLicense === undefined) { // tslint:disable-line strict-type-predicates (false positive) - return License.MIT; - } - if (typeof packageJsonLicense === "string" && packageJsonLicense === "MIT") { - throw new Error(`Specifying '"license": "MIT"' is redundant, this is the default.`); - } - if (allLicenses.includes(packageJsonLicense as License)) { - return packageJsonLicense as License; - } - throw new Error(`'package.json' license is ${JSON.stringify(packageJsonLicense)}.\nExpected one of: ${JSON.stringify(allLicenses)}}`); + if (packageJsonLicense === undefined) { + // tslint:disable-line strict-type-predicates (false positive) + return License.MIT; + } + if (typeof packageJsonLicense === "string" && packageJsonLicense === "MIT") { + throw new Error(`Specifying '"license": "MIT"' is redundant, this is the default.`); + } + if (allLicenses.includes(packageJsonLicense as License)) { + return packageJsonLicense as License; + } + throw new Error( + `'package.json' license is ${JSON.stringify(packageJsonLicense)}.\nExpected one of: ${JSON.stringify(allLicenses)}}` + ); } export class TypingsVersions { - private readonly map: ReadonlyMap; + private readonly map: ReadonlyMap; + + /** + * Sorted from latest to oldest. + */ + private readonly versions: Semver[]; + + constructor(data: TypingsVersionsRaw) { + const versionMappings = new Map( + Object.keys(data).map(key => { + const version = Semver.parse(key, true); + if (version) { + return [version, key]; + } + throw new Error(`Unable to parse version ${key}`); + }) + ); /** - * Sorted from latest to oldest. + * Sorted from latest to oldest so that we publish the current version first. + * This is important because older versions repeatedly reset the "latest" tag to the current version. */ - private readonly versions: Semver[]; - - constructor(data: TypingsVersionsRaw) { - const versionMappings = new Map(Object.keys(data).map(key => { - const version = Semver.parse(key, true); - if (version) { - return [version, key]; - } - throw new Error(`Unable to parse version ${key}`); - })); - - /** - * Sorted from latest to oldest so that we publish the current version first. - * This is important because older versions repeatedly reset the "latest" tag to the current version. - */ - this.versions = Array.from(versionMappings.keys()).sort(Semver.compare).reverse(); - - this.map = new Map(this.versions.map(version => { - const dataKey = versionMappings.get(version)!; - return [version, new TypingsData(data[dataKey], version.equals(this.versions[0]))]; - })); - } - - getAll(): Iterable { - return this.map.values(); - } - - get(version: DependencyVersion): TypingsData { - return version === "*" ? this.getLatest() : this.getLatestMatch(version); - } - - tryGet(version: DependencyVersion): TypingsData | undefined { - return version === "*" ? this.getLatest() : this.tryGetLatestMatch(version); - } - - getLatest(): TypingsData { - return this.map.get(this.versions[0])!; - } - - private getLatestMatch(version: TypingVersion): TypingsData { - const data = this.tryGetLatestMatch(version); - if (!data) { - throw new Error(`Could not find version ${version.major}.${version.minor ?? "*"}`); - } - return data; - } - - private tryGetLatestMatch(version: TypingVersion): TypingsData | undefined { - const found = this.versions.find(v => v.major === version.major && (version.minor === undefined || v.minor === version.minor)); - return found && this.map.get(found); - } + this.versions = Array.from(versionMappings.keys()) + .sort(Semver.compare) + .reverse(); + + this.map = new Map( + this.versions.map(version => { + const dataKey = versionMappings.get(version)!; + return [version, new TypingsData(data[dataKey], version.equals(this.versions[0]))]; + }) + ); + } + + getAll(): Iterable { + return this.map.values(); + } + + get(version: DependencyVersion): TypingsData { + return version === "*" ? this.getLatest() : this.getLatestMatch(version); + } + + tryGet(version: DependencyVersion): TypingsData | undefined { + return version === "*" ? this.getLatest() : this.tryGetLatestMatch(version); + } + + getLatest(): TypingsData { + return this.map.get(this.versions[0])!; + } + + private getLatestMatch(version: TypingVersion): TypingsData { + const data = this.tryGetLatestMatch(version); + if (!data) { + throw new Error(`Could not find version ${version.major}.${version.minor ?? "*"}`); + } + return data; + } + + private tryGetLatestMatch(version: TypingVersion): TypingsData | undefined { + const found = this.versions.find( + v => v.major === version.major && (version.minor === undefined || v.minor === version.minor) + ); + return found && this.map.get(found); + } } export class TypingsData extends PackageBase { - constructor(private readonly data: TypingsDataRaw, readonly isLatest: boolean) { - super(data); - } - - get testDependencies(): ReadonlyArray { return this.data.testDependencies; } - get contributors(): ReadonlyArray { return this.data.contributors; } - get major(): number { return this.data.libraryMajorVersion; } - get minor(): number { return this.data.libraryMinorVersion; } - - get minTypeScriptVersion(): TypeScriptVersion { - return TypeScriptVersion.isSupported(this.data.minTsVersion) ? this.data.minTsVersion : TypeScriptVersion.lowest; - } - get typesVersions(): ReadonlyArray { return this.data.typesVersions; } - - get files(): ReadonlyArray { return this.data.files; } - get license(): License { return this.data.license; } - get packageJsonDependencies(): ReadonlyArray { return this.data.packageJsonDependencies; } - get contentHash(): string { return this.data.contentHash; } - get declaredModules(): ReadonlyArray { return this.data.declaredModules; } - get projectName(): string { return this.data.projectName; } - get globals(): ReadonlyArray { return this.data.globals; } - get pathMappings(): ReadonlyArray { return this.data.pathMappings; } - - get dependencies(): ReadonlyArray { - return this.data.dependencies; - } - - get versionDirectoryName() { - return this.data.libraryVersionDirectoryName && `v${this.data.libraryVersionDirectoryName}`; - } - - /** Path to this package, *relative* to the DefinitelyTyped directory. */ - get subDirectoryPath(): string { - return this.isLatest ? this.name : `${this.name}/${this.versionDirectoryName}`; - } + constructor(private readonly data: TypingsDataRaw, readonly isLatest: boolean) { + super(data); + } + + get testDependencies(): readonly string[] { + return this.data.testDependencies; + } + get contributors(): readonly Author[] { + return this.data.contributors; + } + get major(): number { + return this.data.libraryMajorVersion; + } + get minor(): number { + return this.data.libraryMinorVersion; + } + + get minTypeScriptVersion(): TypeScriptVersion { + return TypeScriptVersion.isSupported(this.data.minTsVersion) ? this.data.minTsVersion : TypeScriptVersion.lowest; + } + get typesVersions(): readonly TypeScriptVersion[] { + return this.data.typesVersions; + } + + get files(): readonly string[] { + return this.data.files; + } + get license(): License { + return this.data.license; + } + get packageJsonDependencies(): readonly PackageJsonDependency[] { + return this.data.packageJsonDependencies; + } + get contentHash(): string { + return this.data.contentHash; + } + get declaredModules(): readonly string[] { + return this.data.declaredModules; + } + get projectName(): string { + return this.data.projectName; + } + get globals(): readonly string[] { + return this.data.globals; + } + get pathMappings(): readonly PathMapping[] { + return this.data.pathMappings; + } + + get dependencies(): readonly PackageId[] { + return this.data.dependencies; + } + + get versionDirectoryName() { + return this.data.libraryVersionDirectoryName && `v${this.data.libraryVersionDirectoryName}`; + } + + /** Path to this package, *relative* to the DefinitelyTyped directory. */ + get subDirectoryPath(): string { + return this.isLatest ? this.name : `${this.name}/${this.versionDirectoryName}`; + } } /** Uniquely identifies a package. */ export interface PackageId { - readonly name: string; - readonly version: DependencyVersion; + readonly name: string; + readonly version: DependencyVersion; } export interface TypesDataFile { - readonly [packageName: string]: TypingsVersionsRaw; + readonly [packageName: string]: TypingsVersionsRaw; } function readTypesDataFile(): Promise { - return readDataFile("parse-definitions", typesDataFilename) as Promise; + return readDataFile("parse-definitions", typesDataFilename) as Promise; } -export function readNotNeededPackages(dt: FS): ReadonlyArray { - const rawJson = dt.readJson("notNeededPackages.json"); // tslint:disable-line await-promise (tslint bug) - return (rawJson as { readonly packages: ReadonlyArray }).packages.map(raw => new NotNeededPackage(raw)); +export function readNotNeededPackages(dt: FS): readonly NotNeededPackage[] { + const rawJson = dt.readJson("notNeededPackages.json"); // tslint:disable-line await-promise (tslint bug) + return (rawJson as { readonly packages: readonly NotNeededPackageRaw[] }).packages.map( + raw => new NotNeededPackage(raw) + ); } diff --git a/packages/definitions-parser/src/parse-definitions.ts b/packages/definitions-parser/src/parse-definitions.ts index 9d7497b347..e5878e6366 100644 --- a/packages/definitions-parser/src/parse-definitions.ts +++ b/packages/definitions-parser/src/parse-definitions.ts @@ -7,45 +7,51 @@ import { AllPackages, readNotNeededPackages, typesDataFilename, TypingsVersionsR export { parseVersionFromDirectoryName } from "./lib/definition-parser"; export interface ParallelOptions { - readonly nProcesses: number; - readonly definitelyTypedPath: string; + readonly nProcesses: number; + readonly definitelyTypedPath: string; } -export async function parseDefinitions(dt: FS, parallel: ParallelOptions | undefined, log: LoggerWithErrors): Promise { - log.info("Parsing definitions..."); - const typesFS = dt.subDir("types"); - const packageNames = await filterNAtATimeOrdered(parallel ? parallel.nProcesses : 1, typesFS.readdir(), name => typesFS.isDirectory(name)); - log.info(`Found ${packageNames.length} packages.`); +export async function parseDefinitions( + dt: FS, + parallel: ParallelOptions | undefined, + log: LoggerWithErrors +): Promise { + log.info("Parsing definitions..."); + const typesFS = dt.subDir("types"); + const packageNames = await filterNAtATimeOrdered(parallel ? parallel.nProcesses : 1, typesFS.readdir(), name => + typesFS.isDirectory(name) + ); + log.info(`Found ${packageNames.length} packages.`); - const typings: { [name: string]: TypingsVersionsRaw } = {}; + const typings: { [name: string]: TypingsVersionsRaw } = {}; - const start = Date.now(); - if (parallel) { - log.info("Parsing in parallel..."); - await runWithChildProcesses({ - inputs: packageNames, - commandLineArgs: [`${parallel.definitelyTypedPath}/types`], - workerFile: definitionParserWorkerFilename, - nProcesses: parallel.nProcesses, - handleOutput({ data, packageName}: { data: TypingsVersionsRaw, packageName: string }) { - typings[packageName] = data; - }, - }); - } else { - log.info("Parsing non-parallel..."); - for (const packageName of packageNames) { - typings[packageName] = getTypingInfo(packageName, typesFS.subDir(packageName)); - } + const start = Date.now(); + if (parallel) { + log.info("Parsing in parallel..."); + await runWithChildProcesses({ + inputs: packageNames, + commandLineArgs: [`${parallel.definitelyTypedPath}/types`], + workerFile: definitionParserWorkerFilename, + nProcesses: parallel.nProcesses, + handleOutput({ data, packageName }: { data: TypingsVersionsRaw; packageName: string }) { + typings[packageName] = data; + } + }); + } else { + log.info("Parsing non-parallel..."); + for (const packageName of packageNames) { + typings[packageName] = getTypingInfo(packageName, typesFS.subDir(packageName)); } - log.info("Parsing took " + ((Date.now() - start) / 1000) + " s"); - await writeDataFile(typesDataFilename, sorted(typings)); - return AllPackages.from(typings, readNotNeededPackages(dt)); + } + log.info("Parsing took " + (Date.now() - start) / 1000 + " s"); + await writeDataFile(typesDataFilename, sorted(typings)); + return AllPackages.from(typings, readNotNeededPackages(dt)); } function sorted(obj: { [name: string]: T }): { [name: string]: T } { - const out: { [name: string]: T } = {}; - for (const key of Object.keys(obj).sort()) { - out[key] = obj[key]; - } - return out; + const out: { [name: string]: T } = {}; + for (const key of Object.keys(obj).sort()) { + out[key] = obj[key]; + } + return out; } diff --git a/packages/definitions-parser/test/definition-parser.test.ts b/packages/definitions-parser/test/definition-parser.test.ts index e71d27a9fb..d16d2ec1be 100644 --- a/packages/definitions-parser/test/definition-parser.test.ts +++ b/packages/definitions-parser/test/definition-parser.test.ts @@ -2,94 +2,96 @@ import { createMockDT } from "../src/mocks"; import { getTypingInfo } from "../src/lib/definition-parser"; describe(getTypingInfo, () => { - it("keys data by major.minor version", () => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "1.42"); - dt.addOldVersionOfPackage("jquery", "2"); - const info = getTypingInfo("jquery", dt.pkgFS("jquery")); + it("keys data by major.minor version", () => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "1.42"); + dt.addOldVersionOfPackage("jquery", "2"); + const info = getTypingInfo("jquery", dt.pkgFS("jquery")); - expect(Object.keys(info).sort()).toEqual(["1.42", "2.0", "3.3"]); - }); + expect(Object.keys(info).sort()).toEqual(["1.42", "2.0", "3.3"]); + }); - describe("concerning multiple versions", () => { - it("records what the version directory looks like on disk", () => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "2"); - dt.addOldVersionOfPackage("jquery", "1.5"); - const info = getTypingInfo("jquery", dt.pkgFS("jquery")); + describe("concerning multiple versions", () => { + it("records what the version directory looks like on disk", () => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "2"); + dt.addOldVersionOfPackage("jquery", "1.5"); + const info = getTypingInfo("jquery", dt.pkgFS("jquery")); - expect(info).toEqual({ - "1.5": expect.objectContaining({ - libraryVersionDirectoryName: "1.5", - }), - "2.0": expect.objectContaining({ - libraryVersionDirectoryName: "2", - }), - "3.3": expect.objectContaining({ - // The latest version does not have its own version directory - libraryVersionDirectoryName: undefined, - }), - }); - }); + expect(info).toEqual({ + "1.5": expect.objectContaining({ + libraryVersionDirectoryName: "1.5" + }), + "2.0": expect.objectContaining({ + libraryVersionDirectoryName: "2" + }), + "3.3": expect.objectContaining({ + // The latest version does not have its own version directory + libraryVersionDirectoryName: undefined + }) + }); + }); - it("records a path mapping to the version directory", () => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "2"); - dt.addOldVersionOfPackage("jquery", "1.5"); - const info = getTypingInfo("jquery", dt.pkgFS("jquery")); + it("records a path mapping to the version directory", () => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "2"); + dt.addOldVersionOfPackage("jquery", "1.5"); + const info = getTypingInfo("jquery", dt.pkgFS("jquery")); - expect(info).toEqual({ - "1.5": expect.objectContaining({ - pathMappings: [{ - packageName: "jquery", - version: { major: 1, minor: 5 }, - }], - }), - "2.0": expect.objectContaining({ - pathMappings: [{ - packageName: "jquery", - version: { major: 2, minor: undefined }, - }], - }), - "3.3": expect.objectContaining({ - // The latest version does not have path mappings of its own - pathMappings: [], - }), - }); - }); + expect(info).toEqual({ + "1.5": expect.objectContaining({ + pathMappings: [ + { + packageName: "jquery", + version: { major: 1, minor: 5 } + } + ] + }), + "2.0": expect.objectContaining({ + pathMappings: [ + { + packageName: "jquery", + version: { major: 2, minor: undefined } + } + ] + }), + "3.3": expect.objectContaining({ + // The latest version does not have path mappings of its own + pathMappings: [] + }) + }); + }); - describe("validation thereof", () => { - it("throws if a directory exists for the latest major version", () => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "3"); + describe("validation thereof", () => { + it("throws if a directory exists for the latest major version", () => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "3"); - expect(() => { - getTypingInfo("jquery", dt.pkgFS("jquery")); - }).toThrow( - "The latest version is 3.3, so the subdirectory 'v3' is not allowed; " + - "since it applies to any 3.* version, up to and including 3.3.", - ); - }); + expect(() => { + getTypingInfo("jquery", dt.pkgFS("jquery")); + }).toThrow( + "The latest version is 3.3, so the subdirectory 'v3' is not allowed; " + + "since it applies to any 3.* version, up to and including 3.3." + ); + }); - it("throws if a directory exists for the latest minor version", () => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "3.3"); + it("throws if a directory exists for the latest minor version", () => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "3.3"); - expect(() => { - getTypingInfo("jquery", dt.pkgFS("jquery")); - }).toThrow( - "The latest version is 3.3, so the subdirectory 'v3.3' is not allowed.", - ); - }); + expect(() => { + getTypingInfo("jquery", dt.pkgFS("jquery")); + }).toThrow("The latest version is 3.3, so the subdirectory 'v3.3' is not allowed."); + }); - it("does not throw when a minor version is older than the latest", () => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "3.0"); + it("does not throw when a minor version is older than the latest", () => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "3.0"); - expect(() => { - getTypingInfo("jquery", dt.pkgFS("jquery")); - }).not.toThrow(); - }); - }); + expect(() => { + getTypingInfo("jquery", dt.pkgFS("jquery")); + }).not.toThrow(); + }); }); + }); }); diff --git a/packages/definitions-parser/test/get-definitely-typed.test.ts b/packages/definitions-parser/test/get-definitely-typed.test.ts index 6081176d75..5e02442e4f 100644 --- a/packages/definitions-parser/test/get-definitely-typed.test.ts +++ b/packages/definitions-parser/test/get-definitely-typed.test.ts @@ -3,29 +3,32 @@ import { loggerWithErrors, Dir, FS, InMemoryFS } from "@definitelytyped/utils"; import { testo } from "./utils"; testo({ - async downloadDefinitelyTyped() { - const dt = await getDefinitelyTyped({ - definitelyTypedPath: undefined, - parseInParallel: false, - progress: false, - }, loggerWithErrors()[0]); - expect(dt.exists("types")).toBe(true); - expect(dt.exists("buncho")).toBe(false); - }, - createDirs() { - const root = new Dir(undefined); - root.set("file1.txt", "ok"); - expect(root.has("file1.txt")).toBe(true); - expect(root.get("file1.txt")).toBe("ok"); - }, - simpleMemoryFS() { - const root = new Dir(undefined); - root.set("file1.txt", "ok"); - const dir = root.subdir("sub1"); - dir.set("file2.txt", "x"); - const fs: FS = new InMemoryFS(root, "test/"); - expect(fs.exists("file1.txt")).toBe(true); - expect(fs.readFile("file1.txt")).toBe("ok"); - expect(fs.readFile("sub1/file2.txt")).toBe("x"); - }, + async downloadDefinitelyTyped() { + const dt = await getDefinitelyTyped( + { + definitelyTypedPath: undefined, + parseInParallel: false, + progress: false + }, + loggerWithErrors()[0] + ); + expect(dt.exists("types")).toBe(true); + expect(dt.exists("buncho")).toBe(false); + }, + createDirs() { + const root = new Dir(undefined); + root.set("file1.txt", "ok"); + expect(root.has("file1.txt")).toBe(true); + expect(root.get("file1.txt")).toBe("ok"); + }, + simpleMemoryFS() { + const root = new Dir(undefined); + root.set("file1.txt", "ok"); + const dir = root.subdir("sub1"); + dir.set("file2.txt", "x"); + const fs: FS = new InMemoryFS(root, "test/"); + expect(fs.exists("file1.txt")).toBe(true); + expect(fs.readFile("file1.txt")).toBe("ok"); + expect(fs.readFile("sub1/file2.txt")).toBe("x"); + } }); diff --git a/packages/definitions-parser/test/module-info.test.ts b/packages/definitions-parser/test/module-info.test.ts index 2157fa22c6..b80cd04be9 100644 --- a/packages/definitions-parser/test/module-info.test.ts +++ b/packages/definitions-parser/test/module-info.test.ts @@ -6,73 +6,114 @@ import { allReferencedFiles, getModuleInfo, getTestDependencies } from "../src/l const fs = createMockDT().fs; function getBoringReferences() { - return allReferencedFiles(["index.d.ts", "boring-tests.ts"], fs.subDir("types").subDir("boring"), "boring", "types/boring"); + return allReferencedFiles( + ["index.d.ts", "boring-tests.ts"], + fs.subDir("types").subDir("boring"), + "boring", + "types/boring" + ); } testo({ - allReferencedFilesFromTsconfigFiles() { - const { types, tests } = getBoringReferences(); - expect(Array.from(types.keys())).toEqual(["index.d.ts", "secondary.d.ts", "quaternary.d.ts", "tertiary.d.ts", "commonjs.d.ts", "v1.d.ts"]); - expect(Array.from(tests.keys())).toEqual(["boring-tests.ts"]); - }, - allReferencedFilesFromTestIncludesSecondaryInternalFiles() { - const { types, tests } = allReferencedFiles(["boring-tests.ts"], fs.subDir("types").subDir("boring"), "boring", "types/boring"); - expect(Array.from(types.keys())).toEqual(["secondary.d.ts", "quaternary.d.ts", "tertiary.d.ts", "commonjs.d.ts", "v1.d.ts"]); - expect(Array.from(tests.keys())).toEqual(["boring-tests.ts"]); - }, - allReferencedFilesFromTsconfigGlobal() { - const { types, tests } = allReferencedFiles(["jquery-tests.ts", "index.d.ts"], fs.subDir("types").subDir("jquery"), "jquery", "types/jquery"); - expect(Array.from(types.keys())).toEqual(["index.d.ts", "JQuery.d.ts"]); - expect(Array.from(tests.keys())).toEqual(["jquery-tests.ts"]); - }, - allReferencedFilesFromTestIncludesSecondaryTripleSlashTypes() { - const { types, tests } = allReferencedFiles( - ["globby-tests.ts", "test/other-tests.ts"], - fs.subDir("types").subDir("globby"), - "globby", - "types/globby", - ); - expect(Array.from(types.keys())).toEqual(["merges.d.ts"]); - expect(Array.from(tests.keys())).toEqual(["globby-tests.ts", "test/other-tests.ts"]); - }, - getModuleInfoWorksWithOtherFiles() { - const { types } = getBoringReferences(); - // written as if it were from OTHER_FILES.txt - types.set( - "untested.d.ts", - ts.createSourceFile("untested.d.ts", fs.subDir("types").subDir("boring").readFile("untested.d.ts"), ts.ScriptTarget.Latest, false), - ); - const i = getModuleInfo("boring", types); - expect(i.dependencies).toEqual(new Set(["manual", "react", "react-default", "things", "vorticon"])); - }, - getModuleInfoForNestedTypeReferences() { - const { types } = allReferencedFiles( - ["index.d.ts", "globby-tests.ts", "test/other-tests.ts"], - fs.subDir("types").subDir("globby"), - "globby", - "types/globby", - ); - expect(Array.from(types.keys())).toEqual(["index.d.ts", "sneaky.d.ts", "merges.d.ts"]); - const i = getModuleInfo("globby", types); - expect(i.dependencies).toEqual(new Set(["andere"])); - }, - versionTypeRefThrows() { - const fail = new Dir(undefined); - const memFS = new InMemoryFS(fail, "typeref-fails"); - fail.set("index.d.ts", `// Type definitionssrc/ for fail 1.0 + allReferencedFilesFromTsconfigFiles() { + const { types, tests } = getBoringReferences(); + expect(Array.from(types.keys())).toEqual([ + "index.d.ts", + "secondary.d.ts", + "quaternary.d.ts", + "tertiary.d.ts", + "commonjs.d.ts", + "v1.d.ts" + ]); + expect(Array.from(tests.keys())).toEqual(["boring-tests.ts"]); + }, + allReferencedFilesFromTestIncludesSecondaryInternalFiles() { + const { types, tests } = allReferencedFiles( + ["boring-tests.ts"], + fs.subDir("types").subDir("boring"), + "boring", + "types/boring" + ); + expect(Array.from(types.keys())).toEqual([ + "secondary.d.ts", + "quaternary.d.ts", + "tertiary.d.ts", + "commonjs.d.ts", + "v1.d.ts" + ]); + expect(Array.from(tests.keys())).toEqual(["boring-tests.ts"]); + }, + allReferencedFilesFromTsconfigGlobal() { + const { types, tests } = allReferencedFiles( + ["jquery-tests.ts", "index.d.ts"], + fs.subDir("types").subDir("jquery"), + "jquery", + "types/jquery" + ); + expect(Array.from(types.keys())).toEqual(["index.d.ts", "JQuery.d.ts"]); + expect(Array.from(tests.keys())).toEqual(["jquery-tests.ts"]); + }, + allReferencedFilesFromTestIncludesSecondaryTripleSlashTypes() { + const { types, tests } = allReferencedFiles( + ["globby-tests.ts", "test/other-tests.ts"], + fs.subDir("types").subDir("globby"), + "globby", + "types/globby" + ); + expect(Array.from(types.keys())).toEqual(["merges.d.ts"]); + expect(Array.from(tests.keys())).toEqual(["globby-tests.ts", "test/other-tests.ts"]); + }, + getModuleInfoWorksWithOtherFiles() { + const { types } = getBoringReferences(); + // written as if it were from OTHER_FILES.txt + types.set( + "untested.d.ts", + ts.createSourceFile( + "untested.d.ts", + fs + .subDir("types") + .subDir("boring") + .readFile("untested.d.ts"), + ts.ScriptTarget.Latest, + false + ) + ); + const i = getModuleInfo("boring", types); + expect(i.dependencies).toEqual(new Set(["manual", "react", "react-default", "things", "vorticon"])); + }, + getModuleInfoForNestedTypeReferences() { + const { types } = allReferencedFiles( + ["index.d.ts", "globby-tests.ts", "test/other-tests.ts"], + fs.subDir("types").subDir("globby"), + "globby", + "types/globby" + ); + expect(Array.from(types.keys())).toEqual(["index.d.ts", "sneaky.d.ts", "merges.d.ts"]); + const i = getModuleInfo("globby", types); + expect(i.dependencies).toEqual(new Set(["andere"])); + }, + versionTypeRefThrows() { + const fail = new Dir(undefined); + const memFS = new InMemoryFS(fail, "typeref-fails"); + fail.set( + "index.d.ts", + `// Type definitionssrc/ for fail 1.0 // Project: https://youtube.com/s-fails // Definitions by: Type Ref Fails // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped /// -`); - const { types } = allReferencedFiles(["index.d.ts"], memFS, "typeref-fails", "types/typeref-fails"); - expect(Array.from(types.keys())).toEqual(["index.d.ts"]); - expect(() => getModuleInfo("typeref-fails", types)).toThrow("do not directly import specific versions of another types package"); - }, - getTestDependenciesWorks() { - const { types, tests } = getBoringReferences(); - const i = getModuleInfo("boring", types); - const d = getTestDependencies("boring", types, tests.keys(), i.dependencies, fs.subDir("types").subDir("boring")); - expect(d).toEqual(new Set(["super-big-fun-hus"])); - }, +` + ); + const { types } = allReferencedFiles(["index.d.ts"], memFS, "typeref-fails", "types/typeref-fails"); + expect(Array.from(types.keys())).toEqual(["index.d.ts"]); + expect(() => getModuleInfo("typeref-fails", types)).toThrow( + "do not directly import specific versions of another types package" + ); + }, + getTestDependenciesWorks() { + const { types, tests } = getBoringReferences(); + const i = getModuleInfo("boring", types); + const d = getTestDependencies("boring", types, tests.keys(), i.dependencies, fs.subDir("types").subDir("boring")); + expect(d).toEqual(new Set(["super-big-fun-hus"])); + } }); diff --git a/packages/definitions-parser/test/packages.test.ts b/packages/definitions-parser/test/packages.test.ts index a58f418026..944cbf4d61 100644 --- a/packages/definitions-parser/test/packages.test.ts +++ b/packages/definitions-parser/test/packages.test.ts @@ -3,45 +3,45 @@ import { getTypingInfo } from "../src/lib/definition-parser"; import { TypingsVersions } from "../src/packages"; describe(TypingsVersions, () => { - let versions: TypingsVersions; - - beforeAll(() => { - const dt = createMockDT(); - dt.addOldVersionOfPackage("jquery", "1"); - dt.addOldVersionOfPackage("jquery", "2"); - dt.addOldVersionOfPackage("jquery", "2.5"); - versions = new TypingsVersions(getTypingInfo("jquery", dt.pkgFS("jquery"))); - }); - - it("sorts the data from latest to oldest version", () => { - expect(Array.from(versions.getAll()).map(v => v.major)).toEqual([3, 2, 2, 1]); - }); - - it("returns the latest version", () => { - expect(versions.getLatest().major).toEqual(3); - }); - - it("finds the latest version when any version is wanted", () => { - expect(versions.get("*").major).toEqual(3); - }); - - it("finds the latest minor version for the given major version", () => { - expect(versions.get({ major: 2 }).major).toEqual(2); - expect(versions.get({ major: 2 }).minor).toEqual(5); - }); - - it("finds a specific version", () => { - expect(versions.get({ major: 2, minor: 0 }).major).toEqual(2); - expect(versions.get({ major: 2, minor: 0 }).minor).toEqual(0); - }); - - it("formats a version directory names", () => { - expect(versions.get({ major: 2, minor: 0 }).versionDirectoryName).toEqual("v2"); - expect(versions.get({ major: 2, minor: 0 }).subDirectoryPath).toEqual("jquery/v2"); - }); - - it("formats missing version error nicely", () => { - expect(() => versions.get({ major: 111, minor: 1001 })).toThrow("Could not find version 111.1001"); - expect(() => versions.get({ major: 111 })).toThrow("Could not find version 111.*"); - }); + let versions: TypingsVersions; + + beforeAll(() => { + const dt = createMockDT(); + dt.addOldVersionOfPackage("jquery", "1"); + dt.addOldVersionOfPackage("jquery", "2"); + dt.addOldVersionOfPackage("jquery", "2.5"); + versions = new TypingsVersions(getTypingInfo("jquery", dt.pkgFS("jquery"))); + }); + + it("sorts the data from latest to oldest version", () => { + expect(Array.from(versions.getAll()).map(v => v.major)).toEqual([3, 2, 2, 1]); + }); + + it("returns the latest version", () => { + expect(versions.getLatest().major).toEqual(3); + }); + + it("finds the latest version when any version is wanted", () => { + expect(versions.get("*").major).toEqual(3); + }); + + it("finds the latest minor version for the given major version", () => { + expect(versions.get({ major: 2 }).major).toEqual(2); + expect(versions.get({ major: 2 }).minor).toEqual(5); + }); + + it("finds a specific version", () => { + expect(versions.get({ major: 2, minor: 0 }).major).toEqual(2); + expect(versions.get({ major: 2, minor: 0 }).minor).toEqual(0); + }); + + it("formats a version directory names", () => { + expect(versions.get({ major: 2, minor: 0 }).versionDirectoryName).toEqual("v2"); + expect(versions.get({ major: 2, minor: 0 }).subDirectoryPath).toEqual("jquery/v2"); + }); + + it("formats missing version error nicely", () => { + expect(() => versions.get({ major: 111, minor: 1001 })).toThrow("Could not find version 111.1001"); + expect(() => versions.get({ major: 111 })).toThrow("Could not find version 111.*"); + }); }); diff --git a/packages/definitions-parser/test/parse-definitions.test.ts b/packages/definitions-parser/test/parse-definitions.test.ts index 29f759739e..d047ca7433 100644 --- a/packages/definitions-parser/test/parse-definitions.test.ts +++ b/packages/definitions-parser/test/parse-definitions.test.ts @@ -4,27 +4,27 @@ import { loggerWithErrors } from "@definitelytyped/utils"; import { testo } from "./utils"; testo({ - // async parseDefinitions() { - // const log = loggerWithErrors()[0] - // const dt = await getDefinitelyTyped(Options.defaults, log); - // const defs = await parseDefinitions(dt, undefined, log) - // expect(defs.allNotNeeded().length).toBeGreaterThan(0) - // expect(defs.allTypings().length).toBeGreaterThan(5000) - // const j = defs.tryGetLatestVersion("jquery") - // expect(j).toBeDefined() - // expect(j!.fullNpmName).toContain("types") - // expect(j!.fullNpmName).toContain("jquery") - // expect(defs.allPackages().length).toEqual(defs.allTypings().length + defs.allNotNeeded().length) - // }, - async mockParse() { - const log = loggerWithErrors()[0]; - const defs = await parseDefinitions(createMockDT().fs, undefined, log); - expect(defs.allNotNeeded().length).toBe(1); - expect(defs.allTypings().length).toBe(3); - const j = defs.tryGetLatestVersion("jquery"); - expect(j).toBeDefined(); - expect(j!.fullNpmName).toContain("types"); - expect(j!.fullNpmName).toContain("jquery"); - expect(defs.allPackages().length).toEqual(defs.allTypings().length + defs.allNotNeeded().length); - }, + // async parseDefinitions() { + // const log = loggerWithErrors()[0] + // const dt = await getDefinitelyTyped(Options.defaults, log); + // const defs = await parseDefinitions(dt, undefined, log) + // expect(defs.allNotNeeded().length).toBeGreaterThan(0) + // expect(defs.allTypings().length).toBeGreaterThan(5000) + // const j = defs.tryGetLatestVersion("jquery") + // expect(j).toBeDefined() + // expect(j!.fullNpmName).toContain("types") + // expect(j!.fullNpmName).toContain("jquery") + // expect(defs.allPackages().length).toEqual(defs.allTypings().length + defs.allNotNeeded().length) + // }, + async mockParse() { + const log = loggerWithErrors()[0]; + const defs = await parseDefinitions(createMockDT().fs, undefined, log); + expect(defs.allNotNeeded().length).toBe(1); + expect(defs.allTypings().length).toBe(3); + const j = defs.tryGetLatestVersion("jquery"); + expect(j).toBeDefined(); + expect(j!.fullNpmName).toContain("types"); + expect(j!.fullNpmName).toContain("jquery"); + expect(defs.allPackages().length).toEqual(defs.allTypings().length + defs.allNotNeeded().length); + } }); diff --git a/packages/definitions-parser/test/utils.ts b/packages/definitions-parser/test/utils.ts index 1ee062658a..6ae68e1d47 100644 --- a/packages/definitions-parser/test/utils.ts +++ b/packages/definitions-parser/test/utils.ts @@ -1,5 +1,5 @@ export function testo(o: { [s: string]: () => void }) { for (const k of Object.keys(o)) { - test(k, o[k], 100_000); + test(k, o[k], 100_000); } } diff --git a/packages/header-parser/src/index.ts b/packages/header-parser/src/index.ts index 9725504d46..75a5fb179a 100644 --- a/packages/header-parser/src/index.ts +++ b/packages/header-parser/src/index.ts @@ -11,101 +11,106 @@ Example: */ export interface Header { - readonly nonNpm: boolean; - readonly libraryName: string; - readonly libraryMajorVersion: number; - readonly libraryMinorVersion: number; - readonly typeScriptVersion: AllTypeScriptVersion; - readonly projects: ReadonlyArray; - readonly contributors: ReadonlyArray; + readonly nonNpm: boolean; + readonly libraryName: string; + readonly libraryMajorVersion: number; + readonly libraryMinorVersion: number; + readonly typeScriptVersion: AllTypeScriptVersion; + readonly projects: readonly string[]; + readonly contributors: readonly Author[]; } export interface Author { - readonly name: string; - readonly url: string; - readonly githubUsername: string | undefined; + readonly name: string; + readonly url: string; + readonly githubUsername: string | undefined; } export interface ParseError { - readonly index: number; - readonly line: number; - readonly column: number; - readonly expected: ReadonlyArray; + readonly index: number; + readonly line: number; + readonly column: number; + readonly expected: readonly string[]; } export function isTypeScriptVersion(str: string): str is TypeScriptVersion { - return TypeScriptVersion.all.includes(str as TypeScriptVersion); + return TypeScriptVersion.all.includes(str as TypeScriptVersion); } -export function makeTypesVersionsForPackageJson(typesVersions: ReadonlyArray): unknown { - if (typesVersions.length === 0) { return undefined; } +export function makeTypesVersionsForPackageJson(typesVersions: readonly TypeScriptVersion[]): unknown { + if (typesVersions.length === 0) { + return undefined; + } - const out: { [key: string]: { readonly "*": ReadonlyArray } } = {}; - for (const version of typesVersions) { - out[`>=${version}.0-0`] = { "*": [`ts${version}/*`] }; - } - return out; + const out: { [key: string]: { readonly "*": readonly string[] } } = {}; + for (const version of typesVersions) { + out[`>=${version}.0-0`] = { "*": [`ts${version}/*`] }; + } + return out; } export function parseHeaderOrFail(mainFileContent: string): Header { - const header = parseHeader(mainFileContent, /*strict*/false); - if (isParseError(header)) { - throw new Error(renderParseError(header)); - } - return header; + const header = parseHeader(mainFileContent, /*strict*/ false); + if (isParseError(header)) { + throw new Error(renderParseError(header)); + } + return header; } export function validate(mainFileContent: string): ParseError | undefined { - const h = parseHeader(mainFileContent, /*strict*/true); - return isParseError(h) ? h : undefined; + const h = parseHeader(mainFileContent, /*strict*/ true); + return isParseError(h) ? h : undefined; } -export function renderExpected(expected: ReadonlyArray): string { - return expected.length === 1 ? expected[0] : `one of\n\t${expected.join("\n\t")}`; +export function renderExpected(expected: readonly string[]): string { + return expected.length === 1 ? expected[0] : `one of\n\t${expected.join("\n\t")}`; } function renderParseError({ line, column, expected }: ParseError): string { - return `At ${line}:${column} : Expected ${renderExpected(expected)}`; + return `At ${line}:${column} : Expected ${renderExpected(expected)}`; } function isParseError(x: {}): x is ParseError { - // tslint:disable-next-line strict-type-predicates - return (x as ParseError).expected !== undefined; + // tslint:disable-next-line strict-type-predicates + return (x as ParseError).expected !== undefined; } /** @param strict If true, we allow fewer things to be parsed. Turned on by linting. */ function parseHeader(text: string, strict: boolean): Header | ParseError { - const res = headerParser(strict).parse(text); - return res.status - ? res.value - : { index: res.index.offset, line: res.index.line, column: res.index.column, expected: res.expected }; + const res = headerParser(strict).parse(text); + return res.status + ? res.value + : { index: res.index.offset, line: res.index.line, column: res.index.column, expected: res.expected }; } function headerParser(strict: boolean): pm.Parser
{ - return pm.seqMap( - pm.regex(/\/\/ Type definitions for (non-npm package )?/), - parseLabel(strict), - pm.string("// Project: "), - projectParser, - pm.regexp(/\r?\n\/\/ Definitions by: /), - contributorsParser(strict), - definitionsParser, - typeScriptVersionParser, - pm.all, // Don't care about the rest of the file - // tslint:disable-next-line:variable-name - (str, label, _project, projects, _defsBy, contributors, _definitions, typeScriptVersion) => ({ - libraryName: label.name, - libraryMajorVersion: label.major, - libraryMinorVersion: label.minor, - nonNpm: str.endsWith("non-npm package "), - projects, contributors, typeScriptVersion, - })); + return pm.seqMap( + pm.regex(/\/\/ Type definitions for (non-npm package )?/), + parseLabel(strict), + pm.string("// Project: "), + projectParser, + pm.regexp(/\r?\n\/\/ Definitions by: /), + contributorsParser(strict), + definitionsParser, + typeScriptVersionParser, + pm.all, // Don't care about the rest of the file + // tslint:disable-next-line:variable-name + (str, label, _project, projects, _defsBy, contributors, _definitions, typeScriptVersion) => ({ + libraryName: label.name, + libraryMajorVersion: label.major, + libraryMinorVersion: label.minor, + nonNpm: str.endsWith("non-npm package "), + projects, + contributors, + typeScriptVersion + }) + ); } interface Label { - readonly name: string; - readonly major: number; - readonly minor: number; + readonly name: string; + readonly major: number; + readonly minor: number; } /* @@ -123,132 +128,151 @@ Use `\s\s+` to ensure at least 2 spaces, to disambiguate from the next line bei */ const separator: pm.Parser = pm.regexp(/(, )|(,?\r?\n\/\/\s\s+)/); -const projectParser: pm.Parser> = pm.sepBy1(pm.regexp(/[^,\r\n]+/), separator); - -function contributorsParser(strict: boolean): pm.Parser> { - const contributor: pm.Parser = strict - ? pm.seqMap( - pm.regexp(/([^<]+) /, 1), - pm.regexp(/\/, 1), - (name, githubUsername) => ({ name, url: `https://github.com/${githubUsername}`, githubUsername })) - // In non-strict mode, allows arbitrary URL, and trailing whitespace. - : pm.seqMap(pm.regexp(/([^<]+) /, 1), pm.regexp(/<([^>]+)> */, 1), (name, url) => { - const rgx = /^https\:\/\/github.com\/([a-zA-Z\d\-]+)$/; - const match = rgx.exec(url); - // tslint:disable-next-line no-null-keyword - return ({ name, url, githubUsername: match === null ? undefined : match[1] }); - }); - return pm.sepBy1(contributor, separator); +const projectParser: pm.Parser = pm.sepBy1(pm.regexp(/[^,\r\n]+/), separator); + +function contributorsParser(strict: boolean): pm.Parser { + const contributor: pm.Parser = strict + ? pm.seqMap( + pm.regexp(/([^<]+) /, 1), + pm.regexp(/\/, 1), + (name, githubUsername) => ({ name, url: `https://github.com/${githubUsername}`, githubUsername }) + ) + : // In non-strict mode, allows arbitrary URL, and trailing whitespace. + pm.seqMap(pm.regexp(/([^<]+) /, 1), pm.regexp(/<([^>]+)> */, 1), (name, url) => { + const rgx = /^https\:\/\/github.com\/([a-zA-Z\d\-]+)$/; + const match = rgx.exec(url); + // tslint:disable-next-line no-null-keyword + return { name, url, githubUsername: match === null ? undefined : match[1] }; + }); + return pm.sepBy1(contributor, separator); } // TODO: Should we do something with the URL? const definitionsParser = pm.regexp(/\r?\n\/\/ Definitions: [^\r\n]+/); function parseLabel(strict: boolean): pm.Parser