Commits (29)
......@@ -124,10 +124,10 @@ Release:
only:
- web
variables:
GIT_AUTHOR_NAME: "David Sveningsson"
GIT_AUTHOR_EMAIL: "ext@sidvind.com"
GIT_COMMITTER_NAME: "David Sveningsson"
GIT_COMMITTER_EMAIL: "ext@sidvind.com"
GIT_AUTHOR_NAME: ${GITLAB_USER_NAME}
GIT_AUTHOR_EMAIL: ${GITLAB_USER_EMAIL}
GIT_COMMITTER_NAME: ${HTML_VALIDATE_BOT_NAME}
GIT_COMMITTER_EMAIL: ${HTML_VALIDATE_BOT_EMAIL}
script:
- npm run semantic-release
......@@ -162,6 +162,10 @@ html-validate-vue:
<<: *downstream
trigger: html-validate/html-validate-vue
html-validate-vue-webpack-plugin:
<<: *downstream
trigger: html-validate/html-validate-vue
protractor-html-validate:
<<: *downstream
trigger: html-validate/protractor-html-validate
......
......@@ -17,6 +17,7 @@
# will contain errors
/test-files/**/*.html
/tests/**/*.html
# local test files
/sites
# html-validate changelog
## [2.20.1](https://gitlab.com/html-validate/html-validate/compare/v2.20.0...v2.20.1) (2020-04-19)
### Bug Fixes
- handle loading js-files via `extends` again ([e29987f](https://gitlab.com/html-validate/html-validate/commit/e29987f213a1f295751c285c582209047c68bc2b))
# [2.20.0](https://gitlab.com/html-validate/html-validate/compare/v2.19.0...v2.20.0) (2020-04-05)
### Bug Fixes
......
......@@ -10,12 +10,18 @@ module.exports = function (grunt) {
const Dgeni = require("dgeni");
const done = this.async();
const dgeni = new Dgeni([require("./docs/dgeni")]);
dgeni
.generate()
.then(done)
.catch(() => {
grunt.fatal("Dgeni failed to generate docs");
});
try {
dgeni
.generate()
.then(done)
.catch(() => {
grunt.fatal("Dgeni failed to generate docs");
});
} catch (err) {
/* eslint-disable-next-line no-console */
console.error(err);
grunt.fatal("Dgeni failed to generate docs");
}
});
grunt.registerTask("docs", "Build documentation app", [
......
This diff is collapsed.
{
"name": "html-validate",
"version": "2.20.0",
"version": "2.20.1",
"description": "html linter",
"keywords": [
"html",
......@@ -66,7 +66,7 @@
}
},
"lint-staged": {
"*.{ts,js,json,md,scss}": "prettier --write"
"*.{ts,js,json,html,md,scss}": "prettier --write"
},
"prettier": "@html-validate/prettier-config",
"renovate": {
......@@ -93,12 +93,13 @@
},
"devDependencies": {
"@babel/core": "7.9.0",
"@babel/preset-env": "7.9.0",
"@babel/preset-env": "7.9.5",
"@commitlint/cli": "8.3.5",
"@html-validate/commitlint-config": "1.0.3",
"@html-validate/eslint-config": "1.2.1",
"@html-validate/eslint-config": "1.2.3",
"@html-validate/jest-config": "1.0.1",
"@html-validate/prettier-config": "1.0.1",
"@html-validate/semantic-release-config": "1.0.14",
"@html-validate/semantic-release-config": "1.0.16",
"@types/babel__code-frame": "7.0.1",
"@types/estree": "0.0.44",
"@types/glob": "7.1.1",
......@@ -106,8 +107,8 @@
"@types/jest": "25.2.1",
"@types/json-merge-patch": "0.0.4",
"@types/minimist": "1.2.0",
"@types/node": "11.15.9",
"autoprefixer": "9.7.5",
"@types/node": "11.15.11",
"autoprefixer": "9.7.6",
"babelify": "10.0.0",
"bootstrap-sass": "3.4.1",
"canonical-path": "1.0.0",
......@@ -115,7 +116,7 @@
"dgeni": "0.4.12",
"dgeni-front-matter": "1.0.2",
"dgeni-packages": "0.28.3",
"eslint-plugin-array-func": "3.1.4",
"eslint-plugin-array-func": "3.1.5",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-security": "1.4.0",
"eslint-plugin-sonarjs": "0.5.0",
......@@ -129,26 +130,25 @@
"grunt-postcss": "0.9.0",
"grunt-sass": "3.1.0",
"highlight.js": "9.18.1",
"husky": "4.2.3",
"jest": "25.2.7",
"jest-diff": "25.2.6",
"jest-junit": "10.0.0",
"jquery": "3.4.1",
"lint-staged": "10.1.1",
"husky": "4.2.5",
"jest": "25.3.0",
"jest-diff": "25.3.0",
"jquery": "3.5.0",
"lint-staged": "10.1.3",
"load-grunt-tasks": "5.1.0",
"marked": "0.8.2",
"minimatch": "3.0.4",
"prettier": "2.0.2",
"prettier": "2.0.4",
"sass": "1.26.3",
"semantic-release": "17.0.4",
"semantic-release": "17.0.6",
"serve-static": "1.14.1",
"stringmap": "0.2.2",
"strip-ansi": "6.0.0",
"ts-jest": "25.3.1",
"ts-jest": "25.4.0",
"typescript": "3.8.3"
},
"jest": {
"collectCoverage": true,
"preset": "@html-validate/jest-config",
"collectCoverageFrom": [
"src/**/*.ts",
"!src/**/*.spec.ts",
......@@ -156,21 +156,6 @@
"!src/shim.ts",
"!src/cli/html-validate.ts"
],
"coverageReporters": [
"text",
"text-summary",
"html"
],
"reporters": [
"default",
[
"jest-junit",
{
"outputDirectory": "temp",
"outputName": "./jest.xml"
}
]
],
"roots": [
"<rootDir>/docs",
"<rootDir>/elements",
......@@ -179,14 +164,6 @@
],
"snapshotSerializers": [
"pretty-format/build/plugins/ConvertAnsi"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.+|(\\.|/)(test|spec))\\.tsx?$",
"moduleFileExtensions": [
"ts",
"js"
]
}
}
......@@ -12,6 +12,7 @@ npm test
echo Run linting
npm run eslint -- --max-warnings 0
npm run prettier:check
echo Lint documentation
npm run htmlvalidate -- --config docs/htmlvalidate-templates.json 'docs/**/*.html'
......
......@@ -109,29 +109,29 @@ function handleValidationError(err: SchemaValidationError): void {
} else {
console.log(chalk.red(`A configuration error was found:`));
}
if (console.group) console.group();
console.group();
{
console.log(err.prettyError());
}
if (console.group) console.groupEnd();
console.groupEnd();
}
function handleUserError(err: UserError): void {
console.error(chalk.red("Caught exception:"));
if (console.group) console.group();
console.group();
{
console.error(err);
}
if (console.group) console.groupEnd();
console.groupEnd();
}
function handleUnknownError(err: Error): void {
console.error(chalk.red("Caught exception:"));
if (console.group) console.group();
console.group();
{
console.error(err);
}
if (console.group) console.groupEnd();
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(
......
......@@ -183,9 +183,31 @@ describe("config", () => {
});
describe("fromFile()", () => {
it("should support JSON", () => {
const fileDir = path.resolve(__dirname, "../../test-files");
it("should support JSON file", () => {
expect.assertions(1);
const config = Config.fromFile(path.join(fileDir, "config.json"));
expect(Array.from(config.getRules().entries())).toEqual([
["foo", [Severity.ERROR, {}]],
["bar", [Severity.WARN, {}]],
["baz", [Severity.DISABLED, {}]],
]);
});
it("should support js file", () => {
expect.assertions(1);
const config = Config.fromFile(path.join(fileDir, "config.js"));
expect(Array.from(config.getRules().entries())).toEqual([
["foo", [Severity.ERROR, {}]],
["bar", [Severity.WARN, {}]],
["baz", [Severity.DISABLED, {}]],
]);
});
it("should support js file without extension", () => {
expect.assertions(1);
const config = Config.fromFile(`${process.cwd()}/test-files/config.json`);
const config = Config.fromFile(path.join(fileDir, "config"));
expect(Array.from(config.getRules().entries())).toEqual([
["foo", [Severity.ERROR, {}]],
["bar", [Severity.WARN, {}]],
......
......@@ -64,8 +64,12 @@ function mergeInternal(base: ConfigData, rhs: ConfigData): ConfigData {
function loadFromFile(filename: string): ConfigData {
let json;
try {
const data = fs.readFileSync(filename, "utf-8");
json = JSON.parse(data);
/* remove cached copy so we always load a fresh copy, important for editors
* which keep a long-running instance of [[HtmlValidate]] around. */
delete require.cache[require.resolve(filename)];
/* load using require as it can process both js and json */
json = require(filename); // eslint-disable-line import/no-dynamic-require
} catch (err) {
throw new ConfigError(
`Failed to read configuration from "${filename}"`,
......
......@@ -22,14 +22,14 @@ export interface Plugin {
*
* Hint: import and use the name from `package.json`.
*/
name?: string;
name?: string | null;
/**
* Initialization callback.
*
* Called once per plugin during initialization.
*/
init?: () => void;
init?: () => void | null;
/**
* Setup callback.
......@@ -40,7 +40,7 @@ export interface Plugin {
* @param eventhandler Eventhandler from parser. Can be used to listen for
* parser events.
*/
setup?: (source: Source, eventhandler: EventHandler) => void;
setup?: (source: Source, eventhandler: EventHandler) => void | null;
/**
* Configuration presets.
......@@ -51,12 +51,12 @@ export interface Plugin {
*
* "extends": ["my-plugin:foobar"]
*/
configs?: Record<string, ConfigData>;
configs?: Record<string, ConfigData | null> | null;
/**
* List of new rules present.
*/
rules?: Record<string, RuleConstructor<any, any>>;
rules?: Record<string, RuleConstructor<any, any> | null> | null;
/**
* Transformer available in this plugin.
......@@ -80,10 +80,10 @@ export interface Plugin {
* "^.*\\.foo$": "my-plugin:foobar"
* }
*/
transformer?: Transformer | Record<string, Transformer>;
transformer?: Transformer | Record<string, Transformer | null> | null;
/**
* Extend metadata validation schema.
*/
elementSchema?: SchemaValidationPatch;
elementSchema?: SchemaValidationPatch | null;
}
module.exports = {
rules: {
foo: "error",
bar: "warn",
baz: "off",
},
};
# Integration tests
Each folder contains a set of tests.
{
"elements": ["./elements.js"],
"rules": {
"deprecated": "error",
"no-unknown-elements": "error"
}
}
# Elements JS
This test ensures `elements` in `.htmlvalidate.json` can load a javascript file.
import path from "path";
import HtmlValidate from "../../src/htmlvalidate";
import "../../src/matchers";
it("should handle elements js file", () => {
expect.assertions(2);
const htmlvalidate = new HtmlValidate();
const report = htmlvalidate.validateFile(
path.join(__dirname, "my-file.html")
);
expect(report).toBeInvalid();
expect(report.results[0].messages).toMatchInlineSnapshot(`
Array [
Object {
"column": 2,
"context": Object {
"tagName": "my-element",
},
"line": 1,
"message": "<my-element> is deprecated",
"offset": 1,
"ruleId": "deprecated",
"selector": "my-element",
"severity": 2,
"size": 10,
},
]
`);
});
module.exports = {
"my-element": {
deprecated: true,
},
};
<my-element></my-element>
{
"elements": ["./elements.json"],
"rules": {
"deprecated": "error",
"no-unknown-elements": "error"
}
}
# Elements JS
This test ensures `elements` in `.htmlvalidate.json` can load a javascript file.
import path from "path";
import HtmlValidate from "../../src/htmlvalidate";
import "../../src/matchers";
it("should handle elements json file", () => {
expect.assertions(2);
const htmlvalidate = new HtmlValidate();
const report = htmlvalidate.validateFile(
path.join(__dirname, "my-file.html")
);
expect(report).toBeInvalid();
expect(report.results[0].messages).toMatchInlineSnapshot(`
Array [
Object {
"column": 2,
"context": Object {
"tagName": "my-element",
},
"line": 1,
"message": "<my-element> is deprecated",
"offset": 1,
"ruleId": "deprecated",
"selector": "my-element",
"severity": 2,
"size": 10,
},
]
`);
});
{
"my-element": {
"deprecated": true
}
}
<my-element></my-element>
{
"extends": ["./config.js"]
}
# Extend JS
This test ensures `extend` in `.htmlvalidate.json` can load a javascript file.
module.exports = {
rules: {
"close-order": "error",
},
};
import path from "path";
import HtmlValidate from "../../src/htmlvalidate";
import "../../src/matchers";
it("should handle extending js file", () => {
expect.assertions(2);
const htmlvalidate = new HtmlValidate();
const report = htmlvalidate.validateFile(
path.join(__dirname, "my-file.html")
);
expect(report).toBeInvalid();
expect(report.results[0].messages).toMatchInlineSnapshot(`
Array [
Object {
"column": 16,
"context": undefined,
"line": 1,
"message": "Mismatched close-tag, expected '</p>' but found '</i>'.",
"offset": 15,
"ruleId": "close-order",
"selector": null,
"severity": 2,
"size": 2,
},
]
`);
});
{
"extends": ["./config.json"]
}
# Extend JSON
This test ensures `extend` in `.htmlvalidate.json` can load a JSON file.
{
"rules": {
"close-order": "error"
}
}
import path from "path";
import HtmlValidate from "../../src/htmlvalidate";
import "../../src/matchers";
it("should handle extending json file", () => {
expect.assertions(2);
const htmlvalidate = new HtmlValidate();
const report = htmlvalidate.validateFile(
path.join(__dirname, "my-file.html")
);
expect(report).toBeInvalid();
expect(report.results[0].messages).toMatchInlineSnapshot(`
Array [
Object {
"column": 16,
"context": undefined,
"line": 1,
"message": "Mismatched close-tag, expected '</p>' but found '</i>'.",
"offset": 15,
"ruleId": "close-order",
"selector": null,
"severity": 2,
"size": 2,
},
]
`);
});
# Jest
These tests ensures jest matchers work properly.
import "../../../src/matchers";
import "../../src/matchers";
it("should validate ok", () => {
expect.assertions(1);
......