diff --git a/htmlhint-server/package.json b/htmlhint-server/package.json index 95b5af7..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": "*" @@ -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..ac47e18 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,67 @@ function getRange(error: htmlhint.Error, lines: string[]): any { }; } +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): 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); + }); + } + else{ + 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): any{ + filepath = path.resolve(filepath); + try{ + var module = require(filepath); + module(linter); + } + catch(e){} +} + /** * Given an htmlhint.Error type return a VS Code server Diagnostic object */ @@ -178,17 +244,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 +273,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 +302,7 @@ documents.onDidChangeContent((event) => { // The VS Code htmlhint settings have changed. Revalidate all documents. connection.onDidChangeConfiguration((params) => { settings = params.settings; - + loadRules(); 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/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 7f1f7ac..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.0", + "version": "0.5.2", "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": [