├── .codecov.yml ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cli-main.js ├── cli.js ├── index.js ├── jest.config.js ├── lib ├── json-formatter.js ├── options-manager.js └── project-init.js ├── media ├── demo.png ├── logo.png └── logo.svg ├── package-lock.json ├── package.json └── test ├── fixtures ├── default │ ├── bar.js │ ├── foo.js │ └── package.json ├── gitignore │ ├── .gitignore │ ├── bar.js │ └── foo.js ├── negative-gitignore │ ├── .gitignore │ ├── bar.js │ └── foo.js └── pkgConf │ ├── bar.js │ ├── baz.js │ ├── foo.js │ └── package.json ├── index.test.js ├── options-manager.test.js └── test-setup.js /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: matchai 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless/ 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | 78 | #DynamoDB Local files 79 | .dynamodb/ 80 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "node" 5 | - "10" 6 | - "9" 7 | - "8" 8 | 9 | script: 10 | - npm test -- --coverage 11 | - npx codecov 12 | 13 | deploy: 14 | - provider: script 15 | skip_cleanup: true 16 | script: npx travis-deploy-once "npx semantic-release" 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2018 Matan Kushner 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | lightning bolt 3 |

ts-quick️

4 |

Zero-configuration JavaScript static analysis tool

5 |

6 | Build Status 7 | Code Coverage 8 | NPM Version 9 | NPM License 10 |

11 |

