...
 
Commits (40)
{
"parser": "@typescript-eslint/parser",
"env": {
"node": true
},
"extends": [
"sidvind/es2017",
"plugin:@typescript-eslint/recommended",
"@html-validate",
"plugin:array-func/recommended",
"plugin:prettier/recommended",
"plugin:node/recommended-module",
"plugin:import/errors",
"plugin:import/warnings",
......@@ -18,68 +14,34 @@
"plugin:sonarjs/recommended"
],
"plugins": [
"@typescript-eslint",
"array-func",
"node",
"prettier",
"security",
"sonarjs"
],
"plugins": ["array-func", "node", "security", "sonarjs"],
"rules": {
"@typescript-eslint/array-type": "off",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],
"@typescript-eslint/explicit-member-accessibility": "error",
"@typescript-eslint/indent": "off",
"@typescript-eslint/member-delimiter-style": "error",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/type-annotation-spacing": "error",
"consistent-this": "off",
"import/named": "off",
"no-console": "warn",
"no-dupe-class-members": "off",
"no-undef": "off",
"node/no-missing-import": "off",
"node/no-unsupported-features/es-syntax": "off",
"prettier/prettier": "warn",
"security/detect-non-literal-fs-filename": "off",
"security/detect-non-literal-require": "off",
"security/detect-object-injection": "off",
"security/detect-unsafe-regex": "off",
"strict": "off"
"security/detect-unsafe-regex": "off"
},
"overrides": [
{
"files": "*.spec.ts",
"env": {
"jest": true
},
"plugins": ["jest"],
"extends": ["plugin:jest/recommended", "plugin:jest/style"],
"files": "**/*.ts",
"extends": ["@html-validate/eslint-config/typescript"],
"rules": {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "warn",
"jest/no-test-prefixes": "warn",
"sonarjs/no-duplicate-string": "off",
"sonarjs/no-identical-functions": "off"
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-var-requires": "off"
}
},
{
"files": "*.js",
"files": "*.spec.[jt]s",
"extends": ["@html-validate/eslint-config/jest"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
"sonarjs/no-duplicate-string": "off",
"sonarjs/no-identical-functions": "off"
}
}
]
......
{
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": true,
"overrides": [
{
"files": "docs/**/*.md",
"options": {
"useTabs": false
}
},
{
"files": "**/{*,.*}.json",
"options": {
"useTabs": false
}
}
]
}
# html-validate changelog
# [2.8.0](https://gitlab.com/html-validate/html-validate/compare/v2.7.0...v2.8.0) (2020-01-02)
### Features
- **rule:** validate matching case for start and end tags ([288cf86](https://gitlab.com/html-validate/html-validate/commit/288cf867dc6b1fdaf899cc695bb70b35c9a720a0))
- **rules:** refactor `parseStyle` from `element-case` and `attr-case` ([24d8fad](https://gitlab.com/html-validate/html-validate/commit/24d8fad19ba677502e1c19f8180efea44aa9cf34))
- **rules:** support multiple case styles ([5a397bd](https://gitlab.com/html-validate/html-validate/commit/5a397bd9aa281710f24925bec8dcc1bc29605403)), closes [#50](https://gitlab.com/html-validate/html-validate/issues/50)
- **rules:** support pascalcase and camelcase for `element-case` rule ([be7d692](https://gitlab.com/html-validate/html-validate/commit/be7d692838826a0de908d6cbb2867d02c43cee66))
# [2.7.0](https://gitlab.com/html-validate/html-validate/compare/v2.6.0...v2.7.0) (2019-12-16)
### Bug Fixes
......
@ngdoc content
@module about
@name About html-validate
@description
---
docType: content
title: About html-validate
---
# About
......
......@@ -90,3 +90,10 @@ footer {
.table-center {
text-align: center;
}
.alert .fa {
font-size: 1.3em;
margin-right: 0.5em;
position: relative;
top: 1px;
}
@ngdoc content
@module dev
@name Events
@description
---
docType: content
title: Events
---
# Events
......
@ngdoc content
@module usage
@name Developers guide
@description
---
docType: content
title: Developers guide
---
# Developers guide
......
@ngdoc content
@module dev
@name Transformers
@description
---
docType: content
title: Transformers
---
# Transformers
......
@ngdoc content
@module dev
@name Using API
@description
---
docType: content
title: Using API
---
# Using API
......
@ngdoc content
@module dev
@name Writing plugins
@description
---
docType: content
title: Writing plugins
---
# Writing plugins
......
@ngdoc content
@module dev
@name Writing rules
@description
---
docType: content
title: Writing rules
---
# Writing rules
......
const Package = require("dgeni").Package;
const pkg = new Package("dgeni-bootstrap", []);
pkg.config(function(inlineTagProcessor, getInjectables) {
const inlineTagsDefs = getInjectables(require("./inline-tag-defs"));
inlineTagProcessor.inlineTagDefinitions = inlineTagProcessor.inlineTagDefinitions.concat(
inlineTagsDefs
);
});
module.exports = pkg;
module.exports = function alertDangerInlineTagDef() {
return {
name: "alert-danger",
description: "Add bootstrap danger alert",
handler: function(doc, tagName, tagDescription) {
return `<div class="alert alert-danger"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> ${tagDescription}</div>`;
},
};
};
module.exports = function alertInfoInlineTagDef() {
return {
name: "alert-info",
description: "Add bootstrap info alert",
handler: function(doc, tagName, tagDescription) {
return `<div class="alert alert-info"><i class="fa fa-info-circle" aria-hidden="true"></i> ${tagDescription}</div>`;
},
};
};
module.exports = function alertSucccessInlineTagDef() {
return {
name: "alert-success",
description: "Add bootstrap success alert",
handler: function(doc, tagName, tagDescription) {
return `<div class="alert alert-success"><i class="fa fa-check" aria-hidden="true"></i> ${tagDescription}</div>`;
},
};
};
module.exports = function alertWarningInlineTagDef() {
return {
name: "alert-warning",
description: "Add bootstrap warning alert",
handler: function(doc, tagName, tagDescription) {
return `<div class="alert alert-warning"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> ${tagDescription}</div>`;
},
};
};
module.exports = [
require("./alert-success"),
require("./alert-info"),
require("./alert-warning"),
require("./alert-danger"),
];
const preamble = `
@ngdoc changelog
@module usage
@name Changelog
@description
`;
function fixHeading(src) {
return src.replace(/^# html-validate changelog/, "# Changelog");
}
function dropUpcoming(src) {
return src.replace(/^## Upcoming release$[^]*?^(?=## )/m, "");
}
function prepare(src) {
src = fixHeading(src);
src = dropUpcoming(src);
return preamble + src;
return fixHeading(src);
}
module.exports = function changelogFileReader() {
......@@ -26,6 +12,8 @@ module.exports = function changelogFileReader() {
getDocs: function(fileInfo) {
return [
{
docType: "changelog",
title: "Changelog",
content: prepare(fileInfo.content),
startingLine: 1,
},
......
......@@ -3,8 +3,8 @@ const Package = require("dgeni").Package;
const packagePath = __dirname;
module.exports = new Package("html-validate-docs", [
require("dgeni-packages/ngdoc"),
require("dgeni-packages/nunjucks"),
require("dgeni-front-matter"),
require("./bootstrap"),
require("./highlight"),
require("./inline-validate"),
require("./schema"),
......@@ -17,17 +17,6 @@ module.exports = new Package("html-validate-docs", [
renderDocsProcessor.extraData.tracking = process.env.GA_TRACKING_ID;
})
/* disable unused module generation */
.config(function(
moduleDocsProcessor,
generateComponentGroupsProcessor,
collectKnownIssuesProcessor
) {
moduleDocsProcessor.$enabled = false;
generateComponentGroupsProcessor.$enabled = false;
collectKnownIssuesProcessor.$enabled = false;
})
/* configure markdown syntax highlighting */
.config(function(highlight) {
highlight.configure({
......@@ -41,21 +30,16 @@ module.exports = new Package("html-validate-docs", [
readFilesProcessor.fileReaders.push(changelogFileReader);
})
.config(function(
log,
readFilesProcessor,
templateFinder,
writeFilesProcessor,
copySchema
) {
.config(function(log, readFilesProcessor, writeFilesProcessor, copySchema) {
log.level = "info";
readFilesProcessor.basePath = path.resolve(packagePath, "../..");
readFilesProcessor.sourceFiles = [
{
include: "docs/**/*.md",
exclude: "docs/dgeni/**/*.md",
basePath: "docs",
fileReader: "ngdocFileReader",
fileReader: "frontMatterFileReader",
},
{
include: "CHANGELOG.md",
......@@ -110,7 +94,12 @@ module.exports = new Package("html-validate-docs", [
}
},
getAliases: function(doc) {
return [doc.id, doc.name, `${doc.docType}:${doc.name}`];
const alias = [doc.id];
if (doc.name) {
alias.push(doc.name);
alias.push(`${doc.docType}:${doc.name}`);
}
return alias;
},
});
......
# Inline HTML-validate results
Processes the markup inside the tag and runs HTML-Validate on it.
If there are any errors they will be printed using `codeframe` formatter.
Jest snapshot tests will also be generated for each validation to ensure changes to documentation validations will be consistent with intended changes.
```html
<validate name="document-unique-name">
<!-- my markup -->
</validate>
```
## Usage
The following attributes can be set:
- `name` - a per-document unique name.
- `rules` - a whitespace-separated list of rules to enable. Default: all rules from recommended list
- `elements` - path to custom metadata (relative to current file). Default: `html5`
- `results` - set to `true` to force validation results to show up even if it is 0. Default `auto`.
Rule options can be set by using the rule name as attribute and providing a JSON-encoded string with options.
......@@ -21,7 +21,7 @@ module.exports = function generateInlineValidationsProcessor(log, validateMap) {
startingLine: validation.doc.startingLine,
endingLine: validation.doc.endingLine,
validate: validation,
template: "inline/inlineValidation.template.html",
template: "inlineValidation.template.html",
};
}
};
......@@ -34,6 +34,7 @@ module.exports = function parseValidatesProcessor(
const name = attr.name;
const rules = attr.rules ? attr.rules.split(/ +/) : undefined;
const showResults = attr.results ? Boolean(attr.results) : "auto";
const elements = readElements(doc.fileInfo, attr.elements);
const id = uniqueName(validateMap, `markup-${attr.name}`);
const markup = trimIndentation(validateMarkup);
......@@ -43,6 +44,7 @@ module.exports = function parseValidatesProcessor(
config,
name,
markup,
showResults,
id,
doc,
};
......
......@@ -17,6 +17,10 @@ module.exports = function generateValidationResultsProcessor(log, validateMap) {
htmlvalidate = new HtmlValidate(validation.config);
validation.report = htmlvalidate.validateString(validation.markup);
validation.codeframe = codeframe(validation.report.results);
if (validation.showResults === "auto") {
validation.showResults = !validation.report.valid;
}
});
chalk.level = oldLevel;
}
......
......@@ -4,13 +4,19 @@
</p>
<div class="inline-validation">
<div class="markup"
{%- for attrName, attrValue in doc.validate.attributes %}{$ attrName $}="{$ attrValue $}"{% endfor %}>
<pre><code class="hljs lang-html">{$ doc.validate.markup | highlight('html') $}</code></pre>
{%- for attrName, attrValue in doc.validate.attributes %}{{ attrName }}="{{ attrValue }}"{% endfor %}>
<pre><code class="hljs lang-html">{{ doc.validate.markup | highlight('html') }}</code></pre>
</div>
{%- if not doc.validate.report.valid %}
{%- if doc.validate.showResults %}
<div class="messages">
<pre>{$ doc.validate.codeframe | escape $}</pre>
<pre>
{%- if not doc.validate.report.valid -%}
{{- doc.validate.codeframe | escape -}}
{%- else -%}
0 errors found
{%- endif -%}
</pre>
</div>
{%- endif %}
</div>
......
import HtmlValidate from "{$ doc.fileInfo.docRoot $}/../src/htmlvalidate";
import HtmlValidate from "{{ doc.fileInfo.docRoot }}/../src/htmlvalidate";
const markup: { [key: string]: string } = {};
{%- for validation in doc.validations %}
markup["{$ validation.name $}"] = `{$ validation.markup $}`;
markup["{{ validation.name }}"] = `{{ validation.markup }}`;
{%- endfor %}
describe("{$ doc.fileInfo.fullpath $}", () => {
describe("{{ doc.fileInfo.fullpath }}", () => {
{%- for validation in doc.validations %}
it("inline validation: {$ validation.name $}", () => {
const htmlvalidate = new HtmlValidate({$ validation.config | dump $});
const report = htmlvalidate.validateString(markup["{$ validation.name $}"]);
it("inline validation: {{ validation.name }}", () => {
const htmlvalidate = new HtmlValidate({{ validation.config | dump }});
const report = htmlvalidate.validateString(markup["{{ validation.name }}"]);
expect(report.results).toMatchSnapshot();
});
{%- endfor %}
......
{$ doc.fileContents $}
\ No newline at end of file
{{ doc.fileContents }}
\ No newline at end of file
......@@ -13,10 +13,10 @@ function compareName(a, b) {
}
function isRuleDocument(doc) {
return doc.docType === "rule" && doc.module === "rules";
return doc.docType === "rule";
}
module.exports = function rulesProcessor(moduleMap, renderDocsProcessor) {
module.exports = function rulesProcessor(renderDocsProcessor) {
return {
$runAfter: ["paths-computed"],
$runBefore: ["rendering-docs"],
......@@ -24,17 +24,25 @@ module.exports = function rulesProcessor(moduleMap, renderDocsProcessor) {
};
function process(docs) {
const ruleDocs = docs.filter(isRuleDocument);
/* compule rule source paths */
docs.filter(isRuleDocument).forEach(doc => {
ruleDocs.forEach(doc => {
const docPath = doc.fileInfo.projectRelativePath;
doc.ruleSourcePath = docPath
.replace("docs", "src")
.replace(/\.md$/, ".ts");
});
/* generate title */
ruleDocs.forEach(doc => {
if (!doc.title) {
doc.title = `${doc.summary} (${doc.name})`;
}
});
/* find all available rules */
const rules = docs
.filter(isRuleDocument)
const rules = ruleDocs
.map(doc => ({
name: doc.name,
url: doc.outputPath,
......
......@@ -5,18 +5,18 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{#- <!-- [html-validate-disable-next long-title] --> #}
<title>HTML-validate{% if doc.name %} - {$ doc.title or doc.name $}{% endif %}</title>
<title>HTML-validate{% if doc.title or doc.name %} - {{ (doc.title or doc.name) | escape }}{% endif %}</title>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/ico">
<link rel="stylesheet" href="{$ '/assets/docs.min.css' | assetHash $}">
<link rel="stylesheet" href="{{ '/assets/docs.min.css' | assetHash }}">
{%- if tracking %}
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id={$ tracking $}"></script>
<script async src="https://www.googletagmanager.com/gtag/js?id={{ tracking }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{$ tracking $}');
gtag('config', '{{ tracking }}');
</script>
{% endif %}
</head>
......@@ -52,8 +52,8 @@
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Developers guide <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{$ pkg.repository.url $}">Getting the source code</a></li>
<li><a href="{$ pkg.bugs.url $}">File issue</a></li>
<li><a href="{{ pkg.repository.url }}">Getting the source code</a></li>
<li><a href="{{ pkg.bugs.url }}">File issue</a></li>
<li role="separator" class="divider"></li>
<li><a href="/dev/using-api.html">Using API</a></li>
<li><a href="/dev/writing-rules.html">Writing rules</a></li>
......@@ -65,7 +65,7 @@
<li><a href="/changelog">Changelog</a></li>
<li><a href="/about">About</a></li>
</ul>
<p class="navbar-text navbar-right">{$ pkg.name $}-{$ pkg.version $}</p>
<p class="navbar-text navbar-right">{{ pkg.name }}-{{ pkg.version }}</p>
</div>
</div>
</nav>
......@@ -76,22 +76,22 @@
{%- endblock %}
</main>
{%- if doc.fileInfo.projectRelativePath and doc.module != 'error' %}
{%- if doc.fileInfo.projectRelativePath and doc.docType != 'error' %}
<footer>
<div class="container">
<a href="https://gitlab.com/html-validate/html-validate/edit/master/{$ doc.fileInfo.projectRelativePath $}" class="btn btn-default"><i class="fa fa-pencil">&nbsp;</i>Edit this page</a>
<a href="https://gitlab.com/html-validate/html-validate/edit/master/{{ doc.fileInfo.projectRelativePath }}" class="btn btn-default"><i class="fa fa-pencil">&nbsp;</i>Edit this page</a>
{%- block footer %}
{%- endblock %}
</div>
</footer>
{%- endif %}
<script src="{$ '/assets/docs.js' | assetHash $}"></script>
<script src="{{ '/assets/docs.js' | assetHash }}"></script>
</body>
</html>
<!--
Document ID: {$ doc.id $}
Document type: {$ doc.docType $}
Document name: {$ doc.name $}
Document alias: {$ doc.aliases $}
Document ID: {{ doc.id }}
Document type: {{ doc.docType }}
Document name: {{ doc.name }}
Document alias: {{ doc.aliases }}
-->
......@@ -2,5 +2,5 @@
{% block content %}
<!-- [html-validate-disable no-dup-id, heading-level: changelog generator generates headings with these errors] -->
{$ doc.description | marked $}
{{ doc.description | marked }}
{% endblock %}
{% extends "base.template.html" %}
{% block content %}
{$ doc.description | marked $}
{{ doc.description | marked }}
{% endblock %}
......@@ -3,10 +3,10 @@
{% block content %}
<div class="panel panel-danger">
<div class="panel-heading">
<h1 class="panel-title">{$ doc.name $}</h1>
<h1 class="panel-title">{{ doc.title }}</h1>
</div>
<div class="panel-body">
{$ doc.description | marked $}
{{ doc.description | marked }}
</div>
</div>
{% endblock %}
......@@ -9,12 +9,12 @@
</div>
<div class="frontpage">
{$ doc.description | marked $}
{{ doc.description | marked }}
{% for block in doc.blocks %}
<h2>{$ block.title $}</h2>
<div class="frontpage {$ block.className $}">
{$ block.body | marked $}
<h2>{{ block.title }}</h2>
<div class="frontpage {{ block.className }}">
{{ block.body | marked }}
</div>
{% endfor %}
</div>
......
{% extends "base.template.html" %}
{% block content %}
{$ doc.description | marked $}
{{ doc.description | marked }}
{% endblock %}
{% block footer %}
<a href="https://gitlab.com/html-validate/html-validate/blob/master/{$ doc.ruleSourcePath $}" class="btn btn-default"><i class="fa fa-search">&nbsp;</i>View rule source</a>
<a href="https://gitlab.com/html-validate/html-validate/blob/master/{{ doc.ruleSourcePath }}" class="btn btn-default"><i class="fa fa-search">&nbsp;</i>View rule source</a>
{% endblock %}
......@@ -18,8 +18,8 @@
<span class="fa fa-file-text-o"></span>
{% endif %}
</td>
<td><a href="/{$ rule.url $}">{$ rule.name $}</a></td>
<td>{$ rule.summary | escape $}</td>
<td><a href="/{{ rule.url }}">{{ rule.name }}</a></td>
<td>{{ rule.summary | escape }}</td>
</tr>
</tbody>
{% endfor %}
......@@ -29,24 +29,24 @@
{% block content %}
<h1>Available rules</h1>
{$ doc.description | marked $}
{{ doc.description | marked }}
{$ ruleTable(rules['other']) $}
{{ ruleTable(rules['other']) }}
<h2>Content model</h2>
{$ ruleTable(rules['content-model']) $}
{{ ruleTable(rules['content-model']) }}
<h2>Accessibility</h2>
{$ ruleTable(rules['a17y']) $}
{{ ruleTable(rules['a17y']) }}
<h2>SEO</h2>
{$ ruleTable(rules['seo']) $}
{{ ruleTable(rules['seo']) }}
<h2>Style</h2>
{$ ruleTable(rules['style']) $}
{{ ruleTable(rules['style']) }}
<h2>Document</h2>
<p>These rules is to be used on full documents.</p>
{$ ruleTable(rules['document']) $}
{{ ruleTable(rules['document']) }}
{% endblock %}
@ngdoc error
@module error
@name 404: Not found
@description
---
docType: error
title: "404: Not found"
---
The page you requested could not be found.
@ngdoc content
@module frameworks
@name Usage with Vue.js
@description
---
docType: content
title: Usage with Vue.js
---
# Usage with Vue.js
......
{
"extends": ["html-validate:recommended", "html-validate:document"],
"rules": {
"long-title": ["error", { "maxlength": 120 }],
"no-trailing-whitespace": "off",
"require-sri": "off"
}
......
@ngdoc frontpage
@module frontpage
@name
@description
---
docType: frontpage
---
@block Features
......
......@@ -25,4 +25,29 @@ Array [
]
`;
exports[`docs/rules/attr-case.md inline validation: multiple 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 4,
"context": undefined,
"line": 3,
"message": "Attribute \\"fooBar\\" should be lowercase or uppercase",
"offset": 33,
"ruleId": "attr-case",
"severity": 2,
"size": 6,
},
],
"source": "<p foobar></p>
<p FOOBAR></p>
<p fooBar></p>",
"warningCount": 0,
},
]
`;
exports[`docs/rules/attr-case.md inline validation: svg-viewbox 1`] = `Array []`;
......@@ -24,3 +24,51 @@ Array [
},
]
`;
exports[`docs/rules/element-case.md inline validation: matching 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 13,
"context": undefined,
"line": 1,
"message": "Start and end tag must not differ in casing",
"offset": 12,
"ruleId": "element-case",
"severity": 2,
"size": 7,
},
],
"source": "<FooBar>...</Foobar>",
"warningCount": 0,
},
]
`;
exports[`docs/rules/element-case.md inline validation: multiple 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 2,
"context": undefined,
"line": 3,
"message": "Element \\"fooBar\\" should be lowercase or PascalCase",
"offset": 39,
"ruleId": "element-case",
"severity": 2,
"size": 6,
},
],
"source": "<foo-bar></foo-bar>
<FooBar></FooBar>
<fooBar></fooBar>",
"warningCount": 0,
},
]
`;
......@@ -3,6 +3,9 @@ import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<p ID="foo"></p>`;
markup["correct"] = `<p id="foo"></p>`;
markup["multiple"] = `<p foobar></p>
<p FOOBAR></p>
<p fooBar></p>`;
markup["svg-viewbox"] = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" />`;
describe("docs/rules/attr-case.md", () => {
......@@ -16,6 +19,11 @@ describe("docs/rules/attr-case.md", () => {
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: multiple", () => {
const htmlvalidate = new HtmlValidate({"rules":{"attr-case":["error",{"style":["lowercase","uppercase"]}]}});
const report = htmlvalidate.validateString(markup["multiple"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: svg-viewbox", () => {
const htmlvalidate = new HtmlValidate({"rules":{"attr-case":"error"}});
const report = htmlvalidate.validateString(markup["svg-viewbox"]);
......
......@@ -3,6 +3,10 @@ import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<DIV>...</DIV>`;
markup["correct"] = `<div>...</div>`;
markup["matching"] = `<FooBar>...</Foobar>`;
markup["multiple"] = `<foo-bar></foo-bar>
<FooBar></FooBar>
<fooBar></fooBar>`;
describe("docs/rules/element-case.md", () => {
it("inline validation: incorrect", () => {
......@@ -15,4 +19,14 @@ describe("docs/rules/element-case.md", () => {
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: matching", () => {
const htmlvalidate = new HtmlValidate({"rules":{"element-case":["error",{"style":"pascalcase"}]}});
const report = htmlvalidate.validateString(markup["matching"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: multiple", () => {
const htmlvalidate = new HtmlValidate({"rules":{"element-case":["error",{"style":["lowercase","pascalcase"]}]}});
const report = htmlvalidate.validateString(markup["multiple"]);
expect(report.results).toMatchSnapshot();
});
});
@ngdoc rule
@module rules
@name attr-case
@category style
@summary Require a specific case for attribute names
@description
---
docType: rule
name: attr-case
category: style
summary: Require a specific case for attribute names
---
# attribute name case (`attr-case`)
......@@ -41,6 +41,17 @@ This rule takes an optional object:
- `pascalcase` requires all attribute names to be PascalCase.
- `uppercase` requires all attribute names to be UPPERCASE.
Multiple styles can be set as an array of strings.
With multiple styles the attribute must match at least one pattern to be considered valid.
For instance, when configured with `{"style": ["lowercase", "uppercase"]}` attributes can be either lowercase or uppercase:
<validate name="multiple" rules="attr-case" attr-case='{"style": ["lowercase", "uppercase"]}'>
<p foobar></p>
<p FOOBAR></p>
<p fooBar></p>
</validate>
### `ignoreForeign`
By default attributes on foreign elements (such as `<svg>` and `<math>`) are
......
@ngdoc rule
@module rules
@name attr-quotes
@category style
@summary Require attribute quoting
@description
---
docType: rule
name: attr-quotes
category: style
summary: Require attribute quoting
---
# requires a specific style of attribute quoting (`attr-quotes`)
......
@ngdoc rule
@module rules
@name attribute-allowed-values
@category content-model
@summary Validate permitted attribute values
@description
---
docType: rule
name: attribute-allowed-values
category: content-model
summary: Validate permitted attribute values
---
# Allowed attribute values (`attribute-allowed-values`)
......
@ngdoc rule
@module rules
@name attribute-boolean-style
@category style
@summary Require a specific style for boolean attributes
@description
---
docType: rule
name: attribute-boolean-style
category: style
summary: Require a specific style for boolean attributes
---
# Boolean attribute style (`attribute-boolean-style`)
......
@ngdoc rule
@module rules
@name class-pattern
@category style
@summary Require classes to match a specific pattern
@description
---
docType: rule
name: class-pattern
category: style
summary: Require classes to match a specific pattern
---
# require a specific class format (`class-pattern`)
......
@ngdoc rule
@module rules
@name close-attr
@summary Disallow end tags from having attributes
@description
---
docType: rule
name: close-attr
summary: Disallow end tags from having attributes
---
# disallows end tags with attributes (`close-attr`)
......
@ngdoc rule
@module rules
@name close-order
@summary Require elements to be closed in correct order
@description
---
docType: rule
name: close-order
summary: Require elements to be closed in correct order
---
# disallows elements closed in the wrong order (`close-order`)
......
@ngdoc rule
@module rules
@name deprecated-rule
@summary Disallow usage of deprecated rules
@description
---
docType: rule
name: deprecated-rule
summary: Disallow usage of deprecated rules
---
# Disallow usage of deprecated rules (`deprecated-rule`)
......
@ngdoc rule
@module rules
@name deprecated
@summary Disallow usage of deprecated elements
@description
---
docType: rule
name: deprecated
summary: Disallow usage of deprecated elements
---
# disallows usage of deprecated elements (`deprecated`)
......
@ngdoc rule
@module rules
@name doctype-html
@summary Require usage of "html" doctype
@description
---
docType: rule
name: doctype-html
summary: Require usage of "html" doctype
---
# Require usage of "html" doctype (`doctype-html`)
......
@ngdoc rule
@module rules
@name element-case
@category style
@summary Require a specific case for element names
@description
---
docType: rule
name: element-case
category: style
summary: Require a specific case for element names
---
# element name case (`element-case`)
......@@ -23,6 +23,14 @@ Examples of **correct** code for this rule:
<div>...</div>
</validate>
### Matching case
When using styles such as `pascalcase` the start and end tag must have matching case:
<validate name="matching" rules="element-case" element-case='{"style": "pascalcase"}'>
<FooBar>...</Foobar>
</validate>
## Options
This rule takes an optional object:
......@@ -35,5 +43,18 @@ This rule takes an optional object:
### Style
- `camelcase` requires all element names to be camelCase.
- `lowercase` requires all element names to be lowercase.
- `uppercase` requires all element names to be uppercase.
- `pascalcase` requires all element names to be PascalCase.
- `uppercase` requires all element names to be UPPERCASE.
Multiple styles can be set as an array of strings.
With multiple styles the element name must match at least one pattern to be considered valid.
For instance, when configured with `{"style": ["lowercase", "pascalcase"]}` element names can be either lowercase or PascalCase:
<validate name="multiple" rules="element-case" element-case='{"style": ["lowercase", "pascalcase"]}'>
<foo-bar></foo-bar>
<FooBar></FooBar>
<fooBar></fooBar>
</validate>
@ngdoc rule
@module rules
@name element-name
@summary Disallow invalid element names
@description
---
docType: rule
name: element-name
summary: Disallow invalid element names
---
# disallow invalid element names (`element-name`)
......
@ngdoc rule
@module rules
@name element-permitted-content
@category content-model
@summary Validate permitted content from content model
@description
---
docType: rule
name: element-permitted-content
category: content-model
summary: Validate permitted content from content model
---
# verify element content model (`element-permitted-content`)
......
@ngdoc rule
@module rules
@name element-permitted-occurrences
@category content-model
@summary Validate permitted number of occurrences from content model
@description
---
docType: rule
name: element-permitted-occurrences
category: content-model
summary: Validate permitted number of occurrences from content model
---
# verify element content model (`element-permitted-occurrences`)
......
@ngdoc rule
@module rules
@name element-permitted-order
@category content-model
@summary Validate required order from content model
@description
---
docType: rule
name: element-permitted-order
category: content-model
summary: Validate required order from content model
---
# verify element content model (`element-permitted-order`)
......
@ngdoc rule
@module rules
@name element-required-attributes
@category content-model
@summary Ensure required attributes are set
@description
---
docType: rule
name: element-required-attributes
category: content-model
summary: Ensure required attributes are set
---
# Require attribute (`element-required-attributes`)
......
@ngdoc rule
@module rules
@name element-required-content
@category content-model
@summary Ensure required elements are present
@description
---
docType: rule
name: element-required-content
category: content-model
summary: Ensure required elements are present
---
# Ensure required elements are present (`element-required-content`)
......
@ngdoc rule
@module rules
@name empty-heading
@category a17y
@summary Require headings to have textual content
@description
---
docType: rule
name: empty-heading
category: a17y
summary: Require headings to have textual content
---
# require headings to have textual content (`empty-heading`)
......
@ngdoc rule
@module rules
@name empty-title
@category a17y
@summary Require title to have textual content
@description
---
docType: rule
name: empty-title
category: a17y
summary: Require title to have textual content
---
# require title to have textual content (`empty-title`)
......
@ngdoc rule
@module rules
@name heading-level
@category document
@summary Require headings to start at h1 and be sequential
@description
---
docType: rule
name: heading-level
category: document
summary: Require headings to start at h1 and be sequential
---
# heading level (`heading-level`)
......
@ngdoc rule
@module rules
@name id-pattern
@category style
@summary Require IDs to match a specific pattern
@description
---
docType: rule
name: id-pattern
category: style
summary: Require IDs to match a specific pattern
---
# require a specific ID format (`id-pattern`)
......
@ngdoc rules
@module rules
@name Available rules
@description
---
docType: rules
name: Available rules
---
Rules with <span class="fa fa-check"></span> are enabled by