From a5e77122a156044b86c2b0376ba4fd0bfad8280a Mon Sep 17 00:00:00 2001 From: Du Toit Date: Thu, 27 Sep 2018 09:29:09 +1000 Subject: [PATCH 1/3] Add rules directory configuration --- htmlhint-server/package.json | 3 +- htmlhint-server/src/server.ts | 84 +++++++++++++++++-- .../src/typings-custom/htmlhint.d.ts | 1 + htmlhint/package.json | 7 +- 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/htmlhint-server/package.json b/htmlhint-server/package.json index 95b5af7..c177b05 100644 --- a/htmlhint-server/package.json +++ b/htmlhint-server/package.json @@ -10,7 +10,8 @@ "fs": "0.0.2", "htmlhint": "^0.9.13", "strip-json-comments": "^2.0.0", - "vscode-languageserver": "^3.4.0" + "vscode-languageserver": "^3.4.0", + "glob": "5.0.15" }, "devDependencies": { "@types/node": "^6.0.41", diff --git a/htmlhint-server/src/server.ts b/htmlhint-server/src/server.ts index 3c228c9..b5756fa 100644 --- a/htmlhint-server/src/server.ts +++ b/htmlhint-server/src/server.ts @@ -10,18 +10,23 @@ import * as path from 'path'; import * as server from 'vscode-languageserver'; import htmlhint = require('htmlhint'); import fs = require('fs'); +import glob = require('glob'); + let stripJsonComments: any = require('strip-json-comments'); interface Settings { htmlhint: { enable: boolean; options: any; + rulesDir: string; } [key: string]: any; } let settings: Settings = null; let linter: any = null; +let rulesLoaded: boolean = false; +let rootFolder: string = null; /** * This variable is used to cache loaded htmlhintrc objects. It is a dictionary from path -> config object. @@ -60,6 +65,74 @@ function getRange(error: htmlhint.Error, lines: string[]): any { }; } +function loadRules(HTMLHint: any, force: boolean): any { + if (force) { + rulesLoaded = false; + } + + if (rulesLoaded || htmlhint == null) { + return; + } + try { + if (!settings.htmlhint || !settings.htmlhint.rulesDir) { + return; + } + let rulesDir = settings.htmlhint.rulesDir; + let absoluteDir = ''; + //check absolute path + let exists = fs.existsSync(rulesDir); + if (exists) { + absoluteDir = rulesDir; + } else { + //relative dir, find it based on workspace roots + absoluteDir = path.resolve(rootFolder, rulesDir); + exists = fs.existsSync(absoluteDir); + } + + if (exists) { + //load all rules + loadCustomRules(absoluteDir, HTMLHint); + } + } + catch (e) { } + finally { + rulesLoaded = true; + } +} + +// load custom rles +function loadCustomRules(rulesdir:string, HTMLHint:any):any{ + rulesdir = rulesdir.replace(/\\/g, '/'); + if(fs.existsSync(rulesdir)){ + if(fs.statSync(rulesdir).isDirectory()){ + rulesdir += /\/$/.test(rulesdir)?'':'/'; + rulesdir += '**/*.js'; + var arrFiles = glob.sync(rulesdir, { + 'dot': false, + 'nodir': true, + 'strict': false, + 'silent': true + }); + arrFiles.forEach(function(file){ + loadRule(file, HTMLHint); + }); + } + else{ + loadRule(rulesdir, HTMLHint); + } + } +} + +// load rule +function loadRule(filepath: string, HTMLHint: any): any{ + filepath = path.resolve(filepath); + try{ + var module = require(filepath); + module(HTMLHint); + } + catch(e){} +} + /** * Given an htmlhint.Error type return a VS Code server Diagnostic object */ @@ -178,17 +251,16 @@ function trace(message: string, verbose?: string): void { } connection.onInitialize((params: server.InitializeParams, token: server.CancellationToken) => { - let rootFolder = params.rootPath; + rootFolder = params.rootPath; let initOptions: { nodePath: string; } = params.initializationOptions; let nodePath = initOptions ? (initOptions.nodePath ? initOptions.nodePath : undefined) : undefined; - const result= server.Files.resolveModule2(rootFolder, 'htmlhint', nodePath, trace). + const result = server.Files.resolveModule2(rootFolder, 'htmlhint', nodePath, trace). then((value): server.InitializeResult | server.ResponseError => { linter = value.HTMLHint; - //connection.window.showInformationMessage(`onInitialize() - found local htmlhint (version ! ${value.HTMLHint.version})`); - + //connection.window.showInformationMessage(`onInitialize() using external htmlhint(version ! ${linter.version})`); let result: server.InitializeResult = { capabilities: { textDocumentSync: documents.syncKind } }; return result; }, (error) => { @@ -208,11 +280,9 @@ function doValidate(connection: server.IConnection, document: server.TextDocumen let fsPath = server.Files.uriToFilePath(uri); let contents = document.getText(); let lines = contents.split('\n'); - let config = getConfiguration(fsPath); let errors: htmlhint.Error[] = linter.verify(contents, config); - let diagnostics: server.Diagnostic[] = []; if (errors.length > 0) { errors.forEach(each => { @@ -239,7 +309,7 @@ documents.onDidChangeContent((event) => { // The VS Code htmlhint settings have changed. Revalidate all documents. connection.onDidChangeConfiguration((params) => { settings = params.settings; - + loadRules(linter, true); validateAllTextDocuments(connection, documents.all()); }); diff --git a/htmlhint-server/src/typings-custom/htmlhint.d.ts b/htmlhint-server/src/typings-custom/htmlhint.d.ts index 050a93e..914eb06 100644 --- a/htmlhint-server/src/typings-custom/htmlhint.d.ts +++ b/htmlhint-server/src/typings-custom/htmlhint.d.ts @@ -5,6 +5,7 @@ declare module 'htmlhint' { export interface Verifier { verify(text: string): Error[]; + loadCustomRules(rulesdir: string): any; } export interface Error { diff --git a/htmlhint/package.json b/htmlhint/package.json index 7f1f7ac..c9cbb38 100644 --- a/htmlhint/package.json +++ b/htmlhint/package.json @@ -3,7 +3,7 @@ "displayName": "HTMLHint", "description": "VS Code integration for HTMLHint - A Static Code Analysis Tool for HTML", "icon": "HTMLHint.png", - "version": "0.5.0", + "version": "0.5.1", "publisher": "mkaufman", "galleryBanner": { "color": "#0000FF", @@ -40,6 +40,11 @@ "default": true, "description": "Control whether htmlhint is enabled for HTML files or not." }, + "htmlhint.rulesDir": { + "type": "string", + "default": "", + "description": "Folder containing custom rules, absolute or relative to workspace root." + }, "htmlhint.documentSelector": { "type": "array", "default": [ From cc873595e4e7930e022a70a05141326d3f32e526 Mon Sep 17 00:00:00 2001 From: Du Toit Date: Thu, 8 Nov 2018 12:44:17 +1100 Subject: [PATCH 2/3] Use the module's linter rather than passing HTMLhint as a parameter --- htmlhint-server/src/server.ts | 73 ++++++++++++++++------------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/htmlhint-server/src/server.ts b/htmlhint-server/src/server.ts index b5756fa..ac47e18 100644 --- a/htmlhint-server/src/server.ts +++ b/htmlhint-server/src/server.ts @@ -65,43 +65,34 @@ function getRange(error: htmlhint.Error, lines: string[]): any { }; } -function loadRules(HTMLHint: any, force: boolean): any { - if (force) { - rulesLoaded = false; - } - - if (rulesLoaded || htmlhint == null) { - return; - } - try { - if (!settings.htmlhint || !settings.htmlhint.rulesDir) { - return; - } - let rulesDir = settings.htmlhint.rulesDir; - let absoluteDir = ''; - //check absolute path - let exists = fs.existsSync(rulesDir); - if (exists) { - absoluteDir = rulesDir; - } else { - //relative dir, find it based on workspace roots - absoluteDir = path.resolve(rootFolder, rulesDir); - exists = fs.existsSync(absoluteDir); - } - - if (exists) { - //load all rules - loadCustomRules(absoluteDir, HTMLHint); - } - } - catch (e) { } - finally { - rulesLoaded = true; - } +function loadRules(): any { + rulesLoaded = false; + if (linter == null || !settings.htmlhint || !settings.htmlhint.rulesDir) { + return; + } + + let rulesDir = settings.htmlhint.rulesDir; + let absoluteDir = ''; + //check absolute path + let exists = fs.existsSync(rulesDir); + if (exists) { + absoluteDir = rulesDir; + } else { + //relative dir, find it based on workspace roots + absoluteDir = path.resolve(rootFolder, rulesDir); + exists = fs.existsSync(absoluteDir); + } + + if (exists) { + //load all rules + loadCustomRules(absoluteDir); + } + rulesLoaded = true; } +// loadCustomRules is a direct copy from ./htmlhint-server/node_modules/htmlhint/bin/htmlhint lines 139-160 // load custom rles -function loadCustomRules(rulesdir:string, HTMLHint:any):any{ +function loadCustomRules(rulesdir:string): any{ rulesdir = rulesdir.replace(/\\/g, '/'); if(fs.existsSync(rulesdir)){ if(fs.statSync(rulesdir).isDirectory()){ @@ -114,21 +105,23 @@ function loadCustomRules(rulesdir:string, HTMLHint:any):any{ 'silent': true }); arrFiles.forEach(function(file){ - loadRule(file, HTMLHint); + loadRule(file); }); } else{ - loadRule(rulesdir, HTMLHint); + loadRule(rulesdir); } } -} +} +// loadCustomRules is a modified copy from ./htmlhint-server/node_modules/htmlhint/bin/htmlhint lines 162-170 +// Only the variable name HTMLHint has been changed to linter // load rule -function loadRule(filepath: string, HTMLHint: any): any{ +function loadRule(filepath: string): any{ filepath = path.resolve(filepath); try{ var module = require(filepath); - module(HTMLHint); + module(linter); } catch(e){} } @@ -309,7 +302,7 @@ documents.onDidChangeContent((event) => { // The VS Code htmlhint settings have changed. Revalidate all documents. connection.onDidChangeConfiguration((params) => { settings = params.settings; - loadRules(linter, true); + loadRules(); validateAllTextDocuments(connection, documents.all()); }); From bcd52dddaea5f6a51f416c76a4b7d7568afcca50 Mon Sep 17 00:00:00 2001 From: Du Toit Date: Thu, 8 Nov 2018 12:44:47 +1100 Subject: [PATCH 3/3] Update readme and bump version to 0.5.2 --- htmlhint-server/package.json | 2 +- htmlhint/README.md | 3 ++- htmlhint/package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/htmlhint-server/package.json b/htmlhint-server/package.json index c177b05..421e1bd 100644 --- a/htmlhint-server/package.json +++ b/htmlhint-server/package.json @@ -1,6 +1,6 @@ { "name": "htmlhint-server", - "version": "0.5.0", + "version": "0.5.2", "description": "HtmlHint Linter Server", "engines": { "node": "*" diff --git a/htmlhint/README.md b/htmlhint/README.md index 8f6506c..5550f43 100644 --- a/htmlhint/README.md +++ b/htmlhint/README.md @@ -78,11 +78,12 @@ If your file type already has an associated language service other than "html", ## Settings -The HTMLHint extension provides three [settings](https://code.visualstudio.com/docs/customization/userandworkspace): +The HTMLHint extension provides four [settings](https://code.visualstudio.com/docs/customization/userandworkspace): * `htmlhint.enable` - disable the HTMLHint extension globally or per workspace. * `htmlhint.documentSelector` - specify additional language services to be linted * `htmlhint.options` - provide a rule set to override on disk `.htmlhintrc` or HTMLHint defaults. +* `htmlhint.rulesDir` - provide an absolute or relative path to a folder containing custom rules. You can change settings globally (**File** > **Preferences** > **User Settings**) or per workspace (**File** > **Preferences** > **Workspace Settings**). The **Preferences** menu is under **Code** on macOS. diff --git a/htmlhint/package.json b/htmlhint/package.json index c9cbb38..f43f553 100644 --- a/htmlhint/package.json +++ b/htmlhint/package.json @@ -3,7 +3,7 @@ "displayName": "HTMLHint", "description": "VS Code integration for HTMLHint - A Static Code Analysis Tool for HTML", "icon": "HTMLHint.png", - "version": "0.5.1", + "version": "0.5.2", "publisher": "mkaufman", "galleryBanner": { "color": "#0000FF",