...
 
Commits (50)
# html-validate changelog
## [2.23.1](https://gitlab.com/html-validate/html-validate/compare/v2.23.0...v2.23.1) (2020-06-21)
### Bug Fixes
- **rules:** `no-trailing-whitespace` handles CRLF (windows) newlines ([2aaddc2](https://gitlab.com/html-validate/html-validate/commit/2aaddc2daaa219f16031cc105e0d396387eac07c)), closes [#93](https://gitlab.com/html-validate/html-validate/issues/93)
# [2.23.0](https://gitlab.com/html-validate/html-validate/compare/v2.22.0...v2.23.0) (2020-05-18)
### Bug Fixes
......
......@@ -4,6 +4,7 @@ const Package = require("dgeni").Package;
const packagePath = __dirname;
module.exports = new Package("html-validate-docs", [
require("dgeni-packages/links"),
require("dgeni-front-matter"),
require("./bootstrap"),
require("./highlight"),
......
This diff is collapsed.
{
"name": "html-validate",
"version": "2.23.0",
"version": "2.23.1",
"description": "html linter",
"keywords": [
"html",
......@@ -92,36 +92,36 @@
"minimist": "^1.2.0"
},
"devDependencies": {
"@babel/core": "7.9.6",
"@babel/preset-env": "7.9.6",
"@commitlint/cli": "8.3.5",
"@babel/core": "7.10.3",
"@babel/preset-env": "7.10.3",
"@commitlint/cli": "9.0.1",
"@html-validate/commitlint-config": "1.0.3",
"@html-validate/eslint-config": "1.5.1",
"@html-validate/jest-config": "1.0.8",
"@html-validate/eslint-config": "1.5.5",
"@html-validate/jest-config": "1.0.12",
"@html-validate/prettier-config": "1.0.1",
"@html-validate/semantic-release-config": "1.0.21",
"@html-validate/semantic-release-config": "1.0.24",
"@types/babel__code-frame": "7.0.1",
"@types/estree": "0.0.44",
"@types/glob": "7.1.1",
"@types/glob": "7.1.2",
"@types/inquirer": "6.5.0",
"@types/jest": "25.2.2",
"@types/jest": "26.0.0",
"@types/json-merge-patch": "0.0.5",
"@types/minimist": "1.2.0",
"@types/node": "11.15.12",
"autoprefixer": "9.8.0",
"@types/node": "11.15.16",
"autoprefixer": "9.8.1",
"babelify": "10.0.0",
"bootstrap-sass": "3.4.1",
"canonical-path": "1.0.0",
"cssnano": "4.1.10",
"dgeni": "0.4.12",
"dgeni-front-matter": "1.0.2",
"dgeni-front-matter": "2.0.0",
"dgeni-packages": "0.28.3",
"eslint-plugin-array-func": "3.1.5",
"eslint-plugin-array-func": "3.1.6",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-security": "1.4.0",
"eslint-plugin-sonarjs": "0.5.0",
"font-awesome": "4.7.0",
"front-matter": "3.2.1",
"front-matter": "4.0.2",
"grunt": "1.1.0",
"grunt-browserify": "5.3.0",
"grunt-cli": "1.3.2",
......@@ -129,23 +129,23 @@
"grunt-contrib-copy": "1.0.0",
"grunt-postcss": "0.9.0",
"grunt-sass": "3.1.0",
"highlight.js": "10.0.3",
"highlight.js": "10.1.1",
"husky": "4.2.5",
"jest": "25.5.4",
"jest-diff": "25.5.0",
"jquery": "3.5.1",
"lint-staged": "10.2.2",
"lint-staged": "10.2.11",
"load-grunt-tasks": "5.1.0",
"marked": "1.1.0",
"minimatch": "3.0.4",
"prettier": "2.0.5",
"sass": "1.26.5",
"semantic-release": "17.0.7",
"sass": "1.26.8",
"semantic-release": "17.0.8",
"serve-static": "1.14.1",
"stringmap": "0.2.2",
"strip-ansi": "6.0.0",
"ts-jest": "26.0.0",
"typescript": "3.9.2"
"ts-jest": "26.1.0",
"typescript": "3.9.5"
},
"jest": {
"preset": "@html-validate/jest-config",
......
......@@ -2,6 +2,7 @@ import { EventDump } from "../engine";
const jsonFiltered = ["parent", "children", "meta"];
/* eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types */
export function eventReplacer(key: string, value: any): string {
if (value && key === "location") {
return `${value.filename}:${value.line}:${value.column}`;
......
......@@ -29,7 +29,7 @@ export interface ConfigData {
*
* If elements isn't specified it defaults to `["html5"]`
*/
elements?: Array<string | object>;
elements?: Array<string | Record<string, unknown>>;
/**
* List of plugins.
......
......@@ -316,7 +316,7 @@ export class Config {
public get(): ConfigData {
const config = Object.assign({}, this.config);
if (config.elements) {
config.elements = config.elements.map((cur: string | object) => {
config.elements = config.elements.map((cur: string | MetaDataTable) => {
if (typeof cur === "string") {
return cur.replace(this.rootDir, "<rootDir>");
} else {
......@@ -408,7 +408,7 @@ export class Config {
}
for (const [key, schema] of Object.entries(properties)) {
if (schema.copyable && !MetaCopyableProperty.includes(key)) {
if ((schema as any).copyable && !MetaCopyableProperty.includes(key)) {
MetaCopyableProperty.push(key);
}
}
......
......@@ -3,7 +3,7 @@ import { MetaElement } from "../meta";
import { AttributeData } from "../parser";
export type ProcessAttributeCallback = (
this: {},
this: unknown,
attr: AttributeData
) => Iterable<AttributeData>;
......
......@@ -68,7 +68,10 @@ export class Attribute {
*/
public valueMatches(pattern: RegExp, dynamicMatches?: boolean): boolean;
public valueMatches(pattern: string, dynamicMatches?: boolean): boolean;
public valueMatches(pattern: any, dynamicMatches: boolean = true): boolean {
public valueMatches(
pattern: RegExp | string,
dynamicMatches: boolean = true
): boolean {
/* dynamic values matches everything */
if (this.value instanceof DynamicValue) {
return dynamicMatches;
......
......@@ -3,8 +3,19 @@ import { Parser } from "../parser";
import { reset as resetDOMCounter } from "./domnode";
import { HtmlElement } from "./htmlelement";
import { Selector } from "./selector";
import { NodeType } from "./nodetype";
interface StrippedHtmlElement {
id: string;
class: string | null;
nodeName: string;
nodeType: NodeType;
tagName: string;
unique: number;
testId: string | null;
}
function stripHtmlElement(node: HtmlElement): object {
function stripHtmlElement(node: HtmlElement): StrippedHtmlElement {
return {
id: node.id,
class: node.hasAttribute("class") ? node.classList.join(" ") : null,
......@@ -16,7 +27,7 @@ function stripHtmlElement(node: HtmlElement): object {
};
}
function fetch(it: IterableIterator<HtmlElement>): object[] {
function fetch(it: IterableIterator<HtmlElement>): StrippedHtmlElement[] {
return Array.from(it, stripHtmlElement);
}
......
......@@ -497,7 +497,7 @@ describe("Engine", () => {
it("should handle missing setup callback", () => {
expect.assertions(1);
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: abstract method not implemented, but plugin might be vanilla js so want to handle the case
class MyRule extends Rule {}
......
......@@ -167,7 +167,7 @@ export class Engine<T extends Parser = Parser> {
*/
public getRuleDocumentation(
ruleId: string,
context?: any
context?: any // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
): RuleDocumentation {
const rules = this.config.getRules();
if (rules.has(ruleId)) {
......@@ -396,7 +396,7 @@ export class Engine<T extends Parser = Parser> {
protected loadRule(
ruleId: string,
severity: Severity,
options: any,
options: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
parser: Parser,
report: Reporter
): Rule {
......
......@@ -19,8 +19,8 @@ export class SchemaValidationError extends UserError {
public constructor(
filename: string | null,
message: string,
obj: any,
schema: any,
obj: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
schema: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
errors: Ajv.ErrorObject[]
) {
const summary = getSummary(schema, obj, errors);
......
......@@ -54,6 +54,7 @@ export class EventHandler {
* @param event {string} - Event name.
* @param [data] {any} - Event data.
*/
/* eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types */
public trigger(event: string, data: any): void {
const callbacks = [].concat(
this.listeners[event] || [],
......
import path from "path";
import { codeFrameColumns } from "@babel/code-frame";
import chalk = require("chalk");
import { Message, Result } from "../reporter";
import { Formatter } from "./formatter";
import chalk = require("chalk");
interface SourcePoint {
line: number;
column: number;
......
import stripAnsi = require("strip-ansi");
import { Severity } from "./config";
import { Token, TokenType } from "./lexer";
import "./matchers";
import { Report, Reporter } from "./reporter";
import stripAnsi = require("strip-ansi");
let reportOk: Report;
let reportError: Report;
let reportMultipleErrors: Report;
......@@ -255,7 +254,7 @@ describe("toHTMLValidate()", () => {
it("should support jsdom", () => {
expect.assertions(2);
/* eslint-disable-next-line @typescript-eslint/ban-ts-ignore */
/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
// @ts-ignore DOM library not available
const doc = document;
......
/* eslint-disable @typescript-eslint/no-namespace, @typescript-eslint/ban-ts-ignore, prefer-template, sonarjs/no-duplicate-string */
/* eslint-disable @typescript-eslint/no-namespace, @typescript-eslint/ban-ts-comment, prefer-template, sonarjs/no-duplicate-string */
import diff from "jest-diff";
import deepmerge from "deepmerge";
......@@ -15,12 +15,15 @@ interface TokenMatcher {
declare global {
namespace jest {
/* eslint-disable-next-line @typescript-eslint/ban-types */
interface Matchers<R, T = {}> {
toBeValid(): R;
toBeInvalid(): R;
toBeToken(expected: TokenMatcher): R;
toHaveError(ruleId: string, message: string, context?: any): R;
toHaveErrors(errors: Array<[string, string] | {}>): R;
toHaveErrors(
errors: Array<[string, string] | Record<string, unknown>>
): R;
/**
* Validate string or HTMLElement.
......@@ -114,7 +117,7 @@ function toHaveError(
function toHaveErrors(
this: jest.MatcherUtils,
report: Report,
errors: Array<[string, string] | {}>
errors: Array<[string, string] | Record<string, unknown>>
): jest.CustomMatcherResult {
const actual = flattenMessages(report);
const matcher = errors.map((entry) => {
......
......@@ -73,7 +73,10 @@ const ajvRegexpKeyword: Ajv.KeywordDefinition = {
export class MetaTable {
public readonly elements: ElementTable;
private schema: object;
/* jsonMergePatch.apply returns Object */
/* eslint-disable-next-line @typescript-eslint/ban-types */
private schema: Object;
public constructor() {
this.elements = {};
......
......@@ -569,6 +569,7 @@ export class Parser {
public trigger(event: "whitespace", data: WhitespaceEvent): void;
public trigger(event: "conditional", data: ConditionalEvent): void;
public trigger(event: "directive", data: DirectiveEvent): void;
/* eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types */
public trigger(event: any, data: any): void {
if (typeof data.location === "undefined") {
throw Error("Triggered event must contain location");
......
......@@ -5,8 +5,8 @@ import { RuleConstructor } from "../rule";
import { Transformer } from "../transform";
export interface SchemaValidationPatch {
properties?: object;
definitions?: object;
properties?: Record<string, unknown>;
definitions?: Record<string, unknown>;
}
export interface Plugin {
......
......@@ -210,6 +210,7 @@ export abstract class Rule<ContextType = void, OptionsType = void> {
callback: (event: ConditionalEvent) => void
): void;
public on(event: "*", callback: (event: Event) => void): void;
/* eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types */
public on(event: string, callback: any): void {
this.parser.on(event, (event: string, data: any) => {
if (this.isEnabled()) {
......
......@@ -10,34 +10,48 @@ describe("rule no-trailing-whitespace", () => {
});
});
it("should not report when there is no trailing whitespace", () => {
expect.assertions(1);
const report = htmlvalidate.validateString("<div>\n foo\n</div>");
expect(report).toBeValid();
});
describe.each`
newline | description
${"\n"} | ${"LR"}
${"\r\n"} | ${"CRLF"}
`("$description", ({ newline }) => {
it("should not report when there is no trailing whitespace", () => {
expect.assertions(1);
const report = htmlvalidate.validateString(
`<div>${newline} foo${newline}</div>`
);
expect(report).toBeValid();
});
it("should report error when tag have trailing whitespace", () => {
expect.assertions(2);
const report = htmlvalidate.validateString("<p> \n</p>");
expect(report).toBeInvalid();
expect(report).toHaveError("no-trailing-whitespace", "Trailing whitespace");
});
it("should report error when tag have trailing whitespace", () => {
expect.assertions(2);
const report = htmlvalidate.validateString(`<p> ${newline}</p>`);
expect(report).toBeInvalid();
expect(report).toHaveError(
"no-trailing-whitespace",
"Trailing whitespace"
);
});
it("should report error when empty line have trailing whitespace", () => {
expect.assertions(2);
const report = htmlvalidate.validateString("<p>\n \n</p>");
expect(report).toBeInvalid();
expect(report).toHaveError("no-trailing-whitespace", "Trailing whitespace");
});
it("should report error when empty line have trailing whitespace", () => {
expect.assertions(2);
const report = htmlvalidate.validateString(`<p>${newline} \n</p>`);
expect(report).toBeInvalid();
expect(report).toHaveError(
"no-trailing-whitespace",
"Trailing whitespace"
);
});
it("should report error for both tabs and spaces", () => {
expect.assertions(2);
const report = htmlvalidate.validateString("<p>\n \n\t\n</p>");
expect(report).toBeInvalid();
expect(report).toHaveErrors([
["no-trailing-whitespace", "Trailing whitespace"],
["no-trailing-whitespace", "Trailing whitespace"],
]);
it("should report error for both tabs and spaces", () => {
expect.assertions(2);
const report = htmlvalidate.validateString("<p>\n \n\t\n</p>");
expect(report).toBeInvalid();
expect(report).toHaveErrors([
["no-trailing-whitespace", "Trailing whitespace"],
["no-trailing-whitespace", "Trailing whitespace"],
]);
});
});
it("smoketest", () => {
......
......@@ -12,7 +12,7 @@ export default class NoTrailingWhitespace extends Rule {
public setup(): void {
this.on("whitespace", (event: WhitespaceEvent) => {
if (event.text.match(/^[ \t]+\n$/)) {
if (event.text.match(/^[ \t]+\r?\n$/)) {
this.report(undefined, "Trailing whitespace", event.location);
}
});
......