├── .editorconfig ├── .gitattributes ├── .gitignore ├── .npmignore ├── .travis.yml ├── .unibeautifyrc.yml ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── Gruntfile.js ├── LICENSE ├── README.md ├── additional_rule_metadata.json ├── appveyor.yml ├── cwe_descriptions.json ├── index.js ├── license.txt ├── package-lock.json ├── package.json ├── recommended_ruleset.js ├── src ├── idLengthRule.ts ├── maxFuncArgsRule.ts ├── minClassCohesionRule.ts ├── newspaperOrderRule.ts ├── noCommentedOutCodeRule.ts ├── noComplexConditionalsRule.ts ├── noFeatureEnvyRule.ts ├── noFlagArgsRule.ts ├── noForEachPushRule.ts ├── noMapWithoutUsageRule.ts ├── preferDryConditionalsRule.ts ├── tests │ ├── IdLengthRuleTests.ts │ ├── MaxFuncArgsRuleTests.ts │ ├── MinClassCohesionRuleTests.ts │ ├── NewspaperOrderRuleTests.ts │ ├── NoCommentedOutCodeRuleTests.ts │ ├── NoComplexConditionalsRuleTests.ts │ ├── NoFeatureEnvyRuleTests.ts │ ├── NoFlagArgsRuleTests.ts │ ├── NoForEachPushRuleTests.ts │ ├── NoMapWithoutUsageRuleTests.ts │ ├── PreferDryConditionalsRuleTests.ts │ ├── TestHelper.ts │ ├── TryCatchFirstRuleTests.ts │ └── tslint.json ├── tryCatchFirstRule.ts └── utils │ ├── AstUtils.ts │ ├── BannedTermWalker.ts │ ├── BaseFormatter.ts │ ├── ChaiUtils.ts │ ├── DirectedAcyclicGraph.ts │ ├── ErrorTolerantWalker.ts │ ├── ExtendedMetadata.ts │ ├── JsxAttribute.ts │ ├── MochaUtils.ts │ ├── NoStringParameterToFunctionCallWalker.ts │ ├── Scope.ts │ ├── ScopedSymbolTrackingWalker.ts │ ├── TypeGuard.ts │ ├── Utils.ts │ ├── attributes │ ├── IAria.ts │ ├── IDom.ts │ ├── IRole.ts │ ├── README.md │ ├── ariaSchema.json │ ├── domSchema.json │ └── roleSchema.json │ ├── getImplicitRole.ts │ └── implicitRoles │ ├── README.md │ ├── a.ts │ ├── area.ts │ ├── article.ts │ ├── aside.ts │ ├── body.ts │ ├── button.ts │ ├── datalist.ts │ ├── dd.ts │ ├── details.ts │ ├── dialog.ts │ ├── dl.ts │ ├── dt.ts │ ├── footer.ts │ ├── form.ts │ ├── h1.ts │ ├── h2.ts │ ├── h3.ts │ ├── h4.ts │ ├── h5.ts │ ├── h6.ts │ ├── header.ts │ ├── hr.ts │ ├── img.ts │ ├── index.ts │ ├── input.ts │ ├── li.ts │ ├── link.ts │ ├── main.ts │ ├── math.ts │ ├── menu.ts │ ├── menuitem.ts │ ├── meter.ts │ ├── nav.ts │ ├── ol.ts │ ├── optgroup.ts │ ├── option.ts │ ├── output.ts │ ├── progress.ts │ ├── section.ts │ ├── select.ts │ ├── summary.ts │ ├── table.ts │ ├── tbody.ts │ ├── td.ts │ ├── textarea.ts │ ├── tfoot.ts │ ├── th.ts │ ├── thead.ts │ ├── tr.ts │ └── ul.ts ├── templates ├── recommended_ruleset.js.snippet ├── rule-tests.snippet └── rule.snippet ├── test-data ├── ExportName │ ├── ExportNameRuleFailingTestInput.ts │ ├── ExportNameRuleFailingTestInput2.tsx │ ├── ExportNameRulePassingTestInput.ts │ └── ExportNameRulePassingTestInput2.tsx ├── NoCookies │ ├── NoCookiesFailingTestInput.ts │ ├── NoCookiesPassingTestInput.ts │ └── NoCookiesTestInput-error.ts ├── NoFunctionConstructorWithStringArgsTestInput.ts ├── NoMapWithoutUsage │ └── JavaScriptExpressionInReact.tsx ├── NoOctalLiteral │ └── NoOctalLiteralTestInput-passing.ts ├── NoReservedKeywords │ ├── NoReservedKeywordsTestInput-any.ts │ ├── NoReservedKeywordsTestInput-as.ts │ ├── NoReservedKeywordsTestInput-boolean.ts │ ├── NoReservedKeywordsTestInput-break.ts │ ├── NoReservedKeywordsTestInput-case.ts │ ├── NoReservedKeywordsTestInput-catch.ts │ ├── NoReservedKeywordsTestInput-class.ts │ ├── NoReservedKeywordsTestInput-const.ts │ ├── NoReservedKeywordsTestInput-constructor.ts │ ├── NoReservedKeywordsTestInput-continue.ts │ ├── NoReservedKeywordsTestInput-debugger.ts │ ├── NoReservedKeywordsTestInput-declare.ts │ ├── NoReservedKeywordsTestInput-default.ts │ ├── NoReservedKeywordsTestInput-delete.ts │ ├── NoReservedKeywordsTestInput-do.ts │ ├── NoReservedKeywordsTestInput-else.ts │ ├── NoReservedKeywordsTestInput-enum.ts │ ├── NoReservedKeywordsTestInput-export.ts │ ├── NoReservedKeywordsTestInput-extends.ts │ ├── NoReservedKeywordsTestInput-false.ts │ ├── NoReservedKeywordsTestInput-finally.ts │ ├── NoReservedKeywordsTestInput-for.ts │ ├── NoReservedKeywordsTestInput-from.ts │ ├── NoReservedKeywordsTestInput-function.ts │ ├── NoReservedKeywordsTestInput-get.ts │ ├── NoReservedKeywordsTestInput-if.ts │ ├── NoReservedKeywordsTestInput-implements.ts │ ├── NoReservedKeywordsTestInput-import.ts │ ├── NoReservedKeywordsTestInput-in.ts │ ├── NoReservedKeywordsTestInput-instanceof.ts │ ├── NoReservedKeywordsTestInput-interface.ts │ ├── NoReservedKeywordsTestInput-let.ts │ ├── NoReservedKeywordsTestInput-module.ts │ ├── NoReservedKeywordsTestInput-new.ts │ ├── NoReservedKeywordsTestInput-null.ts │ ├── NoReservedKeywordsTestInput-number.ts │ ├── NoReservedKeywordsTestInput-of.ts │ ├── NoReservedKeywordsTestInput-package.ts │ ├── NoReservedKeywordsTestInput-private.ts │ ├── NoReservedKeywordsTestInput-protected.ts │ ├── NoReservedKeywordsTestInput-public.ts │ ├── NoReservedKeywordsTestInput-require.ts │ ├── NoReservedKeywordsTestInput-return.ts │ ├── NoReservedKeywordsTestInput-set.ts │ ├── NoReservedKeywordsTestInput-static.ts │ ├── NoReservedKeywordsTestInput-string.ts │ ├── NoReservedKeywordsTestInput-super.ts │ ├── NoReservedKeywordsTestInput-switch.ts │ ├── NoReservedKeywordsTestInput-symbol.ts │ ├── NoReservedKeywordsTestInput-this.ts │ ├── NoReservedKeywordsTestInput-throw.ts │ ├── NoReservedKeywordsTestInput-true.ts │ ├── NoReservedKeywordsTestInput-try.ts │ ├── NoReservedKeywordsTestInput-type.ts │ ├── NoReservedKeywordsTestInput-typeof.ts │ ├── NoReservedKeywordsTestInput-var.ts │ ├── NoReservedKeywordsTestInput-void.ts │ ├── NoReservedKeywordsTestInput-while.ts │ ├── NoReservedKeywordsTestInput-with.ts │ └── NoReservedKeywordsTestInput-yield.ts ├── NoUnnecessarySemicolonsTestInput.ts └── PreferTypeCastRuleTests-passing.tsx ├── tsconfig.json ├── tsconfig.testdata.json ├── tslint-warnings.csv └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | charset = utf-8 7 | 8 | [src/**] 9 | indent_size = 4 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | 4 | # TypeScrit temporary/cache files 5 | **/.baseDir.ts 6 | **/.tscache/ 7 | tscommand-*.tmp.txt 8 | 9 | **/dist/ 10 | 11 | # IDEA 12 | .idea 13 | tslint-microsoft-contrib.iml 14 | 15 | # vim swap files 16 | *.sw* 17 | 18 | **/tags 19 | 20 | # local npm cache 21 | .npm_cache 22 | 23 | out.html 24 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | 4 | # TypeScrit temporary/cache files 5 | **/.baseDir.ts 6 | **/.tscache/ 7 | tscommand-*.tmp.txt 8 | 9 | # IDEA 10 | .idea 11 | tslint-microsoft-contrib.iml 12 | 13 | # vim swap files 14 | *.sw* 15 | 16 | **/tags 17 | 18 | # local npm cache 19 | .npm_cache 20 | 21 | out.html 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '10' 5 | - '8' 6 | # - '6' 7 | # - '4' 8 | 9 | before_script: 10 | - npm install -g grunt-cli 11 | 12 | sudo: false 13 | -------------------------------------------------------------------------------- /.unibeautifyrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | TypeScript: 3 | beautifiers: ["Prettier"] 4 | align_assignments: false 5 | arrow_parens: "as-needed" 6 | break_chained_methods: true 7 | end_with_comma: true 8 | end_with_semicolon: true 9 | indent_char: " " 10 | indent_style: "space" 11 | indent_size: 4 12 | jsx_brackets: false 13 | multiline_ternary: true 14 | object_curly_spacing: true 15 | quotes: "single" 16 | space_after_anon_function: false 17 | wrap_line_length: 140 18 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 6 | "EditorConfig.EditorConfig", 7 | "eg2.tslint" 8 | ] 9 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Mocha Tests", 11 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", 12 | "args": [ 13 | "-u", 14 | "tdd", 15 | "--timeout", 16 | "999999", 17 | "--colors", 18 | "${workspaceRoot}/dist/src/tests/**/*.js" 19 | ], 20 | //"internalConsoleOptions": "openOnSessionStart", 21 | "protocol": "inspector", 22 | "cwd": "${workspaceRoot}", 23 | "sourceMaps": true, 24 | "outFiles": [ 25 | "${workspaceRoot}/dist/src/**/*.js" 26 | ], 27 | "preLaunchTask": "build" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.insertSpaces": true, 4 | "editor.tabSize": 4, 5 | "tslint.nodePath": "./node_modules/tslint/bin/tslint", 6 | "typescript.tsdk": "./node_modules/typescript/lib", 7 | "files.trimTrailingWhitespace": true 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "identifier": "build", 8 | "type": "grunt", 9 | "task": "ts", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Glavin Wiechert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tslint-clean-code [![Build Status](https://travis-ci.org/Glavin001/tslint-clean-code.svg?branch=master)](https://travis-ci.org/Glavin001/tslint-clean-code) [![Build status](https://ci.appveyor.com/api/projects/status/8femxsete95is18a/branch/master?svg=true)](https://ci.appveyor.com/project/Glavin001/tslint-clean-code/branch/master) 2 | ====== 3 | 4 | > A set of [TSLint](https://github.com/palantir/tslint) rules used to enforce [Clean Code](https://www.amazon.ca/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) practices. Inspired by [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.ca/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882). 5 | 6 | :point_right: Sign up for [**CodePass**, *the Quickest Way To Solve Your Coding Errors*](https://codepass.ca/)! :boom: 7 | 8 | ## Installation 9 | 10 | ```bash 11 | npm install tslint-clean-code 12 | ``` 13 | 14 | ## Configuration 15 | 16 | ##### Configure `tslint.json` 17 | 18 | In your `tslint.json` file, extend this package. 19 | For example: 20 | 21 | ```json 22 | { 23 | "extends": [ 24 | "tslint-clean-code" 25 | ], 26 | "rules": { 27 | "newspaper-order": true 28 | } 29 | } 30 | ``` 31 | 32 | You can also extend other tslint config packages to combine this plugin with other community custom rules. 33 | 34 | ##### Configure your Grunt build task 35 | 36 | Add the new rulesDirectory to your tslint task: 37 | 38 | grunt.initConfig({ 39 | tslint: { 40 | options: { 41 | rulesDirectory: 'node_modules/tslint-clean-code/dist/src', 42 | configuration: grunt.file.readJSON("tslint.json") 43 | }, 44 | files: { 45 | src: ['src/file1.ts', 'src/file2.ts'] 46 | } 47 | } 48 | }) 49 | 50 | The tslint.json file does not change format when using this package. Just add our rule definitions to your existing tslint.json file. 51 | 52 | ## Supported Rules 53 | 54 | Rule Name | Description | Since 55 | :---------- | :------------ | ------------- 56 | `id-length` | Enforces a minimum and/or maximum identifier length convention. | 0.1.0 57 | `try-catch-first` | Try-catch blocks must be first within the scope. Try-catch blocks are transactions and should leave your program in a consistent state, no matter what happens in the try. | 0.1.0 58 | `max-func-args` | Limit the number of input arguments for a function. The ideal number of arguments for a function is zero (niladic). | 0.1.0 59 | `min-class-cohesion` | The more variables a method manipulates the more cohesive that method is to its class. A class in which each variable is used by each method is maximally cohesive. We would like cohesion to be high. When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole. | 0.1.0 60 | `newspaper-order` | We would like a source file to be like a newspaper article. Detail should increase as we move downward, until at the end we find the lowest level functions and details in the source file. | 0.1.0 61 | `no-flag-args` | Functions should only do one thing, therefore passing a boolean into a function is a bad practice. The function does one thing if the flag is true and another if the flag is false! | 0.1.0 62 | `no-for-each-push` | Enforce using `Array.prototype.map` instead of `Array.prototype.forEach` and `Array.prototype.push`. | 0.1.0 63 | `no-feature-envy` | A method accesses the data of another object more than its own data. | 0.1.8 64 | `no-map-without-usage` | Ensure results of `Array.prototype.map` is either assigned to variable or returned | 0.1.0 65 | `no-complex-conditionals` | Enforce the maximum complexity of conditional expressions. | 0.1.0 66 | `prefer-dry-conditionals` | Don't-Repeat-Yourself in if statement conditionals, instead use Switch statements. | 0.1.0 67 | `no-commented-out-code` | Code must not be commented out. | 0.2.0 68 | 69 | ## Development 70 | 71 | To develop tslint-clean-code simply clone the repository, install dependencies and run grunt: 72 | 73 | ``` 74 | git clone git@github.com:Glavin001/tslint-clean-code.git --config core.autocrlf=input --config core.eol=lf 75 | cd tslint-clean-code 76 | npm install 77 | grunt all 78 | grunt create-rule --rule-name=no-something-or-other 79 | ``` 80 | 81 | ## Debug code 82 | 83 | If command fails because of file access permissions, prefix it with sudo. 84 | 85 | ```bash 86 | npm install -g node-inspector 87 | ``` 88 | 89 | Then run: 90 | 91 | ```bash 92 | node-debug grunt mochaTest 93 | ``` 94 | 95 | The `node-debug` command will load Node Inspector in your default browser (works in Chrome and Opera only). 96 | 97 | Set a breakpoint somewhere in your code and resume execution. Your breakpoint should be hit. 98 | 99 | ## Thank you 100 | 101 | Thank you to maintainers of [tslint-microsoft-contrib](https://github.com/Microsoft/tslint-microsoft-contrib), from which this repository was forked. The initial structure was kept and new rules were added, this would not have been possible without Microsoft's awesome work! 102 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against the latest version of this Node.js version 2 | environment: 3 | nodejs_version: "8" 4 | 5 | install: 6 | # Get the latest stable version of Node.js 7 | - ps: Install-Product node $env:nodejs_version 8 | - npm install -g grunt-cli 9 | - npm install 10 | 11 | test_script: 12 | # Output useful info for debugging. 13 | - node --version 14 | - npm --version 15 | # run tests 16 | - npm test 17 | 18 | # Don't actually build. 19 | build: off 20 | -------------------------------------------------------------------------------- /cwe_descriptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "75": "Failure to Sanitize Special Elements into a Different Plane (Special Element Injection)", 3 | "79": "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')", 4 | "85": "Doubled Character XSS Manipulations", 5 | "95": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", 6 | "116": "Improper Encoding or Escaping of Output", 7 | "157": "Failure to Sanitize Paired Delimiters", 8 | "159": "Failure to Sanitize Special Element", 9 | "242": "Use of Inherently Dangerous Function", 10 | "315": "Cleartext Storage of Sensitive Information in a Cookie", 11 | "319": "Cleartext Transmission of Sensitive Information", 12 | "330": "Use of Insufficiently Random Values", 13 | "351": "Insufficient Type Distinction", 14 | "398": "Indicator of Poor Code Quality", 15 | "453": "Insecure Default Variable Initialization", 16 | "454": "External Initialization of Trusted Variables or Data Stores", 17 | "456": "Missing Initialization of a Variable", 18 | "462": "Duplicate Key in Associative List (Alist)", 19 | "474": "Use of Function with Inconsistent Implementations", 20 | "478": "Missing Default Case in Switch Statement", 21 | "480": "Use of Incorrect Operator", 22 | "481": "Assigning instead of Comparing", 23 | "483": "Incorrect Block Delimitation", 24 | "484": "Omitted Break Statement in Switch", 25 | "539": "Information Exposure Through Persistent Cookies", 26 | "546": "Suspicious Comment", 27 | "563": "Assignment to Variable without Use ('Unused Variable')", 28 | "565": "Reliance on Cookies without Validation and Integrity Checking", 29 | "570": "Expression is Always False", 30 | "571": "Expression is Always True", 31 | "584": "Return Inside Finally Block", 32 | "597": "Use of Wrong Operator in String Comparison", 33 | "614": "Sensitive Cookie in HTTPS Session Without 'Secure' Attribute", 34 | "670": "Always-Incorrect Control Flow Implementation", 35 | "676": "Use of Potentially Dangerous Function", 36 | "694": "Use of Multiple Resources with Duplicate Identifier", 37 | "704": "Incorrect Type Conversion or Cast", 38 | "705": "Incorrect Control Flow Scoping", 39 | "710": "Coding Standards Violation", 40 | "749": "Exposed Dangerous Method or Function", 41 | "915": "Improperly Controlled Modification of Dynamically-Determined Object Attributes" 42 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // unopinionated extensable tslint configuration 2 | // loads rules for extending packages, but does not enable any 3 | module.exports = { 4 | rulesDirectory: "./dist/src", 5 | }; -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | tslint-microsoft-contrib 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 10 | software and associated documentation files (the ""Software""), to deal in the Software 11 | without restriction, including without limitation the rights to use, copy, modify, 12 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to the following 14 | conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all copies 17 | or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 20 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 24 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tslint-clean-code", 3 | "version": "0.2.10", 4 | "description": "TSLint rules for enforcing Clean Code", 5 | "main": "index.js", 6 | "analyze": true, 7 | "dependencies": { 8 | "memoize-decorator": "^1.0.2", 9 | "tsutils": "2.7.1" 10 | }, 11 | "devDependencies": { 12 | "@types/chai": "^4.1.2", 13 | "@types/mocha": "^2.2.48", 14 | "@types/node": "^9.4.6", 15 | "@types/react": "^15.6.14", 16 | "@types/underscore": "^1.8.7", 17 | "chai": "3.2.0", 18 | "grunt": "^1.0.1", 19 | "grunt-contrib-clean": "^1.1.0", 20 | "grunt-contrib-copy": "^1.0.0", 21 | "grunt-contrib-watch": "^1.0.0", 22 | "grunt-mocha-test": "0.13.2", 23 | "grunt-ts": "^6.0.0-beta.17", 24 | "grunt-tslint": "^5.0.1", 25 | "load-grunt-tasks": "3.2.0", 26 | "mocha": "2.2.5", 27 | "strip-indent": "^2.0.0", 28 | "time-grunt": "1.2.1", 29 | "tslint": "5.1.0", 30 | "tslint-microsoft-contrib": "^5.0.1", 31 | "typescript": "^2.4.2", 32 | "underscore": "1.8.3" 33 | }, 34 | "peerDependencies": { 35 | "tslint": ">=5.1.0" 36 | }, 37 | "scripts": { 38 | "test": "grunt all", 39 | "lint": "tslint 'src/**/*.ts' -e 'src/tests/references.ts'", 40 | "lint-fix": "tslint --fix 'src/**/*.ts' -e 'src/tests/references.ts'", 41 | "build": "grunt ts", 42 | "prepublishOnly": "npm run test" 43 | }, 44 | "repository": { 45 | "type": "git", 46 | "url": "git+https://github.com/Glavin001/tslint-clean-code.git" 47 | }, 48 | "keywords": [ 49 | "tslint", 50 | "typescript", 51 | "clean", 52 | "code" 53 | ], 54 | "author": "Glavin Wiechert", 55 | "license": "MIT", 56 | "bugs": { 57 | "url": "https://github.com/Glavin001/tslint-clean-code/issues" 58 | }, 59 | "homepage": "https://github.com/Glavin001/tslint-clean-code#readme" 60 | } 61 | -------------------------------------------------------------------------------- /src/idLengthRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | export const FAILURE_MIN_STRING: string = 'Too short; difficult to understand its purpose without context'; 8 | export const FAILURE_MAX_STRING: string = 'Too long; difficult to read and potentially less maintainable'; 9 | 10 | const OPTION_MIN_STRING: string = 'min'; 11 | const OPTION_MAX_STRING: string = 'max'; 12 | const OPTION_EXCEPTIONS_STRING: string = 'exceptions'; 13 | 14 | /** 15 | * Implementation of the id-length rule. 16 | */ 17 | export class Rule extends Lint.Rules.AbstractRule { 18 | public static metadata: ExtendedMetadata = { 19 | ruleName: 'id-length', 20 | type: 'maintainability', 21 | description: 'This rule enforces a minimum and/or maximum identifier length convention.', 22 | options: { 23 | definitions: { 24 | 'minimum-length': { 25 | type: 'integer', 26 | minimum: 1, 27 | default: 2, 28 | }, 29 | 'maximum-length': { 30 | type: 'integer', 31 | minimum: 1, 32 | }, 33 | exceptions: { 34 | type: 'array', 35 | items: { 36 | type: 'string', 37 | }, 38 | minLength: 0, 39 | uniqueItems: true, 40 | }, 41 | }, 42 | type: 'array', 43 | items: { 44 | type: 'array', 45 | items: { 46 | oneOf: [ 47 | { 48 | title: 'Only the minimum length', 49 | $ref: '#/definitions/minimum-length', 50 | }, 51 | { 52 | title: 'Only the exceptions array', 53 | $ref: '#/definitions/exceptions', 54 | }, 55 | { 56 | title: 'Configuration object', 57 | type: 'object', 58 | properties: { 59 | min: { $ref: '#/definitions/minimum-length' }, 60 | max: { $ref: '#/definitions/maximum-length' }, 61 | exceptions: { $ref: '#/definitions/exceptions' }, 62 | }, 63 | additionalProperties: false, 64 | minProperties: 1, 65 | }, 66 | ], 67 | }, 68 | minItems: 1, 69 | maxItems: 1, 70 | }, 71 | }, 72 | optionsDescription: ` 73 | One of the following combinations can be provided: 74 | * Minimum desired length. 75 | * An array of exceptions. 76 | * Minimum desired length and an exceptions array. 77 | * A configuration object containing at least one of the following properties: 78 | * \`"${OPTION_MIN_STRING}"\` 79 | * \`"${OPTION_MAX_STRING}"\` 80 | * \`"${OPTION_EXCEPTIONS_STRING}"\` 81 | `, 82 | optionExamples: [ 83 | [true], 84 | [true, 2], 85 | [true, ['x', 'y', 'f', 'c']], 86 | [true, 2, ['x', 'y', 'f', 'c']], 87 | [ 88 | true, 89 | { 90 | min: 2, 91 | max: 10, 92 | exceptions: ['x', 'y', 'f', 'c'], 93 | }, 94 | ], 95 | ], 96 | typescriptOnly: true, 97 | issueClass: 'Non-SDL', 98 | issueType: 'Warning', 99 | severity: 'Important', 100 | level: 'Opportunity for Excellence', 101 | group: 'Correctness', 102 | recommendation: 'true,', 103 | commonWeaknessEnumeration: '', 104 | }; 105 | 106 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 107 | return this.applyWithWalker(new IdLengthRuleWalker(sourceFile, this.getOptions())); 108 | } 109 | } 110 | 111 | class IdLengthRuleWalker extends ErrorTolerantWalker { 112 | private min: number = 2; 113 | private max: number = Infinity; 114 | private exceptions: string[] = []; 115 | 116 | constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { 117 | super(sourceFile, options); 118 | this.parseOptions(); 119 | } 120 | 121 | protected visitIdentifier(node: ts.Identifier): void { 122 | this.checkAndReport(node); 123 | super.visitIdentifier(node); 124 | } 125 | 126 | private checkAndReport(node: ts.Identifier): void { 127 | const { text } = node; 128 | if (this.exceptions.indexOf(text) === -1) { 129 | if (text.length < this.min) { 130 | return this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_MIN_STRING + ': ' + text); 131 | } 132 | if (text.length > this.max) { 133 | return this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_MAX_STRING + ': ' + text); 134 | } 135 | } 136 | } 137 | 138 | private parseOptions(): void { 139 | this.getOptions().forEach((opt: any) => { 140 | if (typeof opt === 'boolean') { 141 | return; 142 | } 143 | if (typeof opt === 'number') { 144 | this.min = opt; 145 | return; 146 | } 147 | if (Array.isArray(opt)) { 148 | this.exceptions = opt; 149 | return; 150 | } 151 | if (typeof opt === 'object') { 152 | this.min = typeof opt[OPTION_MIN_STRING] === 'number' ? opt[OPTION_MIN_STRING] : this.min; 153 | this.max = typeof opt[OPTION_MAX_STRING] === 'number' ? opt[OPTION_MAX_STRING] : this.max; 154 | this.exceptions = opt[OPTION_EXCEPTIONS_STRING] || this.exceptions; 155 | return; 156 | } 157 | }); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/maxFuncArgsRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | export const FAILURE_STRING: string = 'Exceeds maximum function argument list length of '; 8 | export const FAILURE_RECOMMENDATION_STRING: string = 9 | '\nConsider these two solutions for refactoring:\n' + 10 | '1) Create a Class and pass common arguments into the constructor as instance properties. ' + 11 | 'Move this function to the new Class with a reduced arguments list.\n' + 12 | '2) Instantiate an object containing each of the arguments and pass in the object instance as a single argument.'; 13 | export const DEFAULT_MAX_ARGS_LENGTH: number = 3; 14 | 15 | /** 16 | * Implementation of the newspaper-order rule. 17 | */ 18 | export class Rule extends Lint.Rules.AbstractRule { 19 | public static metadata: ExtendedMetadata = { 20 | ruleName: 'max-func-args', 21 | type: 'maintainability', 22 | description: 'The ideal number of arguments for a function is zero (niladic).', 23 | options: null, 24 | optionsDescription: '', 25 | typescriptOnly: true, 26 | issueClass: 'Non-SDL', 27 | issueType: 'Warning', 28 | severity: 'Important', 29 | level: 'Opportunity for Excellence', 30 | group: 'Correctness', 31 | recommendation: '[true, 3],', 32 | commonWeaknessEnumeration: '', 33 | }; 34 | 35 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 36 | return this.applyWithWalker(new MaxFunctionArgsRuleWalker(sourceFile, this.getOptions())); 37 | } 38 | } 39 | 40 | class MaxFunctionArgsRuleWalker extends ErrorTolerantWalker { 41 | private maxArgs: number = DEFAULT_MAX_ARGS_LENGTH; 42 | 43 | constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { 44 | super(sourceFile, options); 45 | this.parseOptions(); 46 | } 47 | 48 | protected visitFunctionDeclaration(node: ts.FunctionDeclaration): void { 49 | this.checkAndReport(node); 50 | super.visitFunctionDeclaration(node); 51 | } 52 | 53 | protected visitArrowFunction(node: ts.ArrowFunction): void { 54 | this.checkAndReport(node); 55 | super.visitArrowFunction(node); 56 | } 57 | 58 | protected visitMethodDeclaration(node: ts.MethodDeclaration): void { 59 | this.checkAndReport(node); 60 | super.visitMethodDeclaration(node); 61 | } 62 | 63 | private checkAndReport(node: ts.SignatureDeclaration) { 64 | if (node.parameters.length > this.maxArgs) { 65 | const failureMessage = this.makeFailureMessage(); 66 | const { start, width } = this.getStartAndWidth(node.parameters); 67 | this.addFailureAt(start, width, failureMessage); 68 | } 69 | } 70 | 71 | private getStartAndWidth(nodes: ts.NodeArray) { 72 | const { pos, end } = nodes; 73 | const start = pos; 74 | const width = end - pos; 75 | return { 76 | start, 77 | width, 78 | }; 79 | } 80 | 81 | private makeFailureMessage(): string { 82 | return FAILURE_STRING + this.maxArgs + FAILURE_RECOMMENDATION_STRING; 83 | } 84 | 85 | private parseOptions(): void { 86 | this.getOptions().forEach((opt: any) => { 87 | if (typeof opt === 'boolean') { 88 | return; 89 | } 90 | if (typeof opt === 'number') { 91 | this.maxArgs = opt; 92 | return; 93 | } 94 | }); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/noCommentedOutCodeRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | import { forEachTokenWithTrivia } from 'tsutils'; 7 | 8 | const FAILURE_STRING: string = 'No commented out code.'; 9 | 10 | /** 11 | * Implementation of the no-commented-out-code rule. 12 | */ 13 | export class Rule extends Lint.Rules.AbstractRule { 14 | public static metadata: ExtendedMetadata = { 15 | ruleName: 'no-commented-out-code', 16 | type: 'maintainability', // one of: 'functionality' | 'maintainability' | 'style' | 'typescript' 17 | description: 'Code must not be commented out.', 18 | options: null, 19 | optionsDescription: '', 20 | optionExamples: [], // Remove this property if the rule has no options 21 | typescriptOnly: false, 22 | issueClass: 'Non-SDL', // one of: 'SDL' | 'Non-SDL' | 'Ignored' 23 | issueType: 'Warning', // one of: 'Error' | 'Warning' 24 | severity: 'Low', // one of: 'Critical' | 'Important' | 'Moderate' | 'Low' 25 | level: 'Opportunity for Excellence', // one of 'Mandatory' | 'Opportunity for Excellence' 26 | group: 'Clarity', // one of 'Ignored' | 'Security' | 'Correctness' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated' 27 | commonWeaknessEnumeration: '', // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) 28 | }; 29 | 30 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 31 | return this.applyWithWalker(new NoCommentedOutCodeRuleWalker(sourceFile, this.getOptions())); 32 | } 33 | } 34 | 35 | class NoCommentedOutCodeRuleWalker extends ErrorTolerantWalker { 36 | public visitSourceFile(node: ts.SourceFile) { 37 | forEachTokenWithTrivia(node, (text, tokenSyntaxKind, range) => { 38 | if (tokenSyntaxKind === ts.SyntaxKind.SingleLineCommentTrivia || tokenSyntaxKind === ts.SyntaxKind.MultiLineCommentTrivia) { 39 | this.scanCommentForCode(range.pos, text.substring(range.pos, range.end)); 40 | } 41 | }); 42 | } 43 | 44 | private scanCommentForCode(startPosition: number, commentText: string) { 45 | const trimmedCommentText = this.trimTextLines(commentText); 46 | 47 | if (this.isTextCode(trimmedCommentText)) { 48 | this.handleSuspiciousComment(startPosition, commentText); 49 | } 50 | } 51 | 52 | /** 53 | * Removes spaces and comment delimiters at beginning 54 | * and end of each line 55 | */ 56 | private trimTextLines(text: string): string { 57 | const lines = text.split('\n'); 58 | 59 | const trimAtStart = /^\s*\/*\**\s*/; 60 | const trimAtEnd = /\s*\**\/*\s*$/; 61 | 62 | const trimmedLines = lines.map(line => { 63 | return line.replace(trimAtStart, '').replace(trimAtEnd, ''); 64 | }); 65 | 66 | return trimmedLines.join('\n'); 67 | } 68 | 69 | private isTextCode(text: string): boolean { 70 | if (this.isTextSingleWord(text)) { 71 | return false; 72 | } 73 | if (this.isTextTsLintFlag(text)) { 74 | return false; 75 | } 76 | if (this.isTextToDoLikeNote(text)) { 77 | return false; 78 | } 79 | 80 | return this.isTextCodeWithoutErrors(text); 81 | } 82 | 83 | private isTextSingleWord(text: string): boolean { 84 | const pattern = new RegExp('^([\\w-]*)$'); 85 | return pattern.test(text.trim()); 86 | } 87 | 88 | /** 89 | * TSLint flags will be will be parsed as labeled statements and thus 90 | * may result in valid code, so they need to be handled separately 91 | */ 92 | private isTextTsLintFlag(text: string): boolean { 93 | return text.startsWith('tslint:'); 94 | } 95 | 96 | /** 97 | * These notes followed by a colon will be parsed as labeled statements 98 | * and thus may result in valid code, so they need to be handled separately 99 | */ 100 | private isTextToDoLikeNote(text: string): boolean { 101 | return /^(NOTE|TODO|FIXME|BUG|HACK|XXX):/.test(text); 102 | } 103 | 104 | /** 105 | * If text contains statements but not one error, it must be code 106 | */ 107 | private isTextCodeWithoutErrors(text: string) { 108 | const sourceFile = this.createSourceFileFromText(text); 109 | 110 | if (!this.hasSourceFileStatements(sourceFile)) { 111 | return false; 112 | } 113 | 114 | const sourceFileDiagnostics = this.getSourceFileDiagnostics(sourceFile); 115 | 116 | return sourceFileDiagnostics.length === 0; 117 | } 118 | 119 | private createSourceFileFromText(text: string): ts.SourceFile { 120 | return ts.createSourceFile('', text, ts.ScriptTarget.ES5, true); 121 | } 122 | 123 | private hasSourceFileStatements(sourceFile: ts.SourceFile): boolean { 124 | return sourceFile && sourceFile.statements.length > 0; 125 | } 126 | 127 | /** 128 | * The most efficient way to get a source file's diagnostics is from parseDiagnostics, 129 | * which isn't exposed in the API, since the cast to any. 130 | * @see https://github.com/Microsoft/TypeScript/issues/21940 131 | * Tried using ts.Program.getSyntacticDiagnostics + getDeclarationDiagnostics, which 132 | * wasn't quiet as fast. 133 | */ 134 | private getSourceFileDiagnostics(sourceFile: ts.SourceFile): ts.Diagnostic[] { 135 | return (sourceFile).parseDiagnostics; 136 | } 137 | 138 | private handleSuspiciousComment(startPosition: number, commentText: string) { 139 | this.addFailureAt(startPosition, commentText.length, FAILURE_STRING); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/noComplexConditionalsRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | /** 8 | * Implementation of the no-complex-conditionals rule. 9 | */ 10 | export class Rule extends Lint.Rules.AbstractRule { 11 | public static metadata: ExtendedMetadata = { 12 | ruleName: 'no-complex-conditionals', 13 | type: 'maintainability', // one of: 'functionality' | 'maintainability' | 'style' | 'typescript' 14 | description: 'Enforce the maximum complexity of conditional expressions.', 15 | options: null, 16 | optionsDescription: '', 17 | optionExamples: [], //Remove this property if the rule has no options 18 | typescriptOnly: false, 19 | issueClass: 'Non-SDL', // one of: 'SDL' | 'Non-SDL' | 'Ignored' 20 | issueType: 'Warning', // one of: 'Error' | 'Warning' 21 | severity: 'Moderate', // one of: 'Critical' | 'Important' | 'Moderate' | 'Low' 22 | level: 'Opportunity for Excellence', // one of 'Mandatory' | 'Opportunity for Excellence' 23 | group: 'Clarity', // one of 'Ignored' | 'Security' | 'Correctness' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated' 24 | commonWeaknessEnumeration: '', // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) 25 | }; 26 | 27 | public static FAILURE_STRING: string = 28 | 'Conditional expression is too complex. ' + 'Consider moving expression to a variable or function with a meaningful name.'; 29 | 30 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 31 | return this.applyWithWalker(new NoComplexConditionalsRuleWalker(sourceFile, this.getOptions())); 32 | } 33 | } 34 | 35 | class NoComplexConditionalsRuleWalker extends ErrorTolerantWalker { 36 | private threshold: number = 3; 37 | 38 | constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { 39 | super(sourceFile, options); 40 | this.parseOptions(); 41 | } 42 | 43 | protected visitIfStatement(node: ts.IfStatement): void { 44 | const { expression } = node; 45 | const complexity = this.countComplexity(expression); 46 | // console.log('complexity', complexity); //tslint:disable-line 47 | if (complexity > this.threshold) { 48 | this.addFailureAtNode(expression, Rule.FAILURE_STRING); 49 | } 50 | super.visitIfStatement(node); 51 | } 52 | 53 | private countComplexity(expression: ts.Expression): number { 54 | let complexity = 0; 55 | const cb = (node: ts.Node) => { 56 | // console.log('complexity', complexity); //tslint:disable-line 57 | if (this.increasesComplexity(node)) { 58 | complexity = complexity + 1; 59 | } 60 | return ts.forEachChild(node, cb); 61 | }; 62 | // ts.forEachChild(expression, cb); 63 | cb(expression); 64 | return complexity; 65 | } 66 | 67 | private increasesComplexity(node: ts.Node): boolean { 68 | // console.log(ts.SyntaxKind[node.kind] + ' ' + node.getText()); 69 | switch (node.kind) { 70 | case ts.SyntaxKind.CaseClause: 71 | return (node).statements.length > 0; 72 | case ts.SyntaxKind.CatchClause: 73 | case ts.SyntaxKind.ConditionalExpression: 74 | case ts.SyntaxKind.DoStatement: 75 | case ts.SyntaxKind.ForStatement: 76 | case ts.SyntaxKind.ForInStatement: 77 | case ts.SyntaxKind.ForOfStatement: 78 | case ts.SyntaxKind.IfStatement: 79 | case ts.SyntaxKind.WhileStatement: 80 | return true; 81 | 82 | case ts.SyntaxKind.BinaryExpression: 83 | switch ((node).operatorToken.kind) { 84 | case ts.SyntaxKind.BarBarToken: 85 | case ts.SyntaxKind.AmpersandAmpersandToken: 86 | return true; 87 | default: 88 | return false; 89 | } 90 | 91 | default: 92 | return false; 93 | } 94 | } 95 | 96 | private parseOptions(): void { 97 | this.getOptions().forEach((opt: any) => { 98 | if (typeof opt === 'boolean') { 99 | return; 100 | } 101 | if (typeof opt === 'number') { 102 | this.threshold = opt; 103 | return; 104 | } 105 | }); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/noFlagArgsRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | export const FAILURE_STRING: string = 'Flag (boolean) arguments are not allowed: '; 8 | 9 | /** 10 | * Implementation of the newspaper-order rule. 11 | */ 12 | export class Rule extends Lint.Rules.AbstractRule { 13 | public static metadata: ExtendedMetadata = { 14 | ruleName: 'no-flag-args', 15 | type: 'maintainability', 16 | description: 17 | 'Passing a boolean into a function is a truly terrible practice. ' + 18 | 'It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing.', 19 | options: null, 20 | optionsDescription: '', 21 | typescriptOnly: true, 22 | issueClass: 'Non-SDL', 23 | issueType: 'Warning', 24 | severity: 'Important', 25 | level: 'Opportunity for Excellence', 26 | group: 'Correctness', 27 | recommendation: 'true,', 28 | commonWeaknessEnumeration: '', 29 | }; 30 | 31 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 32 | return this.applyWithWalker(new NoFlagArgsRuleWalker(sourceFile, this.getOptions())); 33 | } 34 | } 35 | 36 | class NoFlagArgsRuleWalker extends ErrorTolerantWalker { 37 | protected visitParameterDeclaration(node: ts.ParameterDeclaration): void { 38 | if (this.isBooleanParameter(node) && !this.belongsToSetAssessor(node)) { 39 | const failureMessage = this.makeFailureMessage(node, FAILURE_STRING); 40 | this.addFailureAt(node.getStart(), node.getWidth(), failureMessage); 41 | } 42 | super.visitParameterDeclaration(node); 43 | } 44 | 45 | private isBooleanParameter(node: ts.ParameterDeclaration): boolean { 46 | const { type } = node; 47 | return type && type.kind === ts.SyntaxKind.BooleanKeyword; 48 | } 49 | 50 | private belongsToSetAssessor(node: ts.ParameterDeclaration): boolean { 51 | const { parent } = node; 52 | return parent && parent.kind === ts.SyntaxKind.SetAccessor; 53 | } 54 | 55 | private makeFailureMessage(node: ts.ParameterDeclaration, failureString: string): string { 56 | const paramName = node.name.getText(); 57 | const pascalCaseParamName = this.toPascalCase(paramName); 58 | const functionName: string | undefined = node.parent.name && node.parent.name.getText(); 59 | const recommendation = functionName 60 | ? '\nSplit the function into two, such as ' + 61 | `${functionName}When${pascalCaseParamName}` + 62 | ' and ' + 63 | `${functionName}WhenNot${pascalCaseParamName}.` 64 | : '\nSplit the function into two.'; 65 | return failureString + paramName + recommendation; 66 | } 67 | 68 | private toPascalCase(str: string) { 69 | if (typeof str !== 'string' || str.length === 0) { 70 | return str; 71 | } 72 | return str[0].toUpperCase() + str.slice(1); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/noForEachPushRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | /** 8 | * Implementation of the id-length rule. 9 | */ 10 | export class Rule extends Lint.Rules.AbstractRule { 11 | public static metadata: ExtendedMetadata = { 12 | ruleName: 'no-for-each-push', 13 | type: 'maintainability', 14 | description: 'Enforce using Array.prototype.map instead of Array.prototype.forEach and Array.prototype.push.', 15 | options: null, 16 | optionsDescription: '', 17 | typescriptOnly: true, 18 | issueClass: 'Non-SDL', 19 | issueType: 'Warning', 20 | severity: 'Important', 21 | level: 'Opportunity for Excellence', 22 | group: 'Correctness', 23 | recommendation: 'true,', 24 | commonWeaknessEnumeration: '', 25 | }; 26 | 27 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 28 | return this.applyWithWalker(new NoForeachPushRuleWalker(sourceFile, this.getOptions())); 29 | } 30 | 31 | public static FAILURE_STRING: string = 32 | 'Do not use Array.prototype.push inside of Array.prototype.forEach. ' + 'Use Array.prototype.map instead to replace both.'; 33 | } 34 | 35 | class NoForeachPushRuleWalker extends ErrorTolerantWalker { 36 | protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { 37 | this.checkAndReport(node); 38 | super.visitPropertyAccessExpression(node); 39 | } 40 | 41 | private checkAndReport(node: ts.PropertyAccessExpression): void { 42 | const isCallExpression = ts.isCallExpression(node.parent); 43 | const isForEach = node.name.text === 'forEach'; 44 | // console.log('checkAndReport', isCallExpression, isForEach); // tslint:disable-line 45 | if (isCallExpression && isForEach) { 46 | if (this.doesCallPush(node)) { 47 | this.addFailureAtNode(node.parent, Rule.FAILURE_STRING); 48 | } 49 | } 50 | } 51 | 52 | private doesCallPush(node: ts.PropertyAccessExpression) { 53 | const walker = new PushCallWalker(); 54 | walker.walk(node.parent); 55 | return walker.isFound; 56 | } 57 | } 58 | 59 | class PushCallWalker extends Lint.SyntaxWalker { 60 | private foundPush: boolean = false; 61 | private foundIf: boolean = false; 62 | 63 | protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { 64 | const isCallExpression = ts.isCallExpression(node.parent); 65 | const isPush = node.name.text === 'push'; 66 | // console.log('PushCallWalker', isCallExpression, isPush, node); // tslint:disable-line 67 | if (isCallExpression && isPush) { 68 | this.foundPush = true; 69 | return; 70 | } 71 | super.visitPropertyAccessExpression(node); 72 | } 73 | 74 | protected visitIfStatement(): void { 75 | this.foundIf = true; 76 | return; 77 | } 78 | 79 | public get isFound(): boolean { 80 | return !this.foundIf && this.foundPush; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/noMapWithoutUsageRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | /** 8 | * Implementation of the no-map-without-usage rule. 9 | */ 10 | export class Rule extends Lint.Rules.AbstractRule { 11 | public static metadata: ExtendedMetadata = { 12 | ruleName: 'no-map-without-usage', 13 | type: 'maintainability', // one of: 'functionality' | 'maintainability' | 'style' | 'typescript' 14 | description: 'Prevent Array.prototype.map from being called and results not used.', 15 | options: null, 16 | optionsDescription: '', 17 | optionExamples: [], //Remove this property if the rule has no options 18 | typescriptOnly: false, 19 | issueClass: 'Non-SDL', // one of: 'SDL' | 'Non-SDL' | 'Ignored' 20 | issueType: 'Warning', // one of: 'Error' | 'Warning' 21 | severity: 'Low', // one of: 'Critical' | 'Important' | 'Moderate' | 'Low' 22 | level: 'Opportunity for Excellence', // one of 'Mandatory' | 'Opportunity for Excellence' 23 | group: 'Correctness', // one of 'Ignored' | 'Security' | 'Correctness' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated' 24 | commonWeaknessEnumeration: '', // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) 25 | }; 26 | 27 | public static FAILURE_STRING: string = 28 | 'Return value from Array.prototype.map should be assigned to a variable. ' + 'Consider using Array.prototype.forEach instead.'; 29 | 30 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 31 | return this.applyWithWalker(new NoMapWithoutAssignmentRuleWalker(sourceFile, this.getOptions())); 32 | } 33 | } 34 | 35 | class NoMapWithoutAssignmentRuleWalker extends ErrorTolerantWalker { 36 | protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { 37 | this.checkAndReport(node); 38 | super.visitPropertyAccessExpression(node); 39 | } 40 | 41 | private checkAndReport(node: ts.PropertyAccessExpression): void { 42 | if (this.isMapCall(node) && !this.isAssignment(node) && !this.isUsed(node)) { 43 | this.addFailureAtNode(node, Rule.FAILURE_STRING); 44 | } 45 | } 46 | 47 | private isMapCall(node: ts.PropertyAccessExpression): boolean { 48 | const isCallExpression = ts.isCallExpression(node.parent); 49 | const isMap = node.name.text === 'map'; 50 | return isCallExpression && isMap; 51 | } 52 | 53 | private isAssignment(node: ts.PropertyAccessExpression): boolean { 54 | const { parent: parent1 } = node; 55 | if (parent1 && ts.isCallExpression(parent1)) { 56 | const { parent: parent2 } = parent1; 57 | const parentIsAssignment = 58 | ts.isPropertyAssignment(parent2) || 59 | ts.isVariableDeclaration(parent2) || 60 | (ts.isBinaryExpression(parent2) && parent2.operatorToken.kind === ts.SyntaxKind.FirstAssignment); 61 | if (parentIsAssignment) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | private isUsed(node: ts.PropertyAccessExpression): boolean { 69 | const { parent: parent1 } = node; 70 | if (parent1 && ts.isCallExpression(parent1)) { 71 | const { parent: parent2 } = parent1; 72 | if (this.parentUsesNode(parent2)) { 73 | return true; 74 | } 75 | } 76 | return false; 77 | } 78 | 79 | private parentUsesNode(parent?: ts.Node) { 80 | return ( 81 | parent && 82 | (ts.isPropertyAccessExpression(parent) || 83 | ts.isPropertyDeclaration(parent) || 84 | ts.isReturnStatement(parent) || 85 | ts.isCallOrNewExpression(parent) || 86 | ts.isSpreadElement(parent) || 87 | ts.isJsxExpression(parent) || 88 | ts.isConditionalExpression(parent) || 89 | ts.isArrayLiteralExpression(parent) || 90 | ts.isBinaryExpression(parent) || 91 | ts.isArrowFunction(parent)) 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/preferDryConditionalsRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | /** 8 | * Implementation of the prefer-dry-conditionals rule. 9 | */ 10 | export class Rule extends Lint.Rules.AbstractRule { 11 | public static metadata: ExtendedMetadata = { 12 | ruleName: 'prefer-dry-conditionals', 13 | type: 'maintainability', // one of: 'functionality' | 'maintainability' | 'style' | 'typescript' 14 | description: "Don't-Repeat-Yourself in if statement conditionals, instead use Switch statements.", 15 | options: null, 16 | optionsDescription: '', 17 | optionExamples: [], //Remove this property if the rule has no options 18 | typescriptOnly: false, 19 | issueClass: 'Non-SDL', // one of: 'SDL' | 'Non-SDL' | 'Ignored' 20 | issueType: 'Warning', // one of: 'Error' | 'Warning' 21 | severity: 'Low', // one of: 'Critical' | 'Important' | 'Moderate' | 'Low' 22 | level: 'Opportunity for Excellence', // one of 'Mandatory' | 'Opportunity for Excellence' 23 | group: 'Clarity', // one of 'Ignored' | 'Security' | 'Correctness' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated' 24 | commonWeaknessEnumeration: '', // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) 25 | }; 26 | 27 | public static FAILURE_STRING(switchExpression: string, caseExpressions: string[]): string { 28 | const cases: string[] = caseExpressions.map(text => ` case ${text}:\n // ...\n break;`); 29 | return ( 30 | "Don't Repeat Yourself in If statements. Try using a Switch statement instead:\n" + 31 | ` switch (${switchExpression}) {\n${cases.join('\n')}\n default:\n // ...\n}` 32 | ); 33 | } 34 | 35 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 36 | return this.applyWithWalker(new PreferDryConditionalsRuleWalker(sourceFile, this.getOptions())); 37 | } 38 | } 39 | 40 | class PreferDryConditionalsRuleWalker extends ErrorTolerantWalker { 41 | private threshold: number = 1; 42 | 43 | constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { 44 | super(sourceFile, options); 45 | this.parseOptions(); 46 | } 47 | 48 | protected visitIfStatement(node: ts.IfStatement): void { 49 | this.checkAndReport(node); 50 | super.visitIfStatement(node); 51 | } 52 | 53 | private checkAndReport(node: ts.IfStatement): void { 54 | const { expression, parent } = node; 55 | if (!ts.isIfStatement(parent) && ts.isBinaryExpression(expression)) { 56 | const ifStatements: ts.IfStatement[] = this.allNestedIfStatements(node); 57 | const exceedsThreshold: boolean = ifStatements.length > this.threshold; 58 | if (!exceedsThreshold) { 59 | return; 60 | } 61 | const areAllBinaryExpressions: boolean = ifStatements.every(statement => ts.isBinaryExpression(statement.expression)); 62 | if (areAllBinaryExpressions) { 63 | const binaryExpressions: ts.BinaryExpression[] = ifStatements.map(statement => statement.expression); 64 | this.checkBinaryExpressions(binaryExpressions); 65 | } 66 | } 67 | } 68 | 69 | private allNestedIfStatements(node: ts.IfStatement): ts.IfStatement[] { 70 | const ifStatements: ts.IfStatement[] = [node]; 71 | let curr: ts.IfStatement = node; 72 | while (ts.isIfStatement(curr.elseStatement)) { 73 | ifStatements.push(curr.elseStatement); 74 | curr = curr.elseStatement; 75 | } 76 | return ifStatements; 77 | } 78 | 79 | private checkBinaryExpressions(expressions: ts.BinaryExpression[]): void { 80 | // console.log('expressions', expressions); 81 | if (expressions.length <= 1) { 82 | return; 83 | } 84 | const firstExpression = expressions[0]; 85 | const expectedOperatorToken = firstExpression.operatorToken; 86 | const isEqualityCheck = 87 | expectedOperatorToken.kind === ts.SyntaxKind.EqualsEqualsToken || 88 | expectedOperatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken; 89 | if (!isEqualityCheck) { 90 | return; 91 | } 92 | 93 | const expectedLeft = firstExpression.left; 94 | const expectedRight = firstExpression.right; 95 | 96 | const hasSameOperator = expressions.every(expression => expression.operatorToken.kind === expectedOperatorToken.kind); 97 | if (!hasSameOperator) { 98 | return; 99 | } 100 | 101 | const leftExpressions = expressions.map(expression => expression.left); 102 | const rightExpressions = expressions.map(expression => expression.right); 103 | 104 | const hasSameLeft = leftExpressions.every(expression => expression.getText() === expectedLeft.getText()); 105 | const hasSameRight = rightExpressions.every(expression => expression.getText() === expectedRight.getText()); 106 | if (hasSameLeft) { 107 | this.addFailureAtNode( 108 | firstExpression.parent, 109 | Rule.FAILURE_STRING(expectedLeft.getText(), rightExpressions.map(expression => expression.getText())) 110 | ); 111 | } else if (hasSameRight) { 112 | this.addFailureAtNode( 113 | firstExpression.parent, 114 | Rule.FAILURE_STRING(expectedRight.getText(), leftExpressions.map(expression => expression.getText())) 115 | ); 116 | } 117 | } 118 | 119 | private parseOptions(): void { 120 | this.getOptions().forEach((opt: any) => { 121 | if (typeof opt === 'boolean') { 122 | return; 123 | } 124 | if (typeof opt === 'number') { 125 | this.threshold = Math.max(1, opt); 126 | return; 127 | } 128 | }); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/tests/IdLengthRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { FAILURE_MIN_STRING } from '../idLengthRule'; 3 | 4 | /** 5 | * Unit tests. 6 | */ 7 | describe('idLengthRule', (): void => { 8 | const ruleName: string = 'id-length'; 9 | 10 | context('default options', () => { 11 | it('should pass on identifiers with length 2', (): void => { 12 | const script: string = ` 13 | let x1; 14 | const y1; 15 | function f1() {} 16 | class c1() {} 17 | `; 18 | TestHelper.assertViolations(ruleName, script, []); 19 | }); 20 | 21 | it('should fail on identifiers with length 1', (): void => { 22 | const script: string = ` 23 | let x; 24 | const y; 25 | function f() {} 26 | class c() {} 27 | `; 28 | TestHelper.assertViolations(ruleName, script, [ 29 | { 30 | failure: FAILURE_MIN_STRING + ': x', 31 | name: 'file.ts', 32 | ruleName: 'id-length', 33 | ruleSeverity: 'ERROR', 34 | startPosition: { 35 | character: 21, 36 | line: 2, 37 | }, 38 | }, 39 | { 40 | failure: FAILURE_MIN_STRING + ': y', 41 | name: 'file.ts', 42 | ruleName: 'id-length', 43 | ruleSeverity: 'ERROR', 44 | startPosition: { 45 | character: 23, 46 | line: 3, 47 | }, 48 | }, 49 | { 50 | failure: FAILURE_MIN_STRING + ': f', 51 | name: 'file.ts', 52 | ruleName: 'id-length', 53 | ruleSeverity: 'ERROR', 54 | startPosition: { 55 | character: 26, 56 | line: 4, 57 | }, 58 | }, 59 | { 60 | failure: FAILURE_MIN_STRING + ': c', 61 | name: 'file.ts', 62 | ruleName: 'id-length', 63 | ruleSeverity: 'ERROR', 64 | startPosition: { 65 | character: 23, 66 | line: 5, 67 | }, 68 | }, 69 | ]); 70 | }); 71 | }); 72 | 73 | context('change options', () => { 74 | let options: any[]; 75 | beforeEach((): void => { 76 | options = [true]; 77 | }); 78 | 79 | context('object option', () => { 80 | context('exceptions', () => { 81 | beforeEach((): void => { 82 | options = [ 83 | true, 84 | { 85 | exceptions: ['x', 'y', 'f', 'c'], 86 | }, 87 | ]; 88 | }); 89 | 90 | it('should pass on identifiers of length 1 and are exceptions', (): void => { 91 | const script: string = ` 92 | let x; 93 | const y; 94 | function f() {} 95 | class c() {} 96 | `; 97 | TestHelper.assertViolationsWithOptions(ruleName, options, script, []); 98 | }); 99 | }); 100 | 101 | context('min = 0', () => { 102 | beforeEach((): void => { 103 | options = [ 104 | true, 105 | { 106 | min: 0, 107 | }, 108 | ]; 109 | }); 110 | 111 | it('should pass on identifiers of length 1', (): void => { 112 | const script: string = ` 113 | let x; 114 | const y; 115 | function f() {} 116 | class c() {} 117 | `; 118 | TestHelper.assertViolationsWithOptions(ruleName, options, script, []); 119 | }); 120 | }); 121 | }); 122 | 123 | context('array option (exception)', () => { 124 | beforeEach((): void => { 125 | options = [true, ['x', 'y', 'f', 'c']]; 126 | }); 127 | 128 | it('should pass on identifiers of length 1 and are exceptions', (): void => { 129 | const script: string = ` 130 | let x; 131 | const y; 132 | function f() {} 133 | class c() {} 134 | `; 135 | TestHelper.assertViolationsWithOptions(ruleName, options, script, []); 136 | }); 137 | }); 138 | 139 | context('number option (minimum)', () => { 140 | context('option = 0', () => { 141 | beforeEach((): void => { 142 | options = [true, 0]; 143 | }); 144 | 145 | it('should pass on identifiers of length 1', (): void => { 146 | const script: string = ` 147 | let x; 148 | const y; 149 | function f() {} 150 | class c() {} 151 | `; 152 | TestHelper.assertViolationsWithOptions(ruleName, options, script, []); 153 | }); 154 | }); 155 | }); 156 | }); 157 | }); 158 | -------------------------------------------------------------------------------- /src/tests/MaxFuncArgsRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { FAILURE_STRING, FAILURE_RECOMMENDATION_STRING, DEFAULT_MAX_ARGS_LENGTH } from '../maxFuncArgsRule'; 3 | 4 | /** 5 | * Unit tests. 6 | */ 7 | describe('maxFuncArgsRule', (): void => { 8 | const ruleName: string = 'max-func-args'; 9 | 10 | context('Anonymous Function', () => { 11 | const maxLength = DEFAULT_MAX_ARGS_LENGTH; 12 | 13 | it('should pass on function with 0 arguments', (): void => { 14 | const script: string = ` 15 | function () { 16 | } 17 | `; 18 | TestHelper.assertViolations(ruleName, script, []); 19 | }); 20 | 21 | it('should fail on function with 5 arguments', (): void => { 22 | const script: string = ` 23 | function (a1, a2, a3, a4, a5) { 24 | } 25 | `; 26 | TestHelper.assertViolations(ruleName, script, [ 27 | { 28 | failure: FAILURE_STRING + maxLength + FAILURE_RECOMMENDATION_STRING, 29 | name: 'file.ts', 30 | ruleName: 'max-func-args', 31 | ruleSeverity: 'ERROR', 32 | startPosition: { 33 | character: 31, 34 | line: 2, 35 | }, 36 | }, 37 | ]); 38 | }); 39 | }); 40 | 41 | context('Arrow Function', () => { 42 | const maxLength = DEFAULT_MAX_ARGS_LENGTH; 43 | 44 | it('should pass on function with 0 arguments', (): void => { 45 | const script: string = ` 46 | () => { 47 | } 48 | `; 49 | TestHelper.assertViolations(ruleName, script, []); 50 | }); 51 | 52 | it('should fail on function with 5 arguments', (): void => { 53 | const script: string = ` 54 | (a1, a2, a3, a4, a5) => { 55 | } 56 | `; 57 | TestHelper.assertViolations(ruleName, script, [ 58 | { 59 | failure: FAILURE_STRING + maxLength + FAILURE_RECOMMENDATION_STRING, 60 | name: 'file.ts', 61 | ruleName: 'max-func-args', 62 | ruleSeverity: 'ERROR', 63 | startPosition: { 64 | character: 30, 65 | line: 2, 66 | }, 67 | }, 68 | ]); 69 | }); 70 | }); 71 | 72 | context('Class method', () => { 73 | const maxLength = DEFAULT_MAX_ARGS_LENGTH; 74 | 75 | it('should pass on function with 0 arguments', (): void => { 76 | const script: string = ` 77 | class MyClass { 78 | private myFunc(): string { 79 | // ... 80 | } 81 | } 82 | `; 83 | TestHelper.assertViolations(ruleName, script, []); 84 | }); 85 | 86 | it('should fail on function with 5 arguments', (): void => { 87 | const script: string = ` 88 | class MyClass { 89 | private myFunc( 90 | arg1: Date | string | void, arg2: boolean, 91 | arg3?: Date | string | void, arg4?: boolean, 92 | arg5: string = "en" 93 | ): string { 94 | // ... 95 | } 96 | } 97 | `; 98 | TestHelper.assertViolations(ruleName, script, [ 99 | { 100 | failure: FAILURE_STRING + maxLength + FAILURE_RECOMMENDATION_STRING, 101 | name: 'file.ts', 102 | ruleName: 'max-func-args', 103 | ruleSeverity: 'ERROR', 104 | startPosition: { 105 | character: 32, 106 | line: 3, 107 | }, 108 | }, 109 | ]); 110 | }); 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /src/tests/NoComplexConditionalsRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { Rule } from '../noComplexConditionalsRule'; 3 | const { FAILURE_STRING } = Rule; 4 | 5 | /** 6 | * Unit tests. 7 | */ 8 | describe('noComplexConditionalsRule', (): void => { 9 | const ruleName: string = 'no-complex-conditionals'; 10 | 11 | it('should pass on single variable as conditional expression', (): void => { 12 | const script: string = ` 13 | const shouldDoStuff = (((status === 1 || status === 2 || status === 3) || isSkyBlue) && isRightDay); 14 | if (shouldDoStuff) { 15 | doStuff(); 16 | } 17 | `; 18 | 19 | TestHelper.assertViolations(ruleName, script, []); 20 | }); 21 | 22 | it('should pass on function call as conditional expression', (): void => { 23 | const script: string = ` 24 | class StuffDoer { 25 | doTheThing() { 26 | if (this.shouldDoStuff()) { 27 | doStuff(); 28 | } 29 | } 30 | shouldDoStuff() { 31 | return (((status === 1 || status === 2 || status === 3) || isSkyBlue) && isRightDay); 32 | } 33 | } 34 | `; 35 | 36 | TestHelper.assertViolations(ruleName, script, []); 37 | }); 38 | 39 | it('should fail on complex conditional expression in if statement', (): void => { 40 | const script: string = ` 41 | // Test 42 | if (((status === 1 || status === 2 || status === 3) || isSkyBlue) && isRightDay) { 43 | doStuff(); 44 | } 45 | `; 46 | 47 | TestHelper.assertViolations(ruleName, script, [ 48 | { 49 | failure: FAILURE_STRING, 50 | name: 'file.ts', 51 | ruleName: ruleName, 52 | ruleSeverity: 'ERROR', 53 | startPosition: { 54 | character: 13, 55 | line: 3, 56 | }, 57 | }, 58 | ]); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/tests/NoFlagArgsRuleTests.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable no-multiline-string 2 | import { TestHelper } from './TestHelper'; 3 | import { FAILURE_STRING } from '../noFlagArgsRule'; 4 | 5 | /** 6 | * Unit tests. 7 | */ 8 | describe('noFlagArgsRule', (): void => { 9 | const ruleName: string = 'no-flag-args'; 10 | 11 | context('Anonymous Function', () => { 12 | it('should pass on string parameter', (): void => { 13 | const script: string = ` 14 | function (arg1: string) { 15 | } 16 | `; 17 | TestHelper.assertViolations(ruleName, script, []); 18 | }); 19 | 20 | it('should fail on boolean parameter', (): void => { 21 | const script: string = ` 22 | function (arg1: boolean) { 23 | } 24 | `; 25 | TestHelper.assertViolations(ruleName, script, [ 26 | { 27 | failure: FAILURE_STRING + 'arg1' + '\nSplit the function into two.', 28 | name: 'file.ts', 29 | ruleName: 'no-flag-args', 30 | ruleSeverity: 'ERROR', 31 | startPosition: { 32 | character: 23, 33 | line: 2, 34 | }, 35 | }, 36 | ]); 37 | }); 38 | }); 39 | 40 | context('Anonymous Function', () => { 41 | it('should pass on string parameter', (): void => { 42 | const script: string = ` 43 | function (arg1: string) { 44 | } 45 | `; 46 | TestHelper.assertViolations(ruleName, script, []); 47 | }); 48 | 49 | it('should fail on boolean parameter', (): void => { 50 | const script: string = ` 51 | function (arg1: boolean) { 52 | } 53 | `; 54 | TestHelper.assertViolations(ruleName, script, [ 55 | { 56 | failure: FAILURE_STRING + 'arg1' + '\nSplit the function into two.', 57 | name: 'file.ts', 58 | ruleName: 'no-flag-args', 59 | ruleSeverity: 'ERROR', 60 | startPosition: { 61 | character: 23, 62 | line: 2, 63 | }, 64 | }, 65 | ]); 66 | }); 67 | }); 68 | 69 | context('Named Function', () => { 70 | it('should pass on string parameter', (): void => { 71 | const script: string = ` 72 | function doStuff(stuff: string) { 73 | } 74 | `; 75 | TestHelper.assertViolations(ruleName, script, []); 76 | }); 77 | 78 | it('should fail on boolean parameter', (): void => { 79 | const script: string = ` 80 | function doStuff(shouldDoStuff: boolean) { 81 | } 82 | `; 83 | TestHelper.assertViolations(ruleName, script, [ 84 | { 85 | failure: 86 | FAILURE_STRING + 87 | 'shouldDoStuff' + 88 | '\nSplit the function into two, such as doStuffWhenShouldDoStuff and doStuffWhenNotShouldDoStuff.', 89 | name: 'file.ts', 90 | ruleName: 'no-flag-args', 91 | ruleSeverity: 'ERROR', 92 | startPosition: { 93 | character: 30, 94 | line: 2, 95 | }, 96 | }, 97 | ]); 98 | }); 99 | }); 100 | 101 | context('Arrow Function', () => { 102 | it('should pass on string parameter', (): void => { 103 | const script: string = ` 104 | (arg1: string) => { 105 | } 106 | `; 107 | TestHelper.assertViolations(ruleName, script, []); 108 | }); 109 | 110 | it('should fail on boolean parameter', (): void => { 111 | const script: string = ` 112 | (arg1: boolean) => { 113 | } 114 | `; 115 | TestHelper.assertViolations(ruleName, script, [ 116 | { 117 | failure: FAILURE_STRING + 'arg1' + '\nSplit the function into two.', 118 | name: 'file.ts', 119 | ruleName: 'no-flag-args', 120 | ruleSeverity: 'ERROR', 121 | startPosition: { 122 | character: 22, 123 | line: 2, 124 | }, 125 | }, 126 | ]); 127 | }); 128 | }); 129 | 130 | context('Class Methods', () => { 131 | it('should pass on setter method', (): void => { 132 | const script: string = ` 133 | class MyClass { 134 | private value: boolean; 135 | public set myValue(newValue: boolean) { 136 | this.value = newValue; 137 | } 138 | } 139 | `; 140 | TestHelper.assertViolations(ruleName, script, []); 141 | }); 142 | 143 | it('should pass on getter method', (): void => { 144 | const script: string = ` 145 | class MyClass { 146 | private value: boolean; 147 | public get myValue() { 148 | return this.value; 149 | } 150 | } 151 | `; 152 | TestHelper.assertViolations(ruleName, script, []); 153 | }); 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /src/tests/NoForEachPushRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { Rule } from '../noForEachPushRule'; 3 | const { FAILURE_STRING } = Rule; 4 | 5 | /** 6 | * Unit tests. 7 | */ 8 | describe('noForEachPushRule', (): void => { 9 | const ruleName: string = 'no-for-each-push'; 10 | 11 | it('should pass using Array.prototype.map', (): void => { 12 | const script: string = ` 13 | const arr1 = [1,2,3]; 14 | const results = arr1.map(item => item * 2); 15 | `; 16 | TestHelper.assertViolations(ruleName, script, []); 17 | }); 18 | 19 | it('should fail using Array.prototype.push nested within Array.prototype.forEach', (): void => { 20 | const script: string = ` 21 | const arr1 = [1,2,3]; 22 | const results = []; 23 | arr1.forEach(item => { 24 | const newValue = item * 2; 25 | results.push(newValue); 26 | }); 27 | `; 28 | TestHelper.assertViolations(ruleName, script, [ 29 | { 30 | failure: FAILURE_STRING, 31 | name: 'file.ts', 32 | ruleName: ruleName, 33 | ruleSeverity: 'ERROR', 34 | startPosition: { 35 | character: 13, 36 | line: 4, 37 | }, 38 | }, 39 | ]); 40 | }); 41 | 42 | it('should pass using Array.prototype.push nested within Array.prototype.forEach with an If Statement', (): void => { 43 | const script: string = ` 44 | const arr1 = [1,2,3]; 45 | const results = []; 46 | arr1.forEach(item => { 47 | if (item === 2) { 48 | return; 49 | } 50 | const newValue = item * 2; 51 | results.push(newValue); 52 | }); 53 | `; 54 | TestHelper.assertViolations(ruleName, script, []); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/tests/NoMapWithoutUsageRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { Rule } from '../noMapWithoutUsageRule'; 3 | const { FAILURE_STRING } = Rule; 4 | 5 | /** 6 | * Unit tests. 7 | */ 8 | describe('noMapWithoutUsageRule', (): void => { 9 | const ruleName: string = 'no-map-without-usage'; 10 | 11 | it('should pass on Array.forEach', (): void => { 12 | const script: string = ` 13 | arr.forEach(item => doStuff(item)); 14 | `; 15 | 16 | TestHelper.assertViolations(ruleName, script, []); 17 | }); 18 | 19 | it('should pass on Array.prototype.map with assignment', (): void => { 20 | const script: string = ` 21 | const results = arr.map(item => doStuff(item)); 22 | `; 23 | 24 | TestHelper.assertViolations(ruleName, script, []); 25 | }); 26 | 27 | it('should pass on Array.prototype.map with assignment to an existing variable', (): void => { 28 | const script: string = ` 29 | let results; 30 | results = arr.map(item => doStuff(item)); 31 | `; 32 | 33 | TestHelper.assertViolations(ruleName, script, []); 34 | }); 35 | 36 | it('should pass on Array.prototype.map with assignment to object property', (): void => { 37 | const script: string = ` 38 | const obj = { 39 | key: arr.map(item => doStuff(item)) 40 | }; 41 | `; 42 | 43 | TestHelper.assertViolations(ruleName, script, []); 44 | }); 45 | 46 | it('should pass on Array.prototype.map with additional access on result', (): void => { 47 | const script: string = ` 48 | arr.map(item => doStuff(item)) 49 | .forEach(); 50 | `; 51 | 52 | TestHelper.assertViolations(ruleName, script, []); 53 | }); 54 | 55 | it('should pass on Array.prototype.map with results returned', (): void => { 56 | const script: string = ` 57 | function () { 58 | return arr.map(item => doStuff(item)); 59 | } 60 | `; 61 | 62 | TestHelper.assertViolations(ruleName, script, []); 63 | }); 64 | 65 | it('should pass on Array.prototype.map with results used in parent call', (): void => { 66 | const script: string = ` 67 | doStuff(arr.map(item => doStuff(item))) 68 | `; 69 | 70 | TestHelper.assertViolations(ruleName, script, []); 71 | }); 72 | 73 | it('should pass on Array.prototype.map with spread', (): void => { 74 | const script: string = ` 75 | doOtherStuff(...arr.map(item => doStuff(item))) 76 | `; 77 | 78 | TestHelper.assertViolations(ruleName, script, []); 79 | }); 80 | 81 | it('should pass on Array.prototype.map within true case of ternary/conditional expression', (): void => { 82 | const script: string = ` 83 | b ? arr.map(curr => curr * 2) : []; 84 | `; 85 | 86 | TestHelper.assertViolations(ruleName, script, []); 87 | }); 88 | 89 | it('should pass on Array.prototype.map within false case of ternary/conditional expression', (): void => { 90 | const script: string = ` 91 | b ? [] : arr.map(curr => curr * 2); 92 | `; 93 | 94 | TestHelper.assertViolations(ruleName, script, []); 95 | }); 96 | 97 | it('should pass on Array.prototype.map as array value', (): void => { 98 | const script: string = ` 99 | const arr2 = [1, arr.map(curr => curr * 2), 3]; 100 | `; 101 | 102 | TestHelper.assertViolations(ruleName, script, []); 103 | }); 104 | 105 | it('should pass on Array.prototype.map within Binary Expression after &&', (): void => { 106 | const script: string = ` 107 | arr && arr.map(curr => curr * 2); 108 | `; 109 | 110 | TestHelper.assertViolations(ruleName, script, []); 111 | }); 112 | 113 | it('should pass on Array.prototype.map within Binary Expression after ||', (): void => { 114 | const script: string = ` 115 | b && firstThing() || arr.map(curr => curr * 2); 116 | `; 117 | 118 | TestHelper.assertViolations(ruleName, script, []); 119 | }); 120 | 121 | it('should pass on Array.prototype.map within arrow function return shorthand', (): void => { 122 | const script: string = ` 123 | this.getFiles() 124 | .then(files => files.map(file => path.join(this.basePath, file))) 125 | `; 126 | 127 | TestHelper.assertViolations(ruleName, script, []); 128 | }); 129 | 130 | it('should pass on Array.prototype.map within arrow function return shorthand inside a constructor', (): void => { 131 | const script: string = ` 132 | new Map( 133 | [{ id: 0, name: 'John' }] 134 | .map<[number, string]>(entity => [entity.id, entity.name]) 135 | ); 136 | `; 137 | 138 | TestHelper.assertViolations(ruleName, script, []); 139 | }); 140 | 141 | it('should pass on Array.prototype.map with assignment to a property initializer', (): void => { 142 | const script: string = ` 143 | class Example { 144 | items = [1, 2].map(item => doStuff(item)); 145 | }`; 146 | 147 | TestHelper.assertViolations(ruleName, script, []); 148 | }); 149 | 150 | it('should fail on Array.prototype.map within arrow function block without return statement', (): void => { 151 | const script: string = ` 152 | this.getFiles() 153 | .then(files => { 154 | files.map(file => path.join(this.basePath, file)); 155 | }) 156 | `; 157 | 158 | TestHelper.assertViolations(ruleName, script, [ 159 | { 160 | failure: FAILURE_STRING, 161 | name: 'file.ts', 162 | ruleName: ruleName, 163 | ruleSeverity: 'ERROR', 164 | startPosition: { 165 | character: 17, 166 | line: 4, 167 | }, 168 | }, 169 | ]); 170 | }); 171 | 172 | it('should fail on Array.prototype.map without assignment', (): void => { 173 | const script: string = ` 174 | arr.map(item => doStuff(item)); 175 | `; 176 | 177 | TestHelper.assertViolations(ruleName, script, [ 178 | { 179 | failure: FAILURE_STRING, 180 | name: 'file.ts', 181 | ruleName: ruleName, 182 | ruleSeverity: 'ERROR', 183 | startPosition: { 184 | character: 9, 185 | line: 2, 186 | }, 187 | }, 188 | ]); 189 | }); 190 | 191 | context('React', () => { 192 | it('should pass on JavaScript Expression within TSX', () => { 193 | const file: string = 'test-data/NoMapWithoutUsage/JavaScriptExpressionInReact.tsx'; 194 | TestHelper.assertViolations(ruleName, file, []); 195 | }); 196 | }); 197 | }); 198 | -------------------------------------------------------------------------------- /src/tests/PreferDryConditionalsRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { Rule } from '../preferDryConditionalsRule'; 3 | const { FAILURE_STRING } = Rule; 4 | 5 | /** 6 | * Unit tests. 7 | */ 8 | describe('preferDryConditionalsRule', (): void => { 9 | const ruleName: string = 'prefer-dry-conditionals'; 10 | 11 | it('should pass on switch statement', (): void => { 12 | const script: string = ` 13 | switch (obj.name) { 14 | case "Stuff": 15 | doStuff(); 16 | break; 17 | case "Other": 18 | doOtherStuff(); 19 | break; 20 | default: 21 | doFallback(); 22 | } 23 | `; 24 | 25 | TestHelper.assertViolations(ruleName, script, []); 26 | }); 27 | 28 | it('should pass on if statements with different operator', (): void => { 29 | const script: string = ` 30 | if (obj.name === "Stuff") { 31 | doStuff(); 32 | } else if (obj.name !== "Other") { 33 | doOtherStuff(); 34 | } else { 35 | doFallback(); 36 | } 37 | `; 38 | 39 | TestHelper.assertViolations(ruleName, script, []); 40 | }); 41 | 42 | it('should pass on single if statement', (): void => { 43 | const script: string = ` 44 | if (obj.name === "Stuff") { 45 | doStuff(); 46 | } else { 47 | doFallback(); 48 | } 49 | `; 50 | 51 | TestHelper.assertViolations(ruleName, script, []); 52 | }); 53 | 54 | it('should pass on if statement which is not equality', (): void => { 55 | const script: string = ` 56 | if (obj.name === 1) { 57 | doStuff(); 58 | } else if (obj.name >= 4) { 59 | doOtherStuff(); 60 | } else { 61 | doFallback(); 62 | } 63 | `; 64 | 65 | TestHelper.assertViolations(ruleName, script, []); 66 | }); 67 | 68 | it('should fail on multiple if statements with same left side', (): void => { 69 | const script: string = ` 70 | if (obj.name === "Stuff") { 71 | doStuff(); 72 | } else if (obj.name === "Other") { 73 | doOtherStuff(); 74 | } else { 75 | doFallback(); 76 | } 77 | `; 78 | 79 | TestHelper.assertViolations(ruleName, script, [ 80 | { 81 | failure: FAILURE_STRING('obj.name', ['"Stuff"', '"Other"']), 82 | name: 'file.ts', 83 | ruleName: ruleName, 84 | ruleSeverity: 'ERROR', 85 | startPosition: { 86 | character: 9, 87 | line: 2, 88 | }, 89 | }, 90 | ]); 91 | }); 92 | 93 | it('should fail on multiple if statements with same right side', (): void => { 94 | const script: string = ` 95 | if ("Stuff" === obj.name) { 96 | doStuff(); 97 | } else if ("Other" === obj.name) { 98 | doOtherStuff(); 99 | } else { 100 | doFallback(); 101 | } 102 | `; 103 | 104 | TestHelper.assertViolations(ruleName, script, [ 105 | { 106 | failure: FAILURE_STRING('obj.name', ['"Stuff"', '"Other"']), 107 | name: 'file.ts', 108 | ruleName: ruleName, 109 | ruleSeverity: 'ERROR', 110 | startPosition: { 111 | character: 9, 112 | line: 2, 113 | }, 114 | }, 115 | ]); 116 | }); 117 | 118 | it('should fail on multiple if statements with same string on right side', (): void => { 119 | const script: string = ` 120 | if (obj.a === "Stuff") { 121 | doStuff(); 122 | } else if (obj.b === "Stuff") { 123 | doOtherStuff(); 124 | } else { 125 | doFallback(); 126 | } 127 | `; 128 | 129 | TestHelper.assertViolations(ruleName, script, [ 130 | { 131 | failure: FAILURE_STRING('"Stuff"', ['obj.a', 'obj.b']), 132 | name: 'file.ts', 133 | ruleName: ruleName, 134 | ruleSeverity: 'ERROR', 135 | startPosition: { 136 | character: 11, 137 | line: 2, 138 | }, 139 | }, 140 | ]); 141 | }); 142 | 143 | context('options', () => { 144 | let options: any[] = [true]; 145 | 146 | context('threshold = 3', () => { 147 | beforeEach(() => { 148 | options = [true, 3]; 149 | }); 150 | 151 | it('should pass on 2 if statements with same string on right side', (): void => { 152 | const script: string = ` 153 | if (obj.a === "Stuff") { 154 | doStuff(); 155 | } else if (obj.b === "Stuff") { 156 | doOtherStuff(); 157 | } else { 158 | doFallback(); 159 | } 160 | `; 161 | 162 | TestHelper.assertViolationsWithOptions(ruleName, options, script, []); 163 | }); 164 | 165 | it('should fail on 3 if statements with same right side', (): void => { 166 | const script: string = ` 167 | if ("Stuff" === obj.name) { 168 | doStuff(); 169 | } else if ("Other" === obj.name) { 170 | doOtherStuff(); 171 | } else if ("Another" === obj.name) { 172 | doOtherStuff(); 173 | } else { 174 | doFallback(); 175 | } 176 | `; 177 | 178 | TestHelper.assertViolations(ruleName, script, [ 179 | { 180 | failure: FAILURE_STRING('obj.name', ['"Stuff"', '"Other"', '"Another"']), 181 | name: 'file.ts', 182 | ruleName: ruleName, 183 | ruleSeverity: 'ERROR', 184 | startPosition: { 185 | character: 19, 186 | line: 2, 187 | }, 188 | }, 189 | ]); 190 | }); 191 | }); 192 | }); 193 | }); 194 | -------------------------------------------------------------------------------- /src/tests/TryCatchFirstRuleTests.ts: -------------------------------------------------------------------------------- 1 | import { TestHelper } from './TestHelper'; 2 | import { FAILURE_STRING } from '../tryCatchFirstRule'; 3 | 4 | /** 5 | * Unit tests. 6 | */ 7 | describe('tryCatchFirstRule', (): void => { 8 | const ruleName: string = 'try-catch-first'; 9 | 10 | it('should pass on with try-catch being top-level of SourceFile', (): void => { 11 | const script: string = ` 12 | try { 13 | } catch (error) { 14 | } 15 | `; 16 | TestHelper.assertViolations(ruleName, script, []); 17 | }); 18 | 19 | it('should pass on with try-catch being top-level of FunctionDeclaration', (): void => { 20 | const script: string = ` 21 | function () { 22 | try { 23 | } catch (error) { 24 | } 25 | } 26 | `; 27 | TestHelper.assertViolations(ruleName, script, []); 28 | }); 29 | 30 | it('should pass on with try-catch being top-level of ArrowFunction', (): void => { 31 | const script: string = ` 32 | () => { 33 | try { 34 | } catch (error) { 35 | } 36 | } 37 | `; 38 | TestHelper.assertViolations(ruleName, script, []); 39 | }); 40 | 41 | it('should pass on with try-catch being top-level of MethodDeclaration', (): void => { 42 | const script: string = ` 43 | class MyClass { 44 | doStuff() { 45 | try { 46 | } catch (error) { 47 | } 48 | } 49 | } 50 | `; 51 | TestHelper.assertViolations(ruleName, script, []); 52 | }); 53 | 54 | it('should pass on with try-catch being top-level of GetAccessor', (): void => { 55 | const script: string = ` 56 | class MyClass { 57 | get stuff() { 58 | try { 59 | } catch (error) { 60 | } 61 | } 62 | } 63 | `; 64 | TestHelper.assertViolations(ruleName, script, []); 65 | }); 66 | 67 | it('should pass on with try-catch being top-level of SetAccessor', (): void => { 68 | const script: string = ` 69 | class MyClass { 70 | set stuff() { 71 | try { 72 | } catch (error) { 73 | } 74 | } 75 | } 76 | `; 77 | TestHelper.assertViolations(ruleName, script, []); 78 | }); 79 | 80 | it('should fail on with try-catch not being top-level of FunctionDeclaration', (): void => { 81 | const script: string = ` 82 | function () { 83 | if (true) { 84 | try { 85 | } catch (error) { 86 | } 87 | } 88 | } 89 | `; 90 | TestHelper.assertViolations(ruleName, script, [ 91 | { 92 | failure: FAILURE_STRING, 93 | name: 'file.ts', 94 | ruleName: 'try-catch-first', 95 | ruleSeverity: 'ERROR', 96 | startPosition: { 97 | character: 21, 98 | line: 4, 99 | }, 100 | }, 101 | ]); 102 | }); 103 | 104 | it('should fail on with try-catch not being top-level of named FunctionDeclaration', (): void => { 105 | const script: string = ` 106 | function funName() { 107 | if (true) { 108 | try { 109 | } catch (error) { 110 | } 111 | } 112 | } 113 | `; 114 | TestHelper.assertViolations(ruleName, script, [ 115 | { 116 | failure: FAILURE_STRING, 117 | name: 'file.ts', 118 | ruleName: 'try-catch-first', 119 | ruleSeverity: 'ERROR', 120 | startPosition: { 121 | character: 21, 122 | line: 4, 123 | }, 124 | }, 125 | ]); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /src/tests/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "../../tslint.json" 4 | ], 5 | "rules": { 6 | "no-multiline-string": false, 7 | "quotemark": false, 8 | "object-literal-key-quotes": false, 9 | "max-func-body-length": false 10 | } 11 | } -------------------------------------------------------------------------------- /src/tryCatchFirstRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; 5 | import { ExtendedMetadata } from './utils/ExtendedMetadata'; 6 | 7 | export const FAILURE_STRING: string = 'Try-catch blocks must be at the top of the scope'; 8 | 9 | /** 10 | * Implementation of the newspaper-order rule. 11 | */ 12 | export class Rule extends Lint.Rules.AbstractRule { 13 | public static metadata: ExtendedMetadata = { 14 | ruleName: 'try-catch-first', 15 | type: 'maintainability', 16 | description: 17 | 'Try-catch blocks must be first within the scope. ' + 18 | 'Try-catch blocks are transactions and should leave your program in a consistent state, no matter what happens in the try.', 19 | options: null, 20 | optionsDescription: '', 21 | typescriptOnly: true, 22 | issueClass: 'Non-SDL', 23 | issueType: 'Warning', 24 | severity: 'Important', 25 | level: 'Opportunity for Excellence', 26 | group: 'Correctness', 27 | recommendation: 'true,', 28 | commonWeaknessEnumeration: '', 29 | }; 30 | 31 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 32 | return this.applyWithWalker(new TryCatchFirstRuleWalker(sourceFile, this.getOptions())); 33 | } 34 | } 35 | 36 | class TryCatchFirstRuleWalker extends ErrorTolerantWalker { 37 | private scopeKinds: ts.SyntaxKind[] = [ 38 | ts.SyntaxKind.FunctionDeclaration, 39 | ts.SyntaxKind.ArrowFunction, 40 | ts.SyntaxKind.SourceFile, 41 | ts.SyntaxKind.MethodDeclaration, 42 | ts.SyntaxKind.GetAccessor, 43 | ts.SyntaxKind.SetAccessor, 44 | ]; 45 | 46 | protected visitTryStatement(node: ts.TryStatement): void { 47 | this.checkAndReport(node); 48 | super.visitTryStatement(node); 49 | } 50 | 51 | private checkAndReport(node: ts.TryStatement) { 52 | const block = node.parent; 53 | const { parent } = block; 54 | // console.log('checkAndReport', block, parent); // tslint:disable-line no-console 55 | const isFirst = this.scopeKinds.indexOf((parent || block).kind) !== -1; 56 | if (!isFirst) { 57 | const failureMessage = this.makeFailureMessage(); 58 | this.addFailureAt(node.getStart(), node.getWidth(), failureMessage); 59 | } 60 | } 61 | 62 | private makeFailureMessage(): string { 63 | return FAILURE_STRING; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/utils/BannedTermWalker.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { ErrorTolerantWalker } from './ErrorTolerantWalker'; 4 | 5 | /** 6 | * Implementation of the banned-term rulesets. 7 | */ 8 | export class BannedTermWalker extends ErrorTolerantWalker { 9 | private failureString: string; 10 | private bannedTerms: string[]; 11 | private allowQuotedProperties: boolean = false; 12 | 13 | constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, failureString: string, bannedTerms: string[]) { 14 | super(sourceFile, options); 15 | this.failureString = failureString; 16 | this.bannedTerms = bannedTerms; 17 | this.getOptions().forEach((opt: any) => { 18 | if (typeof opt === 'object') { 19 | this.allowQuotedProperties = opt['allow-quoted-properties'] === true; 20 | } 21 | }); 22 | } 23 | 24 | protected visitFunctionDeclaration(node: ts.FunctionDeclaration): void { 25 | this.validateNode(node); 26 | super.visitFunctionDeclaration(node); 27 | } 28 | 29 | protected visitGetAccessor(node: ts.AccessorDeclaration): void { 30 | this.validateNode(node); 31 | super.visitGetAccessor(node); 32 | } 33 | 34 | protected visitMethodDeclaration(node: ts.MethodDeclaration): void { 35 | this.validateNode(node); 36 | super.visitMethodDeclaration(node); 37 | } 38 | 39 | protected visitParameterDeclaration(node: ts.ParameterDeclaration): void { 40 | // typescript 2.0 introduces function level 'this' types 41 | if (node.name.getText() !== 'this') { 42 | this.validateNode(node); 43 | } 44 | super.visitParameterDeclaration(node); 45 | } 46 | 47 | protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void { 48 | this.validateNode(node); 49 | super.visitPropertyDeclaration(node); 50 | } 51 | 52 | protected visitPropertySignature(node: ts.Node): void { 53 | if (node.kind === ts.SyntaxKind.PropertySignature) { 54 | const signature: ts.PropertySignature = node; 55 | const propertyName = signature.name; 56 | // ignore StringLiteral property names if that option is set 57 | if (this.allowQuotedProperties === false || propertyName.kind !== ts.SyntaxKind.StringLiteral) { 58 | this.validateNode(node); 59 | } 60 | } else { 61 | this.validateNode(node); 62 | } 63 | super.visitPropertySignature(node); 64 | } 65 | 66 | protected visitSetAccessor(node: ts.AccessorDeclaration): void { 67 | this.validateNode(node); 68 | super.visitSetAccessor(node); 69 | } 70 | 71 | protected visitVariableDeclaration(node: ts.VariableDeclaration): void { 72 | this.validateNode(node); 73 | super.visitVariableDeclaration(node); 74 | } 75 | 76 | private validateNode(node: ts.Node): void { 77 | if ((node).name) { 78 | if ((node).name.text) { 79 | const text: string = (node).name.text; 80 | if (this.isBannedTerm(text)) { 81 | this.addFailureAt(node.getStart(), node.getWidth(), this.failureString + text); 82 | } 83 | } 84 | } 85 | } 86 | 87 | private isBannedTerm(text: string): boolean { 88 | return this.bannedTerms.indexOf(text) !== -1; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/utils/BaseFormatter.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as fs from 'fs'; 4 | import { Formatters } from 'tslint'; 5 | import { RuleFailure } from 'tslint'; 6 | 7 | /** 8 | * A base class for formatters that fix linting issues. 9 | */ 10 | export class BaseFormatter extends Formatters.AbstractFormatter { 11 | private ruleName: string; 12 | private applyFix: (this: BaseFormatter, failure: RuleFailure) => void; 13 | 14 | constructor(ruleName: string, applyFix: (this: BaseFormatter, failure: RuleFailure) => void) { 15 | super(); 16 | this.ruleName = ruleName; 17 | this.applyFix = applyFix.bind(this); 18 | } 19 | 20 | public format(allFailures: RuleFailure[]): string { 21 | /* tslint:disable:no-increment-decrement */ 22 | for (let index = allFailures.length - 1; index >= 0; index--) { 23 | /* tslint:enable:no-increment-decrement */ 24 | const failure = allFailures[index]; 25 | if (failure.getRuleName() === this.ruleName) { 26 | this.applyFix(failure); 27 | } 28 | } 29 | const outputLines = allFailures.map(this.formatFailure); 30 | return outputLines.join('\n') + '\n'; 31 | } 32 | 33 | private formatFailure(failure: RuleFailure): string { 34 | const fileName: string = failure.getFileName(); 35 | const failureString: string = failure.getFailure(); 36 | const ruleName: string = failure.getRuleName(); 37 | const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); 38 | const positionTuple = '[' + (lineAndCharacter.line + 1) + ', ' + (lineAndCharacter.character + 1) + ']'; 39 | return '(' + ruleName + ') ' + fileName + positionTuple + ': ' + failureString; 40 | } 41 | 42 | protected readFile(fileName: string): string { 43 | return fs.readFileSync(fileName, { encoding: 'UTF-8' }); 44 | } 45 | 46 | protected writeFile(fileName: string, fileContents: string): void { 47 | fs.writeFileSync(fileName, fileContents, { encoding: 'UTF-8' }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/ChaiUtils.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | /** 4 | * Utility methods for the chai.related rules. 5 | */ 6 | export namespace ChaiUtils { 7 | export function isExpectInvocation(node: ts.PropertyAccessExpression | ts.CallExpression): boolean { 8 | const callExpression: ts.CallExpression = getLeftMostCallExpression(node); 9 | if (callExpression == null) { 10 | return false; 11 | } 12 | return /.*\.?expect/.test(callExpression.expression.getText()); 13 | } 14 | 15 | export function getLeftMostCallExpression(node: ts.PropertyAccessExpression | ts.CallExpression): ts.CallExpression { 16 | let leftSide: ts.Node = node.expression; 17 | while (leftSide != null) { 18 | if (leftSide.kind === ts.SyntaxKind.CallExpression) { 19 | return leftSide; 20 | } else if (leftSide.kind === ts.SyntaxKind.PropertyAccessExpression) { 21 | leftSide = (leftSide).expression; 22 | } else { 23 | return null; // cannot walk any further left in the property expression 24 | } 25 | } 26 | return null; 27 | } 28 | 29 | export function getFirstExpectCallParameter(node: ts.CallExpression): ts.Node { 30 | const expectCall: ts.CallExpression = ChaiUtils.getLeftMostCallExpression(node); 31 | if (expectCall.arguments.length > 0) { 32 | return expectCall.arguments[0]; 33 | } 34 | return null; 35 | } 36 | 37 | export function getFirstExpectationParameter(node: ts.CallExpression): ts.Node { 38 | if (node.arguments.length > 0) { 39 | return node.arguments[0]; 40 | } 41 | return null; 42 | } 43 | 44 | export function isEqualsInvocation(propExpression: ts.PropertyAccessExpression): boolean { 45 | return /equal|equals|eq|eql|eqs/.test(propExpression.name.getText()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/DirectedAcyclicGraph.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable no-increment-decrement 2 | /** 3 | * TypeScript program to print all topological sorts of a graph 4 | * Original C++ source code from http://www.geeksforgeeks.org/all-topological-sorts-of-a-directed-acyclic-graph/ 5 | */ 6 | export class DirectedAcyclicGraph { 7 | private numVertices: number; // No. of vertices 8 | 9 | constructor(numVertices: number) { 10 | this.numVertices = numVertices; 11 | 12 | // Initialising all indegree with 0 13 | for (let curr: number = 0; curr < numVertices; curr = curr + 1) { 14 | this.inDegree.push(0); 15 | this.adj.push([]); 16 | } 17 | } 18 | 19 | // Pointer to an array containing adjacency list 20 | private adj: number[][] = []; 21 | 22 | // Vector to store indegree of vertices 23 | private inDegree: number[] = []; 24 | 25 | // The function does all Topological Sort. 26 | // It uses recursive alltopologicalSortUtil() 27 | public alltopologicalSort(): number[][] { 28 | const { numVertices } = this; 29 | // Mark all the vertices as not visited 30 | const visited: boolean[] = []; 31 | for (let curr: number = 0; curr < numVertices; curr = curr + 1) { 32 | visited.push(false); 33 | } 34 | const res: number[] = []; 35 | return this.alltopologicalSortUtil(res, visited); 36 | } 37 | 38 | // A function used by alltopologicalSort 39 | // Prints all Topological Sorts 40 | // Main recursive function to print all possible 41 | // topological sorts 42 | private alltopologicalSortUtil(res: number[], visited: boolean[]): number[][] { 43 | // console.log('topSort', res, visited); // tslint:disable-line no-console 44 | const { numVertices, inDegree, adj } = this; 45 | // To indicate whether all topological are found 46 | // or not 47 | let flag: boolean = false; 48 | const allSorts: number[][] = []; 49 | const floor: number = inDegree.reduce((result, val, index) => { 50 | // console.log('i', index, val, visited[index]); // tslint:disable-line no-console 51 | if (visited[index] === false) { 52 | return Math.min(result, val); 53 | } 54 | return result; 55 | }, Infinity); 56 | // console.log('floor', floor); // tslint:disable-line no-console 57 | 58 | for (let vertex: number = 0; vertex < numVertices; vertex++) { 59 | // If indegree is 0 and not yet visited then 60 | // only choose that vertex 61 | // console.log('i', i, inDegree[i], visited[i]); // tslint:disable-line no-console 62 | if (inDegree[vertex] === floor && !visited[vertex]) { 63 | // console.log('visit', i, inDegree[i], visited[i]); // tslint:disable-line no-console 64 | 65 | // reducing indegree of adjacent vertices 66 | let adjIndex: number; 67 | for (adjIndex = 0; adjIndex < adj[vertex].length; adjIndex++) { 68 | const jv = adj[vertex][adjIndex]; 69 | inDegree[jv]--; 70 | } 71 | 72 | // including in result 73 | visited[vertex] = true; 74 | allSorts.push(...this.alltopologicalSortUtil(res.concat(vertex), visited)); 75 | 76 | // resetting visited, res and indegree for 77 | // backtracking 78 | visited[vertex] = false; 79 | for (adjIndex = 0; adjIndex < adj[vertex].length; adjIndex++) { 80 | const jv = adj[vertex][adjIndex]; 81 | inDegree[jv]++; 82 | } 83 | 84 | flag = true; 85 | } 86 | } 87 | 88 | // We reach here if all vertices are visited. 89 | // So we print the solution here 90 | if (!flag) { 91 | // console.log('Finished', visited); // tslint:disable-line no-console 92 | allSorts.push(res); 93 | } 94 | return allSorts; 95 | } 96 | 97 | // function to add an edge to graph 98 | // Utility function to add edge 99 | public addEdge(src: number, dest: number): void { 100 | this.adj[src].push(dest); // Add w to v's list. 101 | // increasing inner degree of w by 1 102 | this.inDegree[dest]++; 103 | } 104 | } 105 | 106 | /* 107 | // Driver program to test above functions 108 | function main(): number { 109 | // Create a graph given in the above diagram 110 | const g = new DirectedAcyclicGraph(6); 111 | g.addEdge(5, 2); 112 | g.addEdge(5, 0); 113 | g.addEdge(4, 0); 114 | g.addEdge(4, 1); 115 | g.addEdge(2, 3); 116 | g.addEdge(3, 1); 117 | 118 | console.log('All Topological sorts\n'); // tslint:disable-line no-console 119 | 120 | const allSorts = g.alltopologicalSort(); 121 | console.log(allSorts); // tslint:disable-line no-console 122 | 123 | return 0; 124 | } 125 | 126 | main(); 127 | */ 128 | -------------------------------------------------------------------------------- /src/utils/ErrorTolerantWalker.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | /** 5 | * A base walker class that gracefully handles unexpected errors. 6 | * Errors are often thrown when the TypeChecker is invoked. 7 | */ 8 | export class ErrorTolerantWalker extends Lint.RuleWalker { 9 | public static DEBUG: boolean = false; 10 | 11 | protected visitNode(node: ts.Node): void { 12 | try { 13 | super.visitNode(node); 14 | } catch (error) { 15 | // turn this on when trying out new rules on foreign codebases 16 | if (ErrorTolerantWalker.DEBUG) { 17 | const msg: string = 18 | 'An error occurred visiting a node.' + 19 | '\nWalker: ' + 20 | this.getClassName() + 21 | '\nNode: ' + 22 | (node.getFullText ? node.getFullText() : '') + 23 | '\n' + 24 | error; 25 | 26 | this.addFailureAt(node.getStart ? node.getStart() : 0, node.getWidth ? node.getWidth() : 0, msg); 27 | } 28 | } 29 | } 30 | 31 | private getClassName(): string { 32 | // Some versions of IE have the word "function" in the constructor name and 33 | // have the function body there as well. This rips out and returns the function name. 34 | const result: string = this.constructor.toString().match(/function\s+([\w\$]+)\s*\(/)[1] || ''; 35 | if (result == null || result.length === 0) { 36 | throw new Error('Could not determine class name from input: ' + this.constructor.toString()); 37 | } 38 | return result; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/ExtendedMetadata.ts: -------------------------------------------------------------------------------- 1 | import * as Lint from 'tslint'; 2 | 3 | /** 4 | * Additional information that each rule must specify. 5 | */ 6 | export interface ExtendedMetadata extends Lint.IRuleMetadata { 7 | issueClass: IssueClass; 8 | issueType: IssueType; 9 | severity: Severity; 10 | level: Level; 11 | group: Group; 12 | recommendation?: string; 13 | commonWeaknessEnumeration?: string; 14 | } 15 | 16 | /** 17 | * The Security Development Lifecycle defines many rules: https://www.microsoft.com/en-us/sdl/ 18 | * SDL - Use this value if the rule is based on an SDL recommendation. 19 | * Non-SDL - Use this value when you want a rule to show up in Microsoft's Warnings Central 20 | * Ignored - Use this value to exclude the rule from Warnings Central 21 | */ 22 | export type IssueClass = 'SDL' | 'Non-SDL' | 'Ignored'; 23 | export type IssueType = 'Error' | 'Warning'; 24 | export type Severity = 'Critical' | 'Important' | 'Moderate' | 'Low'; 25 | 26 | /** 27 | * Mandatory - This means that all teams should be using this rule with no exceptions. 28 | * Opportunity for Excellence - This means that we recommend using the rule. 29 | */ 30 | export type Level = 'Mandatory' | 'Opportunity for Excellence'; 31 | 32 | /** 33 | * Ignored - Use this value to exclude the rule from recommended_ruleset.js and the deployed tslint.json file. 34 | */ 35 | export type Group = 'Ignored' | 'Security' | 'Correctness' | 'Accessibility' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated'; 36 | -------------------------------------------------------------------------------- /src/utils/MochaUtils.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | import { AstUtils } from './AstUtils'; 4 | import { Utils } from './Utils'; 5 | 6 | /** 7 | * Common functions for Mocha AST. 8 | */ 9 | export namespace MochaUtils { 10 | export function isMochaTest(node: ts.SourceFile): boolean { 11 | return Utils.exists( 12 | node.statements, 13 | (statement: ts.Statement): boolean => { 14 | return isStatementDescribeCall(statement); 15 | } 16 | ); 17 | } 18 | 19 | export function isStatementDescribeCall(statement: ts.Statement): boolean { 20 | if (statement.kind === ts.SyntaxKind.ExpressionStatement) { 21 | const expression: ts.Expression = (statement).expression; 22 | if (expression.kind === ts.SyntaxKind.CallExpression) { 23 | const call: ts.CallExpression = expression; 24 | return isDescribe(call); 25 | } 26 | } 27 | return false; 28 | } 29 | 30 | /** 31 | * Tells you if the call is a describe or context call. 32 | */ 33 | export function isDescribe(call: ts.CallExpression): boolean { 34 | const functionName: string = AstUtils.getFunctionName(call); 35 | const callText: string = call.expression.getText(); 36 | return functionName === 'describe' || functionName === 'context' || /(describe|context)\.(only|skip|timeout)/.test(callText); 37 | } 38 | 39 | /** 40 | * Tells you if the call is an it(), specify(), before(), etc. 41 | */ 42 | export function isLifecycleMethod(call: ts.CallExpression): boolean { 43 | const functionName: string = AstUtils.getFunctionName(call); 44 | return ( 45 | functionName === 'it' || 46 | functionName === 'specify' || 47 | functionName === 'before' || 48 | functionName === 'beforeEach' || 49 | functionName === 'beforeAll' || 50 | functionName === 'after' || 51 | functionName === 'afterEach' || 52 | functionName === 'afterAll' 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/utils/NoStringParameterToFunctionCallWalker.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { ScopedSymbolTrackingWalker } from './ScopedSymbolTrackingWalker'; 4 | 5 | import { AstUtils } from './AstUtils'; 6 | 7 | /** 8 | * A walker that creates failures whenever it detects a string parameter is being passed to a certain constructor. . 9 | */ 10 | export class NoStringParameterToFunctionCallWalker extends ScopedSymbolTrackingWalker { 11 | private failureString: string; 12 | private targetFunctionName: string; 13 | 14 | public constructor(sourceFile: ts.SourceFile, targetFunctionName: string, options: Lint.IOptions, program?: ts.Program) { 15 | super(sourceFile, options, program); 16 | this.targetFunctionName = targetFunctionName; 17 | this.failureString = 'Forbidden ' + targetFunctionName + ' string parameter: '; 18 | } 19 | 20 | protected visitCallExpression(node: ts.CallExpression) { 21 | this.validateExpression(node); 22 | super.visitCallExpression(node); 23 | } 24 | 25 | private validateExpression(node: ts.CallExpression): void { 26 | const functionName: string = AstUtils.getFunctionName(node); 27 | const firstArg: ts.Expression = node.arguments[0]; 28 | if (functionName === this.targetFunctionName && firstArg != null) { 29 | if (!this.isExpressionEvaluatingToFunction(firstArg)) { 30 | const msg: string = 31 | this.failureString + 32 | firstArg 33 | .getFullText() 34 | .trim() 35 | .substring(0, 40); 36 | this.addFailureAt(node.getStart(), node.getWidth(), msg); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/Scope.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { ErrorTolerantWalker } from './ErrorTolerantWalker'; 4 | import { AstUtils } from './AstUtils'; 5 | 6 | /** 7 | * Tracks nested scope of variables. 8 | */ 9 | export class Scope { 10 | public parent: Scope; 11 | private symbols: { [index: string]: number } = {}; 12 | 13 | constructor(parent: Scope) { 14 | this.parent = parent; 15 | } 16 | 17 | public addGlobalScope(node: ts.Node, sourceFile: ts.SourceFile, options: Lint.IOptions): void { 18 | const refCollector = new GlobalReferenceCollector(sourceFile, options); 19 | refCollector.visitNode(node); 20 | refCollector.functionIdentifiers.forEach( 21 | (identifier: string): void => { 22 | this.addFunctionSymbol(identifier); 23 | } 24 | ); 25 | refCollector.nonFunctionIdentifiers.forEach( 26 | (identifier: string): void => { 27 | this.addNonFunctionSymbol(identifier); 28 | } 29 | ); 30 | } 31 | 32 | public addParameters(parameters: ts.NodeArray): void { 33 | parameters.forEach( 34 | (parm: ts.ParameterDeclaration): void => { 35 | if (AstUtils.isDeclarationFunctionType(parm)) { 36 | this.addFunctionSymbol(parm.name.getText()); 37 | } else { 38 | this.addNonFunctionSymbol(parm.name.getText()); 39 | } 40 | } 41 | ); 42 | } 43 | 44 | public addFunctionSymbol(symbolString: string): void { 45 | this.symbols[symbolString] = ts.SyntaxKind.FunctionType; 46 | } 47 | 48 | public addNonFunctionSymbol(symbolString: string): void { 49 | this.symbols[symbolString] = ts.SyntaxKind.Unknown; 50 | } 51 | 52 | public isFunctionSymbol(symbolString: string): boolean { 53 | if (this.symbols[symbolString] === ts.SyntaxKind.FunctionType) { 54 | return true; 55 | } 56 | if (this.symbols[symbolString] === ts.SyntaxKind.Unknown) { 57 | return false; 58 | } 59 | if (this.parent != null) { 60 | return this.parent.isFunctionSymbol(symbolString); 61 | } 62 | return false; 63 | } 64 | } 65 | 66 | class GlobalReferenceCollector extends ErrorTolerantWalker { 67 | public functionIdentifiers: string[] = []; 68 | public nonFunctionIdentifiers: string[] = []; 69 | 70 | /* tslint:disable:no-empty */ 71 | protected visitModuleDeclaration(): void {} // do not descend into fresh scopes 72 | protected visitClassDeclaration(): void {} // do not descend into fresh scopes 73 | protected visitArrowFunction(): void {} // do not descend into fresh scopes 74 | protected visitFunctionExpression(): void {} // do not descend into fresh scopes 75 | /* tslint:enable:no-empty */ 76 | 77 | // need to make this public so it can be invoked outside of class 78 | /* tslint:disable:no-unnecessary-override */ 79 | public visitNode(node: ts.Node): void { 80 | super.visitNode(node); 81 | } 82 | /* tslint:enable:no-unnecessary-override */ 83 | 84 | protected visitVariableDeclaration(node: ts.VariableDeclaration): void { 85 | if (AstUtils.isDeclarationFunctionType(node)) { 86 | this.functionIdentifiers.push(node.name.getText()); 87 | } else { 88 | this.nonFunctionIdentifiers.push(node.name.getText()); 89 | } 90 | // do not descend 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/utils/TypeGuard.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | /** 4 | * TypeScript 2.0 will have more features to support type guard. 5 | * https://www.typescriptlang.org/docs/handbook/advanced-types.html 6 | * We could avoid 'as' cast if switching to 2.0 7 | */ 8 | 9 | export function isJsxAttribute(node: ts.Node): node is ts.JsxAttribute { 10 | return node && node.kind === ts.SyntaxKind.JsxAttribute; 11 | } 12 | 13 | export function isJsxSpreadAttribute(node: ts.Node): node is ts.JsxSpreadAttribute { 14 | return node && node.kind === ts.SyntaxKind.JsxSpreadAttribute; 15 | } 16 | 17 | export function isJsxExpression(node: ts.Node): node is ts.JsxExpression { 18 | return node && node.kind === ts.SyntaxKind.JsxExpression; 19 | } 20 | 21 | /** 22 | * There is no type of NumericLiteral in typescript, guarded as LiteralExpression. 23 | */ 24 | export function isNumericLiteral(node: ts.Node): node is ts.LiteralExpression { 25 | return node && node.kind === ts.SyntaxKind.NumericLiteral; 26 | } 27 | 28 | export function isStringLiteral(node: ts.Node): node is ts.StringLiteral { 29 | return node && node.kind === ts.SyntaxKind.StringLiteral; 30 | } 31 | 32 | export function isJsxElement(node: ts.Node): node is ts.JsxElement { 33 | return node && node.kind === ts.SyntaxKind.JsxElement; 34 | } 35 | 36 | export function isJsxSelfClosingElement(node: ts.Node): node is ts.JsxSelfClosingElement { 37 | return node && node.kind === ts.SyntaxKind.JsxSelfClosingElement; 38 | } 39 | 40 | export function isJsxOpeningElement(node: ts.Node): node is ts.JsxOpeningElement { 41 | return node && node.kind === ts.SyntaxKind.JsxOpeningElement; 42 | } 43 | 44 | export function isTrueKeyword(node: ts.Node): node is ts.LiteralExpression { 45 | return node && node.kind === ts.SyntaxKind.TrueKeyword; 46 | } 47 | export function isFalseKeyword(node: ts.Node): node is ts.LiteralExpression { 48 | return node && node.kind === ts.SyntaxKind.FalseKeyword; 49 | } 50 | export function isNullKeyword(node: ts.Node): node is ts.LiteralExpression { 51 | return node && node.kind === ts.SyntaxKind.NullKeyword; 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/Utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Control flow functions. 3 | */ 4 | /* tslint:disable:no-increment-decrement id-length */ 5 | import * as ts from 'typescript'; 6 | export namespace Utils { 7 | /** 8 | * Logical 'any' or 'exists' function. 9 | */ 10 | export function exists(list: ts.NodeArray, predicate: (t: T) => boolean): boolean { 11 | if (list != null) { 12 | for (let i = 0; i < list.length; i++) { 13 | const obj: T = list[i]; 14 | if (predicate(obj)) { 15 | return true; 16 | } 17 | } 18 | } 19 | return false; 20 | } 21 | 22 | /** 23 | * A contains function. 24 | */ 25 | export function contains(list: ts.NodeArray, element: T): boolean { 26 | return exists( 27 | list, 28 | (item: T): boolean => { 29 | return item === element; 30 | } 31 | ); 32 | } 33 | 34 | /** 35 | * A removeAll function. 36 | */ 37 | export function removeAll(source: ts.NodeArray, elementsToRemove: ts.NodeArray): T[] { 38 | if (source == null || source.length === 0) { 39 | return []; 40 | } 41 | if (elementsToRemove == null || elementsToRemove.length === 0) { 42 | return [].concat(source); // be sure to return a copy of the array 43 | } 44 | 45 | return source.filter( 46 | (sourceElement: T): boolean => { 47 | return !contains(elementsToRemove, sourceElement); 48 | } 49 | ); 50 | } 51 | 52 | /** 53 | * A remove() function. 54 | */ 55 | export function remove(source: ts.NodeArray, elementToRemove: T): T[] { 56 | return removeAll(source, [elementToRemove]); 57 | } 58 | 59 | export function trimTo(source: string, maxLength: number): string { 60 | if (source == null) { 61 | return ''; 62 | } 63 | if (source.length <= maxLength) { 64 | return source; 65 | } 66 | return source.substr(0, maxLength - 2) + '...'; 67 | } 68 | 69 | /** 70 | * Check whether two arrays are equal. 71 | */ 72 | export function arraysShallowEqual(arr1: any[], arr2: any[]) { 73 | if (arr1.length !== arr2.length) { 74 | return false; 75 | } 76 | for (let i = arr1.length; i--; ) { 77 | if (arr1[i] !== arr2[i]) { 78 | return false; 79 | } 80 | } 81 | return true; 82 | } 83 | } 84 | /* tslint:enable:no-increment-decrement */ 85 | -------------------------------------------------------------------------------- /src/utils/attributes/IAria.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface of aria attribute. 3 | */ 4 | export interface IAria { 5 | // tslint:disable-next-line:no-reserved-keywords 6 | type: string; 7 | values: string[]; 8 | allowUndefined: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/attributes/IDom.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface of dom 3 | */ 4 | export interface IDom { 5 | supportAria: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/attributes/IRole.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface of role. 3 | */ 4 | export interface IRole { 5 | requiredProps: string[]; 6 | additionalSupportedProps: string[]; 7 | isAbstract: boolean; 8 | } 9 | 10 | /** 11 | * Interface of role schema. 12 | */ 13 | export interface IRoleSchema { 14 | roles: IRole[]; 15 | globalSupportedProps: string[]; 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/attributes/README.md: -------------------------------------------------------------------------------- 1 | This folder contains data from w3c specifications. 2 | 3 | 1. roleSchema.json 4 | This file is composed of non-abstract role and abstract roles. 5 | Each abstract role has only global props/states as its supported props. 6 | 7 | The reference of roles and it's props/states https://www.w3.org/TR/wai-aria-1.1/#role_definitions 8 | 9 | 2. ariaSchema.json 10 | This file is composed of aria-* attributes as well as their types or limited values if any. 11 | Each aria-* attribute has a required type. 12 | Some attributes has a group of supported values, they MUST only use one of these values. 13 | 14 | The reference of aria-* states and properties https://www.w3.org/TR/wai-aria-1.1/#state_prop_def 15 | -------------------------------------------------------------------------------- /src/utils/attributes/ariaSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "aria-activedescendant": { 3 | "type": "string" 4 | }, 5 | "aria-atomic": { 6 | "type": "boolean" 7 | }, 8 | "aria-autocomplete": { 9 | "type": "token", 10 | "values": [ 11 | "inline", 12 | "list", 13 | "both", 14 | "none" 15 | ] 16 | }, 17 | "aria-busy": { 18 | "type": "boolean" 19 | }, 20 | "aria-checked": { 21 | "type": "tristate" 22 | }, 23 | "aria-colcount": { 24 | "type": "integer" 25 | }, 26 | "aria-colindex": { 27 | "type": "integer" 28 | }, 29 | "aria-colspan": { 30 | "type": "integer" 31 | }, 32 | "aria-controls": { 33 | "type": "string" 34 | }, 35 | "aria-current": { 36 | "type": "token", 37 | "values": [ 38 | "page", 39 | "step", 40 | "location", 41 | "date", 42 | "time", 43 | "true", 44 | "false" 45 | ] 46 | }, 47 | "aria-describedby": { 48 | "type": "string" 49 | }, 50 | "aria-details": { 51 | "type": "string" 52 | }, 53 | "aria-disabled": { 54 | "type": "boolean" 55 | }, 56 | "aria-dropeffect": { 57 | "type": "tokenlist", 58 | "values": [ 59 | "copy", 60 | "move", 61 | "link", 62 | "execute", 63 | "popup", 64 | "none" 65 | ] 66 | }, 67 | "aria-errormessage": { 68 | "type": "string" 69 | }, 70 | "aria-expanded": { 71 | "type": "boolean", 72 | "allowUndefined": true 73 | }, 74 | "aria-flowto": { 75 | "type": "string" 76 | }, 77 | "aria-grabbed": { 78 | "type": "boolean", 79 | "allowUndefined": true 80 | }, 81 | "aria-haspopup": { 82 | "type": "token", 83 | "values": [ 84 | "false", 85 | "true", 86 | "menu", 87 | "listbox", 88 | "tree", 89 | "grid", 90 | "dialog" 91 | ] 92 | }, 93 | "aria-hidden": { 94 | "type": "boolean", 95 | "allowUndefined": true 96 | }, 97 | "aria-invalid": { 98 | "type": "token", 99 | "values": [ 100 | "grammar", 101 | "false", 102 | "spelling", 103 | "true" 104 | ] 105 | }, 106 | "aria-keyshortcuts": { 107 | "type": "string" 108 | }, 109 | "aria-label": { 110 | "type": "string" 111 | }, 112 | "aria-labelledby": { 113 | "type": "string" 114 | }, 115 | "aria-level": { 116 | "type": "integer" 117 | }, 118 | "aria-live": { 119 | "type": "token", 120 | "values": [ 121 | "off", 122 | "polite", 123 | "assertive" 124 | ] 125 | }, 126 | "aria-modal": { 127 | "type": "boolean" 128 | }, 129 | "aria-multiline": { 130 | "type": "boolean" 131 | }, 132 | "aria-multiselectable": { 133 | "type": "boolean" 134 | }, 135 | "aria-orientation": { 136 | "type": "token", 137 | "values": [ 138 | "vertical", 139 | "horizontal" 140 | ] 141 | }, 142 | "aria-owns": { 143 | "type": "string" 144 | }, 145 | "aria-placeholder": { 146 | "type": "string" 147 | }, 148 | "aria-posinset": { 149 | "type": "integer" 150 | }, 151 | "aria-pressed": { 152 | "type": "tristate" 153 | }, 154 | "aria-readonly": { 155 | "type": "boolean" 156 | }, 157 | "aria-relevant": { 158 | "type": "tokenlist", 159 | "values": [ 160 | "additions", 161 | "removals", 162 | "text", 163 | "all" 164 | ] 165 | }, 166 | "aria-required": { 167 | "type": "boolean" 168 | }, 169 | "aria-roledescription": { 170 | "type": "string" 171 | }, 172 | "aria-rowcount": { 173 | "type": "integer" 174 | }, 175 | "aria-rowindex": { 176 | "type": "integer" 177 | }, 178 | "aria-rowspan": { 179 | "type": "integer" 180 | }, 181 | "aria-selected": { 182 | "type": "boolean", 183 | "allowUndefined": true 184 | }, 185 | "aria-setsize": { 186 | "type": "integer" 187 | }, 188 | "aria-sort": { 189 | "type": "token", 190 | "values": [ 191 | "ascending", 192 | "descending", 193 | "none", 194 | "other" 195 | ] 196 | }, 197 | "aria-valuemax": { 198 | "type": "number" 199 | }, 200 | "aria-valuemin": { 201 | "type": "number" 202 | }, 203 | "aria-valuenow": { 204 | "type": "number" 205 | }, 206 | "aria-valuetext": { 207 | "type": "string" 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/utils/attributes/domSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": { 3 | "supportAria": true 4 | }, 5 | "abbr": { 6 | "supportAria": true 7 | }, 8 | "address": { 9 | "supportAria": true 10 | }, 11 | "area": { 12 | "supportAria": true 13 | }, 14 | "article": { 15 | "supportAria": true 16 | }, 17 | "aside": { 18 | "supportAria": true 19 | }, 20 | "audio": { 21 | "supportAria": true 22 | }, 23 | "b": { 24 | "supportAria": true 25 | }, 26 | "base": { 27 | "supportAria": false 28 | }, 29 | "bdi": { 30 | "supportAria": true 31 | }, 32 | "bdo": { 33 | "supportAria": true 34 | }, 35 | "big": { 36 | "supportAria": true 37 | }, 38 | "blockquote": { 39 | "supportAria": true 40 | }, 41 | "body": { 42 | "supportAria": true 43 | }, 44 | "br": { 45 | "supportAria": true 46 | }, 47 | "button": { 48 | "supportAria": true 49 | }, 50 | "canvas": { 51 | "supportAria": true 52 | }, 53 | "caption": { 54 | "supportAria": true 55 | }, 56 | "cite": { 57 | "supportAria": true 58 | }, 59 | "code": { 60 | "supportAria": true 61 | }, 62 | "col": { 63 | "supportAria": false 64 | }, 65 | "colgroup": { 66 | "supportAria": false 67 | }, 68 | "data": { 69 | "supportAria": true 70 | }, 71 | "datalist": { 72 | "supportAria": true 73 | }, 74 | "dd": { 75 | "supportAria": true 76 | }, 77 | "del": { 78 | "supportAria": true 79 | }, 80 | "details": { 81 | "supportAria": true 82 | }, 83 | "dfn": { 84 | "supportAria": true 85 | }, 86 | "dialog": { 87 | "supportAria": true 88 | }, 89 | "div": { 90 | "supportAria": true 91 | }, 92 | "dl": { 93 | "supportAria": true 94 | }, 95 | "dt": { 96 | "supportAria": true 97 | }, 98 | "em": { 99 | "supportAria": true 100 | }, 101 | "embed": { 102 | "supportAria": true 103 | }, 104 | "fieldset": { 105 | "supportAria": true 106 | }, 107 | "figcaption": { 108 | "supportAria": true 109 | }, 110 | "figure": { 111 | "supportAria": true 112 | }, 113 | "footer": { 114 | "supportAria": true 115 | }, 116 | "form": { 117 | "supportAria": true 118 | }, 119 | "h1": { 120 | "supportAria": true 121 | }, 122 | "h2": { 123 | "supportAria": true 124 | }, 125 | "h3": { 126 | "supportAria": true 127 | }, 128 | "h4": { 129 | "supportAria": true 130 | }, 131 | "h5": { 132 | "supportAria": true 133 | }, 134 | "h6": { 135 | "supportAria": true 136 | }, 137 | "head": { 138 | "supportAria": false 139 | }, 140 | "header": { 141 | "supportAria": true 142 | }, 143 | "hgroup": { 144 | "supportAria": true 145 | }, 146 | "hr": { 147 | "supportAria": true 148 | }, 149 | "html": { 150 | "supportAria": false 151 | }, 152 | "i": { 153 | "supportAria": true 154 | }, 155 | "iframe": { 156 | "supportAria": true 157 | }, 158 | "img": { 159 | "supportAria": true 160 | }, 161 | "input": { 162 | "supportAria": true 163 | }, 164 | "ins": { 165 | "supportAria": true 166 | }, 167 | "kbd": { 168 | "supportAria": true 169 | }, 170 | "keygen": { 171 | "supportAria": true 172 | }, 173 | "label": { 174 | "supportAria": true 175 | }, 176 | "legend": { 177 | "supportAria": true 178 | }, 179 | "li": { 180 | "supportAria": true 181 | }, 182 | "link": { 183 | "supportAria": false 184 | }, 185 | "main": { 186 | "supportAria": true 187 | }, 188 | "map": { 189 | "supportAria": true 190 | }, 191 | "mark": { 192 | "supportAria": true 193 | }, 194 | "menu": { 195 | "supportAria": true 196 | }, 197 | "menuitem": { 198 | "supportAria": true 199 | }, 200 | "meta": { 201 | "supportAria": false 202 | }, 203 | "meter": { 204 | "supportAria": true 205 | }, 206 | "nav": { 207 | "supportAria": true 208 | }, 209 | "noscript": { 210 | "supportAria": false 211 | }, 212 | "object": { 213 | "supportAria": true 214 | }, 215 | "ol": { 216 | "supportAria": true 217 | }, 218 | "optgroup": { 219 | "supportAria": true 220 | }, 221 | "option": { 222 | "supportAria": true 223 | }, 224 | "output": { 225 | "supportAria": true 226 | }, 227 | "p": { 228 | "supportAria": true 229 | }, 230 | "param": { 231 | "supportAria": false 232 | }, 233 | "picture": { 234 | "supportAria": false 235 | }, 236 | "pre": { 237 | "supportAria": true 238 | }, 239 | "progress": { 240 | "supportAria": true 241 | }, 242 | "q": { 243 | "supportAria": true 244 | }, 245 | "rp": { 246 | "supportAria": true 247 | }, 248 | "rt": { 249 | "supportAria": true 250 | }, 251 | "ruby": { 252 | "supportAria": true 253 | }, 254 | "s": { 255 | "supportAria": true 256 | }, 257 | "samp": { 258 | "supportAria": true 259 | }, 260 | "script": { 261 | "supportAria": false 262 | }, 263 | "section": { 264 | "supportAria": true 265 | }, 266 | "select": { 267 | "supportAria": true 268 | }, 269 | "small": { 270 | "supportAria": true 271 | }, 272 | "source": { 273 | "supportAria": false 274 | }, 275 | "span": { 276 | "supportAria": true 277 | }, 278 | "strong": { 279 | "supportAria": true 280 | }, 281 | "style": { 282 | "supportAria": false 283 | }, 284 | "sub": { 285 | "supportAria": true 286 | }, 287 | "summary": { 288 | "supportAria": true 289 | }, 290 | "sup": { 291 | "supportAria": true 292 | }, 293 | "table": { 294 | "supportAria": true 295 | }, 296 | "tbody": { 297 | "supportAria": true 298 | }, 299 | "td": { 300 | "supportAria": true 301 | }, 302 | "textarea": { 303 | "supportAria": true 304 | }, 305 | "tfoot": { 306 | "supportAria": true 307 | }, 308 | "th": { 309 | "supportAria": true 310 | }, 311 | "thead": { 312 | "supportAria": true 313 | }, 314 | "time": { 315 | "supportAria": true 316 | }, 317 | "title": { 318 | "supportAria": false 319 | }, 320 | "tr": { 321 | "supportAria": true 322 | }, 323 | "track": { 324 | "supportAria": false 325 | }, 326 | "u": { 327 | "supportAria": true 328 | }, 329 | "ul": { 330 | "supportAria": true 331 | }, 332 | "var": { 333 | "supportAria": true 334 | }, 335 | "video": { 336 | "supportAria": true 337 | }, 338 | "wbr": { 339 | "supportAria": true 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/utils/getImplicitRole.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as implicitRoles from './implicitRoles'; 3 | import { isJsxElement, isJsxSelfClosingElement, isJsxOpeningElement } from './TypeGuard'; 4 | 5 | /** 6 | * @returns { string } the implicit role or undefined if no corresponding role for a 7 | * JsxElement, JsxSelfClosingElement or JsxOpeningElement. 8 | * The implementation is inspired and re-implemented from 9 | * https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/src/util/getImplicitRole.js 10 | * A reference about implicit role: https://www.w3.org/TR/html-aria/#sec-strong-native-semantics. 11 | * A reference about no corresponding role: https://www.w3.org/TR/html-aria/#dfn-no-corresponding-role. 12 | */ 13 | export function getImplicitRole(node: ts.Node): string { 14 | let tagName: string; 15 | 16 | if (isJsxElement(node)) { 17 | tagName = node.openingElement.tagName.getText(); 18 | } else if (isJsxSelfClosingElement(node)) { 19 | tagName = node.tagName.getText(); 20 | } else if (isJsxOpeningElement(node)) { 21 | tagName = node.tagName.getText(); 22 | } else { 23 | tagName = undefined; 24 | } 25 | 26 | return tagName && implicitRoles[tagName] && implicitRoles[tagName](node); 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/README.md: -------------------------------------------------------------------------------- 1 | All the implementation are inspired by https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/src/util/implicitRoles 2 | Reimplemented in typescript. -------------------------------------------------------------------------------- /src/utils/implicitRoles/a.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable id-length 2 | import * as ts from 'typescript'; 3 | import { getJsxAttributesFromJsxElement } from '../JsxAttribute'; 4 | 5 | const hrefString: string = 'href'; 6 | 7 | /** 8 | * @Returns the implicit role for an anchor tag. 9 | */ 10 | function getImplicitRoleForAnchor(node: ts.Node): string { 11 | return getJsxAttributesFromJsxElement(node)[hrefString] ? 'link' : undefined; 12 | } 13 | 14 | export { getImplicitRoleForAnchor as a }; 15 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/area.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getJsxAttributesFromJsxElement } from '../JsxAttribute'; 3 | 4 | const hrefString: string = 'href'; 5 | 6 | /** 7 | * @Returns the implicit role for an area tag. 8 | */ 9 | function getImplicitRoleForArea(node: ts.Node): string { 10 | return getJsxAttributesFromJsxElement(node)[hrefString] ? 'link' : undefined; 11 | } 12 | 13 | export { getImplicitRoleForArea as area }; 14 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/article.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an article tag. 3 | */ 4 | function getImplicitRoleForArticle(): string { 5 | return 'article'; 6 | } 7 | 8 | export { getImplicitRoleForArticle as article }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/aside.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an aside tag. 3 | */ 4 | function getImplicitRoleForAside(): string { 5 | return 'complementary'; 6 | } 7 | 8 | export { getImplicitRoleForAside as aside }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/body.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a body tag. 3 | */ 4 | function getImplicitRoleForBody(): string { 5 | return 'document'; 6 | } 7 | 8 | export { getImplicitRoleForBody as body }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/button.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a button tag. 3 | */ 4 | function getImplicitRoleForButton(): string { 5 | return 'button'; 6 | } 7 | 8 | export { getImplicitRoleForButton as button }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/datalist.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a datalist tag. 3 | */ 4 | function getImplicitRoleForDatalist(): string { 5 | return 'listbox'; 6 | } 7 | 8 | export { getImplicitRoleForDatalist as datalist }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/dd.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a dd tag. 3 | */ 4 | function getImplicitRoleForDd(): string { 5 | return 'definition'; 6 | } 7 | 8 | export { getImplicitRoleForDd as dd }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/details.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a details tag. 3 | */ 4 | function getImplicitRoleForDetails(): string { 5 | return 'group'; 6 | } 7 | 8 | export { getImplicitRoleForDetails as details }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/dialog.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a dialog tag. 3 | */ 4 | function getImplicitRoleForDialog(): string { 5 | return 'dialog'; 6 | } 7 | 8 | export { getImplicitRoleForDialog as dialog }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/dl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a dl tag. 3 | */ 4 | function getImplicitRoleForDl(): string { 5 | return 'list'; 6 | } 7 | 8 | export { getImplicitRoleForDl as dl }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/dt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a dt tag. 3 | */ 4 | function getImplicitRoleForDt(): string { 5 | return 'listitem'; 6 | } 7 | 8 | export { getImplicitRoleForDt as dt }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/footer.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getAncestorNode } from '../JsxAttribute'; 3 | 4 | /** 5 | * @Returns the implicit role for a footer tag. 6 | */ 7 | function getImplicitRoleForFooter(node: ts.Node): string { 8 | return getAncestorNode(node, 'article') || getAncestorNode(node, 'section') ? undefined : 'contentinfo'; 9 | } 10 | 11 | export { getImplicitRoleForFooter as footer }; 12 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/form.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a form tag. 3 | */ 4 | function getImplicitRoleForForm(): string { 5 | return 'form'; 6 | } 7 | 8 | export { getImplicitRoleForForm as form }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/h1.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an h1 tag. 3 | */ 4 | function getImplicitRoleForH1(): string { 5 | return 'heading'; 6 | } 7 | 8 | export { getImplicitRoleForH1 as h1 }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/h2.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an h2 tag. 3 | */ 4 | function getImplicitRoleForH2(): string { 5 | return 'heading'; 6 | } 7 | 8 | export { getImplicitRoleForH2 as h2 }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/h3.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an h3 tag. 3 | */ 4 | function getImplicitRoleForH3(): string { 5 | return 'heading'; 6 | } 7 | 8 | export { getImplicitRoleForH3 as h3 }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/h4.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an h4 tag. 3 | */ 4 | function getImplicitRoleForH4(): string { 5 | return 'heading'; 6 | } 7 | 8 | export { getImplicitRoleForH4 as h4 }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/h5.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an h5 tag. 3 | */ 4 | function getImplicitRoleForH5(): string { 5 | return 'heading'; 6 | } 7 | 8 | export { getImplicitRoleForH5 as h5 }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/h6.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an h6 tag. 3 | */ 4 | function getImplicitRoleForH6(): string { 5 | return 'heading'; 6 | } 7 | 8 | export { getImplicitRoleForH6 as h6 }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/header.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getAncestorNode } from '../JsxAttribute'; 3 | 4 | /** 5 | * @Returns the implicit role for a header tag. 6 | */ 7 | function getImplicitRoleForHeader(node: ts.Node): string { 8 | return getAncestorNode(node, 'article') || getAncestorNode(node, 'section') ? undefined : 'banner'; 9 | } 10 | 11 | export { getImplicitRoleForHeader as header }; 12 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/hr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an hr tag. 3 | */ 4 | function getImplicitRoleForHr(): string { 5 | return 'separator'; 6 | } 7 | 8 | export { getImplicitRoleForHr as hr }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/img.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getJsxAttributesFromJsxElement, getStringLiteral } from '../JsxAttribute'; 3 | 4 | const altString: string = 'alt'; 5 | 6 | /** 7 | * @Returns the implicit role for an img tag. 8 | */ 9 | function getImplicitRoleForImg(node: ts.Node): string { 10 | const alt: ts.JsxAttribute = getJsxAttributesFromJsxElement(node)[altString]; 11 | 12 | if (alt && getStringLiteral(alt)) { 13 | return 'img'; 14 | } 15 | 16 | return 'presentation'; 17 | } 18 | 19 | export { getImplicitRoleForImg as img }; 20 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable id-length 2 | import { a } from './a'; 3 | import { area } from './area'; 4 | import { article } from './article'; 5 | import { aside } from './aside'; 6 | import { body } from './body'; 7 | import { button } from './button'; 8 | import { datalist } from './datalist'; 9 | import { dd } from './dd'; 10 | import { details } from './details'; 11 | import { dialog } from './dialog'; 12 | import { dl } from './dl'; 13 | import { dt } from './dt'; 14 | import { footer } from './footer'; 15 | import { form } from './form'; 16 | import { h1 } from './h1'; 17 | import { h2 } from './h2'; 18 | import { h3 } from './h3'; 19 | import { h4 } from './h4'; 20 | import { h5 } from './h5'; 21 | import { h6 } from './h6'; 22 | import { header } from './header'; 23 | import { hr } from './hr'; 24 | import { img } from './img'; 25 | import { input } from './input'; 26 | import { li } from './li'; 27 | import { link } from './link'; 28 | import { main } from './main'; 29 | import { math } from './math'; 30 | import { menu } from './menu'; 31 | import { menuitem } from './menuitem'; 32 | import { meter } from './meter'; 33 | import { nav } from './nav'; 34 | import { ol } from './ol'; 35 | import { optgroup } from './optgroup'; 36 | import { option } from './option'; 37 | import { output } from './output'; 38 | import { progress } from './progress'; 39 | import { section } from './section'; 40 | import { select } from './select'; 41 | import { summary } from './summary'; 42 | import { table } from './table'; 43 | import { tbody } from './tbody'; 44 | import { td } from './td'; 45 | import { textarea } from './textarea'; 46 | import { tfoot } from './tfoot'; 47 | import { th } from './th'; 48 | import { thead } from './thead'; 49 | import { tr } from './tr'; 50 | import { ul } from './ul'; 51 | 52 | /** 53 | * Export function for getting implicit role based on tag name. 54 | */ 55 | export { 56 | a, 57 | area, 58 | article, 59 | aside, 60 | body, 61 | button, 62 | datalist, 63 | dd, 64 | details, 65 | dialog, 66 | dl, 67 | dt, 68 | footer, 69 | form, 70 | h1, 71 | h2, 72 | h3, 73 | h4, 74 | h5, 75 | h6, 76 | header, 77 | hr, 78 | img, 79 | input, 80 | li, 81 | link, 82 | main, 83 | math, 84 | menu, 85 | menuitem, 86 | meter, 87 | nav, 88 | ol, 89 | optgroup, 90 | option, 91 | output, 92 | progress, 93 | section, 94 | select, 95 | summary, 96 | table, 97 | tbody, 98 | td, 99 | textarea, 100 | tfoot, 101 | th, 102 | thead, 103 | tr, 104 | ul, 105 | }; 106 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/input.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getJsxAttributesFromJsxElement, getStringLiteral } from '../JsxAttribute'; 3 | 4 | const typeString: string = 'type'; 5 | const listString: string = 'list'; 6 | 7 | /** 8 | * @Returns the implicit role for an input tag. 9 | */ 10 | function getImplicitRoleForInput(node: ts.Node): string { 11 | const attributes: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(node); 12 | const typeAttribute: ts.JsxAttribute = attributes[typeString]; 13 | 14 | if (typeAttribute) { 15 | const value: string = getStringLiteral(typeAttribute) || ''; 16 | 17 | // tslint:disable:no-switch-case-fall-through 18 | switch (value.toUpperCase()) { 19 | case 'BUTTON': 20 | case 'IMAGE': 21 | case 'RESET': 22 | case 'SUBMIT': 23 | return 'button'; 24 | case 'CHECKBOX': 25 | return 'checkbox'; 26 | case 'NUMBER': 27 | return 'spinbutton'; 28 | case 'PASSWORD': 29 | return 'textbox'; 30 | case 'RADIO': 31 | return 'radio'; 32 | case 'RANGE': 33 | return 'slider'; 34 | case 'SEARCH': 35 | return attributes[listString] ? 'combobox' : 'searchbox'; 36 | case 'EMAIL': 37 | case 'TEL': 38 | case 'URL': 39 | case 'TEXT': 40 | return attributes[listString] ? 'combobox' : 'textbox'; 41 | case 'COLOR': 42 | case 'DATE': 43 | case 'DATETIME': 44 | case 'FILE': 45 | case 'HIDDEN': 46 | case 'MONTH': 47 | case 'TIME': 48 | case 'WEEK': 49 | return undefined; 50 | default: 51 | return 'textbox'; 52 | } 53 | } 54 | // tslint:enable:no-switch-case-fall-through 55 | 56 | return 'textbox'; 57 | } 58 | 59 | export { getImplicitRoleForInput as input }; 60 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/li.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { isJsxElement } from '../TypeGuard'; 3 | 4 | /** 5 | * @Returns the implicit role for an li tag. 6 | */ 7 | function getImplicitRoleForLi(node: ts.Node): string { 8 | const parentNode: ts.Node = node.parent; 9 | let parentTagName: string; 10 | 11 | if (isJsxElement(parentNode)) { 12 | parentTagName = parentNode.openingElement.tagName.getText(); 13 | } 14 | return parentTagName === 'ol' || parentTagName === 'ul' ? 'listitem' : undefined; 15 | } 16 | 17 | export { getImplicitRoleForLi as li }; 18 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/link.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getJsxAttributesFromJsxElement } from '../JsxAttribute'; 3 | 4 | const hrefString: string = 'href'; 5 | 6 | /** 7 | * @Returns the implicit role for a link tag. 8 | */ 9 | function getImplicitRoleForLink(node: ts.Node): string { 10 | return getJsxAttributesFromJsxElement(node)[hrefString] ? 'link' : undefined; 11 | } 12 | 13 | export { getImplicitRoleForLink as link }; 14 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a main tag. 3 | */ 4 | function getImplicitRoleForMain(): string { 5 | return 'main'; 6 | } 7 | 8 | export { getImplicitRoleForMain as main }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/math.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a math tag. 3 | */ 4 | function getImplicitRoleForMath(): string { 5 | return 'math'; 6 | } 7 | 8 | export { getImplicitRoleForMath as math }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/menu.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getJsxAttributesFromJsxElement, getStringLiteral } from '../JsxAttribute'; 3 | 4 | const typeString: string = 'type'; 5 | 6 | /** 7 | * @Returns the implicit role for a menu tag. 8 | */ 9 | function getImplicitRoleForMenu(node: ts.Node): string { 10 | const typeAttribute: ts.JsxAttribute = getJsxAttributesFromJsxElement(node)[typeString]; 11 | 12 | if (typeAttribute) { 13 | const value: string = getStringLiteral(typeAttribute) || undefined; 14 | 15 | return value && value.toUpperCase() === 'TOOLBAR' ? 'toolbar' : undefined; 16 | } 17 | 18 | return undefined; 19 | } 20 | 21 | export { getImplicitRoleForMenu as menu }; 22 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/menuitem.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { getJsxAttributesFromJsxElement, getStringLiteral } from '../JsxAttribute'; 3 | 4 | const typeString: string = 'type'; 5 | 6 | /** 7 | * @Returns the implicit role for a menuitem tag. 8 | */ 9 | function getImplicitRoleForMenuitem(node: ts.Node): string { 10 | const typeAttribute: ts.JsxAttribute = getJsxAttributesFromJsxElement(node)[typeString]; 11 | 12 | if (typeAttribute) { 13 | const value: string = getStringLiteral(typeAttribute) || ''; 14 | 15 | switch (value.toUpperCase()) { 16 | case 'COMMAND': 17 | return 'menuitem'; 18 | case 'CHECKBOX': 19 | return 'menuitemcheckbox'; 20 | case 'RADIO': 21 | return 'menuitemradio'; 22 | default: 23 | return undefined; 24 | } 25 | } 26 | 27 | return undefined; 28 | } 29 | 30 | export { getImplicitRoleForMenuitem as menuitem }; 31 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/meter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a meter tag. 3 | */ 4 | function getImplicitRoleForMeter(): string { 5 | return 'progressbar'; 6 | } 7 | 8 | export { getImplicitRoleForMeter as meter }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/nav.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a nav tag. 3 | */ 4 | function getImplicitRoleForNav(): string { 5 | return 'navigation'; 6 | } 7 | 8 | export { getImplicitRoleForNav as nav }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/ol.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an ol tag. 3 | */ 4 | function getImplicitRoleForOl(): string { 5 | return 'list'; 6 | } 7 | 8 | export { getImplicitRoleForOl as ol }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/optgroup.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a optgroup tag. 3 | */ 4 | function getImplicitRoleForOptgroup(): string { 5 | return 'group'; 6 | } 7 | 8 | export { getImplicitRoleForOptgroup as optgroup }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/option.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an option tag. 3 | */ 4 | function getImplicitRoleForOption(): string { 5 | return 'option'; 6 | } 7 | 8 | export { getImplicitRoleForOption as option }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/output.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for an output tag. 3 | */ 4 | function getImplicitRoleForOutput(): string { 5 | return 'status'; 6 | } 7 | 8 | export { getImplicitRoleForOutput as output }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/progress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a progress tag. 3 | */ 4 | function getImplicitRoleForProgress(): string { 5 | return 'progressbar'; 6 | } 7 | 8 | export { getImplicitRoleForProgress as progress }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/section.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a section tag. 3 | */ 4 | function getImplicitRoleForSection(): string { 5 | return 'region'; 6 | } 7 | 8 | export { getImplicitRoleForSection as section }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/select.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a select tag. 3 | */ 4 | function getImplicitRoleForSelect(): string { 5 | return 'listbox'; 6 | } 7 | 8 | export { getImplicitRoleForSelect as select }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/summary.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a summary tag. 3 | */ 4 | function getImplicitRoleForSummary(): string { 5 | return 'button'; 6 | } 7 | 8 | export { getImplicitRoleForSummary as summary }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/table.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a table tag. 3 | */ 4 | function getImplicitRoleForTable(): string { 5 | return 'table'; 6 | } 7 | 8 | export { getImplicitRoleForTable as table }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/tbody.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a tbody tag. 3 | */ 4 | function getImplicitRoleForTbody(): string { 5 | return 'rowgroup'; 6 | } 7 | 8 | export { getImplicitRoleForTbody as tbody }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/td.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a td tag. 3 | */ 4 | function getImplicitRoleForTd(): string { 5 | return 'cell'; 6 | } 7 | 8 | export { getImplicitRoleForTd as td }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/textarea.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a textarea tag. 3 | */ 4 | function getImplicitRoleForTextarea(): string { 5 | return 'textbox'; 6 | } 7 | 8 | export { getImplicitRoleForTextarea as textarea }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/tfoot.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a tfoot tag. 3 | */ 4 | function getImplicitRoleForTfoot(): string { 5 | return 'rowgroup'; 6 | } 7 | 8 | export { getImplicitRoleForTfoot as tfoot }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/th.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a th tag. 3 | * The implicit role is columnheader or rowheader, the func only return columnheader. 4 | */ 5 | function getImplicitRoleForTh(): string { 6 | return 'columnheader'; 7 | } 8 | 9 | export { getImplicitRoleForTh as th }; 10 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/thead.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a thead tag. 3 | */ 4 | function getImplicitRoleForThead(): string { 5 | return 'rowgroup'; 6 | } 7 | 8 | export { getImplicitRoleForThead as thead }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/tr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a tr tag. 3 | */ 4 | function getImplicitRoleForTr(): string { 5 | return 'row'; 6 | } 7 | 8 | export { getImplicitRoleForTr as tr }; 9 | -------------------------------------------------------------------------------- /src/utils/implicitRoles/ul.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Returns the implicit role for a ul tag. 3 | */ 4 | function getImplicitRoleForUl(): string { 5 | return 'list'; 6 | } 7 | 8 | export { getImplicitRoleForUl as ul }; 9 | -------------------------------------------------------------------------------- /templates/recommended_ruleset.js.snippet: -------------------------------------------------------------------------------- 1 | /** 2 | * These rule settings are a broad, general recommendation for a good default configuration. 3 | * This file is exported in the npm/nuget package as ./tslint.json. 4 | */ 5 | module.exports = { 6 | "rules": { 7 | 8 | /** 9 | * Security Rules. The following rules should be turned on because they find security issues 10 | * or are recommended in the Microsoft Secure Development Lifecycle (SDL) 11 | */ 12 | %security_rules% 13 | 14 | /** 15 | * Common Bugs and Correctness. The following rules should be turned on because they find 16 | * common bug patterns in the code or enforce type safety. 17 | */ 18 | %correctness_rules% 19 | 20 | /** 21 | * Code Clarity. The following rules should be turned on because they make the code 22 | * generally more clear to the reader. 23 | */ 24 | %clarity_rules% 25 | 26 | /** 27 | * Accessibility. The following rules should be turned on to guarantee the best user 28 | * experience for keyboard and screen reader users. 29 | */ 30 | %accessibilityy_rules% 31 | 32 | /** 33 | * Whitespace related rules. The only recommended whitespace strategy is to pick a single format and 34 | * be consistent. 35 | */ 36 | %whitespace_rules% 37 | 38 | /** 39 | * Controversial/Configurable rules. 40 | */ 41 | %configurable_rules% 42 | 43 | /** 44 | * Deprecated rules. The following rules are deprecated for various reasons. 45 | */ 46 | %deprecated_rules% 47 | } 48 | }; 49 | 50 | -------------------------------------------------------------------------------- /templates/rule-tests.snippet: -------------------------------------------------------------------------------- 1 | import {TestHelper} from './TestHelper'; 2 | 3 | /** 4 | * Unit tests. 5 | */ 6 | describe('%RULE_FILE_NAME%', () : void => { 7 | 8 | const ruleName : string = '%RULE_NAME%'; 9 | 10 | it('should pass on xxx', () : void => { 11 | const script : string = ` 12 | // todo: add passing example 13 | `; 14 | 15 | TestHelper.assertViolations(ruleName, script, [ ]); 16 | }); 17 | 18 | it('should fail on xxx', () : void => { 19 | const script : string = ` 20 | // todo: add failing example and update assertions 21 | `; 22 | 23 | TestHelper.assertViolations(ruleName, script, [ ]); 24 | }); 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /templates/rule.snippet: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker'; 5 | import {ExtendedMetadata} from './utils/ExtendedMetadata'; 6 | // use (and contribute to) AstUtils for common AST functions // TODO: delete comment 7 | import {AstUtils} from './utils/AstUtils'; 8 | // use Utils instead of Underscore functions // TODO: delete comment 9 | import {Utils} from './utils/Utils'; 10 | 11 | const FAILURE_STRING: string = 'Some error message: '; // TODO: Define an error message 12 | 13 | /** 14 | * Implementation of the %RULE_NAME% rule. 15 | */ 16 | export class Rule extends Lint.Rules.AbstractRule { 17 | 18 | public static metadata: ExtendedMetadata = { 19 | ruleName: '%RULE_NAME%', 20 | type: 'maintainability', // one of: 'functionality' | 'maintainability' | 'style' | 'typescript' 21 | description: '... add a meaningful one line description', 22 | options: null, 23 | optionsDescription: '', 24 | optionExamples: [], //Remove this property if the rule has no options 25 | typescriptOnly: false, 26 | issueClass: 'Non-SDL', // one of: 'SDL' | 'Non-SDL' | 'Ignored' 27 | issueType: 'Warning', // one of: 'Error' | 'Warning' 28 | severity: 'Low', // one of: 'Critical' | 'Important' | 'Moderate' | 'Low' 29 | level: 'Opportunity for Excellence', // one of 'Mandatory' | 'Opportunity for Excellence' 30 | group: 'Clarity', // one of 'Ignored' | 'Security' | 'Correctness' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated' 31 | commonWeaknessEnumeration: '...' // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) 32 | }; 33 | 34 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 35 | return this.applyWithWalker(new %WALKER_NAME%(sourceFile, this.getOptions())); 36 | } 37 | } 38 | 39 | class %WALKER_NAME% extends ErrorTolerantWalker { 40 | 41 | protected visitNode(node: ts.Node): void { 42 | console.log(ts.SyntaxKind[node.kind] + ' ' + node.getText()); 43 | super.visitNode(node); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test-data/ExportName/ExportNameRuleFailingTestInput.ts: -------------------------------------------------------------------------------- 1 | class ThisIsNotTheNameOfTheFile {} 2 | export = ThisIsNotTheNameOfTheFile; // does not match filename 3 | -------------------------------------------------------------------------------- /test-data/ExportName/ExportNameRuleFailingTestInput2.tsx: -------------------------------------------------------------------------------- 1 | class ThisIsNotTheNameOfTheFile {} 2 | export = ThisIsNotTheNameOfTheFile; // does not match filename 3 | -------------------------------------------------------------------------------- /test-data/ExportName/ExportNameRulePassingTestInput.ts: -------------------------------------------------------------------------------- 1 | class ExportNameRulePassingTestInput {} 2 | export = ExportNameRulePassingTestInput; // matches filename 3 | -------------------------------------------------------------------------------- /test-data/ExportName/ExportNameRulePassingTestInput2.tsx: -------------------------------------------------------------------------------- 1 | class ExportNameRulePassingTestInput {} 2 | export = ExportNameRulePassingTestInput; // matches filename 3 | -------------------------------------------------------------------------------- /test-data/NoCookies/NoCookiesFailingTestInput.ts: -------------------------------------------------------------------------------- 1 | function documentFunction(): Document { 2 | return window.document; 3 | } 4 | 5 | document.cookie = '...'; 6 | this.document.cookie = '...'; 7 | window.document.cookie = '...'; 8 | 9 | documentFunction().cookie = '...'; 10 | 11 | var doc = document; 12 | doc.cookie = '...'; 13 | -------------------------------------------------------------------------------- /test-data/NoCookies/NoCookiesPassingTestInput.ts: -------------------------------------------------------------------------------- 1 | interface DocumentLikeAPI { 2 | cookie: string; 3 | } 4 | 5 | function documentLikeAPIFunction(): DocumentLikeAPI { 6 | return null; 7 | } 8 | 9 | // These usages are OK because they are not on the DOM document 10 | var document2: DocumentLikeAPI = documentLikeAPIFunction(); 11 | document2.cookie = '...'; 12 | document2.cookie = '...'; 13 | documentLikeAPIFunction().cookie = '...'; 14 | -------------------------------------------------------------------------------- /test-data/NoCookies/NoCookiesTestInput-error.ts: -------------------------------------------------------------------------------- 1 | namespace Sample { 2 | function method() { 3 | return document.cookie; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test-data/NoFunctionConstructorWithStringArgsTestInput.ts: -------------------------------------------------------------------------------- 1 | var f = new Function('doSomething()'); 2 | 3 | () => { 4 | alert(1); 5 | }; 6 | 7 | var x = function() { 8 | alert(1); 9 | }; 10 | -------------------------------------------------------------------------------- /test-data/NoMapWithoutUsage/JavaScriptExpressionInReact.tsx: -------------------------------------------------------------------------------- 1 | import React = require('react'); 2 | 3 | export class MyComponent extends React.Component<{}, {}> { 4 | public render() { 5 | const arr = [1, 2, 3]; 6 | return ( 7 |
    8 | {arr.map(curr => ( 9 |
  • {curr}
  • 10 | ))} 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test-data/NoOctalLiteral/NoOctalLiteralTestInput-passing.ts: -------------------------------------------------------------------------------- 1 | function demoScriptPass1() { 2 | var x = 'Sample text \xB2'; 3 | var y = 'Sample text \0111'; // longer than octal 4 | } 5 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-any.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace any { 3 | 4 | } 5 | 6 | namespace SampleAny1 { 7 | // module variables 8 | var any; 9 | } 10 | 11 | namespace SampleAny2 { 12 | // module function 13 | function any() {} 14 | } 15 | 16 | class SampleAny3 { 17 | // class variables 18 | private any; 19 | } 20 | 21 | // class properties 22 | class SampleAny4 { 23 | private var; 24 | set any(value) {} 25 | get any() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleAny5 { 31 | any() {} // class methods 32 | method(any) {} // method parameters 33 | private func = any => {}; // arrow function parameters 34 | } 35 | 36 | // interface declarations 37 | interface SampleAny6 { 38 | any: any; 39 | } 40 | 41 | // function parameters 42 | function methodAny(any) {} 43 | 44 | // local variables 45 | var any; 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-as.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperAs { 3 | namespace as { 4 | // class names 5 | class as {} 6 | } 7 | } 8 | 9 | namespace SampleAs1 { 10 | // module variables 11 | var as; 12 | } 13 | 14 | namespace SampleAs2 { 15 | // module function 16 | function as() {} 17 | } 18 | 19 | class SampleAs3 { 20 | // class variables 21 | private as; 22 | } 23 | 24 | // class properties 25 | class SampleAs4 { 26 | private var; 27 | set as(value) {} 28 | get as() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleAs5 { 34 | as() {} // class methods 35 | method(as) {} // method parameters 36 | private func = as => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleAs6 { 41 | as: any; 42 | } 43 | 44 | // function parameters 45 | function methodAs(as) {} 46 | 47 | // local variables 48 | var as; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-boolean.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperBoolean { 3 | namespace boolean { } 4 | } 5 | 6 | namespace SampleBoolean1 { 7 | // module variables 8 | var boolean; 9 | } 10 | 11 | namespace SampleBoolean2 { 12 | // module function 13 | function boolean() {} 14 | } 15 | 16 | class SampleBoolean3 { 17 | // class variables 18 | private boolean; 19 | } 20 | 21 | // class properties 22 | class SampleBoolean4 { 23 | private var; 24 | set boolean(value) {} 25 | get boolean() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleBoolean5 { 31 | boolean() {} // class methods 32 | method(boolean) {} // method parameters 33 | private func = boolean => {}; // arrow function parameters 34 | } 35 | 36 | // interface declarations 37 | interface SampleBoolean6 { 38 | boolean: any; 39 | } 40 | 41 | // function parameters 42 | function methodBoolean(boolean) {} 43 | 44 | // local variables 45 | var boolean; 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-break.ts: -------------------------------------------------------------------------------- 1 | class SampleBreak3 { 2 | // class variables 3 | private break; 4 | } 5 | 6 | // class properties 7 | class SampleBreak4 { 8 | private var; 9 | set break(value) {} 10 | get break() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleBreak5 { 16 | break() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleBreak6 { 21 | break: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-case.ts: -------------------------------------------------------------------------------- 1 | class SampleCase3 { 2 | // class variables 3 | private case; 4 | } 5 | 6 | // class properties 7 | class SampleCase4 { 8 | private var; 9 | set case(value) {} 10 | get case() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleCase5 { 16 | case() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleCase6 { 21 | case: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-catch.ts: -------------------------------------------------------------------------------- 1 | class SampleCatch3 { 2 | // class variables 3 | private catch; 4 | } 5 | 6 | // class properties 7 | class SampleCatch4 { 8 | private var; 9 | set catch(value) {} 10 | get catch() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleCatch5 { 16 | catch() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleCatch6 { 21 | catch: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-class.ts: -------------------------------------------------------------------------------- 1 | // class properties 2 | class SampleClass4 { 3 | set class(value) {} 4 | } 5 | 6 | class SampleClass5 { 7 | class() {} // class methods 8 | } 9 | 10 | // interface declarations 11 | interface SampleClass6 { 12 | class: any; 13 | } 14 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-const.ts: -------------------------------------------------------------------------------- 1 | class SampleConst3 { 2 | // class variables 3 | private const; 4 | } 5 | 6 | // class properties 7 | class SampleConst4 { 8 | private var; 9 | set const(value) {} 10 | get const() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleConst5 { 16 | const() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleConst6 { 21 | const: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-constructor.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperConstructor { 3 | namespace constructor { 4 | // class names 5 | class constructor {} 6 | } 7 | } 8 | namespace SampleConstructor1 { 9 | // module variables 10 | var constructor; 11 | } 12 | 13 | namespace SampleConstructor2 { 14 | // module function 15 | function constructor() {} 16 | } 17 | 18 | // class properties 19 | class SampleConstructor4 { 20 | private var; 21 | set constructor(value) {} 22 | get constructor() { 23 | return this.var; 24 | } 25 | } 26 | 27 | class SampleConstructor5 { 28 | constructor() {} // class methods 29 | method(constructor) {} // method parameters 30 | private func = constructor => {}; // arrow function parameters 31 | } 32 | 33 | // interface declarations 34 | interface SampleConstructor6 { 35 | constructor: any; 36 | } 37 | 38 | // function parameters 39 | function methodConstructor(constructor) {} 40 | 41 | // local variables 42 | var constructor; 43 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-continue.ts: -------------------------------------------------------------------------------- 1 | class SampleContinue3 { 2 | // class variables 3 | private continue; 4 | } 5 | 6 | // class properties 7 | class SampleContinue4 { 8 | private var; 9 | set continue(value) {} 10 | get continue() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleContinue5 { 16 | continue() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleContinue6 { 21 | continue: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-debugger.ts: -------------------------------------------------------------------------------- 1 | class SampleDebugger3 { 2 | // class variables 3 | private debugger; 4 | } 5 | 6 | // class properties 7 | class SampleDebugger4 { 8 | private var; 9 | set debugger(value) {} 10 | get debugger() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleDebugger5 { 16 | debugger() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleDebugger6 { 21 | debugger: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-declare.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperDeclare { 3 | namespace declare { 4 | // class names 5 | class declare {} 6 | } 7 | } 8 | 9 | namespace SampleDeclare1 { 10 | // module variables 11 | var declare; 12 | } 13 | 14 | namespace SampleDeclare2 { 15 | // module function 16 | function declare() {} 17 | } 18 | 19 | class SampleDeclare3 { 20 | // class variables 21 | private declare; 22 | } 23 | 24 | // class properties 25 | class SampleDeclare4 { 26 | private var; 27 | set declare(value) {} 28 | get declare() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleDeclare5 { 34 | declare() {} // class methods 35 | method(declare) {} // method parameters 36 | private func = declare => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleDeclare6 { 41 | declare: any; 42 | } 43 | 44 | // function parameters 45 | function methodDeclare(declare) {} 46 | 47 | // local variables 48 | var declare; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-default.ts: -------------------------------------------------------------------------------- 1 | class SampleDefault3 { 2 | // class variables 3 | private default; 4 | } 5 | 6 | // class properties 7 | class SampleDefault4 { 8 | private var; 9 | set default(value) {} 10 | get default() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleDefault5 { 16 | default() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleDefault6 { 21 | default: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-delete.ts: -------------------------------------------------------------------------------- 1 | class SampleDelete3 { 2 | // class variables 3 | private delete; 4 | } 5 | 6 | // class properties 7 | class SampleDelete4 { 8 | private var; 9 | set delete(value) {} 10 | get delete() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleDelete5 { 16 | delete() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleDelete6 { 21 | delete: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-do.ts: -------------------------------------------------------------------------------- 1 | class SampleDo3 { 2 | // class variables 3 | private do; 4 | } 5 | 6 | // class properties 7 | class SampleDo4 { 8 | private var; 9 | set do(value) {} 10 | get do() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleDo5 { 16 | do() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleDo6 { 21 | do: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-else.ts: -------------------------------------------------------------------------------- 1 | class SampleElse3 { 2 | // class variables 3 | private else; 4 | } 5 | 6 | // class properties 7 | class SampleElse4 { 8 | private var; 9 | set else(value) {} 10 | get else() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleElse5 { 16 | else() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleElse6 { 21 | else: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-enum.ts: -------------------------------------------------------------------------------- 1 | class SampleEnum3 { 2 | // class variables 3 | private enum; 4 | } 5 | 6 | // class properties 7 | class SampleEnum4 { 8 | private var; 9 | set enum(value) {} 10 | get enum() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleEnum5 { 16 | enum() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleEnum6 { 21 | enum: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-export.ts: -------------------------------------------------------------------------------- 1 | class SampleExport3 { 2 | // class variables 3 | private export; 4 | } 5 | 6 | // class properties 7 | class SampleExport4 { 8 | private var; 9 | set export(value) {} 10 | get export() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleExport5 { 16 | export() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleExport6 { 21 | export: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-extends.ts: -------------------------------------------------------------------------------- 1 | class SampleExtends3 { 2 | // class variables 3 | private extends; 4 | } 5 | 6 | // class properties 7 | class SampleExtends4 { 8 | private var; 9 | set extends(value) {} 10 | get extends() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleExtends5 { 16 | extends() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleExtends6 { 21 | extends: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-false.ts: -------------------------------------------------------------------------------- 1 | class SampleFalse3 { 2 | // class variables 3 | private false; 4 | } 5 | 6 | // class properties 7 | class SampleFalse4 { 8 | private var; 9 | set false(value) {} 10 | get false() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleFalse5 { 16 | false() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleFalse6 { 21 | false: any; 22 | } 23 | 24 | // local variables 25 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-finally.ts: -------------------------------------------------------------------------------- 1 | class SampleFinally3 { 2 | // class variables 3 | private finally; 4 | } 5 | 6 | // class properties 7 | class SampleFinally4 { 8 | private var; 9 | set finally(value) {} 10 | get finally() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleFinally5 { 16 | finally() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleFinally6 { 21 | finally: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-for.ts: -------------------------------------------------------------------------------- 1 | class SampleFor3 { 2 | // class variables 3 | private for; 4 | } 5 | 6 | // class properties 7 | class SampleFor4 { 8 | private var; 9 | set for(value) {} 10 | get for() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleFor5 { 16 | for() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleFor6 { 21 | for: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-from.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperFrom { 3 | namespace from { 4 | // class names 5 | class from {} 6 | } 7 | } 8 | 9 | namespace SampleFrom1 { 10 | // module variables 11 | var from; 12 | } 13 | 14 | namespace SampleFrom2 { 15 | // module function 16 | function from() {} 17 | } 18 | 19 | class SampleFrom3 { 20 | // class variables 21 | private from; 22 | } 23 | 24 | // class properties 25 | class SampleFrom4 { 26 | private var; 27 | set from(value) {} 28 | get from() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleFrom5 { 34 | from() {} // class methods 35 | method(from) {} // method parameters 36 | private func = from => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleFrom6 { 41 | from: any; 42 | } 43 | 44 | // function parameters 45 | function methodFrom(from) {} 46 | 47 | // local variables 48 | var from; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-function.ts: -------------------------------------------------------------------------------- 1 | class SampleFunction3 { 2 | // class variables 3 | private function; 4 | } 5 | 6 | // class properties 7 | class SampleFunction4 { 8 | private var; 9 | set function(value) {} 10 | get function() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleFunction5 { 16 | function() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleFunction6 { 21 | function: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-get.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperGet { 3 | namespace get { 4 | // class names 5 | class get {} 6 | } 7 | } 8 | 9 | namespace SampleGet1 { 10 | // module variables 11 | var get; 12 | } 13 | 14 | namespace SampleGet2 { 15 | // module function 16 | function get() {} 17 | } 18 | 19 | class SampleGet3 { 20 | // class variables 21 | private get; 22 | } 23 | 24 | // class properties 25 | class SampleGet4 { 26 | private var; 27 | set get(value) {} 28 | get get() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleGet5 { 34 | get() {} // class methods 35 | method(get) {} // method parameters 36 | private func = get => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleGet6 { 41 | get: any; 42 | } 43 | 44 | // function parameters 45 | function methodGet(get) {} 46 | 47 | // local variables 48 | var get; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-if.ts: -------------------------------------------------------------------------------- 1 | class SampleIf3 { 2 | // class variables 3 | private if; 4 | } 5 | 6 | // class properties 7 | class SampleIf4 { 8 | private var; 9 | set if(value) {} 10 | get if() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleIf5 { 16 | if() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleIf6 { 21 | if: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-implements.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace implements { 3 | 4 | } 5 | 6 | namespace SampleImplements1 { 7 | // module variables 8 | var implements; 9 | } 10 | 11 | namespace SampleImplements2 { 12 | // module function 13 | function implements() {} 14 | } 15 | 16 | class SampleImplements3 { 17 | // class variables 18 | private implements; 19 | } 20 | 21 | // class properties 22 | class SampleImplements4 { 23 | private var; 24 | set implements(value) {} 25 | get implements() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleImplements5 { 31 | implements() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SampleImplements6 { 36 | implements: any; 37 | } 38 | 39 | // function parameters 40 | function method(implements) {} 41 | 42 | // local variables 43 | var implements; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-import.ts: -------------------------------------------------------------------------------- 1 | class SampleImport3 { 2 | // class variables 3 | private import; 4 | } 5 | 6 | // class properties 7 | class SampleImport4 { 8 | private var; 9 | set import(value) {} 10 | get import() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleImport5 { 16 | import() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleImport6 { 21 | import: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-in.ts: -------------------------------------------------------------------------------- 1 | class SampleIn3 { 2 | // class variables 3 | private in; 4 | } 5 | 6 | // class properties 7 | class SampleIn4 { 8 | private var; 9 | set in(value) {} 10 | get in() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleIn5 { 16 | in() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleIn6 { 21 | in: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-instanceof.ts: -------------------------------------------------------------------------------- 1 | class SampleInstanceOf3 { 2 | // class variables 3 | private instanceof; 4 | } 5 | 6 | // class properties 7 | class SampleInstanceOf4 { 8 | private var; 9 | set instanceof(value) {} 10 | get instanceof() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleInstanceOf5 { 16 | instanceof() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleInstanceOf6 { 21 | instanceof: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-interface.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace interface { 3 | 4 | } 5 | 6 | namespace SampleInterface1 { 7 | // module variables 8 | var interface; 9 | } 10 | 11 | namespace SampleInterface2 { 12 | // module function 13 | function interface() {} 14 | } 15 | 16 | class SampleInterface3 { 17 | // class variables 18 | private interface; 19 | } 20 | 21 | // class properties 22 | class SampleInterface4 { 23 | private var; 24 | set interface(value) {} 25 | get interface() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleInterface5 { 31 | interface() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SampleInterface6 { 36 | interface: any; 37 | } 38 | 39 | // function parameters 40 | function methodInterface(interface) {} 41 | 42 | // local variables 43 | var interface; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-let.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace let { 3 | 4 | } 5 | 6 | namespace SampleLet1 { 7 | // module variables 8 | var let; 9 | } 10 | 11 | namespace SampleLet2 { 12 | // module function 13 | function let() {} 14 | } 15 | 16 | class SampleLet3 { 17 | // class variables 18 | private let; 19 | } 20 | 21 | // class properties 22 | class SampleLet4 { 23 | private var; 24 | set let(value) {} 25 | get let() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleLet5 { 31 | let() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SampleLet6 { 36 | let: any; 37 | } 38 | 39 | // function parameters 40 | function methodLet(let) {} 41 | 42 | // local variables 43 | var let; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-module.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperModule { 3 | namespace module { 4 | // class names 5 | class module {} 6 | } 7 | } 8 | 9 | namespace SampleModule1 { 10 | // module variables 11 | var module; 12 | } 13 | 14 | namespace SampleModule2 { 15 | // module function 16 | function module() {} 17 | } 18 | 19 | class SampleModule3 { 20 | // class variables 21 | private module; 22 | } 23 | 24 | // class properties 25 | class SampleModule4 { 26 | private var; 27 | set module(value) {} 28 | get module() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleModule5 { 34 | module() {} // class methods 35 | method(module) {} // method parameters 36 | private func = module => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleModule6 { 41 | module: any; 42 | } 43 | 44 | // function parameters 45 | function methodModule(module) {} 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-new.ts: -------------------------------------------------------------------------------- 1 | class SampleNew3 { 2 | // class variables 3 | private new; 4 | } 5 | 6 | // class properties 7 | class SampleNew4 { 8 | private var; 9 | set new(value) {} 10 | get new() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleNew5 { 16 | new() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleNew6 { 21 | new: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-null.ts: -------------------------------------------------------------------------------- 1 | class SampleNull3 { 2 | // class variables 3 | private null; 4 | } 5 | 6 | // class properties 7 | class SampleNull4 { 8 | private var; 9 | set null(value) {} 10 | get null() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleNull5 { 16 | null() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleNull6 { 21 | null: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-number.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperNumber { 3 | namespace number { } 4 | } 5 | 6 | namespace SampleNumber1 { 7 | // module variables 8 | var number; 9 | } 10 | 11 | namespace SampleNumber2 { 12 | // module function 13 | function number() {} 14 | } 15 | 16 | class SampleNumber3 { 17 | // class variables 18 | private number; 19 | } 20 | 21 | // class properties 22 | class SampleNumber4 { 23 | private var; 24 | set number(value) {} 25 | get number() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleNumber5 { 31 | number() {} // class methods 32 | method(number) {} // method parameters 33 | private func = number => {}; // arrow function parameters 34 | } 35 | 36 | // interface declarations 37 | interface SampleNumber6 { 38 | number: any; 39 | } 40 | 41 | // function parameters 42 | function methodNumber(number) {} 43 | 44 | // local variables 45 | var number; 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-of.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperOf { 3 | namespace of { 4 | // class names 5 | class of {} 6 | } 7 | } 8 | 9 | namespace SampleOf1 { 10 | // module variables 11 | var of; 12 | } 13 | 14 | namespace SampleOf2 { 15 | // module function 16 | function of() {} 17 | } 18 | 19 | class SampleOf3 { 20 | // class variables 21 | private of; 22 | } 23 | 24 | // class properties 25 | class SampleOf4 { 26 | private var; 27 | set of(value) {} 28 | get of() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleOf5 { 34 | of() {} // class methods 35 | method(of) {} // method parameters 36 | private func = of => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleOf6 { 41 | of: any; 42 | } 43 | 44 | // function parameters 45 | function methodOf(of) {} 46 | 47 | // local variables 48 | var of; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-package.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace package { 3 | 4 | } 5 | 6 | namespace SamplePackage1 { 7 | // module variables 8 | var package; 9 | } 10 | 11 | namespace SamplePackage2 { 12 | // module function 13 | function package() {} 14 | } 15 | 16 | class SamplePackage3 { 17 | // class variables 18 | private package; 19 | } 20 | 21 | // class properties 22 | class SamplePackage4 { 23 | private var; 24 | set package(value) {} 25 | get package() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SamplePackage5 { 31 | package() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SamplePackage6 { 36 | package: any; 37 | } 38 | 39 | // function parameters 40 | function methodPackage(package) {} 41 | 42 | // local variables 43 | var package; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-private.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace private { 3 | 4 | } 5 | 6 | namespace SamplePrivate1 { 7 | // module variables 8 | var private; 9 | } 10 | 11 | namespace SamplePrivate2 { 12 | // module function 13 | function private() {} 14 | } 15 | 16 | class SamplePrivate3 { 17 | // class variables 18 | private private; 19 | } 20 | 21 | // class properties 22 | class SamplePrivate4 { 23 | private var; 24 | set private(value) {} 25 | get private() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SamplePrivate5 { 31 | private() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SamplePrivate6 { 36 | private: any; 37 | } 38 | 39 | // function parameters 40 | function methodPrivate(private) {} 41 | 42 | // local variables 43 | var private; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-protected.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace protected { 3 | 4 | } 5 | 6 | namespace SampleProtected1 { 7 | // module variables 8 | var protected; 9 | } 10 | 11 | namespace SampleProtected2 { 12 | // module function 13 | function protected() {} 14 | } 15 | 16 | class SampleProtected3 { 17 | // class variables 18 | private protected; 19 | } 20 | 21 | // class properties 22 | class SampleProtected4 { 23 | private var; 24 | set protected(value) {} 25 | get protected() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleProtected5 { 31 | protected() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SampleProtected6 { 36 | protected: any; 37 | } 38 | 39 | // function parameters 40 | function methodProtected(protected) {} 41 | 42 | // local variables 43 | var protected; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-public.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace public { 3 | 4 | } 5 | 6 | namespace SamplePublic1 { 7 | // module variables 8 | var public; 9 | } 10 | 11 | namespace SamplePublic2 { 12 | // module function 13 | function public() {} 14 | } 15 | 16 | class SamplePublic3 { 17 | // class variables 18 | private public; 19 | } 20 | 21 | // class properties 22 | class SamplePublic4 { 23 | private var; 24 | set public(value) {} 25 | get public() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SamplePublic5 { 31 | public() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SamplePublic6 { 36 | public: any; 37 | } 38 | 39 | // function parameters 40 | function methodPublic(public) {} 41 | 42 | // local variables 43 | var public; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-require.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperRequire { 3 | namespace require { 4 | // class names 5 | class require {} 6 | } 7 | } 8 | 9 | namespace SampleRequire1 { 10 | // module variables 11 | var require; 12 | } 13 | 14 | namespace SampleRequire2 { 15 | // module function 16 | function require() {} 17 | } 18 | 19 | class SampleRequire3 { 20 | // class variables 21 | private require; 22 | } 23 | 24 | // class properties 25 | class SampleRequire4 { 26 | private var; 27 | set require(value) {} 28 | get require() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleRequire5 { 34 | require() {} // class methods 35 | method(require) {} // method parameters 36 | private func = require => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleRequire6 { 41 | require: any; 42 | } 43 | 44 | // function parameters 45 | function methodRequire(require) {} 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-return.ts: -------------------------------------------------------------------------------- 1 | class SampleReturn3 { 2 | // class variables 3 | private return; 4 | } 5 | 6 | // class properties 7 | class SampleReturn4 { 8 | private var; 9 | set return(value) {} 10 | get return() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleReturn5 { 16 | return() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleReturn6 { 21 | return: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-set.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperSet { 3 | namespace set { 4 | // class names 5 | class set {} 6 | } 7 | } 8 | 9 | namespace SampleSet1 { 10 | // module variables 11 | var set; 12 | } 13 | 14 | namespace SampleSet2 { 15 | // module function 16 | function set() {} 17 | } 18 | 19 | class SampleSet3 { 20 | // class variables 21 | private set; 22 | } 23 | 24 | // class properties 25 | class SampleSet4 { 26 | private var; 27 | set set(value) {} 28 | get set() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleSet5 { 34 | set() {} // class methods 35 | method(set) {} // method parameters 36 | private func = set => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleSet6 { 41 | set: any; 42 | } 43 | 44 | // function parameters 45 | function methodSet(set) {} 46 | 47 | // local variables 48 | var set; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-static.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace static { 3 | 4 | } 5 | 6 | namespace SampleStatic1 { 7 | // module variables 8 | var static; 9 | } 10 | 11 | namespace SampleStatic2 { 12 | // module function 13 | function static() {} 14 | } 15 | 16 | class SampleStatic3 { 17 | // class variables 18 | private static; 19 | } 20 | 21 | // class properties 22 | class SampleStatic4 { 23 | private var; 24 | set static(value) {} 25 | get static() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleStatic5 { 31 | static() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SampleStatic6 { 36 | static: any; 37 | } 38 | 39 | // function parameters 40 | function methodStatic(static) {} 41 | 42 | // local variables 43 | var static; 44 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-string.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperString { 3 | namespace string { } 4 | } 5 | 6 | namespace SampleString1 { 7 | // module variables 8 | var string; 9 | } 10 | 11 | namespace SampleString2 { 12 | // module function 13 | function string() {} 14 | } 15 | 16 | class SampleString3 { 17 | // class variables 18 | private string; 19 | } 20 | 21 | // class properties 22 | class SampleString4 { 23 | private var; 24 | set string(value) {} 25 | get string() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleString5 { 31 | string() {} // class methods 32 | method(string) {} // method parameters 33 | private func = string => {}; // arrow function parameters 34 | } 35 | 36 | // interface declarations 37 | interface SampleString6 { 38 | string: any; 39 | } 40 | 41 | // function parameters 42 | function methodString(string) {} 43 | 44 | // local variables 45 | var string; 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-super.ts: -------------------------------------------------------------------------------- 1 | class SampleSuper3 { 2 | // class variables 3 | private super; 4 | } 5 | 6 | // class properties 7 | class SampleSuper4 { 8 | private var; 9 | set super(value) {} 10 | get super() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleSuper5 { 16 | super() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleSuper6 { 21 | super: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-switch.ts: -------------------------------------------------------------------------------- 1 | class SampleSwitch3 { 2 | // class variables 3 | private switch; 4 | } 5 | 6 | // class properties 7 | class SampleSwitch4 { 8 | private var; 9 | set switch(value) {} 10 | get switch() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleSwitch5 { 16 | switch() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleSwitch6 { 21 | switch: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-symbol.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperSymbol { 3 | namespace symbol { } 4 | } 5 | 6 | namespace SampleSymbol1 { 7 | // module variables 8 | var symbol; 9 | } 10 | 11 | namespace SampleSymbol2 { 12 | // module function 13 | function symbol() {} 14 | } 15 | 16 | class SampleSymbol3 { 17 | // class variables 18 | private symbol; 19 | } 20 | 21 | // class properties 22 | class SampleSymbol4 { 23 | private var; 24 | set symbol(value) {} 25 | get symbol() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleSymbol5 { 31 | symbol() {} // class methods 32 | method(symbol) {} // method parameters 33 | private func = symbol => {}; // arrow function parameters 34 | } 35 | 36 | // interface declarations 37 | interface SampleSymbol6 { 38 | symbol: any; 39 | } 40 | 41 | // function parameters 42 | function methodSymbol(symbol) {} 43 | 44 | // local variables 45 | var symbol; 46 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-this.ts: -------------------------------------------------------------------------------- 1 | class SampleThis3 { 2 | // class variables 3 | private this; 4 | } 5 | 6 | // class properties 7 | class SampleThis4 { 8 | private var; 9 | set this(value) {} 10 | get this() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleThis5 { 16 | this() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleThis6 { 21 | this: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-throw.ts: -------------------------------------------------------------------------------- 1 | class SampleThrow3 { 2 | // class variables 3 | private throw; 4 | } 5 | 6 | // class properties 7 | class SampleThrow4 { 8 | private var; 9 | set throw(value) {} 10 | get throw() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleThrow5 { 16 | throw() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleThrow6 { 21 | throw: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-true.ts: -------------------------------------------------------------------------------- 1 | class SampleTrue3 { 2 | // class variables 3 | private true; 4 | } 5 | 6 | // class properties 7 | class SampleTrue4 { 8 | private var; 9 | set true(value) {} 10 | get true() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleTrue5 { 16 | true() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleTrue6 { 21 | true: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-try.ts: -------------------------------------------------------------------------------- 1 | class SampleTry3 { 2 | // class variables 3 | private try; 4 | } 5 | 6 | // class properties 7 | class SampleTry4 { 8 | private var; 9 | set try(value) {} 10 | get try() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleTry5 { 16 | try() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleTry6 { 21 | try: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-type.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace WrapperType { 3 | namespace type { 4 | // class names 5 | class type {} 6 | } 7 | } 8 | 9 | namespace SampleType1 { 10 | // module variables 11 | var type; 12 | } 13 | 14 | namespace SampleType2 { 15 | // module function 16 | function type() {} 17 | } 18 | 19 | class SampleType3 { 20 | // class variables 21 | private type; 22 | } 23 | 24 | // class properties 25 | class SampleType4 { 26 | private var; 27 | set type(value) {} 28 | get type() { 29 | return this.var; 30 | } 31 | } 32 | 33 | class SampleType5 { 34 | type() {} // class methods 35 | method(type) {} // method parameters 36 | private func = type => {}; // arrow function parameters 37 | } 38 | 39 | // interface declarations 40 | interface SampleType6 { 41 | type: any; 42 | } 43 | 44 | // function parameters 45 | function methodType(type) {} 46 | 47 | // local variables 48 | var type; 49 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-typeof.ts: -------------------------------------------------------------------------------- 1 | class SampleTypeOf3 { 2 | // class variables 3 | private typeof; 4 | } 5 | 6 | // class properties 7 | class SampleTypeOf4 { 8 | private var; 9 | set typeof(value) {} 10 | get typeof() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleTypeOf5 { 16 | typeof() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleTypeOf6 { 21 | typeof: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-var.ts: -------------------------------------------------------------------------------- 1 | class SampleVar3 { 2 | // class variables 3 | private var; 4 | } 5 | 6 | // class properties 7 | class SampleVar4a { 8 | private _var; 9 | set var(value) {} 10 | get var() { 11 | return this._var; 12 | } 13 | } 14 | 15 | // class properties 16 | class SampleVar4b { 17 | private var; 18 | } 19 | 20 | class SampleVar5 { 21 | var() {} // class methods 22 | } 23 | 24 | // interface declarations 25 | interface SampleVar6 { 26 | var: any; 27 | } 28 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-void.ts: -------------------------------------------------------------------------------- 1 | class SampleVoid3 { 2 | // class variables 3 | private void; 4 | } 5 | 6 | // class properties 7 | class SampleVoid4 { 8 | private var; 9 | set void(value) {} 10 | get void() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleVoid5 { 16 | void() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleVoid6 { 21 | void: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-while.ts: -------------------------------------------------------------------------------- 1 | class SampleWhile3 { 2 | // class variables 3 | private while; 4 | } 5 | 6 | // class properties 7 | class SampleWhile4 { 8 | private var; 9 | set while(value) {} 10 | get while() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleWhile5 { 16 | while() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleWhile6 { 21 | while: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-with.ts: -------------------------------------------------------------------------------- 1 | class SampleWith3 { 2 | // class variables 3 | private with; 4 | } 5 | 6 | // class properties 7 | class SampleWith4 { 8 | private var; 9 | set with(value) {} 10 | get with() { 11 | return this.var; 12 | } 13 | } 14 | 15 | class SampleWith5 { 16 | with() {} // class methods 17 | } 18 | 19 | // interface declarations 20 | interface SampleWith6 { 21 | with: any; 22 | } 23 | -------------------------------------------------------------------------------- /test-data/NoReservedKeywords/NoReservedKeywordsTestInput-yield.ts: -------------------------------------------------------------------------------- 1 | // module names 2 | namespace yield { 3 | 4 | } 5 | 6 | namespace SampleYield1 { 7 | // module variables 8 | var yield; 9 | } 10 | 11 | namespace SampleYield2 { 12 | // module function 13 | function yield() {} 14 | } 15 | 16 | class SampleYield3 { 17 | // class variables 18 | private yield; 19 | } 20 | 21 | // class properties 22 | class SampleYield4 { 23 | private var; 24 | set yield(value) {} 25 | get yield() { 26 | return this.var; 27 | } 28 | } 29 | 30 | class SampleYield5 { 31 | yield() {} // class methods 32 | } 33 | 34 | // interface declarations 35 | interface SampleYield6 { 36 | yield: any; 37 | } 38 | 39 | // function parameters 40 | function methodYield(yield) {} 41 | 42 | // local variables 43 | var yield; 44 | -------------------------------------------------------------------------------- /test-data/NoUnnecessarySemicolonsTestInput.ts: -------------------------------------------------------------------------------- 1 | // these are 3 empty statements 2 | // this is an empty statement but should not trigger a warning 3 | while (false) {} 4 | -------------------------------------------------------------------------------- /test-data/PreferTypeCastRuleTests-passing.tsx: -------------------------------------------------------------------------------- 1 | let myVariable = 100; 2 | let myString = (myVariable as any) as string; 3 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "forceConsistentCasingInFileNames": true, 5 | "noFallthroughCasesInSwitch": true, 6 | "noImplicitReturns": true, 7 | "noUnusedLocals": true, 8 | "noUnusedParameters": true, 9 | "noImplicitAny": false, 10 | "noImplicitThis": false, 11 | "strictNullChecks": false, 12 | "declaration": true, 13 | "jsx": "react", 14 | "lib": [ 15 | "es6", 16 | "dom", 17 | "scripthost" 18 | ], 19 | "module": "commonjs", 20 | "outDir": "dist/src", 21 | "removeComments": true, 22 | "sourceMap": true, 23 | "target": "es5", 24 | "experimentalDecorators": true 25 | }, 26 | "include": [ 27 | "src" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.testdata.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "jsx": "react", 5 | "module": "commonjs", 6 | "outDir": "dist/test-data", 7 | "removeComments": true, 8 | "sourceMap": true, 9 | "target": "es5" 10 | }, 11 | "include": [ "test-data" ] 12 | } 13 | --------------------------------------------------------------------------------