From 50b7c9e25d6665995c0a8021d4d25811f4780a65 Mon Sep 17 00:00:00 2001 From: Peter Burns Date: Wed, 21 Dec 2016 16:30:39 -0800 Subject: [PATCH] Initial scaffolding for the new linter, without any rules implemented yet (#10) * Initial scaffolding for the new linter, without any rules implemented yet. Extracted by rictic from usergenic's https://github.com/Polymer/polymer-linter/pull/1 PR * Tighten down and standardize tsconfig * Improve error handling. We should never crash based on an analysis error, instead we could just turn that error into a warning on the related file. * Standardize on tslint from analyzer, + the new prefer-const * Also recover from errors thrown from lint rules. --- .clang-format | 12 ++++++ .editorconfig | 19 +++++++++ .gitignore | 2 + .npmignore | 5 +++ .vscode/settings.json | 19 +++++++++ README.md | 10 ++++- package.json | 41 +++++++++++++++++++ src/linter.ts | 91 +++++++++++++++++++++++++++++++++++++++++++ src/rule.ts | 18 +++++++++ test/mocha.opts | 3 ++ tsconfig.json | 24 ++++++++++++ tslint.json | 59 ++++++++++++++++++++++++++++ 12 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 .clang-format create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .vscode/settings.json create mode 100644 package.json create mode 100644 src/linter.ts create mode 100644 src/rule.ts create mode 100644 test/mocha.opts create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..62cdf4c --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: Google +AlignAfterOpenBracket: AlwaysBreak +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackArguments: false +# This breaks async functions sometimes, see +# https://github.com/Polymer/polymer-analyzer/pull/393 +# BinPackParameters: false diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ae71ce1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2014 The Polymer Project Authors. All rights reserved. +# This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +# The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +# The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +# Code distributed by Google as part of the polymer project is also +# subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt + +# Polymer EditorConfig + +root = true + +[*] +charset = utf-8 +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3063f07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +lib +node_modules diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..2da8484 --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +# npmignore +CHANGELOG.md +README.md +node_modules +test diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6bfa3ea --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "clang-format.style": "file", + "editor.formatOnSave": true, + "editor.formatOnType": true, + "search.exclude": { + "node_modules/": true, + "lib/": true + }, + "json.schemas": [ + { + "fileMatch": [ + "elements.json" + ], + "url": "./lib/analysis.schema.json" + } + ], + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/README.md b/README.md index 878f787..1d8894f 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# polymer-linter +# Polymer Linter + +Catch errors in your Polymer project before even running your code. + +## Work in progress + +Polymer Linter is being developed to replace [polylint][1], but is not yet complete. For a working linter, use [polylint][1]. + +[1]: https://www.github.com/polymerlabs/polylint diff --git a/package.json b/package.json new file mode 100644 index 0000000..70e0d96 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "polymer-linter", + "version": "1.0.0-alpha.1", + "description": "Lint Polymer!", + "main": "lib/linter.js", + "dependencies": { + "dom5": "^2.0.0", + "parse5": "^2.2.1", + "polymer-analyzer": "^2.0.0-alpha.14" + }, + "devDependencies": { + "@types/mocha": "^2.2.32", + "clang-format": "^1.0.45", + "mocha": "^3.1.0", + "source-map-support": "^0.4.3", + "tslint": "^4.1.1", + "typescript": "^2.0.3" + }, + "scripts": { + "build": "tsc", + "clean": "rm -rf lib; mkdir -p lib", + "format": "find src | grep '\\.[jt]s$' | xargs clang-format --style=file -i", + "lint": "tslint -c tslint.json src/*.ts src/**/*.ts", + "test": "npm run format && npm run clean && npm run build && npm run lint && mocha" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Polymer/polymer-linter.git" + }, + "keywords": [ + "polymer", + "lint", + "linter" + ], + "author": "The Polymer Project authors", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/Polymer/polymer-linter/issues" + }, + "homepage": "https://github.com/Polymer/polymer-linter#readme" +} diff --git a/src/linter.ts b/src/linter.ts new file mode 100644 index 0000000..91f0956 --- /dev/null +++ b/src/linter.ts @@ -0,0 +1,91 @@ +/** + * @license + * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ + +import {Analyzer} from 'polymer-analyzer'; +import {Severity, Warning, WarningCarryingException} from 'polymer-analyzer/lib/warning/warning'; + +import {Rule} from './rule'; + +/** + * The Linter is a simple class which groups together a set of Rules and applies + * them to a set of file urls which can be resolved and loaded by the provided + * Analyzer. A default Analyzer is prepared if one is not provided. + */ +export class Linter { + private _analyzer: Analyzer; + private _rules: Rule[]; + + constructor(rules: Rule[], analyzer: Analyzer) { + this._analyzer = analyzer; + this._rules = Array.from(rules); + } + + /** + * Given an array of filenames, lint the files and return an array of all + * warnings produced evaluating the linter rules. + */ + public async lint(files: string[]): Promise { + let warnings: Warning[] = []; + const analysisResult = await this._analyzeAll(files); + const documents = analysisResult.documents; + const analysisWarnings = analysisResult.warnings; + warnings = warnings.concat(analysisWarnings); + for (const document of documents) { + for (const rule of this._rules) { + try { + warnings = warnings.concat(await rule.check(document)); + } catch (e) { + warnings.push(this._getWarningFromError( + e, + document.url, + 'internal-lint-error', + `Internal error during linting: ${e ? e.message : e}`)); + } + } + } + return warnings; + } + + private async _analyzeAll(files: string[]) { + const documents = []; + const warnings: Warning[] = []; + for (const file of files) { + try { + documents.push(await this._analyzer.analyze(file)); + } catch (e) { + warnings.push(this._getWarningFromError( + e, + file, + 'unable-to-analyze-file', + `Internal Error while analyzing: ${e ? e.message : e}`)); + } + } + + return {documents, warnings}; + } + + private _getWarningFromError( + e: any, file: string, code: string, message: string) { + if (e instanceof WarningCarryingException) { + return e.warning; + } + return { + code, + message, + severity: Severity.WARNING, + sourceRange: + {file, start: {line: 0, column: 0}, end: {line: 0, column: 0}} + }; + } +} diff --git a/src/rule.ts b/src/rule.ts new file mode 100644 index 0000000..fa19995 --- /dev/null +++ b/src/rule.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ + +import {Document} from 'polymer-analyzer/lib/model/document'; +import {Warning} from 'polymer-analyzer/lib/warning/warning'; + +export interface Rule { check(document: Document): Promise; } diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..71ecf92 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--ui tdd +--require source-map-support/register +lib/test diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c7321eb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "isolatedModules": false, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitThis": true, + "strictNullChecks": true, + "removeComments": false, + "preserveConstEnums": true, + "suppressImplicitAnyIndexErrors": true, + "lib": ["es2017"], + "outDir": "./lib", + "declaration": true, + "sourceMap": true, + "pretty": true + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..cc69136 --- /dev/null +++ b/tslint.json @@ -0,0 +1,59 @@ +{ + "rules": { + "class-name": true, + "indent": [ + true, + "spaces" + ], + "prefer-const": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": true, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "semicolon": [ + true, + "always" + ], + "trailing-comma": [ + true, + "multiline" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +}