diff --git a/.gitmodules b/.gitmodules index e5cf4c016e9..bd4620625cf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,24 +2,19 @@ path = tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter url = https://github.com/Microsoft/TypeScript-React-Starter ignore = all - shallow = true [submodule "tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter"] path = tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter url = https://github.com/Microsoft/TypeScript-Node-Starter.git ignore = all - shallow = true [submodule "tests/cases/user/TypeScript-React-Native-Starter/TypeScript-React-Native-Starter"] path = tests/cases/user/TypeScript-React-Native-Starter/TypeScript-React-Native-Starter url = https://github.com/Microsoft/TypeScript-React-Native-Starter.git ignore = all - shallow = true [submodule "tests/cases/user/TypeScript-Vue-Starter/TypeScript-Vue-Starter"] path = tests/cases/user/TypeScript-Vue-Starter/TypeScript-Vue-Starter url = https://github.com/Microsoft/TypeScript-Vue-Starter.git ignore = all - shallow = true [submodule "tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter"] path = tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter url = https://github.com/Microsoft/TypeScript-WeChat-Starter.git ignore = all - shallow = true diff --git a/.travis.yml b/.travis.yml index 06e912c55f5..b4a5227c34a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ branches: - master - release-2.5 - release-2.6 + - release-2.7 install: - npm uninstall typescript --no-save diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6dbb7e514a0..1d11db2f87b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,14 @@ Issues that ask questions answered in the FAQ will be closed without elaboration ## 2. Search for Duplicates -[Search the existing issues](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&q=is%3Aissue) before logging a new one. +[Search the existing issues](https://github.com/Microsoft/TypeScript/search?type=Issues) before logging a new one. + +Some search tips: + * *Don't* restrict your search to only open issues. An issue with a title similar to yours may have been closed as a duplicate of one with a less-findable title. + * Check for synonyms. For example, if your bug involves an interface, it likely also occurs with type aliases or classes. + * Search for the title of the issue you're about to log. This sounds obvious but 80% of the time this is sufficient to find a duplicate when one exists. + * Read more than the first page of results. Many bugs here use the same words so relevancy sorting is not particularly strong. + * If you have a crash, search for the first few topmost function names shown in the call stack. ## 3. Do you have a question? @@ -183,3 +190,10 @@ jake baseline-accept ``` to establish the new baselines as the desired behavior. This will change the files in `tests\baselines\reference`, which should be included as part of your commit. It's important to carefully validate changes in the baselines. + +## Localization + +All strings the user may see are stored in [`diagnosticMessages.json`](./src/compiler/diagnosticMessages.json). +If you make changes to it, run `jake generate-diagnostics` to push them to the `Diagnostic` interface in [`diagnosticInformationMap.generated.ts`](./src/compiler/diagnosticInformationMap.generated.ts). + +See [coding guidelines on diagnostic messages](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#diagnostic-messages). diff --git a/Gulpfile.ts b/Gulpfile.ts index 4d90d5cf24a..996fa1fc140 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -150,7 +150,8 @@ const es2018LibrarySourceMap = es2018LibrarySource.map(source => ({ target: "lib." + source, sources: ["header.d.ts", source] })); const esnextLibrarySource = [ - "esnext.asynciterable.d.ts" + "esnext.asynciterable.d.ts", + "esnext.promise.d.ts" ]; const esnextLibrarySourceMap = esnextLibrarySource.map(source => diff --git a/Jakefile.js b/Jakefile.js index 1544ccec0a3..a7533fea1b5 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -130,6 +130,7 @@ var harnessSources = harnessCoreSources.concat([ "textStorage.ts", "moduleResolution.ts", "tsconfigParsing.ts", + "asserts.ts", "builder.ts", "commandLineParsing.ts", "configurationExtension.ts", @@ -212,7 +213,8 @@ var es2018LibrarySourceMap = es2018LibrarySource.map(function (source) { }); var esnextLibrarySource = [ - "esnext.asynciterable.d.ts" + "esnext.asynciterable.d.ts", + "esnext.promise.d.ts" ]; var esnextLibrarySourceMap = esnextLibrarySource.map(function (source) { @@ -795,7 +797,7 @@ compileFile( /*prereqs*/[builtLocalDirectory, tscFile, tsserverLibraryFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources), /*prefixes*/[], /*useBuiltCompiler:*/ true, - /*opts*/ { types: ["node", "mocha"], lib: "es6" }); + /*opts*/ { types: ["node", "mocha", "chai"], lib: "es6" }); var internalTests = "internal/"; @@ -1195,22 +1197,12 @@ task("update-sublime", ["local", serverFile], function () { }); var tslintRuleDir = "scripts/tslint/rules"; -var tslintRules = [ - "booleanTriviaRule", - "debugAssertRule", - "nextLineRule", - "noBomRule", - "noIncrementDecrementRule", - "noInOperatorRule", - "noTypeAssertionWhitespaceRule", - "objectLiteralSurroundingSpaceRule", - "typeOperatorSpacingRule", -]; +var tslintRules = fs.readdirSync(tslintRuleDir); var tslintRulesFiles = tslintRules.map(function (p) { - return path.join(tslintRuleDir, p + ".ts"); + return path.join(tslintRuleDir, p); }); var tslintRulesOutFiles = tslintRules.map(function (p) { - return path.join(builtLocalDirectory, "tslint/rules", p + ".js"); + return path.join(builtLocalDirectory, "tslint/rules", p.replace(".ts", ".js")); }); var tslintFormattersDir = "scripts/tslint/formatters"; var tslintFormatters = [ diff --git a/package-lock.json b/package-lock.json index 5dc35c5bb26..4a071afa0f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,19 +42,13 @@ "dev": true, "requires": { "@types/insert-module-globals": "7.0.0", - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/chai": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.7.tgz", - "integrity": "sha512-OsFOcyJH0SViMMHzDKZdapG8sfoH7qcpYgKhgyw9xn5MgkCvAplkf0FUaRFB+HTrVH6gQ/GU7sbIUDM1usVEUA==", - "dev": true - }, - "@types/colors": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/colors/-/colors-1.1.3.tgz", - "integrity": "sha1-VBOwp6GxbdGL4OP9V9L+7Mgcx3Y=", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.10.tgz", + "integrity": "sha512-Ejh1AXTY8lm+x91X/yar3G2z4x9RyKwdTVdyyu7Xj3dNB35fMNCnEWqTO9FgS3zjzlRNqk1MruYhgb8yhRN9rA==", "dev": true }, "@types/convert-source-map": { @@ -69,7 +63,7 @@ "integrity": "sha512-18mSs54BvzV8+TTQxt0ancig6tsuPZySnhp3cQkWFFDmDMavU4pmWwR+bHHqRBWODYqpzIzVkqKLuk/fP6yypQ==", "dev": true, "requires": { - "@types/glob": "5.0.33" + "@types/glob": "5.0.34" } }, "@types/events": { @@ -79,13 +73,14 @@ "dev": true }, "@types/glob": { - "version": "5.0.33", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.33.tgz", - "integrity": "sha512-BcD4yyWz+qmCggaYMSFF0Xn7GkO6tgwm3Fh9Gxk/kQmEU3Z7flQTnVlMyKBUNvXXNTCCyjqK4XT4/2hLd1gQ2A==", + "version": "5.0.34", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.34.tgz", + "integrity": "sha512-sUvpieq+HsWTLdkeOI8Mi8u22Ag3AoGuM3sv+XMP1bKtbaIAHpEA2f52K2mz6vK5PVhTa3bFyRZLZMqTxOo2Cw==", "dev": true, "requires": { - "@types/minimatch": "3.0.1", - "@types/node": "8.0.54" + "@types/events": "1.1.0", + "@types/minimatch": "3.0.2", + "@types/node": "8.5.2" } }, "@types/gulp": { @@ -94,7 +89,7 @@ "integrity": "sha512-h9clNJu8X6+zW74ZLa5zhh5HP0LxnvlelVXdXby6pM/DDEj/gKqmmFXKwjzvupZKlMpof02jr6c3JokPbHXQgg==", "dev": true, "requires": { - "@types/node": "8.0.54", + "@types/node": "8.5.2", "@types/orchestrator": "0.3.2", "@types/vinyl": "2.0.1" } @@ -105,7 +100,7 @@ "integrity": "sha512-CUCFADlITzzBfBa2bdGzhKtvBr4eFh+evb+4igVbvPoO5RyPfHifmyQlZl6lM7q19+OKncRlFXDU7B4X9Ayo2g==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/gulp-help": { @@ -115,7 +110,7 @@ "dev": true, "requires": { "@types/gulp": "3.8.35", - "@types/node": "8.0.54", + "@types/node": "8.5.2", "@types/orchestrator": "0.3.2" } }, @@ -125,7 +120,7 @@ "integrity": "sha512-e7J/Zv5Wd7CC0WpuA2syWVitgwrkG0u221e41w7r07XUR6hMH6kHPkq9tUrusHkbeW8QbuLbis5fODOwQCyggQ==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/gulp-sourcemaps": { @@ -134,7 +129,7 @@ "integrity": "sha512-+7BAmptW2bxyJnJcCEuie7vLoop3FwWgCdBMzyv7MYXED/HeNMeQuX7uPCkp4vfU1TTu4CYFH0IckNPvo0VePA==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/insert-module-globals": { @@ -143,7 +138,7 @@ "integrity": "sha512-zudCJPwluh1VUDB6Gl/OQdRp+fYy3+47huJB/JMQubMS2p+sH18MCVK4WUz3FqaWLB12yh5ELxVR/+tqwlm/qA==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/merge2": { @@ -152,13 +147,13 @@ "integrity": "sha512-GjaXY4OultxbaOOk7lCLO7xvEcFpdjExC605YmfI6X29vhHKpJfMWKCDZd3x+BITrZaXKg97DgV/SdGVSwdzxA==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/minimatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", - "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.2.tgz", + "integrity": "sha512-tctoxbfuMCxeI2CAsnwoZQfaBA+T7gPzDzDuiiFnyCSSyGYEB92cmRTh6E3tdR1hWsprbJ9IdbvX3PzLmJU/GA==", "dev": true }, "@types/minimist": { @@ -173,7 +168,7 @@ "integrity": "sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/mocha": { @@ -183,13 +178,10 @@ "dev": true }, "@types/node": { - "version": "8.0.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.54.tgz", - "integrity": "sha512-qetMdTv3Ytz9u9ESLdcYs45LPI0mczYZIbC184n7kY0jczOqPNQsabBfVCh+na3B2shAfvC459JqHV771A8Rxg==", - "dev": true, - "requires": { - "@types/events": "1.1.0" - } + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz", + "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==", + "dev": true }, "@types/orchestrator": { "version": "0.3.2", @@ -197,7 +189,7 @@ "integrity": "sha512-cKB4yTX0wGaRCSkdHDX2fkGQbMAA8UOshC2U7DQky1CE5o+5q2iQQ8VkbPbE/88uaTtsusvBPMcCX7dgmjxBhQ==", "dev": true, "requires": { - "@types/node": "8.0.54", + "@types/node": "8.5.2", "@types/q": "1.0.6" } }, @@ -214,16 +206,28 @@ "dev": true, "requires": { "@types/gulp": "3.8.35", - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "@types/through2": { "version": "2.0.33", "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.33.tgz", "integrity": "sha1-H/LoihAN+1sUDnu5h5HxGUQA0TE=", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/vinyl": { @@ -232,7 +236,7 @@ "integrity": "sha512-Joudabfn2ZofU2usW04y8OLmN75u7ZQkW0MCT3AnoBf5oUBp5iQ3Pgfz9+y1RdWkzhCPZo9/wBJ7FMWW2JrY0g==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "@types/xml2js": { @@ -241,13 +245,13 @@ "integrity": "sha512-8aKUBSj3oGcnuiBmDLm3BIk09RYg01mz9HlQ2u4aS17oJ25DxjQrEUVGFSBVNOfM45pQW4OjcBPplq6r/exJdA==", "dev": true, "requires": { - "@types/node": "8.0.54" + "@types/node": "8.5.2" } }, "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", + "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", "dev": true, "requires": { "jsonparse": "1.3.1", @@ -275,6 +279,17 @@ "kind-of": "3.2.2", "longest": "1.0.1", "repeat-string": "1.6.1" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, "amdefine": { @@ -283,6 +298,15 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -290,9 +314,18 @@ "dev": true }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", "dev": true }, "archy": { @@ -311,13 +344,10 @@ } }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", @@ -325,6 +355,12 @@ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, "array-differ": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", @@ -383,9 +419,9 @@ "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "arrify": { @@ -420,6 +456,12 @@ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", "dev": true }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, "astw": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", @@ -436,9 +478,9 @@ "dev": true }, "atob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz", - "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", "dev": true }, "babel-code-frame": { @@ -450,6 +492,33 @@ "chalk": "1.1.3", "esutils": "2.0.2", "js-tokens": "3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "balanced-match": { @@ -458,6 +527,21 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.5", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.0", + "pascalcase": "0.1.1" + } + }, "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", @@ -487,14 +571,22 @@ } }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", + "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.1" } }, "brorand": { @@ -509,7 +601,7 @@ "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", "dev": true, "requires": { - "JSONStream": "1.3.1", + "JSONStream": "1.3.2", "combine-source-map": "0.7.2", "defined": "1.0.0", "through2": "2.0.3", @@ -537,7 +629,7 @@ "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", "dev": true, "requires": { - "JSONStream": "1.3.1", + "JSONStream": "1.3.2", "assert": "1.4.1", "browser-pack": "6.0.2", "browser-resolve": "1.11.2", @@ -690,6 +782,23 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + } + }, "cached-path-relative": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", @@ -721,6 +830,15 @@ "requires": { "align-text": "0.1.4", "lazy-cache": "1.0.4" + }, + "dependencies": { + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + } } }, "chai": { @@ -738,16 +856,14 @@ } }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "2.2.1", + "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "supports-color": "4.5.0" } }, "check-error": { @@ -766,6 +882,47 @@ "safe-buffer": "5.1.1" } }, + "class-utils": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.5.tgz", + "integrity": "sha1-F+eTEDdQ+WJ7IXbqNM/RtWWQPIA=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "lazy-cache": "2.0.2", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -816,6 +973,16 @@ "through2": "2.0.3" } }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", @@ -831,10 +998,10 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, "combine-source-map": { @@ -863,6 +1030,12 @@ "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -932,6 +1105,12 @@ "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", "dev": true }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1005,6 +1184,12 @@ "urix": "0.1.0" }, "dependencies": { + "atob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz", + "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=", + "dev": true + }, "source-map": { "version": "0.1.43", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", @@ -1013,6 +1198,24 @@ "requires": { "amdefine": "1.0.1" } + }, + "source-map-resolve": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", + "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=", + "dev": true, + "requires": { + "atob": "1.1.3", + "resolve-url": "0.2.1", + "source-map-url": "0.3.0", + "urix": "0.1.0" + } + }, + "source-map-url": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", + "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=", + "dev": true } } }, @@ -1047,23 +1250,34 @@ "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "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" } }, "debug-fabulous": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-0.2.1.tgz", - "integrity": "sha512-u0TV6HcfLsZ03xLBhdhSViQMldaiQ2o+8/nSILaXkuNSWvxkx66vYJUAam0Eu7gAilJRX/69J4kKdqajQPaPyw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.0.0.tgz", + "integrity": "sha512-dsd50qQ1atDeurcxL7XOjPp4nZCGZzWIONDujDXzl1atSyC3hMbZD+v6440etw+Vt0Pr8ce4TQzHfX3KZM05Mw==", "dev": true, "requires": { "debug": "3.1.0", "memoizee": "0.4.11", "object-assign": "4.1.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "decamelize": { @@ -1072,6 +1286,12 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -1096,6 +1316,15 @@ "clone": "1.0.3" } }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.1" + } + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -1128,7 +1357,7 @@ "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", "dev": true, "requires": { - "JSONStream": "1.3.1", + "JSONStream": "1.3.2", "shasum": "1.0.2", "subarg": "1.0.0", "through2": "2.0.3" @@ -1145,13 +1374,10 @@ } }, "detect-file": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", - "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", - "dev": true, - "requires": { - "fs-exists-sync": "0.1.0" - } + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true }, "detect-newline": { "version": "2.1.0", @@ -1160,9 +1386,9 @@ "dev": true }, "detective": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.0.tgz", - "integrity": "sha512-4mBqSEdMfBpRAo/DQZnTcAXenpiSIJmVKbCMSotS+SFWWcrP/CKM6iBRPdTiEO+wZhlfEsoZlGqpG6ycl5vTqw==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", "dev": true, "requires": { "acorn": "5.2.1", @@ -1401,12 +1627,46 @@ } }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, "expand-range": { @@ -1416,15 +1676,57 @@ "dev": true, "requires": { "fill-range": "2.2.3" + }, + "dependencies": { + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "homedir-polyfill": "1.0.1" } }, "extend": { @@ -1443,21 +1745,29 @@ } }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.2.tgz", + "integrity": "sha512-I0+eZBH+jFGL8F5BnIz2ON2nKCjTS3AS3H/5PeSmCp7UVC70Ym8IhdRiQly2juKYQ//f7z1aj1BRpQniFJoU1w==", "dev": true, "requires": { - "is-extglob": "1.0.0" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" } }, "fancy-log": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", - "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", + "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", "dev": true, "requires": { - "chalk": "1.1.3", + "ansi-gray": "0.1.1", + "color-support": "1.1.3", "time-stamp": "1.1.0" } }, @@ -1492,16 +1802,15 @@ "dev": true }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" } }, "find-index": { @@ -1521,15 +1830,15 @@ } }, "findup-sync": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", - "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, "requires": { - "detect-file": "0.1.0", - "is-glob": "2.0.1", - "micromatch": "2.3.11", - "resolve-dir": "0.1.1" + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.4", + "resolve-dir": "1.0.1" } }, "fined": { @@ -1542,18 +1851,7 @@ "is-plain-object": "2.0.4", "object.defaults": "1.1.0", "object.pick": "1.3.0", - "parse-filepath": "1.0.1" - }, - "dependencies": { - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "1.0.1" - } - } + "parse-filepath": "1.0.2" } }, "first-chunk-stream": { @@ -1563,9 +1861,9 @@ "dev": true }, "flagged-respawn": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", - "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", + "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=", "dev": true }, "for-in": { @@ -1575,19 +1873,22 @@ "dev": true }, "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { "for-in": "1.0.2" } }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", - "dev": true + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } }, "fs.realpath": { "version": "1.0.0", @@ -1622,6 +1923,12 @@ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -1644,15 +1951,42 @@ "requires": { "glob-parent": "2.0.0", "is-glob": "2.0.1" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } } }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" } }, "glob-stream": { @@ -1745,24 +2079,26 @@ } }, "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "0.1.5", - "is-windows": "0.2.0" + "global-prefix": "1.0.2", + "is-windows": "1.0.1", + "resolve-dir": "1.0.1" } }, "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { + "expand-tilde": "2.0.2", "homedir-polyfill": "1.0.1", "ini": "1.3.5", - "is-windows": "0.2.0", + "is-windows": "1.0.1", "which": "1.3.0" } }, @@ -1848,7 +2184,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { - "natives": "1.1.0" + "natives": "1.1.1" } }, "growl": { @@ -1868,7 +2204,7 @@ "deprecated": "0.0.1", "gulp-util": "3.0.8", "interpret": "1.1.0", - "liftoff": "2.3.0", + "liftoff": "2.5.0", "minimist": "1.2.0", "orchestrator": "0.3.8", "pretty-hrtime": "1.0.3", @@ -1876,6 +2212,33 @@ "tildify": "1.2.0", "v8flags": "2.1.1", "vinyl-fs": "0.3.14" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "gulp-clone": { @@ -2117,11 +2480,36 @@ "object-assign": "3.0.0" }, "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "object-assign": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, @@ -2183,7 +2571,7 @@ "acorn": "4.0.13", "convert-source-map": "1.5.1", "css": "2.2.1", - "debug-fabulous": "0.2.1", + "debug-fabulous": "1.0.0", "detect-newline": "2.1.0", "graceful-fs": "4.1.11", "source-map": "0.5.7", @@ -2223,6 +2611,50 @@ "vinyl-fs": "2.4.4" }, "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", @@ -2236,16 +2668,6 @@ "path-is-absolute": "1.0.1" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - } - }, "glob-stream": { "version": "5.3.5", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", @@ -2306,18 +2728,18 @@ } }, "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "1.0.0" } }, "isarray": { @@ -2335,6 +2757,36 @@ "jsonify": "0.0.0" } }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", @@ -2419,7 +2871,7 @@ "beeper": "1.1.1", "chalk": "1.1.3", "dateformat": "2.2.0", - "fancy-log": "1.3.0", + "fancy-log": "1.3.2", "gulplog": "1.0.0", "has-gulplog": "0.1.0", "lodash._reescape": "3.0.0", @@ -2434,12 +2886,37 @@ "vinyl": "0.5.3" }, "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "object-assign": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", "dev": true }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "vinyl": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", @@ -2510,9 +2987,9 @@ "dev": true }, "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, "has-gulplog": { @@ -2524,6 +3001,38 @@ "sparkles": "1.0.0" } }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, "hash-base": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", @@ -2645,7 +3154,7 @@ "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", "dev": true, "requires": { - "JSONStream": "1.3.1", + "JSONStream": "1.3.2", "combine-source-map": "0.7.2", "concat-stream": "1.5.2", "is-buffer": "1.1.6", @@ -2662,13 +3171,33 @@ "dev": true }, "is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", - "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { - "is-relative": "0.2.1", - "is-windows": "0.2.0" + "is-relative": "1.0.0", + "is-windows": "1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, "is-arrayish": { @@ -2692,6 +3221,45 @@ "builtin-modules": "1.1.1" } }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.1.tgz", + "integrity": "sha512-G3fFVFTqfaqu7r4YuSBHKBAuOaLz8Sy7ekklUpFEliaLMP1Y2ZjoN9jS62YWCAPQrQpMUQSitRlrzibbuCZjdA==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -2714,9 +3282,9 @@ "dev": true }, "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-finite": { @@ -2729,21 +3297,41 @@ } }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "2.1.1" } }, "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-odd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", + "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", + "dev": true, + "requires": { + "is-number": "3.0.0" } }, "is-path-cwd": { @@ -2777,14 +3365,6 @@ "dev": true, "requires": { "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "is-posix-bracket": { @@ -2806,12 +3386,12 @@ "dev": true }, "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "0.1.2" + "is-unc-path": "1.0.0" } }, "is-stream": { @@ -2821,9 +3401,9 @@ "dev": true }, "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { "unc-path-regex": "0.1.2" @@ -2842,9 +3422,9 @@ "dev": true }, "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.1.tgz", + "integrity": "sha1-MQ23D3QtJZoWo2kgK1GvhCMzENk=", "dev": true }, "isarray": { @@ -2860,13 +3440,10 @@ "dev": true }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "istanbul": { "version": "0.4.5", @@ -2903,6 +3480,12 @@ "path-is-absolute": "1.0.1" } }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", @@ -3010,13 +3593,10 @@ "dev": true }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true }, "labeled-stream-splicer": { "version": "2.0.0", @@ -3038,11 +3618,13 @@ } }, "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", "dev": true, - "optional": true + "requires": { + "set-getter": "0.1.0" + } }, "lazystream": { "version": "1.0.0", @@ -3073,18 +3655,17 @@ } }, "liftoff": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", - "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", "dev": true, "requires": { "extend": "3.0.1", - "findup-sync": "0.4.3", + "findup-sync": "2.0.0", "fined": "1.1.0", - "flagged-respawn": "0.3.2", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.mapvalues": "4.6.0", + "flagged-respawn": "1.0.0", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", "rechoir": "0.6.2", "resolve": "1.1.7" } @@ -3309,18 +3890,6 @@ "lodash._objecttypes": "2.4.1" } }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, "lodash.keys": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", @@ -3332,12 +3901,6 @@ "lodash.isarray": "3.0.4" } }, - "lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", - "dev": true - }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", @@ -3436,6 +3999,26 @@ "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", "dev": true }, + "make-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.0.tgz", + "integrity": "sha1-V7713IXSOSO6I3ZzJNjo+PPZaUs=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -3448,6 +4031,15 @@ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, "md5.js": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", @@ -3520,24 +4112,24 @@ "dev": true }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.4.tgz", + "integrity": "sha512-kFRtviKYoAJT+t7HggMl0tBFGNAKLw/S7N+CO9qfEQyisob1Oy4pao+geRbkyeEd+V9aOkvZ4mhuyPvI/q9Sfg==", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.0", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "extglob": "2.0.2", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.6", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" } }, "miller-rabin": { @@ -3577,6 +4169,27 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, + "mixin-deep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", + "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -3612,11 +4225,14 @@ "supports-color": "4.4.0" }, "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } }, "supports-color": { "version": "4.4.0", @@ -3641,12 +4257,12 @@ "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", "dev": true, "requires": { - "JSONStream": "1.3.1", + "JSONStream": "1.3.2", "browser-resolve": "1.11.2", "cached-path-relative": "1.0.1", "concat-stream": "1.5.2", "defined": "1.0.0", - "detective": "4.7.0", + "detective": "4.7.1", "duplexer2": "0.1.4", "inherits": "2.0.3", "parents": "1.0.1", @@ -3708,10 +4324,37 @@ } } }, + "nanomatch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.6.tgz", + "integrity": "sha512-WJ6XTCbvWXUFPbi/bDwKcYkCeOGUHzaJj72KbuPqGn78Ba/F5Vu26Zlo6SuMQbCIst1RGKL1zfWBCOGAlbRLAg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "is-odd": "1.0.0", + "kind-of": "5.1.0", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "natives": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.0.tgz", - "integrity": "sha1-6f+EFBimsux6SV6TmYT3jxY+bjE=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz", + "integrity": "sha512-8eRaxn8u/4wN8tGkhlc2cgwwvOLMLUMUn4IYTexMgWd+LyUDfeXVkk2ygQR0hvIHbJQXgHujia3ieUUDwNGkEA==", "dev": true }, "next-tick": { @@ -3762,12 +4405,71 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, "object-keys": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -3778,23 +4480,16 @@ "array-slice": "1.1.0", "for-own": "1.0.0", "isobject": "3.0.1" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "1.0.0", + "make-iterator": "1.0.0" } }, "object.omit": { @@ -3805,6 +4500,17 @@ "requires": { "for-own": "0.1.5", "is-extendable": "0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + } } }, "object.pick": { @@ -3814,14 +4520,6 @@ "dev": true, "requires": { "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "once": { @@ -3935,12 +4633,12 @@ } }, "parse-filepath": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", - "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "0.2.6", + "is-absolute": "1.0.0", "map-cache": "0.2.2", "path-root": "0.1.1" } @@ -3955,6 +4653,23 @@ "is-dotfile": "1.0.3", "is-extglob": "1.0.0", "is-glob": "2.0.1" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } } }, "parse-json": { @@ -3972,6 +4687,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", @@ -4097,6 +4818,12 @@ "pinkie": "2.0.4" } }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -4174,26 +4901,6 @@ "kind-of": "4.0.0" }, "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -4297,6 +5004,15 @@ "is-equal-shallow": "0.1.3" } }, + "regex-not": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", + "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -4337,13 +5053,13 @@ "dev": true }, "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "1.2.2", - "global-modules": "0.2.3" + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" } }, "resolve-url": { @@ -4389,6 +5105,33 @@ "requires": { "chalk": "1.1.3", "gulp-util": "3.0.8" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "safe-buffer": { @@ -4435,6 +5178,27 @@ "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", "dev": true }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dev": true, + "requires": { + "to-object-path": "0.3.0" + } + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + } + }, "sha.js": { "version": "2.4.9", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", @@ -4479,6 +5243,81 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "snapdragon": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", + "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "2.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, "sorcery": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", @@ -4498,14 +5337,15 @@ "dev": true }, "source-map-resolve": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", - "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", "dev": true, "requires": { - "atob": "1.1.3", + "atob": "2.0.3", + "decode-uri-component": "0.2.0", "resolve-url": "0.2.1", - "source-map-url": "0.3.0", + "source-map-url": "0.4.0", "urix": "0.1.0" } }, @@ -4527,9 +5367,9 @@ } }, "source-map-url": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", - "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, "sourcemap-codec": { @@ -4568,12 +5408,80 @@ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", "dev": true }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "stream-browserify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", @@ -4744,10 +5652,13 @@ } }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } }, "syntax-error": { "version": "1.3.0", @@ -4833,6 +5744,75 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "to-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", + "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "regex-not": "1.0.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + } + }, "travis-fold": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/travis-fold/-/travis-fold-0.1.2.tgz", @@ -4846,9 +5826,9 @@ "dev": true }, "ts-node": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz", - "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-4.0.2.tgz", + "integrity": "sha512-mg7l6ON8asjnfzkTi1LFWKaOGHl5Jf1+5ij0MQ502YfC6+4FBgh/idJgw9aN9kei1Rf4/pmFpNuFE1YbcQdOTA==", "dev": true, "requires": { "arrify": "1.0.1", @@ -4857,56 +5837,12 @@ "make-error": "1.3.0", "minimist": "1.2.0", "mkdirp": "0.5.1", - "source-map-support": "0.4.18", - "tsconfig": "6.0.0", + "source-map-support": "0.5.0", + "tsconfig": "7.0.0", "v8flags": "3.0.1", "yn": "2.0.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.1" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, "v8flags": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", @@ -4919,11 +5855,13 @@ } }, "tsconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", - "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", "dev": true, "requires": { + "@types/strip-bom": "3.0.0", + "@types/strip-json-comments": "0.0.30", "strip-bom": "3.0.0", "strip-json-comments": "2.0.1" }, @@ -4937,9 +5875,9 @@ } }, "tslib": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", - "integrity": "sha512-ymKWWZJST0/CkgduC2qkzjMOWr4bouhuURNXCn/inEX0L57BnRG6FhX76o7FOnsjHazCjfU2LKeSrlS2sIKQJg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.1.tgz", + "integrity": "sha1-aUavLR1lGnsYY7Ux1uWvpBqkTqw=", "dev": true }, "tslint": { @@ -4957,36 +5895,10 @@ "minimatch": "3.0.4", "resolve": "1.5.0", "semver": "5.4.1", - "tslib": "1.8.0", - "tsutils": "2.13.0" + "tslib": "1.8.1", + "tsutils": "2.13.1" }, "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.1" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -5001,25 +5913,16 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } } } }, "tsutils": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.13.0.tgz", - "integrity": "sha512-FuWzNJbMsp3gcZMbI3b5DomhW4Ia41vMxjN63nKWI0t7f+I3UmHfRl0TrXJTwI2LUduDG+eR1Mksp3pvtlyCFQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.13.1.tgz", + "integrity": "sha512-XMOEvc2TiYesVSOJMI7OYPnBMSgcvERuGW5Li/J+2A0TuH607BPQnOLQ82oSPZCssB8c9+QGi6qhTBa/f1xQRA==", "dev": true, "requires": { - "tslib": "1.8.0" + "tslib": "1.8.1" } }, "tty-browserify": { @@ -5050,9 +5953,9 @@ "dev": true }, "typescript": { - "version": "2.7.0-dev.20171203", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.0-dev.20171203.tgz", - "integrity": "sha512-FyhV7OvieIXzjktOb9YmixEIR8olL8IrnonCmJQWGnj8Wt6eoQQKQlkXWPy8mpwEaSIXw/nQO0NpGQ+nWokhRw==", + "version": "2.7.0-dev.20171221", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.0-dev.20171221.tgz", + "integrity": "sha512-PM09fTK1T0WSZHNDXqrB/P6lTaQkzAnXlx23Q5J/21oQ8kgU7B7TtUFTX7zzdSZnH78j49aUx5bVuQCRJkBowg==", "dev": true }, "uglify-js": { @@ -5086,12 +5989,78 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } + }, "unique-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", "dev": true }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -5116,6 +6085,45 @@ } } }, + "use": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", + "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "isobject": "3.0.1", + "lazy-cache": "2.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "user-home": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", diff --git a/package.json b/package.json index b3c76e6d515..fb089b64918 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@types/browserify": "latest", - "@types/colors": "latest", + "@types/chai": "latest", "@types/convert-source-map": "latest", "@types/del": "latest", "@types/glob": "latest", @@ -44,7 +44,7 @@ "@types/minimist": "latest", "@types/mkdirp": "latest", "@types/mocha": "latest", - "@types/node": "latest", + "@types/node": "8.5.5", "@types/q": "latest", "@types/run-sequence": "latest", "@types/through2": "latest", @@ -52,6 +52,7 @@ "xml2js": "^0.4.19", "browser-resolve": "^1.11.2", "browserify": "latest", + "chai": "latest", "convert-source-map": "latest", "del": "latest", "gulp": "3.X", @@ -78,7 +79,7 @@ "ts-node": "latest", "tslint": "latest", "vinyl": "latest", - "colors": "latest", + "chalk": "latest", "typescript": "next" }, "scripts": { diff --git a/scripts/tslint/formatters/autolinkableStylishFormatter.ts b/scripts/tslint/formatters/autolinkableStylishFormatter.ts index 6a02ec24f05..23e01bc8587 100644 --- a/scripts/tslint/formatters/autolinkableStylishFormatter.ts +++ b/scripts/tslint/formatters/autolinkableStylishFormatter.ts @@ -1,5 +1,5 @@ import * as Lint from "tslint"; -import * as colors from "colors"; +import chalk from "chalk"; import { sep } from "path"; function groupBy(array: ReadonlyArray | undefined, getGroupId: (elem: T, index: number) => number | string): T[][] { if (!array) { @@ -52,7 +52,7 @@ function getLink(failure: Lint.RuleFailure, color: boolean): string { if (path.indexOf("/") === -1 && path.indexOf("\\") === -1) { path = `.${sep}${path}`; } - return `${color ? (sev === "WARNING" ? colors.blue(sev) : colors.red(sev)) : sev}: ${path}:${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`; + return `${color ? (sev === "WARNING" ? chalk.blue(sev) : chalk.red(sev)) : sev}: ${path}:${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`; } function getLinkMaxSize(failures: Lint.RuleFailure[]): number { @@ -91,7 +91,7 @@ export class Formatter extends Lint.Formatters.AbstractFormatter { const nameMaxSize = getNameMaxSize(group); return ` ${currentFile} -${group.map(f => `${pad(getLink(f, /*color*/ true), getLink(f, /*color*/ false).length, linkMaxSize)} ${colors.grey(pad(f.getRuleName(), f.getRuleName().length, nameMaxSize))} ${colors.yellow(f.getFailure())}`).join("\n")}`; +${group.map(f => `${pad(getLink(f, /*color*/ true), getLink(f, /*color*/ false).length, linkMaxSize)} ${chalk.grey(pad(f.getRuleName(), f.getRuleName().length, nameMaxSize))} ${chalk.yellow(f.getFailure())}`).join("\n")}`; }).join("\n"); } } \ No newline at end of file diff --git a/scripts/tslint/rules/noDoubleSpaceRule.ts b/scripts/tslint/rules/noDoubleSpaceRule.ts new file mode 100644 index 00000000000..34ab470ebce --- /dev/null +++ b/scripts/tslint/rules/noDoubleSpaceRule.ts @@ -0,0 +1,54 @@ +import * as Lint from "tslint/lib"; +import * as ts from "typescript"; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + const lines = sourceFile.text.split("\n"); + const strings = getLiterals(sourceFile); + lines.forEach((line, idx) => { + // Skip indentation. + const firstNonSpace = /\S/.exec(line); + if (firstNonSpace === null) { + return; + } + // Allow common uses of double spaces + // * To align `=` or `!=` signs + // * To align comments at the end of lines + // * To indent inside a comment + // * To use two spaces after a period + // * To include aligned `->` in a comment + const rgx = /[^/*. ] [^-!/= ]/g; + rgx.lastIndex = firstNonSpace.index; + const doubleSpace = rgx.exec(line); + // Also allow to align comments after `@param` + if (doubleSpace !== null && !line.includes("@param")) { + const pos = lines.slice(0, idx).reduce((len, line) => len + 1 + line.length, 0) + doubleSpace.index; + if (!strings.some(s => s.getStart() <= pos && s.end > pos)) { + ctx.addFailureAt(pos + 1, 2, "Use only one space."); + } + } + }); +} + +function getLiterals(sourceFile: ts.SourceFile): ReadonlyArray { + const out: ts.Node[] = []; + sourceFile.forEachChild(function cb(node) { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.TemplateHead: + case ts.SyntaxKind.TemplateMiddle: + case ts.SyntaxKind.TemplateTail: + case ts.SyntaxKind.NoSubstitutionTemplateLiteral: + case ts.SyntaxKind.RegularExpressionLiteral: + out.push(node); + } + node.forEachChild(cb); + }); + return out; +} diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4eab97f28bb..4fdfacdbf50 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -303,7 +303,7 @@ namespace ts { // without names can only come from JSDocFunctionTypes. Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType); const functionType = node.parent; - const index = indexOf(functionType.parameters, node); + const index = functionType.parameters.indexOf(node as ParameterDeclaration); return "arg" + index as __String; case SyntaxKind.JSDocTypedefTag: const name = getNameOfJSDocTypedef(node as JSDocTypedefTag); @@ -1633,7 +1633,7 @@ namespace ts { // to the one we would get for: { <...>(...): T } // // We do that by making an anonymous type literal symbol, and then setting the function - // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable + // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable // from an actual type literal symbol you would have gotten had you used the long form. const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)); addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); @@ -2431,7 +2431,7 @@ namespace ts { if (!isPrototypeProperty && (!targetSymbol || !(targetSymbol.flags & SymbolFlags.Namespace)) && isLegalPosition) { Debug.assert(isIdentifier(propertyAccess.expression)); const identifier = propertyAccess.expression as Identifier; - const flags = SymbolFlags.Module | SymbolFlags.JSContainer; + const flags = SymbolFlags.Module | SymbolFlags.JSContainer; const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.JSContainer; if (targetSymbol) { addDeclarationToSymbol(symbol, identifier, flags); @@ -2538,7 +2538,7 @@ namespace ts { } if (isBindingPattern(node.name)) { - bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + indexOf(node.parent.parameters, node) as __String); + bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + node.parent.parameters.indexOf(node) as __String); } else { declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04832af0647..92e8fdeb3e8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -171,6 +171,7 @@ namespace ts { node = getParseTreeNode(node, isExpression); return node ? getContextualType(node) : undefined; }, + isContextSensitive, getFullyQualifiedName, getResolvedSignature: (node, candidatesOutArray, theArgumentCount) => { node = getParseTreeNode(node, isCallLikeExpression); @@ -185,7 +186,11 @@ namespace ts { }, isValidPropertyAccess: (node, propertyName) => { node = getParseTreeNode(node, isPropertyAccessOrQualifiedName); - return node ? isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName)) : false; + return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName)); + }, + isValidPropertyAccessForCompletions: (node, type, property) => { + node = getParseTreeNode(node, isPropertyAccessExpression); + return !!node && isValidPropertyAccessForCompletions(node, type, property); }, getSignatureFromDeclaration: declaration => { declaration = getParseTreeNode(declaration, isFunctionLike); @@ -334,6 +339,7 @@ namespace ts { const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false); const globals = createSymbolTable(); + const reverseMappedCache = createMap(); let ambientModulesCache: Symbol[] | undefined; /** * List of every ambient module with a "*" wildcard. @@ -529,6 +535,7 @@ namespace ts { Normal = 0, // Normal type checking SkipContextSensitive = 1, // Skip context sensitive function expressions Inferential = 2, // Inferential typing + Contextual = 3, // Normal type checking informed by a contextual type, therefore not cacheable } const enum CallbackCheck { @@ -840,7 +847,7 @@ namespace ts { return true; } const sourceFiles = host.getSourceFiles(); - return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); + return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile); } if (declaration.pos <= usage.pos) { @@ -1213,7 +1220,7 @@ namespace ts { !checkAndReportErrorForExtendingInterface(errorLocation) && !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && - !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) { + !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) { let suggestion: string | undefined; if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) { suggestion = getSuggestionForNonexistentSymbol(originalLocation, name, meaning); @@ -2218,7 +2225,8 @@ namespace ts { // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain // but only if the symbolFromSymbolTable can be qualified - const accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined; + const candidateTable = getExportsOfSymbol(resolvedImportedSymbol); + const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable); if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) { return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); } @@ -2855,7 +2863,10 @@ namespace ts { typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context)); } if (resolvedType.stringIndexInfo) { - typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context)); + const indexInfo = resolvedType.objectFlags & ObjectFlags.ReverseMapped ? + createIndexInfo(anyType, resolvedType.stringIndexInfo.isReadonly, resolvedType.stringIndexInfo.declaration) : + resolvedType.stringIndexInfo; + typeElements.push(indexInfoToIndexSignatureDeclarationHelper(indexInfo, IndexKind.String, context)); } if (resolvedType.numberIndexInfo) { typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context)); @@ -2867,7 +2878,7 @@ namespace ts { } for (const propertySymbol of properties) { - const propertyType = getTypeOfSymbol(propertySymbol); + const propertyType = getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true); @@ -3676,7 +3687,10 @@ namespace ts { writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } - buildIndexSignatureDisplay(resolved.stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack); + const stringIndexInfo = resolved.objectFlags & ObjectFlags.ReverseMapped && resolved.stringIndexInfo ? + createIndexInfo(anyType, resolved.stringIndexInfo.isReadonly, resolved.stringIndexInfo.declaration) : + resolved.stringIndexInfo; + buildIndexSignatureDisplay(stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack); buildIndexSignatureDisplay(resolved.numberIndexInfo, writer, IndexKind.Number, enclosingDeclaration, globalFlags, symbolStack); for (const p of resolved.properties) { if (globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) { @@ -3687,7 +3701,7 @@ namespace ts { writer.reportPrivateInBaseOfClassExpression(symbolName(p)); } } - const t = getTypeOfSymbol(p); + const t = getCheckFlags(p) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(p); if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) { const signatures = getSignaturesOfType(t, SignatureKind.Call); for (const signature of signatures) { @@ -3953,7 +3967,12 @@ namespace ts { writePunctuation(writer, SyntaxKind.CloseBracketToken); writePunctuation(writer, SyntaxKind.ColonToken); writeSpace(writer); - buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack); + if (info.type) { + buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack); + } + else { + writeKeyword(writer, SyntaxKind.AnyKeyword); + } writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } @@ -4212,7 +4231,7 @@ namespace ts { // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been // assigned by contextual typing. - function getTypeForBindingElementParent(node: VariableLikeDeclaration) { + function getTypeForBindingElementParent(node: BindingElementGrandparent) { const symbol = getSymbolOfNode(node); return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false); } @@ -4319,7 +4338,7 @@ namespace ts { } else { // Use specific property type when parent is a tuple or numeric index type when parent is an array - const propName = "" + indexOf(pattern.elements, declaration); + const propName = "" + pattern.elements.indexOf(declaration); type = isTupleLikeType(parentType) ? getTypeOfPropertyOfType(parentType, propName as __String) : elementType; @@ -4340,7 +4359,7 @@ namespace ts { type = getTypeWithFacts(type, TypeFacts.NEUndefined); } return declaration.initializer ? - getUnionType([type, checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) : + getUnionType([type, checkExpressionCached(declaration.initializer)], UnionReduction.Subtype) : type; } @@ -4367,15 +4386,15 @@ namespace ts { } // Return the inferred type for a variable, parameter, or property declaration - function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type { + function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, includeOptionality: boolean): Type { // A variable declared in a for..in statement is of type string, or of type keyof T when the // right hand expression is of a type parameter type. - if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) { + if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) { const indexType = getIndexType(checkNonNullExpression((declaration.parent.parent).expression)); return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType; } - if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) { + if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) { // checkRightHandSideOfForOf will return undefined if the for-of expression type was // missing properties/signatures required to get its iteratedType (like // [Symbol.iterator] or next). This may be because we accessed properties from anyType, @@ -4388,11 +4407,11 @@ namespace ts { return getTypeForBindingElement(declaration); } + const isOptional = !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken && includeOptionality; // Use type from type annotation if one is present - const typeNode = getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - const declaredType = getTypeFromTypeNode(typeNode); - return addOptionality(declaredType, /*optional*/ !!declaration.questionToken && includeOptionality); + const declaredType = tryGetTypeFromEffectiveTypeNode(declaration); + if (declaredType) { + return addOptionality(declaredType, isOptional); } if ((noImplicitAny || isInJavaScriptFile(declaration)) && @@ -4436,14 +4455,14 @@ namespace ts { type = getContextuallyTypedParameterType(declaration); } if (type) { - return addOptionality(type, /*optional*/ !!declaration.questionToken && includeOptionality); + return addOptionality(type, isOptional); } } // Use the type of the initializer expression if one is present if (declaration.initializer) { const type = checkDeclarationInitializer(declaration); - return addOptionality(type, /*optional*/ !!declaration.questionToken && includeOptionality); + return addOptionality(type, isOptional); } if (isJsxAttribute(declaration)) { @@ -4452,11 +4471,6 @@ namespace ts { return trueType; } - // If it is a short-hand property assignment, use the type of the identifier - if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) { - return checkIdentifier(declaration.name); - } - // If the declaration specifies a binding pattern, use the type implied by the binding pattern if (isBindingPattern(declaration.name)) { return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true); @@ -4508,7 +4522,7 @@ namespace ts { } } - const type = jsDocType || getUnionType(types, /*subtypeReduction*/ true); + const type = jsDocType || getUnionType(types, UnionReduction.Subtype); return getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor)); } @@ -4601,7 +4615,7 @@ namespace ts { // Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the // binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the // tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string. - function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type { + function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, reportErrors?: boolean): Type { let type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true); if (type) { if (reportErrors) { @@ -4609,21 +4623,15 @@ namespace ts { } // always widen a 'unique symbol' type if the type was created for a different declaration. - if (type.flags & TypeFlags.UniqueESSymbol && !declaration.type && type.symbol !== getSymbolOfNode(declaration)) { + if (type.flags & TypeFlags.UniqueESSymbol && (isBindingElement(declaration) || !declaration.type) && type.symbol !== getSymbolOfNode(declaration)) { type = esSymbolType; } - // During a normal type check we'll never get to here with a property assignment (the check of the containing - // object literal uses a different path). We exclude widening only so that language services and type verification - // tools see the actual type. - if (declaration.kind === SyntaxKind.PropertyAssignment) { - return type; - } return getWidenedType(type); } // Rest parameters default to type any[], other parameters default to type any - type = declaration.dotDotDotToken ? anyArrayType : anyType; + type = isParameter(declaration) && declaration.dotDotDotToken ? anyArrayType : anyType; // Report implicit any errors unless this is a private property within an ambient declaration if (reportErrors && noImplicitAny) { @@ -4640,6 +4648,13 @@ namespace ts { return isPrivateWithinAmbient(memberDeclaration); } + function tryGetTypeFromEffectiveTypeNode(declaration: Declaration) { + const typeNode = getEffectiveTypeAnnotationNode(declaration); + if (typeNode) { + return getTypeFromTypeNode(typeNode); + } + } + function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { @@ -4674,8 +4689,39 @@ namespace ts { declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { type = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol); } + else if (isJSDocPropertyTag(declaration) + || isPropertyAccessExpression(declaration) + || isIdentifier(declaration) + || (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration)) + || isMethodSignature(declaration)) { + + // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` + if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { + return getTypeOfFuncClassEnumModule(symbol); + } + type = tryGetTypeFromEffectiveTypeNode(declaration) || anyType; + } + else if (isPropertyAssignment(declaration)) { + type = tryGetTypeFromEffectiveTypeNode(declaration) || checkPropertyAssignment(declaration); + } + else if (isJsxAttribute(declaration)) { + type = tryGetTypeFromEffectiveTypeNode(declaration) || checkJsxAttribute(declaration); + } + else if (isShorthandPropertyAssignment(declaration)) { + type = tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionForMutableLocation(declaration.name, CheckMode.Normal); + } + else if (isObjectLiteralMethod(declaration)) { + type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal); + } + else if (isParameter(declaration) + || isPropertyDeclaration(declaration) + || isPropertySignature(declaration) + || isVariableDeclaration(declaration) + || isBindingElement(declaration)) { + type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); + } else { - type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); + Debug.fail("Unhandled declaration kind! " + (ts as any).SyntaxKind[declaration.kind]); } if (!popTypeResolution()) { @@ -4863,6 +4909,9 @@ namespace ts { if (getCheckFlags(symbol) & CheckFlags.Instantiated) { return getTypeOfInstantiatedSymbol(symbol); } + if (getCheckFlags(symbol) & CheckFlags.ReverseMapped) { + return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol); + } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { return getTypeOfVariableOrParameterOrProperty(symbol); } @@ -5091,11 +5140,11 @@ namespace ts { return type.resolvedBaseTypes; } - function resolveBaseTypesOfClass(type: InterfaceType): void { - type.resolvedBaseTypes = emptyArray; + function resolveBaseTypesOfClass(type: InterfaceType) { + type.resolvedBaseTypes = resolvingEmptyArray; const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) { - return; + return type.resolvedBaseTypes = emptyArray; } const baseTypeNode = getBaseTypeNodeOfClass(type); const typeArgs = typeArgumentsFromTypeReferenceNode(baseTypeNode); @@ -5118,24 +5167,31 @@ namespace ts { const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode); if (!constructors.length) { error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments); - return; + return type.resolvedBaseTypes = emptyArray; } baseType = getReturnTypeOfSignature(constructors[0]); } if (baseType === unknownType) { - return; + return type.resolvedBaseTypes = emptyArray; } if (!isValidBaseType(baseType)) { error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType)); - return; + return type.resolvedBaseTypes = emptyArray; } if (type === baseType || hasBaseType(baseType, type)) { error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); - return; + return type.resolvedBaseTypes = emptyArray; } - type.resolvedBaseTypes = [baseType]; + if (type.resolvedBaseTypes === resolvingEmptyArray) { + // Circular reference, likely through instantiation of default parameters + // (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset + // as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a + // partial instantiation of the members without the base types fully resolved + (type as Type as ResolvedType).members = undefined; + } + return type.resolvedBaseTypes = [baseType]; } function areAllOuterTypeParametersApplied(type: Type): boolean { @@ -5341,7 +5397,7 @@ namespace ts { } } if (memberTypeList.length) { - const enumType = getUnionType(memberTypeList, /*subtypeReduction*/ false, symbol, /*aliasTypeArguments*/ undefined); + const enumType = getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined); if (enumType.flags & TypeFlags.Union) { enumType.flags |= TypeFlags.EnumLiteral; enumType.symbol = symbol; @@ -5447,7 +5503,7 @@ namespace ts { */ function isThislessVariableLikeDeclaration(node: VariableLikeDeclaration): boolean { const typeNode = getEffectiveTypeAnnotationNode(node); - return typeNode ? isThislessType(typeNode) : !node.initializer; + return typeNode ? isThislessType(typeNode) : !hasInitializer(node); } /** @@ -5770,6 +5826,7 @@ namespace ts { if (source.symbol && members === getMembersOfSymbol(source.symbol)) { members = createSymbolTable(source.declaredProperties); } + setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); const thisArgument = lastOrUndefined(typeArguments); for (const baseType of baseTypes) { const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; @@ -5904,7 +5961,7 @@ namespace ts { if (unionSignatures.length > 1) { let thisParameter = signature.thisParameter; if (forEach(unionSignatures, sig => sig.thisParameter)) { - const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), /*subtypeReduction*/ true); + const thisType = getUnionType(map(unionSignatures, sig => sig.thisParameter ? getTypeOfSymbol(sig.thisParameter) : anyType), UnionReduction.Subtype); thisParameter = createSymbolWithType(signature.thisParameter, thisType); } s = cloneSignature(signature); @@ -5930,7 +5987,7 @@ namespace ts { indexTypes.push(indexInfo.type); isAnyReadonly = isAnyReadonly || indexInfo.isReadonly; } - return createIndexInfo(getUnionType(indexTypes, /*subtypeReduction*/ true), isAnyReadonly); + return createIndexInfo(getUnionType(indexTypes, UnionReduction.Subtype), isAnyReadonly); } function resolveUnionTypeMembers(type: UnionType) { @@ -6032,6 +6089,7 @@ namespace ts { if (symbol.exports) { members = getExportsOfSymbol(symbol); } + setStructuredTypeMembers(type, members, emptyArray, emptyArray, undefined, undefined); if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); const baseConstructorType = getBaseConstructorTypeOfClass(classType); @@ -6064,6 +6122,23 @@ namespace ts { } } + function resolveReverseMappedTypeMembers(type: ReverseMappedType) { + const indexInfo = getIndexInfoOfType(type.source, IndexKind.String); + const readonlyMask = type.mappedType.declaration.readonlyToken ? false : true; + const optionalMask = type.mappedType.declaration.questionToken ? 0 : SymbolFlags.Optional; + const stringIndexInfo = indexInfo && createIndexInfo(inferReverseMappedType(indexInfo.type, type.mappedType), readonlyMask && indexInfo.isReadonly); + const members = createSymbolTable(); + for (const prop of getPropertiesOfType(type.source)) { + const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); + const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; + inferredProp.declarations = prop.declarations; + inferredProp.propertyType = getTypeOfSymbol(prop); + inferredProp.mappedType = type.mappedType; + members.set(prop.escapedName, inferredProp); + } + setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); + } + /** Resolve the members of a mapped type { [P in K]: T } */ function resolveMappedTypeMembers(type: MappedType) { const members: SymbolTable = createSymbolTable(); @@ -6203,6 +6278,9 @@ namespace ts { else if ((type).objectFlags & ObjectFlags.ClassOrInterface) { resolveClassOrInterfaceMembers(type); } + else if ((type).objectFlags & ObjectFlags.ReverseMapped) { + resolveReverseMappedTypeMembers(type as ReverseMappedType); + } else if ((type).objectFlags & ObjectFlags.Anonymous) { resolveAnonymousTypeMembers(type); } @@ -6618,7 +6696,7 @@ namespace ts { } } if (propTypes.length) { - return getUnionType(propTypes, /*subtypeReduction*/ true); + return getUnionType(propTypes, UnionReduction.Subtype); } } return undefined; @@ -6682,7 +6760,7 @@ namespace ts { if (node.initializer) { const signatureDeclaration = node.parent; const signature = getSignatureFromDeclaration(signatureDeclaration); - const parameterIndex = ts.indexOf(signatureDeclaration.parameters, node); + const parameterIndex = signatureDeclaration.parameters.indexOf(node); Debug.assert(parameterIndex >= 0); return parameterIndex >= signature.minArgumentCount; } @@ -6690,7 +6768,7 @@ namespace ts { if (iife) { return !node.type && !node.dotDotDotToken && - indexOf((node.parent as SignatureDeclaration).parameters, node) >= iife.arguments.length; + node.parent.parameters.indexOf(node) >= iife.arguments.length; } return false; @@ -7005,7 +7083,7 @@ namespace ts { type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper); } else if (signature.unionSignatures) { - type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true); + type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype); } else { type = getReturnTypeFromBody(signature.declaration); @@ -7847,7 +7925,7 @@ namespace ts { // expression constructs such as array literals and the || and ?: operators). Named types can // circularly reference themselves and therefore cannot be subtype reduced during their declaration. // For example, "type Item = string | (() => Item" is a named type that circularly references itself. - function getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + function getUnionType(types: Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { if (types.length === 0) { return neverType; } @@ -7859,11 +7937,15 @@ namespace ts { if (typeSet.containsAny) { return anyType; } - if (subtypeReduction) { - removeSubtypes(typeSet); - } - else if (typeSet.containsLiteralOrUniqueESSymbol) { - removeRedundantLiteralTypes(typeSet); + switch (unionReduction) { + case UnionReduction.Literal: + if (typeSet.containsLiteralOrUniqueESSymbol) { + removeRedundantLiteralTypes(typeSet); + } + break; + case UnionReduction.Subtype: + removeSubtypes(typeSet); + break; } if (typeSet.length === 0) { return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType : @@ -7939,7 +8021,7 @@ namespace ts { function getTypeFromUnionTypeNode(node: UnionTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*subtypeReduction*/ false, + links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), UnionReduction.Literal, getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node)); } return links.resolvedType; @@ -8014,7 +8096,7 @@ namespace ts { // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. const unionType = typeSet[unionIndex]; return getUnionType(map(unionType.types, t => getIntersectionType(replaceElement(typeSet, unionIndex, t))), - /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); + UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } const id = getTypeListId(typeSet); let type = intersectionTypes.get(id); @@ -8325,10 +8407,7 @@ namespace ts { if (right.flags & TypeFlags.Union) { return mapType(right, t => getSpreadType(left, t, symbol, propagatedFlags)); } - if (right.flags & TypeFlags.NonPrimitive) { - return nonPrimitiveType; - } - if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike)) { + if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive)) { return left; } @@ -8389,8 +8468,7 @@ namespace ts { emptyArray, getNonReadonlyIndexSignature(stringIndexInfo), getNonReadonlyIndexSignature(numberIndexInfo)); - spread.flags |= propagatedFlags; - spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral; + spread.flags |= propagatedFlags | TypeFlags.ContainsObjectLiteral; (spread as ObjectType).objectFlags |= (ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread); return spread; } @@ -8631,7 +8709,7 @@ namespace ts { * This is used during inference when instantiating type parameter defaults. */ function createBackreferenceMapper(typeParameters: TypeParameter[], index: number): TypeMapper { - return t => indexOf(typeParameters, t) >= index ? emptyObjectType : t; + return t => typeParameters.indexOf(t) >= index ? emptyObjectType : t; } function isInferenceContext(mapper: TypeMapper): mapper is InferenceContext { @@ -8853,7 +8931,7 @@ namespace ts { } } if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { - return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); + return getUnionType(instantiateTypes((type).types, mapper), UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } if (type.flags & TypeFlags.Intersection) { return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); @@ -9330,6 +9408,10 @@ namespace ts { return false; } + function isIgnoredJsxProperty(source: Type, sourceProp: Symbol, targetMemberType: Type | undefined) { + return source.flags & TypeFlags.JsxAttributes && !(isUnhyphenatedJsxName(sourceProp.escapedName) || targetMemberType); + } + /** * Checks if 'source' is related to 'target' (e.g.: is a assignable to). * @param source The left-hand-side of the relation. @@ -9576,7 +9658,7 @@ namespace ts { } if (target.flags & TypeFlags.Union) { const discriminantType = findMatchingDiscriminantType(source, target as UnionType); - if (discriminantType) { + if (discriminantType) { // check excess properties against discriminant type only, not the entire union return hasExcessProperties(source, discriminantType, reportErrors); } @@ -10058,6 +10140,9 @@ namespace ts { if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (sourceProp && sourceProp !== targetProp) { + if (isIgnoredJsxProperty(source, sourceProp, getTypeOfSymbol(targetProp))) { + continue; + } const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { @@ -10282,6 +10367,9 @@ namespace ts { function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { let result = Ternary.True; for (const prop of getPropertiesOfObjectType(source)) { + if (isIgnoredJsxProperty(source, prop, /*targetMemberType*/ undefined)) { + continue; + } if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) { const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors); if (!related) { @@ -10464,7 +10552,7 @@ namespace ts { let result = "" + type.target.id; for (const t of type.typeArguments) { if (isUnconstrainedTypeParameter(t)) { - let index = indexOf(typeParameters, t); + let index = typeParameters.indexOf(t); if (index < 0) { index = typeParameters.length; typeParameters.push(t); @@ -10715,7 +10803,7 @@ namespace ts { const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); return primaryTypes.length ? getNullableType(getSupertypeOrUnion(primaryTypes), getFalsyFlagsOfTypes(types) & TypeFlags.Nullable) : - getUnionType(types, /*subtypeReduction*/ true); + getUnionType(types, UnionReduction.Subtype); } // Return the leftmost type for which no type to the right is a subtype. @@ -11006,7 +11094,7 @@ namespace ts { // Widening an empty object literal transitions from a highly restrictive type to // a highly inclusive one. For that reason we perform subtype reduction here if the // union includes empty object types (e.g. reducing {} | string to just {}). - return getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType)); + return getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType) ? UnionReduction.Subtype : UnionReduction.Literal); } if (isArrayType(type) || isTupleType(type)) { return createTypeReference((type).target, sameMap((type).typeArguments, getWidenedType)); @@ -11219,42 +11307,45 @@ namespace ts { * property is computed by inferring from the source property type to X for the type * variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). */ - function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type { + function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type { + const key = source.id + "," + target.id; + if (reverseMappedCache.has(key)) { + return reverseMappedCache.get(key); + } + reverseMappedCache.set(key, undefined); + const type = createReverseMappedType(source, target); + reverseMappedCache.set(key, type); + return type; + } + + function createReverseMappedType(source: Type, target: MappedType) { const properties = getPropertiesOfType(source); - let indexInfo = getIndexInfoOfType(source, IndexKind.String); - if (properties.length === 0 && !indexInfo) { + if (properties.length === 0 && !getIndexInfoOfType(source, IndexKind.String)) { return undefined; } - const typeParameter = getIndexedAccessType((getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target)); - const inference = createInferenceInfo(typeParameter); - const inferences = [inference]; - const templateType = getTemplateTypeFromMappedType(target); - const readonlyMask = target.declaration.readonlyToken ? false : true; - const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional; - const members = createSymbolTable(); + // If any property contains context sensitive functions that have been skipped, the source type + // is incomplete and we can't infer a meaningful input type. for (const prop of properties) { - const propType = getTypeOfSymbol(prop); - // If any property contains context sensitive functions that have been skipped, the source type - // is incomplete and we can't infer a meaningful input type. - if (propType.flags & TypeFlags.ContainsAnyFunctionType) { + if (getTypeOfSymbol(prop).flags & TypeFlags.ContainsAnyFunctionType) { return undefined; } - const checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0; - const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags); - inferredProp.declarations = prop.declarations; - inferredProp.type = inferTargetType(propType); - members.set(prop.escapedName, inferredProp); } - if (indexInfo) { - indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly); - } - return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined); + const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType; + reversed.source = source; + reversed.mappedType = target; + return reversed; + } - function inferTargetType(sourceType: Type): Type { - inference.candidates = undefined; - inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack); - return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType; - } + function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) { + return inferReverseMappedType(symbol.propertyType, symbol.mappedType); + } + + function inferReverseMappedType(sourceType: Type, target: MappedType): Type { + const typeParameter = getIndexedAccessType((getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target)); + const templateType = getTemplateTypeFromMappedType(target); + const inference = createInferenceInfo(typeParameter); + inferTypes([inference], sourceType, templateType); + return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType; } function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) { @@ -11270,7 +11361,7 @@ namespace ts { return undefined; } - function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[]) { + function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) { let symbolStack: Symbol[]; let visited: Map; inferFromTypes(originalSource, originalTarget); @@ -11358,7 +11449,7 @@ namespace ts { return; } } - else if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { + if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments const sourceTypes = (source).typeArguments || emptyArray; const targetTypes = (target).typeArguments || emptyArray; @@ -11487,13 +11578,7 @@ namespace ts { // such that direct inferences to T get priority over inferences to Partial, for example. const inference = getInferenceInfoForType((constraintType).type); if (inference && !inference.isFixed) { - const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol); - if (contains(mappedTypeStack, key)) { - return; - } - (mappedTypeStack || (mappedTypeStack = [])).push(key); - const inferredType = inferTypeForHomomorphicMappedType(source, target, mappedTypeStack); - mappedTypeStack.pop(); + const inferredType = inferTypeForHomomorphicMappedType(source, target); if (inferredType) { const savePriority = priority; priority |= InferencePriority.MappedType; @@ -11612,7 +11697,7 @@ namespace ts { if (candidates.length > 1) { const objectLiterals = filter(candidates, isObjectLiteralType); if (objectLiterals.length) { - const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, /*subtypeReduction*/ true)); + const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, UnionReduction.Subtype)); return concatenate(filter(candidates, t => !isObjectLiteralType(t)), [objectLiteralsType]); } } @@ -11639,7 +11724,7 @@ namespace ts { // union types were requested or if all inferences were made from the return type position, infer a // union type. Otherwise, infer a common supertype. const unwidenedType = inference.priority & InferencePriority.Contravariant ? getCommonSubtype(baseCandidates) : - context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, /*subtypeReduction*/ true) : + context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, UnionReduction.Subtype) : getCommonSupertype(baseCandidates); inferredType = getWidenedType(unwidenedType); } @@ -11743,7 +11828,7 @@ namespace ts { const container = (node as BindingElement).parent.parent; const key = container.kind === SyntaxKind.BindingElement ? getFlowCacheKey(container) : (container.initializer && getFlowCacheKey(container.initializer)); const text = getBindingElementNameText(node as BindingElement); - const result = key && text && (key + "." + text); + const result = key && text && (key + "." + text); return result; } return undefined; @@ -12037,7 +12122,7 @@ namespace ts { } function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { - return getTypeOfDestructuredArrayElement(getAssignedType(node), indexOf(node.elements, element)); + return getTypeOfDestructuredArrayElement(getAssignedType(node), node.elements.indexOf(element)); } function getAssignedTypeOfSpreadExpression(node: SpreadElement): Type { @@ -12081,7 +12166,7 @@ namespace ts { const type = pattern.kind === SyntaxKind.ObjectBindingPattern ? getTypeOfDestructuredProperty(parentType, node.propertyName || node.name) : !node.dotDotDotToken ? - getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) : + getTypeOfDestructuredArrayElement(parentType, pattern.elements.indexOf(node)) : getTypeOfDestructuredSpreadExpression(parentType); return getTypeWithDefault(type, node.initializer); } @@ -12213,7 +12298,7 @@ namespace ts { // Apply a mapping function to a type and return the resulting type. If the source type // is a union type, the mapping function is applied to each constituent type and a union // of the resulting types is returned. - function mapType(type: Type, mapper: (t: Type) => Type): Type { + function mapType(type: Type, mapper: (t: Type) => Type, noReductions?: boolean): Type { if (!(type.flags & TypeFlags.Union)) { return mapper(type); } @@ -12234,7 +12319,7 @@ namespace ts { } } } - return mappedTypes ? getUnionType(mappedTypes) : mappedType; + return mappedTypes ? getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal) : mappedType; } function extractTypesOfKind(type: Type, kind: TypeFlags) { @@ -12293,7 +12378,7 @@ namespace ts { return elementType.flags & TypeFlags.Never ? autoArrayType : createArrayType(elementType.flags & TypeFlags.Union ? - getUnionType((elementType).types, /*subtypeReduction*/ true) : + getUnionType((elementType).types, UnionReduction.Subtype) : elementType); } @@ -12326,7 +12411,7 @@ namespace ts { // At flow control branch or loop junctions, if the type along every antecedent code path // is an evolving array type, we construct a combined evolving array type. Otherwise we // finalize all evolving array types. - function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: boolean) { + function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: UnionReduction) { return isEvolvingArrayTypeList(types) ? getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))) : getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction); @@ -12618,7 +12703,7 @@ namespace ts { seenIncomplete = true; } } - return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction), seenIncomplete); + return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal), seenIncomplete); } function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { @@ -12647,7 +12732,7 @@ namespace ts { // path that leads to the top. for (let i = flowLoopStart; i < flowLoopCount; i++) { if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key && flowLoopTypes[i].length) { - return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], /*subtypeReduction*/ false), /*incomplete*/ true); + return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], UnionReduction.Literal), /*incomplete*/ true); } } // Add the flow loop junction and reference to the in-process stack and analyze @@ -12666,7 +12751,7 @@ namespace ts { firstAntecedentType = flowType; } const type = getTypeFromFlowType(flowType); - // If we see a value appear in the cache it is a sign that control flow analysis + // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up // the resulting type and bail out. const cached = cache.get(key); @@ -12689,7 +12774,7 @@ namespace ts { } // The result is incomplete if the first antecedent (the non-looping control flow path) // is incomplete. - const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction); + const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal); if (isIncomplete(firstAntecedentType)) { return createFlowType(result, /*incomplete*/ true); } @@ -12872,7 +12957,7 @@ namespace ts { const discriminantType = getUnionType(clauseTypes); const caseType = discriminantType.flags & TypeFlags.Never ? neverType : - replacePrimitivesWithLiterals(filterType(type, t => isTypeComparableTo(discriminantType, t)), discriminantType); + replacePrimitivesWithLiterals(filterType(type, t => areTypesComparable(discriminantType, t)), discriminantType); if (!hasDefaultClause) { return caseType; } @@ -13125,6 +13210,12 @@ namespace ts { return type; } + function markAliasReferenced(symbol: Symbol, location: Node) { + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { + markAliasSymbolAsReferenced(symbol); + } + } + function checkIdentifier(node: Identifier): Type { const symbol = getResolvedSymbol(node); if (symbol === unknownSymbol) { @@ -13153,9 +13244,9 @@ namespace ts { } // We should only mark aliases as referenced if there isn't a local value declaration - // for the symbol. - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { - markAliasSymbolAsReferenced(symbol); + // for the symbol. Also, don't mark any property access expression LHS - checkPropertyAccessExpression will handle that + if (!(node.parent && isPropertyAccessExpression(node.parent) && node.parent.expression === node)) { + markAliasReferenced(symbol, node); } const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); @@ -13373,7 +13464,7 @@ namespace ts { } } - function findFirstSuperCall(n: Node): Node { + function findFirstSuperCall(n: Node): SuperCall | undefined { if (isSuperCall(n)) { return n; } @@ -13389,12 +13480,12 @@ namespace ts { * * @param constructor constructor-function to look for super statement */ - function getSuperCallInConstructor(constructor: ConstructorDeclaration): ExpressionStatement { + function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined { const links = getNodeLinks(constructor); // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result if (links.hasSuperCall === undefined) { - links.superCall = findFirstSuperCall(constructor.body); + links.superCall = findFirstSuperCall(constructor.body); links.hasSuperCall = links.superCall ? true : false; } return links.superCall; @@ -13829,7 +13920,7 @@ namespace ts { if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { const iife = getImmediatelyInvokedFunctionExpression(func); if (iife && iife.arguments) { - const indexOfParameter = indexOf(func.parameters, parameter); + const indexOfParameter = func.parameters.indexOf(parameter); if (parameter.dotDotDotToken) { const restTypes: Type[] = []; for (let i = indexOfParameter; i < iife.arguments.length; i++) { @@ -13850,7 +13941,7 @@ namespace ts { if (contextualSignature) { const funcHasRestParameters = hasRestParameter(func); const len = func.parameters.length - (funcHasRestParameters ? 1 : 0); - let indexOfParameter = indexOf(func.parameters, parameter); + let indexOfParameter = func.parameters.indexOf(parameter); if (getThisParameter(func) !== undefined && !contextualSignature.thisParameter) { Debug.assert(indexOfParameter !== 0); // Otherwise we should not have called `getContextuallyTypedParameterType`. indexOfParameter -= 1; @@ -13881,7 +13972,7 @@ namespace ts { // the contextual type of an initializer expression is the type annotation of the containing declaration, if present. function getContextualTypeForInitializerExpression(node: Expression): Type { const declaration = node.parent; - if (node === declaration.initializer || node.kind === SyntaxKind.EqualsToken) { + if (hasInitializer(declaration) && node === declaration.initializer) { const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode) { return getTypeFromTypeNode(typeNode); @@ -13897,7 +13988,7 @@ namespace ts { } if (isBindingPattern(declaration.parent)) { const parentDeclaration = declaration.parent.parent; - const name = declaration.propertyName || declaration.name; + const name = (declaration as BindingElement).propertyName || (declaration as BindingElement).name; if (parentDeclaration.kind !== SyntaxKind.BindingElement) { const parentTypeNode = getEffectiveTypeAnnotationNode(parentDeclaration); if (parentTypeNode && !isBindingPattern(name)) { @@ -13981,7 +14072,7 @@ namespace ts { // In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter. function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type { const args = getEffectiveCallArguments(callTarget); - const argIndex = indexOf(args, arg); + const argIndex = args.indexOf(arg); if (argIndex >= 0) { // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. @@ -14013,12 +14104,6 @@ namespace ts { case SyntaxKind.AmpersandAmpersandToken: case SyntaxKind.CommaToken: return node === right ? getContextualType(binaryExpression) : undefined; - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - // For completions after `x === ` - return node === operatorToken ? getTypeOfExpression(binaryExpression.left) : undefined; default: return undefined; } @@ -14048,11 +14133,11 @@ namespace ts { return mapType(type, t => { const prop = t.flags & TypeFlags.StructuredType ? getPropertyOfType(t, name) : undefined; return prop ? getTypeOfSymbol(prop) : undefined; - }); + }, /*noReductions*/ true); } function getIndexTypeOfContextualType(type: Type, kind: IndexKind) { - return mapType(type, t => getIndexTypeOfStructuredType(t, kind)); + return mapType(type, t => getIndexTypeOfStructuredType(t, kind), /*noReductions*/ true); } // Return true if the given contextual type is a tuple-like type @@ -14234,12 +14319,8 @@ namespace ts { return getContextualTypeForReturnExpression(node); case SyntaxKind.YieldExpression: return getContextualTypeForYieldOperand(parent); + case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: - if (node.kind === SyntaxKind.NewKeyword) { // for completions after `new ` - return getContextualType(parent as NewExpression); - } - // falls through - case SyntaxKind.CallExpression: return getContextualTypeForArgument(parent, node); case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: @@ -14274,12 +14355,6 @@ namespace ts { case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxSelfClosingElement: return getAttributesTypeFromJsxOpeningLikeElement(parent); - case SyntaxKind.CaseClause: { - if (node.kind === SyntaxKind.CaseKeyword) { // for completions after `case ` - const switchStatement = (parent as CaseClause).parent.parent; - return getTypeOfExpression(switchStatement.expression); - } - } } return undefined; } @@ -14459,7 +14534,7 @@ namespace ts { } } return createArrayType(elementTypes.length ? - getUnionType(elementTypes, /*subtypeReduction*/ true) : + getUnionType(elementTypes, UnionReduction.Subtype) : strictNullChecks ? implicitNeverType : undefinedWideningType); } @@ -14538,7 +14613,7 @@ namespace ts { propTypes.push(getTypeOfSymbol(properties[i])); } } - const unionType = propTypes.length ? getUnionType(propTypes, /*subtypeReduction*/ true) : undefinedType; + const unionType = propTypes.length ? getUnionType(propTypes, UnionReduction.Subtype) : undefinedType; return createIndexInfo(unionType, /*isReadonly*/ false); } @@ -14550,7 +14625,7 @@ namespace ts { let propertiesTable = createSymbolTable(); let propertiesArray: Symbol[] = []; let spread: Type = emptyObjectType; - let propagatedFlags: TypeFlags = 0; + let propagatedFlags: TypeFlags = TypeFlags.FreshLiteral; const contextualType = getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && @@ -14737,14 +14812,14 @@ namespace ts { type.flags & TypeFlags.UnionOrIntersection && !forEach((type).types, t => !isValidSpreadType(t))); } - function checkJsxSelfClosingElement(node: JsxSelfClosingElement): Type { - checkJsxOpeningLikeElementOrOpeningFragment(node); + function checkJsxSelfClosingElement(node: JsxSelfClosingElement, checkMode: CheckMode): Type { + checkJsxOpeningLikeElementOrOpeningFragment(node, checkMode); return getJsxGlobalElementType() || anyType; } - function checkJsxElement(node: JsxElement): Type { + function checkJsxElement(node: JsxElement, checkMode: CheckMode): Type { // Check attributes - checkJsxOpeningLikeElementOrOpeningFragment(node.openingElement); + checkJsxOpeningLikeElementOrOpeningFragment(node.openingElement, checkMode); // Perform resolution on the closing tag so that rename/go to definition/etc work if (isJsxIntrinsicIdentifier(node.closingElement.tagName)) { @@ -14757,8 +14832,8 @@ namespace ts { return getJsxGlobalElementType() || anyType; } - function checkJsxFragment(node: JsxFragment): Type { - checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment); + function checkJsxFragment(node: JsxFragment, checkMode: CheckMode): Type { + checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment, checkMode); if (compilerOptions.jsx === JsxEmit.React && compilerOptions.jsxFactory) { error(node, Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory); @@ -14791,6 +14866,12 @@ namespace ts { } } + function checkJsxAttribute(node: JsxAttribute, checkMode?: CheckMode) { + return node.initializer + ? checkExpressionForMutableLocation(node.initializer, checkMode) + : trueType; // is sugar for + } + /** * Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element. * @@ -14800,11 +14881,10 @@ namespace ts { * @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, * which also calls getSpreadType. */ - function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, filter?: (symbol: Symbol) => boolean, checkMode?: CheckMode) { + function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode) { const attributes = openingLikeElement.attributes; let attributesTable = createSymbolTable(); let spread: Type = emptyObjectType; - let attributesArray: Symbol[] = []; let hasSpreadAnyType = false; let typeToIntersect: Type; let explicitlySpecifyChildrenAttribute = false; @@ -14813,9 +14893,7 @@ namespace ts { for (const attributeDecl of attributes.properties) { const member = attributeDecl.symbol; if (isJsxAttribute(attributeDecl)) { - const exprType = attributeDecl.initializer ? - checkExpression(attributeDecl.initializer, checkMode) : - trueType; // is sugar for + const exprType = checkJsxAttribute(attributeDecl, checkMode); const attributeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.escapedName); attributeSymbol.declarations = member.declarations; @@ -14826,24 +14904,22 @@ namespace ts { attributeSymbol.type = exprType; attributeSymbol.target = member; attributesTable.set(attributeSymbol.escapedName, attributeSymbol); - attributesArray.push(attributeSymbol); if (attributeDecl.name.escapedText === jsxChildrenPropertyName) { explicitlySpecifyChildrenAttribute = true; } } else { Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute); - if (attributesArray.length > 0) { - spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0); - attributesArray = []; + if (attributesTable.size > 0) { + spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, TypeFlags.JsxAttributes); attributesTable = createSymbolTable(); } - const exprType = checkExpression(attributeDecl.expression); + const exprType = checkExpressionCached(attributeDecl.expression, checkMode); if (isTypeAny(exprType)) { hasSpreadAnyType = true; } if (isValidSpreadType(exprType)) { - spread = getSpreadType(spread, exprType, openingLikeElement.symbol, /*propagatedFlags*/ 0); + spread = getSpreadType(spread, exprType, openingLikeElement.symbol, TypeFlags.JsxAttributes); } else { typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType; @@ -14852,18 +14928,8 @@ namespace ts { } if (!hasSpreadAnyType) { - if (spread !== emptyObjectType) { - if (attributesArray.length > 0) { - spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0); - } - attributesArray = getPropertiesOfType(spread); - } - - attributesTable = createSymbolTable(); - for (const attr of attributesArray) { - if (!filter || filter(attr)) { - attributesTable.set(attr.escapedName, attr); - } + if (attributesTable.size > 0) { + spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, TypeFlags.JsxAttributes); } } @@ -14881,30 +14947,30 @@ namespace ts { error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, unescapeLeadingUnderscores(jsxChildrenPropertyName)); } - // If there are children in the body of JSX element, create dummy attribute "children" with anyType so that it will pass the attribute checking process + // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName); childrenPropSymbol.type = childrenTypes.length === 1 ? childrenTypes[0] : - createArrayType(getUnionType(childrenTypes, /*subtypeReduction*/ false)); - attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol); + createArrayType(getUnionType(childrenTypes)); + const childPropMap = createSymbolTable(); + childPropMap.set(jsxChildrenPropertyName, childrenPropSymbol); + spread = getSpreadType(spread, createAnonymousType(attributes.symbol, childPropMap, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined), attributes.symbol, TypeFlags.JsxAttributes); + } } if (hasSpreadAnyType) { return anyType; } - - const attributeType = createJsxAttributesType(attributes.symbol, attributesTable); - return typeToIntersect && attributesTable.size ? getIntersectionType([typeToIntersect, attributeType]) : - typeToIntersect ? typeToIntersect : attributeType; + return typeToIntersect && spread !== emptyObjectType ? getIntersectionType([typeToIntersect, spread]) : (typeToIntersect || spread); /** * Create anonymous type from given attributes symbol table. * @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable * @param attributesTable a symbol table of attributes property */ - function createJsxAttributesType(symbol: Symbol, attributesTable: UnderscoreEscapedMap) { - const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); + function createJsxAttributesType() { + const result = createAnonymousType(attributes.symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral; result.objectFlags |= ObjectFlags.ObjectLiteral; return result; @@ -14933,8 +14999,8 @@ namespace ts { * (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used) * @param node a JSXAttributes to be resolved of its type */ - function checkJsxAttributes(node: JsxAttributes, checkMode?: CheckMode) { - return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, /*filter*/ undefined, checkMode); + function checkJsxAttributes(node: JsxAttributes, checkMode: CheckMode) { + return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, checkMode); } function getJsxType(name: __String) { @@ -14990,7 +15056,7 @@ namespace ts { * element is not a class element, or the class element type cannot be determined, returns 'undefined'. * For example, in the element , the element instance type is `MyClass` (not `typeof MyClass`). */ - function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type) { + function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type, sourceAttributesType: Type) { Debug.assert(!(valueType.flags & TypeFlags.Union)); if (isTypeAny(valueType)) { // Short-circuit if the class tag is using an element type 'any' @@ -15013,7 +15079,8 @@ namespace ts { for (const signature of signatures) { if (signature.typeParameters) { const isJavascript = isInJavaScriptFile(node); - const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0, isJavascript); + const inferenceContext = createInferenceContext(signature, /*flags*/ isJavascript ? InferenceFlags.AnyDefault : 0); + const typeArguments = inferJsxTypeArguments(signature, sourceAttributesType, inferenceContext); instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments, isJavascript)); } else { @@ -15021,7 +15088,7 @@ namespace ts { } } - return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true); + return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), UnionReduction.Subtype); } /** @@ -15198,6 +15265,7 @@ namespace ts { * * @param openingLikeElement a non-intrinsic JSXOPeningLikeElement * @param shouldIncludeAllStatelessAttributesType a boolean indicating whether to include all attributes types from all stateless function signature + * @param sourceAttributesType Is the attributes type the user passed, and is used to create inferences in the target type if present * @param elementType an instance type of the given opening-like element. If undefined, the function will check type openinglikeElement's tagname. * @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global (imported from react.d.ts) * @return attributes type if able to resolve the type of node @@ -15206,14 +15274,15 @@ namespace ts { */ function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean, - elementType: Type = checkExpression(openingLikeElement.tagName), + sourceAttributesType: Type | undefined, + elementType: Type, elementClassType?: Type): Type { if (elementType.flags & TypeFlags.Union) { const types = (elementType as UnionType).types; return getUnionType(types.map(type => { - return resolveCustomJsxElementAttributesType(openingLikeElement, shouldIncludeAllStatelessAttributesType, type, elementClassType); - }), /*subtypeReduction*/ true); + return resolveCustomJsxElementAttributesType(openingLikeElement, shouldIncludeAllStatelessAttributesType, sourceAttributesType, type, elementClassType); + }), UnionReduction.Subtype); } // If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type @@ -15243,7 +15312,7 @@ namespace ts { } // Get the element instance type (the result of newing or invoking this tag) - const elemInstanceType = getJsxElementInstanceType(openingLikeElement, elementType); + const elemInstanceType = getJsxElementInstanceType(openingLikeElement, elementType, sourceAttributesType || emptyObjectType); // If we should include all stateless attributes type, then get all attributes type from all stateless function signature. // Otherwise get only attributes type from the signature picked by choose-overload logic. @@ -15256,7 +15325,7 @@ namespace ts { } // Issue an error if this return type isn't assignable to JSX.ElementClass - if (elementClassType) { + if (elementClassType && sourceAttributesType) { checkTypeRelatedTo(elemInstanceType, elementClassType, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements); } @@ -15339,14 +15408,20 @@ namespace ts { * @param node a custom JSX opening-like element * @param shouldIncludeAllStatelessAttributesType a boolean value used by language service to get all possible attributes type from an overload stateless function component */ - function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type { - const links = getNodeLinks(node); - const linkLocation = shouldIncludeAllStatelessAttributesType ? "resolvedJsxElementAllAttributesType" : "resolvedJsxElementAttributesType"; - if (!links[linkLocation]) { - const elemClassType = getJsxGlobalElementClassType(); - return links[linkLocation] = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType); + function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, sourceAttributesType: Type, shouldIncludeAllStatelessAttributesType: boolean): Type { + if (!sourceAttributesType) { + // This ensures we cache non-inference uses of this calculation (ie, contextual types or services) + const links = getNodeLinks(node); + const linkLocation = shouldIncludeAllStatelessAttributesType ? "resolvedJsxElementAllAttributesType" : "resolvedJsxElementAttributesType"; + if (!links[linkLocation]) { + const elemClassType = getJsxGlobalElementClassType(); + return links[linkLocation] = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, sourceAttributesType, checkExpression(node.tagName), elemClassType); + } + return links[linkLocation]; + } + else { + return resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, sourceAttributesType, checkExpression(node.tagName), getJsxGlobalElementClassType()); } - return links[linkLocation]; } /** @@ -15361,7 +15436,7 @@ namespace ts { else { // Because in language service, the given JSX opening-like element may be incomplete and therefore, // we can't resolve to exact signature if the element is a stateless function component so the best thing to do is return all attributes type from all overloads. - return getCustomJsxElementAttributesType(node, /*shouldIncludeAllStatelessAttributesType*/ true); + return getCustomJsxElementAttributesType(node, /*sourceAttributesType*/ undefined, /*shouldIncludeAllStatelessAttributesType*/ true); } } @@ -15375,7 +15450,7 @@ namespace ts { return getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); } else { - return getCustomJsxElementAttributesType(node, /*shouldIncludeAllStatelessAttributesType*/ false); + return getCustomJsxElementAttributesType(node, /*sourceAttributesType*/ undefined, /*shouldIncludeAllStatelessAttributesType*/ false); } } @@ -15435,7 +15510,7 @@ namespace ts { } } - function checkJsxOpeningLikeElementOrOpeningFragment(node: JsxOpeningLikeElement | JsxOpeningFragment) { + function checkJsxOpeningLikeElementOrOpeningFragment(node: JsxOpeningLikeElement | JsxOpeningFragment, checkMode: CheckMode) { const isNodeOpeningLikeElement = isJsxOpeningLikeElement(node); if (isNodeOpeningLikeElement) { @@ -15460,7 +15535,7 @@ namespace ts { } if (isNodeOpeningLikeElement) { - checkJsxAttributesAssignableToTagNameAttributes(node); + checkJsxAttributesAssignableToTagNameAttributes(node, checkMode); } else { checkJsxChildren((node as JsxOpeningFragment).parent); @@ -15507,25 +15582,23 @@ namespace ts { * Check assignablity between given attributes property, "source attributes", and the "target attributes" * @param openingLikeElement an opening-like JSX element to check its JSXAttributes */ - function checkJsxAttributesAssignableToTagNameAttributes(openingLikeElement: JsxOpeningLikeElement) { + function checkJsxAttributesAssignableToTagNameAttributes(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode) { // The function involves following steps: // 1. Figure out expected attributes type by resolving tagName of the JSX opening-like element, targetAttributesType. // During these steps, we will try to resolve the tagName as intrinsic name, stateless function, stateful component (in the order) // 2. Solved JSX attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element. // 3. Check if the two are assignable to each other - // targetAttributesType is a type of an attributes from resolving tagName of an opening-like JSX element. - const targetAttributesType = isJsxIntrinsicIdentifier(openingLikeElement.tagName) ? - getIntrinsicAttributesTypeFromJsxOpeningLikeElement(openingLikeElement) : - getCustomJsxElementAttributesType(openingLikeElement, /*shouldIncludeAllStatelessAttributesType*/ false); // sourceAttributesType is a type of an attributes properties. // i.e
// attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes". - const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement, - attribute => { - return isUnhyphenatedJsxName(attribute.escapedName) || !!(getPropertyOfType(targetAttributesType, attribute.escapedName)); - }); + const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement, checkMode); + + // targetAttributesType is a type of an attributes from resolving tagName of an opening-like JSX element. + const targetAttributesType = isJsxIntrinsicIdentifier(openingLikeElement.tagName) ? + getIntrinsicAttributesTypeFromJsxOpeningLikeElement(openingLikeElement) : + getCustomJsxElementAttributesType(openingLikeElement, sourceAttributesType, /*shouldIncludeAllStatelessAttributesType*/ false); // If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type. // but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass. @@ -15536,11 +15609,16 @@ namespace ts { // Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement); // After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType. - // This will allow excess properties in spread type as it is very common pattern to spread outter attributes into React component in its render method. + // This will allow excess properties in spread type as it is very common pattern to spread outer attributes into React component in its render method. if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) { for (const attribute of openingLikeElement.attributes.properties) { - if (isJsxAttribute(attribute) && !isKnownProperty(targetAttributesType, attribute.name.escapedText, /*isComparingJsxAttributes*/ true)) { - error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attribute.name), typeToString(targetAttributesType)); + if (!isJsxAttribute(attribute)) { + continue; + } + const attrName = attribute.name as Identifier; + const isNotIgnoredJsxProperty = (isUnhyphenatedJsxName(idText(attrName)) || !!(getPropertyOfType(targetAttributesType, attrName.escapedText))); + if (isNotIgnoredJsxProperty && !isKnownProperty(targetAttributesType, attrName.escapedText, /*isComparingJsxAttributes*/ true)) { + error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attrName), typeToString(targetAttributesType)); // We break here so that errors won't be cascading break; } @@ -15713,15 +15791,20 @@ namespace ts { function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { let propType: Type; - let leftSymbol = getNodeLinks(left) && getNodeLinks(left).resolvedSymbol; - const leftWasReferenced = leftSymbol && getSymbolLinks(leftSymbol).referenced; const leftType = checkNonNullExpression(left); + const parentSymbol = getNodeLinks(left).resolvedSymbol; const apparentType = getApparentType(getWidenedType(leftType)); if (isTypeAny(apparentType) || apparentType === silentNeverType) { + if (isIdentifier(left) && parentSymbol) { + markAliasReferenced(parentSymbol, node); + } return apparentType; } const assignmentKind = getAssignmentTargetKind(node); const prop = getPropertyOfType(apparentType, right.escapedText); + if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) { + markAliasReferenced(parentSymbol, node); + } if (!prop) { const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String); if (!(indexInfo && indexInfo.type)) { @@ -15738,13 +15821,6 @@ namespace ts { else { checkPropertyNotUsedBeforeDeclaration(prop, node, right); markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword); - // Reset the referenced-ness of the LHS expression if this access refers to a const enum or const enum only module - leftSymbol = getNodeLinks(left) && getNodeLinks(left).resolvedSymbol; - if (leftSymbol && !leftWasReferenced && getSymbolLinks(leftSymbol).referenced && - !(isNonLocalAlias(leftSymbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(prop)) - ) { - getSymbolLinks(leftSymbol).referenced = undefined; - } getNodeLinks(node).resolvedSymbol = prop; checkPropertyAccessibility(node, left, apparentType, prop); if (assignmentKind) { @@ -15824,6 +15900,9 @@ namespace ts { * In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration. */ function isPropertyDeclaredInAncestorClass(prop: Symbol): boolean { + if (!(prop.parent.flags & SymbolFlags.Class)) { + return false; + } let classType = getTypeOfSymbol(prop.parent) as InterfaceType; while (true) { classType = getSuperClass(classType); @@ -15984,37 +16063,48 @@ namespace ts { } function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) { - if (prop && - noUnusedIdentifiers && - (prop.flags & SymbolFlags.ClassMember) && - prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private) - && !(nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly))) { + if (!prop || !noUnusedIdentifiers || !(prop.flags & SymbolFlags.ClassMember) || !prop.valueDeclaration || !hasModifier(prop.valueDeclaration, ModifierFlags.Private)) { + return; + } + if (nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly) && !(prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor))) { + return; + } - if (isThisAccess) { - // Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters). - const containingMethod = findAncestor(nodeForCheckWriteOnly, isFunctionLikeDeclaration); - if (containingMethod && containingMethod.symbol === prop) { - return; - } + if (isThisAccess) { + // Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters). + const containingMethod = findAncestor(nodeForCheckWriteOnly, isFunctionLikeDeclaration); + if (containingMethod && containingMethod.symbol === prop) { + return; } + } - if (getCheckFlags(prop) & CheckFlags.Instantiated) { - getSymbolLinks(prop).target.isReferenced = true; - } - else { - prop.isReferenced = true; - } + if (getCheckFlags(prop) & CheckFlags.Instantiated) { + getSymbolLinks(prop).target.isReferenced = true; + } + else { + prop.isReferenced = true; } } function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: __String): boolean { - const left = node.kind === SyntaxKind.PropertyAccessExpression - ? (node).expression - : (node).left; - + const left = node.kind === SyntaxKind.PropertyAccessExpression ? node.expression : node.left; return isValidPropertyAccessWithType(node, left, propertyName, getWidenedType(checkExpression(left))); } + function isValidPropertyAccessForCompletions(node: PropertyAccessExpression, type: Type, property: Symbol): boolean { + return isValidPropertyAccessWithType(node, node.expression, property.escapedName, type) + && (!(property.flags & ts.SymbolFlags.Method) || isValidMethodAccess(property, type)); + } + function isValidMethodAccess(method: Symbol, type: Type) { + const propType = getTypeOfFuncClassEnumModule(method); + const signatures = getSignaturesOfType(propType, SignatureKind.Call); + Debug.assert(signatures.length !== 0); + return signatures.some(sig => { + const thisType = getThisTypeOfSignature(sig); + return !thisType || isTypeAssignableTo(type, thisType); + }); + } + function isValidPropertyAccessWithType( node: PropertyAccessExpression | QualifiedName, left: LeftHandSideExpression | QualifiedName, @@ -16367,6 +16457,13 @@ namespace ts { return getSignatureInstantiation(signature, getInferredTypes(context), isInJavaScriptFile(contextualSignature.declaration)); } + function inferJsxTypeArguments(signature: Signature, sourceAttributesType: Type, context: InferenceContext): Type[] { + const paramType = getTypeAtPosition(signature, 0); + inferTypes(context.inferences, sourceAttributesType, paramType); + + return getInferredTypes(context); + } + function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: boolean[], context: InferenceContext): Type[] { // Clear out all the inference results from the last time inferTypeArguments was called on this context for (const inference of context.inferences) { @@ -17141,7 +17238,7 @@ namespace ts { } excludeCount--; if (excludeCount > 0) { - excludeArgument[indexOf(excludeArgument, /*value*/ true)] = false; + excludeArgument[excludeArgument.indexOf(/*value*/ true)] = false; } else { excludeArgument = undefined; @@ -17798,7 +17895,7 @@ namespace ts { const type = getTypeOfSymbol(symbol); if (strictNullChecks) { const declaration = symbol.valueDeclaration; - if (declaration && (declaration).initializer) { + if (declaration && hasInitializer(declaration)) { return getOptionalType(type); } } @@ -17917,7 +18014,6 @@ namespace ts { } function getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type { - const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); if (!func.body) { return unknownType; } @@ -17935,9 +18031,9 @@ namespace ts { } } else { - let types: Type[]; + let types = checkAndAggregateReturnExpressionTypes(func, checkMode); if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function - types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), checkAndAggregateReturnExpressionTypes(func, checkMode)); + types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), types); if (!types || types.length === 0) { const iterableIteratorAny = functionFlags & FunctionFlags.Async ? createAsyncIterableIteratorType(anyType) // AsyncGenerator function @@ -17950,7 +18046,6 @@ namespace ts { } } else { - types = checkAndAggregateReturnExpressionTypes(func, checkMode); if (!types) { // For an async function, the return type will not be never, but rather a Promise for never. return functionFlags & FunctionFlags.Async @@ -17966,9 +18061,10 @@ namespace ts { } // Return a union of the return expression types. - type = getUnionType(types, /*subtypeReduction*/ true); + type = getUnionType(types, UnionReduction.Subtype); } + const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); if (!contextualSignature) { reportErrorsFromWidening(func, type); } @@ -18058,7 +18154,8 @@ namespace ts { return true; } - function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] { + /** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means return `void`, `undefined` means return `never`. */ + function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] | undefined { const functionFlags = getFunctionFlags(func); const aggregatedTypes: Type[] = []; let hasReturnWithNoExpression = functionHasImplicitReturn(func); @@ -18083,8 +18180,7 @@ namespace ts { hasReturnWithNoExpression = true; } }); - if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || - func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction)) { + if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || mayReturnNever(func))) { return undefined; } if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) { @@ -18092,6 +18188,17 @@ namespace ts { } return aggregatedTypes; } + function mayReturnNever(func: FunctionLikeDeclaration): boolean { + switch (func.kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return true; + case SyntaxKind.MethodDeclaration: + return func.parent.kind === SyntaxKind.ObjectLiteralExpression; + default: + return false; + } + } /** * TypeScript Specification 1.0 (6.3) - July 2014 @@ -18874,7 +18981,7 @@ namespace ts { leftType; case SyntaxKind.BarBarToken: return getTypeFacts(leftType) & TypeFacts.Falsy ? - getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], /*subtypeReduction*/ true) : + getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) : leftType; case SyntaxKind.EqualsToken: checkAssignmentOperator(rightType); @@ -19034,7 +19141,7 @@ namespace ts { checkExpression(node.condition); const type1 = checkExpression(node.whenTrue, checkMode); const type2 = checkExpression(node.whenFalse, checkMode); - return getUnionType([type1, type2], /*subtypeReduction*/ true); + return getUnionType([type1, type2], UnionReduction.Subtype); } function checkTemplateExpression(node: TemplateExpression): Type { @@ -19050,13 +19157,13 @@ namespace ts { return stringType; } - function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper): Type { + function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper | undefined): Type { const saveContextualType = node.contextualType; const saveContextualMapper = node.contextualMapper; node.contextualType = contextualType; node.contextualMapper = contextualMapper; const checkMode = contextualMapper === identityMapper ? CheckMode.SkipContextSensitive : - contextualMapper ? CheckMode.Inferential : CheckMode.Normal; + contextualMapper ? CheckMode.Inferential : CheckMode.Contextual; const result = checkExpression(node, checkMode); node.contextualType = saveContextualType; node.contextualMapper = saveContextualMapper; @@ -19066,6 +19173,9 @@ namespace ts { function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { + if (checkMode) { + return checkExpression(node, checkMode); + } // When computing a type that we're going to cache, we need to ignore any ongoing control flow // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart // to the top of the stack ensures all transient types are computed from a known point. @@ -19082,7 +19192,7 @@ namespace ts { return node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression; } - function checkDeclarationInitializer(declaration: VariableLikeDeclaration) { + function checkDeclarationInitializer(declaration: HasExpressionInitializer) { const type = getTypeOfExpression(declaration.initializer, /*cache*/ true); return getCombinedNodeFlags(declaration) & NodeFlags.Const || (getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration)) || @@ -19091,7 +19201,7 @@ namespace ts { function isLiteralOfContextualType(candidateType: Type, contextualType: Type): boolean { if (contextualType) { - if (contextualType.flags & TypeFlags.Union && !(contextualType.flags & TypeFlags.Boolean)) { + if (contextualType.flags & TypeFlags.UnionOrIntersection && !(contextualType.flags & TypeFlags.Boolean)) { // If the contextual type is a union containing both of the 'true' and 'false' types we // don't consider it a literal context for boolean literals. const types = (contextualType).types; @@ -19331,11 +19441,11 @@ namespace ts { case SyntaxKind.JsxExpression: return checkJsxExpression(node, checkMode); case SyntaxKind.JsxElement: - return checkJsxElement(node); + return checkJsxElement(node, checkMode); case SyntaxKind.JsxSelfClosingElement: - return checkJsxSelfClosingElement(node); + return checkJsxSelfClosingElement(node, checkMode); case SyntaxKind.JsxFragment: - return checkJsxFragment(node); + return checkJsxFragment(node, checkMode); case SyntaxKind.JsxAttributes: return checkJsxAttributes(node, checkMode); case SyntaxKind.JsxOpeningElement: @@ -19389,7 +19499,7 @@ namespace ts { error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature); } if (node.name && isIdentifier(node.name) && (node.name.escapedText === "this" || node.name.escapedText === "new")) { - if (indexOf(func.parameters, node) !== 0) { + if (func.parameters.indexOf(node) !== 0) { error(node, Diagnostics.A_0_parameter_must_be_the_first_parameter, node.name.escapedText as string); } if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) { @@ -19764,7 +19874,7 @@ namespace ts { } } - function checkPropertyDeclaration(node: PropertyDeclaration) { + function checkPropertyDeclaration(node: PropertyDeclaration | PropertySignature) { // Grammar checking if (!checkGrammarDecoratorsAndModifiers(node) && !checkGrammarProperty(node)) checkGrammarComputedPropertyName(node.name); checkVariableLikeDeclaration(node); @@ -19810,24 +19920,6 @@ namespace ts { return; } - function containsSuperCallAsComputedPropertyName(n: Declaration): boolean { - const name = getNameOfDeclaration(n); - return name && containsSuperCall(name); - } - - function containsSuperCall(n: Node): boolean { - if (isSuperCall(n)) { - return true; - } - else if (isFunctionLike(n)) { - return false; - } - else if (isClassLike(n)) { - return forEach((n).members, containsSuperCallAsComputedPropertyName); - } - return forEachChild(n, containsSuperCall); - } - function isInstancePropertyWithInitializer(n: Node): boolean { return n.kind === SyntaxKind.PropertyDeclaration && !hasModifier(n, ModifierFlags.Static) && @@ -20475,7 +20567,7 @@ namespace ts { return undefined; } - return typeAsPromise.promisedTypeOfPromise = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature), /*subtypeReduction*/ true); + return typeAsPromise.promisedTypeOfPromise = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature), UnionReduction.Subtype); } /** @@ -20514,7 +20606,7 @@ namespace ts { const promisedType = getPromisedTypeOfPromise(type); if (promisedType) { - if (type.id === promisedType.id || indexOf(awaitedTypeStack, promisedType.id) >= 0) { + if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) { // Verify that we don't have a bad actor in the form of a promise whose // promised type is the same as the promise type, or a mutually recursive // promise. If so, we return undefined as we cannot guess the shape. If this @@ -21149,20 +21241,32 @@ namespace ts { function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void { if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) { - if (node.members) { - for (const member of node.members) { - if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) { + for (const member of node.members) { + switch (member.kind) { + case SyntaxKind.MethodDeclaration: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + if (member.kind === SyntaxKind.SetAccessor && member.symbol.flags & SymbolFlags.GetAccessor) { + // Already would have reported an error on the getter. + break; + } if (!member.symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) { error(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(member.symbol)); } - } - else if (member.kind === SyntaxKind.Constructor) { + break; + case SyntaxKind.Constructor: for (const parameter of (member).parameters) { if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) { error(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol)); } } - } + break; + case SyntaxKind.IndexSignature: + // Can't be private + break; + default: + Debug.fail(); } } } @@ -21445,7 +21549,7 @@ namespace ts { } // Check that a parameter initializer contains no references to parameters declared to the right of itself - function checkParameterInitializer(node: VariableLikeDeclaration): void { + function checkParameterInitializer(node: HasExpressionInitializer): void { if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) { return; } @@ -21517,9 +21621,11 @@ namespace ts { } // Check variable, parameter, or property declaration - function checkVariableLikeDeclaration(node: VariableLikeDeclaration) { + function checkVariableLikeDeclaration(node: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement) { checkDecorators(node); - checkSourceElement(node.type); + if (!isBindingElement(node)) { + checkSourceElement(node.type); + } // JSDoc `function(string, string): string` syntax results in parameters with no name if (!node.name) { @@ -21530,7 +21636,7 @@ namespace ts { // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); + checkComputedPropertyName(node.name); if (node.initializer) { checkExpressionCached(node.initializer); } @@ -21542,11 +21648,11 @@ namespace ts { } // check computed properties inside property names of binding elements if (node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.propertyName); + checkComputedPropertyName(node.propertyName); } // check private/protected variable access - const parent = (node.parent).parent; + const parent = node.parent.parent; const parentType = getTypeForBindingElementParent(parent); const name = node.propertyName || node.name; const property = getPropertyOfType(parentType, getTextOfPropertyName(name)); @@ -21579,7 +21685,7 @@ namespace ts { return; } const symbol = getSymbolOfNode(node); - const type = convertAutoToAny(getTypeOfVariableOrParameterOrProperty(symbol)); + const type = convertAutoToAny(getTypeOfSymbol(symbol)); if (node === symbol.valueDeclaration) { // Node is the primary declaration of the symbol, just validate the initializer // Don't validate for-in initializer as it is already an error @@ -21807,7 +21913,7 @@ namespace ts { checkGrammarForInOrForOfStatement(node); const rightType = checkNonNullExpression(node.expression); - // TypeScript 1.0 spec (April 2014): 5.4 + // TypeScript 1.0 spec (April 2014): 5.4 // In a 'for-in' statement of the form // for (let VarDecl in Expr) Statement // VarDecl must be a variable declaration without a type annotation that declares a variable of type Any, @@ -21906,7 +22012,7 @@ namespace ts { const arrayTypes = (inputType).types; const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike)); if (filteredTypes !== arrayTypes) { - arrayType = getUnionType(filteredTypes, /*subtypeReduction*/ true); + arrayType = getUnionType(filteredTypes, UnionReduction.Subtype); } } else if (arrayType.flags & TypeFlags.StringLike) { @@ -21956,7 +22062,7 @@ namespace ts { return stringType; } - return getUnionType([arrayElementType, stringType], /*subtypeReduction*/ true); + return getUnionType([arrayElementType, stringType], UnionReduction.Subtype); } return arrayElementType; @@ -22054,7 +22160,7 @@ namespace ts { return undefined; } - const returnType = getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true); + const returnType = getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype); const iteratedType = getIteratedTypeOfIterator(returnType, errorNode, /*isAsyncIterator*/ !!asyncMethodType); if (checkAssignability && errorNode && iteratedType) { // If `checkAssignability` was specified, we were called from @@ -22129,7 +22235,7 @@ namespace ts { return undefined; } - let nextResult = getUnionType(map(nextMethodSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true); + let nextResult = getUnionType(map(nextMethodSignatures, getReturnTypeOfSignature), UnionReduction.Subtype); if (isTypeAny(nextResult)) { return undefined; } @@ -22193,57 +22299,58 @@ namespace ts { function checkReturnStatement(node: ReturnStatement) { // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - const functionBlock = getContainingFunction(node); - if (!functionBlock) { - grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body); - } + if (checkGrammarStatementInAmbientContext(node)) { + return; } const func = getContainingFunction(node); - if (func) { - const signature = getSignatureFromDeclaration(func); - const returnType = getReturnTypeOfSignature(signature); - const functionFlags = getFunctionFlags(func); - if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function + if (!func) { + grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body); + return; + } + + const signature = getSignatureFromDeclaration(func); + const returnType = getReturnTypeOfSignature(signature); + const functionFlags = getFunctionFlags(func); + const isGenerator = functionFlags & FunctionFlags.Generator; + if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) { + const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType; + if (isGenerator) { // AsyncGenerator function or Generator function // A generator does not need its return expressions checked against its return type. // Instead, the yield expressions are checked against the element type. - // TODO: Check return expressions of generators when return type tracking is added + // TODO: Check return types of generators when return type tracking is added // for generators. return; } - if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) { - const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType; - if (func.kind === SyntaxKind.SetAccessor) { - if (node.expression) { - error(node, Diagnostics.Setters_cannot_return_a_value); - } - } - else if (func.kind === SyntaxKind.Constructor) { - if (node.expression && !checkTypeAssignableTo(exprType, returnType, node)) { - error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); - } - } - else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func)) { - if (functionFlags & FunctionFlags.Async) { // Async function - const promisedType = getPromisedTypeOfPromise(returnType); - const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - if (promisedType) { - // If the function has a return type, but promisedType is - // undefined, an error will be reported in checkAsyncFunctionReturnType - // so we don't need to report one here. - checkTypeAssignableTo(awaitedType, promisedType, node); - } - } - else { - checkTypeAssignableTo(exprType, returnType, node); - } + else if (func.kind === SyntaxKind.SetAccessor) { + if (node.expression) { + error(node, Diagnostics.Setters_cannot_return_a_value); } } - else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) { - // The function has a return type, but the return statement doesn't have an expression. - error(node, Diagnostics.Not_all_code_paths_return_a_value); + else if (func.kind === SyntaxKind.Constructor) { + if (node.expression && !checkTypeAssignableTo(exprType, returnType, node)) { + error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); + } } + else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func)) { + if (functionFlags & FunctionFlags.Async) { // Async function + const promisedType = getPromisedTypeOfPromise(returnType); + const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); + if (promisedType) { + // If the function has a return type, but promisedType is + // undefined, an error will be reported in checkAsyncFunctionReturnType + // so we don't need to report one here. + checkTypeAssignableTo(awaitedType, promisedType, node); + } + } + else { + checkTypeAssignableTo(exprType, returnType, node); + } + } + } + else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType) && !isGenerator) { + // The function has a return type, but the return statement doesn't have an expression. + error(node, Diagnostics.Not_all_code_paths_return_a_value); } } @@ -22568,9 +22675,11 @@ namespace ts { // type parameter at this position, we report an error. const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint); const targetConstraint = getConstraintFromTypeParameter(target); - if ((sourceConstraint || targetConstraint) && - (!sourceConstraint || !targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint))) { - return false; + if (sourceConstraint) { + // relax check if later interface augmentation has no constraint + if (!targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint)) { + return false; + } } // If the type parameter node has a default and it is not identical to the default @@ -22684,7 +22793,12 @@ namespace ts { const t = getTypeFromTypeNode(typeRefNode); if (t !== unknownType) { if (isValidBaseType(t)) { - checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(t, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); + checkTypeAssignableTo(typeWithThis, + getTypeWithThisArgument(t, type.thisType), + node.name || node, + t.symbol && t.symbol.flags & SymbolFlags.Class ? + Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass : + Diagnostics.Class_0_incorrectly_implements_interface_1); } else { error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface); @@ -23683,7 +23797,7 @@ namespace ts { return checkParameter(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - return checkPropertyDeclaration(node); + return checkPropertyDeclaration(node); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: @@ -24391,6 +24505,7 @@ namespace ts { return undefined; case SyntaxKind.StringLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: // 1). import x = require("./mo/*gotToDefinitionHere*/d") // 2). External module name in an import declaration // 3). Dynamic import call or require in javascript @@ -24491,7 +24606,7 @@ namespace ts { } if (isBindingPattern(node)) { - return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true); + return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true); } if (isInRightSideOfImportOrExportAssignment(node)) { @@ -24536,7 +24651,7 @@ namespace ts { const typeOfArrayLiteral = getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr.parent); const elementType = checkIteratedTypeOrElementType(typeOfArrayLiteral || unknownType, expr.parent, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || unknownType; return checkArrayLiteralDestructuringElementAssignment(expr.parent, typeOfArrayLiteral, - indexOf((expr.parent).elements, expr), elementType || unknownType); + (expr.parent).elements.indexOf(expr), elementType || unknownType); } // Gets the property symbol corresponding to the property in destructuring assignment @@ -24589,36 +24704,28 @@ namespace ts { } function getRootSymbols(symbol: Symbol): Symbol[] { + const roots = getImmediateRootSymbols(symbol); + return roots ? flatMap(roots, getRootSymbols) : [symbol]; + } + function getImmediateRootSymbols(symbol: Symbol): ReadonlyArray | undefined { if (getCheckFlags(symbol) & CheckFlags.Synthetic) { - const symbols: Symbol[] = []; - const name = symbol.escapedName; - forEach(getSymbolLinks(symbol).containingType.types, t => { - const symbol = getPropertyOfType(t, name); - if (symbol) { - symbols.push(symbol); - } - }); - return symbols; + return mapDefined(getSymbolLinks(symbol).containingType.types, type => getPropertyOfType(type, symbol.escapedName)); } else if (symbol.flags & SymbolFlags.Transient) { - const transient = symbol as TransientSymbol; - if (transient.leftSpread) { - return [...getRootSymbols(transient.leftSpread), ...getRootSymbols(transient.rightSpread)]; - } - if (transient.syntheticOrigin) { - return getRootSymbols(transient.syntheticOrigin); - } - - let target: Symbol; - let next = symbol; - while (next = getSymbolLinks(next).target) { - target = next; - } - if (target) { - return [target]; - } + const { leftSpread, rightSpread, syntheticOrigin } = symbol as TransientSymbol; + return leftSpread ? [leftSpread, rightSpread] + : syntheticOrigin ? [syntheticOrigin] + : singleElementArray(tryGetAliasTarget(symbol)); } - return [symbol]; + return undefined; + } + function tryGetAliasTarget(symbol: Symbol): Symbol | undefined { + let target: Symbol | undefined; + let next = symbol; + while (next = getSymbolLinks(next).target) { + target = next; + } + return target; } // Emitter support @@ -24739,7 +24846,7 @@ namespace ts { // AND // - binding is not declared in loop, should be renamed to avoid name reuse across siblings // let a, b - // { let x = 1; a = () => x; } + // { let x = 1; a = () => x; } // { let x = 100; b = () => x; } // console.log(a()); // should print '1' // console.log(b()); // should print '100' @@ -25200,7 +25307,7 @@ namespace ts { // walk the parent chain for symbols to make sure that top level parent symbol is in the global scope // external modules cannot define or contribute to type declaration files - let current = symbol; + let current = symbol; while (true) { const parent = getParentOfSymbol(current); if (parent) { @@ -25664,10 +25771,6 @@ namespace ts { } function checkGrammarTypeParameterList(typeParameters: NodeArray, file: SourceFile): boolean { - if (checkGrammarForDisallowedTrailingComma(typeParameters)) { - return true; - } - if (typeParameters && typeParameters.length === 0) { const start = typeParameters.pos - "<".length; const end = skipTrivia(file.text, typeParameters.end) + ">".length; @@ -26472,7 +26575,7 @@ namespace ts { } } - function checkGrammarProperty(node: PropertyDeclaration) { + function checkGrammarProperty(node: PropertyDeclaration | PropertySignature) { if (isClassLike(node.parent)) { if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) { return true; @@ -26499,7 +26602,7 @@ namespace ts { return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } - if (node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer || + if (isPropertyDeclaration(node) && node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer || node.flags & NodeFlags.Ambient || hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract))) { return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index da0ea4aee56..33ecf570c03 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -145,6 +145,7 @@ namespace ts { "es2017.intl": "lib.es2017.intl.d.ts", "es2017.typedarrays": "lib.es2017.typedarrays.d.ts", "esnext.asynciterable": "lib.esnext.asynciterable.d.ts", + "esnext.promise": "lib.esnext.promise.d.ts", }), }, showInSimplifiedHelpView: true, @@ -1863,7 +1864,7 @@ namespace ts { return normalizeNonListOptionValue(option, basePath, value); } - function normalizeNonListOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { + function normalizeNonListOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { if (option.isFilePath) { value = normalizePath(combinePaths(basePath, value)); if (value === "") { @@ -1906,21 +1907,6 @@ namespace ts { */ const invalidTrailingRecursionPattern = /(^|\/)\*\*\/?$/; - /** - * Tests for a path with multiple recursive directory wildcards. - * Matches **\** and **\a\**, but not **\a**b. - * - * NOTE: used \ in place of / above to avoid issues with multiline comments. - * - * Breakdown: - * (^|\/) # matches either the beginning of the string or a directory separator. - * \*\*\/ # matches a recursive directory wildcard "**" followed by a directory separator. - * (.*\/)? # optionally matches any number of characters followed by a directory separator. - * \*\* # matches a recursive directory wildcard "**" - * ($|\/) # matches either the end of the string or a directory separator. - */ - const invalidMultipleRecursionPatterns = /(^|\/)\*\*\/(.*\/)?\*\*($|\/)/; - /** * Tests for a path where .. appears after a recursive directory wildcard. * Matches **\..\*, **\a\..\*, and **\.., but not ..\**\* @@ -2115,9 +2101,6 @@ namespace ts { if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { return Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; } - else if (invalidMultipleRecursionPatterns.test(spec)) { - return Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0; - } else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { return Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3a8f8e9de18..08f6d369aaa 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -288,6 +288,8 @@ namespace ts { return undefined; } + export function findLast(array: ReadonlyArray, predicate: (element: T, index: number) => element is U): U | undefined; + export function findLast(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined; export function findLast(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined { for (let i = array.length - 1; i >= 0; i--) { const value = array[i]; @@ -333,17 +335,6 @@ namespace ts { return false; } - export function indexOf(array: ReadonlyArray, value: T): number { - if (array) { - for (let i = 0; i < array.length; i++) { - if (array[i] === value) { - return i; - } - } - } - return -1; - } - export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray, start?: number): number { for (let i = start || 0; i < text.length; i++) { if (contains(charCodes, text.charCodeAt(i))) { @@ -1417,8 +1408,8 @@ namespace ts { return Array.isArray ? Array.isArray(value) : value instanceof Array; } - export function toArray(value: T | ReadonlyArray): ReadonlyArray; export function toArray(value: T | T[]): T[]; + export function toArray(value: T | ReadonlyArray): ReadonlyArray; export function toArray(value: T | T[]): T[] { return isArray(value) ? value : [value]; } @@ -1995,11 +1986,6 @@ namespace ts { return /^\.\.?($|[\\/])/.test(path); } - /** @deprecated Use `!isExternalModuleNameRelative(moduleName)` instead. */ - export function moduleHasNonRelativeName(moduleName: string): boolean { - return !isExternalModuleNameRelative(moduleName); - } - export function getEmitScriptTarget(compilerOptions: CompilerOptions) { return compilerOptions.target || ScriptTarget.ES3; } @@ -2086,7 +2072,7 @@ namespace ts { function getNormalizedPathComponentsOfUrl(url: string) { // Get root length of http://www.website.com/folder1/folder2/ - // In this example the root is: http://www.website.com/ + // In this example the root is: http://www.website.com/ // normalized path components should be ["http://www.website.com/", "folder1", "folder2"] const urlLength = url.length; @@ -2119,7 +2105,7 @@ namespace ts { } else { // Can't find the host assume the rest of the string as component - // but make sure we append "/" to it as root is not joined using "/" + // but make sure we append "/" to it as root is not joined using "/" // eg. if url passed in was http://website.com we want to use root as [http://website.com/] // so that other path manipulations will be correct and it can be merged with relative paths correctly return [url + directorySeparator]; @@ -2140,7 +2126,7 @@ namespace ts { const directoryComponents = getNormalizedPathOrUrlComponents(directoryPathOrUrl, currentDirectory); if (directoryComponents.length > 1 && lastOrUndefined(directoryComponents) === "") { // If the directory path given was of type test/cases/ then we really need components of directory to be only till its name - // that is ["test", "cases", ""] needs to be actually ["test", "cases"] + // that is ["test", "cases", ""] needs to be actually ["test", "cases"] directoryComponents.pop(); } @@ -2382,7 +2368,6 @@ namespace ts { function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { let subpattern = ""; - let hasRecursiveDirectoryWildcard = false; let hasWrittenComponent = false; const components = getNormalizedPathComponents(spec, basePath); const lastComponent = lastOrUndefined(components); @@ -2401,12 +2386,7 @@ namespace ts { let optionalCount = 0; for (let component of components) { if (component === "**") { - if (hasRecursiveDirectoryWildcard) { - return undefined; - } - subpattern += doubleAsteriskRegexFragment; - hasRecursiveDirectoryWildcard = true; } else { if (usage === "directories") { @@ -3053,4 +3033,8 @@ namespace ts { } export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty + + export function singleElementArray(t: T | undefined): T[] | undefined { + return t === undefined ? undefined : [t]; + } } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 90d9ff3de5a..b2d259ef259 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -64,7 +64,7 @@ namespace ts { let currentIdentifiers: Map; let isCurrentFileExternalModule: boolean; let reportedDeclarationError = false; - let errorNameNode: DeclarationName; + let errorNameNode: DeclarationName | QualifiedName; const emitJsDocComments = compilerOptions.removeComments ? noop : writeJsDocComments; const emit = compilerOptions.stripInternal ? stripInternal : emitNode; let needsDeclare = true; @@ -1199,7 +1199,7 @@ namespace ts { write(">"); } } - else { + else { emitHeritageClause([baseTypeNode], /*isImplementsList*/ false); } } @@ -1372,7 +1372,7 @@ namespace ts { // if this is property of type literal, // or is parameter of method/call/construct/index signature of type literal // emit only if type is specified - if (node.type) { + if (hasType(node)) { write(": "); emitType(node.type); } @@ -1866,6 +1866,7 @@ namespace ts { // it allows emitSeparatedList to write separator appropriately) // Example: // original: function foo([, x, ,]) {} + // tslint:disable-next-line no-double-space // emit : function foo([ , x, , ]) {} write(" "); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index fab7583d90c..826efa6994b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2276,6 +2276,10 @@ "category": "Error", "code": 2719 }, + "Class '{0}' incorrectly implements class '{1}'. Did you mean to extend '{1}' and inherit its members as a subclass?": { + "category": "Error", + "code": 2720 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", @@ -2623,10 +2627,6 @@ "category": "Error", "code": 5010 }, - "File specification cannot contain multiple recursive directory wildcards ('**'): '{0}'.": { - "category": "Error", - "code": 5011 - }, "Cannot read file '{0}': {1}.": { "category": "Error", "code": 5012 @@ -3420,6 +3420,14 @@ "category": "Message", "code": 6187 }, + "Numeric separators are not allowed here.": { + "category": "Error", + "code": 6188 + }, + "Multiple consecutive numeric separators are not permitted.": { + "category": "Error", + "code": 6189 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index dfc22e31833..65c17911488 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3831,13 +3831,13 @@ namespace ts { if (isLeftSideOfBinary) { // No need to parenthesize the left operand when the binary operator is // left associative: - // (a*b)/x -> a*b/x - // (a**b)/x -> a**b/x + // (a*b)/x -> a*b/x + // (a**b)/x -> a**b/x // // Parentheses are needed for the left operand when the binary operator is // right associative: - // (a/b)**x -> (a/b)**x - // (a**b)**x -> (a**b)**x + // (a/b)**x -> (a/b)**x + // (a**b)**x -> (a**b)**x return binaryOperatorAssociativity === Associativity.Right; } else { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index cb28be4677a..a072579ed63 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -88,20 +88,48 @@ namespace ts { case SyntaxKind.SpreadAssignment: return visitNode(cbNode, (node).expression); case SyntaxKind.Parameter: + return visitNodes(cbNode, cbNodes, node.decorators) || + visitNodes(cbNode, cbNodes, node.modifiers) || + visitNode(cbNode, (node).dotDotDotToken) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).initializer); case SyntaxKind.PropertyDeclaration: + return visitNodes(cbNode, cbNodes, node.decorators) || + visitNodes(cbNode, cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).exclamationToken) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).initializer); case SyntaxKind.PropertySignature: + return visitNodes(cbNode, cbNodes, node.decorators) || + visitNodes(cbNode, cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).initializer); case SyntaxKind.PropertyAssignment: + return visitNodes(cbNode, cbNodes, node.decorators) || + visitNodes(cbNode, cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).initializer); case SyntaxKind.VariableDeclaration: + return visitNodes(cbNode, cbNodes, node.decorators) || + visitNodes(cbNode, cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).exclamationToken) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).initializer); case SyntaxKind.BindingElement: return visitNodes(cbNode, cbNodes, node.decorators) || visitNodes(cbNode, cbNodes, node.modifiers) || - visitNode(cbNode, (node).propertyName) || - visitNode(cbNode, (node).dotDotDotToken) || - visitNode(cbNode, (node).name) || - visitNode(cbNode, (node).questionToken) || - visitNode(cbNode, (node).exclamationToken) || - visitNode(cbNode, (node).type) || - visitNode(cbNode, (node).initializer); + visitNode(cbNode, (node).propertyName) || + visitNode(cbNode, (node).dotDotDotToken) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).initializer); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: @@ -557,7 +585,7 @@ namespace ts { // 'disallow-in' set to 'false'. Otherwise, if we had 'allowsIn' set to 'true', then almost // all nodes would need extra state on them to store this info. // - // Note: 'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6 + // Note: 'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6 // grammar specification. // // An important thing about these context concepts. By default they are effectively inherited @@ -673,7 +701,7 @@ namespace ts { function getLanguageVariant(scriptKind: ScriptKind) { // .tsx and .jsx files are treated as jsx language variant. - return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard; + return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard; } function initializeState(_sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) { @@ -1401,9 +1429,13 @@ namespace ts { return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isIdentifierOrPattern(); case ParsingContext.TypeParameters: return isIdentifier(); - case ParsingContext.ArgumentExpressions: case ParsingContext.ArrayLiteralMembers: - return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isStartOfExpression(); + if (token() === SyntaxKind.CommaToken) { + return true; + } + // falls through + case ParsingContext.ArgumentExpressions: + return token() === SyntaxKind.DotDotDotToken || isStartOfExpression(); case ParsingContext.Parameters: return isStartOfParameter(); case ParsingContext.TypeArguments: @@ -1425,7 +1457,7 @@ namespace ts { function isValidHeritageClauseObjectLiteral() { Debug.assert(token() === SyntaxKind.OpenBraceToken); if (nextToken() === SyntaxKind.CloseBraceToken) { - // if we see "extends {}" then only treat the {} as what we're extending (and not + // if we see "extends {}" then only treat the {} as what we're extending (and not // the class body) if we have: // // extends {} { @@ -1521,7 +1553,7 @@ namespace ts { function isVariableDeclaratorListTerminator(): boolean { // If we can consume a semicolon (either explicitly, or with ASI), then consider us done - // with parsing the list of variable declarators. + // with parsing the list of variable declarators. if (canParseSemicolon()) { return true; } @@ -2241,7 +2273,7 @@ namespace ts { // // // - // We do *not* want to consume the > as we're consuming the expression for "". + // We do *not* want to consume the `>` as we're consuming the expression for "". node.expression = parseUnaryExpressionOrHigher(); } } @@ -3061,7 +3093,7 @@ namespace ts { // And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression". // // If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is - // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done + // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done // with AssignmentExpression if we see one. const arrowExpression = tryParseParenthesizedArrowFunctionExpression() || tryParseAsyncSimpleArrowFunctionExpression(); if (arrowExpression) { @@ -3091,7 +3123,7 @@ namespace ts { // we're in '2' or '3'. Consume the assignment and return. // // Note: we call reScanGreaterToken so that we get an appropriately merged token - // for cases like > > = becoming >>= + // for cases like `> > =` becoming `>>=` if (isLeftHandSideExpression(expr) && isAssignmentOperator(reScanGreaterToken())) { return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher()); } @@ -3247,7 +3279,7 @@ namespace ts { if (first === SyntaxKind.OpenParenToken) { if (second === SyntaxKind.CloseParenToken) { - // Simple cases: "() =>", "(): ", and "() {". + // Simple cases: "() =>", "(): ", and "() {". // This is an arrow function with no parameters. // The last one is not actually an arrow function, // but this is probably what the user intended. @@ -3867,7 +3899,8 @@ namespace ts { // We don't want to eagerly consume all import keyword as import call expression so we look a head to find "(" // For example: // var foo3 = require("subfolder - // import * as foo1 from "module-from-node -> we want this import to be a statement rather than import call expression + // import * as foo1 from "module-from-node + // We want this import to be a statement rather than import call expression sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport; expression = parseTokenNode(); } @@ -3917,7 +3950,7 @@ namespace ts { // treated as the invocation of "new Foo". We disambiguate that in code (to match // the original grammar) by making sure that if we see an ObjectCreationExpression // we always consume arguments if they are there. So we treat "new Foo()" as an - // object creation only, and not at all as an invocation) Another way to think + // object creation only, and not at all as an invocation. Another way to think // about this is that for every "new" that we see, we will consume an argument list if // it is there as part of the *associated* object creation node. Any additional // argument lists we see, will become invocation expressions. @@ -4333,7 +4366,7 @@ namespace ts { const typeArguments = parseDelimitedList(ParsingContext.TypeArguments, parseType); if (!parseExpected(SyntaxKind.GreaterThanToken)) { - // If it doesn't have the closing > then it's definitely not an type argument list. + // If it doesn't have the closing `>` then it's definitely not an type argument list. return undefined; } @@ -5366,8 +5399,8 @@ namespace ts { // off. The grammar would look something like this: // // MemberVariableDeclaration[Yield]: - // AccessibilityModifier_opt PropertyName TypeAnnotation_opt Initializer_opt[In]; - // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initializer_opt[In, ?Yield]; + // AccessibilityModifier_opt PropertyName TypeAnnotation_opt Initializer_opt[In]; + // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initializer_opt[In, ?Yield]; // // The checker may still error in the static case to explicitly disallow the yield expression. node.initializer = hasModifier(node, ModifierFlags.Static) @@ -5462,6 +5495,7 @@ namespace ts { switch (token()) { case SyntaxKind.OpenParenToken: // Method declaration case SyntaxKind.LessThanToken: // Generic Method declaration + case SyntaxKind.ExclamationToken: // Non-null assertion on property name case SyntaxKind.ColonToken: // Type Annotation for declaration case SyntaxKind.EqualsToken: // Initializer for declaration case SyntaxKind.QuestionToken: // Not valid, but permitted so that it gets caught later on. @@ -7048,7 +7082,7 @@ namespace ts { // If the 'pos' is before the start of the change, then we don't need to touch it. // If it isn't, then the 'pos' must be inside the change. How we update it will - // depend if delta is positive or negative. If delta is positive then we have + // depend if delta is positive or negative. If delta is positive then we have // something like: // // -------------------AAA----------------- @@ -7073,7 +7107,7 @@ namespace ts { // If the 'end' is after the change range, then we always adjust it by the delta // amount. However, if the end is in the change range, then how we adjust it - // will depend on if delta is positive or negative. If delta is positive then we + // will depend on if delta is positive or negative. If delta is positive then we // have something like: // // -------------------AAA----------------- diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bd31e50726f..2c9df7dd516 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -5,7 +5,7 @@ namespace ts { const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/; - export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string { + export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string | undefined { return forEachAncestorDirectory(searchPath, ancestor => { const fileName = combinePaths(ancestor, configName); return fileExists(fileName) ? fileName : undefined; @@ -190,6 +190,7 @@ namespace ts { readFile: fileName => sys.readFile(fileName), trace: (s: string) => sys.write(s + newLine), directoryExists: directoryName => sys.directoryExists(directoryName), + getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "", getDirectories: (path: string) => sys.getDirectories(path), realpath }; @@ -239,22 +240,28 @@ namespace ts { return errorMessage; } - const redForegroundEscapeSequence = "\u001b[91m"; - const yellowForegroundEscapeSequence = "\u001b[93m"; - const blueForegroundEscapeSequence = "\u001b[93m"; + /** @internal */ + export enum ForegroundColorEscapeSequences { + Grey = "\u001b[90m", + Red = "\u001b[91m", + Yellow = "\u001b[93m", + Blue = "\u001b[94m", + Cyan = "\u001b[96m" + } const gutterStyleSequence = "\u001b[30;47m"; const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; function getCategoryFormat(category: DiagnosticCategory): string { switch (category) { - case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence; - case DiagnosticCategory.Error: return redForegroundEscapeSequence; - case DiagnosticCategory.Message: return blueForegroundEscapeSequence; + case DiagnosticCategory.Warning: return ForegroundColorEscapeSequences.Yellow; + case DiagnosticCategory.Error: return ForegroundColorEscapeSequences.Red; + case DiagnosticCategory.Message: return ForegroundColorEscapeSequences.Blue; } } - function formatAndReset(text: string, formatStyle: string) { + /** @internal */ + export function formatColorAndReset(text: string, formatStyle: string) { return formatStyle + text + resetEscapeSequence; } @@ -287,7 +294,7 @@ namespace ts { // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines, // so we'll skip ahead to the second-to-last line. if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) { - context += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine(); + context += formatColorAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine(); i = lastLine - 1; } @@ -298,12 +305,12 @@ namespace ts { lineContent = lineContent.replace("\t", " "); // convert tabs to single spaces // Output the gutter and the actual contents of the line. - context += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator; + context += formatColorAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator; context += lineContent + host.getNewLine(); // Output the gutter and the error span for the line using tildes. - context += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator; - context += redForegroundEscapeSequence; + context += formatColorAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator; + context += ForegroundColorEscapeSequences.Red; if (i === firstLine) { // If we're on the last line, then limit it to the last character of the last line. // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position. @@ -322,13 +329,19 @@ namespace ts { context += resetEscapeSequence; } - output += host.getNewLine(); - output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; + output += formatColorAndReset(relativeFileName, ForegroundColorEscapeSequences.Cyan); + output += ":"; + output += formatColorAndReset(`${ firstLine + 1 }`, ForegroundColorEscapeSequences.Yellow); + output += ":"; + output += formatColorAndReset(`${ firstLineChar + 1 }`, ForegroundColorEscapeSequences.Yellow); + output += " - "; } const categoryColor = getCategoryFormat(diagnostic.category); const category = DiagnosticCategory[diagnostic.category].toLowerCase(); - output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()) }`; + output += formatColorAndReset(category, categoryColor); + output += formatColorAndReset(` TS${ diagnostic.code }: `, ForegroundColorEscapeSequences.Grey); + output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()); if (diagnostic.file) { output += host.getNewLine(); @@ -337,7 +350,7 @@ namespace ts { output += host.getNewLine(); } - return output; + return output + host.getNewLine(); } export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string { diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index f085c3ffc06..e4f7821897d 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -193,7 +193,7 @@ namespace ts { /* As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers IdentifierStart :: - Can contain Unicode 3.0.0 categories: + Can contain Unicode 3.0.0 categories: Uppercase letter (Lu), Lowercase letter (Ll), Titlecase letter (Lt), @@ -201,7 +201,7 @@ namespace ts { Other letter (Lo), or Letter number (Nl). IdentifierPart :: = - Can contain IdentifierStart + Unicode 3.0.0 categories: + Can contain IdentifierStart + Unicode 3.0.0 categories: Non-spacing mark (Mn), Combining spacing mark (Mc), Decimal number (Nd), or @@ -216,7 +216,7 @@ namespace ts { /* As per ECMAScript Language Specification 5th Edition, Section 7.6: ISyntaxToken Names and Identifiers IdentifierStart :: - Can contain Unicode 6.2 categories: + Can contain Unicode 6.2 categories: Uppercase letter (Lu), Lowercase letter (Ll), Titlecase letter (Lt), @@ -224,7 +224,7 @@ namespace ts { Other letter (Lo), or Letter number (Nl). IdentifierPart :: - Can contain IdentifierStart + Unicode 6.2 categories: + Can contain IdentifierStart + Unicode 6.2 categories: Non-spacing mark (Mn), Combining spacing mark (Mc), Decimal number (Nd), @@ -561,9 +561,9 @@ namespace ts { return false; } - function scanConflictMarkerTrivia(text: string, pos: number, error?: ErrorCallback) { + function scanConflictMarkerTrivia(text: string, pos: number, error?: (diag: DiagnosticMessage, pos?: number, len?: number) => void) { if (error) { - error(Diagnostics.Merge_conflict_marker_encountered, mergeConflictMarkerLength); + error(Diagnostics.Merge_conflict_marker_encountered, pos, mergeConflictMarkerLength); } const ch = text.charCodeAt(pos); @@ -852,34 +852,92 @@ namespace ts { scanRange, }; - function error(message: DiagnosticMessage, length?: number): void { + function error(message: DiagnosticMessage): void; + function error(message: DiagnosticMessage, errPos: number, length: number): void; + function error(message: DiagnosticMessage, errPos: number = pos, length?: number): void { if (onError) { + const oldPos = pos; + pos = errPos; onError(message, length || 0); + pos = oldPos; } } + function scanNumberFragment(): string { + let start = pos; + let allowSeparator = false; + let isPreviousTokenSeparator = false; + let result = ""; + while (true) { + const ch = text.charCodeAt(pos); + if (ch === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator; + if (allowSeparator) { + allowSeparator = false; + isPreviousTokenSeparator = true; + result += text.substring(start, pos); + } + else if (isPreviousTokenSeparator) { + error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1); + } + else { + error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1); + } + pos++; + start = pos; + continue; + } + if (isDigit(ch)) { + allowSeparator = true; + isPreviousTokenSeparator = false; + pos++; + continue; + } + break; + } + if (text.charCodeAt(pos - 1) === CharacterCodes._) { + error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1); + } + return result + text.substring(start, pos); + } + function scanNumber(): string { const start = pos; - while (isDigit(text.charCodeAt(pos))) pos++; + const mainFragment = scanNumberFragment(); + let decimalFragment: string; + let scientificFragment: string; if (text.charCodeAt(pos) === CharacterCodes.dot) { pos++; - while (isDigit(text.charCodeAt(pos))) pos++; + decimalFragment = scanNumberFragment(); } let end = pos; if (text.charCodeAt(pos) === CharacterCodes.E || text.charCodeAt(pos) === CharacterCodes.e) { pos++; tokenFlags |= TokenFlags.Scientific; if (text.charCodeAt(pos) === CharacterCodes.plus || text.charCodeAt(pos) === CharacterCodes.minus) pos++; - if (isDigit(text.charCodeAt(pos))) { - pos++; - while (isDigit(text.charCodeAt(pos))) pos++; - end = pos; - } - else { + const preNumericPart = pos; + const finalFragment = scanNumberFragment(); + if (!finalFragment) { error(Diagnostics.Digit_expected); } + else { + scientificFragment = text.substring(end, preNumericPart) + finalFragment; + end = pos; + } + } + if (tokenFlags & TokenFlags.ContainsSeparator) { + let result = mainFragment; + if (decimalFragment) { + result += "." + decimalFragment; + } + if (scientificFragment) { + result += scientificFragment; + } + return "" + +result; + } + else { + return "" + +(text.substring(start, end)); // No need to use all the fragments; no _ removal needed } - return "" + +(text.substring(start, end)); } function scanOctalDigits(): number { @@ -894,23 +952,41 @@ namespace ts { * Scans the given number of hexadecimal digits in the text, * returning -1 if the given number is unavailable. */ - function scanExactNumberOfHexDigits(count: number): number { - return scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ false); + function scanExactNumberOfHexDigits(count: number, canHaveSeparators: boolean): number { + return scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ false, canHaveSeparators); } /** * Scans as many hexadecimal digits as are available in the text, * returning -1 if the given number of digits was unavailable. */ - function scanMinimumNumberOfHexDigits(count: number): number { - return scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ true); + function scanMinimumNumberOfHexDigits(count: number, canHaveSeparators: boolean): number { + return scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ true, canHaveSeparators); } - function scanHexDigits(minCount: number, scanAsManyAsPossible: boolean): number { + function scanHexDigits(minCount: number, scanAsManyAsPossible: boolean, canHaveSeparators: boolean): number { let digits = 0; let value = 0; + let allowSeparator = false; + let isPreviousTokenSeparator = false; while (digits < minCount || scanAsManyAsPossible) { const ch = text.charCodeAt(pos); + if (canHaveSeparators && ch === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator; + if (allowSeparator) { + allowSeparator = false; + isPreviousTokenSeparator = true; + } + else if (isPreviousTokenSeparator) { + error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1); + } + else { + error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1); + } + pos++; + continue; + } + allowSeparator = canHaveSeparators; if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) { value = value * 16 + ch - CharacterCodes._0; } @@ -925,10 +1001,14 @@ namespace ts { } pos++; digits++; + isPreviousTokenSeparator = false; } if (digits < minCount) { value = -1; } + if (text.charCodeAt(pos - 1) === CharacterCodes._) { + error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1); + } return value; } @@ -1097,7 +1177,7 @@ namespace ts { } function scanHexadecimalEscape(numDigits: number): string { - const escapedValue = scanExactNumberOfHexDigits(numDigits); + const escapedValue = scanExactNumberOfHexDigits(numDigits, /*canHaveSeparators*/ false); if (escapedValue >= 0) { return String.fromCharCode(escapedValue); @@ -1109,7 +1189,7 @@ namespace ts { } function scanExtendedUnicodeEscape(): string { - const escapedValue = scanMinimumNumberOfHexDigits(1); + const escapedValue = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false); let isInvalidExtendedEscape = false; // Validate the value of the digit @@ -1162,7 +1242,7 @@ namespace ts { if (pos + 5 < end && text.charCodeAt(pos + 1) === CharacterCodes.u) { const start = pos; pos += 2; - const value = scanExactNumberOfHexDigits(4); + const value = scanExactNumberOfHexDigits(4, /*canHaveSeparators*/ false); pos = start; return value; } @@ -1218,8 +1298,27 @@ namespace ts { // For counting number of digits; Valid binaryIntegerLiteral must have at least one binary digit following B or b. // Similarly valid octalIntegerLiteral must have at least one octal digit following o or O. let numberOfDigits = 0; + let separatorAllowed = false; + let isPreviousTokenSeparator = false; while (true) { const ch = text.charCodeAt(pos); + // Numeric seperators are allowed anywhere within a numeric literal, except not at the beginning, or following another separator + if (ch === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator; + if (separatorAllowed) { + separatorAllowed = false; + isPreviousTokenSeparator = true; + } + else if (isPreviousTokenSeparator) { + error(Diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, pos, 1); + } + else { + error(Diagnostics.Numeric_separators_are_not_allowed_here, pos, 1); + } + pos++; + continue; + } + separatorAllowed = true; const valueOfCh = ch - CharacterCodes._0; if (!isDigit(ch) || valueOfCh >= base) { break; @@ -1227,11 +1326,17 @@ namespace ts { value = value * base + valueOfCh; pos++; numberOfDigits++; + isPreviousTokenSeparator = false; } // Invalid binaryIntegerLiteral or octalIntegerLiteral if (numberOfDigits === 0) { return -1; } + if (text.charCodeAt(pos - 1) === CharacterCodes._) { + // Literal ends with underscore - not allowed + error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1); + return value; + } return value; } @@ -1435,7 +1540,7 @@ namespace ts { case CharacterCodes._0: if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.X || text.charCodeAt(pos + 1) === CharacterCodes.x)) { pos += 2; - let value = scanMinimumNumberOfHexDigits(1); + let value = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ true); if (value < 0) { error(Diagnostics.Hexadecimal_digit_expected); value = 0; @@ -1489,7 +1594,7 @@ namespace ts { return token = SyntaxKind.NumericLiteral; case CharacterCodes.colon: pos++; - return token = SyntaxKind.ColonToken; + return token = SyntaxKind.ColonToken; case CharacterCodes.semicolon: pos++; return token = SyntaxKind.SemicolonToken; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index b5cd1d9e322..828c5744bbd 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -420,7 +420,7 @@ namespace ts { host.getCanonicalFileName, /*isAbsolutePathAnUrl*/ true); - sourceMapSourceIndex = indexOf(sourceMapData.sourceMapSources, source); + sourceMapSourceIndex = sourceMapData.sourceMapSources.indexOf(source); if (sourceMapSourceIndex === -1) { sourceMapSourceIndex = sourceMapData.sourceMapSources.length; sourceMapData.sourceMapSources.push(source); diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 5b4c12b4f23..5d46c8430d2 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -968,7 +968,7 @@ namespace ts { name: "typescript:asyncValues", scoped: false, text: ` - var __asyncValues = (this && this.__asyncIterator) || function (o) { + var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator]; return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index bd2a4ef554d..8df05e4f3ab 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -3190,8 +3190,8 @@ namespace ts { // `throw` methods that step through the generator when invoked. // // parameters: - // thisArg The value to use as the `this` binding for the transformed generator body. - // body A function that acts as the transformed generator body. + // @param thisArg The value to use as the `this` binding for the transformed generator body. + // @param body A function that acts as the transformed generator body. // // variables: // _ Persistent state for the generator that is shared between the helper and the diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 3c1bba280f0..cdb0d0667ad 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -81,7 +81,7 @@ namespace ts { let classAliases: Identifier[]; /** - * Keeps track of whether we are within any containing namespaces when performing + * Keeps track of whether we are within any containing namespaces when performing * just-in-time substitution while printing an expression identifier. */ let applicableSubstitutions: TypeScriptSubstitutionFlags; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index ec869f89ce2..26023e14a8b 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -158,8 +158,12 @@ namespace ts { }; } + function createWatchStatusReporter(options: CompilerOptions) { + return ts.createWatchStatusReporter(sys, !!options.pretty); + } + function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { - const watchCompilerHost = ts.createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, reportDiagnostic); + const watchCompilerHost = ts.createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, reportDiagnostic, createWatchStatusReporter(configParseResult.options)); updateWatchCompilationHost(watchCompilerHost); watchCompilerHost.rootFiles = configParseResult.fileNames; watchCompilerHost.options = configParseResult.options; @@ -169,7 +173,7 @@ namespace ts { } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { - const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, reportDiagnostic); + const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, reportDiagnostic, createWatchStatusReporter(options)); updateWatchCompilationHost(watchCompilerHost); createWatchProgram(watchCompilerHost); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5c5d7e4c775..14789d23eb9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -438,24 +438,24 @@ namespace ts { } export const enum NodeFlags { - None = 0, - Let = 1 << 0, // Variable declaration - Const = 1 << 1, // Variable declaration - NestedNamespace = 1 << 2, // Namespace declaration - Synthesized = 1 << 3, // Node was synthesized during transformation - Namespace = 1 << 4, // Namespace declaration - ExportContext = 1 << 5, // Export context (initialized by binding) - ContainsThis = 1 << 6, // Interface contains references to "this" - HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding) - HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding) + None = 0, + Let = 1 << 0, // Variable declaration + Const = 1 << 1, // Variable declaration + NestedNamespace = 1 << 2, // Namespace declaration + Synthesized = 1 << 3, // Node was synthesized during transformation + Namespace = 1 << 4, // Namespace declaration + ExportContext = 1 << 5, // Export context (initialized by binding) + ContainsThis = 1 << 6, // Interface contains references to "this" + HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding) + HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding) GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope - HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding) - DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed - YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator - DecoratorContext = 1 << 13, // If node was parsed as part of a decorator - AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function - ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript + HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding) + DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed + YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator + DecoratorContext = 1 << 13, // If node was parsed as part of a decorator + AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function + ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node @@ -584,6 +584,40 @@ namespace ts { | JSDocFunctionType | EndOfFileToken; + export type HasType = + | SignatureDeclaration + | VariableDeclaration + | ParameterDeclaration + | PropertySignature + | PropertyDeclaration + | TypePredicateNode + | ParenthesizedTypeNode + | TypeOperatorNode + | MappedTypeNode + | AssertionExpression + | TypeAliasDeclaration + | JSDocTypeExpression + | JSDocNonNullableType + | JSDocNullableType + | JSDocOptionalType + | JSDocVariadicType; + + export type HasInitializer = + | HasExpressionInitializer + | ForStatement + | ForInStatement + | ForOfStatement + | JsxAttribute; + + export type HasExpressionInitializer = + | VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertySignature + | PropertyDeclaration + | PropertyAssignment + | EnumMember; + /* @internal */ export type MutableNodeArray = NodeArray & T[]; @@ -793,6 +827,9 @@ namespace ts { initializer?: Expression; // Optional initializer } + /*@internal*/ + export type BindingElementGrandparent = BindingElement["parent"]["parent"]; + export interface PropertySignature extends TypeElement, JSDocContainer { kind: SyntaxKind.PropertySignature; name: PropertyName; // Declared property name @@ -848,25 +885,18 @@ namespace ts { expression: Expression; } - // SyntaxKind.VariableDeclaration - // SyntaxKind.Parameter - // SyntaxKind.BindingElement - // SyntaxKind.Property - // SyntaxKind.PropertyAssignment - // SyntaxKind.JsxAttribute - // SyntaxKind.ShorthandPropertyAssignment - // SyntaxKind.EnumMember - // SyntaxKind.JSDocPropertyTag - // SyntaxKind.JSDocParameterTag - export interface VariableLikeDeclaration extends NamedDeclaration { - propertyName?: PropertyName; - dotDotDotToken?: DotDotDotToken; - name: DeclarationName; - questionToken?: QuestionToken; - exclamationToken?: ExclamationToken; - type?: TypeNode; - initializer?: Expression; - } + export type VariableLikeDeclaration = + | VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertyDeclaration + | PropertyAssignment + | PropertySignature + | JsxAttribute + | ShorthandPropertyAssignment + | EnumMember + | JSDocPropertyTag + | JSDocParameterTag; export interface PropertyLikeDeclaration extends NamedDeclaration { name: PropertyName; @@ -1364,7 +1394,7 @@ namespace ts { export type BinaryOperatorToken = Token; - export interface BinaryExpression extends Expression, Declaration { + export interface BinaryExpression extends Expression, Declaration { kind: SyntaxKind.BinaryExpression; left: Expression; operatorToken: BinaryOperatorToken; @@ -1490,8 +1520,9 @@ namespace ts { HexSpecifier = 1 << 6, // e.g. `0x00000000` BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000` OctalSpecifier = 1 << 8, // e.g. `0o777` + ContainsSeparator = 1 << 9, // e.g. `0b1100_0101` BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, - NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinarySpecifier | OctalSpecifier + NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinarySpecifier | OctalSpecifier | ContainsSeparator } export interface NumericLiteral extends LiteralExpression { @@ -2509,7 +2540,7 @@ namespace ts { export interface ParseConfigHost { useCaseSensitiveFileNames: boolean; - readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number): string[]; + readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; /** * Gets a value indicating whether the specified path exists and is a file. @@ -2738,6 +2769,8 @@ namespace ts { getAugmentedPropertiesOfType(type: Type): Symbol[]; getRootSymbols(symbol: Symbol): Symbol[]; getContextualType(node: Expression): Type | undefined; + /* @internal */ isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean; + /** * returns unknownSignature in the case of an error. * @param argumentCount Apparent number of arguments, passed in case of a possibly incomplete call. This should come from an ArgumentListInfo. See `signatureHelp.ts`. @@ -2752,6 +2785,8 @@ namespace ts { getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; + /** Exclude accesses to private properties or methods with a `this` parameter that `type` doesn't satisfy. */ + /* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression, type: Type, property: Symbol): boolean; /** Follow all aliases to get the original symbol. */ getAliasedSymbol(symbol: Symbol): Symbol; /** Follow a *single* alias to get the immediately aliased symbol. */ @@ -2786,7 +2821,7 @@ namespace ts { /* @internal */ getNullType(): Type; /* @internal */ getESSymbolType(): Type; /* @internal */ getNeverType(): Type; - /* @internal */ getUnionType(types: Type[], subtypeReduction?: boolean): Type; + /* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type; /* @internal */ createArrayType(elementType: Type): Type; /* @internal */ createPromiseType(type: Type): Type; @@ -2841,6 +2876,13 @@ namespace ts { /* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined; } + /* @internal */ + export const enum UnionReduction { + None = 0, + Literal, + Subtype + } + export enum NodeBuilderFlags { None = 0, // Options @@ -2935,9 +2977,9 @@ namespace ts { None = 0x00000000, // Write symbols's type argument if it is instantiated symbol - // eg. class C { p: T } <-- Show p as C.p here + // eg. class C { p: T } <-- Show p as C.p here // var a: C; - // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p + // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p WriteTypeParametersOrArguments = 0x00000001, // Use only external alias information to get the symbol name in the given context @@ -3209,6 +3251,7 @@ namespace ts { ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s) ContainsStatic = 1 << 9, // Synthetic property with static constituent(s) Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name + ReverseMapped = 1 << 11, // property of reverse-inferred homomorphic mapped type. Synthetic = SyntheticProperty | SyntheticMethod } @@ -3218,6 +3261,12 @@ namespace ts { isRestParameter?: boolean; } + /* @internal */ + export interface ReverseMappedSymbol extends TransientSymbol { + propertyType: Type; + mappedType: MappedType; + } + export const enum InternalSymbolName { Call = "__call", // Call signatures Constructor = "__constructor", // Constructor implementations @@ -3323,7 +3372,7 @@ namespace ts { resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. - superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing + superCall?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing switchTypes?: Type[]; // Cached array of switch case expression types } @@ -3452,6 +3501,7 @@ namespace ts { EvolvingArray = 1 << 8, // Evolving array type ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties ContainsSpread = 1 << 10, // Object literal contains spread operation + ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type ClassOrInterface = Class | Interface } @@ -3559,6 +3609,12 @@ namespace ts { finalArrayType?: Type; // Final array type of evolving array type } + /* @internal */ + export interface ReverseMappedType extends ObjectType { + source: Type; + mappedType: MappedType; + } + /* @internal */ // Resolved object, union, or intersection type export interface ResolvedType extends ObjectType, UnionOrIntersectionType { @@ -3572,7 +3628,7 @@ namespace ts { /* @internal */ // Object literals are initially marked fresh. Freshness disappears following an assignment, - // before a type assertion, or when an object literal's type is widened. The regular + // before a type assertion, or when an object literal's type is widened. The regular // version of a fresh type is identical except for the TypeFlags.FreshObjectLiteral flag. export interface FreshObjectLiteralType extends ResolvedType { regularType: ResolvedType; // Regular version of fresh type @@ -4324,11 +4380,12 @@ namespace ts { * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just * 'throw new Error("NotImplemented")' */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ - resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; + getEnvironmentVariable?(name: string): string; /* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void; /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ba382bfeeb7..07d861bc8bd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3,6 +3,7 @@ /* @internal */ namespace ts { export const emptyArray: never[] = [] as never[]; + export const resolvingEmptyArray: never[] = [] as never[]; export const emptyMap: ReadonlyMap = createMap(); export const externalHelpersModuleNameText = "tslib"; @@ -346,7 +347,7 @@ namespace ts { export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile) { // If we don't need to downlevel and we can reach the original source text using // the node's parent reference, then simply get the text as it was originally written. - if (!nodeIsSynthesized(node) && node.parent) { + if (!nodeIsSynthesized(node) && node.parent && !(isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator)) { return getSourceTextOfNodeFromSourceFile(sourceFile, node); } @@ -548,7 +549,7 @@ namespace ts { // Return display name of an identifier // Computed property names will just be emitted as "[]", where is the source // text of the expression in the computed property. - export function declarationNameToString(name: DeclarationName) { + export function declarationNameToString(name: DeclarationName | QualifiedName) { return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } @@ -784,7 +785,7 @@ namespace ts { case SyntaxKind.PropertySignature: case SyntaxKind.Parameter: case SyntaxKind.VariableDeclaration: - return node === (parent).type; + return node === (parent as HasType).type; case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: @@ -802,7 +803,7 @@ namespace ts { return node === (parent).type; case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: - return (parent).typeArguments && indexOf((parent).typeArguments, node) >= 0; + return contains((parent).typeArguments, node); case SyntaxKind.TaggedTemplateExpression: // TODO (drosen): TaggedTemplateExpressions may eventually support type arguments. return false; @@ -1340,7 +1341,7 @@ namespace ts { case SyntaxKind.EnumMember: case SyntaxKind.PropertyAssignment: case SyntaxKind.BindingElement: - return (parent).initializer === node; + return (parent as HasInitializer).initializer === node; case SyntaxKind.ExpressionStatement: case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: @@ -1650,7 +1651,7 @@ namespace ts { result = addRange(result, getJSDocParameterTags(node as ParameterDeclaration)); } - if (isVariableLike(node) && node.initializer && hasJSDocNodes(node.initializer)) { + if (isVariableLike(node) && hasInitializer(node) && hasJSDocNodes(node.initializer)) { result = addRange(result, node.initializer.jsDoc); } @@ -2816,8 +2817,8 @@ namespace ts { * Gets the effective type annotation of a variable, parameter, or property. If the node was * parsed in a JavaScript file, gets the type annotation from JSDoc. */ - export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration, checkJSDoc?: boolean): TypeNode | undefined { - if (node.type) { + export function getEffectiveTypeAnnotationNode(node: Node, checkJSDoc?: boolean): TypeNode | undefined { + if (hasType(node)) { return node.type; } if (checkJSDoc || isInJavaScriptFile(node)) { @@ -3916,8 +3917,8 @@ namespace ts { // // { // oldStart3: Min(oldStart1, oldStart2), - // oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)), - // newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)) + // oldEnd3: Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)), + // newEnd3: Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)) // } const oldStart1 = oldStartN; @@ -4402,7 +4403,7 @@ namespace ts { return node.kind === SyntaxKind.RegularExpressionLiteral; } - export function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression { + export function isNoSubstitutionTemplateLiteral(node: Node): node is NoSubstitutionTemplateLiteral { return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; } @@ -5233,6 +5234,18 @@ namespace ts { return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor); } + /* @internal */ + export function isMethodOrAccessor(node: Node): node is MethodDeclaration | AccessorDeclaration { + switch (node.kind) { + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return true; + default: + return false; + } + } + // Type members export function isTypeElement(node: Node): node is TypeElement { @@ -5812,4 +5825,22 @@ namespace ts { export function hasJSDocNodes(node: Node): node is HasJSDoc { return !!(node as JSDocContainer).jsDoc && (node as JSDocContainer).jsDoc.length > 0; } + + /** True if has type node attached to it. */ + /* @internal */ + export function hasType(node: Node): node is HasType { + return !!(node as HasType).type; + } + + /** True if has initializer node attached to it. */ + /* @internal */ + export function hasInitializer(node: Node): node is HasInitializer { + return !!(node as HasInitializer).initializer; + } + + /** True if has initializer node attached to it. */ + /* @internal */ + export function hasOnlyExpressionInitializer(node: Node): node is HasExpressionInitializer { + return hasInitializer(node) && !isForStatement(node) && !isForInStatement(node) && !isForOfStatement(node) && !isJsxAttribute(node); + } } diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 762487dc0c7..a8e7a3d3dc4 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -31,6 +31,31 @@ namespace ts { }; } + function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic) { + if (system.clearScreen && diagnostic.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) { + system.clearScreen(); + } + } + + /** + * Create a function that reports watch status by writing to the system and handles the formating of the diagnostic + */ + export function createWatchStatusReporter(system: System, pretty?: boolean): WatchStatusReporter { + return pretty ? + (diagnostic: Diagnostic, newLine: string) => { + clearScreenIfNotWatchingForFileChanges(system, diagnostic); + let output = `[${ formatColorAndReset(new Date().toLocaleTimeString(), ForegroundColorEscapeSequences.Grey) }] `; + output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine + newLine}`; + system.write(output); + } : + (diagnostic: Diagnostic, newLine: string) => { + clearScreenIfNotWatchingForFileChanges(system, diagnostic); + let output = new Date().toLocaleTimeString() + " - "; + output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine + newLine}`; + system.write(output); + }; + } + /** * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors */ @@ -151,7 +176,7 @@ namespace ts { /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ - function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter): WatchCompilerHost { + function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost { let host: DirectoryStructureHost = system; const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames; const writeFileName = (s: string) => system.write(s + system.newLine); @@ -173,12 +198,13 @@ namespace ts { getDirectories: path => system.getDirectories(path), readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth), realpath: system.realpath && (path => system.realpath(path)), + getEnvironmentVariable: system.getEnvironmentVariable && (name => system.getEnvironmentVariable(name)), watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile(path, callback, pollingInterval)) : () => noopFileWatcher, watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory(path, callback, recursive)) : () => noopFileWatcher, setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout.call(system, callback, ms, ...args)) : noop, clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout(timeoutId)) : noop, trace: s => system.write(s), - onWatchStatusChange, + onWatchStatusChange: reportWatchStatus || createWatchStatusReporter(system), createDirectory: path => system.createDirectory(path), writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), onCachedDirectoryStructureHostCreate: cacheHost => host = cacheHost || system, @@ -189,13 +215,6 @@ namespace ts { return getDirectoryPath(normalizePath(system.getExecutingFilePath())); } - function onWatchStatusChange(diagnostic: Diagnostic, newLine: string) { - if (system.clearScreen && diagnostic.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) { - system.clearScreen(); - } - system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`); - } - function emitFilesAndReportErrorUsingBuilder(program: Program) { builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, builderProgramHost, builderProgram); emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName); @@ -238,9 +257,9 @@ namespace ts { /** * Creates the watch compiler host from system for config file in watch mode */ - export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfConfigFile { + export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter | undefined, reportWatchStatus: WatchStatusReporter | undefined): WatchCompilerHostOfConfigFile { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); - const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfConfigFile; + const host = createWatchCompilerHost(system, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile; host.onConfigFileDiagnostic = reportDiagnostic; host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); host.configFileName = configFileName; @@ -251,8 +270,8 @@ namespace ts { /** * Creates the watch compiler host from system for compiling root files and options in watch mode */ - export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, reportDiagnostic || createDiagnosticReporter(system)) as WatchCompilerHostOfFilesAndCompilerOptions; + export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined, reportWatchStatus: WatchStatusReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { + const host = createWatchCompilerHost(system, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions; host.rootFiles = rootFiles; host.options = options; return host; @@ -261,6 +280,7 @@ namespace ts { namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; + export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string) => void; export interface WatchCompilerHost { /** If provided, callback to invoke before each program creation */ @@ -299,11 +319,13 @@ namespace ts { realpath?(path: string): string; /** If provided would be used to write log about compilation */ trace?(s: string): void; + /** If provided is used to get the environment variable */ + getEnvironmentVariable?(name: string): string; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ - resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; /** Used to watch changes in source files, missing files needed to update the program or config file */ watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; @@ -403,15 +425,15 @@ namespace ts { /** * Create the watched program for config file */ - export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { - return createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend, system, reportDiagnostic)); + export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfConfigFile { + return createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend, system, reportDiagnostic, reportWatchStatus)); } /** * Create the watched program for root files and compiler options */ - export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { - return createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system, reportDiagnostic)); + export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfFilesAndCompilerOptions { + return createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system, reportDiagnostic, reportWatchStatus)); } /** @@ -498,6 +520,7 @@ namespace ts { directoryExists: directoryStructureHost.directoryExists && (path => directoryStructureHost.directoryExists(path)), getDirectories: directoryStructureHost.getDirectories && (path => directoryStructureHost.getDirectories(path)), realpath: host.realpath && (s => host.realpath(s)), + getEnvironmentVariable: host.getEnvironmentVariable ? (name => host.getEnvironmentVariable(name)) : (() => ""), onReleaseOldSourceFile, // Members for ResolutionCacheHost toPath, diff --git a/src/harness/externalCompileRunner.ts b/src/harness/externalCompileRunner.ts index 2a44badd813..c5e1c6ea9ff 100644 --- a/src/harness/externalCompileRunner.ts +++ b/src/harness/externalCompileRunner.ts @@ -42,21 +42,29 @@ abstract class ExternalCompileRunnerBase extends RunnerBase { const stdio = isWorker ? "pipe" : "inherit"; let types: string[]; if (fs.existsSync(path.join(cwd, "test.json"))) { - const update = cp.spawnSync("git", ["submodule", "update", "--remote"], { cwd, timeout, shell: true, stdio }); - if (update.status !== 0) throw new Error(`git submodule update for ${directoryName} failed!`); + const submoduleDir = path.join(cwd, directoryName); + const reset = cp.spawnSync("git", ["reset", "HEAD", "--hard"], { cwd: submoduleDir, timeout, shell: true, stdio }); + if (reset.status !== 0) throw new Error(`git reset for ${directoryName} failed: ${reset.stderr.toString()}`); + const clean = cp.spawnSync("git", ["clean", "-f"], { cwd: submoduleDir, timeout, shell: true, stdio }); + if (clean.status !== 0) throw new Error(`git clean for ${directoryName} failed: ${clean.stderr.toString()}`); + const update = cp.spawnSync("git", ["submodule", "update", "--remote", "."], { cwd: submoduleDir, timeout, shell: true, stdio }); + if (update.status !== 0) throw new Error(`git submodule update for ${directoryName} failed: ${update.stderr.toString()}`); const config = JSON.parse(fs.readFileSync(path.join(cwd, "test.json"), { encoding: "utf8" })) as UserConfig; ts.Debug.assert(!!config.types, "Bad format from test.json: Types field must be present."); types = config.types; - cwd = path.join(cwd, directoryName); + cwd = submoduleDir; } if (fs.existsSync(path.join(cwd, "package.json"))) { if (fs.existsSync(path.join(cwd, "package-lock.json"))) { fs.unlinkSync(path.join(cwd, "package-lock.json")); } + if (fs.existsSync(path.join(cwd, "node_modules"))) { + require("del").sync(path.join(cwd, "node_modules")); + } const install = cp.spawnSync(`npm`, ["i"], { cwd, timeout, shell: true, stdio }); - if (install.status !== 0) throw new Error(`NPM Install for ${directoryName} failed!`); + if (install.status !== 0) throw new Error(`NPM Install for ${directoryName} failed: ${install.stderr.toString()}`); } const args = [path.join(__dirname, "tsc.js")]; if (types) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 0d5216d62b5..12b98eebcca 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -297,7 +297,7 @@ namespace FourSlash { const host = new Utils.MockParseConfigHost(baseDir, /*ignoreCase*/ false, this.inputFiles); const configJsonObj = ts.parseConfigFileTextToJson(configFileName, this.inputFiles.get(configFileName)); - assert(configJsonObj.config !== undefined); + assert.isTrue(configJsonObj.config !== undefined); const { options, errors } = ts.parseJsonConfigFileContent(configJsonObj.config, host, baseDir); @@ -441,18 +441,17 @@ namespace FourSlash { this.goToPosition(marker.position); } - public goToEachMarker(action: () => void) { - const markers = this.getMarkers(); - assert(markers.length !== 0); - for (const marker of markers) { - this.goToMarker(marker); - action(); + public goToEachMarker(markers: ReadonlyArray, action: (marker: FourSlash.Marker, index: number) => void) { + assert(markers.length); + for (let i = 0; i < markers.length; i++) { + this.goToMarker(markers[i]); + action(markers[i], i); } } public goToEachRange(action: () => void) { const ranges = this.getRanges(); - assert(ranges.length !== 0); + assert(ranges.length); for (const range of ranges) { this.goToRangeStart(range); action(); @@ -799,7 +798,7 @@ namespace FourSlash { } const entries = this.getCompletionListAtCaret().entries; - assert(items.length <= entries.length, `Amount of expected items in completion list [ ${items.length} ] is greater than actual number of items in list [ ${entries.length} ]`); + assert.isTrue(items.length <= entries.length, `Amount of expected items in completion list [ ${items.length} ] is greater than actual number of items in list [ ${entries.length} ]`); ts.zipWith(entries, items, (entry, item) => { assert.equal(entry.name, item, `Unexpected item in completion list`); }); @@ -873,7 +872,7 @@ namespace FourSlash { }); } - public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean, options?: FourSlashInterface.VerifyCompletionListContainsOptions) { + public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, options?: FourSlashInterface.VerifyCompletionListContainsOptions) { const completions = this.getCompletionListAtCaret(options); if (completions) { this.assertItemInCompletionList(completions.entries, entryId, text, documentation, kind, spanIndex, hasAction, options); @@ -894,7 +893,7 @@ namespace FourSlash { * @param expectedKind the kind of symbol (see ScriptElementKind) * @param spanIndex the index of the range that the completion item's replacement text span should match */ - public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number, options?: FourSlashInterface.CompletionsAtOptions) { + public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, options?: FourSlashInterface.CompletionsAtOptions) { let replacementSpan: ts.TextSpan; if (spanIndex !== undefined) { replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex); @@ -903,7 +902,7 @@ namespace FourSlash { const completions = this.getCompletionListAtCaret(options); if (completions) { let filterCompletions = completions.entries.filter(e => e.name === entryId.name && e.source === entryId.source); - filterCompletions = expectedKind ? filterCompletions.filter(e => e.kind === expectedKind) : filterCompletions; + filterCompletions = expectedKind ? filterCompletions.filter(e => e.kind === expectedKind || (typeof expectedKind === "object" && e.kind === expectedKind.kind)) : filterCompletions; filterCompletions = filterCompletions.filter(entry => { const details = this.getCompletionEntryDetails(entry.name); const documentation = details && ts.displayPartsToString(details.documentation); @@ -953,7 +952,7 @@ namespace FourSlash { public verifyCompletionEntryDetails(entryName: string, expectedText: string, expectedDocumentation?: string, kind?: string, tags?: ts.JSDocTagInfo[]) { const details = this.getCompletionEntryDetails(entryName); - assert.isDefined(details, "no completion entry available"); + assert(details, "no completion entry available"); assert.equal(ts.displayPartsToString(details.displayParts), expectedText, this.assertionMessageAtLastKnownMarker("completion entry details text")); @@ -1088,7 +1087,7 @@ namespace FourSlash { public verifyRangesReferenceEachOther(ranges?: Range[]) { ranges = ranges || this.getRanges(); - assert(ranges.length !== 0); + assert(ranges.length); for (const range of ranges) { this.verifyReferencesOf(range, ranges); } @@ -1374,6 +1373,7 @@ Actual: ${stringify(fullActual)}`); public verifyCurrentParameterIsVariable(isVariable: boolean) { const signature = this.getActiveSignatureHelpItem(); + assert.isOk(signature); assert.equal(isVariable, signature.isVariadic); } @@ -2024,7 +2024,7 @@ Actual: ${stringify(fullActual)}`); const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition); if (negative) { - assert(implementations && implementations.length > 0, "Expected at least one implementation but got 0"); + assert.isTrue(implementations && implementations.length > 0, "Expected at least one implementation but got 0"); } else { assert.isUndefined(implementations, "Expected implementation list to be empty but implementations returned"); @@ -3089,7 +3089,7 @@ Actual: ${stringify(fullActual)}`); entryId: ts.Completions.CompletionEntryIdentifier, text: string | undefined, documentation: string | undefined, - kind: string | undefined, + kind: string | undefined | { kind?: string, kindModifiers?: string }, spanIndex: number | undefined, hasAction: boolean | undefined, options: FourSlashInterface.VerifyCompletionListContainsOptions | undefined, @@ -3123,12 +3123,24 @@ Actual: ${stringify(fullActual)}`); } if (kind !== undefined) { - assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId)); + if (typeof kind === "string") { + assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId)); + } + else { + if (kind.kind) { + assert.equal(item.kind, kind.kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId)); + } + if (kind.kindModifiers !== undefined) { + assert.equal(item.kindModifiers, kind.kindModifiers, this.assertionMessageAtLastKnownMarker("completion item kindModifiers for " + entryId)); + } + } } + + if (spanIndex !== undefined) { const span = this.getTextSpanForRangeAtIndex(spanIndex); - assert(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + entryId)); + assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + entryId)); } assert.equal(item.hasAction, hasAction); @@ -3763,8 +3775,11 @@ namespace FourSlashInterface { this.state.goToMarker(name); } - public eachMarker(action: () => void) { - this.state.goToEachMarker(action); + public eachMarker(markers: ReadonlyArray, action: (marker: FourSlash.Marker, index: number) => void): void; + public eachMarker(action: (marker: FourSlash.Marker, index: number) => void): void; + public eachMarker(a: ReadonlyArray | ((marker: FourSlash.Marker, index: number) => void), b?: (marker: FourSlash.Marker, index: number) => void): void { + const markers = typeof a === "function" ? this.state.getMarkers() : a.map(m => this.state.getMarkerByName(m)); + this.state.goToEachMarker(markers, typeof a === "function" ? a : b); } public rangeStart(range: FourSlash.Range) { @@ -3839,7 +3854,7 @@ namespace FourSlashInterface { // Verifies the completion list contains the specified symbol. The // completion list is brought up if necessary - public completionListContains(entryId: string | ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean, options?: VerifyCompletionListContainsOptions) { + public completionListContains(entryId: string | ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, options?: VerifyCompletionListContainsOptions) { if (typeof entryId === "string") { entryId = { name: entryId, source: undefined }; } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 8f56a239943..612224cb312 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -23,60 +23,39 @@ /// /// /// +/// + // Block scoped definitions work poorly for global variables, temporarily enable var /* tslint:disable:no-var-keyword */ -function assert(expr: boolean, msg?: string | (() => string)): void { - if (!expr) { - throw new Error(typeof msg === "string" ? msg : msg()); - } -} -namespace assert { - export function isFalse(expr: boolean, msg = "Expected value to be false."): void { - assert(!expr, msg); - } - export function equal(a: T, b: T, msg?: string): void { - assert(a === b, msg || (() => `Expected to be equal:\nExpected:\n${JSON.stringify(a)}\nActual:\n${JSON.stringify(b)}`)); - } - export function notEqual(a: T, b: T, msg = "Expected values to not be equal."): void { - assert(a !== b, msg); - } - export function isDefined(x: {} | null | undefined, msg = "Expected value to be defined."): void { - assert(x !== undefined && x !== null, msg); - } - export function isUndefined(x: {} | null | undefined, msg = "Expected value to be undefined."): void { - assert(x === undefined, msg); - } - export function deepEqual(a: T, b: T, msg?: string): void { - assert(isDeepEqual(a, b), msg || (() => `Expected values to be deeply equal:\nExpected:\n${JSON.stringify(a, undefined, 4)}\nActual:\n${JSON.stringify(b, undefined, 4)}`)); - } - export function lengthOf(a: ReadonlyArray<{}>, length: number, msg = "Expected length to match."): void { - assert(a.length === length, msg); - } - export function throws(cb: () => void, msg = "Expected callback to throw"): void { - let threw = false; - try { - cb(); - } - catch { - threw = true; - } - assert(threw, msg); - } +// this will work in the browser via browserify +var _chai: typeof chai = require("chai"); +var assert: typeof _chai.assert = _chai.assert; +{ + // chai's builtin `assert.isFalse` is featureful but slow - we don't use those features, + // so we'll just overwrite it as an alterative to migrating a bunch of code off of chai + assert.isFalse = (expr, msg) => { if (expr as any as boolean !== false) throw new Error(msg); }; - function isDeepEqual(a: T, b: T): boolean { - if (a === b) { - return true; + const assertDeepImpl = assert.deepEqual; + assert.deepEqual = (a, b, msg) => { + if (ts.isArray(a) && ts.isArray(b)) { + assertDeepImpl(arrayExtraKeysObject(a), arrayExtraKeysObject(b), "Array extra keys differ"); } - if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) { - return false; + assertDeepImpl(a, b, msg); + + function arrayExtraKeysObject(a: ReadonlyArray<{} | null | undefined>): object { + const obj: { [key: string]: {} | null | undefined } = {}; + for (const key in a) { + if (Number.isNaN(Number(key))) { + obj[key] = a[key]; + } + } + return obj; } - const aKeys = Object.keys(a).sort(); - const bKeys = Object.keys(b).sort(); - return aKeys.length === bKeys.length && aKeys.every((key, i) => bKeys[i] === key && isDeepEqual((a as any)[key], (b as any)[key])); - } + }; } + declare var __dirname: string; // Node-specific var global: NodeJS.Global = Function("return this").call(undefined); @@ -84,7 +63,7 @@ declare var window: {}; declare var XMLHttpRequest: { new(): XMLHttpRequest; }; -interface XMLHttpRequest { +interface XMLHttpRequest { readonly readyState: number; readonly responseText: string; readonly status: number; @@ -389,8 +368,8 @@ namespace Utils { return; } - assert(!!array1, "array1"); - assert(!!array2, "array2"); + assert(array1, "array1"); + assert(array2, "array2"); assert.equal(array1.length, array2.length, "array1.length !== array2.length"); @@ -413,8 +392,8 @@ namespace Utils { return; } - assert(!!node1, "node1"); - assert(!!node2, "node2"); + assert(node1, "node1"); + assert(node2, "node2"); assert.equal(node1.pos, node2.pos, "node1.pos !== node2.pos"); assert.equal(node1.end, node2.end, "node1.end !== node2.end"); assert.equal(node1.kind, node2.kind, "node1.kind !== node2.kind"); @@ -444,8 +423,8 @@ namespace Utils { return; } - assert(!!array1, "array1"); - assert(!!array2, "array2"); + assert(array1, "array1"); + assert(array2, "array2"); assert.equal(array1.pos, array2.pos, "array1.pos !== array2.pos"); assert.equal(array1.end, array2.end, "array1.end !== array2.end"); assert.equal(array1.length, array2.length, "array1.length !== array2.length"); @@ -895,7 +874,7 @@ namespace Harness { // Cache of lib files from "built/local" let libFileNameSourceFileMap: ts.Map | undefined; - // Cache of lib files from "tests/lib/" + // Cache of lib files from "tests/lib/" const testLibFileNameSourceFileMap = ts.createMap(); const es6TestLibFileNameSourceFileMap = ts.createMap(); @@ -1301,7 +1280,7 @@ namespace Harness { function findResultCodeFile(fileName: string) { const sourceFile = result.program.getSourceFile(fileName); - assert.isDefined(sourceFile, "Program has no source file with name '" + fileName + "'"); + assert(sourceFile, "Program has no source file with name '" + fileName + "'"); // Is this file going to be emitted separately let sourceFileName: string; const outFile = options.outFile || options.out; @@ -1984,7 +1963,7 @@ namespace Harness { const data = testUnitData[i]; if (ts.getBaseFileName(data.name).toLowerCase() === "tsconfig.json") { const configJson = ts.parseJsonText(data.name, data.content); - assert(configJson.endOfFileToken !== undefined); + assert.isTrue(configJson.endOfFileToken !== undefined); let baseDir = ts.normalizePath(ts.getDirectoryPath(data.name)); if (rootDir) { baseDir = ts.getNormalizedAbsolutePath(baseDir, rootDir); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 393b8b95081..d51517c159b 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -176,7 +176,8 @@ namespace Harness.LanguageService { */ public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { const script: ScriptInfo = this.getScriptInfo(fileName); - assert(!!script); + assert.isOk(script); + return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position); } } @@ -378,7 +379,7 @@ namespace Harness.LanguageService { classification: parseInt(result[i + 1]) }; - assert(t.length > 0, "Result length should be greater than 0, got :" + t.length); + assert.isTrue(t.length > 0, "Result length should be greater than 0, got :" + t.length); position += t.length; } const finalLexState = parseInt(result[result.length - 1]); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index c617cc7a0a3..f8d4d171f01 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -186,7 +186,7 @@ class ProjectRunner extends RunnerBase { getCanonicalFileName: Harness.Compiler.getCanonicalFileName, useCaseSensitiveFileNames: () => Harness.IO.useCaseSensitiveFileNames(), getNewLine: () => Harness.IO.newLine(), - fileExists: fileName => fileName === Harness.Compiler.defaultLibFileName || getSourceFileText(fileName) !== undefined, + fileExists: fileName => fileName === Harness.Compiler.defaultLibFileName || getSourceFileText(fileName) !== undefined, readFile: fileName => Harness.IO.readFile(fileName), getDirectories: path => Harness.IO.getDirectories(path) }; diff --git a/src/harness/sourceMapRecorder.ts b/src/harness/sourceMapRecorder.ts index 5ab8407067e..606d4e67acd 100644 --- a/src/harness/sourceMapRecorder.ts +++ b/src/harness/sourceMapRecorder.ts @@ -285,10 +285,10 @@ namespace Harness.SourceMapRecorder { } export function recordNewSourceFileSpan(sourceMapSpan: ts.SourceMapSpan, newSourceFileCode: string) { - assert(spansOnSingleLine.length === 0 || spansOnSingleLine[0].sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine, "new file source map span should be on new line. We currently handle only that scenario"); + assert.isTrue(spansOnSingleLine.length === 0 || spansOnSingleLine[0].sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine, "new file source map span should be on new line. We currently handle only that scenario"); recordSourceMapSpan(sourceMapSpan); - assert(spansOnSingleLine.length === 1); + assert.isTrue(spansOnSingleLine.length === 1); sourceMapRecorder.WriteLine("-------------------------------------------------------------------"); sourceMapRecorder.WriteLine("emittedFile:" + jsFile.fileName); sourceMapRecorder.WriteLine("sourceFile:" + sourceMapSources[spansOnSingleLine[0].sourceMapSpan.sourceIndex]); @@ -331,7 +331,7 @@ namespace Harness.SourceMapRecorder { function getMarkerId(markerIndex: number) { let markerId = ""; if (spanMarkerContinues) { - assert(markerIndex === 0); + assert.isTrue(markerIndex === 0); markerId = "1->"; } else { diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 7353f3f7efc..25642ab5179 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -5,7 +5,7 @@ "outFile": "../../built/local/run.js", "declaration": false, "types": [ - "node", "mocha" + "node", "mocha", "chai" ], "lib": [ "es6", @@ -108,6 +108,7 @@ "./unittests/reuseProgramStructure.ts", "./unittests/moduleResolution.ts", "./unittests/tsconfigParsing.ts", + "./unittests/asserts.ts", "./unittests/builder.ts", "./unittests/commandLineParsing.ts", "./unittests/configurationExtension.ts", diff --git a/src/harness/unittests/asserts.ts b/src/harness/unittests/asserts.ts new file mode 100644 index 00000000000..ec274c44c22 --- /dev/null +++ b/src/harness/unittests/asserts.ts @@ -0,0 +1,11 @@ +/// + +namespace ts { + describe("assert", () => { + it("deepEqual", () => { + assert.throws(() => assert.deepEqual(createNodeArray([createIdentifier("A")]), createNodeArray([createIdentifier("B")]))); + assert.throws(() => assert.deepEqual(createNodeArray([], /*hasTrailingComma*/ true), createNodeArray([], /*hasTrailingComma*/ false))); + assert.deepEqual(createNodeArray([createIdentifier("A")], /*hasTrailingComma*/ true), createNodeArray([createIdentifier("A")], /*hasTrailingComma*/ true)); + }); + }); +} diff --git a/src/harness/unittests/commandLineParsing.ts b/src/harness/unittests/commandLineParsing.ts index 5f0cc2acc78..8cfd4ad406c 100644 --- a/src/harness/unittests/commandLineParsing.ts +++ b/src/harness/unittests/commandLineParsing.ts @@ -12,7 +12,7 @@ namespace ts { const parsedErrors = parsed.errors; const expectedErrors = expectedParsedCommandLine.errors; - assert(parsedErrors.length === expectedErrors.length, `Expected error: ${JSON.stringify(expectedErrors)}. Actual error: ${JSON.stringify(parsedErrors)}.`); + assert.isTrue(parsedErrors.length === expectedErrors.length, `Expected error: ${JSON.stringify(expectedErrors)}. Actual error: ${JSON.stringify(parsedErrors)}.`); for (let i = 0; i < parsedErrors.length; i++) { const parsedError = parsedErrors[i]; const expectedError = expectedErrors[i]; @@ -23,7 +23,7 @@ namespace ts { const parsedFileNames = parsed.fileNames; const expectedFileNames = expectedParsedCommandLine.fileNames; - assert(parsedFileNames.length === expectedFileNames.length, `Expected fileNames: [${JSON.stringify(expectedFileNames)}]. Actual fileNames: [${JSON.stringify(parsedFileNames)}].`); + assert.isTrue(parsedFileNames.length === expectedFileNames.length, `Expected fileNames: [${JSON.stringify(expectedFileNames)}]. Actual fileNames: [${JSON.stringify(parsedFileNames)}].`); for (let i = 0; i < parsedFileNames.length; i++) { const parsedFileName = parsedFileNames[i]; const expectedFileName = expectedFileNames[i]; @@ -60,7 +60,7 @@ namespace ts { assertParseResult(["--lib", "es5,invalidOption", "0.ts"], { errors: [{ - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category, code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code, @@ -263,7 +263,7 @@ namespace ts { assertParseResult(["--lib", "es5,", "es7", "0.ts"], { errors: [{ - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category, code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code, @@ -283,7 +283,7 @@ namespace ts { assertParseResult(["--lib", "es5, ", "es7", "0.ts"], { errors: [{ - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category, code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code, diff --git a/src/harness/unittests/compileOnSave.ts b/src/harness/unittests/compileOnSave.ts index 808e03a0689..a64d7bdd2a5 100644 --- a/src/harness/unittests/compileOnSave.ts +++ b/src/harness/unittests/compileOnSave.ts @@ -25,7 +25,7 @@ namespace ts.projectSystem { const actualResultSingleProjectFileNameList = actualResultSingleProject.fileNames.sort(); const expectedResultSingleProjectFileNameList = map(expectedResultSingleProject.files, f => f.path).sort(); - assert( + assert.isTrue( arrayIsEqualTo(actualResultSingleProjectFileNameList, expectedResultSingleProjectFileNameList), `For project ${actualResultSingleProject.projectFileName}, the actual result is ${actualResultSingleProjectFileNameList}, while expected ${expectedResultSingleProjectFileNameList}`); } @@ -563,7 +563,7 @@ namespace ts.projectSystem { session.executeCommand(compileFileRequest); const expectedEmittedFileName = "/a/b/f1.js"; - assert(host.fileExists(expectedEmittedFileName)); + assert.isTrue(host.fileExists(expectedEmittedFileName)); assert.equal(host.readFile(expectedEmittedFileName), `"use strict";\r\nexports.__esModule = true;\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`); }); @@ -600,11 +600,11 @@ namespace ts.projectSystem { session.executeCommand(emitRequest); const expectedOutFileName = "/a/b/dist.js"; - assert(host.fileExists(expectedOutFileName)); + assert.isTrue(host.fileExists(expectedOutFileName)); const outFileContent = host.readFile(expectedOutFileName); - assert(outFileContent.indexOf(file1.content) !== -1); - assert(outFileContent.indexOf(file2.content) === -1); - assert(outFileContent.indexOf(file3.content) === -1); + assert.isTrue(outFileContent.indexOf(file1.content) !== -1); + assert.isTrue(outFileContent.indexOf(file2.content) === -1); + assert.isTrue(outFileContent.indexOf(file3.content) === -1); }); it("should use project root as current directory so that compile on save results in correct file mapping", () => { @@ -634,19 +634,19 @@ namespace ts.projectSystem { // Verify js file const expectedOutFileName = "/root/TypeScriptProject3/TypeScriptProject3/" + outFileName; - assert(host.fileExists(expectedOutFileName)); + assert.isTrue(host.fileExists(expectedOutFileName)); const outFileContent = host.readFile(expectedOutFileName); verifyContentHasString(outFileContent, file1.content); verifyContentHasString(outFileContent, `//# ${"sourceMappingURL"}=${outFileName}.map`); // Sometimes tools can sometimes see this line as a source mapping url comment, so we obfuscate it a little // Verify map file const expectedMapFileName = expectedOutFileName + ".map"; - assert(host.fileExists(expectedMapFileName)); + assert.isTrue(host.fileExists(expectedMapFileName)); const mapFileContent = host.readFile(expectedMapFileName); verifyContentHasString(mapFileContent, `"sources":["${inputFileName}"]`); function verifyContentHasString(content: string, str: string) { - assert(stringContains(content, str), `Expected "${content}" to have "${str}"`); + assert.isTrue(stringContains(content, str), `Expected "${content}" to have "${str}"`); } }); }); diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts index ebe5b631059..5a1b155fbcf 100644 --- a/src/harness/unittests/configurationExtension.ts +++ b/src/harness/unittests/configurationExtension.ts @@ -113,7 +113,7 @@ namespace ts { const caseSensitiveHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, testContents); function verifyDiagnostics(actual: Diagnostic[], expected: {code: number, category: DiagnosticCategory, messageText: string}[]) { - assert(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`); + assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`); for (let i = 0; i < actual.length; i++) { const actualError = actual[i]; const expectedError = expected[i]; diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index 9f5ca179efd..42e0903c93c 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -16,7 +16,7 @@ namespace ts { assert.equal(parsedCompilerOptions, expectedCompilerOptions); const expectedErrors = expectedResult.errors; - assert(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`); + assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`); for (let i = 0; i < actualErrors.length; i++) { const actualError = actualErrors[i]; const expectedError = expectedErrors[i]; @@ -42,15 +42,15 @@ namespace ts { const actualErrors = filter(actualParseErrors, error => error.code !== Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); const expectedErrors = expectedResult.errors; - assert(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`); + assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`); for (let i = 0; i < actualErrors.length; i++) { const actualError = actualErrors[i]; const expectedError = expectedErrors[i]; assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`); assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`); - assert.isDefined(actualError.file); - assert(actualError.start > 0); - assert(actualError.length > 0); + assert(actualError.file); + assert(actualError.start); + assert(actualError.length); } } @@ -266,7 +266,7 @@ namespace ts { file: undefined, start: 0, length: 0, - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category }] @@ -297,7 +297,7 @@ namespace ts { file: undefined, start: 0, length: 0, - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category }] @@ -328,7 +328,7 @@ namespace ts { file: undefined, start: 0, length: 0, - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category }] @@ -359,7 +359,7 @@ namespace ts { file: undefined, start: 0, length: 0, - messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.", + messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable', 'esnext.promise'.", code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category }] diff --git a/src/harness/unittests/convertTypeAcquisitionFromJson.ts b/src/harness/unittests/convertTypeAcquisitionFromJson.ts index ddeec6b9869..be1ada10a97 100644 --- a/src/harness/unittests/convertTypeAcquisitionFromJson.ts +++ b/src/harness/unittests/convertTypeAcquisitionFromJson.ts @@ -17,16 +17,16 @@ namespace ts { function verifyErrors(actualErrors: Diagnostic[], expectedResult: ExpectedResult, hasLocation?: boolean) { const expectedErrors = expectedResult.errors; - assert(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`); + assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`); for (let i = 0; i < actualErrors.length; i++) { const actualError = actualErrors[i]; const expectedError = expectedErrors[i]; assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`); assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`); if (hasLocation) { - assert.isDefined(actualError.file); - assert(actualError.start > 0); - assert(actualError.length > 0); + assert(actualError.file); + assert(actualError.start); + assert(actualError.length); } } } diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index 61ffbd01a63..71b550cba8f 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -262,6 +262,18 @@ namespace N { // Force this test to be TS-only y = [#|this.x|]; } }`); + + // TODO (https://github.com/Microsoft/TypeScript/issues/20727): the extracted constant should have a type annotation. + testExtractConstant("extractConstant_ContextualType", ` +interface I { a: 1 | 2 | 3 } +let i: I = [#|{ a: 1 }|]; +`); + + testExtractConstant("extractConstant_ContextualType_Lambda", ` +const myObj: { member(x: number, y: string): void } = { + member: [#|(x, y) => x + y|], +} +`); }); function testExtractConstant(caption: string, text: string) { diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index 08bae357f0e..493c9639c3d 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -39,7 +39,7 @@ namespace ts { assert.equal(end, expectedRange.end, "incorrect end of range"); } else { - assert(!result.targetRange, `expected range to extract to be undefined`); + assert.isTrue(!result.targetRange, `expected range to extract to be undefined`); } } diff --git a/src/harness/unittests/hostNewLineSupport.ts b/src/harness/unittests/hostNewLineSupport.ts index ac5031ed56b..95a05f101f7 100644 --- a/src/harness/unittests/hostNewLineSupport.ts +++ b/src/harness/unittests/hostNewLineSupport.ts @@ -47,7 +47,7 @@ namespace ts { assert(!result.emitSkipped, "emit was skipped"); assert(result.outputFiles.length === 1, "a number of files other than 1 was output"); assert(result.outputFiles[0].name === "input.js", `Expected output file name input.js, but got ${result.outputFiles[0].name}`); - assert.isDefined(result.outputFiles[0].text.match(options.newLine === NewLineKind.CarriageReturnLineFeed ? /\r\n/ : /[^\r]\n/), "expected to find appropriate newlines"); + assert(result.outputFiles[0].text.match(options.newLine === NewLineKind.CarriageReturnLineFeed ? /\r\n/ : /[^\r]\n/), "expected to find appropriate newlines"); assert(!result.outputFiles[0].text.match(options.newLine === NewLineKind.CarriageReturnLineFeed ? /[^\r]\n/ : /\r\n/), "expected not to find inappropriate newlines"); } diff --git a/src/harness/unittests/incrementalParser.ts b/src/harness/unittests/incrementalParser.ts index 2facc4aa152..c71b89d3da6 100644 --- a/src/harness/unittests/incrementalParser.ts +++ b/src/harness/unittests/incrementalParser.ts @@ -66,7 +66,7 @@ namespace ts { assertSameDiagnostics(newTree, incrementalNewTree); // There should be no reused nodes between two trees that are fully parsed. - assert(reusedElements(oldTree, newTree) === 0); + assert.isTrue(reusedElements(oldTree, newTree) === 0); assert.equal(newTree.fileName, incrementalNewTree.fileName, "newTree.fileName !== incrementalNewTree.fileName"); assert.equal(newTree.text, incrementalNewTree.text, "newTree.text !== incrementalNewTree.text"); diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 51647c46368..b7215f5ea35 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -7,7 +7,7 @@ namespace ts { function parsesCorrectly(name: string, content: string) { it(name, () => { const typeAndDiagnostics = ts.parseJSDocTypeExpressionForTests(content); - assert(typeAndDiagnostics && typeAndDiagnostics.diagnostics.length === 0, "no errors issued"); + assert.isTrue(typeAndDiagnostics && typeAndDiagnostics.diagnostics.length === 0, "no errors issued"); Harness.Baseline.runBaseline("JSDocParsing/TypeExpressions.parsesCorrectly." + name + ".json", () => Utils.sourceFileToJSON(typeAndDiagnostics.jsDocTypeExpression.type)); @@ -17,7 +17,7 @@ namespace ts { function parsesIncorrectly(name: string, content: string) { it(name, () => { const type = ts.parseJSDocTypeExpressionForTests(content); - assert(!type || type.diagnostics.length > 0); + assert.isTrue(!type || type.diagnostics.length > 0); }); } @@ -106,7 +106,7 @@ namespace ts { function parsesIncorrectly(name: string, content: string) { it(name, () => { const type = parseIsolatedJSDocComment(content); - assert(!type || type.diagnostics.length > 0); + assert.isTrue(!type || type.diagnostics.length > 0); }); } diff --git a/src/harness/unittests/languageService.ts b/src/harness/unittests/languageService.ts index 2760df63c8c..f14b9daad82 100644 --- a/src/harness/unittests/languageService.ts +++ b/src/harness/unittests/languageService.ts @@ -42,7 +42,7 @@ export function Component(x: Config): any;` }, }); const definitions = languageService.getDefinitionAtPosition("foo.ts", 160); // 160 is the latter `vueTemplateHtml` position - assert.isDefined(definitions); + expect(definitions).to.exist; // tslint:disable-line no-unused-expression }); }); } \ No newline at end of file diff --git a/src/harness/unittests/matchFiles.ts b/src/harness/unittests/matchFiles.ts index f2c2369ef37..17a0db04fdf 100644 --- a/src/harness/unittests/matchFiles.ts +++ b/src/harness/unittests/matchFiles.ts @@ -50,6 +50,7 @@ namespace ts { "/dev/x/b.ts", "/dev/x/y/a.ts", "/dev/x/y/b.ts", + "/dev/q/a/c/b/d.ts", "/dev/js/a.js", "/dev/js/b.js", ]); @@ -1171,13 +1172,17 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [ - createDiagnosticForConfigFile(json, 12, 11, ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"), - ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, - caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]") + errors: [], + fileNames: [ + "c:/dev/x/a.ts", + "c:/dev/x/aa.ts", + "c:/dev/x/b.ts", + "c:/dev/x/y/a.ts", + "c:/dev/x/y/b.ts", ], - fileNames: [], - wildcardDirectories: {} + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + } }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath); }); @@ -1192,13 +1197,9 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [ - createDiagnosticForConfigFile(json, 34, 9, ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**") - ], + errors: [], fileNames: [ "c:/dev/a.ts", - "c:/dev/x/a.ts", - "c:/dev/x/y/a.ts", "c:/dev/z/a.ts" ], wildcardDirectories: { @@ -1426,5 +1427,60 @@ namespace ts { }); }); }); + + describe("exclude or include patterns which start with **", () => { + it("can exclude dirs whose pattern starts with **", () => { + const json = { + exclude: [ + "**/x" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "/dev/A.ts", + "/dev/B.ts", + "/dev/a.ts", + "/dev/b.ts", + "/dev/c.d.ts", + "/dev/q/a/c/b/d.ts", + "/dev/z/a.ts", + "/dev/z/aba.ts", + "/dev/z/abz.ts", + "/dev/z/b.ts", + "/dev/z/bba.ts", + "/dev/z/bbz.ts", + ], + wildcardDirectories: { + "/dev": ts.WatchDirectoryFlags.Recursive + } + }; + validateMatches(expected, json, caseSensitiveHost, caseSensitiveBasePath); + }); + it("can include dirs whose pattern starts with **", () => { + const json = { + include: [ + "**/x", + "**/a/**/b" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "/dev/x/a.ts", + "/dev/x/b.ts", + "/dev/x/y/a.ts", + "/dev/x/y/b.ts", + "/dev/q/a/c/b/d.ts", + ], + wildcardDirectories: { + "/dev": ts.WatchDirectoryFlags.Recursive + } + }; + validateMatches(expected, json, caseSensitiveHost, caseSensitiveBasePath); + }); + }); }); } diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 1c18ac273ae..0dd016b2f22 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -4,9 +4,9 @@ namespace ts { export function checkResolvedModule(expected: ResolvedModuleFull, actual: ResolvedModuleFull): boolean { if (!expected === !actual) { if (expected) { - assert(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); - assert(expected.extension === actual.extension, `'ext': expected '${expected.extension}' to be equal to '${actual.extension}'`); - assert(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); + assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.isTrue(expected.extension === actual.extension, `'ext': expected '${expected.extension}' to be equal to '${actual.extension}'`); + assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); } return true; } @@ -14,7 +14,7 @@ namespace ts { } export function checkResolvedModuleWithFailedLookupLocations(actual: ResolvedModuleWithFailedLookupLocations, expectedResolvedModule: ResolvedModuleFull, expectedFailedLookupLocations: string[]): void { - assert(actual.resolvedModule !== undefined, "module should be resolved"); + assert.isTrue(actual.resolvedModule !== undefined, "module should be resolved"); checkResolvedModule(actual.resolvedModule, expectedResolvedModule); assert.deepEqual(actual.failedLookupLocations, expectedFailedLookupLocations); } @@ -58,7 +58,7 @@ namespace ts { realpath, directoryExists: path => directories.has(path), fileExists: path => { - assert(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); + assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); return map.has(path); } }; @@ -351,7 +351,7 @@ namespace ts { // try to get file using a relative name for (const relativeFileName of relativeNamesToCheck) { - assert(program.getSourceFile(relativeFileName) !== undefined, `expected to get file by relative name, got undefined`); + assert.isTrue(program.getSourceFile(relativeFileName) !== undefined, `expected to get file by relative name, got undefined`); } } @@ -441,7 +441,7 @@ export = C; "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }); - test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "/a/b/d.ts"], []); + test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "/a/b/d.ts"], []); }); it("should fail when two files used in program differ only in casing (tripleslash references)", () => { @@ -449,7 +449,7 @@ export = C; "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }); - test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); + test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); }); it("should fail when two files used in program differ only in casing (imports)", () => { @@ -457,7 +457,7 @@ export = C; "/a/b/c.ts": `import {x} from "D"`, "/a/b/d.ts": "export var x" }); - test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); + test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); }); it("should fail when two files used in program differ only in casing (imports, relative module names)", () => { @@ -465,7 +465,7 @@ export = C; "moduleA.ts": `import {x} from "./ModuleB"`, "moduleB.ts": "export var x" }); - test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts"], [1149]); + test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts"], [1149]); }); it("should fail when two files exist on disk that differs only in casing", () => { @@ -474,7 +474,7 @@ export = C; "/a/b/D.ts": "export var x", "/a/b/d.ts": "export var y" }); - test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ true, ["c.ts", "d.ts"], [1149]); + test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ true, ["c.ts", "d.ts"], [1149]); }); it("should fail when module name in 'require' calls has inconsistent casing", () => { @@ -483,7 +483,7 @@ export = C; "moduleB.ts": `import a = require("./moduleC")`, "moduleC.ts": "export var x" }); - test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts", "moduleC.ts"], [1149, 1149]); + test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts", "moduleC.ts"], [1149, 1149]); }); it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => { @@ -496,7 +496,7 @@ import a = require("./moduleA"); import b = require("./moduleB"); ` }); - test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); + test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); }); it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => { const files = createMapFromTemplate({ @@ -508,7 +508,7 @@ import a = require("./moduleA"); import b = require("./moduleB"); ` }); - test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []); + test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []); }); }); @@ -1074,7 +1074,7 @@ import b = require("./moduleB"); assert.equal(diagnostics1.length, 1, "expected one diagnostic"); createProgram(names, {}, compilerHost, program1); - assert(program1.structureIsReused === StructureIsReused.Completely); + assert.isTrue(program1.structureIsReused === StructureIsReused.Completely); const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics(); assert.equal(diagnostics2.length, 1, "expected one diagnostic"); assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic"); diff --git a/src/harness/unittests/programMissingFiles.ts b/src/harness/unittests/programMissingFiles.ts index e6e2b9af714..2a7f6d24ea7 100644 --- a/src/harness/unittests/programMissingFiles.ts +++ b/src/harness/unittests/programMissingFiles.ts @@ -6,7 +6,7 @@ namespace ts { const map = arrayToSet(expected) as Map; for (const missing of missingPaths) { const value = map.get(missing); - assert(value, `${missing} to be ${value === undefined ? "not present" : "present only once"}, in actual: ${missingPaths} expected: ${expected}`); + assert.isTrue(value, `${missing} to be ${value === undefined ? "not present" : "present only once"}, in actual: ${missingPaths} expected: ${expected}`); map.set(missing, false); } const notFound = arrayFrom(mapDefinedIterator(map.keys(), k => map.get(k) === true ? k : undefined)); diff --git a/src/harness/unittests/projectErrors.ts b/src/harness/unittests/projectErrors.ts index d146b3af080..dae465a3ef3 100644 --- a/src/harness/unittests/projectErrors.ts +++ b/src/harness/unittests/projectErrors.ts @@ -5,7 +5,7 @@ namespace ts.projectSystem { describe("Project errors", () => { function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: ReadonlyArray): void { - assert(projectFiles !== undefined, "missing project files"); + assert.isTrue(projectFiles !== undefined, "missing project files"); checkProjectErrorsWorker(projectFiles.projectErrors, expectedErrors); } @@ -15,7 +15,7 @@ namespace ts.projectSystem { for (let i = 0; i < errors.length; i++) { const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n"); const expectedMessage = expectedErrors[i]; - assert(actualMessage.indexOf(expectedMessage) === 0, `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`); + assert.isTrue(actualMessage.indexOf(expectedMessage) === 0, `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`); } } } @@ -24,7 +24,7 @@ namespace ts.projectSystem { assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`); if (expectedErrors.length) { zipWith(errors, expectedErrors, ({ message: actualMessage }, expectedMessage) => { - assert(startsWith(actualMessage, actualMessage), `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`); + assert.isTrue(startsWith(actualMessage, actualMessage), `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`); }); } } @@ -137,13 +137,13 @@ namespace ts.projectSystem { { projectService.checkNumberOfProjects({ configuredProjects: 1 }); const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f); - assert(configuredProject !== undefined, "should find configured project"); + assert.isTrue(configuredProject !== undefined, "should find configured project"); checkProjectErrors(configuredProject, []); const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors(); checkProjectErrorsWorker(projectErrors, [ "'{' expected." ]); - assert.isDefined(projectErrors[0].file); + assert.isNotNull(projectErrors[0].file); assert.equal(projectErrors[0].file.fileName, corruptedConfig.path); } // fix config and trigger watcher @@ -151,7 +151,7 @@ namespace ts.projectSystem { { projectService.checkNumberOfProjects({ configuredProjects: 1 }); const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f); - assert(configuredProject !== undefined, "should find configured project"); + assert.isTrue(configuredProject !== undefined, "should find configured project"); checkProjectErrors(configuredProject, []); const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors(); checkProjectErrorsWorker(projectErrors, []); @@ -182,7 +182,7 @@ namespace ts.projectSystem { { projectService.checkNumberOfProjects({ configuredProjects: 1 }); const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f); - assert(configuredProject !== undefined, "should find configured project"); + assert.isTrue(configuredProject !== undefined, "should find configured project"); checkProjectErrors(configuredProject, []); const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors(); checkProjectErrorsWorker(projectErrors, []); @@ -192,13 +192,13 @@ namespace ts.projectSystem { { projectService.checkNumberOfProjects({ configuredProjects: 1 }); const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f); - assert(configuredProject !== undefined, "should find configured project"); + assert.isTrue(configuredProject !== undefined, "should find configured project"); checkProjectErrors(configuredProject, []); const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors(); checkProjectErrorsWorker(projectErrors, [ "'{' expected." ]); - assert.isDefined(projectErrors[0].file); + assert.isNotNull(projectErrors[0].file); assert.equal(projectErrors[0].file.fileName, corruptedConfig.path); } }); diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 17b63fbd578..f44705f8cc2 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -193,14 +193,14 @@ namespace ts { function checkCache(caption: string, program: Program, fileName: string, expectedContent: Map, getCache: (f: SourceFile) => Map, entryChecker: (expected: T, original: T) => boolean): void { const file = program.getSourceFile(fileName); - assert(file !== undefined, `cannot find file ${fileName}`); + assert.isTrue(file !== undefined, `cannot find file ${fileName}`); const cache = getCache(file); if (expectedContent === undefined) { - assert(cache === undefined, `expected ${caption} to be undefined`); + assert.isTrue(cache === undefined, `expected ${caption} to be undefined`); } else { - assert(cache !== undefined, `expected ${caption} to be set`); - assert(mapsAreEqual(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); + assert.isTrue(cache !== undefined, `expected ${caption} to be set`); + assert.isTrue(mapsAreEqual(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); } } @@ -329,7 +329,7 @@ namespace ts { const options: CompilerOptions = { target, noLib: true }; const program1 = newProgram(files, ["a.ts"], options); - assert(program1.getMissingFilePaths().length !== 0); + assert.notDeepEqual(emptyArray, program1.getMissingFilePaths()); const program2 = updateProgram(program1, ["a.ts"], options, noop); assert.deepEqual(program1.getMissingFilePaths(), program2.getMissingFilePaths()); @@ -341,7 +341,7 @@ namespace ts { const options: CompilerOptions = { target, noLib: true }; const program1 = newProgram(files, ["a.ts"], options); - assert(program1.getMissingFilePaths().length !== 0); + assert.notDeepEqual(emptyArray, program1.getMissingFilePaths()); const newTexts: NamedSourceText[] = files.concat([{ name: "non-existing-file.ts", text: SourceText.New("", "", `var x = 1`) }]); const program2 = updateProgram(program1, ["a.ts"], options, noop, newTexts); @@ -873,7 +873,7 @@ namespace ts { updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.3" })); }); assert.equal(program1.structureIsReused, StructureIsReused.Not); - assert.lengthOf(program2.getSemanticDiagnostics(), 0); + assert.deepEqual(program2.getSemanticDiagnostics(), []); }); }); }); @@ -900,7 +900,7 @@ namespace ts { /*hasInvalidatedResolution*/ returnFalse, /*hasChangedAutomaticTypeDirectiveNames*/ false ); - assert(actual); + assert.isTrue(actual); } function duplicate(options: CompilerOptions): CompilerOptions; diff --git a/src/harness/unittests/services/colorization.ts b/src/harness/unittests/services/colorization.ts index b03285d3333..17d4132e9e5 100644 --- a/src/harness/unittests/services/colorization.ts +++ b/src/harness/unittests/services/colorization.ts @@ -50,7 +50,7 @@ describe("Colorization", () => { const actualEntry = getEntryAtPosition(result, actualEntryPosition); - assert.isDefined(actualEntry, "Could not find classification entry for '" + expectedEntry.value + "' at position: " + actualEntryPosition); + assert(actualEntry, "Could not find classification entry for '" + expectedEntry.value + "' at position: " + actualEntryPosition); assert.equal(actualEntry.classification, expectedEntry.classification, "Classification class does not match expected. Expected: " + ts.TokenClass[expectedEntry.classification] + ", Actual: " + ts.TokenClass[actualEntry.classification]); assert.equal(actualEntry.length, expectedEntry.value.length, "Classification length does not match expected. Expected: " + ts.TokenClass[expectedEntry.value.length] + ", Actual: " + ts.TokenClass[actualEntry.length]); } diff --git a/src/harness/unittests/services/patternMatcher.ts b/src/harness/unittests/services/patternMatcher.ts index 62946afde37..fef382e8a3b 100644 --- a/src/harness/unittests/services/patternMatcher.ts +++ b/src/harness/unittests/services/patternMatcher.ts @@ -140,25 +140,25 @@ describe("PatternMatcher", () => { it("PreferCaseSensitiveCamelCaseMatchToLongPattern1", () => { const match = getFirstMatch("FogBar", "FBB"); - assert(match === undefined); + assert.isTrue(match === undefined); }); it("PreferCaseSensitiveCamelCaseMatchToLongPattern2", () => { const match = getFirstMatch("FogBar", "FoooB"); - assert(match === undefined); + assert.isTrue(match === undefined); }); it("CamelCaseMatchPartiallyUnmatched", () => { const match = getFirstMatch("FogBarBaz", "FZ"); - assert(match === undefined); + assert.isTrue(match === undefined); }); it("CamelCaseMatchCompletelyUnmatched", () => { const match = getFirstMatch("FogBarBaz", "ZZ"); - assert(match === undefined); + assert.isTrue(match === undefined); }); it("TwoUppercaseCharacters", () => { @@ -220,7 +220,7 @@ describe("PatternMatcher", () => { it("PreferCaseSensitiveMiddleUnderscore3", () => { const match = getFirstMatch("Fog_Bar", "F__B"); - assert(undefined === match); + assert.isTrue(undefined === match); }); it("PreferCaseSensitiveMiddleUnderscore4", () => { @@ -264,25 +264,25 @@ describe("PatternMatcher", () => { it("AllLowerPattern1", () => { const match = getFirstMatch("FogBarChangedEventArgs", "changedeventargs"); - assert(undefined !== match); + assert.isTrue(undefined !== match); }); it("AllLowerPattern2", () => { const match = getFirstMatch("FogBarChangedEventArgs", "changedeventarrrgh"); - assert(undefined === match); + assert.isTrue(undefined === match); }); it("AllLowerPattern3", () => { const match = getFirstMatch("ABCDEFGH", "bcd"); - assert(undefined !== match); + assert.isTrue(undefined !== match); }); it("AllLowerPattern4", () => { const match = getFirstMatch("AbcdefghijEfgHij", "efghij"); - assert(undefined === match); + assert.isTrue(undefined === match); }); }); @@ -370,13 +370,13 @@ describe("PatternMatcher", () => { it("BlankPattern", () => { const matches = getAllMatches("AddMetadataReference", ""); - assert(matches === undefined); + assert.isTrue(matches === undefined); }); it("WhitespaceOnlyPattern", () => { const matches = getAllMatches("AddMetadataReference", " "); - assert(matches === undefined); + assert.isTrue(matches === undefined); }); it("EachWordSeparately1", () => { @@ -403,13 +403,13 @@ describe("PatternMatcher", () => { it("MixedCasing", () => { const matches = getAllMatches("AddMetadataReference", "mEta"); - assert(matches === undefined); + assert.isTrue(matches === undefined); }); it("MixedCasing2", () => { const matches = getAllMatches("AddMetadataReference", "Data"); - assert(matches === undefined); + assert.isTrue(matches === undefined); }); it("AsteriskSplit", () => { @@ -421,7 +421,7 @@ describe("PatternMatcher", () => { it("LowercaseSubstring1", () => { const matches = getAllMatches("Operator", "a"); - assert(matches === undefined); + assert.isTrue(matches === undefined); }); it("LowercaseSubstring2", () => { @@ -441,7 +441,7 @@ describe("PatternMatcher", () => { it("DottedPattern2", () => { const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "C.Q"); - assert(match === undefined); + assert.isTrue(match === undefined); }); it("DottedPattern3", () => { @@ -464,13 +464,13 @@ describe("PatternMatcher", () => { it("DottedPattern6", () => { const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "F.F.B.B.Quux"); - assert(match === undefined); + assert.isTrue(match === undefined); }); it("DottedPattern7", () => { let match = getFirstMatch("UIElement", "UIElement"); match = getFirstMatch("GetKeyword", "UIElement"); - assert(match === undefined); + assert.isTrue(match === undefined); }); }); @@ -508,8 +508,8 @@ describe("PatternMatcher", () => { } function assertInRange(val: number, low: number, high: number) { - assert(val >= low); - assert(val <= high); + assert.isTrue(val >= low); + assert.isTrue(val <= high); } function verifyBreakIntoCharacterSpans(original: string, ...parts: string[]): void { @@ -521,6 +521,6 @@ describe("PatternMatcher", () => { } function assertContainsKind(kind: ts.PatternMatchKind, results: ts.PatternMatch[]) { - assert(ts.forEach(results, r => r.kind === kind)); + assert.isTrue(ts.forEach(results, r => r.kind === kind)); } }); diff --git a/src/harness/unittests/services/preProcessFile.ts b/src/harness/unittests/services/preProcessFile.ts index acbb8edd67c..1e13bc3e345 100644 --- a/src/harness/unittests/services/preProcessFile.ts +++ b/src/harness/unittests/services/preProcessFile.ts @@ -18,7 +18,7 @@ describe("PreProcessFile:", () => { return; } if (!expected) { - assert(false, `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); + assert.isTrue(false, `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); } assert.equal(actual.length, expected.length, `[${kind}] Actual array's length does not match expected length. Expected files: ${JSON.stringify(expected)}, actual files: ${JSON.stringify(actual)}`); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 0959462692b..7d1e5b0816c 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -1,5 +1,7 @@ /// +const expect: typeof _chai.expect = _chai.expect; + namespace ts.server { let lastWrittenToHost: string; const noopFileWatcher: FileWatcher = { close: noop }; @@ -83,7 +85,7 @@ namespace ts.server { } }; - assert.throws(() => session.executeCommand(req)); + expect(() => session.executeCommand(req)).to.throw(); }); it("should output an error response when a command does not exist", () => { const req: protocol.Request = { @@ -102,7 +104,7 @@ namespace ts.server { request_seq: 0, success: false }; - assert.deepEqual(lastSent, expected); + expect(lastSent).to.deep.equal(expected); }); it("should return a tuple containing the response and if a response is required on success", () => { const req: protocol.ConfigureRequest = { @@ -117,18 +119,17 @@ namespace ts.server { } }; - assert.deepEqual(session.executeCommand(req), { + expect(session.executeCommand(req)).to.deep.equal({ responseRequired: false }); - const expected: protocol.Response = { + expect(lastSent).to.deep.equal({ command: CommandNames.Configure, type: "response", success: true, request_seq: 0, seq: 0, body: undefined - }; - assert.deepEqual(lastSent, expected); + }); }); it("should handle literal types in request", () => { const configureRequest: protocol.ConfigureRequest = { @@ -313,15 +314,14 @@ namespace ts.server { session.onMessage(JSON.stringify(req)); - const expected: protocol.ConfigureResponse = { + expect(lastSent).to.deep.equal({ command: CommandNames.Configure, type: "response", success: true, request_seq: 0, seq: 0, body: undefined - }; - assert.deepEqual(lastSent, expected); + }); }); }); @@ -333,9 +333,9 @@ namespace ts.server { const resultMsg = `Content-Length: ${len}\r\n\r\n${strmsg}\n`; session.send = Session.prototype.send; - assert.isDefined(session.send); - session.send(msg); - assert.equal(lastWrittenToHost, resultMsg); + assert(session.send); + expect(session.send(msg)).to.not.exist; // tslint:disable-line no-unused-expression + expect(lastWrittenToHost).to.equal(resultMsg); }); }); @@ -352,7 +352,11 @@ namespace ts.server { session.addProtocolHandler(command, () => result); - assert.deepEqual(session.executeCommand({ command, seq: 0, type: "request" }), result); + expect(session.executeCommand({ + command, + seq: 0, + type: "request" + })).to.deep.equal(result); }); it("throws when a duplicate handler is passed", () => { const respBody = { @@ -366,7 +370,8 @@ namespace ts.server { session.addProtocolHandler(command, () => resp); - assert.throws(() => session.addProtocolHandler(command, () => resp), `Protocol handler already exists for command "${command}"`); + expect(() => session.addProtocolHandler(command, () => resp)) + .to.throw(`Protocol handler already exists for command "${command}"`); }); }); @@ -379,13 +384,12 @@ namespace ts.server { session.event(info, evt); - const expected: protocol.Event = { + expect(lastSent).to.deep.equal({ type: "event", seq: 0, event: evt, body: info - }; - assert.deepEqual(lastSent, expected); + }); }); }); @@ -400,15 +404,14 @@ namespace ts.server { session.output(body, command, /*reqSeq*/ 0); - const expected: protocol.Response = { + expect(lastSent).to.deep.equal({ seq: 0, request_seq: 0, type: "response", command, body, success: true - }; - assert.deepEqual(lastSent, expected); + }); }); }); }); @@ -469,16 +472,14 @@ namespace ts.server { session.onMessage(JSON.stringify(request)); const lastSent = session.lastSent as protocol.Response; - assert.deepEqual({ ...lastSent, message: undefined }, { - request_seq: 0, + expect(lastSent).to.contain({ seq: 0, type: "response", command, - success: false, - message: undefined, + success: false }); - assert(ts.stringContains(lastSent.message, "myMessage") && ts.stringContains(lastSent.message, "f1")); + expect(lastSent.message).has.string("myMessage").and.has.string("f1"); }); }); @@ -518,24 +519,23 @@ namespace ts.server { session.output(body, command, /*reqSeq*/ 0); - const expected: protocol.Response = { + expect(session.lastSent).to.deep.equal({ seq: 0, request_seq: 0, type: "response", command, body, success: true - }; - assert.deepEqual(session.lastSent, expected); + }); }); it("can add and respond to new protocol handlers", () => { const session = new TestSession(); - assert.deepEqual(session.executeCommand({ + expect(session.executeCommand({ seq: 0, type: "request", command: session.customHandler - }), { + })).to.deep.equal({ response: undefined, responseRequired: true }); @@ -545,7 +545,8 @@ namespace ts.server { new class extends TestSession { constructor() { super(); - assert(this.projectService instanceof ProjectService); + assert(this.projectService); + expect(this.projectService).to.be.instanceOf(ProjectService); } }(); }); @@ -669,9 +670,9 @@ namespace ts.server { // Add an event handler cli.on("testevent", (eventinfo) => { - assert.equal(eventinfo, toEvent); + expect(eventinfo).to.equal(toEvent); responses++; - assert.equal(responses, 1); + expect(responses).to.equal(1); }); // Trigger said event from the server @@ -681,8 +682,8 @@ namespace ts.server { cli.execute("echo", toEcho, (resp) => { assert(resp.success, resp.message); responses++; - assert.equal(responses, 2); - assert.deepEqual(resp.body, toEcho); + expect(responses).to.equal(2); + expect(resp.body).to.deep.equal(toEcho); }); // Queue a configure command @@ -694,7 +695,7 @@ namespace ts.server { }, (resp) => { assert(resp.success, resp.message); responses++; - assert.equal(responses, 3); + expect(responses).to.equal(3); done(); }); diff --git a/src/harness/unittests/textChanges.ts b/src/harness/unittests/textChanges.ts index ed571b37399..934da4e3ba1 100644 --- a/src/harness/unittests/textChanges.ts +++ b/src/harness/unittests/textChanges.ts @@ -103,7 +103,7 @@ namespace M /*body */ createBlock(statements) ); - changeTracker.insertNodeBefore(sourceFile, /*before*/findChild("M2", sourceFile), newFunction, { suffix: newLineCharacter }); + changeTracker.insertNodeBefore(sourceFile, /*before*/findChild("M2", sourceFile), newFunction); // replace statements with return statement const newStatement = createReturn( @@ -129,12 +129,11 @@ function bar() { changeTracker.deleteRange(sourceFile, { pos: text.indexOf("function foo"), end: text.indexOf("function bar") }); }); } - function findVariableStatementContaining(name: string, sourceFile: SourceFile) { - const varDecl = findChild(name, sourceFile); - assert.equal(varDecl.kind, SyntaxKind.VariableDeclaration); - const varStatement = varDecl.parent.parent; - assert.equal(varStatement.kind, SyntaxKind.VariableStatement); - return varStatement; + function findVariableStatementContaining(name: string, sourceFile: SourceFile): VariableStatement { + return cast(findVariableDeclarationContaining(name, sourceFile).parent.parent, isVariableStatement); + } + function findVariableDeclarationContaining(name: string, sourceFile: SourceFile): VariableDeclaration { + return cast(findChild(name, sourceFile), isVariableDeclaration); } { const text = ` @@ -306,11 +305,11 @@ var y; // comment 4 var z = 3; // comment 5 // comment 6 var a = 4; // comment 7`; - runSingleFileTest("insertNodeAt1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.insertNodeAt(sourceFile, text.indexOf("var y"), createTestClass(), { suffix: newLineCharacter }); + runSingleFileTest("insertNodeBefore3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { + changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass()); }); - runSingleFileTest("insertNodeAt2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - changeTracker.insertNodeAt(sourceFile, text.indexOf("; // comment 4"), createTestVariableDeclaration("z1")); + runSingleFileTest("insertNodeAfterVariableDeclaration", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { + changeTracker.insertNodeAfter(sourceFile, findVariableDeclarationContaining("y", sourceFile), createTestVariableDeclaration("z1")); }); } { @@ -325,23 +324,22 @@ namespace M { var a = 4; // comment 7 }`; runSingleFileTest("insertNodeBefore1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter }); + changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass()); }); runSingleFileTest("insertNodeBefore2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.insertNodeBefore(sourceFile, findChild("M", sourceFile), createTestClass(), { suffix: newLineCharacter }); + changeTracker.insertNodeBefore(sourceFile, findChild("M", sourceFile), createTestClass()); }); runSingleFileTest("insertNodeAfter1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass()); }); runSingleFileTest("insertNodeAfter2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass(), { prefix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass()); }); } - function findOpenBraceForConstructor(sourceFile: SourceFile) { + function findConstructor(sourceFile: SourceFile): ConstructorDeclaration { const classDecl = sourceFile.statements[0]; - const constructorDecl = forEach(classDecl.members, m => m.kind === SyntaxKind.Constructor && (m).body && m); - return constructorDecl.body.getFirstToken(); + return find(classDecl.members, (m): m is ConstructorDeclaration => isConstructorDeclaration(m) && !!m.body)!; } function createTestSuperCall() { const superCall = createCall( @@ -359,8 +357,8 @@ class A { } } `; - runSingleFileTest("insertNodeAfter3", /*placeOpenBraceOnNewLineForFunctions*/ false, text1, /*validateNodes*/ false, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findOpenBraceForConstructor(sourceFile), createTestSuperCall(), { suffix: newLineCharacter }); + runSingleFileTest("insertNodeAtConstructorStart", /*placeOpenBraceOnNewLineForFunctions*/ false, text1, /*validateNodes*/ false, (sourceFile, changeTracker) => { + changeTracker.insertNodeAtConstructorStart(sourceFile, findConstructor(sourceFile), createTestSuperCall()); }); const text2 = ` class A { @@ -370,7 +368,7 @@ class A { } `; runSingleFileTest("insertNodeAfter4", /*placeOpenBraceOnNewLineForFunctions*/ false, text2, /*validateNodes*/ false, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), createTestSuperCall(), { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), createTestSuperCall()); }); const text3 = ` class A { @@ -379,8 +377,8 @@ class A { } } `; - runSingleFileTest("insertNodeAfter3-block with newline", /*placeOpenBraceOnNewLineForFunctions*/ false, text3, /*validateNodes*/ false, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findOpenBraceForConstructor(sourceFile), createTestSuperCall(), { suffix: newLineCharacter }); + runSingleFileTest("insertNodeAtConstructorStart-block with newline", /*placeOpenBraceOnNewLineForFunctions*/ false, text3, /*validateNodes*/ false, (sourceFile, changeTracker) => { + changeTracker.insertNodeAtConstructorStart(sourceFile, findConstructor(sourceFile), createTestSuperCall()); }); } { @@ -638,7 +636,7 @@ class A { } const insertAfter = findChild("x", sourceFile); for (const newNode of newNodes) { - changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode); } }); } @@ -649,7 +647,7 @@ class A { } `; runSingleFileTest("insertNodeAfterInClass1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined), { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined)); }); } { @@ -659,7 +657,7 @@ class A { } `; runSingleFileTest("insertNodeAfterInClass2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined), { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined)); }); } { @@ -698,7 +696,7 @@ class A { /*questionToken*/ undefined, createKeywordTypeNode(SyntaxKind.AnyKeyword), /*initializer*/ undefined); - changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode); }); } { @@ -716,7 +714,7 @@ class A { /*questionToken*/ undefined, createKeywordTypeNode(SyntaxKind.AnyKeyword), /*initializer*/ undefined); - changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode); }); } { @@ -733,7 +731,7 @@ interface A { /*questionToken*/ undefined, createKeywordTypeNode(SyntaxKind.AnyKeyword), /*initializer*/ undefined); - changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode); }); } { @@ -750,7 +748,7 @@ interface A { /*questionToken*/ undefined, createKeywordTypeNode(SyntaxKind.AnyKeyword), /*initializer*/ undefined); - changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode); }); } { @@ -759,7 +757,7 @@ let x = foo `; runSingleFileTest("insertNodeInStatementListAfterNodeWithoutSeparator1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { const newNode = createStatement(createParen(createLiteral(1))); - changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), newNode, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), newNode); }); } }); diff --git a/src/harness/unittests/textStorage.ts b/src/harness/unittests/textStorage.ts index 78a736487cc..aa8231aa31a 100644 --- a/src/harness/unittests/textStorage.ts +++ b/src/harness/unittests/textStorage.ts @@ -33,20 +33,20 @@ namespace ts.textStorage { for (let offset = 0; offset < end - start; offset++) { const pos1 = ts1.lineOffsetToPosition(line + 1, offset + 1); const pos2 = ts2.lineOffsetToPosition(line + 1, offset + 1); - assert(pos1 === pos2, `lineOffsetToPosition ${line + 1}-${offset + 1}: expected ${pos1} to equal ${pos2}`); + assert.isTrue(pos1 === pos2, `lineOffsetToPosition ${line + 1}-${offset + 1}: expected ${pos1} to equal ${pos2}`); } const {start: start1, length: length1 } = ts1.lineToTextSpan(line); const {start: start2, length: length2 } = ts2.lineToTextSpan(line); - assert(start1 === start2, `lineToTextSpan ${line}::start:: expected ${start1} to equal ${start2}`); - assert(length1 === length2, `lineToTextSpan ${line}::length:: expected ${length1} to equal ${length2}`); + assert.isTrue(start1 === start2, `lineToTextSpan ${line}::start:: expected ${start1} to equal ${start2}`); + assert.isTrue(length1 === length2, `lineToTextSpan ${line}::length:: expected ${length1} to equal ${length2}`); } for (let pos = 0; pos < f.content.length; pos++) { const { line: line1, offset: offset1 } = ts1.positionToLineOffset(pos); const { line: line2, offset: offset2 } = ts2.positionToLineOffset(pos); - assert(line1 === line2, `positionToLineOffset ${pos}::line:: expected ${line1} to equal ${line2}`); - assert(offset1 === offset2, `positionToLineOffset ${pos}::offset:: expected ${offset1} to equal ${offset2}`); + assert.isTrue(line1 === line2, `positionToLineOffset ${pos}::line:: expected ${line1} to equal ${line2}`); + assert.isTrue(offset1 === offset2, `positionToLineOffset ${pos}::offset:: expected ${offset1} to equal ${offset2}`); } }); @@ -55,16 +55,16 @@ namespace ts.textStorage { const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path)); ts1.getSnapshot(); - assert(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 1"); + assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 1"); ts1.edit(0, 5, " "); - assert(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 1"); + assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 1"); ts1.useText(); - assert(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 2"); + assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 2"); ts1.getLineInfo(0); - assert(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 2"); + assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 2"); }); }); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 389d1255ea0..b2e677ad988 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -124,7 +124,7 @@ namespace ts.tscWatch { function assertWatchDiagnosticAt(host: WatchedSystem, outputAt: number, diagnosticMessage: DiagnosticMessage) { const output = host.getOutput()[outputAt]; - assert(endsWith(output, getWatchDiagnosticWithoutDate(host, diagnosticMessage)), "outputs[" + outputAt + "] is " + output); + assert.isTrue(endsWith(output, getWatchDiagnosticWithoutDate(host, diagnosticMessage)), "outputs[" + outputAt + "] is " + output); } function getWatchDiagnosticWithoutDate(host: WatchedSystem, diagnosticMessage: DiagnosticMessage) { @@ -1530,11 +1530,11 @@ namespace ts.tscWatch { function verifyEmittedFiles(host: WatchedSystem, emittedFiles: EmittedFile[]) { for (const { path, content, shouldBeWritten } of emittedFiles) { if (shouldBeWritten) { - assert(host.fileExists(path), `Expected file ${path} to be present`); + assert.isTrue(host.fileExists(path), `Expected file ${path} to be present`); assert.equal(host.readFile(path), content, `Contents of file ${path} do not match`); } else { - assert(!host.fileExists(path), `Expected file ${path} to be absent`); + assert.isNotTrue(host.fileExists(path), `Expected file ${path} to be absent`); } } } @@ -1710,7 +1710,7 @@ namespace ts.tscWatch { return false; } fileExistsIsCalled = true; - assert(fileName.indexOf("/f2.") !== -1); + assert.isTrue(fileName.indexOf("/f2.") !== -1); return originalFileExists.call(host, fileName); }; @@ -1725,7 +1725,7 @@ namespace ts.tscWatch { getDiagnosticModuleNotFoundOfFile(watch(), root, "f2") ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); - assert(fileExistsIsCalled); + assert.isTrue(fileExistsIsCalled); } { let fileExistsCalled = false; @@ -1734,7 +1734,7 @@ namespace ts.tscWatch { return false; } fileExistsCalled = true; - assert(fileName.indexOf("/f1.") !== -1); + assert.isTrue(fileName.indexOf("/f1.") !== -1); return originalFileExists.call(host, fileName); }; @@ -1745,7 +1745,7 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); checkOutputErrors(host, [f1IsNotModule, cannotFindFoo], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); - assert(fileExistsCalled); + assert.isTrue(fileExistsCalled); } }); @@ -1778,7 +1778,7 @@ namespace ts.tscWatch { const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD }); - assert(fileExistsCalledForBar, "'fileExists' should be called"); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "bar") ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); @@ -1789,7 +1789,7 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); - assert(fileExistsCalledForBar, "'fileExists' should be called."); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); }); it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => { @@ -1820,13 +1820,13 @@ namespace ts.tscWatch { const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD }); - assert(fileExistsCalledForBar, "'fileExists' should be called"); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); fileExistsCalledForBar = false; host.reloadFS(files); host.runQueuedTimeoutCallbacks(); - assert(fileExistsCalledForBar, "'fileExists' should be called."); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "bar") ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); @@ -1835,7 +1835,7 @@ namespace ts.tscWatch { host.reloadFS(filesWithImported); host.checkTimeoutQueueLengthAndRun(1); checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); - assert(fileExistsCalledForBar, "'fileExists' should be called."); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); }); it("works when module resolution changes to ambient module", () => { @@ -2037,7 +2037,7 @@ declare module "fs" { checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path)); const outputFile1 = changeExtension((outputFolder + getBaseFileName(file1.path)), ".js"); - assert(host.fileExists(outputFile1)); + assert.isTrue(host.fileExists(outputFile1)); assert.equal(host.readFile(outputFile1), file1.content + host.newLine); }); }); diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index 0f03b5630b3..8d56360bba5 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -11,20 +11,20 @@ namespace ts { function assertParseError(jsonText: string) { const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText); assert.deepEqual(parsed.config, {}); - assert(undefined !== parsed.error); + assert.isTrue(undefined !== parsed.error); } function assertParseErrorWithExcludesKeyword(jsonText: string) { { const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText); const parsedCommand = ts.parseJsonConfigFileContent(parsed.config, ts.sys, "tests/cases/unittests"); - assert(parsedCommand.errors && parsedCommand.errors.length === 1 && + assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 && parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code); } { const parsed = ts.parseJsonText("/apath/tsconfig.json", jsonText); const parsedCommand = ts.parseJsonSourceFileConfigFileContent(parsed, ts.sys, "tests/cases/unittests"); - assert(parsedCommand.errors && parsedCommand.errors.length === 1 && + assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 && parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code); } } @@ -44,26 +44,26 @@ namespace ts { function assertParseFileList(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedFileList: string[]) { { const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList); - assert(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort())); + assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort())); } { const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList); - assert(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort())); + assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort())); } } function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number, noLocation?: boolean) { { const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList); - assert(parsed.errors.length >= 0); - assert(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`); + assert.isTrue(parsed.errors.length >= 0); + assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`); } { const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList); - assert(parsed.errors.length >= 0); - assert(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`); + assert.isTrue(parsed.errors.length >= 0); + assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`); if (!noLocation) { - assert(parsed.errors.filter(e => e.code === expectedDiagnosticCode && e.file && e.start && e.length).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)} with location information`); + assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode && e.file && e.start && e.length).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)} with location information`); } } } @@ -241,7 +241,7 @@ namespace ts { }, files: ["file1.ts"] }; - assert(diagnostics.length === 2); + assert.isTrue(diagnostics.length === 2); assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult)); }); diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 39dde23b28a..34c8f9e9656 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -468,8 +468,8 @@ namespace ts.projectSystem { assert.equal(outputs[index], server.formatMessage(expectedEvent, nullLogger, Utils.byteLength, session.host.newLine)); if (isMostRecent) { - assert.equal(events.length, index + 1, JSON.stringify(events)); - assert.equal(outputs.length, index + 1, JSON.stringify(outputs)); + assert.strictEqual(events.length, index + 1, JSON.stringify(events)); + assert.strictEqual(outputs.length, index + 1, JSON.stringify(outputs)); } } @@ -572,8 +572,8 @@ namespace ts.projectSystem { const projectService = createProjectService(host); const { configFileName, configFileErrors } = projectService.openClientFile(file1.path); - assert.isDefined(configFileName, "should find config file"); - assert(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); + assert(configFileName, "should find config file"); + assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); checkNumberOfInferredProjects(projectService, 0); checkNumberOfConfiguredProjects(projectService, 1); @@ -612,8 +612,8 @@ namespace ts.projectSystem { const projectService = createProjectService(host); const { configFileName, configFileErrors } = projectService.openClientFile(file1.path); - assert.isDefined(configFileName, "should find config file"); - assert(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); + assert(configFileName, "should find config file"); + assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); checkNumberOfInferredProjects(projectService, 0); checkNumberOfConfiguredProjects(projectService, 1); @@ -821,7 +821,7 @@ namespace ts.projectSystem { host.reloadFS([file1, commonFile2, libFile]); host.runQueuedTimeoutCallbacks(); checkNumberOfInferredProjects(projectService, 1); - assert.equal(projectService.inferredProjects[0], project, "Inferred project should be same"); + assert.strictEqual(projectService.inferredProjects[0], project, "Inferred project should be same"); checkProjectRootFiles(project, [file1.path]); checkProjectActualFiles(project, [file1.path, libFile.path, commonFile2.path]); diags = session.executeCommand(getErrRequest).response as server.protocol.Diagnostic[]; @@ -1024,11 +1024,11 @@ namespace ts.projectSystem { projectService.openExternalProject({ rootFiles: toExternalFiles([file1.path]), options: {}, projectFileName: proj1name }); const proj1 = projectService.findProject(proj1name); - assert(proj1.languageServiceEnabled); + assert.isTrue(proj1.languageServiceEnabled); projectService.openExternalProject({ rootFiles: toExternalFiles([file2.path]), options: {}, projectFileName: proj2name }); const proj2 = projectService.findProject(proj2name); - assert(proj2.languageServiceEnabled); + assert.isTrue(proj2.languageServiceEnabled); projectService.openExternalProject({ rootFiles: toExternalFiles([file3.path]), options: {}, projectFileName: proj3name }); const proj3 = projectService.findProject(proj3name); @@ -1100,18 +1100,18 @@ namespace ts.projectSystem { projectService.openClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects.get(configFile.path); - assert(project.hasOpenRef()); // file1 + assert.isTrue(project.hasOpenRef()); // file1 projectService.closeClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); assert.isFalse(project.hasOpenRef()); // No open files assert.isFalse(project.isClosed()); projectService.openClientFile(file2.path); checkNumberOfConfiguredProjects(projectService, 1); - assert.equal(projectService.configuredProjects.get(configFile.path), project); - assert(project.hasOpenRef()); // file2 + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); + assert.isTrue(project.hasOpenRef()); // file2 assert.isFalse(project.isClosed()); }); @@ -1134,18 +1134,18 @@ namespace ts.projectSystem { projectService.openClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects.get(configFile.path); - assert(project.hasOpenRef()); // file1 + assert.isTrue(project.hasOpenRef()); // file1 projectService.closeClientFile(file1.path); checkNumberOfConfiguredProjects(projectService, 1); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); assert.isFalse(project.hasOpenRef()); // No files assert.isFalse(project.isClosed()); projectService.openClientFile(libFile.path); checkNumberOfConfiguredProjects(projectService, 0); assert.isFalse(project.hasOpenRef()); // No files + project closed - assert(project.isClosed()); + assert.isTrue(project.isClosed()); }); it("should not close external project with no open files", () => { @@ -1184,6 +1184,24 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 0); }); + it("external project for dynamic file", () => { + const externalProjectName = "^ScriptDocument1 file1.ts"; + const externalFiles = toExternalFiles(["^ScriptDocument1 file1.ts"]); + const host = createServerHost([]); + const projectService = createProjectService(host); + projectService.openExternalProject({ + rootFiles: externalFiles, + options: {}, + projectFileName: externalProjectName + }); + + checkNumberOfExternalProjects(projectService, 1); + checkNumberOfInferredProjects(projectService, 0); + + externalFiles[0].content = "let x =1;"; + projectService.applyChangesInOpenFiles(externalFiles, [], []); + }); + it("external project that included config files", () => { const file1 = { path: "/a/b/f1.ts", @@ -1233,28 +1251,28 @@ namespace ts.projectSystem { // open client file - should not lead to creation of inferred project projectService.openClientFile(file1.path, file1.content); checkNumberOfProjects(projectService, { configuredProjects: 2 }); - assert.equal(projectService.configuredProjects.get(config1.path), proj1); - assert.equal(projectService.configuredProjects.get(config2.path), proj2); + assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1); + assert.strictEqual(projectService.configuredProjects.get(config2.path), proj2); projectService.openClientFile(file3.path, file3.content); checkNumberOfProjects(projectService, { configuredProjects: 2, inferredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config1.path), proj1); - assert.equal(projectService.configuredProjects.get(config2.path), proj2); + assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1); + assert.strictEqual(projectService.configuredProjects.get(config2.path), proj2); projectService.closeExternalProject(externalProjectName); // open file 'file1' from configured project keeps project alive checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config1.path), proj1); + assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1); assert.isUndefined(projectService.configuredProjects.get(config2.path)); projectService.closeClientFile(file3.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config1.path), proj1); + assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1); assert.isUndefined(projectService.configuredProjects.get(config2.path)); projectService.closeClientFile(file1.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config1.path), proj1); + assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1); assert.isUndefined(projectService.configuredProjects.get(config2.path)); projectService.openClientFile(file2.path, file2.content); @@ -1286,14 +1304,14 @@ namespace ts.projectSystem { const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, { includeExternalModuleExports: false }); // should contain completions for string - assert(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'"); + assert.isTrue(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'"); assert.isFalse(completions1.entries.some(e => e.name === "toExponential"), "should not contain 'toExponential'"); service.closeClientFile(f2.path); const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, { includeExternalModuleExports: false }); // should contain completions for string assert.isFalse(completions2.entries.some(e => e.name === "charAt"), "should not contain 'charAt'"); - assert(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'"); + assert.isTrue(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'"); }); it("clear mixed content file after closing", () => { @@ -1317,7 +1335,7 @@ namespace ts.projectSystem { checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]); const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, { includeExternalModuleExports: false }); - assert(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'"); + assert.isTrue(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'"); service.closeClientFile(f2.path); const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, { includeExternalModuleExports: false }); @@ -1387,16 +1405,16 @@ namespace ts.projectSystem { }); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); projectService.closeExternalProject(externalProjectName); // configured project is alive since file is still open checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); projectService.closeClientFile(file1.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); projectService.openClientFile(file2.path); checkNumberOfProjects(projectService, { inferredProjects: 1 }); @@ -1910,7 +1928,7 @@ namespace ts.projectSystem { // The configured project should now be updated to include html file checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(configuredProjectAt(projectService, 0), configuredProj, "Same configured project should be updated"); + assert.strictEqual(configuredProjectAt(projectService, 0), configuredProj, "Same configured project should be updated"); checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, config.path]); // Open HTML file @@ -2176,18 +2194,18 @@ namespace ts.projectSystem { projectService.openClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); const project1 = projectService.configuredProjects.get(tsconfig1.path); - assert(project1.hasOpenRef(), "Has open ref count in project1 - 1"); // file2 + assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 1"); // file2 assert.equal(project1.getScriptInfo(file2.path).containingProjects.length, 1, "containing projects count"); assert.isFalse(project1.isClosed()); projectService.openClientFile(file1.path); checkNumberOfProjects(projectService, { configuredProjects: 2 }); - assert(project1.hasOpenRef(), "Has open ref count in project1 - 2"); // file2 - assert.equal(projectService.configuredProjects.get(tsconfig1.path), project1); + assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 2"); // file2 + assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); assert.isFalse(project1.isClosed()); const project2 = projectService.configuredProjects.get(tsconfig2.path); - assert(project2.hasOpenRef(), "Has open ref count in project2 - 2"); // file1 + assert.isTrue(project2.hasOpenRef(), "Has open ref count in project2 - 2"); // file1 assert.isFalse(project2.isClosed()); assert.equal(project1.getScriptInfo(file1.path).containingProjects.length, 2, `${file1.path} containing projects count`); @@ -2196,9 +2214,9 @@ namespace ts.projectSystem { projectService.closeClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 2 }); assert.isFalse(project1.hasOpenRef(), "Has open ref count in project1 - 3"); // No files - assert(project2.hasOpenRef(), "Has open ref count in project2 - 3"); // file1 - assert.equal(projectService.configuredProjects.get(tsconfig1.path), project1); - assert.equal(projectService.configuredProjects.get(tsconfig2.path), project2); + assert.isTrue(project2.hasOpenRef(), "Has open ref count in project2 - 3"); // file1 + assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); + assert.strictEqual(projectService.configuredProjects.get(tsconfig2.path), project2); assert.isFalse(project1.isClosed()); assert.isFalse(project2.isClosed()); @@ -2206,18 +2224,18 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { configuredProjects: 2 }); assert.isFalse(project1.hasOpenRef(), "Has open ref count in project1 - 4"); // No files assert.isFalse(project2.hasOpenRef(), "Has open ref count in project2 - 4"); // No files - assert.equal(projectService.configuredProjects.get(tsconfig1.path), project1); - assert.equal(projectService.configuredProjects.get(tsconfig2.path), project2); + assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); + assert.strictEqual(projectService.configuredProjects.get(tsconfig2.path), project2); assert.isFalse(project1.isClosed()); assert.isFalse(project2.isClosed()); projectService.openClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(tsconfig1.path), project1); + assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1); assert.isUndefined(projectService.configuredProjects.get(tsconfig2.path)); - assert(project1.hasOpenRef(), "Has open ref count in project1 - 5"); // file2 + assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 5"); // file2 assert.isFalse(project1.isClosed()); - assert(project2.isClosed()); + assert.isTrue(project2.isClosed()); }); it("Open ref of configured project when open file gets added to the project as part of configured file update", () => { @@ -2255,7 +2273,7 @@ namespace ts.projectSystem { checkOpenFiles(projectService, files); checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 }); const configProject1 = projectService.configuredProjects.get(configFile.path); - assert(configProject1.hasOpenRef()); // file1 and file3 + assert.isTrue(configProject1.hasOpenRef()); // file1 and file3 checkProjectActualFiles(configProject1, [file1.path, file3.path, configFile.path]); const inferredProject1 = projectService.inferredProjects[0]; checkProjectActualFiles(inferredProject1, [file2.path]); @@ -2272,7 +2290,7 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 1); const inferredProject3 = projectService.inferredProjects[0]; checkProjectActualFiles(inferredProject3, [file4.path]); - assert.equal(inferredProject3, inferredProject2); + assert.strictEqual(inferredProject3, inferredProject2); projectService.closeClientFile(file1.path); projectService.closeClientFile(file2.path); @@ -2298,7 +2316,7 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 1); const inferredProject5 = projectService.inferredProjects[0]; checkProjectActualFiles(inferredProject4, [file4.path]); - assert.equal(inferredProject5, inferredProject4); + assert.strictEqual(inferredProject5, inferredProject4); const file5: FileOrFolder = { path: "/file5.ts", @@ -2307,13 +2325,13 @@ namespace ts.projectSystem { host.reloadFS(files.concat(configFile, file5)); projectService.openClientFile(file5.path); verifyScriptInfosAreUndefined([file1, file2, file3]); - assert.equal(projectService.getScriptInfoForPath(file4.path as Path), find(infos, info => info.path === file4.path)); + assert.strictEqual(projectService.getScriptInfoForPath(file4.path as Path), find(infos, info => info.path === file4.path)); assert.isDefined(projectService.getScriptInfoForPath(file5.path as Path)); checkOpenFiles(projectService, [file4, file5]); checkNumberOfConfiguredProjects(projectService, 0); function verifyScriptInfos() { - infos.forEach(info => assert.equal(projectService.getScriptInfoForPath(info.path), info)); + infos.forEach(info => assert.strictEqual(projectService.getScriptInfoForPath(info.path), info)); } function verifyScriptInfosAreUndefined(files: FileOrFolder[]) { @@ -2325,7 +2343,7 @@ namespace ts.projectSystem { function verifyConfiguredProjectStateAfterUpdate(hasOpenRef: boolean) { checkNumberOfConfiguredProjects(projectService, 1); const configProject2 = projectService.configuredProjects.get(configFile.path); - assert.equal(configProject2, configProject1); + assert.strictEqual(configProject2, configProject1); checkProjectActualFiles(configProject2, [file1.path, file2.path, file3.path, configFile.path]); assert.equal(configProject2.hasOpenRef(), hasOpenRef); } @@ -2364,7 +2382,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 }); const configuredProject = projectService.configuredProjects.get(configFile.path); - assert(configuredProject.hasOpenRef()); // file1 and file3 + assert.isTrue(configuredProject.hasOpenRef()); // file1 and file3 checkProjectActualFiles(configuredProject, [file1.path, file3.path, configFile.path]); const inferredProject1 = projectService.inferredProjects[0]; checkProjectActualFiles(inferredProject1, [file2.path]); @@ -2376,23 +2394,23 @@ namespace ts.projectSystem { configFile.content = "{}"; host.reloadFS(files.concat(configFile)); // Time out is not yet run so there is project update pending - assert(configuredProject.hasOpenRef()); // Pending update and file2 might get into the project + assert.isTrue(configuredProject.hasOpenRef()); // Pending update and file2 might get into the project projectService.openClientFile(file4.path); checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 }); - assert.equal(projectService.configuredProjects.get(configFile.path), configuredProject); - assert(configuredProject.hasOpenRef()); // Pending update and F2 might get into the project - assert.equal(projectService.inferredProjects[0], inferredProject1); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject); + assert.isTrue(configuredProject.hasOpenRef()); // Pending update and F2 might get into the project + assert.strictEqual(projectService.inferredProjects[0], inferredProject1); const inferredProject2 = projectService.inferredProjects[1]; checkProjectActualFiles(inferredProject2, [file4.path]); host.runQueuedTimeoutCallbacks(); checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(configFile.path), configuredProject); - assert(configuredProject.hasOpenRef()); // file2 + assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject); + assert.isTrue(configuredProject.hasOpenRef()); // file2 checkProjectActualFiles(configuredProject, [file1.path, file2.path, file3.path, configFile.path]); - assert.equal(projectService.inferredProjects[0], inferredProject2); + assert.strictEqual(projectService.inferredProjects[0], inferredProject2); checkProjectActualFiles(inferredProject2, [file4.path]); }); @@ -2427,7 +2445,7 @@ namespace ts.projectSystem { options: {} }); service.checkNumberOfProjects({ externalProjects: 1 }); - assert(service.externalProjects[0].languageServiceEnabled, "language service should be enabled"); + assert.isTrue(service.externalProjects[0].languageServiceEnabled, "language service should be enabled"); service.openExternalProject({ projectFileName, @@ -2464,12 +2482,12 @@ namespace ts.projectSystem { projectService.openClientFile(f1.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); const project = projectService.configuredProjects.get(config.path); - assert(project.hasOpenRef()); // f1 + assert.isTrue(project.hasOpenRef()); // f1 assert.isFalse(project.isClosed()); projectService.closeClientFile(f1.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config.path), project); + assert.strictEqual(projectService.configuredProjects.get(config.path), project); assert.isFalse(project.hasOpenRef()); // No files assert.isFalse(project.isClosed()); @@ -2488,7 +2506,7 @@ namespace ts.projectSystem { projectService.openClientFile(f4.path); projectService.checkNumberOfProjects({ inferredProjects: 1 }); assert.isFalse(project.hasOpenRef()); // No files - assert(project.isClosed()); + assert.isTrue(project.isClosed()); for (const f of [f1, f2, f3]) { // All the script infos should not be present since the project is closed and orphan script infos are collected @@ -2540,7 +2558,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { configuredProjects: 1 }); const project = configuredProjectAt(projectService, 0); assert.isFalse(project.languageServiceEnabled, "Language service enabled"); - assert(!!lastEvent, "should receive event"); + assert.isTrue(!!lastEvent, "should receive event"); assert.equal(lastEvent.data.project, project, "project name"); assert.equal(lastEvent.data.project.getProjectName(), config.path, "config path"); assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state"); @@ -2548,9 +2566,9 @@ namespace ts.projectSystem { host.reloadFS([f1, f2, configWithExclude]); host.checkTimeoutQueueLengthAndRun(2); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert(project.languageServiceEnabled, "Language service enabled"); + assert.isTrue(project.languageServiceEnabled, "Language service enabled"); assert.equal(lastEvent.data.project, project, "project"); - assert(lastEvent.data.languageServiceEnabled, "Language service state"); + assert.isTrue(lastEvent.data.languageServiceEnabled, "Language service state"); }); it("syntactic features work even if language service is disabled", () => { @@ -2592,7 +2610,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { configuredProjects: 1 }); const project = configuredProjectAt(projectService, 0); assert.isFalse(project.languageServiceEnabled, "Language service enabled"); - assert(!!lastEvent, "should receive event"); + assert.isTrue(!!lastEvent, "should receive event"); assert.equal(lastEvent.data.project, project, "project name"); assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state"); @@ -2747,7 +2765,7 @@ namespace ts.projectSystem { host.runQueuedTimeoutCallbacks(); watchedRecursiveDirectories.pop(); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); checkProjectActualFiles(project, mapDefined(files, file => file === file2a ? undefined : file.path)); checkWatchedFiles(host, mapDefined(files, file => file === file1 ? undefined : file.path)); checkWatchedDirectories(host, [], /*recursive*/ false); @@ -2756,7 +2774,7 @@ namespace ts.projectSystem { // On next file open the files file2a should be closed and not watched any more projectService.openClientFile(file2.path); checkNumberOfProjects(projectService, { configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(configFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(configFile.path), project); checkProjectActualFiles(project, mapDefined(files, file => file === file2a ? undefined : file.path)); checkWatchedFiles(host, [libFile.path, configFile.path]); checkWatchedDirectories(host, [], /*recursive*/ false); @@ -2979,7 +2997,7 @@ namespace ts.projectSystem { }); projectService.checkNumberOfProjects({ externalProjects: 1 }); const typeAcquisition = projectService.externalProjects[0].getTypeAcquisition(); - assert(typeAcquisition.enable, "Typine acquisition should be enabled"); + assert.isTrue(typeAcquisition.enable, "Typine acquisition should be enabled"); }); }); @@ -3050,7 +3068,7 @@ namespace ts.projectSystem { const localFunctionNavToRequst = makeSessionRequest(CommandNames.Navto, { searchValue: "foo", file: file1.path, projectFileName: configFile.path }); const items2 = session.executeCommand(localFunctionNavToRequst).response as protocol.NavtoItem[]; - assert(containsNavToItem(items2, "foo", "function"), `Cannot find function symbol "foo".`); + assert.isTrue(containsNavToItem(items2, "foo", "function"), `Cannot find function symbol "foo".`); }); }); @@ -3264,18 +3282,18 @@ namespace ts.projectSystem { projectService.openClientFile(f.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); const project = projectService.configuredProjects.get(config.path); - assert(project.hasOpenRef()); // f + assert.isTrue(project.hasOpenRef()); // f projectService.closeClientFile(f.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config.path), project); + assert.strictEqual(projectService.configuredProjects.get(config.path), project); assert.isFalse(project.hasOpenRef()); // No files assert.isFalse(project.isClosed()); projectService.openClientFile(f.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); - assert.equal(projectService.configuredProjects.get(config.path), project); - assert(project.hasOpenRef()); // f + assert.strictEqual(projectService.configuredProjects.get(config.path), project); + assert.isTrue(project.hasOpenRef()); // f assert.isFalse(project.isClosed()); }); }); @@ -3872,16 +3890,16 @@ namespace ts.projectSystem { { file: file2.path } ); let errorResult = session.executeCommand(file2GetErrRequest).response; - assert(errorResult.length === 0); + assert.isTrue(errorResult.length === 0); const closeFileRequest = makeSessionRequest(CommandNames.Close, { file: file1.path }); session.executeCommand(closeFileRequest); errorResult = session.executeCommand(file2GetErrRequest).response; - assert(errorResult.length !== 0); + assert.isTrue(errorResult.length !== 0); openFilesForSession([file1], session); errorResult = session.executeCommand(file2GetErrRequest).response; - assert(errorResult.length === 0); + assert.isTrue(errorResult.length === 0); }); it("should be turned on for js-only external projects", () => { @@ -3917,7 +3935,7 @@ namespace ts.projectSystem { { file: dTsFile.path } ); const errorResult = session.executeCommand(dTsFileGetErrRequest).response; - assert(errorResult.length === 0); + assert.isTrue(errorResult.length === 0); }); it("should be turned on for js-only external projects with skipLibCheck=false", () => { @@ -3953,7 +3971,7 @@ namespace ts.projectSystem { { file: dTsFile.path } ); const errorResult = session.executeCommand(dTsFileGetErrRequest).response; - assert(errorResult.length === 0); + assert.isTrue(errorResult.length === 0); }); it("should not report bind errors for declaration files with skipLibCheck=true", () => { @@ -3984,14 +4002,14 @@ namespace ts.projectSystem { { file: dTsFile1.path } ); const error1Result = session.executeCommand(dTsFile1GetErrRequest).response; - assert(error1Result.length === 0); + assert.isTrue(error1Result.length === 0); const dTsFile2GetErrRequest = makeSessionRequest( CommandNames.SemanticDiagnosticsSync, { file: dTsFile2.path } ); const error2Result = session.executeCommand(dTsFile2GetErrRequest).response; - assert(error2Result.length === 0); + assert.isTrue(error2Result.length === 0); }); it("should report semanitc errors for loose JS files with '// @ts-check' and skipLibCheck=true", () => { @@ -4012,7 +4030,7 @@ namespace ts.projectSystem { { file: jsFile.path } ); const errorResult = session.executeCommand(getErrRequest).response; - assert(errorResult.length === 1); + assert.isTrue(errorResult.length === 1); assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code); }); @@ -4039,7 +4057,7 @@ namespace ts.projectSystem { { file: jsFile.path } ); const errorResult = session.executeCommand(getErrRequest).response; - assert(errorResult.length === 1); + assert.isTrue(errorResult.length === 1); assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code); }); @@ -4068,7 +4086,7 @@ namespace ts.projectSystem { { file: jsFile.path } ); const errorResult = session.executeCommand(getErrRequest).response; - assert(errorResult.length === 1); + assert.isTrue(errorResult.length === 1); assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code); }); }); @@ -4096,7 +4114,7 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 1); const inferredProject = projectService.inferredProjects[0]; - assert(inferredProject.containsFile(file1.path)); + assert.isTrue(inferredProject.containsFile(file1.path)); }); it("should be able to handle @types if input file list is empty", () => { @@ -4236,7 +4254,7 @@ namespace ts.projectSystem { arguments: { file: f1.path } }); checkScriptInfoAndProjects(0, f1.content, "contents of closed file"); - assert.equal(info.getSnapshot(), snap); + assert.strictEqual(info.getSnapshot(), snap); // reload from temp file session.executeCommandSeq({ @@ -4244,7 +4262,7 @@ namespace ts.projectSystem { arguments: { file: f1.path, tmpfile: tmp.path } }); checkScriptInfoAndProjects(0, tmp.content, "contents of temp file"); - assert.notEqual(info.getSnapshot(), snap); + assert.notStrictEqual(info.getSnapshot(), snap); // reload from own file session.executeCommandSeq({ @@ -4252,11 +4270,11 @@ namespace ts.projectSystem { arguments: { file: f1.path } }); checkScriptInfoAndProjects(0, f1.content, "contents of closed file"); - assert.notEqual(info.getSnapshot(), snap); + assert.notStrictEqual(info.getSnapshot(), snap); function checkScriptInfoAndProjects(inferredProjects: number, contentsOfInfo: string, captionForContents: string) { checkNumberOfProjects(projectService, { inferredProjects }); - assert.equal(projectService.getScriptInfo(f1.path), info); + assert.strictEqual(projectService.getScriptInfo(f1.path), info); checkScriptInfoContents(contentsOfInfo, captionForContents); } @@ -4515,7 +4533,7 @@ namespace ts.projectSystem { seq: 2, arguments: { projectFileName: projectName } }).response as ReadonlyArray; - assert(diags.length === 0); + assert.isTrue(diags.length === 0); session.executeCommand({ type: "request", @@ -4529,7 +4547,7 @@ namespace ts.projectSystem { seq: 4, arguments: { projectFileName: projectName } }).response as ReadonlyArray; - assert(diagsAfterUpdate.length === 0); + assert.isTrue(diagsAfterUpdate.length === 0); }); it("for external project", () => { @@ -4556,7 +4574,7 @@ namespace ts.projectSystem { seq: 2, arguments: { projectFileName } }).response as ReadonlyArray; - assert(diags.length === 0); + assert.isTrue(diags.length === 0); session.executeCommand({ type: "request", @@ -4574,7 +4592,7 @@ namespace ts.projectSystem { seq: 4, arguments: { projectFileName } }).response as ReadonlyArray; - assert(diagsAfterUpdate.length === 0); + assert.isTrue(diagsAfterUpdate.length === 0); }); }); @@ -4758,7 +4776,7 @@ namespace ts.projectSystem { isCancellationRequested: () => false, setRequest: requestId => { if (expectedRequestId === undefined) { - assert(false, "unexpected call"); + assert.isTrue(false, "unexpected call"); } assert.equal(requestId, expectedRequestId); }, @@ -5017,7 +5035,7 @@ namespace ts.projectSystem { ); const highlightResponse = session.executeCommand(highlightRequest).response as protocol.OccurrencesResponseItem[]; const firstOccurence = highlightResponse[0]; - assert(firstOccurence.isInString, "Highlights should be marked with isInString"); + assert.isTrue(firstOccurence.isInString, "Highlights should be marked with isInString"); } { @@ -5026,7 +5044,7 @@ namespace ts.projectSystem { { file: file1.path, line: 3, offset: 13 } ); const highlightResponse = session.executeCommand(highlightRequest).response as protocol.OccurrencesResponseItem[]; - assert(highlightResponse.length === 2); + assert.isTrue(highlightResponse.length === 2); const firstOccurence = highlightResponse[0]; assert.isUndefined(firstOccurence.isInString, "Highlights should not be marked with isInString if on property name"); } @@ -5037,7 +5055,7 @@ namespace ts.projectSystem { { file: file1.path, line: 4, offset: 14 } ); const highlightResponse = session.executeCommand(highlightRequest).response as protocol.OccurrencesResponseItem[]; - assert(highlightResponse.length === 2); + assert.isTrue(highlightResponse.length === 2); const firstOccurence = highlightResponse[0]; assert.isUndefined(firstOccurence.isInString, "Highlights should not be marked with isInString if on indexer"); } @@ -5061,13 +5079,13 @@ namespace ts.projectSystem { let project = projectService.inferredProjects[0]; let options = project.getCompilationSettings(); - assert(options.maxNodeModuleJsDepth === 2); + assert.isTrue(options.maxNodeModuleJsDepth === 2); // Assert the option sticks projectService.setCompilerOptionsForInferredProjects({ target: ScriptTarget.ES2016 }); project = projectService.inferredProjects[0]; options = project.getCompilationSettings(); - assert(options.maxNodeModuleJsDepth === 2); + assert.isTrue(options.maxNodeModuleJsDepth === 2); }); it("should return to normal state when all js root files are removed from project", () => { @@ -5090,7 +5108,7 @@ namespace ts.projectSystem { projectService.openClientFile(file2.path); project = projectService.inferredProjects[0]; - assert(project.getCompilationSettings().maxNodeModuleJsDepth === 2); + assert.isTrue(project.getCompilationSettings().maxNodeModuleJsDepth === 2); projectService.closeClientFile(file2.path); project = projectService.inferredProjects[0]; @@ -5134,7 +5152,7 @@ namespace ts.projectSystem { seq: 2, arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true } }).response as ReadonlyArray; - assert(diags.length === 2); + assert.isTrue(diags.length === 2); configFile.content = configFileContentWithoutCommentLine; host.reloadFS([file, configFile]); @@ -5145,7 +5163,7 @@ namespace ts.projectSystem { seq: 2, arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true } }).response as ReadonlyArray; - assert(diagsAfterEdit.length === 2); + assert.isTrue(diagsAfterEdit.length === 2); verifyDiagnostic(diags[0], diagsAfterEdit[0]); verifyDiagnostic(diags[1], diagsAfterEdit[1]); @@ -5271,7 +5289,7 @@ namespace ts.projectSystem { function verifyCalledOn(callback: CalledMaps, name: string) { const calledMap = calledMaps[callback]; const result = calledMap.get(name); - assert(result && !!result.length, `${callback} should be called with name: ${name}: ${arrayFrom(calledMap.keys())}`); + assert.isTrue(result && !!result.length, `${callback} should be called with name: ${name}: ${arrayFrom(calledMap.keys())}`); } function verifyNoCall(callback: CalledMaps) { @@ -5283,7 +5301,7 @@ namespace ts.projectSystem { const calledMap = calledMaps[callback]; ts.TestFSWithWatch.verifyMapSize(callback, calledMap, arrayFrom(expectedKeys.keys())); expectedKeys.forEach((called, name) => { - assert(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`); + assert.isTrue(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`); assert.equal(calledMap.get(name).length, called, `${callback} is expected to be called ${called} times with ${name}. Actual entry: ${calledMap.get(name)}`); }); } @@ -5356,10 +5374,10 @@ namespace ts.projectSystem { try { // trigger synchronization to make sure that LSHost will try to find 'f2' module on disk verifyImportedDiagnostics(); - assert(false, `should not find file '${imported.path}'`); + assert.isTrue(false, `should not find file '${imported.path}'`); } catch (e) { - assert(e.message.indexOf(`Could not find file: '${imported.path}'.`) === 0); + assert.isTrue(e.message.indexOf(`Could not find file: '${imported.path}'.`) === 0); } const f2Lookups = getLocationsForModuleLookup("f2"); callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, f2Lookups, 1); @@ -5544,7 +5562,7 @@ namespace ts.projectSystem { callsTrackingHost.verifyNoHostCallsExceptFileExistsOnce(["/a/b/models/tsconfig.json", "/a/b/models/jsconfig.json"]); checkNumberOfConfiguredProjects(projectService, 1); - assert.equal(projectService.configuredProjects.get(tsconfigFile.path), project); + assert.strictEqual(projectService.configuredProjects.get(tsconfigFile.path), project); }); describe("WatchDirectories for config file with", () => { @@ -5631,7 +5649,7 @@ namespace ts.projectSystem { callsTrackingHost.verifyNoCall(CalledMapsWithFiveArgs.readDirectory); checkNumberOfConfiguredProjects(projectService, 1); - assert.equal(projectService.configuredProjects.get(canonicalConfigPath), project); + assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project); verifyProjectAndWatchedDirectories(); callsTrackingHost.clear(); @@ -5640,7 +5658,7 @@ namespace ts.projectSystem { assert.equal(configFile2, configFileName); checkNumberOfConfiguredProjects(projectService, 1); - assert.equal(projectService.configuredProjects.get(canonicalConfigPath), project); + assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project); verifyProjectAndWatchedDirectories(); callsTrackingHost.verifyNoHostCalls(); @@ -5842,7 +5860,7 @@ namespace ts.projectSystem { forEach(actual, f => { assert.isFalse(seen.has(f), `${caption}: Found duplicate ${f}. Actual: ${actual} Expected: ${expected}`); seen.set(f, true); - assert(contains(expected, f), `${caption}: Expected not to contain ${f}. Actual: ${actual} Expected: ${expected}`); + assert.isTrue(contains(expected, f), `${caption}: Expected not to contain ${f}. Actual: ${actual} Expected: ${expected}`); }); } @@ -6313,7 +6331,7 @@ namespace ts.projectSystem { else { // file2 addition wont be detected projectFiles.pop(); - assert(host.fileExists(file2.path)); + assert.isTrue(host.fileExists(file2.path)); } verifyProject(); @@ -6380,7 +6398,7 @@ namespace ts.projectSystem { assert.equal(projectChangedEvents.length, expectedEvents.length, `Incorrect number of events Actual: ${eventsToString(projectChangedEvents)} Expected: ${eventsToString(expectedEvents)}`); forEach(projectChangedEvents, (actualEvent, i) => { const expectedEvent = expectedEvents[i]; - assert.equal(actualEvent.eventName, expectedEvent.eventName); + assert.strictEqual(actualEvent.eventName, expectedEvent.eventName); verifyFiles("openFiles", actualEvent.data.openFiles, expectedEvent.data.openFiles); }); diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index cb2d350744c..a84520095a4 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -323,7 +323,7 @@ namespace ts.projectSystem { typeAcquisition: { enable: true, include: ["jquery"] } }); - assert(enqueueIsCalled, "expected enqueueIsCalled to be true"); + assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true"); installer.installAll(/*expectedCount*/ 1); // auto is set in type acquisition - use it even if project contains only .ts files @@ -629,7 +629,7 @@ namespace ts.projectSystem { installer.executePendingCommands(); // expected all typings file to exist for (const f of typingFiles) { - assert(host.fileExists(f.path), `expected file ${f.path} to exist`); + assert.isTrue(host.fileExists(f.path), `expected file ${f.path} to exist`); } host.checkTimeoutQueueLengthAndRun(2); checkNumberOfProjects(projectService, { externalProjects: 1 }); @@ -965,8 +965,8 @@ namespace ts.projectSystem { installer.installAll(/*expectedCount*/1); - assert(host.fileExists(node.path), "typings for 'node' should be created"); - assert(host.fileExists(commander.path), "typings for 'commander' should be created"); + assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created"); + assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created"); checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]); }); @@ -1009,7 +1009,7 @@ namespace ts.projectSystem { installer.installAll(/*expectedCount*/ 1); }); - it("cached unresolved typings are not recomputed if program structure did not change", () => { + it("should recompute resolutions after typings are installed", () => { const host = createServerHost([]); const session = createSession(host); const f = { @@ -1051,7 +1051,7 @@ namespace ts.projectSystem { session.executeCommand(changeRequest); host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); - assert.equal(version1, version2, "set of unresolved imports should not change"); + assert.notEqual(version1, version2, "set of unresolved imports should change"); }); }); @@ -1107,7 +1107,7 @@ namespace ts.projectSystem { projectService.openClientFile(f1.path); installer.checkPendingCommands(/*expectedCount*/ 0); - assert(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); + assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); }); }); @@ -1256,7 +1256,7 @@ namespace ts.projectSystem { installer.installAll(/*expectedCount*/ 1); - assert(seenTelemetryEvent); + assert.isTrue(seenTelemetryEvent); host.checkTimeoutQueueLengthAndRun(2); checkNumberOfProjects(projectService, { inferredProjects: 1 }); checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]); @@ -1307,10 +1307,10 @@ namespace ts.projectSystem { installer.installAll(/*expectedCount*/ 1); - assert(!!beginEvent); - assert(!!endEvent); - assert(beginEvent.eventId === endEvent.eventId); - assert(endEvent.installSuccess); + assert.isTrue(!!beginEvent); + assert.isTrue(!!endEvent); + assert.isTrue(beginEvent.eventId === endEvent.eventId); + assert.isTrue(endEvent.installSuccess); host.checkTimeoutQueueLengthAndRun(2); checkNumberOfProjects(projectService, { inferredProjects: 1 }); checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]); @@ -1353,9 +1353,9 @@ namespace ts.projectSystem { installer.installAll(/*expectedCount*/ 1); - assert(!!beginEvent); - assert(!!endEvent); - assert(beginEvent.eventId === endEvent.eventId); + assert.isTrue(!!beginEvent); + assert.isTrue(!!endEvent); + assert.isTrue(beginEvent.eventId === endEvent.eventId); assert.isFalse(endEvent.installSuccess); checkNumberOfProjects(projectService, { inferredProjects: 1 }); checkProjectActualFiles(projectService.inferredProjects[0], [f1.path]); diff --git a/src/harness/unittests/versionCache.ts b/src/harness/unittests/versionCache.ts index 60b478d4869..bbd23f25dac 100644 --- a/src/harness/unittests/versionCache.ts +++ b/src/harness/unittests/versionCache.ts @@ -34,7 +34,7 @@ var p:Point=new Point(); var q:Point=p;`; const { lines } = server.LineIndex.linesFromText(testContent); - assert(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line"); + assert.isTrue(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line"); const lineIndex = new server.LineIndex(); lineIndex.load(lines); @@ -94,7 +94,7 @@ that was purple at the tips and grew 1cm per day`; ({ lines, lineMap } = server.LineIndex.linesFromText(testContent)); - assert(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line"); + assert.isTrue(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line"); const lineIndex = new server.LineIndex(); lineIndex.load(lines); @@ -203,10 +203,10 @@ and grew 1cm per day`; const testFileName = "src/compiler/scanner.ts"; testContent = Harness.IO.readFile(testFileName); const totalChars = testContent.length; - assert(totalChars > 0, "Failed to read test file."); + assert.isTrue(totalChars > 0, "Failed to read test file."); ({ lines, lineMap } = server.LineIndex.linesFromText(testContent)); - assert(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line"); + assert.isTrue(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line"); lineIndex = new server.LineIndex(); lineIndex.load(lines); diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 8accd6f621e..54572814d8e 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -162,7 +162,7 @@ namespace Utils { /** * Reads the directory at the given path and retrieves a list of file names and a list * of directory names within it. Suitable for use with ts.matchFiles() - * @param path The path to the directory to be read + * @param path The path to the directory to be read */ getAccessibleFileSystemEntries(path: string) { const entry = this.traversePath(path); diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 104b33e8ee6..8cc65c7a0b2 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -139,7 +139,7 @@ interface Array {}` function checkMapKeys(caption: string, map: Map, expectedKeys: ReadonlyArray) { verifyMapSize(caption, map, expectedKeys); for (const name of expectedKeys) { - assert(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); + assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); } } diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index 001c63b44a1..2a1f6dd6433 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -3596,8 +3596,8 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec slot: string; readonly shadowRoot: ShadowRoot | null; getAttribute(name: string): string | null; - getAttributeNode(name: string): Attr; - getAttributeNodeNS(namespaceURI: string, localName: string): Attr; + getAttributeNode(name: string): Attr | null; + getAttributeNodeNS(namespaceURI: string, localName: string): Attr | null; getAttributeNS(namespaceURI: string, localName: string): string; getBoundingClientRect(): ClientRect; getClientRects(): ClientRectList; @@ -3757,9 +3757,10 @@ declare var External: { }; interface File extends Blob { - readonly lastModifiedDate: any; + readonly lastModifiedDate: Date; readonly name: string; readonly webkitRelativePath: string; + readonly lastModified: number; } declare var File: { @@ -5200,6 +5201,10 @@ interface HTMLIFrameElement extends HTMLElement, GetSVGDocument { * Sets or retrieves the width of the object. */ width: string; + /** + * Sets or retrives the content of the page that is to contain. + */ + srcdoc: string; addEventListener(type: K, listener: (this: HTMLIFrameElement, ev: HTMLIFrameElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: HTMLIFrameElement, ev: HTMLIFrameElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -5495,8 +5500,9 @@ interface HTMLInputElement extends HTMLElement { * Sets the start and end positions of a selection in a text field. * @param start The offset into the text field for the start of the selection. * @param end The offset into the text field for the end of the selection. + * @param direction The direction in which the selection is performed. */ - setSelectionRange(start?: number, end?: number, direction?: string): void; + setSelectionRange(start: number, end: number, direction?: "forward" | "backward" | "none"): void; /** * Decrements a range input control's value by the value given by the Step attribute. If the optional parameter is used, it will decrement the input control's step value multiplied by the parameter's value. * @param n Value to decrement the value by. @@ -6057,6 +6063,7 @@ interface HTMLObjectElement extends HTMLElement, GetSVGDocument { * Returns whether an element will successfully validate based on forms validation rules and constraints. */ readonly willValidate: boolean; + typemustmatch: boolean; /** * Returns whether a form will validate when it is submitted, without having to submit it. */ @@ -6958,8 +6965,9 @@ interface HTMLTextAreaElement extends HTMLElement { * Sets the start and end positions of a selection in a text field. * @param start The offset into the text field for the start of the selection. * @param end The offset into the text field for the end of the selection. + * @param direction The direction in which the selection is performed. */ - setSelectionRange(start: number, end: number): void; + setSelectionRange(start: number, end: number, direction?: "forward" | "backward" | "none"): void; addEventListener(type: K, listener: (this: HTMLTextAreaElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: HTMLTextAreaElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -12213,7 +12221,7 @@ interface URL { declare var URL: { prototype: URL; - new(url: string, base?: string): URL; + new(url: string, base?: string | URL): URL; createObjectURL(object: any, options?: ObjectURLOptions): string; revokeObjectURL(url: string): void; }; @@ -15029,6 +15037,7 @@ interface SVGElementTagNameMap { "view": SVGViewElement; } +/** @deprecated Directly use HTMLElementTagNameMap or SVGElementTagNameMap as appropriate, instead. */ interface ElementTagNameMap extends HTMLElementTagNameMap, SVGElementTagNameMap { } declare var Audio: { new(src?: string): HTMLAudioElement; }; diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 14687838f6e..0f1a68de4c2 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -992,7 +992,6 @@ interface ReadonlyArray { * Combines two or more arrays. * @param items Additional items to add to the end of array1. */ - // TODO: https://github.com/Microsoft/TypeScript/issues/20454 concat(...items: (T[] | ReadonlyArray)[]): T[]; /** * Combines two or more arrays. diff --git a/src/lib/esnext.d.ts b/src/lib/esnext.d.ts index 71fab82a866..685a10ef4d7 100644 --- a/src/lib/esnext.d.ts +++ b/src/lib/esnext.d.ts @@ -1,2 +1,3 @@ -/// +/// /// +/// diff --git a/src/lib/esnext.promise.d.ts b/src/lib/esnext.promise.d.ts new file mode 100644 index 00000000000..28f903870b6 --- /dev/null +++ b/src/lib/esnext.promise.d.ts @@ -0,0 +1,12 @@ +/** + * Represents the completion of an asynchronous operation + */ +interface Promise { + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): Promise +} diff --git a/src/lib/webworker.generated.d.ts b/src/lib/webworker.generated.d.ts index 6eb17c33c5e..4a582d3c9c6 100644 --- a/src/lib/webworker.generated.d.ts +++ b/src/lib/webworker.generated.d.ts @@ -414,9 +414,10 @@ declare var EventTarget: { }; interface File extends Blob { - readonly lastModifiedDate: any; + readonly lastModifiedDate: Date; readonly name: string; readonly webkitRelativePath: string; + readonly lastModified: number; } declare var File: { @@ -1061,7 +1062,7 @@ interface URL { declare var URL: { prototype: URL; - new(url: string, base?: string): URL; + new(url: string, base?: string | URL): URL; createObjectURL(object: any, options?: ObjectURLOptions): string; revokeObjectURL(url: string): void; }; diff --git a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl index d7800b1c397..b01830dcac5 100644 --- a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -154,7 +154,7 @@ - + @@ -867,8 +867,8 @@ - - + + @@ -879,8 +879,8 @@ - - + + @@ -891,8 +891,8 @@ - - + + @@ -903,8 +903,8 @@ - - + + @@ -915,6 +915,9 @@ + + + @@ -1381,7 +1384,7 @@ - + @@ -1545,8 +1548,8 @@ - - + + @@ -1962,8 +1965,8 @@ - - + + @@ -1986,8 +1989,8 @@ - - + + @@ -2067,6 +2070,15 @@ + + + + + + + + + @@ -2364,8 +2376,8 @@ - - + + @@ -2376,8 +2388,8 @@ - - + + @@ -2388,8 +2400,8 @@ - - + + @@ -2400,8 +2412,8 @@ - - + + @@ -2484,8 +2496,8 @@ - - + + @@ -3453,15 +3465,6 @@ - - - - - - - - - @@ -3756,8 +3759,8 @@ - - + + @@ -3780,8 +3783,8 @@ - - + + @@ -3801,8 +3804,8 @@ - - + + @@ -3954,8 +3957,8 @@ - - + + @@ -3966,8 +3969,8 @@ - - + + @@ -3978,8 +3981,8 @@ - - + + @@ -3990,8 +3993,8 @@ - - + + @@ -4527,8 +4530,8 @@ - - + + @@ -4749,6 +4752,15 @@ + + + + + + + + + @@ -4839,6 +4851,15 @@ + + + + + + + + + @@ -5445,8 +5466,8 @@ - - + + @@ -5901,8 +5922,8 @@ - - + + @@ -6330,8 +6351,8 @@ - - + + @@ -6450,6 +6471,9 @@ + + + @@ -6558,6 +6582,9 @@ + + + @@ -7413,6 +7440,9 @@ + + + diff --git a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7349ab473c9..921ca600aa6 100644 --- a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -867,8 +867,8 @@ - - + + @@ -879,8 +879,8 @@ - - + + @@ -891,8 +891,8 @@ - - + + @@ -903,8 +903,8 @@ - - + + @@ -915,6 +915,9 @@ + + + @@ -1545,8 +1548,8 @@ - - + + @@ -1962,8 +1965,8 @@ - - + + @@ -1986,8 +1989,8 @@ - - + + @@ -2067,6 +2070,15 @@ + + + + + + + + + @@ -2364,8 +2376,8 @@ - - + + @@ -2376,8 +2388,8 @@ - - + + @@ -2388,8 +2400,8 @@ - - + + @@ -2400,8 +2412,8 @@ - - + + @@ -2484,8 +2496,8 @@ - - + + @@ -3244,7 +3256,7 @@ - + @@ -3453,15 +3465,6 @@ - - - - - - - - - @@ -3756,8 +3759,8 @@ - - + + @@ -3780,8 +3783,8 @@ - - + + @@ -3801,8 +3804,8 @@ - - + + @@ -3954,8 +3957,8 @@ - - + + @@ -3966,8 +3969,8 @@ - - + + @@ -3978,8 +3981,8 @@ - - + + @@ -3990,8 +3993,8 @@ - - + + @@ -4527,8 +4530,8 @@ - - + + @@ -4749,6 +4752,15 @@ + + + + + + + + + @@ -4839,6 +4851,15 @@ + + + + + + + + + @@ -5445,8 +5466,8 @@ - - + + @@ -5901,8 +5922,8 @@ - - + + @@ -6330,8 +6351,8 @@ - - + + @@ -6450,6 +6471,9 @@ + + + @@ -6558,6 +6582,9 @@ + + + @@ -6628,7 +6655,7 @@ - + @@ -7413,6 +7440,9 @@ + + + diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index 17076cc9ae0..30850814f26 100644 --- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -876,8 +876,8 @@ - - + + @@ -888,7 +888,7 @@ - + @@ -900,8 +900,8 @@ - - + + @@ -912,8 +912,8 @@ - - + + @@ -924,6 +924,9 @@ + + + @@ -1554,8 +1557,8 @@ - - + + @@ -1971,7 +1974,7 @@ - + @@ -1995,7 +1998,7 @@ - + @@ -2076,6 +2079,15 @@ + + + + + + + + + @@ -2098,7 +2110,7 @@ - + @@ -2373,8 +2385,8 @@ - - + + @@ -2385,8 +2397,8 @@ - - + + @@ -2397,8 +2409,8 @@ - - + + @@ -2409,8 +2421,8 @@ - - + + @@ -2493,7 +2505,7 @@ - + @@ -2860,7 +2872,7 @@ - + @@ -3462,15 +3474,6 @@ - - - - - - - - - @@ -3765,7 +3768,7 @@ - + @@ -3789,8 +3792,8 @@ - - + + @@ -3810,8 +3813,8 @@ - - + + @@ -3963,8 +3966,8 @@ - - + + @@ -3975,8 +3978,8 @@ - - + + @@ -3987,7 +3990,7 @@ - + @@ -3999,7 +4002,7 @@ - + @@ -4222,7 +4225,7 @@ - + @@ -4231,7 +4234,7 @@ - + @@ -4536,7 +4539,7 @@ - + @@ -4758,6 +4761,15 @@ + + + + + + + + + @@ -4848,6 +4860,15 @@ + + + + + + + + + @@ -5454,7 +5475,7 @@ - + @@ -5910,8 +5931,8 @@ - - + + @@ -6339,8 +6360,8 @@ - - + + @@ -6415,7 +6436,7 @@ - + @@ -6459,6 +6480,9 @@ + + + @@ -6567,6 +6591,9 @@ + + + @@ -7422,6 +7449,9 @@ + + + @@ -8695,7 +8725,7 @@ - + diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index be79110aafd..922edbca2de 100644 --- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -864,8 +864,8 @@ - - + + @@ -876,8 +876,8 @@ - - + + @@ -888,8 +888,8 @@ - - + + @@ -900,8 +900,8 @@ - - + + @@ -912,6 +912,9 @@ + + + @@ -1542,8 +1545,8 @@ - - + + @@ -1959,8 +1962,8 @@ - - + + @@ -1983,8 +1986,8 @@ - - + + @@ -2064,6 +2067,15 @@ + + + + + + + + + @@ -2188,7 +2200,7 @@ - + @@ -2361,8 +2373,8 @@ - - + + @@ -2373,8 +2385,8 @@ - - + + @@ -2385,8 +2397,8 @@ - - + + @@ -2397,8 +2409,8 @@ - - + + @@ -2481,8 +2493,8 @@ - - + + @@ -3450,15 +3462,6 @@ - - - - - - - - - @@ -3753,8 +3756,8 @@ - - + + @@ -3777,7 +3780,7 @@ - + @@ -3798,8 +3801,8 @@ - - + + @@ -3951,8 +3954,8 @@ - - + + @@ -3963,8 +3966,8 @@ - - + + @@ -3975,8 +3978,8 @@ - - + + @@ -3987,8 +3990,8 @@ - - + + @@ -4524,8 +4527,8 @@ - - + + @@ -4746,6 +4749,15 @@ + + + + + + + + + @@ -4836,6 +4848,15 @@ + + + + + + + + + @@ -5439,8 +5460,8 @@ - - + + @@ -5895,7 +5916,7 @@ - + @@ -6321,8 +6342,8 @@ - - + + @@ -6441,6 +6462,9 @@ + + + @@ -6549,6 +6573,9 @@ + + + @@ -7404,6 +7431,9 @@ + + + diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 1b1ece74cb2..c2febd1ee9b 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -427,7 +427,7 @@ - + @@ -436,7 +436,7 @@ - + @@ -523,7 +523,7 @@ - + @@ -876,8 +876,8 @@ - - + + @@ -888,8 +888,8 @@ - - + + @@ -900,8 +900,8 @@ - - + + @@ -912,8 +912,8 @@ - - + + @@ -924,6 +924,9 @@ + + + @@ -1066,7 +1069,7 @@ - + @@ -1417,7 +1420,7 @@ - + @@ -1429,7 +1432,7 @@ - + @@ -1554,8 +1557,8 @@ - - + + @@ -1971,8 +1974,8 @@ - - + + @@ -1995,8 +1998,8 @@ - - + + @@ -2076,6 +2079,15 @@ + + + + + + + + + @@ -2373,8 +2385,8 @@ - - + + @@ -2385,8 +2397,8 @@ - - + + @@ -2397,8 +2409,8 @@ - - + + @@ -2409,8 +2421,8 @@ - - + + @@ -2493,8 +2505,8 @@ - - + + @@ -2932,7 +2944,7 @@ - + @@ -2971,7 +2983,7 @@ - + @@ -3462,15 +3474,6 @@ - - - - - - - - - @@ -3765,8 +3768,8 @@ - - + + @@ -3789,8 +3792,8 @@ - - + + @@ -3810,8 +3813,8 @@ - - + + @@ -3901,7 +3904,7 @@ - + @@ -3963,8 +3966,8 @@ - - + + @@ -3975,8 +3978,8 @@ - - + + @@ -3987,8 +3990,8 @@ - - + + @@ -3999,8 +4002,8 @@ - - + + @@ -4536,8 +4539,8 @@ - - + + @@ -4758,6 +4761,15 @@ + + + + + + + + + @@ -4848,6 +4860,15 @@ + + + + + + + + + @@ -5454,8 +5475,8 @@ - - + + @@ -5910,8 +5931,8 @@ - - + + @@ -6339,8 +6360,8 @@ - - + + @@ -6459,6 +6480,9 @@ + + + @@ -6567,6 +6591,9 @@ + + + @@ -6943,7 +6970,7 @@ - + @@ -7081,7 +7108,7 @@ - + @@ -7422,6 +7449,9 @@ + + + diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index 4ae94d84fc8..efd7b273f5d 100644 --- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -876,8 +876,8 @@ - - + + @@ -888,8 +888,8 @@ - - + + @@ -900,8 +900,8 @@ - - + + @@ -912,8 +912,8 @@ - - + + @@ -924,6 +924,9 @@ + + + @@ -1554,8 +1557,8 @@ - - + + @@ -1971,8 +1974,8 @@ - - + + @@ -1995,8 +1998,8 @@ - - + + @@ -2076,6 +2079,15 @@ + + + + + + + + + @@ -2373,8 +2385,8 @@ - - + + @@ -2385,8 +2397,8 @@ - - + + @@ -2397,8 +2409,8 @@ - - + + @@ -2409,8 +2421,8 @@ - - + + @@ -2493,8 +2505,8 @@ - - + + @@ -3462,15 +3474,6 @@ - - - - - - - - - @@ -3765,8 +3768,8 @@ - - + + @@ -3789,8 +3792,8 @@ - - + + @@ -3810,8 +3813,8 @@ - - + + @@ -3963,8 +3966,8 @@ - - + + @@ -3975,8 +3978,8 @@ - - + + @@ -3987,8 +3990,8 @@ - - + + @@ -3999,8 +4002,8 @@ - - + + @@ -4536,8 +4539,8 @@ - - + + @@ -4758,6 +4761,15 @@ + + + + + + + + + @@ -4848,6 +4860,15 @@ + + + + + + + + + @@ -4924,7 +4945,7 @@ - + @@ -5454,8 +5475,8 @@ - - + + @@ -5503,7 +5524,7 @@ - + @@ -5910,8 +5931,8 @@ - - + + @@ -6339,8 +6360,8 @@ - - + + @@ -6459,6 +6480,9 @@ + + + @@ -6567,6 +6591,9 @@ + + + @@ -7422,6 +7449,9 @@ + + + diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index fc1b53929eb..8f52506d6b9 100644 --- a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -867,8 +867,8 @@ - - + + @@ -879,8 +879,8 @@ - - + + @@ -891,8 +891,8 @@ - - + + @@ -903,8 +903,8 @@ - - + + @@ -915,6 +915,9 @@ + + + @@ -1057,7 +1060,7 @@ - + @@ -1102,7 +1105,7 @@ - + @@ -1545,8 +1548,8 @@ - - + + @@ -1962,8 +1965,8 @@ - - + + @@ -1975,7 +1978,7 @@ - + @@ -1986,8 +1989,8 @@ - - + + @@ -2067,6 +2070,15 @@ + + + + + + + + + @@ -2155,7 +2167,7 @@ - + @@ -2364,8 +2376,8 @@ - - + + @@ -2376,8 +2388,8 @@ - - + + @@ -2388,8 +2400,8 @@ - - + + @@ -2400,8 +2412,8 @@ - - + + @@ -2484,8 +2496,8 @@ - - + + @@ -2578,7 +2590,7 @@ - + @@ -2824,7 +2836,7 @@ - + @@ -2851,7 +2863,7 @@ - + @@ -2923,7 +2935,7 @@ - + @@ -2932,7 +2944,7 @@ - + @@ -2941,7 +2953,7 @@ - + @@ -3025,7 +3037,7 @@ - + @@ -3043,7 +3055,7 @@ - + @@ -3453,15 +3465,6 @@ - - - - - - - - - @@ -3756,8 +3759,8 @@ - - + + @@ -3769,7 +3772,7 @@ - + @@ -3780,8 +3783,8 @@ - - + + @@ -3801,8 +3804,8 @@ - - + + @@ -3892,7 +3895,7 @@ - + @@ -3901,7 +3904,7 @@ - + @@ -3910,7 +3913,7 @@ - + @@ -3954,8 +3957,8 @@ - - + + @@ -3966,8 +3969,8 @@ - - + + @@ -3978,8 +3981,8 @@ - - + + @@ -3990,8 +3993,8 @@ - - + + @@ -4527,8 +4530,8 @@ - - + + @@ -4749,6 +4752,15 @@ + + + + + + + + + @@ -4839,6 +4851,15 @@ + + + + + + + + + @@ -5445,8 +5466,8 @@ - - + + @@ -5901,8 +5922,8 @@ - - + + @@ -6277,7 +6298,7 @@ - + @@ -6330,8 +6351,8 @@ - - + + @@ -6450,6 +6471,9 @@ + + + @@ -6558,6 +6582,9 @@ + + + @@ -7123,7 +7150,7 @@ - + @@ -7413,6 +7440,9 @@ + + + @@ -8452,7 +8482,7 @@ - + @@ -8461,7 +8491,7 @@ - + diff --git a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 37d536d4029..76488c39e76 100644 --- a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -145,7 +145,7 @@ - + @@ -154,7 +154,7 @@ - + @@ -163,7 +163,7 @@ - + @@ -172,7 +172,7 @@ - + @@ -181,7 +181,7 @@ - + @@ -418,7 +418,7 @@ - + @@ -532,7 +532,7 @@ - + @@ -541,7 +541,7 @@ - + @@ -796,7 +796,7 @@ - + @@ -867,8 +867,8 @@ - - + + @@ -879,8 +879,8 @@ - - + + @@ -891,8 +891,8 @@ - - + + @@ -903,8 +903,8 @@ - - + + @@ -915,6 +915,9 @@ + + + @@ -1102,7 +1105,7 @@ - + @@ -1545,8 +1548,8 @@ - - + + @@ -1962,8 +1965,8 @@ - - + + @@ -1986,8 +1989,8 @@ - - + + @@ -2067,6 +2070,15 @@ + + + + + + + + + @@ -2182,7 +2194,7 @@ - + @@ -2191,7 +2203,7 @@ - + @@ -2364,8 +2376,8 @@ - - + + @@ -2376,8 +2388,8 @@ - - + + @@ -2388,8 +2400,8 @@ - - + + @@ -2400,8 +2412,8 @@ - - + + @@ -2431,7 +2443,7 @@ - + @@ -2484,8 +2496,8 @@ - - + + @@ -2578,7 +2590,7 @@ - + @@ -2842,7 +2854,7 @@ - + @@ -2923,7 +2935,7 @@ - + @@ -2941,7 +2953,7 @@ - + @@ -3172,7 +3184,7 @@ - + @@ -3181,7 +3193,7 @@ - + @@ -3190,7 +3202,7 @@ - + @@ -3453,15 +3465,6 @@ - - - - - - - - - @@ -3756,8 +3759,8 @@ - - + + @@ -3780,8 +3783,8 @@ - - + + @@ -3793,7 +3796,7 @@ - + @@ -3801,8 +3804,8 @@ - - + + @@ -3901,7 +3904,7 @@ - + @@ -3954,8 +3957,8 @@ - - + + @@ -3966,8 +3969,8 @@ - - + + @@ -3978,8 +3981,8 @@ - - + + @@ -3990,8 +3993,8 @@ - - + + @@ -4527,8 +4530,8 @@ - - + + @@ -4585,7 +4588,7 @@ - + @@ -4594,7 +4597,7 @@ - + @@ -4749,6 +4752,15 @@ + + + + + + + + + @@ -4839,6 +4851,15 @@ + + + + + + + + + @@ -4915,7 +4936,7 @@ - + @@ -5188,7 +5209,7 @@ - + @@ -5197,7 +5218,7 @@ - + @@ -5206,7 +5227,7 @@ - + @@ -5215,7 +5236,7 @@ - + @@ -5224,7 +5245,7 @@ - + @@ -5233,7 +5254,7 @@ - + @@ -5242,7 +5263,7 @@ - + @@ -5251,7 +5272,7 @@ - + @@ -5260,7 +5281,7 @@ - + @@ -5269,7 +5290,7 @@ - + @@ -5296,7 +5317,7 @@ - + @@ -5305,7 +5326,7 @@ - + @@ -5314,7 +5335,7 @@ - + @@ -5323,7 +5344,7 @@ - + @@ -5332,7 +5353,7 @@ - + @@ -5341,7 +5362,7 @@ - + @@ -5350,7 +5371,7 @@ - + @@ -5359,7 +5380,7 @@ - + @@ -5386,7 +5407,7 @@ - + @@ -5395,7 +5416,7 @@ - + @@ -5404,7 +5425,7 @@ - + @@ -5413,7 +5434,7 @@ - + @@ -5445,8 +5466,8 @@ - - + + @@ -5494,7 +5515,7 @@ - + @@ -5686,7 +5707,7 @@ - + @@ -5695,7 +5716,7 @@ - + @@ -5767,7 +5788,7 @@ - + @@ -5776,7 +5797,7 @@ - + @@ -5785,7 +5806,7 @@ - + @@ -5794,7 +5815,7 @@ - + @@ -5803,7 +5824,7 @@ - + @@ -5812,7 +5833,7 @@ - + @@ -5821,7 +5842,7 @@ - + @@ -5830,7 +5851,7 @@ - + @@ -5839,7 +5860,7 @@ - + @@ -5848,7 +5869,7 @@ - + @@ -5857,7 +5878,7 @@ - + @@ -5866,7 +5887,7 @@ - + @@ -5901,8 +5922,8 @@ - - + + @@ -6094,7 +6115,7 @@ - + @@ -6103,7 +6124,7 @@ - + @@ -6112,7 +6133,7 @@ - + @@ -6121,7 +6142,7 @@ - + @@ -6142,7 +6163,7 @@ - + @@ -6151,7 +6172,7 @@ - + @@ -6160,7 +6181,7 @@ - + @@ -6169,7 +6190,7 @@ - + @@ -6178,7 +6199,7 @@ - + @@ -6187,7 +6208,7 @@ - + @@ -6196,7 +6217,7 @@ - + @@ -6205,7 +6226,7 @@ - + @@ -6214,7 +6235,7 @@ - + @@ -6223,7 +6244,7 @@ - + @@ -6232,7 +6253,7 @@ - + @@ -6241,7 +6262,7 @@ - + @@ -6250,7 +6271,7 @@ - + @@ -6259,7 +6280,7 @@ - + @@ -6268,7 +6289,7 @@ - + @@ -6277,7 +6298,7 @@ - + @@ -6286,7 +6307,7 @@ - + @@ -6295,7 +6316,7 @@ - + @@ -6304,7 +6325,7 @@ - + @@ -6330,8 +6351,8 @@ - - + + @@ -6450,6 +6471,9 @@ + + + @@ -6558,6 +6582,9 @@ + + + @@ -6639,6 +6666,9 @@ + + + @@ -6811,7 +6841,7 @@ - + @@ -7410,6 +7440,9 @@ + + + @@ -7603,7 +7636,7 @@ - + @@ -7612,7 +7645,7 @@ - + @@ -7621,7 +7654,7 @@ - + @@ -7630,7 +7663,7 @@ - + @@ -7639,7 +7672,7 @@ - + @@ -7657,7 +7690,7 @@ - + @@ -7666,7 +7699,7 @@ - + @@ -7675,7 +7708,7 @@ - + @@ -8449,7 +8482,7 @@ - + @@ -8458,7 +8491,7 @@ - + @@ -8485,7 +8518,7 @@ - + @@ -8530,7 +8563,7 @@ - + @@ -8539,7 +8572,7 @@ - + @@ -8920,7 +8953,7 @@ - + @@ -8929,7 +8962,7 @@ - + @@ -8938,7 +8971,7 @@ - + diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl index 66c672fa88f..2418583bb98 100644 --- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -337,7 +337,7 @@ - + @@ -867,8 +867,8 @@ - - + + @@ -879,8 +879,8 @@ - - + + @@ -891,8 +891,8 @@ - - + + @@ -903,8 +903,8 @@ - - + + @@ -915,6 +915,9 @@ + + + @@ -1545,8 +1548,8 @@ - - + + @@ -1962,8 +1965,8 @@ - - + + @@ -1986,8 +1989,8 @@ - - + + @@ -2067,6 +2070,15 @@ + + + + + + + + + @@ -2364,8 +2376,8 @@ - - + + @@ -2376,8 +2388,8 @@ - - + + @@ -2388,8 +2400,8 @@ - - + + @@ -2400,8 +2412,8 @@ - - + + @@ -2484,8 +2496,8 @@ - - + + @@ -2788,7 +2800,7 @@ - + @@ -2860,7 +2872,7 @@ - + @@ -3106,7 +3118,7 @@ - + @@ -3199,7 +3211,7 @@ - + @@ -3453,15 +3465,6 @@ - - - - - - - - - @@ -3756,8 +3759,8 @@ - - + + @@ -3780,8 +3783,8 @@ - - + + @@ -3801,8 +3804,8 @@ - - + + @@ -3954,8 +3957,8 @@ - - + + @@ -3966,8 +3969,8 @@ - - + + @@ -3978,8 +3981,8 @@ - - + + @@ -3990,8 +3993,8 @@ - - + + @@ -4486,7 +4489,7 @@ - + @@ -4527,8 +4530,8 @@ - - + + @@ -4749,6 +4752,15 @@ + + + + + + + + + @@ -4839,6 +4851,15 @@ + + + + + + + + + @@ -5445,8 +5466,8 @@ - - + + @@ -5901,8 +5922,8 @@ - - + + @@ -6330,8 +6351,8 @@ - - + + @@ -6450,6 +6471,9 @@ + + + @@ -6558,6 +6582,9 @@ + + + @@ -7413,6 +7440,9 @@ + + + diff --git a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 42e692a9cd2..42685f7a45e 100644 --- a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -857,8 +857,8 @@ - - + + @@ -869,8 +869,8 @@ - - + + @@ -881,8 +881,8 @@ - - + + @@ -893,8 +893,8 @@ - - + + @@ -905,6 +905,9 @@ + + + @@ -1371,7 +1374,7 @@ - + @@ -1380,7 +1383,7 @@ - + @@ -1535,8 +1538,8 @@ - - + + @@ -1952,8 +1955,8 @@ - - + + @@ -1976,8 +1979,8 @@ - - + + @@ -2057,6 +2060,15 @@ + + + + + + + + + @@ -2354,8 +2366,8 @@ - - + + @@ -2366,8 +2378,8 @@ - - + + @@ -2378,8 +2390,8 @@ - - + + @@ -2390,8 +2402,8 @@ - - + + @@ -2474,8 +2486,8 @@ - - + + @@ -3443,15 +3455,6 @@ - - - - - - - - - @@ -3746,8 +3749,8 @@ - - + + @@ -3770,8 +3773,8 @@ - - + + @@ -3791,8 +3794,8 @@ - - + + @@ -3944,8 +3947,8 @@ - - + + @@ -3956,8 +3959,8 @@ - - + + @@ -3968,8 +3971,8 @@ - - + + @@ -3980,8 +3983,8 @@ - - + + @@ -4517,8 +4520,8 @@ - - + + @@ -4739,6 +4742,15 @@ + + + + + + + + + @@ -4829,6 +4841,15 @@ + + + + + + + + + @@ -5432,8 +5453,8 @@ - - + + @@ -5888,8 +5909,8 @@ - - + + @@ -6314,8 +6335,8 @@ - - + + @@ -6434,6 +6455,9 @@ + + + @@ -6542,6 +6566,9 @@ + + + @@ -7397,6 +7424,9 @@ + + + diff --git a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl index 4d32384aa27..bcace1bd162 100644 --- a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -276,7 +276,7 @@ - + @@ -411,7 +411,7 @@ - + @@ -857,8 +857,8 @@ - - + + @@ -869,8 +869,8 @@ - - + + @@ -881,8 +881,8 @@ - - + + @@ -893,8 +893,8 @@ - - + + @@ -905,6 +905,9 @@ + + + @@ -1047,7 +1050,7 @@ - + @@ -1092,7 +1095,7 @@ - + @@ -1535,8 +1538,8 @@ - - + + @@ -1952,8 +1955,8 @@ - - + + @@ -1976,8 +1979,8 @@ - - + + @@ -2057,6 +2060,15 @@ + + + + + + + + + @@ -2181,7 +2193,7 @@ - + @@ -2354,8 +2366,8 @@ - - + + @@ -2366,8 +2378,8 @@ - - + + @@ -2378,8 +2390,8 @@ - - + + @@ -2390,8 +2402,8 @@ - - + + @@ -2474,8 +2486,8 @@ - - + + @@ -2904,7 +2916,7 @@ - + @@ -2913,7 +2925,7 @@ - + @@ -2922,7 +2934,7 @@ - + @@ -2931,7 +2943,7 @@ - + @@ -2940,7 +2952,7 @@ - + @@ -2952,7 +2964,7 @@ - + @@ -3443,15 +3455,6 @@ - - - - - - - - - @@ -3746,8 +3749,8 @@ - - + + @@ -3770,8 +3773,8 @@ - - + + @@ -3791,8 +3794,8 @@ - - + + @@ -3882,7 +3885,7 @@ - + @@ -3891,7 +3894,7 @@ - + @@ -3944,8 +3947,8 @@ - - + + @@ -3956,8 +3959,8 @@ - - + + @@ -3968,8 +3971,8 @@ - - + + @@ -3980,8 +3983,8 @@ - - + + @@ -4517,8 +4520,8 @@ - - + + @@ -4739,6 +4742,15 @@ + + + + + + + + + @@ -4829,6 +4841,15 @@ + + + + + + + + + @@ -5432,8 +5453,8 @@ - - + + @@ -5888,8 +5909,8 @@ - - + + @@ -6314,8 +6335,8 @@ - - + + @@ -6434,6 +6455,9 @@ + + + @@ -6542,6 +6566,9 @@ + + + @@ -6918,7 +6945,7 @@ - + @@ -7056,7 +7083,7 @@ - + @@ -7397,6 +7424,9 @@ + + + diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index e0f7c039abd..ca1dcf2d4cb 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -144,7 +144,7 @@ - + @@ -153,7 +153,7 @@ - + @@ -162,7 +162,7 @@ - + @@ -171,7 +171,7 @@ - + @@ -180,7 +180,7 @@ - + @@ -531,7 +531,7 @@ - + @@ -540,7 +540,7 @@ - + @@ -795,7 +795,7 @@ - + @@ -866,8 +866,8 @@ - - + + @@ -878,8 +878,8 @@ - - + + @@ -890,8 +890,8 @@ - - + + @@ -902,8 +902,8 @@ - - + + @@ -914,6 +914,9 @@ + + + @@ -1544,8 +1547,8 @@ - - + + @@ -1961,8 +1964,8 @@ - - + + @@ -1985,8 +1988,8 @@ - - + + @@ -2066,6 +2069,15 @@ + + + + + + + + + @@ -2363,8 +2375,8 @@ - - + + @@ -2375,8 +2387,8 @@ - - + + @@ -2387,8 +2399,8 @@ - - + + @@ -2399,8 +2411,8 @@ - - + + @@ -2483,8 +2495,8 @@ - - + + @@ -3033,7 +3045,7 @@ - + @@ -3452,15 +3464,6 @@ - - - - - - - - - @@ -3755,8 +3758,8 @@ - - + + @@ -3779,8 +3782,8 @@ - - + + @@ -3800,8 +3803,8 @@ - - + + @@ -3953,8 +3956,8 @@ - - + + @@ -3965,8 +3968,8 @@ - - + + @@ -3977,8 +3980,8 @@ - - + + @@ -3989,8 +3992,8 @@ - - + + @@ -4526,8 +4529,8 @@ - - + + @@ -4748,6 +4751,15 @@ + + + + + + + + + @@ -4838,6 +4850,15 @@ + + + + + + + + + @@ -5444,8 +5465,8 @@ - - + + @@ -5900,8 +5921,8 @@ - - + + @@ -6329,8 +6350,8 @@ - - + + @@ -6449,6 +6470,9 @@ + + + @@ -6557,6 +6581,9 @@ + + + @@ -7412,6 +7439,9 @@ + + + @@ -8922,7 +8952,7 @@ - + @@ -8931,7 +8961,7 @@ - + @@ -8940,7 +8970,7 @@ - + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 41eb3440c33..34cf0f01ff6 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -411,7 +411,7 @@ - + @@ -860,8 +860,8 @@ - - + + @@ -872,8 +872,8 @@ - - + + @@ -884,8 +884,8 @@ - - + + @@ -896,8 +896,8 @@ - - + + @@ -908,6 +908,9 @@ + + + @@ -978,7 +981,7 @@ - + @@ -1050,7 +1053,7 @@ - + @@ -1095,7 +1098,7 @@ - + @@ -1538,8 +1541,8 @@ - - + + @@ -1955,8 +1958,8 @@ - - + + @@ -1979,8 +1982,8 @@ - - + + @@ -2060,6 +2063,15 @@ + + + + + + + + + @@ -2175,7 +2187,7 @@ - + @@ -2357,8 +2369,8 @@ - - + + @@ -2369,8 +2381,8 @@ - - + + @@ -2381,8 +2393,8 @@ - - + + @@ -2393,8 +2405,8 @@ - - + + @@ -2477,8 +2489,8 @@ - - + + @@ -2571,7 +2583,7 @@ - + @@ -2916,7 +2928,7 @@ - + @@ -2925,7 +2937,7 @@ - + @@ -2934,7 +2946,7 @@ - + @@ -2982,7 +2994,7 @@ - + @@ -3009,7 +3021,7 @@ - + @@ -3018,7 +3030,7 @@ - + @@ -3165,7 +3177,7 @@ - + @@ -3174,7 +3186,7 @@ - + @@ -3446,15 +3458,6 @@ - - - - - - - - - @@ -3749,8 +3752,8 @@ - - + + @@ -3773,8 +3776,8 @@ - - + + @@ -3794,8 +3797,8 @@ - - + + @@ -3885,7 +3888,7 @@ - + @@ -3894,7 +3897,7 @@ - + @@ -3903,7 +3906,7 @@ - + @@ -3947,8 +3950,8 @@ - - + + @@ -3959,8 +3962,8 @@ - - + + @@ -3971,8 +3974,8 @@ - - + + @@ -3983,8 +3986,8 @@ - - + + @@ -4520,8 +4523,8 @@ - - + + @@ -4689,7 +4692,7 @@ - + @@ -4742,6 +4745,15 @@ + + + + + + + + + @@ -4832,6 +4844,15 @@ + + + + + + + + + @@ -5199,7 +5220,7 @@ - + @@ -5244,7 +5265,7 @@ - + @@ -5307,7 +5328,7 @@ - + @@ -5334,7 +5355,7 @@ - + @@ -5379,7 +5400,7 @@ - + @@ -5388,7 +5409,7 @@ - + @@ -5397,7 +5418,7 @@ - + @@ -5406,7 +5427,7 @@ - + @@ -5438,8 +5459,8 @@ - - + + @@ -5487,7 +5508,7 @@ - + @@ -5787,7 +5808,7 @@ - + @@ -5814,7 +5835,7 @@ - + @@ -5841,7 +5862,7 @@ - + @@ -5894,8 +5915,8 @@ - - + + @@ -6198,7 +6219,7 @@ - + @@ -6207,7 +6228,7 @@ - + @@ -6216,7 +6237,7 @@ - + @@ -6252,7 +6273,7 @@ - + @@ -6261,7 +6282,7 @@ - + @@ -6270,7 +6291,7 @@ - + @@ -6306,7 +6327,7 @@ - + @@ -6323,8 +6344,8 @@ - - + + @@ -6443,6 +6464,9 @@ + + + @@ -6543,7 +6567,7 @@ - + @@ -6551,6 +6575,9 @@ + + + @@ -6632,6 +6659,9 @@ + + + @@ -6924,7 +6954,7 @@ - + @@ -7062,7 +7092,7 @@ - + @@ -7403,6 +7433,9 @@ + + + @@ -8442,7 +8475,7 @@ - + @@ -8451,7 +8484,7 @@ - + @@ -8460,7 +8493,7 @@ - + @@ -8532,7 +8565,7 @@ - + diff --git a/src/server/cancellationToken/cancellationToken.ts b/src/server/cancellationToken/cancellationToken.ts index 6f2f4a8897c..b7243ccd871 100644 --- a/src/server/cancellationToken/cancellationToken.ts +++ b/src/server/cancellationToken/cancellationToken.ts @@ -46,7 +46,7 @@ function createCancellationToken(args: string[]): ServerCancellationToken { let perRequestPipeName: string; let currentRequestId: number; return { - isCancellationRequested: () => perRequestPipeName !== undefined && pipeExists(perRequestPipeName), + isCancellationRequested: () => perRequestPipeName !== undefined && pipeExists(perRequestPipeName), setRequest(requestId: number) { currentRequestId = requestId; perRequestPipeName = namePrefix + requestId; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 6afeae16ef5..9843b50dbb3 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -547,9 +547,11 @@ namespace ts.server { } switch (response.kind) { case ActionSet: + project.resolutionCache.clear(); this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings); break; case ActionInvalidate: + project.resolutionCache.clear(); this.typingsCache.deleteTypingsForProject(response.projectName); break; } @@ -1774,9 +1776,10 @@ namespace ts.server { const path = normalizedPathToPath(fileName, currentDirectory, this.toCanonicalFileName); let info = this.getScriptInfoForPath(path); if (!info) { - Debug.assert(isRootedDiskPath(fileName) || openedByClient, "Script info with relative file name can only be open script info"); - Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names"); const isDynamic = isDynamicFileName(fileName); + Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "Script info with non-dynamic relative file name can only be open script info"); + Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names"); + Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "Dynamic files must always have current directory context since containing external project name will always match the script info name."); // If the file is not opened by client and the file doesnot exist on the disk, return if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) { return; diff --git a/src/server/project.ts b/src/server/project.ts index e44cf6390a1..6f6b32ae558 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -121,6 +121,7 @@ namespace ts.server { private program: Program; private externalFiles: SortedReadonlyArray; private missingFilesMap: Map; + private plugins: PluginModule[] = []; private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap(); private lastCachedUnresolvedImportsList: SortedReadonlyArray; @@ -516,7 +517,18 @@ namespace ts.server { } getExternalFiles(): SortedReadonlyArray { - return emptyArray as SortedReadonlyArray; + return toSortedArray(flatMap(this.plugins, plugin => { + if (typeof plugin.getExternalFiles !== "function") return; + try { + return plugin.getExternalFiles(this); + } + catch (e) { + this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`); + if (e.stack) { + this.projectService.logger.info(e.stack); + } + } + })); } getSourceFile(path: Path) { @@ -1024,6 +1036,84 @@ namespace ts.server { orderedRemoveItem(this.rootFiles, info); this.rootFilesMap.delete(info.path); } + + protected enableGlobalPlugins() { + const host = this.projectService.host; + const options = this.getCompilationSettings(); + + if (!host.require) { + this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded"); + return; + } + + // Search our peer node_modules, then any globally-specified probe paths + // ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/ + const searchPaths = [combinePaths(this.projectService.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations]; + + if (this.projectService.globalPlugins) { + // Enable global plugins with synthetic configuration entries + for (const globalPluginName of this.projectService.globalPlugins) { + // Skip empty names from odd commandline parses + if (!globalPluginName) continue; + + // Skip already-locally-loaded plugins + if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue; + + // Provide global: true so plugins can detect why they can't find their config + this.projectService.logger.info(`Loading global plugin ${globalPluginName}`); + this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths); + } + } + } + + protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) { + this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`); + + const log = (message: string) => { + this.projectService.logger.info(message); + }; + + for (const searchPath of searchPaths) { + const resolvedModule = Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log); + if (resolvedModule) { + this.enableProxy(resolvedModule, pluginConfigEntry); + return; + } + } + this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`); + } + + private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) { + try { + if (typeof pluginModuleFactory !== "function") { + this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did expose a proper factory function`); + return; + } + + const info: PluginCreateInfo = { + config: configEntry, + project: this, + languageService: this.languageService, + languageServiceHost: this, + serverHost: this.projectService.host + }; + + const pluginModule = pluginModuleFactory({ typescript: ts }); + const newLS = pluginModule.create(info); + for (const k of Object.keys(this.languageService)) { + if (!(k in newLS)) { + this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`); + (newLS as any)[k] = (this.languageService as any)[k]; + } + } + this.projectService.logger.info(`Plugin validation succeded`); + this.languageService = newLS; + this.plugins.push(pluginModule); + } + catch (e) { + this.projectService.logger.info(`Plugin activation failed: ${e}`); + } + } } /** @@ -1087,6 +1177,7 @@ namespace ts.server { projectService.host, currentDirectory); this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath); + this.enableGlobalPlugins(); } addRoot(info: ScriptInfo) { @@ -1148,8 +1239,6 @@ namespace ts.server { /*@internal*/ configFileSpecs: ConfigFileSpecs; - private plugins: PluginModule[] = []; - /** Ref count to the project when opened from external project */ private externalProjectRefCount = 0; @@ -1231,69 +1320,7 @@ namespace ts.server { } } - if (this.projectService.globalPlugins) { - // Enable global plugins with synthetic configuration entries - for (const globalPluginName of this.projectService.globalPlugins) { - // Skip empty names from odd commandline parses - if (!globalPluginName) continue; - - // Skip already-locally-loaded plugins - if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue; - - // Provide global: true so plugins can detect why they can't find their config - this.projectService.logger.info(`Loading global plugin ${globalPluginName}`); - this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths); - } - } - } - - private enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) { - this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`); - - const log = (message: string) => { - this.projectService.logger.info(message); - }; - - for (const searchPath of searchPaths) { - const resolvedModule = Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log); - if (resolvedModule) { - this.enableProxy(resolvedModule, pluginConfigEntry); - return; - } - } - this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`); - } - - private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) { - try { - if (typeof pluginModuleFactory !== "function") { - this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did expose a proper factory function`); - return; - } - - const info: PluginCreateInfo = { - config: configEntry, - project: this, - languageService: this.languageService, - languageServiceHost: this, - serverHost: this.projectService.host - }; - - const pluginModule = pluginModuleFactory({ typescript: ts }); - const newLS = pluginModule.create(info); - for (const k of Object.keys(this.languageService)) { - if (!(k in newLS)) { - this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`); - (newLS as any)[k] = (this.languageService as any)[k]; - } - } - this.projectService.logger.info(`Plugin validation succeded`); - this.languageService = newLS; - this.plugins.push(pluginModule); - } - catch (e) { - this.projectService.logger.info(`Plugin activation failed: ${e}`); - } + this.enableGlobalPlugins(); } /** @@ -1322,21 +1349,6 @@ namespace ts.server { return this.typeAcquisition; } - getExternalFiles(): SortedReadonlyArray { - return toSortedArray(flatMap(this.plugins, plugin => { - if (typeof plugin.getExternalFiles !== "function") return; - try { - return plugin.getExternalFiles(this); - } - catch (e) { - this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`); - if (e.stack) { - this.projectService.logger.info(e.stack); - } - } - })); - } - /*@internal*/ watchWildcards(wildcardDirectories: Map) { updateWatchingWildcardDirectories( diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 4bd7a0a0967..d406aa9c65b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -566,7 +566,7 @@ namespace ts.server.protocol { // TODO: GH#20538 /* @internal */ - export interface GetCombinedCodeFixResponse extends Response { + export interface GetCombinedCodeFixResponse extends Response { body: CombinedCodeActions; } diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index fe71040b4f6..9650130634e 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -257,7 +257,7 @@ namespace ts.server { export class ScriptVersionCache { private changes: TextChange[] = []; private readonly versions: LineIndexSnapshot[] = new Array(ScriptVersionCache.maxVersions); - private minVersion = 0; // no versions earlier than min version will maintain change history + private minVersion = 0; // no versions earlier than min version will maintain change history private currentVersion = 0; diff --git a/src/server/server.ts b/src/server/server.ts index 86349fa2be5..8e53c4d5109 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -979,6 +979,10 @@ namespace ts.server { allowLocalPluginLoads }; + logger.info(`Starting TS Server`); + logger.info(`Version: ${versionMajorMinor}`); + logger.info(`Arguments: ${process.argv.join(" ")}`); + const ioSession = new IOSession(options); process.on("uncaughtException", err => { ioSession.logError(err, "unknown"); diff --git a/src/server/session.ts b/src/server/session.ts index 17c5777132f..9dcf58734a7 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1230,7 +1230,7 @@ namespace ts.server { return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source); }); return simplifiedResult - ? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(action, scriptInfo)) })) + ? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(project, action)) })) : result; } @@ -1560,7 +1560,7 @@ namespace ts.server { return undefined; } if (simplifiedResult) { - return codeActions.map(codeAction => this.mapCodeAction(codeAction, scriptInfo)); + return codeActions.map(codeAction => this.mapCodeAction(project, codeAction)); } else { return codeActions; @@ -1613,8 +1613,8 @@ namespace ts.server { return { startPosition, endPosition }; } - private mapCodeAction({ description, changes: unmappedChanges, commands }: CodeAction, scriptInfo: ScriptInfo): protocol.CodeAction { - const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, scriptInfo)); + private mapCodeAction(project: Project, { description, changes: unmappedChanges, commands }: CodeAction): protocol.CodeAction { + const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, project.getScriptInfoForNormalizedPath(toNormalizedPath(change.fileName)))); return { description, changes, commands }; } diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 57dbc5711e7..26df417f879 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -262,8 +262,8 @@ namespace ts.BreakpointResolver { } // Set breakpoint on identifier element of destructuring pattern - // a or ...c or d: x from - // [a, b, ...c] or { a, b } or { d: x } from destructuring pattern + // `a` or `...c` or `d: x` from + // `[a, b, ...c]` or `{ a, b }` or `{ d: x }` from destructuring pattern if ((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.SpreadElement || node.kind === SyntaxKind.PropertyAssignment || @@ -427,8 +427,9 @@ namespace ts.BreakpointResolver { } else { const functionDeclaration = parameter.parent; - const indexOfParameter = indexOf(functionDeclaration.parameters, parameter); - if (indexOfParameter) { + const indexOfParameter = functionDeclaration.parameters.indexOf(parameter); + Debug.assert(indexOfParameter !== -1); + if (indexOfParameter !== 0) { // Not a first parameter, go to previous parameter return spanInParameterDeclaration(functionDeclaration.parameters[indexOfParameter - 1]); } diff --git a/src/services/classifier.ts b/src/services/classifier.ts index 32b3dac2cca..3e4c6058a36 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -837,7 +837,7 @@ namespace ts { return ClassificationType.keyword; } - // Special case < and > If they appear in a generic context they are punctuation, + // Special case `<` and `>`: If they appear in a generic context they are punctuation, // not operators. if (tokenKind === SyntaxKind.LessThanToken || tokenKind === SyntaxKind.GreaterThanToken) { // If the node owning the token has a type argument list or type parameter list, then diff --git a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts index 3d99b571248..de4498035be 100644 --- a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts +++ b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts @@ -21,7 +21,7 @@ namespace ts.codefix { }); function getQualifiedName(sourceFile: SourceFile, pos: number): QualifiedName & { left: Identifier } | undefined { - const qualifiedName = findAncestor(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false), isQualifiedName)!; + const qualifiedName = findAncestor(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ true), isQualifiedName)!; Debug.assert(!!qualifiedName, "Expected position to be owned by a qualified name."); return isIdentifier(qualifiedName.left) ? qualifiedName as QualifiedName & { left: Identifier } : undefined; } diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index e20bdf0fb5b..0fc6a430e19 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -21,8 +21,8 @@ namespace ts.codefix { getAllCodeActions: context => { const seenNames = createMap(); return codeFixAll(context, errorCodes, (changes, diag) => { - const { newLineCharacter, program } = context; - const info = getInfo(diag.file!, diag.start!, context.program.getTypeChecker()); + const { program } = context; + const info = getInfo(diag.file!, diag.start!, program.getTypeChecker()); if (!info) return; const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; if (!addToSeen(seenNames, token.text)) { @@ -31,15 +31,15 @@ namespace ts.codefix { // Always prefer to add a method declaration if possible. if (call) { - addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, newLineCharacter, makeStatic, inJs); + addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs); } else { if (inJs) { - addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic, newLineCharacter); + addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic); } else { const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token); - addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic, newLineCharacter); + addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic); } } }); @@ -96,20 +96,20 @@ namespace ts.codefix { } function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined { - const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic, context.newLineCharacter)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]); return { description, changes, fixId }; } - function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean, newLineCharacter: string): void { + function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): void { if (makeStatic) { if (classDeclaration.kind === SyntaxKind.ClassExpression) { return; } const className = classDeclaration.name.getText(); const staticInitialization = initializePropertyToUndefined(createIdentifier(className), tokenName); - changeTracker.insertNodeAfter(classDeclarationSourceFile, classDeclaration, staticInitialization, { prefix: newLineCharacter, suffix: newLineCharacter }); + changeTracker.insertNodeAfter(classDeclarationSourceFile, classDeclaration, staticInitialization); } else { const classConstructor = getFirstConstructorWithBody(classDeclaration); @@ -117,7 +117,7 @@ namespace ts.codefix { return; } const propertyInitialization = initializePropertyToUndefined(createThis(), tokenName); - changeTracker.insertNodeBefore(classDeclarationSourceFile, classConstructor.body.getLastToken(), propertyInitialization, { suffix: newLineCharacter }); + changeTracker.insertNodeAtConstructorEnd(classDeclarationSourceFile, classConstructor, propertyInitialization); } } @@ -142,13 +142,13 @@ namespace ts.codefix { return typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); } - function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { + function createAddPropertyDeclarationAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]); - const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic, context.newLineCharacter)); + const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); return { description, changes, fixId }; } - function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode, makeStatic: boolean, newLineCharacter: string): void { + function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode, makeStatic: boolean): void { const property = createProperty( /*decorators*/ undefined, /*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, @@ -156,10 +156,10 @@ namespace ts.codefix { /*questionToken*/ undefined, typeNode, /*initializer*/ undefined); - changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, property, newLineCharacter); + changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, property); } - function createAddIndexSignatureAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction { + function createAddIndexSignatureAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction { // Index signatures cannot have the static modifier. const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword); const indexingParameter = createParameter( @@ -176,19 +176,19 @@ namespace ts.codefix { [indexingParameter], typeNode); - const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature, context.newLineCharacter)); + const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); // No fixId here because code-fix-all currently only works on adding individual named properties. return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined }; } - function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { + function getActionForMethodDeclaration(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]); - const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, context.newLineCharacter, makeStatic, inJs)); + const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); return { description, changes, fixId }; } - function addMethodDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, newLineCharacter: string, makeStatic: boolean, inJs: boolean) { + function addMethodDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean) { const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic); - changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration, newLineCharacter); + changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration); } } diff --git a/src/services/codefixes/fixCannotFindModule.ts b/src/services/codefixes/fixCannotFindModule.ts index 2a3736531b2..a28a46caf1b 100644 --- a/src/services/codefixes/fixCannotFindModule.ts +++ b/src/services/codefixes/fixCannotFindModule.ts @@ -4,9 +4,10 @@ namespace ts.codefix { const errorCodes = [Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type.code]; registerCodeFix({ errorCodes, - getCodeActions: context => [ - { fixId, ...tryGetCodeActionForInstallPackageTypes(context.host, context.sourceFile.fileName, getModuleName(context.sourceFile, context.span.start)) } - ], + getCodeActions: context => { + const codeAction = tryGetCodeActionForInstallPackageTypes(context.host, context.sourceFile.fileName, getModuleName(context.sourceFile, context.span.start)); + return codeAction && [{ fixId, ...codeAction }]; + }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (_, diag, commands) => { const pkg = getTypesPackageNameToInstall(context.host, getModuleName(diag.file, diag.start)); diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 754735a9d63..da3685339ab 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -10,12 +10,12 @@ namespace ts.codefix { getCodeActions(context) { const { program, sourceFile, span } = context; const changes = textChanges.ChangeTracker.with(context, t => - addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), context.newLineCharacter, t)); + addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t)); return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { - addMissingMembers(getClass(diag.file!, diag.start!), context.sourceFile, context.program.getTypeChecker(), context.newLineCharacter, changes); + addMissingMembers(getClass(diag.file!, diag.start!), context.sourceFile, context.program.getTypeChecker(), changes); }), }); @@ -28,7 +28,7 @@ namespace ts.codefix { return classDeclaration as ClassLikeDeclaration; } - function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, newLineCharacter: string, changeTracker: textChanges.ChangeTracker): void { + function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker): void { const extendsNode = getClassExtendsHeritageClauseElement(classDeclaration); const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); @@ -36,7 +36,7 @@ namespace ts.codefix { // so duplicates cannot occur. const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember); - createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member, newLineCharacter)); + createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); } function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean { diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 7076f7da344..0236b0c79b7 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -1,15 +1,16 @@ /* @internal */ namespace ts.codefix { - const errorCodes = [Diagnostics.Class_0_incorrectly_implements_interface_1.code]; + const errorCodes = [Diagnostics.Class_0_incorrectly_implements_interface_1.code, + Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass.code]; const fixId = "fixClassIncorrectlyImplementsInterface"; // TODO: share a group with fixClassDoesntImplementInheritedAbstractMember? registerCodeFix({ errorCodes, getCodeActions(context) { - const { newLineCharacter, program, sourceFile, span } = context; + const { program, sourceFile, span } = context; const classDeclaration = getClass(sourceFile, span.start); const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, newLineCharacter, t)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); return { description, changes, fixId }; @@ -22,7 +23,7 @@ namespace ts.codefix { const classDeclaration = getClass(diag.file!, diag.start!); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)) { - addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, context.newLineCharacter, changes); + addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, changes); } } }); @@ -40,7 +41,6 @@ namespace ts.codefix { implementedTypeNode: ExpressionWithTypeArguments, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, - newLineCharacter: string, changeTracker: textChanges.ChangeTracker ): void { // Note that this is ultimately derived from a map indexed by symbol names, @@ -58,12 +58,12 @@ namespace ts.codefix { createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member, newLineCharacter)); + createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void { const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); if (indexInfoOfKind) { - changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration), newLineCharacter); + changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration)); } } } diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 9b67c3469cf..1595bbf3c13 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -5,30 +5,30 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions(context) { - const { sourceFile } = context; - const nodes = getNodes(sourceFile, context.span.start); + const { sourceFile, span } = context; + const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; const { constructor, superCall } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall, context.newLineCharacter)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); return [{ description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), changes, fixId }]; }, fixIds: [fixId], getAllCodeActions(context) { - const { newLineCharacter, sourceFile } = context; + const { sourceFile } = context; const seenClasses = createMap(); // Ensure we only do this once per class. return codeFixAll(context, errorCodes, (changes, diag) => { const nodes = getNodes(diag.file!, diag.start!); if (!nodes) return; const { constructor, superCall } = nodes; if (addToSeen(seenClasses, getNodeId(constructor.parent))) { - doChange(changes, sourceFile, constructor, superCall, newLineCharacter); + doChange(changes, sourceFile, constructor, superCall); } }); }, }); - function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement, newLineCharacter: string): void { - changes.insertNodeAtConstructorStart(sourceFile, constructor, superCall, newLineCharacter); + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement): void { + changes.insertNodeAtConstructorStart(sourceFile, constructor, superCall); changes.deleteNode(sourceFile, superCall); } diff --git a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts index 4c5b24fb0ec..8fe2e8458a8 100644 --- a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts +++ b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts @@ -5,14 +5,14 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions(context) { - const { sourceFile } = context; - const ctr = getNode(sourceFile, context.span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr, context.newLineCharacter)); + const { sourceFile, span } = context; + const ctr = getNode(sourceFile, span.start); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), changes, fixId }]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => - doChange(changes, context.sourceFile, getNode(diag.file, diag.start!), context.newLineCharacter)), + doChange(changes, context.sourceFile, getNode(diag.file, diag.start!))), }); function getNode(sourceFile: SourceFile, pos: number): ConstructorDeclaration { @@ -21,8 +21,8 @@ namespace ts.codefix { return token.parent as ConstructorDeclaration; } - function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ctr: ConstructorDeclaration, newLineCharacter: string) { + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ctr: ConstructorDeclaration) { const superCall = createStatement(createCall(createSuper(), /*typeArguments*/ undefined, /*argumentsArray*/ emptyArray)); - changes.insertNodeAtConstructorStart(sourceFile, ctr, superCall, newLineCharacter); + changes.insertNodeAtConstructorStart(sourceFile, ctr, superCall); } } diff --git a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts index 446b0bddfab..d98ca556f47 100644 --- a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts +++ b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts @@ -27,11 +27,26 @@ namespace ts.codefix { } function doChanges(changes: textChanges.ChangeTracker, sourceFile: SourceFile, extendsToken: Node, heritageClauses: ReadonlyArray): void { - changes.replaceNode(sourceFile, extendsToken, createToken(SyntaxKind.ImplementsKeyword)); - // We replace existing keywords with commas. - for (let i = 1; i < heritageClauses.length; i++) { - const keywordToken = heritageClauses[i].getFirstToken()!; - changes.replaceNode(sourceFile, keywordToken, createToken(SyntaxKind.CommaToken)); + changes.replaceRange(sourceFile, { pos: extendsToken.getStart(), end: extendsToken.end }, createToken(SyntaxKind.ImplementsKeyword)); + + // If there is already an implements clause, replace the implements keyword with a comma. + if (heritageClauses.length === 2 && + heritageClauses[0].token === SyntaxKind.ExtendsKeyword && + heritageClauses[1].token === SyntaxKind.ImplementsKeyword) { + + const implementsToken = heritageClauses[1].getFirstToken()!; + const implementsFullStart = implementsToken.getFullStart(); + changes.replaceRange(sourceFile, { pos: implementsFullStart, end: implementsFullStart }, createToken(SyntaxKind.CommaToken)); + + // Rough heuristic: delete trailing whitespace after keyword so that it's not excessive. + // (Trailing because leading might be indentation, which is more sensitive.) + const text = sourceFile.text; + let end = implementsToken.end; + while (end < text.length && ts.isWhiteSpaceSingleLine(text.charCodeAt(end))) { + end++; + } + + changes.deleteRange(sourceFile, { pos: implementsToken.getStart(), end }); } } } diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 64cdc3181fb..729f14e9ef9 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -9,7 +9,7 @@ namespace ts.codefix { errorCodes, getCodeActions(context) { const { sourceFile } = context; - const info = getInfo(sourceFile, context.span.start, context.program.getTypeChecker()); + const info = getInfo(sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; const { node, suggestion } = info; const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion)); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index bd2710244e6..47e23e717d9 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -121,7 +121,7 @@ namespace ts.codefix { createStubbedMethodBody()); } - function createDummyParameters(argCount: number, names: string[] | undefined, minArgumentCount: number | undefined, inJs: boolean): ParameterDeclaration[] { + function createDummyParameters(argCount: number, names: string[] | undefined, minArgumentCount: number | undefined, inJs: boolean): ParameterDeclaration[] { const parameters: ParameterDeclaration[] = []; for (let i = 0; i < argCount; i++) { const newParameter = createParameter( diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 05a92edf12a..a69152ad2b5 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -30,7 +30,7 @@ namespace ts.codefix { } interface SymbolAndTokenContext extends SymbolContext { - symbolToken: Node | undefined; + symbolToken: Identifier | undefined; } interface ImportCodeFixContext extends SymbolAndTokenContext { @@ -169,7 +169,8 @@ namespace ts.codefix { const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false; const { program } = context; const checker = program.getTypeChecker(); - const symbolToken = getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false); + // This will always be an Identifier, since the diagnostics we fix only fail on identifiers. + const symbolToken = cast(getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false), isIdentifier); return { host: context.host, newLineCharacter: context.newLineCharacter, @@ -213,14 +214,17 @@ namespace ts.codefix { for (const declaration of declarations) { const namespace = getNamespaceImportName(declaration); if (namespace) { - actions.push(getCodeActionForUseExistingNamespaceImport(namespace.text, context, context.symbolToken)); + const moduleSymbol = context.checker.getAliasedSymbol(context.checker.getSymbolAtLocation(namespace)); + if (moduleSymbol && moduleSymbol.exports.has(escapeLeadingUnderscores(context.symbolName))) { + actions.push(getCodeActionForUseExistingNamespaceImport(namespace.text, context, context.symbolToken)); + } } } } return [...actions, ...getCodeActionsForAddImport(moduleSymbols, context, declarations)]; } - function getNamespaceImportName(declaration: AnyImportSyntax): Identifier { + function getNamespaceImportName(declaration: AnyImportSyntax): Identifier | undefined { if (declaration.kind === SyntaxKind.ImportDeclaration) { const namedBindings = declaration.importClause && isImportClause(declaration.importClause) && declaration.importClause.namedBindings; return namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport ? namedBindings.name : undefined; @@ -257,7 +261,7 @@ namespace ts.codefix { } function getCodeActionForNewImport(context: SymbolContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { - const { kind, sourceFile, newLineCharacter, symbolName } = context; + const { kind, sourceFile, symbolName } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier); @@ -276,10 +280,10 @@ namespace ts.codefix { const changes = ChangeTracker.with(context, changeTracker => { if (lastImportDeclaration) { - changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: newLineCharacter }); + changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl); } else { - changeTracker.insertNodeAt(sourceFile, getSourceFileImportLocation(sourceFile), importDecl, { suffix: `${newLineCharacter}${newLineCharacter}` }); + changeTracker.insertNodeAtTopOfFile(sourceFile, importDecl, /*blankLineBetween*/ true); } }); @@ -302,6 +306,10 @@ namespace ts.codefix { return literal; } + function usesJsExtensionOnImports(sourceFile: SourceFile): boolean { + return firstDefined(sourceFile.imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false; + } + function createImportClauseOfKind(kind: ImportKind.Default | ImportKind.Named | ImportKind.Namespace, symbolName: string) { const id = createIdentifier(symbolName); switch (kind) { @@ -325,18 +333,19 @@ namespace ts.codefix { host: LanguageServiceHost, ): string[] { const { baseUrl, paths, rootDirs } = options; + const addJsExtension = usesJsExtensionOnImports(sourceFile); const choicesForEachExportingModule = flatMap(moduleSymbols, moduleSymbol => getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()).map(moduleFileName => { const sourceDirectory = getDirectoryPath(sourceFile.fileName); const global = tryGetModuleNameFromAmbientModule(moduleSymbol) - || tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName) + || tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName, addJsExtension) || tryGetModuleNameAsNodeModule(options, moduleFileName, host, getCanonicalFileName, sourceDirectory) || rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName); if (global) { return [global]; } - const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options); + const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options, addJsExtension); if (!baseUrl) { return [relativePath]; } @@ -346,7 +355,7 @@ namespace ts.codefix { return [relativePath]; } - const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options); + const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options, addJsExtension); if (paths) { const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths); if (fromPaths) { @@ -455,12 +464,13 @@ namespace ts.codefix { host: GetEffectiveTypeRootsHost, getCanonicalFileName: (file: string) => string, moduleFileName: string, + addJsExtension: boolean, ): string | undefined { const roots = getEffectiveTypeRoots(options, host); return roots && firstDefined(roots, unNormalizedTypeRoot => { const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName); if (startsWith(moduleFileName, typeRoot)) { - return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options); + return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options, addJsExtension); } }); } @@ -594,9 +604,13 @@ namespace ts.codefix { return firstDefined(rootDirs, rootDir => getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName)); } - function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions): string { + function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions, addJsExtension: boolean): string { const noExtension = removeFileExtension(fileName); - return getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs ? removeSuffix(noExtension, "/index") : noExtension; + return addJsExtension + ? noExtension + ".js" + : getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs + ? removeSuffix(noExtension, "/index") + : noExtension; } function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined { @@ -683,7 +697,7 @@ namespace ts.codefix { } } - function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext, symbolToken: Node): ImportCodeAction { + function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext, symbolToken: Identifier): ImportCodeAction { const { symbolName, sourceFile } = context; /** @@ -696,13 +710,10 @@ namespace ts.codefix { * namespace instead of altering the import declaration. For example, "foo" would * become "ns.foo" */ - return createCodeAction( - Diagnostics.Change_0_to_1, - [symbolName, `${namespacePrefix}.${symbolName}`], - ChangeTracker.with(context, tracker => - tracker.replaceNode(sourceFile, symbolToken, createPropertyAccess(createIdentifier(namespacePrefix), symbolName))), - "CodeChange", - /*moduleSpecifier*/ undefined); + // Prefix the node instead of it replacing it, because this may be used for import completions and we don't want the text changes to overlap with the identifier being completed. + const changes = ChangeTracker.with(context, tracker => + tracker.changeIdentifierToPropertyAccess(sourceFile, namespacePrefix, symbolToken)); + return createCodeAction(Diagnostics.Change_0_to_1, [symbolName, `${namespacePrefix}.${symbolName}`], changes, "CodeChange", /*moduleSpecifier*/ undefined); } function getImportCodeActions(context: CodeFixContext): ImportCodeAction[] { diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 9782619d41d..95d85bc5aa0 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -70,7 +70,6 @@ namespace ts.codefix { return undefined; } - const containingFunction = getContainingFunction(token); switch (errorCode) { // Variable and Property declarations case Diagnostics.Member_0_implicitly_has_an_1_type.code: @@ -81,6 +80,13 @@ namespace ts.codefix { const symbol = program.getTypeChecker().getSymbolAtLocation(token); return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(symbol.valueDeclaration, sourceFile, program, cancellationToken); } + } + + const containingFunction = getContainingFunction(token); + if (containingFunction === undefined) { + return undefined; + } + switch (errorCode) { // Parameter declarations case Diagnostics.Parameter_0_implicitly_has_an_1_type.code: @@ -148,6 +154,11 @@ namespace ts.codefix { containingFunction.parameters.map(p => isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, sourceFile, program, cancellationToken) : undefined); if (!types) return undefined; + // We didn't actually find a set of type inference positions matching each parameter position + if (containingFunction.parameters.length !== types.length) { + return undefined; + } + const textChanges = arrayFrom(mapDefinedIterator(zipToIterator(containingFunction.parameters, types), ([parameter, type]) => type && !parameter.type && !parameter.initializer ? makeChange(containingFunction, parameter.end, type, program) : undefined)); return textChanges.length ? { declaration: parameterDeclaration, textChanges } : undefined; @@ -170,7 +181,7 @@ namespace ts.codefix { } const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name, sourceFile, program, cancellationToken); - const closeParenToken = getFirstChildOfKind(getAccessorDeclaration, sourceFile, SyntaxKind.CloseParenToken); + const closeParenToken = findChildOfKind(getAccessorDeclaration, SyntaxKind.CloseParenToken, sourceFile); return makeFix(getAccessorDeclaration, closeParenToken.getEnd(), type, program); } @@ -191,8 +202,9 @@ namespace ts.codefix { sourceFile, token.getStart(sourceFile)); - Debug.assert(!!references, "Found no references!"); - Debug.assert(references.length === 1, "Found more references than expected"); + if (!references || references.length !== 1) { + return []; + } return references[0].references.map(r => getTokenAtPosition(program.getSourceFile(r.fileName), r.textSpan.start, /*includeJsDocComment*/ false)); } @@ -209,7 +221,7 @@ namespace ts.codefix { case SyntaxKind.MethodDeclaration: const isConstructor = containingFunction.kind === SyntaxKind.Constructor; const searchToken = isConstructor ? - >getFirstChildOfKind(containingFunction, sourceFile, SyntaxKind.ConstructorKeyword) : + findChildOfKind>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile) : containingFunction.name; if (searchToken) { return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, sourceFile, program, cancellationToken), containingFunction, program.getTypeChecker(), cancellationToken); @@ -281,6 +293,10 @@ namespace ts.codefix { } export function inferTypeForParametersFromReferences(references: Identifier[], declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | undefined { + if (references.length === 0) { + return undefined; + } + if (declaration.parameters) { const usageContext: UsageContext = {}; for (const reference of references) { @@ -305,7 +321,7 @@ namespace ts.codefix { } } if (types.length) { - const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true)); + const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype)); paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type; } } @@ -542,12 +558,12 @@ namespace ts.codefix { return checker.getStringType(); } else if (usageContext.candidateTypes) { - return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), /*subtypeReduction*/ true)); + return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), UnionReduction.Subtype)); } else if (usageContext.properties && hasCallContext(usageContext.properties.get("then" as __String))) { const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then" as __String).callContexts, /*isRestParameter*/ false, checker); const types = paramType.getCallSignatures().map(c => c.getReturnType()); - return checker.createPromiseType(types.length ? checker.getUnionType(types, /*subtypeReduction*/ true) : checker.getAnyType()); + return checker.createPromiseType(types.length ? checker.getUnionType(types, UnionReduction.Subtype) : checker.getAnyType()); } else if (usageContext.properties && hasCallContext(usageContext.properties.get("push" as __String))) { return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push" as __String).callContexts, /*isRestParameter*/ false, checker)); @@ -610,7 +626,7 @@ namespace ts.codefix { } if (types.length) { - const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true)); + const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype)); return isRestParameter ? checker.createArrayType(type) : type; } return undefined; diff --git a/src/services/completions.ts b/src/services/completions.ts index d893f4852e4..18f09efcfe6 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -57,7 +57,8 @@ namespace ts.Completions { // In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag, // instead of simply giving unknown value, the completion will return the tag-name of an associated opening-element. // For example: - // var x =
completion list at "1" will contain "div" with type any + // var x =
+ // The completion list at "1" will contain "div" with type any const tagName = (location.parent.parent).openingElement.tagName; return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, entries: [{ @@ -76,7 +77,7 @@ namespace ts.Completions { // If the current position is a jsDoc tag, only tags should be provided for completion ? JsDoc.getJSDocTagCompletions() : JsDoc.getJSDocParameterNameCompletions(request.tag); - return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; + return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; } const entries: CompletionEntry[] = []; @@ -203,26 +204,24 @@ namespace ts.Completions { // Based on the order we add things we will always see locals first, then globals, then module exports. // So adding a completion for a local will prevent us from adding completions for external module exports sharing the same name. const uniques = createMap(); - if (symbols) { - for (const symbol of symbols) { - const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined; - const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target, allowStringLiteral, origin, recommendedCompletion); - if (!entry) { - continue; - } - - const { name } = entry; - if (uniques.has(name)) { - continue; - } - - // Latter case tests whether this is a global variable. - if (!origin && !(symbol.parent === undefined && !some(symbol.declarations, d => d.getSourceFile() === location.getSourceFile()))) { - uniques.set(name, true); - } - - entries.push(entry); + for (const symbol of symbols) { + const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined; + const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target, allowStringLiteral, origin, recommendedCompletion); + if (!entry) { + continue; } + + const { name } = entry; + if (uniques.has(name)) { + continue; + } + + // Latter case tests whether this is a global variable. + if (!origin && !(symbol.parent === undefined && !some(symbol.declarations, d => d.getSourceFile() === location.getSourceFile()))) { + uniques.set(name, true); + } + + entries.push(entry); } log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (timestamp() - start)); @@ -238,7 +237,7 @@ namespace ts.Completions { function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost, log: Log): CompletionInfo | undefined { const node = findPrecedingToken(position, sourceFile); - if (!node || node.kind !== SyntaxKind.StringLiteral) { + if (!node || !isStringLiteral(node) && !isNoSubstitutionTemplateLiteral(node)) { return undefined; } @@ -266,7 +265,8 @@ namespace ts.Completions { // } // let a: A; // a['/*completion position*/'] - return getStringLiteralCompletionEntriesFromElementAccess(node.parent, typeChecker, compilerOptions.target, log); + const type = typeChecker.getTypeAtLocation(node.parent.expression); + return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(node, type, typeChecker, compilerOptions.target, log); } else if (node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration || isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false) || isImportCall(node.parent) @@ -277,20 +277,18 @@ namespace ts.Completions { // import x = require("/*completion position*/"); // var y = require("/*completion position*/"); // export * from "/*completion position*/"; - const entries = PathCompletions.getStringLiteralCompletionsFromModuleNames(node, compilerOptions, host, typeChecker); + const entries = PathCompletions.getStringLiteralCompletionsFromModuleNames(node, compilerOptions, host, typeChecker); return pathCompletionsInfo(entries); } - else if (isEqualityExpression(node.parent)) { - // Get completions from the type of the other operand - // i.e. switch (a) { - // case '/*completion position*/' + else if (isIndexedAccessTypeNode(node.parent.parent)) { + // Get all apparent property names + // i.e. interface Foo { + // foo: string; + // bar: string; // } - return getStringLiteralCompletionEntriesFromType(typeChecker.getTypeAtLocation(node.parent.left === node ? node.parent.right : node.parent.left), typeChecker); - } - else if (isCaseOrDefaultClause(node.parent)) { - // Get completions from the type of the switch expression - // i.e. x === '/*completion position' - return getStringLiteralCompletionEntriesFromType(typeChecker.getTypeAtLocation((node.parent.parent.parent).expression), typeChecker); + // let x: Foo["/*completion position*/"] + const type = typeChecker.getTypeFromTypeNode(node.parent.parent.objectType); + return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(node, type, typeChecker, compilerOptions.target, log); } else { const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile); @@ -303,7 +301,7 @@ namespace ts.Completions { // Get completion for string literal from string literal type // i.e. var x: "hi" | "hello" = "/*completion position*/" - return getStringLiteralCompletionEntriesFromType(typeChecker.getContextualType(node), typeChecker); + return getStringLiteralCompletionEntriesFromType(getContextualTypeFromParent(node, typeChecker), typeChecker); } } @@ -348,11 +346,10 @@ namespace ts.Completions { return undefined; } - function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined { - const type = typeChecker.getTypeAtLocation(node.expression); + function getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(stringLiteralNode: StringLiteral | NoSubstitutionTemplateLiteral, type: Type, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined { const entries: CompletionEntry[] = []; if (type) { - getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/ false, typeChecker, target, log, /*allowStringLiteral*/ true); + getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, stringLiteralNode, /*performCharacterChecks*/ false, typeChecker, target, log, /*allowStringLiteral*/ true); if (entries.length) { return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } @@ -431,13 +428,13 @@ namespace ts.Completions { position: number, { name, source }: CompletionEntryIdentifier, allSourceFiles: ReadonlyArray, - ): { type: "symbol", symbol: Symbol, location: Node, symbolToOriginInfoMap: SymbolOriginInfoMap } | { type: "request", request: Request } | { type: "none" } { + ): { type: "symbol", symbol: Symbol, location: Node, symbolToOriginInfoMap: SymbolOriginInfoMap, previousToken: Node } | { type: "request", request: Request } | { type: "none" } { const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeExternalModuleExports: true }, compilerOptions.target); if (!completionData) { return { type: "none" }; } - const { symbols, location, allowStringLiteral, symbolToOriginInfoMap, request } = completionData; + const { symbols, location, allowStringLiteral, symbolToOriginInfoMap, request, previousToken } = completionData; if (request) { return { type: "request", request }; } @@ -451,12 +448,14 @@ namespace ts.Completions { return getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral, origin) === name && getSourceFromOrigin(origin) === source; }); - return symbol ? { type: "symbol", symbol, location, symbolToOriginInfoMap } : { type: "none" }; + return symbol ? { type: "symbol", symbol, location, symbolToOriginInfoMap, previousToken } : { type: "none" }; } function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string { return origin && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default - ? codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) + // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase. + ? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined) + || codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) : symbol.name; } @@ -496,8 +495,8 @@ namespace ts.Completions { } } case "symbol": { - const { symbol, location, symbolToOriginInfoMap } = symbolCompletion; - const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, formatContext, getCanonicalFileName, allSourceFiles); + const { symbol, location, symbolToOriginInfoMap, previousToken } = symbolCompletion; + const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles); const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol); const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All); return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions, source: sourceDisplay }; @@ -529,6 +528,7 @@ namespace ts.Completions { host: LanguageServiceHost, compilerOptions: CompilerOptions, sourceFile: SourceFile, + previousToken: Node, formatContext: formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, allSourceFiles: ReadonlyArray, @@ -554,9 +554,9 @@ namespace ts.Completions { formatContext, symbolName: getSymbolName(symbol, symbolOriginInfo, compilerOptions.target), getCanonicalFileName, - symbolToken: undefined, + symbolToken: tryCast(previousToken, isIdentifier), kind: isDefaultExport ? codefix.ImportKind.Default : codefix.ImportKind.Named, - }); + }).slice(0, 1); // Only take the first code action return { sourceDisplay, codeActions }; } @@ -597,11 +597,12 @@ namespace ts.Completions { keywordFilters: KeywordCompletionFilters; symbolToOriginInfoMap: SymbolOriginInfoMap; recommendedCompletion: Symbol | undefined; + previousToken: Node; } type Request = { kind: "JsDocTagName" } | { kind: "JsDocTag" } | { kind: "JsDocParameterName", tag: JSDocParameterTag }; - function getRecommendedCompletion(currentToken: Node, checker: TypeChecker/*, symbolToOriginInfoMap: SymbolOriginInfoMap*/): Symbol | undefined { - const ty = checker.getContextualType(currentToken as Expression); + function getRecommendedCompletion(currentToken: Node, checker: TypeChecker): Symbol | undefined { + const ty = getContextualType(currentToken, checker); const symbol = ty && ty.symbol; // Don't include make a recommended completion for an abstract class return symbol && (symbol.flags & SymbolFlags.Enum || symbol.flags & SymbolFlags.Class && !isAbstractConstructorSymbol(symbol)) @@ -609,6 +610,48 @@ namespace ts.Completions { : undefined; } + function getContextualType(currentToken: Node, checker: ts.TypeChecker): Type | undefined { + const { parent } = currentToken; + switch (currentToken.kind) { + case ts.SyntaxKind.Identifier: + return getContextualTypeFromParent(currentToken as ts.Identifier, checker); + case ts.SyntaxKind.EqualsToken: + return ts.isVariableDeclaration(parent) ? checker.getContextualType(parent.initializer) : + ts.isBinaryExpression(parent) ? checker.getTypeAtLocation(parent.left) : undefined; + case ts.SyntaxKind.NewKeyword: + return checker.getContextualType(parent as ts.Expression); + case ts.SyntaxKind.CaseKeyword: + return getSwitchedType(cast(currentToken.parent, isCaseClause), checker); + default: + return isEqualityOperatorKind(currentToken.kind) && ts.isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind) + // completion at `x ===/**/` should be for the right side + ? checker.getTypeAtLocation(parent.left) + : checker.getContextualType(currentToken as ts.Expression); + } + } + + function getContextualTypeFromParent(node: ts.Expression, checker: ts.TypeChecker): Type | undefined { + const { parent } = node; + switch (parent.kind) { + case ts.SyntaxKind.NewExpression: + return checker.getContextualType(parent as ts.NewExpression); + case ts.SyntaxKind.BinaryExpression: { + const { left, operatorToken, right } = parent as ts.BinaryExpression; + return isEqualityOperatorKind(operatorToken.kind) + ? checker.getTypeAtLocation(node === right ? left : right) + : checker.getContextualType(node); + } + case ts.SyntaxKind.CaseClause: + return (parent as ts.CaseClause).expression === node ? getSwitchedType(parent as ts.CaseClause, checker) : undefined; + default: + return checker.getContextualType(node); + } + } + + function getSwitchedType(caseClause: ts.CaseClause, checker: ts.TypeChecker): ts.Type { + return checker.getTypeAtLocation(caseClause.parent.parent.expression); + } + function getFirstSymbolInChain(symbol: Symbol, enclosingDeclaration: Node, checker: TypeChecker): Symbol | undefined { const chain = checker.getAccessibleSymbolChain(symbol, enclosingDeclaration, /*meaning*/ SymbolFlags.All, /*useOnlyExternalAliasing*/ false); if (chain) return first(chain); @@ -709,6 +752,7 @@ namespace ts.Completions { keywordFilters: KeywordCompletionFilters.None, symbolToOriginInfoMap: undefined, recommendedCompletion: undefined, + previousToken: undefined, }; } @@ -848,8 +892,8 @@ namespace ts.Completions { log("getCompletionData: Semantic work: " + (timestamp() - semanticStart)); - const recommendedCompletion = getRecommendedCompletion(previousToken, typeChecker); - return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion }; + const recommendedCompletion = previousToken && getRecommendedCompletion(previousToken, typeChecker); + return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken }; type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; @@ -918,9 +962,8 @@ namespace ts.Completions { symbols.push(...getPropertiesForCompletion(type, typeChecker, /*isForAccess*/ true)); } else { - // Filter private properties for (const symbol of type.getApparentProperties()) { - if (typeChecker.isValidPropertyAccess((node.parent), symbol.name)) { + if (typeChecker.isValidPropertyAccessForCompletions((node.parent), type, symbol)) { symbols.push(symbol); } } @@ -1111,8 +1154,6 @@ namespace ts.Completions { codefix.forEachExternalModuleToImportFrom(typeChecker, sourceFile, allSourceFiles, moduleSymbol => { for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) { - let { name } = symbol; - // Don't add a completion for a re-export, only for the original. // If `symbol.parent !== moduleSymbol`, this comes from an `export * from "foo"` re-export. Those don't create new symbols. // If `some(...)`, this comes from an `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check). @@ -1120,19 +1161,13 @@ namespace ts.Completions { continue; } - const isDefaultExport = name === InternalSymbolName.Default; + const isDefaultExport = symbol.name === InternalSymbolName.Default; if (isDefaultExport) { - const localSymbol = getLocalSymbolForExportDefault(symbol); - if (localSymbol) { - symbol = localSymbol; - name = localSymbol.name; - } - else { - name = codefix.moduleSymbolToValidIdentifier(moduleSymbol, target); - } + symbol = getLocalSymbolForExportDefault(symbol) || symbol; } - if (stringContainsCharactersInOrder(name.toLowerCase(), tokenTextLowerCase)) { + const origin: SymbolOriginInfo = { moduleSymbol, isDefaultExport }; + if (stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) { symbols.push(symbol); symbolToOriginInfoMap[getSymbolId(symbol)] = { moduleSymbol, isDefaultExport }; } @@ -1325,7 +1360,7 @@ namespace ts.Completions { // through type declaration or inference. // Also proceed if rootDeclaration is a parameter and if its containing function expression/arrow function is contextually typed - // type of parameter will flow in from the contextual type of the function - let canGetType = rootDeclaration.initializer || rootDeclaration.type || rootDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement; + let canGetType = hasInitializer(rootDeclaration) || hasType(rootDeclaration) || rootDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement; if (!canGetType && rootDeclaration.kind === SyntaxKind.Parameter) { if (isExpression(rootDeclaration.parent)) { canGetType = !!typeChecker.getContextualType(rootDeclaration.parent); @@ -1559,7 +1594,7 @@ namespace ts.Completions { switch (contextToken.kind) { case SyntaxKind.OpenParenToken: case SyntaxKind.CommaToken: - return isConstructorDeclaration(contextToken.parent) && contextToken.parent; + return isConstructorDeclaration(contextToken.parent) && contextToken.parent; default: if (isConstructorParameterCompletion(contextToken)) { @@ -1761,7 +1796,10 @@ namespace ts.Completions { return true; } - return isDeclarationName(contextToken) && !isJsxAttribute(contextToken.parent); + return isDeclarationName(contextToken) + && !isJsxAttribute(contextToken.parent) + // Don't block completions if we're in `class C /**/`, because we're *past* the end of the identifier and might want to complete `extends`. + && !(isClassLike(contextToken.parent) && position > previousToken.end); } function isFunctionLikeButNotConstructor(kind: SyntaxKind) { @@ -2076,15 +2114,16 @@ namespace ts.Completions { return isConstructorParameterCompletionKeyword(stringToToken(text)); } - function isEqualityExpression(node: Node): node is BinaryExpression { - return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind); - } - - function isEqualityOperatorKind(kind: SyntaxKind) { - return kind === SyntaxKind.EqualsEqualsToken || - kind === SyntaxKind.ExclamationEqualsToken || - kind === SyntaxKind.EqualsEqualsEqualsToken || - kind === SyntaxKind.ExclamationEqualsEqualsToken; + function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator { + switch (kind) { + case ts.SyntaxKind.EqualsEqualsEqualsToken: + case ts.SyntaxKind.EqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsToken: + return true; + default: + return false; + } } /** Get the corresponding JSDocTag node if the position is in a jsDoc comment */ @@ -2119,8 +2158,7 @@ namespace ts.Completions { /** * Gets all properties on a type, but if that type is a union of several types, - * tries to only include those types which declare properties, not methods. - * This ensures that we don't try providing completions for all the methods on e.g. Array. + * excludes array-like types or callable/constructable types. */ function getPropertiesForCompletion(type: Type, checker: TypeChecker, isForAccess: boolean): Symbol[] { if (!(type.flags & TypeFlags.Union)) { diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 86db21e3225..6e43ac37def 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -43,21 +43,10 @@ namespace ts.FindAllReferences { export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray, sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined { const referencedSymbols = findAllReferencedSymbols(program, cancellationToken, sourceFiles, sourceFile, position); - - if (!referencedSymbols || !referencedSymbols.length) { - return undefined; - } - - const out: ReferencedSymbol[] = []; const checker = program.getTypeChecker(); - for (const { definition, references } of referencedSymbols) { + return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined(referencedSymbols, ({ definition, references }) => // Only include referenced symbols that have a valid definition. - if (definition) { - out.push({ definition: definitionToReferencedSymbolDefinitionInfo(definition, checker), references: references.map(toReferenceEntry) }); - } - } - - return out; + definition && { definition: definitionToReferencedSymbolDefinitionInfo(definition, checker), references: references.map(toReferenceEntry) }); } export function getImplementationsAtPosition(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray, sourceFile: SourceFile, position: number): ImplementationLocation[] { @@ -286,7 +275,7 @@ namespace ts.FindAllReferences.Core { } function isModuleReferenceLocation(node: ts.Node): boolean { - if (node.kind !== SyntaxKind.StringLiteral) { + if (node.kind !== SyntaxKind.StringLiteral && node.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) { return false; } switch (node.parent.kind) { @@ -376,7 +365,7 @@ namespace ts.FindAllReferences.Core { const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations); const result: SymbolAndEntries[] = []; - const state = new State(sourceFiles, /*isForConstructor*/ node.kind === SyntaxKind.ConstructorKeyword, checker, cancellationToken, searchMeaning, options, result); + const state = new State(sourceFiles, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result); if (node.kind === SyntaxKind.DefaultKeyword) { addReference(node, symbol, node, state); @@ -403,6 +392,21 @@ namespace ts.FindAllReferences.Core { return result; } + function getSpecialSearchKind(node: Node): SpecialSearchKind { + switch (node.kind) { + case SyntaxKind.ConstructorKeyword: + return SpecialSearchKind.Constructor; + case SyntaxKind.Identifier: + if (isClassLike(node.parent)) { + Debug.assert(node.parent.name === node); + return SpecialSearchKind.Class; + } + // falls through + default: + return SpecialSearchKind.None; + } + } + /** Handle a few special cases relating to export/import specifiers. */ function skipPastExportOrImportSpecifier(symbol: Symbol, node: Node, checker: TypeChecker): Symbol { const { parent } = node; @@ -439,6 +443,12 @@ namespace ts.FindAllReferences.Core { includes(symbol: Symbol): boolean; } + const enum SpecialSearchKind { + None, + Constructor, + Class, + } + /** * Holds all state needed for the finding references. * Unlike `Search`, there is only one `State`. @@ -472,7 +482,7 @@ namespace ts.FindAllReferences.Core { constructor( readonly sourceFiles: ReadonlyArray, /** True if we're searching for constructor references. */ - readonly isForConstructor: boolean, + readonly specialSearchKind: SpecialSearchKind, readonly checker: TypeChecker, readonly cancellationToken: CancellationToken, readonly searchMeaning: SemanticMeaning, @@ -845,11 +855,18 @@ namespace ts.FindAllReferences.Core { return; } - if (state.isForConstructor) { - findConstructorReferences(referenceLocation, sourceFile, search, state); - } - else { - addReference(referenceLocation, relatedSymbol, search.location, state); + switch (state.specialSearchKind) { + case SpecialSearchKind.None: + addReference(referenceLocation, relatedSymbol, search.location, state); + break; + case SpecialSearchKind.Constructor: + addConstructorReferences(referenceLocation, sourceFile, search, state); + break; + case SpecialSearchKind.Class: + addClassStaticThisReferences(referenceLocation, search, state); + break; + default: + Debug.assertNever(state.specialSearchKind); } getImportOrExportReferences(referenceLocation, referenceSymbol, search, state); @@ -961,27 +978,52 @@ namespace ts.FindAllReferences.Core { } /** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */ - function findConstructorReferences(referenceLocation: Node, sourceFile: SourceFile, search: Search, state: State): void { + function addConstructorReferences(referenceLocation: Node, sourceFile: SourceFile, search: Search, state: State): void { if (isNewExpressionTarget(referenceLocation)) { addReference(referenceLocation, search.symbol, search.location, state); } - const pusher = state.referenceAdder(search.symbol, search.location); + const pusher = () => state.referenceAdder(search.symbol, search.location); if (isClassLike(referenceLocation.parent)) { Debug.assert(referenceLocation.parent.name === referenceLocation); // This is the class declaration containing the constructor. - findOwnConstructorReferences(search.symbol, sourceFile, pusher); + findOwnConstructorReferences(search.symbol, sourceFile, pusher()); } else { // If this class appears in `extends C`, then the extending class' "super" calls are references. const classExtending = tryGetClassByExtendingIdentifier(referenceLocation); - if (classExtending && isClassLike(classExtending)) { - findSuperConstructorAccesses(classExtending, pusher); + if (classExtending) { + findSuperConstructorAccesses(classExtending, pusher()); } } } + function addClassStaticThisReferences(referenceLocation: Node, search: Search, state: State): void { + addReference(referenceLocation, search.symbol, search.location, state); + if (isClassLike(referenceLocation.parent)) { + Debug.assert(referenceLocation.parent.name === referenceLocation); + // This is the class declaration. + addStaticThisReferences(referenceLocation.parent, state.referenceAdder(search.symbol, search.location)); + } + } + + function addStaticThisReferences(classLike: ClassLikeDeclaration, pusher: (node: Node) => void): void { + for (const member of classLike.members) { + if (!(isMethodOrAccessor(member) && hasModifier(member, ModifierFlags.Static))) { + continue; + } + member.body.forEachChild(function cb(node) { + if (node.kind === SyntaxKind.ThisKeyword) { + pusher(node); + } + else if (!isFunctionLike(node)) { + node.forEachChild(cb); + } + }); + } + } + function getPropertyAccessExpressionFromRightHandSide(node: Node): PropertyAccessExpression { return isRightSideOfPropertyAccess(node) && node.parent; } @@ -992,7 +1034,7 @@ namespace ts.FindAllReferences.Core { */ function findOwnConstructorReferences(classSymbol: Symbol, sourceFile: SourceFile, addNode: (node: Node) => void): void { for (const decl of classSymbol.members.get(InternalSymbolName.Constructor).declarations) { - const ctrKeyword = ts.findChildOfKind(decl, ts.SyntaxKind.ConstructorKeyword, sourceFile)!; + const ctrKeyword = findChildOfKind(decl, ts.SyntaxKind.ConstructorKeyword, sourceFile)!; Debug.assert(decl.kind === SyntaxKind.Constructor && !!ctrKeyword); addNode(ctrKeyword); } @@ -1060,7 +1102,7 @@ namespace ts.FindAllReferences.Core { const containingTypeReference = getContainingTypeReference(refNode); if (containingTypeReference && state.markSeenContainingTypeReference(containingTypeReference)) { const parent = containingTypeReference.parent; - if (isVariableLike(parent) && parent.type === containingTypeReference && parent.initializer && isImplementationExpression(parent.initializer)) { + if (hasType(parent) && parent.type === containingTypeReference && hasInitializer(parent) && isImplementationExpression(parent.initializer)) { addReference(parent.initializer); } else if (isFunctionLike(parent) && parent.type === containingTypeReference && (parent as FunctionLikeDeclaration).body) { @@ -1385,7 +1427,7 @@ namespace ts.FindAllReferences.Core { // This is not needed when searching for re-exports. function populateSearchSymbolSet(symbol: Symbol, location: Node, checker: TypeChecker, implementations: boolean): Symbol[] { // The search set contains at least the current symbol - const result = [symbol]; + const result: Symbol[] = []; const containingObjectLiteralElement = getContainingObjectLiteralElement(location); if (containingObjectLiteralElement) { @@ -1402,9 +1444,9 @@ namespace ts.FindAllReferences.Core { // If the location is in a context sensitive location (i.e. in an object literal) try // to get a contextual type for it, and add the property symbol from the contextual // type to the search set - forEach(getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker), contextualSymbol => { - addRange(result, checker.getRootSymbols(contextualSymbol)); - }); + for (const contextualSymbol of getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker)) { + addRootSymbols(contextualSymbol); + } /* Because in short-hand property assignment, location has two meaning : property name and as value of the property * When we do findAllReference at the position of the short-hand property assignment, we would want to have references to position of @@ -1445,9 +1487,7 @@ namespace ts.FindAllReferences.Core { // If this is a union property, add all the symbols from all its source symbols in all unioned types. // If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list for (const rootSymbol of checker.getRootSymbols(sym)) { - if (rootSymbol !== sym) { - result.push(rootSymbol); - } + result.push(rootSymbol); // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { @@ -1471,7 +1511,7 @@ namespace ts.FindAllReferences.Core { * @param previousIterationSymbolsCache a cache of symbol from previous iterations of calling this function to prevent infinite revisiting of the same symbol. * The value of previousIterationSymbol is undefined when the function is first called. */ - function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Symbol[], previousIterationSymbolsCache: SymbolTable, checker: TypeChecker): void { + function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Push, previousIterationSymbolsCache: SymbolTable, checker: TypeChecker): void { if (!symbol) { return; } @@ -1540,9 +1580,7 @@ namespace ts.FindAllReferences.Core { // compare to our searchSymbol const containingObjectLiteralElement = getContainingObjectLiteralElement(referenceLocation); if (containingObjectLiteralElement) { - const contextualSymbol = forEach(getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker), contextualSymbol => - find(checker.getRootSymbols(contextualSymbol), search.includes)); - + const contextualSymbol = firstDefined(getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker), findRootSymbol); if (contextualSymbol) { return contextualSymbol; } @@ -1571,7 +1609,7 @@ namespace ts.FindAllReferences.Core { function findRootSymbol(sym: Symbol): Symbol | undefined { // Unwrap symbols to get to the root (e.g. transient symbols as a result of widening) // Or a union property, use its underlying unioned symbols - return forEach(state.checker.getRootSymbols(sym), rootSymbol => { + return firstDefined(checker.getRootSymbols(sym), rootSymbol => { // if it is in the list, then we are done if (search.includes(rootSymbol)) { return rootSymbol; @@ -1582,12 +1620,12 @@ namespace ts.FindAllReferences.Core { // parent symbol if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { // Parents will only be defined if implementations is true - if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, state.checker))) { + if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, checker))) { return undefined; } const result: Symbol[] = []; - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), state.checker); + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker); return find(result, search.includes); } @@ -1609,28 +1647,12 @@ namespace ts.FindAllReferences.Core { } /** Gets all symbols for one property. Does not get symbols for every property. */ - function getPropertySymbolsFromContextualType(node: ObjectLiteralElement, checker: TypeChecker): Symbol[] | undefined { - const objectLiteral = node.parent; - const contextualType = checker.getContextualType(objectLiteral); + function getPropertySymbolsFromContextualType(node: ObjectLiteralElement, checker: TypeChecker): ReadonlyArray { + const contextualType = checker.getContextualType(node.parent); const name = getNameFromObjectLiteralElement(node); - if (name && contextualType) { - const result: Symbol[] = []; - const symbol = contextualType.getProperty(name); - if (symbol) { - result.push(symbol); - } - - if (contextualType.flags & TypeFlags.Union) { - forEach((contextualType).types, t => { - const symbol = t.getProperty(name); - if (symbol) { - result.push(symbol); - } - }); - } - return result; - } - return undefined; + const symbol = contextualType && name && contextualType.getProperty(name); + return symbol ? [symbol] : + contextualType && contextualType.flags & TypeFlags.Union ? mapDefined((contextualType).types, t => t.getProperty(name)) : emptyArray; } /** @@ -1670,14 +1692,12 @@ namespace ts.FindAllReferences.Core { if (!node) { return false; } - else if (isVariableLike(node)) { - if (node.initializer) { - return true; - } - else if (node.kind === SyntaxKind.VariableDeclaration) { - const parentStatement = getParentStatementOfVariableDeclaration(node); - return parentStatement && hasModifier(parentStatement, ModifierFlags.Ambient); - } + else if (isVariableLike(node) && hasInitializer(node)) { + return true; + } + else if (node.kind === SyntaxKind.VariableDeclaration) { + const parentStatement = getParentStatementOfVariableDeclaration(node); + return parentStatement && hasModifier(parentStatement, ModifierFlags.Ambient); } else if (isFunctionLike(node)) { return !!(node as FunctionLikeDeclaration).body || hasModifier(node, ModifierFlags.Ambient); diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 8214ffd6f2d..470f3237eb9 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -69,10 +69,10 @@ namespace ts.formatting { rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), // More unary operator special-casing. - // DevDiv 181814: Be careful when removing leading whitespace + // DevDiv 181814: Be careful when removing leading whitespace // around unary operators. Examples: - // 1 - -2 --X--> 1--2 - // a + ++b --X--> a+++b + // 1 - -2 --X--> 1--2 + // a + ++b --X--> a+++b rule("SpaceAfterPostincrementWhenFollowedByAdd", SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), rule("SpaceAfterAddWhenFollowedByUnaryPlus", SyntaxKind.PlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), rule("SpaceAfterAddWhenFollowedByPreincrement", SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), @@ -80,7 +80,7 @@ namespace ts.formatting { rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.Delete), // For functions and control block place } on a new line [multi-line rule] rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.NewLine), @@ -198,7 +198,7 @@ namespace ts.formatting { RuleAction.Delete), // decorators - rule("SpaceBeforeAt", anyToken, SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.Space), rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), // Insert space after @ in decorator rule("SpaceAfterDecorator", @@ -231,8 +231,8 @@ namespace ts.formatting { rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Space), rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementContext, isNextTokenNotCloseBracket], RuleAction.Space), - rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementContext], RuleAction.Delete), + rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket], RuleAction.Space), + rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.Delete), // Insert space after function keyword for anonymous functions rule("SpaceAfterAnonymousFunctionKeyword", SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.Space), @@ -302,7 +302,7 @@ namespace ts.formatting { rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Delete), ]; - // These rules are lower in priority than user-configurable + // These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list. const lowPriorityCommonRules = [ // Space after keyword but not before ; or : or ? rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), @@ -312,10 +312,6 @@ namespace ts.formatting { rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - // No space before and after indexer - rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.Delete), - rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] @@ -323,10 +319,15 @@ namespace ts.formatting { "SpaceBetweenStatements", [SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword], anyToken, - [isNonJsxSameLineTokenContext, isNonJsxElementContext, isNotForContext], + [isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNotForContext], RuleAction.Space), // This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter. rule("SpaceAfterTryFinally", [SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword], SyntaxKind.OpenBraceToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + + // No space before and after indexer `x[]` + rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.Delete), + rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), ]; return [ @@ -613,12 +614,12 @@ namespace ts.formatting { return context.TokensAreOnSameLine() && context.contextNode.kind !== SyntaxKind.JsxText; } - function isNonJsxElementContext(context: FormattingContext): boolean { - return context.contextNode.kind !== SyntaxKind.JsxElement; + function isNonJsxElementOrFragmentContext(context: FormattingContext): boolean { + return context.contextNode.kind !== SyntaxKind.JsxElement && context.contextNode.kind !== SyntaxKind.JsxFragment; } function isJsxExpressionContext(context: FormattingContext): boolean { - return context.contextNode.kind === SyntaxKind.JsxExpression; + return context.contextNode.kind === SyntaxKind.JsxExpression || context.contextNode.kind === SyntaxKind.JsxSpreadAttribute; } function isNextTokenParentJsxAttribute(context: FormattingContext): boolean { diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 0a444a7280a..1e6323be9fa 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -367,12 +367,13 @@ namespace ts.formatting { function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorSettings): number { const containingList = getContainingList(node, sourceFile); - return containingList ? getActualIndentationFromList(containingList) : Value.Unknown; - - function getActualIndentationFromList(list: ReadonlyArray): number { - const index = indexOf(list, node); - return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : Value.Unknown; + if (containingList) { + const index = containingList.indexOf(node); + if (index !== -1) { + return deriveActualIndentationFromList(containingList, index, sourceFile, options); + } } + return Value.Unknown; } function getLineIndentationWhenExpressionIsInMultiLine(node: Node, sourceFile: SourceFile, options: EditorSettings): number { @@ -508,6 +509,7 @@ namespace ts.formatting { case SyntaxKind.ArrayBindingPattern: case SyntaxKind.ObjectBindingPattern: case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxOpeningFragment: case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxExpression: case SyntaxKind.MethodSignature: @@ -554,6 +556,8 @@ namespace ts.formatting { (!!(child).namedBindings && (child).namedBindings.kind !== SyntaxKind.NamedImports); case SyntaxKind.JsxElement: return childKind !== SyntaxKind.JsxClosingElement; + case SyntaxKind.JsxFragment: + return childKind !== SyntaxKind.JsxClosingFragment; } // No explicit rule for given nodes so the result will follow the default value argument return indentByDefault; diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index 0432c342c8f..d2ce802f177 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -616,7 +616,7 @@ namespace ts.FindAllReferences { /** If at an export specifier, go to the symbol it refers to. */ function skipExportSpecifierSymbol(symbol: Symbol, checker: TypeChecker): Symbol { - // For `export { foo } from './bar", there's nothing to skip, because it does not create a new alias. But `export { foo } does. + // For `export { foo } from './bar", there's nothing to skip, because it does not create a new alias. But `export { foo } does. if (symbol.declarations) { for (const declaration of symbol.declarations) { if (isExportSpecifier(declaration) && !(declaration as ExportSpecifier).propertyName && !(declaration as ExportSpecifier).parent.parent.moduleSpecifier) { diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 79b08780226..46f2458bba9 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -1,6 +1,5 @@ /* @internal */ namespace ts.JsDoc { - const singleLineTemplate = { newText: "/** */", caretOffset: 3 }; const jsDocTagNames = [ "augments", "author", @@ -113,7 +112,7 @@ namespace ts.JsDoc { function forEachUnique(array: T[], callback: (element: T, index: number) => U): U { if (array) { for (let i = 0; i < array.length; i++) { - if (indexOf(array, array[i]) === i) { + if (array.indexOf(array[i]) === i) { const result = callback(array[i], i); if (result) { return result; @@ -197,9 +196,16 @@ namespace ts.JsDoc { /** * Checks if position points to a valid position to add JSDoc comments, and if so, * returns the appropriate template. Otherwise returns an empty string. - * Invalid positions are - * - within comments, strings (including template literals and regex), and JSXText - * - within a token + * Valid positions are + * - outside of comments, statements, and expressions, and + * - preceding a: + * - function/constructor/method declaration + * - class declarations + * - variable statements + * - namespace declarations + * - interface declarations + * - method signatures + * - type alias declarations * * Hosts should ideally check that: * - The line is all whitespace up to 'position' before performing the insertion. @@ -225,19 +231,17 @@ namespace ts.JsDoc { const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos); if (!commentOwnerInfo) { - // if climbing the tree did not find a declaration with parameters, complete to a single line comment - return singleLineTemplate; + return undefined; } const { commentOwner, parameters } = commentOwnerInfo; - - if (commentOwner.kind === SyntaxKind.JsxText) { + if (commentOwner.getStart() < position) { return undefined; } - if (commentOwner.getStart() < position || parameters.length === 0) { - // if climbing the tree found a declaration with parameters but the request was made inside it - // or if there are no parameters, complete to a single line comment - return singleLineTemplate; + if (!parameters || parameters.length === 0) { + // if there are no parameters, just complete to a single line JSDoc comment + const singleLineResult = "/** */"; + return { newText: singleLineResult, caretOffset: 3 }; } const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position); @@ -247,11 +251,19 @@ namespace ts.JsDoc { const indentationStr = sourceFile.text.substr(lineStart, posLineAndChar.character).replace(/\S/i, () => " "); const isJavaScriptFile = hasJavaScriptFileExtension(sourceFile.fileName); - const docParams = parameters.map(({name}, i) => { - const nameText = isIdentifier(name) ? name.text : `param${i}`; - const type = isJavaScriptFile ? "{any} " : ""; - return `${indentationStr} * @param ${type}${nameText}${newLine}`; - }).join(""); + let docParams = ""; + for (let i = 0; i < parameters.length; i++) { + const currentName = parameters[i].name; + const paramName = currentName.kind === SyntaxKind.Identifier ? + (currentName).escapedText : + "param" + i; + if (isJavaScriptFile) { + docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`; + } + else { + docParams += `${indentationStr} * @param ${paramName}${newLine}`; + } + } // A doc comment consists of the following // * The opening comment line @@ -273,7 +285,7 @@ namespace ts.JsDoc { interface CommentOwnerInfo { readonly commentOwner: Node; - readonly parameters: ReadonlyArray; + readonly parameters?: ReadonlyArray; } function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined { for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) { @@ -285,18 +297,32 @@ namespace ts.JsDoc { const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature; return { commentOwner, parameters }; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumMember: + case SyntaxKind.TypeAliasDeclaration: + return { commentOwner }; + case SyntaxKind.VariableStatement: { const varStatement = commentOwner; const varDeclarations = varStatement.declarationList.declarations; const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer ? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer) : undefined; - return parameters ? { commentOwner, parameters } : undefined; + return { commentOwner, parameters }; } case SyntaxKind.SourceFile: return undefined; + case SyntaxKind.ModuleDeclaration: + // If in walking up the tree, we hit a a nested namespace declaration, + // then we must be somewhere within a dotted namespace name; however we don't + // want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'. + return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner }; + case SyntaxKind.BinaryExpression: { const be = commentOwner as BinaryExpression; if (getSpecialPropertyAssignmentKind(be) === ts.SpecialPropertyAssignmentKind.None) { @@ -305,11 +331,6 @@ namespace ts.JsDoc { const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray; return { commentOwner, parameters }; } - - case SyntaxKind.JsxText: { - const parameters: ReadonlyArray = emptyArray; - return { commentOwner, parameters }; - } } } } diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index 762726adb77..8449805ee71 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -46,7 +46,7 @@ namespace ts.NavigateTo { continue; } - // It was a match! If the pattern has dots in it, then also see if the + // It was a match! If the pattern has dots in it, then also see if the // declaration container matches as well. let containerMatches = matches; if (patternMatcher.patternContainsDots) { diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index 15c1ff99758..cb285d46417 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts.Completions.PathCompletions { - export function getStringLiteralCompletionsFromModuleNames(node: StringLiteral, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] { + export function getStringLiteralCompletionsFromModuleNames(node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] { const literalValue = normalizeSlashes(node.text); const scriptPath = node.getSourceFile().path; diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts index 8f6a468be64..a99683d45ef 100644 --- a/src/services/preProcess.ts +++ b/src/services/preProcess.ts @@ -295,7 +295,7 @@ namespace ts { // import "mod"; // import d from "mod" // import {a as A } from "mod"; - // import * as NS from "mod" + // import * as NS from "mod" // import d, {a, b as B} from "mod" // import i = require("mod"); // import("mod"); diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index b66d14ee449..cddf40ae017 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -50,7 +50,6 @@ namespace ts.refactor.convertFunctionToES6Class { const { file: sourceFile } = context; const ctorSymbol = getConstructorSymbol(context); - const newLine = context.formatContext.options.newLineCharacter; const deletedNodes: Node[] = []; const deletes: (() => any)[] = []; @@ -88,7 +87,7 @@ namespace ts.refactor.convertFunctionToES6Class { } // Because the preceding node could be touched, we need to insert nodes before delete nodes. - changeTracker.insertNodeAfter(sourceFile, precedingNode, newClassDeclaration, { suffix: newLine }); + changeTracker.insertNodeAfter(sourceFile, precedingNode, newClassDeclaration); for (const deleteCallback of deletes) { deleteCallback(); } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index f1a461b0088..b7aa5ac33d1 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -658,11 +658,10 @@ namespace ts.refactor.extractSymbol { case SyntaxKind.Constructor: return "constructor"; case SyntaxKind.FunctionExpression: - return scope.name - ? `function expression '${scope.name.text}'` - : "anonymous function expression"; case SyntaxKind.FunctionDeclaration: - return `function '${scope.name.text}'`; + return scope.name + ? `function '${scope.name.text}'` + : "anonymous function"; case SyntaxKind.ArrowFunction: return "arrow function"; case SyntaxKind.MethodDeclaration: @@ -811,13 +810,10 @@ namespace ts.refactor.extractSymbol { const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end; const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { - changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, { suffix: context.newLineCharacter + context.newLineCharacter }); + changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, /*blankLineBetween*/ true); } else { - changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, { - prefix: isLineBreak(file.text.charCodeAt(scope.getLastToken().pos)) ? context.newLineCharacter : context.newLineCharacter + context.newLineCharacter, - suffix: context.newLineCharacter - }); + changeTracker.insertNodeAtEndOfScope(context.file, scope, newFunction); } const newNodes: Node[] = []; @@ -960,10 +956,12 @@ namespace ts.refactor.extractSymbol { } } - const replacementRange = isReadonlyArray(range.range) - ? { pos: first(range.range).getStart(), end: last(range.range).end } - : { pos: range.range.getStart(), end: range.range.end }; - changeTracker.replaceRangeWithNodes(context.file, replacementRange, newNodes, { nodeSeparator: context.newLineCharacter }); + if (isReadonlyArray(range.range)) { + changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes); + } + else { + changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes); + } const edits = changeTracker.getChanges(); const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range; @@ -1006,7 +1004,7 @@ namespace ts.refactor.extractSymbol { const localNameText = getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file.text); const isJS = isInJavaScriptFile(scope); - const variableType = isJS + const variableType = isJS || !checker.isContextSensitive(node) ? undefined : checker.typeToTypeNode(checker.getContextualType(node), scope, NodeBuilderFlags.NoTruncation); @@ -1041,7 +1039,7 @@ namespace ts.refactor.extractSymbol { // Declare const maxInsertionPos = node.pos; const nodeToInsertBefore = getNodeToInsertPropertyBefore(maxInsertionPos, scope); - changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter }); + changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, /*blankLineBetween*/ true); // Consume changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference); @@ -1056,8 +1054,8 @@ namespace ts.refactor.extractSymbol { const oldVariableDeclaration = getContainingVariableDeclarationIfInList(node, scope); if (oldVariableDeclaration) { // Declare - // CONSIDER: could detect that each is on a separate line - changeTracker.insertNodeAt(context.file, oldVariableDeclaration.getStart(), newVariableDeclaration, { suffix: ", " }); + // CONSIDER: could detect that each is on a separate line (See `extractConstant_VariableList_MultipleLines` in `extractConstants.ts`) + changeTracker.insertNodeBefore(context.file, oldVariableDeclaration, newVariableDeclaration); // Consume const localReference = createIdentifier(localNameText); @@ -1079,17 +1077,10 @@ namespace ts.refactor.extractSymbol { // Declare const nodeToInsertBefore = getNodeToInsertConstantBefore(node, scope); if (nodeToInsertBefore.pos === 0) { - // If we're at the beginning of the file, we need to take care not to insert before header comments - // (e.g. copyright, triple-slash references). Fortunately, this problem has already been solved - // for imports. - const insertionPos = getSourceFileImportLocation(file); - changeTracker.insertNodeAt(context.file, insertionPos, newVariableStatement, { - prefix: insertionPos === 0 ? undefined : context.newLineCharacter, - suffix: isLineBreak(file.text.charCodeAt(insertionPos)) ? context.newLineCharacter : context.newLineCharacter + context.newLineCharacter - }); + changeTracker.insertNodeAtTopOfFile(context.file, newVariableStatement, /*blankLineBetween*/ false); } else { - changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariableStatement, { suffix: context.newLineCharacter + context.newLineCharacter }); + changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariableStatement, /*blankLineBetween*/ false); } // Consume @@ -1286,12 +1277,12 @@ namespace ts.refactor.extractSymbol { * If `scope` contains a function after `minPos`, then return the first such function. * Otherwise, return `undefined`. */ - function getNodeToInsertFunctionBefore(minPos: number, scope: Scope): Node | undefined { + function getNodeToInsertFunctionBefore(minPos: number, scope: Scope): Statement | ClassElement | undefined { return find(getStatementsOrClassElements(scope), child => child.pos >= minPos && isFunctionLikeDeclaration(child) && !isConstructorDeclaration(child)); } - function getNodeToInsertPropertyBefore(maxPos: number, scope: ClassLikeDeclaration): Node { + function getNodeToInsertPropertyBefore(maxPos: number, scope: ClassLikeDeclaration): ClassElement { const members = scope.members; Debug.assert(members.length > 0); // There must be at least one child, since we extracted from one. @@ -1317,7 +1308,7 @@ namespace ts.refactor.extractSymbol { return prevMember; } - function getNodeToInsertConstantBefore(node: Node, scope: Scope): Node { + function getNodeToInsertConstantBefore(node: Node, scope: Scope): Statement { Debug.assert(!isClassLike(scope)); let prevScope: Scope | undefined = undefined; diff --git a/src/services/services.ts b/src/services/services.ts index a600c7ec5ad..86f8b604798 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2053,7 +2053,7 @@ namespace ts { } function getTodoCommentsRegExp(): RegExp { - // NOTE: ?: means 'non-capture group'. It allows us to have groups without having to + // NOTE: `?:` means 'non-capture group'. It allows us to have groups without having to // filter them out later in the final result array. // TODO comments can appear in one of the following forms: diff --git a/src/services/shims.ts b/src/services/shims.ts index 8000a046831..34a8bbb10d9 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -141,7 +141,8 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): string; - getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/, source: string | undefined): string; + // tslint:disable-next-line type-operator-spacing (false positive) + getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -906,11 +907,12 @@ namespace ts { } /** Get a string based representation of a completion list entry details */ - public getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/, source: string | undefined) { + // tslint:disable-next-line type-operator-spacing (false positive) + public getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined) { return this.forwardJSONCall( `getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`, () => { - const localOptions: ts.FormatCodeOptions = JSON.parse(options); + const localOptions: ts.FormatCodeOptions = options === undefined ? undefined : JSON.parse(options); return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source); } ); diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 36c83f5f4af..f5338db954b 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -197,7 +197,7 @@ namespace ts.SignatureHelp { function getArgumentIndex(argumentsList: Node, node: Node) { // The list we got back can include commas. In the presence of errors it may // also just have nodes without commas. For example "Foo(a b c)" will have 3 - // args without commas. We want to find what index we're at. So we count + // args without commas. We want to find what index we're at. So we count // forward until we hit ourselves, only incrementing the index if it isn't a // comma. // @@ -224,12 +224,12 @@ namespace ts.SignatureHelp { // The argument count for a list is normally the number of non-comma children it has. // For example, if you have "Foo(a,b)" then there will be three children of the arg // list 'a' '' 'b'. So, in this case the arg count will be 2. However, there - // is a small subtlety. If you have "Foo(a,)", then the child list will just have + // is a small subtlety. If you have "Foo(a,)", then the child list will just have // 'a' ''. So, in the case where the last child is a comma, we increase the // arg count by one to compensate. // - // Note: this subtlety only applies to the last comma. If you had "Foo(a,," then - // we'll have: 'a' '' '' + // Note: this subtlety only applies to the last comma. If you had "Foo(a,," then + // we'll have: 'a' '' '' // That will give us 2 non-commas. We then add one for the last comma, giving us an // arg count of 3. const listChildren = argumentsList.getChildren(); @@ -253,9 +253,11 @@ namespace ts.SignatureHelp { // not enough to put us in the substitution expression; we should consider ourselves part of // the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1). // + // tslint:disable no-double-space // Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # ` // ^ ^ ^ ^ ^ ^ ^ ^ ^ // Case: 1 1 3 2 1 3 2 2 1 + // tslint:enable no-double-space Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node."); if (isTemplateLiteralKind(node.kind)) { if (isInsideTemplateLiteral(node, position)) { @@ -307,9 +309,8 @@ namespace ts.SignatureHelp { // Otherwise, we will not show signature help past the expression. // For example, // - // ` ${ 1 + 1 foo(10) - // | | - // + // ` ${ 1 + 1 foo(10) + // | | // This is because a Missing node has no width. However, what we actually want is to include trivia // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. if (template.kind === SyntaxKind.TemplateExpression) { diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index fe04342b8f7..7f66d84ba68 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -91,9 +91,14 @@ namespace ts.SymbolDisplay { } export function getSymbolModifiers(symbol: Symbol): string { - return symbol && symbol.declarations && symbol.declarations.length > 0 + const nodeModifiers = symbol && symbol.declarations && symbol.declarations.length > 0 ? getNodeModifiers(symbol.declarations[0]) : ScriptElementKindModifier.none; + + const symbolModifiers = symbol && symbol.flags & SymbolFlags.Optional ? + ScriptElementKindModifier.optionalModifier + : ScriptElementKindModifier.none; + return nodeModifiers && symbolModifiers ? nodeModifiers + "," + symbolModifiers : nodeModifiers || symbolModifiers; } interface SymbolDisplayPartsDocumentationAndSymbolKind { @@ -328,7 +333,7 @@ namespace ts.SymbolDisplay { else if (declaration.kind === SyntaxKind.TypeAliasDeclaration) { // Type alias type parameter // For example - // type list = T[]; // Both T will go through same code path + // type list = T[]; // Both T will go through same code path addInPrefix(); displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); displayParts.push(spacePart()); @@ -496,7 +501,7 @@ namespace ts.SymbolDisplay { addNewLineIfDisplayPartsExist(); if (symbolKind) { pushTypePart(symbolKind); - if (!some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { + if (symbol && !some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { displayParts.push(spacePart()); addFullSymbolName(symbol); } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index ee4b9091565..e73c639ba79 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -117,7 +117,7 @@ namespace ts.textChanges { readonly options?: never; } - export interface ChangeMultipleNodesOptions extends ChangeNodeOptions { + interface ChangeMultipleNodesOptions extends ChangeNodeOptions { nodeSeparator: string; } interface ReplaceWithMultipleNodes extends BaseChange { @@ -192,7 +192,7 @@ namespace ts.textChanges { } export class ChangeTracker { - private changes: Change[] = []; + private readonly changes: Change[] = []; private readonly newLineCharacter: string; private readonly deletedNodesInLists: true[] = []; // Stores ids of nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`. // Map from class id to nodes to insert at the start @@ -319,49 +319,80 @@ namespace ts.textChanges { return this; } - public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { - const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start); - const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options); - return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options); + public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray): void { + this.replaceWithMultiple(sourceFile, oldNode.getStart(sourceFile), oldNode.getEnd(), newNodes, { nodeSeparator: this.newLineCharacter }); } - public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { - const startPosition = getAdjustedStartPosition(sourceFile, oldNodes[0], options, Position.Start); - const endPosition = getAdjustedEndPosition(sourceFile, lastOrUndefined(oldNodes), options); - return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options); + public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray, newNodes: ReadonlyArray): void { + this.replaceWithMultiple(sourceFile, first(oldNodes).getStart(sourceFile), last(oldNodes).getEnd(), newNodes, { nodeSeparator: this.newLineCharacter }); } - public replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { - return this.replaceWithMultiple(sourceFile, range.pos, range.end, newNodes, options); - } - - public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { - const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start); - const endPosition = getAdjustedEndPosition(sourceFile, endNode, options); - return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options); - } - - public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) { + private insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) { this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, options, node: newNode, range: { pos, end: pos } }); return this; } - public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, options: InsertNodeOptions & ConfigurableStart = {}) { - const startPosition = getAdjustedStartPosition(sourceFile, before, options, Position.Start); - return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, options); + public insertNodeAtTopOfFile(sourceFile: SourceFile, newNode: Statement, blankLineBetween: boolean): void { + const pos = getInsertionPositionAtSourceFileTop(sourceFile); + this.insertNodeAt(sourceFile, pos, newNode, { + prefix: pos === 0 ? undefined : this.newLineCharacter, + suffix: (isLineBreak(sourceFile.text.charCodeAt(pos)) ? "" : this.newLineCharacter) + (blankLineBetween ? this.newLineCharacter : ""), + }); } - public insertNodeAtConstructorStart(sourceFile: SourceFile, ctr: ConstructorDeclaration, newStatement: Statement, newLineCharacter: string): void { + public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false) { + const startPosition = getAdjustedStartPosition(sourceFile, before, {}, Position.Start); + return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween)); + } + + public changeIdentifierToPropertyAccess(sourceFile: SourceFile, prefix: string, node: Identifier): void { + const startPosition = getAdjustedStartPosition(sourceFile, node, {}, Position.Start); + this.replaceWithSingle(sourceFile, startPosition, startPosition, createPropertyAccess(createIdentifier(prefix), ""), {}); + } + + private getOptionsForInsertNodeBefore(before: Node, doubleNewlines: boolean): ChangeNodeOptions { + if (isStatement(before) || isClassElement(before)) { + return { suffix: doubleNewlines ? this.newLineCharacter + this.newLineCharacter : this.newLineCharacter }; + } + else if (isVariableDeclaration(before)) { // insert `x = 1, ` into `const x = 1, y = 2; + return { suffix: ", " }; + } + throw Debug.failBadSyntaxKind(before); // We haven't handled this kind of node yet -- add it + } + + public insertNodeAtConstructorStart(sourceFile: SourceFile, ctr: ConstructorDeclaration, newStatement: Statement): void { const firstStatement = firstOrUndefined(ctr.body.statements); if (!firstStatement || !ctr.body.multiLine) { - this.replaceNode(sourceFile, ctr.body, createBlock([newStatement, ...ctr.body.statements], /*multiLine*/ true), { useNonAdjustedEndPosition: true }); + this.replaceConstructorBody(sourceFile, ctr, [newStatement, ...ctr.body.statements]); } else { - this.insertNodeBefore(sourceFile, firstStatement, newStatement, { suffix: newLineCharacter }); + this.insertNodeBefore(sourceFile, firstStatement, newStatement); } } - public insertNodeAtClassStart(sourceFile: SourceFile, cls: ClassLikeDeclaration, newElement: ClassElement, newLineCharacter: string): void { + public insertNodeAtConstructorEnd(sourceFile: SourceFile, ctr: ConstructorDeclaration, newStatement: Statement): void { + const lastStatement = lastOrUndefined(ctr.body.statements); + if (!lastStatement || !ctr.body.multiLine) { + this.replaceConstructorBody(sourceFile, ctr, [...ctr.body.statements, newStatement]); + } + else { + this.insertNodeAfter(sourceFile, lastStatement, newStatement); + } + } + + private replaceConstructorBody(sourceFile: SourceFile, ctr: ConstructorDeclaration, statements: ReadonlyArray): void { + this.replaceNode(sourceFile, ctr.body, createBlock(statements, /*multiLine*/ true), { useNonAdjustedEndPosition: true }); + } + + public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void { + const startPosition = getAdjustedStartPosition(sourceFile, scope.getLastToken(), {}, Position.Start); + this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, { + prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken().pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter, + suffix: this.newLineCharacter + }); + } + + public insertNodeAtClassStart(sourceFile: SourceFile, cls: ClassLikeDeclaration, newElement: ClassElement): void { const firstMember = firstOrUndefined(cls.members); if (!firstMember) { const id = getNodeId(cls).toString(); @@ -375,11 +406,11 @@ namespace ts.textChanges { } } else { - this.insertNodeBefore(sourceFile, firstMember, newElement, { suffix: newLineCharacter }); + this.insertNodeBefore(sourceFile, firstMember, newElement); } } - public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node, options: InsertNodeOptions & ConfigurableEnd = {}): this { + public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node): this { if (isStatementButNotDeclaration(after) || after.kind === SyntaxKind.PropertyDeclaration || after.kind === SyntaxKind.PropertySignature || @@ -396,8 +427,21 @@ namespace ts.textChanges { }); } } - const endPosition = getAdjustedEndPosition(sourceFile, after, options); - return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, options); + const endPosition = getAdjustedEndPosition(sourceFile, after, {}); + return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, this.getInsertNodeAfterOptions(after)); + } + + private getInsertNodeAfterOptions(node: Node): InsertNodeOptions { + if (isClassDeclaration(node) || isModuleDeclaration(node)) { + return { prefix: this.newLineCharacter, suffix: this.newLineCharacter }; + } + else if (isStatement(node) || isClassElement(node) || isTypeElement(node)) { + return { suffix: this.newLineCharacter }; + } + else if (isVariableDeclaration(node)) { + return { prefix: ", " }; + } + throw Debug.failBadSyntaxKind(node); // We haven't handled this kind of node yet -- add it } /** @@ -804,4 +848,46 @@ namespace ts.textChanges { this.lastNonTriviaPosition = 0; } } + + function getInsertionPositionAtSourceFileTop({ text }: SourceFile): number { + const shebang = getShebang(text); + let position = 0; + if (shebang !== undefined) { + position = shebang.length; + advancePastLineBreak(); + } + + // For a source file, it is possible there are detached comments we should not skip + let ranges = getLeadingCommentRanges(text, position); + if (!ranges) return position; + // However we should still skip a pinned comment at the top + if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) { + position = ranges[0].end; + advancePastLineBreak(); + ranges = ranges.slice(1); + } + // As well as any triple slash references + for (const range of ranges) { + if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(text, range.pos, range.end)) { + position = range.end; + advancePastLineBreak(); + continue; + } + break; + } + return position; + + function advancePastLineBreak() { + if (position < text.length) { + const charCode = text.charCodeAt(position); + if (isLineBreak(charCode)) { + position++; + + if (position < text.length && charCode === CharacterCodes.carriageReturn && text.charCodeAt(position) === CharacterCodes.lineFeed) { + position++; + } + } + } + } + } } diff --git a/src/services/types.ts b/src/services/types.ts index 471ae3b3459..813e8790933 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -954,6 +954,7 @@ namespace ts { ambientModifier = "declare", staticModifier = "static", abstractModifier = "abstract", + optionalModifier = "optional" } export const enum ClassificationTypeNames { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 358db5e8b4b..96a1cdd55fc 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -445,7 +445,7 @@ namespace ts { return position < candidate.end || !isCompletedNode(candidate, sourceFile); } - export function isCompletedNode(n: Node, sourceFile: SourceFile): boolean { + function isCompletedNode(n: Node, sourceFile: SourceFile): boolean { if (nodeIsMissing(n)) { return false; } @@ -512,7 +512,7 @@ namespace ts { case SyntaxKind.ExpressionStatement: return isCompletedNode((n).expression, sourceFile) || - hasChildOfKind(n, SyntaxKind.SemicolonToken); + hasChildOfKind(n, SyntaxKind.SemicolonToken, sourceFile); case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ArrayBindingPattern: @@ -540,11 +540,9 @@ namespace ts { return isCompletedNode((n).statement, sourceFile); case SyntaxKind.DoStatement: // rough approximation: if DoStatement has While keyword - then if node is completed is checking the presence of ')'; - const hasWhileKeyword = findChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile); - if (hasWhileKeyword) { - return nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile); - } - return isCompletedNode((n).statement, sourceFile); + return hasChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile) + ? nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile) + : isCompletedNode((n).statement, sourceFile); case SyntaxKind.TypeQuery: return isCompletedNode((n).exprName, sourceFile); @@ -619,12 +617,12 @@ namespace ts { }; } - export function hasChildOfKind(n: Node, kind: SyntaxKind, sourceFile?: SourceFile): boolean { + export function hasChildOfKind(n: Node, kind: SyntaxKind, sourceFile: SourceFile): boolean { return !!findChildOfKind(n, kind, sourceFile); } - export function findChildOfKind(n: Node, kind: SyntaxKind, sourceFile?: SourceFileLike): Node | undefined { - return forEach(n.getChildren(sourceFile), c => c.kind === kind && c); + export function findChildOfKind(n: Node, kind: T["kind"], sourceFile: SourceFileLike): T | undefined { + return find(n.getChildren(sourceFile), (c): c is T => c.kind === kind); } export function findContainingList(node: Node): SyntaxList | undefined { @@ -1109,14 +1107,6 @@ namespace ts { seen.set(key, true); return true; } - - export function singleElementArray(t: T | undefined): T[] { - return t === undefined ? undefined : [t]; - } - - export function getFirstChildOfKind(node: Node, sourceFile: SourceFile, kind: SyntaxKind): Node | undefined { - return find(node.getChildren(sourceFile), c => c.kind === kind); - } } // Display-part writer helpers @@ -1338,48 +1328,6 @@ namespace ts { return position; } - export function getSourceFileImportLocation({ text }: SourceFile) { - const shebang = getShebang(text); - let position = 0; - if (shebang !== undefined) { - position = shebang.length; - advancePastLineBreak(); - } - - // For a source file, it is possible there are detached comments we should not skip - let ranges = getLeadingCommentRanges(text, position); - if (!ranges) return position; - // However we should still skip a pinned comment at the top - if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) { - position = ranges[0].end; - advancePastLineBreak(); - ranges = ranges.slice(1); - } - // As well as any triple slash references - for (const range of ranges) { - if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(text, range.pos, range.end)) { - position = range.end; - advancePastLineBreak(); - continue; - } - break; - } - return position; - - function advancePastLineBreak() { - if (position < text.length) { - const charCode = text.charCodeAt(position); - if (isLineBreak(charCode)) { - position++; - - if (position < text.length && charCode === CharacterCodes.carriageReturn && text.charCodeAt(position) === CharacterCodes.lineFeed) { - position++; - } - } - } - } - } - /** * Creates a deep, memberwise clone of a node with no source map location. * diff --git a/tests/baselines/reference/ES5For-of31.types b/tests/baselines/reference/ES5For-of31.types index ee12f3894a8..309c8d1cab8 100644 --- a/tests/baselines/reference/ES5For-of31.types +++ b/tests/baselines/reference/ES5For-of31.types @@ -5,11 +5,11 @@ var a: string, b: number; for ({ a: b = 1, b: a = ""} of []) { >{ a: b = 1, b: a = ""} : { a?: number; b?: string; } ->a : undefined +>a : number >b = 1 : 1 >b : number >1 : 1 ->b : undefined +>b : string >a = "" : "" >a : string >"" : "" diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 2ef94be91cc..662f82a1ad7 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -453,6 +453,9 @@ declare namespace ts { interface JSDocContainer { } type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | EndOfFileToken; + type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; + type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; + type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertySignature | PropertyDeclaration | PropertyAssignment | EnumMember; interface NodeArray extends ReadonlyArray, TextRange { hasTrailingComma?: boolean; } @@ -604,15 +607,7 @@ declare namespace ts { kind: SyntaxKind.SpreadAssignment; expression: Expression; } - interface VariableLikeDeclaration extends NamedDeclaration { - propertyName?: PropertyName; - dotDotDotToken?: DotDotDotToken; - name: DeclarationName; - questionToken?: QuestionToken; - exclamationToken?: ExclamationToken; - type?: TypeNode; - initializer?: Expression; - } + type VariableLikeDeclaration = VariableDeclaration | ParameterDeclaration | BindingElement | PropertyDeclaration | PropertyAssignment | PropertySignature | JsxAttribute | ShorthandPropertyAssignment | EnumMember | JSDocPropertyTag | JSDocParameterTag; interface PropertyLikeDeclaration extends NamedDeclaration { name: PropertyName; } @@ -1620,7 +1615,7 @@ declare namespace ts { } interface ParseConfigHost { useCaseSensitiveFileNames: boolean; - readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number): string[]; + readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; /** * Gets a value indicating whether the specified path exists and is a file. * @param path The path to test. @@ -2071,6 +2066,7 @@ declare namespace ts { EvolvingArray = 256, ObjectLiteralPatternWithComputedProperties = 512, ContainsSpread = 1024, + ReverseMapped = 2048, ClassOrInterface = 3, } interface ObjectType extends Type { @@ -2472,11 +2468,12 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ - resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; + getEnvironmentVariable?(name: string): string; } interface SourceMapRange extends TextRange { source?: SourceMapSource; @@ -2910,7 +2907,7 @@ declare namespace ts { function isStringLiteral(node: Node): node is StringLiteral; function isJsxText(node: Node): node is JsxText; function isRegularExpressionLiteral(node: Node): node is RegularExpressionLiteral; - function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression; + function isNoSubstitutionTemplateLiteral(node: Node): node is NoSubstitutionTemplateLiteral; function isTemplateHead(node: Node): node is TemplateHead; function isTemplateMiddle(node: Node): node is TemplateMiddle; function isTemplateTail(node: Node): node is TemplateTail; @@ -3758,7 +3755,7 @@ declare namespace ts { function createPrinter(printerOptions?: PrinterOptions, handlers?: PrintHandlers): Printer; } declare namespace ts { - function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; + function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string | undefined; function resolveTripleslashReference(moduleName: string, containingFile: string): string; function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; @@ -4493,6 +4490,7 @@ declare namespace ts { ambientModifier = "declare", staticModifier = "static", abstractModifier = "abstract", + optionalModifier = "optional", } enum ClassificationTypeNames { comment = "comment", @@ -7077,7 +7075,7 @@ declare namespace ts.server { private getCombinedCodeFix({scope, fixId}, simplifiedResult); private applyCodeActionCommand(args); private getStartAndEndPosition(args, scriptInfo); - private mapCodeAction({description, changes: unmappedChanges, commands}, scriptInfo); + private mapCodeAction(project, {description, changes: unmappedChanges, commands}); private mapTextChangesToCodeEdits(project, textChanges); private mapTextChangesToCodeEditsUsingScriptinfo(textChanges, scriptInfo); private convertTextChangeToCodeEdit(change, scriptInfo); @@ -7230,6 +7228,7 @@ declare namespace ts.server { private program; private externalFiles; private missingFilesMap; + private plugins; private cachedUnresolvedImportsPerFile; private lastCachedUnresolvedImportsList; protected languageService: LanguageService; @@ -7344,6 +7343,9 @@ declare namespace ts.server { filesToString(writeProjectFileNames: boolean): string; setCompilerOptions(compilerOptions: CompilerOptions): void; protected removeRoot(info: ScriptInfo): void; + protected enableGlobalPlugins(): void; + protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]): void; + private enableProxy(pluginModuleFactory, configEntry); } /** * If a file is opened and no tsconfig (or jsconfig) is found, @@ -7372,7 +7374,6 @@ declare namespace ts.server { private typeAcquisition; private directoriesWatchedForWildcards; readonly canonicalConfigFilePath: NormalizedPath; - private plugins; /** Ref count to the project when opened from external project */ private externalProjectRefCount; private projectErrors; @@ -7383,8 +7384,6 @@ declare namespace ts.server { updateGraph(): boolean; getConfigFilePath(): NormalizedPath; enablePlugins(): void; - private enablePlugin(pluginConfigEntry, searchPaths); - private enableProxy(pluginModuleFactory, configEntry); /** * Get the errors that dont have any file name associated */ @@ -7396,7 +7395,6 @@ declare namespace ts.server { setProjectErrors(projectErrors: Diagnostic[]): void; setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void; getTypeAcquisition(): TypeAcquisition; - getExternalFiles(): SortedReadonlyArray; close(): void; getEffectiveTypeRoots(): string[]; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 427218aa390..9a46f6155a2 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -453,6 +453,9 @@ declare namespace ts { interface JSDocContainer { } type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | EndOfFileToken; + type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; + type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; + type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertySignature | PropertyDeclaration | PropertyAssignment | EnumMember; interface NodeArray extends ReadonlyArray, TextRange { hasTrailingComma?: boolean; } @@ -604,15 +607,7 @@ declare namespace ts { kind: SyntaxKind.SpreadAssignment; expression: Expression; } - interface VariableLikeDeclaration extends NamedDeclaration { - propertyName?: PropertyName; - dotDotDotToken?: DotDotDotToken; - name: DeclarationName; - questionToken?: QuestionToken; - exclamationToken?: ExclamationToken; - type?: TypeNode; - initializer?: Expression; - } + type VariableLikeDeclaration = VariableDeclaration | ParameterDeclaration | BindingElement | PropertyDeclaration | PropertyAssignment | PropertySignature | JsxAttribute | ShorthandPropertyAssignment | EnumMember | JSDocPropertyTag | JSDocParameterTag; interface PropertyLikeDeclaration extends NamedDeclaration { name: PropertyName; } @@ -1620,7 +1615,7 @@ declare namespace ts { } interface ParseConfigHost { useCaseSensitiveFileNames: boolean; - readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number): string[]; + readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; /** * Gets a value indicating whether the specified path exists and is a file. * @param path The path to test. @@ -2071,6 +2066,7 @@ declare namespace ts { EvolvingArray = 256, ObjectLiteralPatternWithComputedProperties = 512, ContainsSpread = 1024, + ReverseMapped = 2048, ClassOrInterface = 3, } interface ObjectType extends Type { @@ -2472,11 +2468,12 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ - resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; + getEnvironmentVariable?(name: string): string; } interface SourceMapRange extends TextRange { source?: SourceMapSource; @@ -2963,7 +2960,7 @@ declare namespace ts { function isStringLiteral(node: Node): node is StringLiteral; function isJsxText(node: Node): node is JsxText; function isRegularExpressionLiteral(node: Node): node is RegularExpressionLiteral; - function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression; + function isNoSubstitutionTemplateLiteral(node: Node): node is NoSubstitutionTemplateLiteral; function isTemplateHead(node: Node): node is TemplateHead; function isTemplateMiddle(node: Node): node is TemplateMiddle; function isTemplateTail(node: Node): node is TemplateTail; @@ -3705,7 +3702,7 @@ declare namespace ts { function createPrinter(printerOptions?: PrinterOptions, handlers?: PrintHandlers): Printer; } declare namespace ts { - function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; + function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string | undefined; function resolveTripleslashReference(moduleName: string, containingFile: string): string; function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; @@ -3860,6 +3857,7 @@ declare namespace ts { } declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; + type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string) => void; interface WatchCompilerHost { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; @@ -3892,10 +3890,12 @@ declare namespace ts { realpath?(path: string): string; /** If provided would be used to write log about compilation */ trace?(s: string): void; + /** If provided is used to get the environment variable */ + getEnvironmentVariable?(name: string): string; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ - resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; /** Used to watch changes in source files, missing files needed to update the program or config file */ watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ @@ -3960,11 +3960,11 @@ declare namespace ts { /** * Create the watched program for config file */ - function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile; + function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfConfigFile; /** * Create the watched program for root files and compiler options */ - function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions; + function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for root files and compiler options */ @@ -4742,6 +4742,7 @@ declare namespace ts { ambientModifier = "declare", staticModifier = "static", abstractModifier = "abstract", + optionalModifier = "optional", } enum ClassificationTypeNames { comment = "comment", diff --git a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt index ecae349e19e..e01fce65781 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/jsx/file.tsx(42,27): error TS2322: Type '{ a: 10; b: "hi"; children: Element[]; }' is not assignable to type 'IntrinsicAttributes & SingleChildProp'. - Type '{ a: 10; b: "hi"; children: Element[]; }' is not assignable to type 'SingleChildProp'. +tests/cases/conformance/jsx/file.tsx(42,27): error TS2322: Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'IntrinsicAttributes & SingleChildProp'. + Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'SingleChildProp'. Types of property 'children' are incompatible. Type 'Element[]' is not assignable to type 'Element'. Property 'type' is missing in type 'Element[]'. @@ -49,8 +49,8 @@ tests/cases/conformance/jsx/file.tsx(42,27): error TS2322: Type '{ a: 10; b: "hi // Error let k5 = <>