diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d1694ab..2f7bf8c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - node: [18] + node: [20] os: [ubuntu-latest] steps: diff --git a/.gitignore b/.gitignore index fc0322d..d5c1f26 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ node_modules .DS_Store *.vsix *.zip + +# TypeScript build info files +.tsbuildinfo +*/.tsbuildinfo diff --git a/htmlhint-server/src/server.ts b/htmlhint-server/src/server.ts index 74bc4ae..152fe28 100644 --- a/htmlhint-server/src/server.ts +++ b/htmlhint-server/src/server.ts @@ -19,7 +19,7 @@ interface Settings { [key: string]: any; } -let settings: Settings = null; +let settings: Settings | null = null; let linter: any = null; /** @@ -34,6 +34,20 @@ let htmlhintrcOptions: any = {}; */ function getRange(error: htmlhint.Error, lines: string[]): any { let line = lines[error.line - 1]; + if (!line) { + // Fallback if line doesn't exist + return { + start: { + line: error.line - 1, + character: error.col - 1, + }, + end: { + line: error.line - 1, + character: error.col - 1, + }, + }; + } + let isWhitespace = false; let curr = error.col; while (curr < line.length && !isWhitespace) { @@ -84,7 +98,7 @@ function makeDiagnostic( */ function getConfiguration(filePath: string): any { let options: any; - if (settings.htmlhint) { + if (settings?.htmlhint) { if ( settings.htmlhint.configFile && settings.htmlhint.options && @@ -159,7 +173,7 @@ function findConfigForHtmlFile(base: string) { /** * Given a path to a .htmlhintrc file, load it into a javascript object and return it. */ -function loadConfigurationFile(configFile): any { +function loadConfigurationFile(configFile: string): any { let ruleset: any = null; if (fs.existsSync(configFile)) { let config = fs.readFileSync(configFile, "utf8"); @@ -170,16 +184,23 @@ function loadConfigurationFile(configFile): any { return ruleset; } -function getErrorMessage(err: any, document: server.TextDocument): string { - let result: string = null; - if (typeof err.message === "string" || err.message instanceof String) { - result = err.message; - } else { - result = `An unknown error occurred while validating file: ${server.Files.uriToFilePath( - document.uri, - )}`; +function isErrorWithMessage(err: unknown): err is { message: string } { + return ( + typeof err === "object" && + err !== null && + "message" in err && + typeof (err as Record).message === "string" + ); +} + +function getErrorMessage(err: unknown, document: server.TextDocument): string { + if (isErrorWithMessage(err)) { + return err.message; } - return result; + + return `An unknown error occurred while validating file: ${server.Files.uriToFilePath( + document.uri, + )}`; } function validateAllTextDocuments( @@ -284,10 +305,8 @@ function doValidate( } connection.sendDiagnostics({ uri, diagnostics }); } catch (err) { - let message: string = null; - if (typeof err.message === "string" || err.message instanceof String) { - message = err.message; - throw new Error(message); + if (isErrorWithMessage(err)) { + throw new Error(err.message); } throw err; } diff --git a/htmlhint-server/src/tsconfig.json b/htmlhint-server/src/tsconfig.json index 2facac1..04e2b08 100644 --- a/htmlhint-server/src/tsconfig.json +++ b/htmlhint-server/src/tsconfig.json @@ -1,13 +1,37 @@ { "compilerOptions": { - "forceConsistentCasingInFileNames": true, - "target": "es6", "module": "commonjs", + "target": "ES2022", + "lib": ["ES2022"], "moduleResolution": "node", - "skipLibCheck": true, - "sourceMap": true, "outDir": "../../htmlhint/server", - "lib": ["es6"] + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "strict": true, + "noImplicitAny": false, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitOverride": true, + "strictNullChecks": false, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": false, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "exactOptionalPropertyTypes": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": false, + "noPropertyAccessFromIndexSignature": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "incremental": true, + "tsBuildInfoFile": ".tsbuildinfo" }, - "exclude": ["node_modules"] + "include": ["**/*.ts"], + "exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts"] } diff --git a/htmlhint/extension.ts b/htmlhint/extension.ts index 6af1e21..c40b88e 100644 --- a/htmlhint/extension.ts +++ b/htmlhint/extension.ts @@ -26,7 +26,7 @@ export function activate(context: ExtensionContext) { // Get file types to lint from user settings let config = workspace.getConfiguration("htmlhint"); - let languages: string[] = config.get("documentSelector"); + let languages: string[] = config.get("documentSelector") || ["html", "htm"]; let documentSelector = languages.map((language) => ({ language, scheme: "file", diff --git a/htmlhint/tsconfig.json b/htmlhint/tsconfig.json index aeee4e3..9f59e41 100644 --- a/htmlhint/tsconfig.json +++ b/htmlhint/tsconfig.json @@ -1,12 +1,37 @@ { "compilerOptions": { "module": "commonjs", - "target": "es6", + "target": "ES2022", + "lib": ["ES2022", "DOM"], + "moduleResolution": "node", "outDir": "out", - "lib": ["es6", "dom"], - "skipLibCheck": true, "sourceMap": true, - "forceConsistentCasingInFileNames": true + "declaration": true, + "declarationMap": true, + "strict": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitOverride": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noPropertyAccessFromIndexSignature": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "incremental": true, + "tsBuildInfoFile": ".tsbuildinfo" }, - "exclude": ["node_modules", "server"] + "include": ["extension.ts", "**/*.ts"], + "exclude": ["node_modules", "server", "out", "**/*.test.ts", "**/*.spec.ts"] } diff --git a/test/tsconfig.json b/test/tsconfig.json index 70a2c12..7a6e29d 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,13 +1,30 @@ { "compilerOptions": { "module": "commonjs", - "target": "es6", + "target": "ES2022", + "lib": ["ES2022", "DOM"], + "moduleResolution": "node", "outDir": "out", - "lib": ["es6", "dom"], - "types": ["node", "mocha"], + "sourceMap": true, + "strict": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": false, "esModuleInterop": true, - "skipLibCheck": true, - "sourceMap": true + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "types": ["node", "mocha"], + "skipLibCheck": true }, - "include": ["suite/**/*.ts", "runTest.ts"] + "include": ["suite/**/*.ts", "runTest.ts"], + "exclude": ["node_modules"] }