...
 
Commits (11)
# html-validate changelog
# [2.5.0](https://gitlab.com/html-validate/html-validate/compare/v2.4.3...v2.5.0) (2019-12-09)
### Bug Fixes
- **config:** keep track of plugin original name ([9e7ea3e](https://gitlab.com/html-validate/html-validate/commit/9e7ea3e2b36cc71c5e098004bd6e1d232b413ca7))
- **config:** throw error is plugin is missing ([bc61a6b](https://gitlab.com/html-validate/html-validate/commit/bc61a6be2684a53c1704edc62e85a401ca08c1f0))
- **htmlvalidate:** more verbose output from `--dump-source` ([f0089c6](https://gitlab.com/html-validate/html-validate/commit/f0089c68e851f85f873a0b6d741d8b36520a26ee))
- **htmlvalidate:** prefer html-validate:recommended ([8deb03a](https://gitlab.com/html-validate/html-validate/commit/8deb03a246c38afb790aff7c01db602e121baefe))
### Features
- **htmlvalidate:** new method `canValidate` to test if a file can be validated ([f523028](https://gitlab.com/html-validate/html-validate/commit/f5230285017acf3f83838c3f36293d8f5545082d))
## [2.4.3](https://gitlab.com/html-validate/html-validate/compare/v2.4.2...v2.4.3) (2019-12-08)
### Bug Fixes
......
......@@ -26,7 +26,7 @@ markup["frontpage-components"] = `<my-inline>
describe("docs/index.md", () => {
it("inline validation: frontpage-contentmodel", () => {
const htmlvalidate = new HtmlValidate({"extends":["htmlvalidate:recommended"]});
const htmlvalidate = new HtmlValidate({"extends":["html-validate:recommended"]});
const report = htmlvalidate.validateString(markup["frontpage-contentmodel"]);
expect(report.results).toMatchSnapshot();
});
......@@ -36,7 +36,7 @@ describe("docs/index.md", () => {
expect(report.results).toMatchSnapshot();
});
it("inline validation: frontpage-components", () => {
const htmlvalidate = new HtmlValidate({"elements":["html5",{"my-inline":{"phrasing":true,"permittedContent":["@phrasing"]},"my-block":{"flow":true},"my-deprecated":{"phrasing":true,"deprecated":"replaced with <my-other>"}}],"extends":["htmlvalidate:recommended"]});
const htmlvalidate = new HtmlValidate({"elements":["html5",{"my-inline":{"phrasing":true,"permittedContent":["@phrasing"]},"my-block":{"flow":true},"my-deprecated":{"phrasing":true,"deprecated":"replaced with <my-other>"}}],"extends":["html-validate:recommended"]});
const report = htmlvalidate.validateString(markup["frontpage-components"]);
expect(report.results).toMatchSnapshot();
});
......
......@@ -28,7 +28,7 @@ constructor:
```typescript
const htmlvalidate = new HtmlValidate({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
});
```
......
......@@ -106,7 +106,7 @@ module.exports = function parseValidatesProcessor(
return dst;
}, {});
} else {
config.extends = ["htmlvalidate:recommended"];
config.extends = ["html-validate:recommended"];
}
return config;
}
......
{
"extends": ["htmlvalidate:recommended", "htmlvalidate:document"],
"extends": ["html-validate:recommended", "html-validate:document"],
"rules": {
"no-trailing-whitespace": "off",
"require-sri": "off"
......
{
"extends": ["htmlvalidate:recommended"]
"extends": ["html-validate:recommended"]
}
......@@ -4,6 +4,6 @@
@description
Rules with <span class="fa fa-check"></span> are enabled by
`htmlvalidate:recommended`.<br>
`html-validate:recommended`.<br>
Rules with <span class="fa fa-file-text-o"></span> are enabled by
`htmlvalidate:document`.
`html-validate:document`.
......@@ -18,7 +18,7 @@ Create `.htmlvalidate.json`:
```js
{
"extends": [
"htmlvalidate:recommended"
"html-validate:recommended"
],
}
```
......
......@@ -102,7 +102,7 @@ textual description of the content. E.g. it cannot suggest to use `<abbr>` or
</tr>
<tr>
<td class="table-right">H44</td>
<td>Using label elements to associate text labels with form controls.<em>Use {@link rule:input-missing-label} to validate. Rule is only enabled by default in document mode (<code>htmlvalidate:document</code>).</em></td>
<td>Using label elements to associate text labels with form controls.<em>Use {@link rule:input-missing-label} to validate. Rule is only enabled by default in document mode (<code>html-validate:document</code>).</em></td>
<td class="support-yes">Yes</td>
</tr>
<tr>
......
......@@ -143,7 +143,7 @@ const tagNames = [
describe("HTML elements", () => {
const htmlvalidate = new HtmlValidate({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
elements: [
"html5",
{
......
{
"name": "html-validate",
"version": "2.4.3",
"version": "2.5.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -1919,9 +1919,9 @@
}
},
"@babel/preset-env": {
"version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.5.tgz",
"integrity": "sha512-wDPbiaZdGzsJuTWlpLHJxmwslwHGLZ8F5v69zX3oAWeTOFWdy4OJHoTKg26oAnFg052v+/LAPY5os9KB0LrOEA==",
"version": "7.7.6",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz",
"integrity": "sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.7.4",
......@@ -7653,12 +7653,12 @@
"integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A=="
},
"core-js-compat": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.7.tgz",
"integrity": "sha512-57+mgz/P/xsGdjwQYkwtBZR3LuISaxD1dEwVDtbk8xJMqAmwqaxLOvnNT7kdJ7jYE/NjNptyzXi+IQFMi/2fCw==",
"version": "3.4.8",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.8.tgz",
"integrity": "sha512-l3WTmnXHV2Sfu5VuD7EHE2w7y+K68+kULKt5RJg8ZJk3YhHF1qLD4O8v8AmNq+8vbOwnPFFDvds25/AoEvMqlQ==",
"dev": true,
"requires": {
"browserslist": "^4.8.0",
"browserslist": "^4.8.2",
"semver": "^6.3.0"
},
"dependencies": {
......
{
"name": "html-validate",
"version": "2.4.3",
"version": "2.5.0",
"description": "html linter",
"keywords": [
"html",
......@@ -144,7 +144,7 @@
},
"devDependencies": {
"@babel/core": "7.7.5",
"@babel/preset-env": "7.7.5",
"@babel/preset-env": "7.7.6",
"@commitlint/cli": "8.2.0",
"@commitlint/config-conventional": "8.2.0",
"@semantic-release/changelog": "3.0.6",
......
......@@ -6,7 +6,7 @@ exports[`should generate configuration for angularjs 1`] = `
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\"
\\"html-validate:recommended\\"
],
\\"transform\\": {
\\"^.*\\\\\\\\.js$\\": \\"html-validate-angular/js\\",
......@@ -21,7 +21,7 @@ exports[`should generate configuration for combined 1`] = `
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\",
\\"html-validate:recommended\\",
\\"html-valudate-vue:recommended\\"
],
\\"transform\\": {
......@@ -42,7 +42,7 @@ exports[`should generate configuration for default 1`] = `
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\"
\\"html-validate:recommended\\"
]
}"
`;
......@@ -53,7 +53,7 @@ exports[`should generate configuration for markdown 1`] = `
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\"
\\"html-validate:recommended\\"
],
\\"transform\\": {
\\"^.*\\\\\\\\.md$\\": \\"html-validate-markdown\\"
......@@ -67,7 +67,7 @@ exports[`should generate configuration for vuejs 1`] = `
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\",
\\"html-validate:recommended\\",
\\"html-valudate-vue:recommended\\"
],
\\"plugins\\": [
......
......@@ -16,7 +16,7 @@ describe("CLI", () => {
const cli = new CLI();
const htmlvalidate = cli.getValidator();
expect(HtmlValidate).toHaveBeenCalledWith({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
});
expect(htmlvalidate).toBeDefined();
});
......
......@@ -56,7 +56,7 @@ export async function init(cwd: string): Promise<InitResult> {
const exists = fs.existsSync(filename);
const initialConfig: ConfigData = {
elements: ["html5"],
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
};
const when = /* istanbul ignore next */ (answers: any): boolean => {
return !exists || answers.write;
......
......@@ -3,7 +3,7 @@
exports[`ConfigLoader smoketest test-files/config/directive/disable.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -59,7 +59,7 @@ Array [
exports[`ConfigLoader smoketest test-files/config/directive/disable-block.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -146,7 +146,7 @@ Should handle when root element is parent but no children
exports[`ConfigLoader smoketest test-files/config/directive/disable-multiple.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -199,7 +199,7 @@ Array [
exports[`ConfigLoader smoketest test-files/config/directive/disable-next.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -256,7 +256,7 @@ Object {
"<rootDir>/test-files/config/elements/elements.json",
],
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -312,7 +312,7 @@ Array [
exports[`ConfigLoader smoketest test-files/config/file.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -352,7 +352,7 @@ Array [
exports[`ConfigLoader smoketest test-files/config/off/error/file.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -392,7 +392,7 @@ Array [
exports[`ConfigLoader smoketest test-files/config/off/file.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......@@ -409,7 +409,7 @@ exports[`ConfigLoader smoketest test-files/config/off/file.html 2`] = `Array []`
exports[`ConfigLoader smoketest test-files/config/warn/file.html 1`] = `
Object {
"extends": Array [
"htmlvalidate:recommended",
"html-validate:recommended",
],
"plugins": Array [],
"rules": Object {
......
......@@ -66,7 +66,7 @@ describe("config", () => {
it("defaultConfig() should load defaults", () => {
const config = Config.defaultConfig();
expect(config.get()).toEqual({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
rules: expect.any(Object),
plugins: [],
transform: {},
......@@ -212,6 +212,13 @@ describe("config", () => {
);
});
it("should support html-validate:recommended", () => {
const config = Config.fromObject({
extends: ["html-validate:recommended"],
});
expect(config.getRules()).toBeDefined();
});
it("should support htmlvalidate:recommended", () => {
const config = Config.fromObject({
extends: ["htmlvalidate:recommended"],
......@@ -219,6 +226,13 @@ describe("config", () => {
expect(config.getRules()).toBeDefined();
});
it("should support html-validate:document", () => {
const config = Config.fromObject({
extends: ["html-validate:document"],
});
expect(config.getRules()).toBeDefined();
});
it("should support htmlvalidate:document", () => {
const config = Config.fromObject({
extends: ["htmlvalidate:document"],
......@@ -358,6 +372,9 @@ describe("config", () => {
"line": 1,
"offset": 0,
"originalData": "original data",
"transformedBy": Array [
"mock-transform",
],
},
]
`);
......@@ -413,6 +430,9 @@ describe("config", () => {
"filename": "foo.unnamed",
"line": 2,
"offset": 4,
"transformedBy": Array [
"mock-plugin-unnamed",
],
},
]
`);
......@@ -429,6 +449,9 @@ describe("config", () => {
"filename": "bar.named",
"line": 2,
"offset": 4,
"transformedBy": Array [
"mock-plugin-named:foobar",
],
},
]
`);
......@@ -446,6 +469,9 @@ describe("config", () => {
"line": 1,
"offset": 0,
"originalData": "original data",
"transformedBy": Array [
"mock-transform",
],
},
]
`);
......@@ -463,6 +489,17 @@ describe("config", () => {
);
});
it("should throw error if transformer refers to missing plugin", () => {
const config = Config.fromObject({
transform: {
"^.*\\.foo$": "missing-plugin:foo",
},
});
expect(() => config.init()).toThrow(
'Failed to load transformer "missing-plugin:foo": No plugin named "missing-plugin" has been loaded'
);
});
it("should return original source if no transformer is found", () => {
const config = Config.fromObject({
transform: {
......@@ -501,6 +538,10 @@ describe("config", () => {
"line": 1,
"offset": 0,
"originalData": "original data",
"transformedBy": Array [
"mock-transform",
"mock-transform-chain-foo",
],
},
]
`);
......@@ -524,6 +565,10 @@ describe("config", () => {
"line": 1,
"offset": 0,
"originalData": "original data",
"transformedBy": Array [
"mock-transform",
"mock-transform-optional-chain",
],
},
]
`);
......@@ -547,6 +592,9 @@ describe("config", () => {
"line": 1,
"offset": 0,
"originalData": "original data",
"transformedBy": Array [
"<rootDir>/src/transform/__mocks__/mock-transform",
],
},
]
`);
......@@ -623,6 +671,27 @@ describe("config", () => {
});
});
describe("canTransform()", () => {
let config: Config;
beforeEach(() => {
config = Config.fromObject({
transform: {
"^.*\\.foo$": "mock-transform",
},
});
config.init();
});
it("should return true if a transformer can handle the file", () => {
expect(config.canTransform("my-file.foo")).toBeTruthy();
});
it("should return false if no transformer can handle the file", () => {
expect(config.canTransform("my-file.bar")).toBeFalsy();
});
});
describe("init()", () => {
it("should handle being called multiple times", () => {
expect.assertions(1);
......@@ -650,7 +719,9 @@ describe("config", () => {
plugins: ["mock-plugin"],
});
config.init();
expect(config.getPlugins()).toEqual([{ name: "mock-plugin" }]);
expect(config.getPlugins()).toEqual([
expect.objectContaining({ name: "mock-plugin" }),
]);
});
});
......
......@@ -14,9 +14,18 @@ import { parseSeverity, Severity } from "./severity";
interface TransformerEntry {
pattern: RegExp;
name: string;
fn: Transformer;
}
/**
* Internal interface for a loaded plugin.
*/
interface LoadedPlugin extends Plugin {
name: string;
originalName: string;
}
const recommended = require("./recommended");
const document = require("./document");
let rootDirCache: string = null;
......@@ -78,7 +87,7 @@ export class Config {
private initialized: boolean;
protected metaTable: MetaTable;
protected plugins: Plugin[];
protected plugins: LoadedPlugin[];
protected transformers: TransformerEntry[];
protected rootDir: string;
......@@ -108,7 +117,7 @@ export class Config {
* configuration for a file to validate use `ConfigLoader.fromTarget()`.
*
* @param filename - The file to read from or one of the presets such as
* `htmlvalidate:recommended`.
* `html-validate:recommended`.
*/
public static fromFile(filename: string): Config {
const configdata = loadFromFile(filename);
......@@ -310,15 +319,16 @@ export class Config {
return this.plugins;
}
private loadPlugins(plugins: string[]): Plugin[] {
private loadPlugins(plugins: string[]): LoadedPlugin[] {
return plugins.map((moduleName: string) => {
try {
// eslint-disable-next-line security/detect-non-literal-require
const plugin = require(moduleName.replace(
"<rootDir>",
this.rootDir
)) as Plugin;
)) as LoadedPlugin;
plugin.name = plugin.name || moduleName;
plugin.originalName = moduleName;
return plugin;
} catch (err) {
throw new ConfigError(
......@@ -329,19 +339,30 @@ export class Config {
});
}
private loadConfigurations(plugins: Plugin[]): Map<string, ConfigData> {
private loadConfigurations(plugins: LoadedPlugin[]): Map<string, ConfigData> {
const configs: Map<string, ConfigData> = new Map();
/* builtin presets */
configs.set("html-validate:recommended", recommended);
configs.set("html-validate:document", document);
/* aliases for convenience */
configs.set("htmlvalidate:recommended", recommended);
configs.set("htmlvalidate:document", document);
/* presets from plugins */
for (const plugin of plugins) {
for (const [name, config] of Object.entries(plugin.configs || {})) {
configs.set(`${plugin.name}:${name}`, new Config(config).config);
/* add configuration with name provided by plugin */
configs.set(`${plugin.name}:${name}`, config);
/* add configuration with name provided by user (in config file) */
if (plugin.name !== plugin.originalName) {
configs.set(`${plugin.originalName}:${name}`, config);
}
}
}
return configs;
}
......@@ -367,7 +388,16 @@ export class Config {
};
if (transformer) {
try {
return Array.from(transformer.fn.call(context, source));
return Array.from(
transformer.fn.call(context, source),
(cur: Source) => {
/* keep track of which transformers that has been run on this source
* by appending this entry to the transformedBy array */
cur.transformedBy = cur.transformedBy || [];
cur.transformedBy.push(transformer.name);
return cur;
}
);
} catch (err) {
throw new NestedError(
`When transforming "${source.filename}": ${err.message}`,
......@@ -400,6 +430,14 @@ export class Config {
return this.transformSource(source);
}
/**
* Returns true if a transformer matches given filename.
*/
public canTransform(filename: string): boolean {
const entry = this.findTransformer(filename);
return !!entry;
}
private findTransformer(filename: string): TransformerEntry | null {
return this.transformers.find((entry: TransformerEntry) =>
entry.pattern.test(filename)
......@@ -423,6 +461,7 @@ export class Config {
// eslint-disable-next-line security/detect-non-literal-regexp
pattern: new RegExp(pattern),
name,
fn,
};
} catch (err) {
......@@ -453,6 +492,11 @@ export class Config {
if (match) {
const [, pluginName, key] = match;
const plugin = this.plugins.find(cur => cur.name === pluginName);
if (!plugin) {
throw new ConfigError(
`No plugin named "${pluginName}" has been loaded`
);
}
if (!plugin.transformer) {
throw new ConfigError(`Plugin does not expose any transformer`);
}
......
export default {
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
};
......@@ -76,4 +76,11 @@ export interface Source {
* Hooks for processing the source as it is being parsed.
*/
hooks?: SourceHooks;
/**
* Internal property to keep track of what transformers has run on this
* source. Entries are in reverse-order, e.g. the last applied transform is
* first.
*/
transformedBy?: string[];
}
......@@ -74,7 +74,7 @@ describe("Engine", () => {
beforeEach(() => {
config = Config.fromObject({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
rules: {
deprecated: "off",
},
......
......@@ -43,7 +43,7 @@ describe("HtmlValidate", () => {
const htmlvalidate = new HtmlValidate();
expect((htmlvalidate as any).globalConfig.config).toEqual(
expect.objectContaining({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
})
);
});
......@@ -236,6 +236,31 @@ describe("HtmlValidate", () => {
});
});
describe("canValidate()", () => {
let htmlvalidate: HtmlValidate;
beforeEach(() => {
htmlvalidate = new HtmlValidate({
root: true,
transform: {
"^.*\\.foo$": "mock-transform",
},
});
});
it("should return true if file extension is .html", () => {
expect(htmlvalidate.canValidate("my-file.html")).toBeTruthy();
});
it("should return true if a transformer can handle the file", () => {
expect(htmlvalidate.canValidate("my-file.foo")).toBeTruthy();
});
it("should return false if no transformer can handle the file", () => {
expect(htmlvalidate.canValidate("my-file.bar")).toBeFalsy();
});
});
it("dumpTokens() should dump tokens", () => {
const htmlvalidate = new HtmlValidate();
const filename = "foo.html";
......@@ -289,13 +314,14 @@ describe("HtmlValidate", () => {
const filename = "foo.html";
const config = Config.empty();
config.init();
config.transformFilename = jest.fn((filename: string) => [
config.transformFilename = jest.fn((filename: string): Source[] => [
{
data: `first markup`,
filename,
line: 1,
column: 1,
offset: 0,
transformedBy: ["bar", "foo"],
},
{
data: `second markup`,
......@@ -303,22 +329,43 @@ describe("HtmlValidate", () => {
line: 5,
column: 3,
offset: 29,
hooks: {
processElement: () => null,
processAttribute: null,
},
},
{
data: `third markup`,
filename,
line: 12,
column: 1,
offset: 69,
hooks: {},
},
]);
jest.spyOn(htmlvalidate, "getConfigFor").mockImplementation(() => config);
const output = htmlvalidate.dumpSource(filename);
expect(output).toMatchInlineSnapshot(`
Array [
"Source [email protected]:1",
"---",
"first markup",
"---",
"Source [email protected]:3",
"---",
"second markup",
"---",
]
`);
Array [
"Source [email protected]:1 (offset: 0)",
"Transformed by:",
" - foo",
" - bar",
"---",
"first markup",
"---",
"Source [email protected]:3 (offset: 29)",
"Hooks",
" - processElement",
"---",
"second markup",
"---",
"Source [email protected]:1 (offset: 69)",
"---",
"third markup",
"---",
]
`);
});
it("getRuleDocumentation() should delegate call to engine", () => {
......
......@@ -5,6 +5,7 @@ import { Engine, EventDump, TokenDump } from "./engine";
import { Parser } from "./parser";
import { Report, Reporter } from "./reporter";
import { RuleDocumentation } from "./rule";
import path from "path";
/**
* Primary API for using HTML-validate.
......@@ -92,6 +93,27 @@ class HtmlValidate {
);
}
/**
* Returns true if the given filename can be validated.
*
* A file is considered to be validatable if the extension is `.html` or if a
* transformer matches the filename.
*
* This is mostly useful for tooling to determine whenever to validate the
* file or not. CLI tools will run on all the given files anyway.
*/
public canValidate(filename: string): boolean {
/* .html is always supported */
const extension = path.extname(filename).toLowerCase();
if (extension === ".html") {
return true;
}
/* test if there is a matching transformer */
const config = this.getConfigFor(filename);
return config.canTransform(filename);
}
/**
* Tokenize filename and output all tokens.
*
......@@ -149,7 +171,23 @@ class HtmlValidate {
const config = this.getConfigFor(filename);
const sources = config.transformFilename(filename);
return sources.reduce((result: string[], source: Source) => {
result.push(`Source ${source.filename}@${source.line}:${source.column}`);
result.push(
`Source ${source.filename}@${source.line}:${source.column} (offset: ${source.offset})`
);
if (source.transformedBy) {
result.push("Transformed by:");
result = result.concat(
source.transformedBy.reverse().map(name => ` - ${name}`)
);
}
if (source.hooks && Object.keys(source.hooks).length > 0) {
result.push("Hooks");
for (const [key, present] of Object.entries(source.hooks)) {
if (present) {
result.push(` - ${key}`);
}
}
}
result.push("---");
result = result.concat(source.data.split("\n"));
result.push("---");
......
......@@ -117,7 +117,7 @@ it("should handle source missing properties", () => {
};
const htmlvalidate = new HtmlValidate({
root: true,
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
});
const report = htmlvalidate.validateSource(source as Source);
expect(report).toBeInvalid();
......
......@@ -1006,7 +1006,7 @@ describe("parser", () => {
beforeEach(() => {
htmlvalidate = new HtmlValidate({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
});
});
......
......@@ -26,6 +26,7 @@ describe("Plugin", () => {
};
/* reset mock */
jest.resetModules();
mockPlugin = require("mock-plugin");
});
......@@ -86,6 +87,30 @@ describe("Plugin", () => {
transform: {},
});
});
it("should retain original name", () => {
expect.assertions(1);
mockPlugin.name = "my-plugin";
mockPlugin.configs = {
foo: {
rules: {
"my-rule": "error",
},
},
};
config = Config.fromObject({
extends: ["mock-plugin:foo"],
plugins: ["mock-plugin"],
});
expect(config.get()).toEqual({
extends: ["mock-plugin:foo"],
plugins: ["mock-plugin"],
rules: {
"my-rule": "error",
},
transform: {},
});
});
});
describe("configs", () => {
......@@ -325,6 +350,9 @@ describe("Plugin", () => {
"line": 2,
"offset": 4,
"originalData": "original data",
"transformedBy": Array [
"mock-plugin",
],
},
]
`);
......@@ -371,11 +399,27 @@ describe("Plugin", () => {
"line": 2,
"offset": 4,
"originalData": "original data",
"transformedBy": Array [
"mock-plugin:foobar",
],
},
]
`);
});
it("should throw error when named transform is missing plugin", () => {
expect.assertions(1);
mockPlugin.transformer = {};
config = Config.fromObject({
transform: {
".*": "missing-plugin:foobar",
},
});
expect(() => config.init()).toThrow(
'Failed to load transformer "missing-plugin:foobar": No plugin named "missing-plugin" has been loaded'
);
});
it("should throw error when named transform is missing", () => {
expect.assertions(1);
mockPlugin.transformer = {};
......
......@@ -34,7 +34,7 @@ describe("regression tests", () => {
for (const filename of glob.sync("test-files/issues/**/*.html")) {
it(filename, () => {
const htmlvalidate = new HtmlValidate({
extends: ["htmlvalidate:recommended"],
extends: ["html-validate:recommended"],
transform: {
".*": "mock-transformer",
},
......