12 | 13 | --- 14 | 15 | Easily catch bugs and enforce JSDoc types throughout your JavaScript project using the proven power of TypeScript. 16 | Either use it as a standalone CLI, or add it to your test script to ensure that your JS the best it can be. 17 | 18 | ![ts-quick reporting two errors](./media/demo.png) 19 | 20 | ## Install 21 | 22 | ``` 23 | $ npm install --global ts-quick 24 | ``` 25 | 26 | ## Usage 27 | 28 | ``` 29 | $ ts-quick --help 30 | 31 | Usage 32 | $ ts-quick [ ...] 33 | 34 | Options 35 | --init Add ts-quick to your project 36 | --implicitAny Allow variables to implicitly have the "any" type 37 | --ignore Additional paths to ignore [Can be set multiple times] 38 | --cwd= Working directory for files 39 | 40 | Examples 41 | $ ts-quick 42 | $ ts-quick index.js 43 | $ ts-quick *.js !foo.js 44 | $ ts-quick --init 45 | $ ts-quick --implicitAny 46 | 47 | Tips 48 | Put options in package.json instead of using flags so other tools can read it. 49 | ``` 50 | 51 | ## Workflow 52 | 53 | The recommended workflow is to add ts-quick locally to your project to have it run with tests. 54 | 55 | Simply run `$ ts-quick --init` to add ts-quick to your package.json. 56 | 57 | ### Before 58 | 59 | ```json 60 | { 61 | "name": "example-package", 62 | "scripts": { 63 | "test": "jest" 64 | }, 65 | "devDependencies": { 66 | "jest": "^23.0.0" 67 | } 68 | } 69 | ``` 70 | 71 | ### After 72 | 73 | ```json 74 | { 75 | "name": "example-package", 76 | "scripts": { 77 | "test": "ts-quick && jest" 78 | }, 79 | "devDependencies": { 80 | "jest": "^23.0.0", 81 | "ts-quick": "^1.1.0" 82 | } 83 | } 84 | ``` 85 | 86 | Then just run `$ npm test` and ts-quick will be run before your tests. 87 | 88 | ## Config 89 | 90 | You can configure options in ts-quick by putting them in package.json: 91 | 92 | ```json 93 | { 94 | "name": "example-package", 95 | "ts-quick": { 96 | "implicitAny": true, 97 | "ignores": ["./test", "foo.js"] 98 | } 99 | } 100 | ``` 101 | 102 | ### implicitAny 103 | 104 | Type: `boolean`
105 | Default: `false` _(types are required)_ 106 | 107 | If enabled, ts-quick allows expressions and declarations to have an implied `any` type. 108 | 109 | ### ignores 110 | 111 | Type: `Array` 112 | 113 | Some [paths](lib/options-manager.js) are ignored by default, including paths in `.gitignore`. Additional ignores can be added here. 114 | 115 | ## License 116 | 117 | ISC © [Matan Kushner](https://matchai.me/) 118 | 119 | ### Attribution 120 | 121 | The ts-quick logo is a derivative of the "flash-outline" icon in [Typicons](https://github.com/stephenhutchings/typicons.font) by [stephenhutchings](https://github.com/stephenhutchings), used under [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/).
The ts-quick logo is licensed under [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/) by [Matan Kushner](https://matchai.me/). 122 | -------------------------------------------------------------------------------- /cli-main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | const ts = require("typescript"); 4 | const meow = require("meow"); 5 | const tsQuick = require("."); 6 | const projectInit = require("./lib/project-init"); 7 | 8 | const cli = meow( 9 | ` 10 | Usage 11 | $ ts-quick [ ...] 12 | 13 | Options 14 | --init Add ts-quick to your project 15 | --implicitAny Allow variables to implicitly have the "any" type 16 | --ignore Additional paths to ignore [Can be set multiple times] 17 | --cwd= Working directory for files 18 | 19 | Examples 20 | $ ts-quick 21 | $ ts-quick index.js 22 | $ ts-quick *.js !foo.js 23 | $ ts-quick --init 24 | $ ts-quick --implicitAny 25 | 26 | Tips 27 | Put options in package.json instead of using flags so other tools can read it. 28 | `, 29 | { 30 | booleanDefault: undefined, 31 | flags: { 32 | init: { 33 | type: "boolean" 34 | }, 35 | implicitAny: { 36 | type: "boolean" 37 | }, 38 | ignore: { 39 | type: "string" 40 | }, 41 | cwd: { 42 | type: "string" 43 | }, 44 | // WIP: Only present for testing 45 | reporter: { 46 | type: "string" 47 | } 48 | } 49 | } 50 | ); 51 | 52 | const { input, flags: options } = cli; 53 | 54 | /** 55 | * Format and log the reported results. 56 | * @param {ts.Diagnostic[]} diagnostics - A list of reported diagnostics from TypeScript 57 | */ 58 | function log(diagnostics) { 59 | let reporter = tsQuick.typescriptFormatter; 60 | 61 | if (options.reporter === "json") { 62 | reporter = require("./lib/json-formatter"); 63 | } 64 | 65 | process.stdout.write(reporter(diagnostics)); 66 | process.exit(diagnostics.length === 0 ? 0 : 1); 67 | } 68 | 69 | async function main() { 70 | if (options.init) { 71 | await projectInit(options); 72 | } else { 73 | const diagnostics = await tsQuick.analyzeFiles(input, options); 74 | log(diagnostics); 75 | } 76 | } 77 | 78 | main(); 79 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | // @ts-ignore: No type declaration exists 4 | const resolveCwd = require("resolve-cwd"); 5 | 6 | const localCLI = resolveCwd.silent("ts-quick/cli"); 7 | 8 | if (localCLI && localCLI !== __filename) { 9 | const debug = require("debug")("ts-quick"); 10 | debug("Using local install of ts-quick"); 11 | require(localCLI); 12 | } else { 13 | require("./cli-main"); 14 | } 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const ts = require("typescript"); 2 | const globby = require("globby"); 3 | const arrify = require("arrify"); 4 | const optionsManager = require("./lib/options-manager"); 5 | 6 | /** 7 | * Statically analyze the provided fileNames with TypeScript. 8 | * @param {string[]} patterns - A list of glob patterns. 9 | * @param {object} options - The compiler options which should be used by TypeScript. 10 | */ 11 | async function analyzeFiles(patterns, options) { 12 | options = optionsManager.buildConfig(options); 13 | 14 | const isEmptyPatterns = patterns.length === 0; 15 | const defaultPattern = `**/*.{js,jsx,ts,tsx}`; 16 | 17 | const paths = await globby( 18 | isEmptyPatterns ? [defaultPattern] : arrify(patterns), 19 | { 20 | ignore: options.ignores, 21 | gitignore: true, 22 | cwd: options.cwd 23 | } 24 | ); 25 | 26 | let program = ts.createProgram(paths, options.config); 27 | let emitResult = program.emit(); 28 | 29 | let allDiagnostics = ts 30 | .getPreEmitDiagnostics(program) 31 | .concat(emitResult.diagnostics); 32 | 33 | return allDiagnostics; 34 | } 35 | 36 | /** 37 | * Format all provided diagnostics with the TypeScript-provided formatter. 38 | * @param {ts.Diagnostic[]} allDiagnostics - A list of all reported diagnostics from TypeScript. 39 | */ 40 | function typescriptFormatter(allDiagnostics) { 41 | return ts.formatDiagnosticsWithColorAndContext(allDiagnostics, { 42 | getCanonicalFileName: fileName => fileName, 43 | getCurrentDirectory: () => process.cwd(), 44 | getNewLine: () => ts.sys.newLine 45 | }); 46 | } 47 | 48 | module.exports.analyzeFiles = analyzeFiles; 49 | module.exports.typescriptFormatter = typescriptFormatter; 50 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupFilesAfterEnv: ["/test/test-setup.js"] 3 | }; 4 | -------------------------------------------------------------------------------- /lib/json-formatter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Format TypeScript diagnostics as a JSON object 3 | * @param {Object[]} diagnostics - The list of diagnostics from TypeScript 4 | */ 5 | function jsonFormatter(diagnostics) { 6 | // TODO: The bulk of the output is stripped until circular references are solved 7 | return JSON.stringify(diagnostics, (key, val) => { 8 | if (key !== "file") { 9 | return val; 10 | } 11 | const { fileName } = val; 12 | return { fileName }; 13 | }); 14 | } 15 | 16 | module.exports = jsonFormatter; 17 | -------------------------------------------------------------------------------- /lib/options-manager.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const ts = require("typescript"); 3 | const arrify = require("arrify"); 4 | const pkgConf = require("pkg-conf"); 5 | 6 | const DEFAULT_IGNORE = [ 7 | "**/node_modules/**", 8 | "**/bower_components/**", 9 | "flow-typed/**", 10 | "coverage/**", 11 | "{tmp,temp}/**", 12 | "**/*.min.js", 13 | "vendor/**", 14 | "dist/**" 15 | ]; 16 | 17 | const DEFAULT_CONFIG = { 18 | allowJs: true, 19 | checkJs: true, 20 | noEmit: true, 21 | noImplicitAny: true, 22 | target: ts.ScriptTarget.Latest, 23 | jsx: true, 24 | module: ts.ModuleKind.CommonJS 25 | }; 26 | 27 | const defaultOptions = { 28 | config: DEFAULT_CONFIG 29 | }; 30 | 31 | /** 32 | * Normalize all options with plural versions of their option names. 33 | * @param {Object} options - Options provided by the user as cli flags 34 | */ 35 | function normalizeOptions(options) { 36 | options = Object.assign({}, options); 37 | 38 | const aliases = ["ignore"]; 39 | 40 | for (const singular of aliases) { 41 | const plural = singular + "s"; 42 | let value = options[plural] || options[singular]; 43 | 44 | delete options[singular]; 45 | 46 | if (value === undefined) { 47 | continue; 48 | } 49 | 50 | options[plural] = arrify(value); 51 | } 52 | 53 | return options; 54 | } 55 | 56 | /** 57 | * Merge the options provided as flags with the options in package.json 58 | * @param {Object} options - Options provided by the user as cli flags 59 | */ 60 | function mergeWithPkgConf(options) { 61 | options = { cwd: process.cwd(), ...options }; 62 | options.cwd = path.resolve(options.cwd); 63 | const config = pkgConf.sync("ts-quick", { 64 | cwd: options.cwd, 65 | skipOnFalse: true 66 | }); 67 | return { ...config, ...options }; 68 | } 69 | 70 | /** 71 | * Restructure user-provided options and merge them with default options to 72 | * build a config, as required by the TypeScript compiler. 73 | * @param {Object} options - Options provided by the user as cli flags 74 | */ 75 | function buildConfig(options) { 76 | options = mergeWithPkgConf(options); 77 | options = normalizeOptions(options); 78 | options = { ...defaultOptions, ...options }; 79 | 80 | if (options.implicitAny) { 81 | options.config.noImplicitAny = !options.implicitAny; 82 | } 83 | 84 | options.ignores = DEFAULT_IGNORE.concat(options.ignores || []); 85 | return options; 86 | } 87 | 88 | module.exports.buildConfig = buildConfig; 89 | module.exports.mergeWithPkgConf = mergeWithPkgConf; 90 | module.exports.normalizeOptions = normalizeOptions; 91 | -------------------------------------------------------------------------------- /lib/project-init.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const readPkgUp = require("read-pkg-up"); 3 | const writePkg = require("write-pkg"); 4 | // @ts-ignore: No type declaration exists 5 | const hasYarn = require("has-yarn"); 6 | const execa = require("execa"); 7 | 8 | const DEFAULT_TEST_SCRIPT = 'echo "Error: no test specified" && exit 1'; 9 | 10 | /** 11 | * Create a new test script, prepending ts-quick as a test step 12 | * @param {string} test - The previous test script from package.json 13 | */ 14 | function buildTestScript(test) { 15 | if (test && test !== DEFAULT_TEST_SCRIPT) { 16 | // Don't add if it's already there 17 | if (!/^ts-quick( |$)/.test(test)) { 18 | return `ts-quick && ${test}`; 19 | } 20 | 21 | return test; 22 | } 23 | 24 | return "ts-quick"; 25 | } 26 | 27 | /** 28 | * Initialize a project by installing ts-script and adding it to the test script 29 | * @param {Object} options - An object containing the CLI flags 30 | */ 31 | async function projectInit(options = {}) { 32 | const pkgData = await readPkgUp({ 33 | cwd: options.cwd, 34 | normalize: false 35 | }); 36 | const pkg = pkgData.pkg || {}; 37 | const pkgPath = 38 | pkgData.path || path.resolve(options.cwd || "", "package.json"); 39 | const pkgCwd = path.dirname(pkgPath); 40 | 41 | pkg.scripts = pkg.scripts || {}; 42 | pkg.scripts.test = buildTestScript(pkg.scripts.test); 43 | 44 | await writePkg(pkgPath, pkg); 45 | 46 | if (hasYarn(pkgCwd)) { 47 | return execa( 48 | "yarn", 49 | ["add", "--dev", "--ignore-workspace-root-check", "ts-quick"], 50 | { cwd: pkgCwd } 51 | ); 52 | } 53 | 54 | return execa("npm", ["install", "--save-dev", "ts-quick"], { cwd: pkgCwd }); 55 | } 56 | 57 | module.exports = projectInit; 58 | -------------------------------------------------------------------------------- /media/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matchai/ts-quick/947e6980bb2eb95db759e65b362baec2727ca576/media/demo.png -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matchai/ts-quick/947e6980bb2eb95db759e65b362baec2727ca576/media/logo.png -------------------------------------------------------------------------------- /media/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-quick", 3 | "version": "0.0.0-semantic-release", 4 | "description": "⚡️ Zero configuration static analysis using TypeScript", 5 | "license": "ISC", 6 | "repository": "matchai/ts-quick", 7 | "author": "Matan Kushner ", 8 | "bin": "cli.js", 9 | "engines": { 10 | "node": ">=8" 11 | }, 12 | "scripts": { 13 | "test": "./cli.js && jest" 14 | }, 15 | "files": [ 16 | "lib", 17 | "*.js" 18 | ], 19 | "keywords": [ 20 | "typescript", 21 | "static", 22 | "analysis", 23 | "javascript", 24 | "precommit", 25 | "cli", 26 | "check", 27 | "checker", 28 | "code", 29 | "quality", 30 | "verify" 31 | ], 32 | "dependencies": { 33 | "arrify": "2.0.1", 34 | "debug": "4.1.1", 35 | "execa": "2.0.3", 36 | "globby": "10.0.1", 37 | "has-yarn": "2.1.0", 38 | "meow": "5.0.0", 39 | "pkg-conf": "3.1.0", 40 | "read-pkg-up": "5.0.0", 41 | "resolve-cwd": "3.0.0", 42 | "typescript": "3.5.3", 43 | "write-pkg": "4.0.0" 44 | }, 45 | "devDependencies": { 46 | "@types/arrify": "1.0.4", 47 | "@types/debug": "4.1.4", 48 | "@types/execa": "0.9.0", 49 | "@types/meow": "5.0.0", 50 | "@types/node": "10.14.12", 51 | "@types/pkg-conf": "2.1.0", 52 | "@types/read-pkg-up": "3.0.1", 53 | "@types/write-pkg": "3.1.0", 54 | "husky": "2.7.0", 55 | "jest": "24.8.0", 56 | "prettier": "1.18.2", 57 | "pretty-quick": "1.11.1" 58 | }, 59 | "ts-quick": { 60 | "ignore": "./test" 61 | }, 62 | "husky": { 63 | "hooks": { 64 | "pre-commit": "pretty-quick --staged" 65 | } 66 | }, 67 | "renovate": { 68 | "extends": [ 69 | "config:base", 70 | ":rebaseStalePrs", 71 | ":automergeMinor" 72 | ] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/fixtures/default/bar.js: -------------------------------------------------------------------------------- 1 | add(1, 2); 2 | -------------------------------------------------------------------------------- /test/fixtures/default/foo.js: -------------------------------------------------------------------------------- 1 | function square(n) { 2 | return n * n; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "default", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/gitignore/.gitignore: -------------------------------------------------------------------------------- 1 | foo.js 2 | !bar.js 3 | -------------------------------------------------------------------------------- /test/fixtures/gitignore/bar.js: -------------------------------------------------------------------------------- 1 | function square(n) { 2 | return n * n; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/gitignore/foo.js: -------------------------------------------------------------------------------- 1 | function square(n) { 2 | return n * n; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/negative-gitignore/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !foo.js 3 | -------------------------------------------------------------------------------- /test/fixtures/negative-gitignore/bar.js: -------------------------------------------------------------------------------- 1 | function square(n) { 2 | return n * n; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/negative-gitignore/foo.js: -------------------------------------------------------------------------------- 1 | function square(n) { 2 | return n * n; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/pkgConf/bar.js: -------------------------------------------------------------------------------- 1 | add(1, 2); 2 | -------------------------------------------------------------------------------- /test/fixtures/pkgConf/baz.js: -------------------------------------------------------------------------------- 1 | add(1, 2); 2 | -------------------------------------------------------------------------------- /test/fixtures/pkgConf/foo.js: -------------------------------------------------------------------------------- 1 | function square(n) { 2 | return n * n; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/pkgConf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkg-conf", 3 | "version": "1.0.0", 4 | "ts-quick": { 5 | "ignore": "bar.js", 6 | "implicitAny": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const execa = require("execa"); 3 | 4 | const main = (args, options) => 5 | execa(path.join(__dirname, "../cli-main.js"), args, options); 6 | 7 | test("the default configuration finds errors", async () => { 8 | const cwd = path.join(__dirname, "fixtures/default"); 9 | 10 | let error; 11 | try { 12 | await main(["--reporter=json"], { cwd }); 13 | } catch (err) { 14 | error = err; 15 | } 16 | 17 | const diagnostics = JSON.parse(error.stdout); 18 | expect(diagnostics).toHaveLength(2); 19 | expect(diagnostics[0]).toHaveProperty("file.fileName", "bar.js"); 20 | expect(diagnostics[0]).toHaveProperty( 21 | "messageText", 22 | "Cannot find name 'add'." 23 | ); 24 | expect(diagnostics[1]).toHaveProperty("file.fileName", "foo.js"); 25 | expect(diagnostics[1]).toHaveProperty( 26 | "messageText", 27 | "Parameter 'n' implicitly has an 'any' type." 28 | ); 29 | }); 30 | 31 | test("implicitAny ignores missing type annotations", async () => { 32 | const cwd = path.join(__dirname, "fixtures/default"); 33 | 34 | let error; 35 | try { 36 | await main(["--reporter=json", "--implicitAny"], { cwd }); 37 | } catch (err) { 38 | error = err; 39 | } 40 | 41 | const diagnostics = JSON.parse(error.stdout); 42 | expect(diagnostics).toHaveLength(1); 43 | expect(diagnostics[0]).toHaveProperty("file.fileName", "bar.js"); 44 | expect(diagnostics[0]).toHaveProperty( 45 | "messageText", 46 | "Cannot find name 'add'." 47 | ); 48 | }); 49 | 50 | test("correctly ignores files in .gitignore", async () => { 51 | const cwd = path.join(__dirname, "fixtures/gitignore"); 52 | 53 | let error; 54 | try { 55 | await main(["--reporter=json"], { cwd }); 56 | } catch (err) { 57 | error = err; 58 | } 59 | 60 | const diagnostics = JSON.parse(error.stdout); 61 | expect(diagnostics).toHaveLength(1); 62 | expect(diagnostics[0]).toHaveProperty("file.fileName", "bar.js"); 63 | expect(diagnostics[0]).toHaveProperty( 64 | "messageText", 65 | "Parameter 'n' implicitly has an 'any' type." 66 | ); 67 | }); 68 | 69 | test("correctly ignores files explicitly called when also in .gitignore", async () => { 70 | const cwd = path.join(__dirname, "fixtures/gitignore"); 71 | 72 | let error; 73 | try { 74 | await main(["test/foo.js", "--reporter=json"], { cwd }); 75 | } catch (err) { 76 | error = err; 77 | } 78 | 79 | expect(error).toBeUndefined(); 80 | }); 81 | 82 | test("correctly locates negative gitignores", async () => { 83 | const cwd = path.join(__dirname, "fixtures/negative-gitignore"); 84 | 85 | let error; 86 | try { 87 | await main(["--reporter=json"], { cwd }); 88 | } catch (err) { 89 | error = err; 90 | } 91 | 92 | const diagnostics = JSON.parse(error.stdout); 93 | expect(diagnostics).toHaveLength(1); 94 | expect(diagnostics[0]).toHaveProperty("file.fileName", "foo.js"); 95 | expect(diagnostics[0]).toHaveProperty( 96 | "messageText", 97 | "Parameter 'n' implicitly has an 'any' type." 98 | ); 99 | }); 100 | -------------------------------------------------------------------------------- /test/options-manager.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const manager = require("../lib/options-manager"); 3 | 4 | describe("normalizeOptions", () => { 5 | it("makes all the options plural and arrays", () => { 6 | const options = manager.normalizeOptions({ 7 | ignore: "test.js" 8 | }); 9 | 10 | expect(options).toEqual({ 11 | ignores: ["test.js"] 12 | }); 13 | }); 14 | 15 | it("keeps falsey values as falsey", () => { 16 | expect(manager.normalizeOptions({})).toEqual({}); 17 | }); 18 | }); 19 | 20 | describe("buildConfig", () => { 21 | it("correctly outputs the defaults", () => { 22 | const cwd = path.join(__dirname, "fixtures/default"); 23 | const options = manager.buildConfig({ cwd }); 24 | expect(options.cwd).toMatch(/test\/fixtures\/default$/); 25 | expect(options.config).toEqual({ 26 | allowJs: true, 27 | checkJs: true, 28 | noEmit: true, 29 | noImplicitAny: true, 30 | target: 8, 31 | jsx: true, 32 | module: 1 33 | }); 34 | expect(options.ignores).toEqual([ 35 | "**/node_modules/**", 36 | "**/bower_components/**", 37 | "flow-typed/**", 38 | "coverage/**", 39 | "{tmp,temp}/**", 40 | "**/*.min.js", 41 | "vendor/**", 42 | "dist/**" 43 | ]); 44 | }); 45 | 46 | it("correctly accepts implicitAny", () => { 47 | const cwd = path.join(__dirname, "fixtures/default"); 48 | const options = manager.buildConfig({ cwd, implicitAny: true }); 49 | expect(options.cwd).toMatch(/test\/fixtures\/default$/); 50 | expect(options.config).toEqual({ 51 | allowJs: true, 52 | checkJs: true, 53 | noEmit: true, 54 | noImplicitAny: false, 55 | target: 8, 56 | jsx: true, 57 | module: 1 58 | }); 59 | expect(options.ignores).toEqual([ 60 | "**/node_modules/**", 61 | "**/bower_components/**", 62 | "flow-typed/**", 63 | "coverage/**", 64 | "{tmp,temp}/**", 65 | "**/*.min.js", 66 | "vendor/**", 67 | "dist/**" 68 | ]); 69 | }); 70 | }); 71 | 72 | describe("mergeWithPkgConf", () => { 73 | it("correctly formats package config", () => { 74 | const cwd = path.join(__dirname, "fixtures/pkgConf"); 75 | const options = manager.mergeWithPkgConf({ cwd }); 76 | expect(options).toEqual({ 77 | cwd: expect.stringMatching(/test\/fixtures\/pkgConf$/), 78 | ignore: "bar.js", 79 | implicitAny: false 80 | }); 81 | }); 82 | 83 | it("merges options with the package config and flags", () => { 84 | const cwd = path.join(__dirname, "fixtures/pkgConf"); 85 | const options = manager.mergeWithPkgConf({ cwd, implicitAny: true }); 86 | expect(options).toEqual({ 87 | cwd: expect.stringMatching(/test\/fixtures\/pkgConf$/), 88 | ignore: "bar.js", 89 | implicitAny: true 90 | }); 91 | }); 92 | 93 | it("ignores missing package config", () => { 94 | const cwd = path.join(__dirname, "fixtures/default"); 95 | const options = manager.mergeWithPkgConf({ cwd }); 96 | expect(options).toEqual({ 97 | cwd: expect.stringMatching(/test\/fixtures\/default$/) 98 | }); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/test-setup.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(15000); 2 | --------------------------------------------------------------------------------