...
 
Commits (11)
# html-validate changelog
# [2.6.0](https://gitlab.com/html-validate/html-validate/compare/v2.5.0...v2.6.0) (2019-12-12)
### Bug Fixes
- **cli:** useful error message when metadata is invalid ([165da72](https://gitlab.com/html-validate/html-validate/commit/165da729ade4f64a946b83f6cd8b57a69186f51d))
- **elements:** allow `requiredAttributes` and others to be empty array ([244d038](https://gitlab.com/html-validate/html-validate/commit/244d0384ca62a5f73985116699690dd87e3fbea1)), closes [#59](https://gitlab.com/html-validate/html-validate/issues/59)
- **error:** better schema validation error ([9a5f8fe](https://gitlab.com/html-validate/html-validate/commit/9a5f8fe0a6d7fddd53e1002c028fd0218febfede))
### Features
- **lexer:** handle rudimentary template tags such as `<% .. %>` ([a0f6190](https://gitlab.com/html-validate/html-validate/commit/a0f619045642fabac73d6fff6a1d832f37fdc075))
# [2.5.0](https://gitlab.com/html-validate/html-validate/compare/v2.4.3...v2.5.0) (2019-12-09)
### Bug Fixes
......
......@@ -21,17 +21,17 @@
"foreign": { "type": "boolean" },
"void": { "type": "boolean" },
"transparent": { "type": "boolean" },
"implicitClosed": { "type": "array", "contains": { "type": "string" } },
"implicitClosed": { "type": "array", "items": { "type": "string" } },
"scriptSupporting": { "type": "boolean" },
"form": { "type": "boolean" },
"deprecatedAttributes": {
"type": "array",
"contains": { "type": "string" }
"items": { "type": "string" }
},
"requiredAttributes": {
"type": "array",
"contains": { "type": "string" }
"items": { "type": "string" }
},
"attributes": { "$ref": "#/definitions/PermittedAttribute" },
......
{
"name": "html-validate",
"version": "2.5.0",
"version": "2.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -4629,12 +4629,12 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz",
"integrity": "sha512-rT51fNLW0u3fnDGnAHVC5nu+Das+y2CpW10yqvf6/j5xbuUV3FxA3mBaIbM24CXODXjbgUznNb4Kg9XZOUxKAw==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.11.0.tgz",
"integrity": "sha512-G2HHA1vpMN0EEbUuWubiCCfd0R3a30BB+UdvnFkxwZIxYEGOrWEXDv8tBFO9f44CWc47Xv9lLM3VSn4ORLI2bA==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "2.10.0",
"@typescript-eslint/experimental-utils": "2.11.0",
"eslint-utils": "^1.4.3",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
......@@ -4642,20 +4642,20 @@
},
"dependencies": {
"@typescript-eslint/experimental-utils": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz",
"integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.11.0.tgz",
"integrity": "sha512-YxcA/y0ZJaCc/fB/MClhcDxHI0nOBB7v2/WxBju2cOTanX7jO9ttQq6Fy4yW9UaY5bPd9xL3cun3lDVqk67sPQ==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "2.10.0",
"@typescript-eslint/typescript-estree": "2.11.0",
"eslint-scope": "^5.0.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.10.0.tgz",
"integrity": "sha512-oOYnplddQNm/LGVkqbkAwx4TIBuuZ36cAQq9v3nFIU9FmhemHuVzAesMSXNQDdAzCa5bFgCrfD3JWhYVKlRN2g==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.11.0.tgz",
"integrity": "sha512-HGY4+d4MagO6cKMcKfIKaTMxcAv7dEVnji2Zi+vi5VV8uWAM631KjAB5GxFcexMYrwKT0EekRiiGK1/Sd7VFGA==",
"dev": true,
"requires": {
"debug": "^4.1.1",
......@@ -4759,32 +4759,32 @@
}
},
"@typescript-eslint/parser": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.10.0.tgz",
"integrity": "sha512-wQNiBokcP5ZsTuB+i4BlmVWq6o+oAhd8en2eSm/EE9m7BgZUIfEeYFd6z3S+T7bgNuloeiHA1/cevvbBDLr98g==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.11.0.tgz",
"integrity": "sha512-DyGXeqhb3moMioEFZIHIp7oXBBh7dEfPTzGrlyP0Mi9ScCra4SWEGs3kPd18mG7Sy9Wy8z88zmrw5tSGL6r/6A==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "2.10.0",
"@typescript-eslint/typescript-estree": "2.10.0",
"@typescript-eslint/experimental-utils": "2.11.0",
"@typescript-eslint/typescript-estree": "2.11.0",
"eslint-visitor-keys": "^1.1.0"
},
"dependencies": {
"@typescript-eslint/experimental-utils": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz",
"integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.11.0.tgz",
"integrity": "sha512-YxcA/y0ZJaCc/fB/MClhcDxHI0nOBB7v2/WxBju2cOTanX7jO9ttQq6Fy4yW9UaY5bPd9xL3cun3lDVqk67sPQ==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "2.10.0",
"@typescript-eslint/typescript-estree": "2.11.0",
"eslint-scope": "^5.0.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.10.0.tgz",
"integrity": "sha512-oOYnplddQNm/LGVkqbkAwx4TIBuuZ36cAQq9v3nFIU9FmhemHuVzAesMSXNQDdAzCa5bFgCrfD3JWhYVKlRN2g==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.11.0.tgz",
"integrity": "sha512-HGY4+d4MagO6cKMcKfIKaTMxcAv7dEVnji2Zi+vi5VV8uWAM631KjAB5GxFcexMYrwKT0EekRiiGK1/Sd7VFGA==",
"dev": true,
"requires": {
"debug": "^4.1.1",
......@@ -5502,6 +5502,60 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
"array.prototype.flat": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.2.tgz",
"integrity": "sha512-VXjh7lAL4KXKF2hY4FnEW9eRW6IhdvFW1sN/JwLbmECbCgACCnBHNyP3lFiYuttr0jxRN9Bsc5+G27dMseSWqQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.15.0",
"function-bind": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.16.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz",
"integrity": "sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.1.4",
"is-regex": "^1.0.4",
"object-inspect": "^1.7.0",
"object-keys": "^1.1.1",
"string.prototype.trimleft": "^2.1.0",
"string.prototype.trimright": "^2.1.0"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
}
}
},
"arrify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
......@@ -9258,12 +9312,12 @@
}
},
"eslint-module-utils": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
"integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.0.tgz",
"integrity": "sha512-kCo8pZaNz2dsAW7nCUjuVoI11EBXXpIzfNxmaoLhXoRDOnqXLC4iSGVRdZPhOitfbdEfMEfKOiENaK6wDPZEGw==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"debug": "^2.6.9",
"pkg-dir": "^2.0.0"
},
"dependencies": {
......@@ -9361,22 +9415,23 @@
}
},
"eslint-plugin-import": {
"version": "2.18.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz",
"integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==",
"version": "2.19.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz",
"integrity": "sha512-x68131aKoCZlCae7rDXKSAQmbT5DQuManyXo2sK6fJJ0aK5CWAkv6A6HJZGgqC8IhjQxYPgo6/IY4Oz8AFsbBw==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
"array.prototype.flat": "^1.2.1",
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.4.0",
"eslint-module-utils": "^2.4.1",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.0",
"read-pkg-up": "^2.0.0",
"resolve": "^1.11.0"
"resolve": "^1.12.0"
},
"dependencies": {
"doctrine": {
......@@ -9487,9 +9542,9 @@
}
},
"resolve": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
"integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz",
"integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
......@@ -11753,10 +11808,27 @@
"dev": true
},
"highlight.js": {
"version": "9.16.2",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.16.2.tgz",
"integrity": "sha512-feMUrVLZvjy0oC7FVJQcSQRqbBq9kwqnYE4+Kj9ZjbHh3g+BisiPgF49NyQbVLNdrL/qqZr3Ca9yOKwgn2i/tw==",
"dev": true
"version": "9.17.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.17.0.tgz",
"integrity": "sha512-PyO7FK7z8ZC7FqBlmAxm4d+1DYaoS6+uaxt9KGkyP1AnmGRLnWmNod1yp9BFjUyHoDF00k+V57gF6X9ifY7f/A==",
"dev": true,
"requires": {
"handlebars": "^4.5.3"
},
"dependencies": {
"handlebars": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
"dev": true,
"requires": {
"neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4"
}
}
}
},
"hmac-drbg": {
"version": "1.0.1",
......@@ -20423,6 +20495,12 @@
}
}
},
"object-inspect": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"dev": true
},
"object-keys": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
......@@ -25326,6 +25404,26 @@
}
}
},
"string.prototype.trimleft": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
"integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
}
},
"string.prototype.trimright": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
"integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
......
{
"name": "html-validate",
"version": "2.5.0",
"version": "2.6.0",
"description": "html linter",
"keywords": [
"html",
......@@ -41,6 +41,7 @@
"build:docs": "grunt docs",
"clean": "rm -rf build public",
"commitlint": "commitlint",
"debug": "node --inspect ./node_modules/.bin/jest --runInBand --watch --no-coverage",
"eslint": "eslint *.js '{docs,elements,src}/**/*.{js,ts}'",
"eslint:fix": "eslint --fix *.js '{docs,elements,src}/**/*.{js,ts}'",
"htmlvalidate": "./bin/html-validate.js",
......@@ -161,8 +162,8 @@
"@types/json-merge-patch": "0.0.4",
"@types/minimist": "1.2.0",
"@types/node": "11.15.3",
"@typescript-eslint/eslint-plugin": "2.10.0",
"@typescript-eslint/parser": "2.10.0",
"@typescript-eslint/eslint-plugin": "2.11.0",
"@typescript-eslint/parser": "2.11.0",
"autoprefixer": "9.7.3",
"babelify": "10.0.0",
"bootstrap-sass": "3.4.1",
......@@ -173,7 +174,7 @@
"eslint-config-prettier": "6.7.0",
"eslint-config-sidvind": "1.3.2",
"eslint-plugin-array-func": "3.1.3",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-import": "2.19.1",
"eslint-plugin-jest": "23.1.1",
"eslint-plugin-node": "10.0.0",
"eslint-plugin-prettier": "3.1.1",
......@@ -187,7 +188,7 @@
"grunt-contrib-copy": "1.0.0",
"grunt-postcss": "0.9.0",
"grunt-sass": "3.1.0",
"highlight.js": "9.16.2",
"highlight.js": "9.17.0",
"husky": "3.1.0",
"jest": "24.9.0",
"jest-diff": "24.9.0",
......
/* eslint-disable no-console, no-process-exit, sonarjs/no-duplicate-string */
import { TokenDump } from "../engine";
import { SchemaValidationError } from "../error";
import { UserError } from "../error/user-error";
import { Report, Reporter, Result } from "../reporter";
import { eventFormatter } from "./json";
......@@ -8,6 +9,7 @@ const pkg = require("../../package.json");
import chalk from "chalk";
import minimist from "minimist";
import path from "path";
import { CLI } from "./cli";
enum Mode {
......@@ -100,6 +102,45 @@ function renameStdin(report: Report, filename: string): void {
}
}
function handleValidationError(err: SchemaValidationError): void {
const filename = path.relative(process.cwd(), err.filename);
console.log(chalk.red(`A configuration error was found in "${filename}":`));
if (console.group) console.group();
{
console.log(err.prettyError());
}
if (console.group) console.groupEnd();
}
function handleUserError(err: UserError): void {
console.error(chalk.red("Caught exception:"));
if (console.group) console.group();
{
console.error(err);
}
if (console.group) console.groupEnd();
}
function handleUnknownError(err: Error): void {
console.error(chalk.red("Caught exception:"));
if (console.group) console.group();
{
console.error(err);
}
if (console.group) console.groupEnd();
const bugUrl = `${pkg.bugs.url}?issuable_template=Bug`;
console.error(chalk.red(`This is a bug in ${pkg.name}-${pkg.version}.`));
console.error(
chalk.red(
[
`Please file a bug at ${bugUrl}`,
`and include this message in full and if possible the content of the`,
`file being parsed (or a reduced testcase).`,
].join("\n")
)
);
}
const argv: minimist.ParsedArgs = minimist(process.argv.slice(2), {
string: [
"c",
......@@ -263,20 +304,12 @@ try {
process.exit(0);
}
} catch (err) {
console.error(chalk.red("Caught exception:"));
if (console.group) console.group();
{
console.error(err);
}
if (console.group) console.groupEnd();
if (!(err instanceof UserError)) {
const bugUrl = `${pkg.bugs.url}?issuable_template=Bug`;
console.error(chalk.red(`This is a bug in ${pkg.name}-${pkg.version}.`));
console.error(
chalk.red(
`Please file a bug at ${bugUrl}\nand include this message in full and if possible the content of the\nfile being parsed (or a reduced testcase).`
)
);
if (err instanceof SchemaValidationError) {
handleValidationError(err);
} else if (err instanceof UserError) {
handleUserError(err);
} else {
handleUnknownError(err);
}
process.exit(1);
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MetaValidationError should pretty-print validation errors 1`] = `
exports[`SchemaValidationError should pretty-print validation errors 1`] = `
"TYPE should be boolean
1 | {
......
export { UserError } from "./user-error";
export { NestedError } from "./nested-error";
export { SchemaValidationError } from "./schema-validation-error";
import stripAnsi = require("strip-ansi");
import { MetaElement } from "./element";
import { MetaTable } from "./table";
import { MetaValidationError } from "./validation-error";
import { MetaElement } from "../meta/element";
import { MetaTable } from "../meta/table";
import { SchemaValidationError } from "./schema-validation-error";
it("MetaValidationError should pretty-print validation errors ", () => {
it("SchemaValidationError should pretty-print validation errors ", () => {
expect.assertions(1);
const table = new MetaTable();
try {
......@@ -13,7 +13,7 @@ it("MetaValidationError should pretty-print validation errors ", () => {
} as unknown) as MetaElement,
});
} catch (err) {
if (err instanceof MetaValidationError) {
if (err instanceof SchemaValidationError) {
const output = (err.prettyError() as unknown) as string;
/* cannot test prettyError() method with builtin helpers */
......
import Ajv from "ajv";
import betterAjvErrors from "better-ajv-errors";
import { UserError } from "../error/user-error";
import { MetaDataTable } from "./element";
export class MetaValidationError extends UserError {
private obj: MetaDataTable;
export class SchemaValidationError extends UserError {
public filename: string | null;
private obj: any;
private schema: any;
private errors: Ajv.ErrorObject[];
public constructor(
filename: string | null,
message: string,
obj: MetaDataTable,
obj: any,
schema: any,
errors: Ajv.ErrorObject[]
) {
super(message);
this.filename = filename;
this.obj = obj;
this.schema = schema;
this.errors = errors;
......
......@@ -674,4 +674,20 @@ describe("lexer", () => {
expect(token.next()).toBeToken({ type: TokenType.EOF });
});
});
describe("should not choke on templating", () => {
it.each`
input
${"<% ... %>"}
${"<? ... ?>"}
${"<$ ... $>"}
`("$input", ({ input }) => {
const token = lexer.tokenize(inlineSource(input));
expect(token.next()).toBeToken({
type: TokenType.TEMPLATING,
data: [input],
});
expect(token.next()).toBeToken({ type: TokenType.EOF });
});
});
});
......@@ -24,6 +24,7 @@ const MATCH_XML_TAG = /^<\?xml.*?\?>\n/;
const MATCH_TAG_OPEN = /^<(\/?)([a-zA-Z0-9\-:]+)/; // https://www.w3.org/TR/html/syntax.html#start-tags
const MATCH_TAG_CLOSE = /^\/?>/;
const MATCH_TEXT = /^[^]*?(?=(?:[ \t]*(?:\r\n|\r|\n)|<[^ ]|$))/;
const MATCH_TEMPLATING = /^(?:<%.*?%>|<\?.*?\?>|<\$.*?\$>)/;
const MATCH_TAG_LOOKAHEAD = /^[^]*?(?=<|$)/;
const MATCH_ATTR_START = /^([^\t\r\n\f \/><"'=]+)/; // https://www.w3.org/TR/html/syntax.html#elements-attributes
const MATCH_ATTR_SINGLE = /^\s*=\s*'([^']*?)(')/;
......@@ -266,6 +267,7 @@ export class Lexer {
[MATCH_DIRECTIVE, State.TEXT, TokenType.DIRECTIVE],
[MATCH_CONDITIONAL, State.TEXT, TokenType.CONDITIONAL],
[MATCH_COMMENT, State.TEXT, TokenType.COMMENT],
[MATCH_TEMPLATING, State.TEXT, TokenType.TEMPLATING],
[MATCH_TAG_OPEN, State.TAG, TokenType.TAG_OPEN],
[MATCH_TEXT, State.TEXT, TokenType.TEXT],
[MATCH_TAG_LOOKAHEAD, State.TEXT, TokenType.TEXT],
......
......@@ -11,6 +11,7 @@ export enum TokenType {
ATTR_NAME,
ATTR_VALUE,
TEXT,
TEMPLATING,
SCRIPT,
COMMENT,
CONDITIONAL,
......
......@@ -3,7 +3,7 @@ import betterAjvErrors from "better-ajv-errors";
import deepmerge from "deepmerge";
import jsonMergePatch from "json-merge-patch";
import { HtmlElement } from "../dom";
import { UserError } from "../error/user-error";
import { SchemaValidationError, UserError } from "../error";
import { SchemaValidationPatch } from "../plugin";
import {
ElementTable,
......@@ -13,7 +13,6 @@ import {
MetaLookupableProperty,
PropertyExpression,
} from "./element";
import { MetaValidationError } from "./validation-error";
const dynamicKeys = [
"metadata",
......@@ -73,7 +72,10 @@ export class MetaTable {
/**
* Load metadata table from object.
*/
public loadFromObject(obj: MetaDataTable): void {
public loadFromObject(
obj: MetaDataTable,
filename: string | null = null
): void {
const ajv = new Ajv({ jsonPointers: true });
const validator = ajv.compile(this.schema);
const valid = validator(obj);
......@@ -82,7 +84,8 @@ export class MetaTable {
format: "js",
}) as any;
const message = output[0].error;
throw new MetaValidationError(
throw new SchemaValidationError(
filename,
`Element metadata is not valid: ${message}`,
obj,
this.schema,
......@@ -108,7 +111,7 @@ export class MetaTable {
err
);
}
this.loadFromObject(clone(json));
this.loadFromObject(clone(json), filename);
}
public getMetaFor(tagName: string): MetaElement {
......
......@@ -281,6 +281,21 @@ describe("parser", () => {
});
expect(events.shift()).toBeUndefined();
});
describe("templating as text", () => {
it.each`
input
${"<% ... %>"}
${"<? ... ?>"}
${"<$ ... $>"}
`("$input", ({ input }) => {
expect.assertions(1);
const text = `lorem ${input} ipsum`;
const doc = parser.parseHtml(`<p>${text}</p>`);
const element = doc.querySelector("p");
expect(element.textContent).toEqual(text);
});
});
});
describe("should fail on", () => {
......
......@@ -114,6 +114,7 @@ export class Parser {
break;
case TokenType.TEXT:
case TokenType.TEMPLATING:
this.appendText(token.data, token.location);
break;
......
......@@ -118,6 +118,21 @@ describe("rule no-raw-characters", () => {
});
});
describe("should not report templating", () => {
it.each`
input
${"<% ... %>"}
${"<? ... ?>"}
${"<$ ... $>"}
`("$input", ({ input }) => {
expect.assertions(1);
const report = htmlvalidate.validateString(
`<p>lorem ${input} ipsum</p>`
);
expect(report).toBeValid();
});
});
it("smoketest", () => {
const report = htmlvalidate.validateFile(
"test-files/rules/no-raw-characters.html"
......
......@@ -9,6 +9,7 @@ const defaults = {
const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
const unquotedAttrRegexp = /([<>"'=`]|&(?![a-zA-Z0-9#]+;))/g;
const matchTemplate = /^(<%.*?%>|<\?.*?\?>|<\$.*?\$>)$/;
const replacementTable: Map<string, string> = new Map([
['"', "&quot;"],
......@@ -44,6 +45,12 @@ class NoRawCharacters extends Rule {
if (child.nodeType !== NodeType.TEXT_NODE) {
continue;
}
/* workaround for templating <% ... %> etc */
if (child.textContent.match(matchTemplate)) {
continue;
}
this.findRawChars(child.textContent, child.location, textRegexp);
}
});
......