feat(html): whitespace-sensitive formatting (#5168)

- whitespace-sensitive formatting 
  - respect css `display: block`/`white-space: pre` (data from [`html-styles`](https://github.com/marionebl/html-styles))
  - support magic comment (`<!-- display: block -->`)
  - add an option to specify whitespace sensitivity (`--html-whitespace-sensitivity <css|strict|ignore>`)
    - magic comments take precedence
    - (default) `css`: respect default css style (safe in the most cases)
    - `strict`: every node is considered whitespace sensitive (the safest)
    - `ignore`: every node is considered whitespace insensitive (dangerous, the original behavior)
  - inline inline-tags 
- support ie conditional comment (`<!--[if IE]><![endif]-->`)
- indent the script/style content
- no inconsistent output for 2+ attributes
- force break tag if there're multiline attributes
master
Ika 2018-10-13 13:55:38 +08:00 committed by GitHub
parent 3369be0ca2
commit dd4687e7ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 4987 additions and 543 deletions

View File

@ -38,12 +38,14 @@
"globby": "6.1.0",
"graphql": "0.13.2",
"html-element-attributes": "2.0.0",
"html-styles": "1.0.0",
"html-tag-names": "1.1.2",
"htmlparser2": "3.9.2",
"ignore": "3.3.7",
"jest-docblock": "23.2.0",
"json-stable-stringify": "1.0.1",
"leven": "2.1.0",
"lines-and-columns": "1.1.6",
"linguist-languages": "6.2.1-dev.20180706",
"lodash.uniqby": "4.7.0",
"mem": "1.1.0",

View File

@ -5,12 +5,12 @@ module.exports = function(ast, newNode) {
delete newNode.endIndex;
delete newNode.attribs;
if (ast.type === "text") {
if (ast.type === "text" || ast.type === "comment") {
return null;
}
// may be formatted by multiparser
if (ast.type === "yaml") {
if (ast.type === "yaml" || ast.type === "toml") {
return null;
}

View File

@ -0,0 +1,30 @@
"use strict";
const htmlStyles = require("html-styles");
const getCssStyleTags = property =>
htmlStyles
.filter(htmlStyle => htmlStyle.style[property])
.map(htmlStyle =>
htmlStyle.selectorText
.split(",")
.map(selector => selector.trim())
.filter(selector => /^[a-zA-Z0-9]+$/.test(selector))
.reduce((reduced, tagName) => {
reduced[tagName] = htmlStyle.style[property];
return reduced;
}, {})
)
.reduce((reduced, value) => Object.assign(reduced, value), {});
const CSS_DISPLAY_TAGS = getCssStyleTags("display");
const CSS_DISPLAY_DEFAULT = "inline";
const CSS_WHITE_SPACE_TAGS = getCssStyleTags("white-space");
const CSS_WHITE_SPACE_DEFAULT = "normal";
module.exports = {
CSS_DISPLAY_TAGS,
CSS_DISPLAY_DEFAULT,
CSS_WHITE_SPACE_TAGS,
CSS_WHITE_SPACE_DEFAULT
};

View File

@ -1,106 +0,0 @@
"use strict";
const { hasNewlineInRange } = require("../common/util");
const {
builders: { hardline, concat, markAsRoot, literalline },
utils: { removeLines, mapDoc }
} = require("../doc");
function embed(path, print, textToDoc, options) {
const node = path.getValue();
switch (node.type) {
case "text": {
const parent = path.getParentNode();
// Inline JavaScript
if (
parent.type === "script" &&
((!parent.attribs.lang && !parent.attribs.type) ||
parent.attribs.type === "text/javascript" ||
parent.attribs.type === "text/babel" ||
parent.attribs.type === "application/javascript")
) {
const parser = options.parser === "flow" ? "flow" : "babylon";
const doc = textToDoc(node.data, { parser });
return concat([hardline, doc]);
}
// Inline TypeScript
if (
parent.type === "script" &&
(parent.attribs.type === "application/x-typescript" ||
parent.attribs.lang === "ts")
) {
const doc = textToDoc(node.data, { parser: "typescript" }, options);
return concat([hardline, doc]);
}
// Inline Styles
if (parent.type === "style") {
const doc = textToDoc(node.data, { parser: "css" });
return concat([hardline, doc]);
}
break;
}
case "attribute": {
/*
* Vue binding syntax: JS expressions
* :class="{ 'some-key': value }"
* v-bind:id="'list-' + id"
* v-if="foo && !bar"
* @click="someFunction()"
*/
if (/(^@)|(^v-)|:/.test(node.key) && !/^\w+$/.test(node.value)) {
const doc = textToDoc(node.value, {
parser: "__js_expression",
// Use singleQuote since HTML attributes use double-quotes.
// TODO(azz): We still need to do an entity escape on the attribute.
singleQuote: true
});
return concat([
node.key,
'="',
hasNewlineInRange(node.value, 0, node.value.length)
? doc
: removeLines(doc),
'"'
]);
}
break;
}
case "yaml":
return markAsRoot(
concat([
"---",
hardline,
node.value.trim()
? replaceNewlinesWithLiterallines(
textToDoc(node.value, { parser: "yaml" })
)
: "",
"---",
hardline
])
);
}
}
function replaceNewlinesWithLiterallines(doc) {
return mapDoc(
doc,
currentDoc =>
typeof currentDoc === "string" && currentDoc.includes("\n")
? concat(
currentDoc
.split(/(\n)/g)
.map((v, i) => (i % 2 === 0 ? v : literalline))
)
: currentDoc
);
}
module.exports = embed;

View File

@ -2,6 +2,7 @@
const printer = require("./printer-htmlparser2");
const createLanguage = require("../utils/create-language");
const options = require("./options");
const languages = [
createLanguage(require("linguist-languages/data/html"), {
@ -19,5 +20,6 @@ const printers = {
module.exports = {
languages,
printers
printers,
options
};

View File

@ -0,0 +1,28 @@
"use strict";
const CATEGORY_HTML = "HTML";
// format based on https://github.com/prettier/prettier/blob/master/src/main/core-options.js
module.exports = {
htmlWhitespaceSensitivity: {
since: "1.15.0",
category: CATEGORY_HTML,
type: "choice",
default: "css",
description: "How to handle whitespaces in HTML.",
choices: [
{
value: "css",
description: "Respect the default value of CSS display property."
},
{
value: "strict",
description: "Whitespaces are considered sensitive."
},
{
value: "ignore",
description: "Whitespaces are considered insensitive."
}
]
}
};

View File

@ -1,10 +1,12 @@
"use strict";
const parseFrontMatter = require("../utils/front-matter");
const { HTML_TAGS, HTML_ELEMENT_ATTRIBUTES } = require("./utils");
const { HTML_ELEMENT_ATTRIBUTES, HTML_TAGS, mapNode } = require("./utils");
function parse(text /*, parsers, opts*/) {
const { frontMatter, content } = parseFrontMatter(text);
function parse(text, parsers, options, { shouldParseFrontMatter = true } = {}) {
const { frontMatter, content } = shouldParseFrontMatter
? parseFrontMatter(text)
: { frontMatter: null, content: text };
// Inline the require to avoid loading all the JS if we don't use it
const Parser = require("htmlparser2/lib/Parser");
@ -27,7 +29,13 @@ function parse(text /*, parsers, opts*/) {
super.onattribdata(value);
}
onattribend() {
super.onattribend();
if (this._cbs.onattribute) {
this._cbs.onattribute(this._attribname, this._attribvalue);
}
if (this._attribs) {
this._attribs.push([this._attribname, this._attribvalue]);
}
this._attribname = "";
this._attribvalue = null;
}
onselfclosingtag() {
@ -43,15 +51,36 @@ function parse(text /*, parsers, opts*/) {
this.onopentagend();
}
}
onopentagname(name) {
super.onopentagname(name);
if (this._cbs.onopentag) {
this._attribs = [];
}
}
}
/**
* modifications:
* - add `selfClosing` field
* - add `isSelfClosing` field
* - correct `endIndex` for whitespaces before closing tag end marker (e.g., `<x></x\n>`)
*/
class CustomDomHandler extends DomHandler {
onselfclosingtag() {
this._tagStack[this._tagStack.length - 1].selfClosing = true;
this._tagStack[this._tagStack.length - 1].isSelfClosing = true;
}
onclosetag() {
const elem = this._tagStack.pop();
if (this._options.withEndIndices && elem) {
const buffer = this._parser._tokenizer._buffer;
let endIndex = this._parser.endIndex;
while (buffer[endIndex] && buffer[endIndex] !== ">") {
endIndex++;
}
elem.endIndex = buffer[endIndex] ? endIndex : this._parser.endIndex;
}
if (this._elementCB) {
this._elementCB(elem);
}
}
}
@ -66,13 +95,59 @@ function parse(text /*, parsers, opts*/) {
recognizeSelfClosing: true
}).end(content);
const ast = normalize({ type: "root", children: handler.dom }, text);
const ast = normalize(
{
type: "root",
children: handler.dom,
startIndex: 0,
endIndex: text.length
},
text
);
if (frontMatter) {
ast.children.unshift(frontMatter);
}
return ast;
const parseHtml = data =>
parse(data, parsers, options, {
shouldParseFrontMatter: false
});
return mapNode(ast, node => {
const ieConditionalComment = parseIeConditionalComment(node, parseHtml);
return ieConditionalComment ? ieConditionalComment : node;
});
}
function parseIeConditionalComment(node, parseHtml) {
if (node.type !== "comment") {
return null;
}
const match = node.data.match(/^(\[if([^\]]*?)\]>)([\s\S]*?)<!\s*\[endif\]$/);
if (!match) {
return null;
}
const [, openingTagSuffix, condition, data] = match;
const subTree = parseHtml(data);
const baseIndex = node.startIndex + "<!--".length + openingTagSuffix.length;
return Object.assign(
{},
mapNode(subTree, currentNode =>
Object.assign({}, currentNode, {
startIndex: baseIndex + currentNode.startIndex,
endIndex: baseIndex + currentNode.endIndex
})
),
{
type: "ieConditionalComment",
condition: condition.trim().replace(/\s+/g, " ")
}
);
}
function normalize(node, text) {
@ -90,7 +165,7 @@ function normalize(node, text) {
if (node.attribs) {
const CURRENT_HTML_ELEMENT_ATTRIBUTES =
HTML_ELEMENT_ATTRIBUTES[node.name] || Object.create(null);
const attributes = Object.keys(node.attribs).map(attributeKey => {
const attributes = node.attribs.map(([attributeKey, attributeValue]) => {
const lowerCaseAttributeKey = attributeKey.toLowerCase();
return {
type: "attribute",
@ -99,7 +174,7 @@ function normalize(node, text) {
lowerCaseAttributeKey in CURRENT_HTML_ELEMENT_ATTRIBUTES
? lowerCaseAttributeKey
: attributeKey,
value: node.attribs[attributeKey]
value: attributeValue
};
});

View File

@ -0,0 +1,285 @@
"use strict";
const {
VOID_TAGS,
getNodeCssStyleDisplay,
getNodeCssStyleWhiteSpace,
getPrevNode,
isDanglingSpaceSensitiveNode,
isLeadingSpaceSensitiveNode,
isScriptLikeTag,
isTrailingSpaceSensitiveNode,
mapNode
} = require("./utils");
const LineAndColumn = (m => m.default || m)(require("lines-and-columns"));
const PREPROCESS_PIPELINE = [
renameScriptAndStyleWithTag,
processDirectives,
addIsSelfClosing,
extractWhitespaces,
addCssDisplay,
addIsSpaceSensitive,
addStartAndEndLocation,
addShortcuts
];
function preprocess(ast, options) {
for (const fn of PREPROCESS_PIPELINE) {
ast = fn(ast, options);
}
return ast;
}
/** add `startLocation` and `endLocation` field */
function addStartAndEndLocation(ast, options) {
const locator = new LineAndColumn(options.originalText);
return mapNode(ast, node => {
const startLocation = locator.locationForIndex(options.locStart(node));
const endLocation = locator.locationForIndex(options.locEnd(node) - 1);
return Object.assign({}, node, { startLocation, endLocation });
});
}
/** rename `script` and `style` with `tag` */
function renameScriptAndStyleWithTag(ast /*, options */) {
return mapNode(ast, node => {
return node.type === "script" || node.type === "style"
? Object.assign({}, node, { type: "tag" })
: node;
});
}
/** add `isSelfClosing` for void tags, directives, and comments */
function addIsSelfClosing(ast /*, options */) {
return mapNode(ast, node => {
if (
(node.type === "tag" && node.name in VOID_TAGS) ||
node.type === "directive" ||
node.type === "comment"
) {
return Object.assign({}, node, { isSelfClosing: true });
}
return node;
});
}
function processDirectives(ast /*, options */) {
return mapNode(ast, node => {
if (node.type !== "directive") {
return node;
}
const isDoctype = /^!doctype$/i.test(node.name);
const data = node.data.slice(node.name.length).replace(/\s+/g, " ");
return Object.assign({}, node, {
name: isDoctype ? "!DOCTYPE" : node.name,
data: isDoctype ? data.replace(/^\s+html/i, " html") : data,
// workaround for htmlparser2 bug
endIndex:
node.startIndex +
"<".length +
node.name.length +
node.data.length +
">".length
});
});
}
/**
* - add `hasLeadingSpaces` field
* - add `hasTrailingSpaces` field
* - add `hasDanglingSpaces` field for parent nodes
* - add `isWhiteSpaceSensitive`, `isIndentationSensitive` field for text nodes
* - remove insensitive whitespaces
*/
function extractWhitespaces(ast /*, options*/) {
const TYPE_WHITESPACE = "whitespace";
return mapNode(ast, node => {
if (!node.children) {
return node;
}
if (
node.children.length === 0 ||
(node.children.length === 1 &&
node.children[0].type === "text" &&
node.children[0].data.trim().length === 0)
) {
return Object.assign({}, node, {
children: [],
hasDanglingSpaces: node.children.length !== 0
});
}
const cssStyleWhiteSpace = getNodeCssStyleWhiteSpace(node);
const isCssStyleWhiteSpacePre = cssStyleWhiteSpace.startsWith("pre");
const isScriptLike = isScriptLikeTag(node);
return Object.assign({}, node, {
children: node.children
// extract whitespace nodes
.reduce((newChildren, child) => {
if (child.type !== "text") {
return newChildren.concat(child);
}
if (isCssStyleWhiteSpacePre || isScriptLike) {
return newChildren.concat(
Object.assign({}, child, {
isWhiteSpaceSensitive: true,
isIndentationSensitive: isCssStyleWhiteSpacePre
})
);
}
const localChildren = [];
const [, leadingSpaces, text, trailingSpaces] = child.data.match(
/^(\s*)([\s\S]*?)(\s*)$/
);
if (leadingSpaces) {
localChildren.push({ type: TYPE_WHITESPACE });
}
if (text) {
localChildren.push({
type: "text",
data: text,
startIndex: child.startIndex + leadingSpaces.length,
endIndex: child.endIndex - trailingSpaces.length
});
}
if (trailingSpaces) {
localChildren.push({ type: TYPE_WHITESPACE });
}
return newChildren.concat(localChildren);
}, [])
// set hasLeadingSpaces/hasTrailingSpaces and filter whitespace nodes
.reduce((newChildren, child, i, children) => {
if (child.type === TYPE_WHITESPACE) {
return newChildren;
}
const hasLeadingSpaces =
i !== 0 && children[i - 1].type === TYPE_WHITESPACE;
const hasTrailingSpaces =
i !== children.length - 1 &&
children[i + 1].type === TYPE_WHITESPACE;
return newChildren.concat(
Object.assign({}, child, {
hasLeadingSpaces,
hasTrailingSpaces
})
);
}, [])
});
});
}
function addCssDisplay(ast, options) {
return mapNode(ast, (node, stack) => {
const prevNode = getPrevNode(stack);
return Object.assign({}, node, {
cssDisplay: getNodeCssStyleDisplay(node, prevNode, options)
});
});
}
/**
* - add `isLeadingSpaceSensitive` field
* - add `isTrailingSpaceSensitive` field
* - add `isDanglingSpaceSensitive` field for parent nodes
*/
function addIsSpaceSensitive(ast /*, options */) {
return mapNode(ast, node => {
if (!node.children) {
return node;
}
if (node.children.length === 0) {
return Object.assign({}, node, {
isDanglingSpaceSensitive: isDanglingSpaceSensitiveNode(node)
});
}
return Object.assign({}, node, {
children: node.children
// set isLeadingSpaceSensitive
.map((child, i, children) => {
const prevChild = i === 0 ? null : children[i - 1];
const nextChild = i === children.length - 1 ? null : children[i + 1];
return Object.assign({}, child, {
isLeadingSpaceSensitive: isLeadingSpaceSensitiveNode(child, {
parent: node,
prev: prevChild,
next: nextChild
})
});
})
// set isTrailingSpaceSensitive and update isLeadingSpaceSensitive if necessary
.reduce((newChildren, child, i, children) => {
const prevChild = i === 0 ? null : newChildren[i - 1];
const nextChild = i === children.length - 1 ? null : children[i + 1];
const isTrailingSpaceSensitive =
nextChild && !nextChild.isLeadingSpaceSensitive
? false
: isTrailingSpaceSensitiveNode(child, {
parent: node,
prev: prevChild,
next: nextChild
});
return newChildren.concat(
Object.assign(
{},
child,
{ isTrailingSpaceSensitive },
prevChild &&
!prevChild.isTrailingSpaceSensitive &&
child.isLeadingSpaceSensitive
? { isLeadingSpaceSensitive: false }
: null
)
);
}, [])
});
});
}
function addShortcuts(ast /*, options */) {
function _addShortcuts(node, parent, index) {
const prev = index === -1 ? null : parent.children[index - 1];
const next = index === -1 ? null : parent.children[index + 1];
const hasChildren = node.children && node.children.length !== 0;
const firstChild = !hasChildren ? null : node.children[0];
const lastChild = !hasChildren
? null
: node.children[node.children.length - 1];
Object.defineProperties(node, {
parent: { value: parent, enumerable: false },
prev: { value: prev, enumerable: false },
next: { value: next, enumerable: false },
firstChild: { value: firstChild, enumerable: false },
lastChild: { value: lastChild, enumerable: false }
});
if (node.children) {
node.children.forEach((child, childIndex) =>
_addShortcuts(child, node, childIndex)
);
}
}
_addShortcuts(ast, null, -1);
return ast;
}
module.exports = preprocess;

View File

@ -1,236 +1,582 @@
"use strict";
const embed = require("./embed");
const clean = require("./clean");
const { getLast } = require("../common/util");
const { isNextLineEmpty } = require("../common/util-shared");
const {
builders: {
concat,
line,
hardline,
softline,
group,
indent,
conditionalGroup,
dedentToRoot
},
utils: { willBreak, isLineNext, isEmpty }
builders,
utils: { removeLines, stripTrailingHardline }
} = require("../doc");
const {
VOID_TAGS,
breakParent,
group,
hardline,
indent,
join,
line,
literalline,
markAsRoot,
softline
} = builders;
const { hasNewlineInRange } = require("../common/util");
const {
normalizeParts,
dedentString,
forceBreakChildren,
forceBreakContent,
forceNextEmptyLine,
getCommentData,
getLastDescendant,
hasPrettierIgnore,
isPreTagNode,
isScriptTagNode,
isTextAreaTagNode,
isWhitespaceOnlyText
inferScriptParser,
isScriptLikeTag,
preferHardlineAsLeadingSpaces,
replaceDocNewlines,
replaceNewlines
} = require("./utils");
const preprocess = require("./preprocess");
const assert = require("assert");
function genericPrint(path, options, print) {
const n = path.getValue();
function concat(parts) {
const newParts = normalizeParts(parts);
return newParts.length === 0
? ""
: newParts.length === 1
? newParts[0]
: builders.concat(newParts);
}
switch (n.type) {
case "root": {
return concat(printChildren(path, print, options));
}
case "directive": {
return concat([
"<",
n.name === "!doctype"
? n.data
.replace(/\s+/g, " ")
.replace(
/^(!doctype)(( html)?)/i,
(_, doctype, doctypeHtml) =>
doctype.toUpperCase() + doctypeHtml.toLowerCase()
)
: n.data,
">",
hardline
]);
function fill(parts) {
const newParts = [];
let hasSeparator = true;
for (const part of normalizeParts(parts)) {
switch (part) {
case line:
case hardline:
case literalline:
case softline:
newParts.push(part);
hasSeparator = true;
break;
default:
if (!hasSeparator) {
// `fill` needs a separator between each two parts
newParts.push("");
}
newParts.push(part);
hasSeparator = false;
break;
}
}
return builders.fill(newParts);
}
function embed(path, print, textToDoc /*, options */) {
const node = path.getValue();
switch (node.type) {
case "text": {
const parentNode = path.getParentNode();
if (
isPreTagNode(parentNode) ||
isTextAreaTagNode(parentNode) ||
isScriptTagNode(parentNode)
) {
return concat(
n.data.split(/(\n)/g).map((x, i) => (i % 2 === 1 ? hardline : x))
);
if (isScriptLikeTag(node.parent)) {
const parser = inferScriptParser(node.parent);
if (parser) {
return builders.concat([
concat([
breakParent,
printOpeningTagPrefix(node),
markAsRoot(
stripTrailingHardline(textToDoc(node.data, { parser }))
),
printClosingTagSuffix(node)
])
]);
}
}
return n.data.replace(/\s+/g, " ").trim();
}
case "script":
case "style":
case "tag": {
const isVoid = n.name in VOID_TAGS;
const openingPrinted = printOpeningTag(path, print, isVoid);
// Print self closing tag
if (isVoid || n.selfClosing) {
return openingPrinted;
}
const closingPrinted = printClosingTag(n);
// Print tags without children
if (n.children.length === 0) {
return concat([openingPrinted, closingPrinted]);
}
const children = printChildren(path, print, options);
if (isPreTagNode(n) || isTextAreaTagNode(n)) {
return dedentToRoot(
group(concat([openingPrinted, concat(children), closingPrinted]))
);
}
const isScriptTag = isScriptTagNode(n);
if (isScriptTag) {
return group(
concat([openingPrinted, concat(children), closingPrinted])
);
}
const containsTag = n.children.some(
child => ["script", "style", "tag"].indexOf(child.type) !== -1
);
let forcedBreak =
willBreak(openingPrinted) || containsTag || n.attributes.length > 1;
// Trim trailing lines (or empty strings)
while (
children.length &&
(isLineNext(getLast(children)) || isEmpty(getLast(children)))
) {
children.pop();
}
// Trim leading lines (or empty strings)
while (
children.length &&
(isLineNext(children[0]) || isEmpty(children[0]))
) {
children.shift();
}
// Detect whether we will force this element to output over multiple lines.
if (children.some(doc => willBreak(doc))) {
forcedBreak = true;
}
const containsOnlyEmptyTextNodes = n.children.every(isWhitespaceOnlyText);
const printedMultilineChildren = concat([
!isScriptTag && !containsOnlyEmptyTextNodes ? hardline : "",
group(concat(children), { shouldBreak: true })
]);
const multiLineElem = group(
concat([
openingPrinted,
indent(printedMultilineChildren),
hardline,
closingPrinted
])
);
if (forcedBreak) {
return multiLineElem;
}
return conditionalGroup([
group(concat([openingPrinted, concat(children), closingPrinted])),
multiLineElem
]);
}
case "comment": {
return concat(["<!--", n.data, "-->"]);
break;
}
case "attribute": {
if (n.value === null) {
return n.key;
/*
* Vue binding syntax: JS expressions
* :class="{ 'some-key': value }"
* v-bind:id="'list-' + id"
* v-if="foo && !bar"
* @click="someFunction()"
*/
if (/(^@)|(^v-)|:/.test(node.key) && !/^\w+$/.test(node.value)) {
const doc = textToDoc(node.value, {
parser: "__js_expression",
// Use singleQuote since HTML attributes use double-quotes.
// TODO(azz): We still need to do an entity escape on the attribute.
singleQuote: true
});
return concat([
node.key,
'="',
hasNewlineInRange(node.value, 0, node.value.length)
? doc
: removeLines(doc),
'"'
]);
}
return concat([n.key, '="', n.value.replace(/"/g, "&quot;"), '"']);
break;
}
// front matter
case "yaml":
case "toml":
return concat([n.raw, hardline]);
default:
/* istanbul ignore next */
throw new Error("unknown htmlparser2 type: " + n.type);
return markAsRoot(
concat([
"---",
hardline,
node.value.trim().length === 0
? ""
: replaceDocNewlines(
textToDoc(node.value, { parser: "yaml" }),
literalline
),
"---"
])
);
}
}
function printOpeningTag(path, print, isVoid) {
const n = path.getValue();
function genericPrint(path, options, print) {
const node = path.getValue();
switch (node.type) {
case "root":
return concat([group(printChildren(path, options, print)), hardline]);
case "tag":
case "ieConditionalComment":
return concat([
group(
concat([
printOpeningTag(path, options, print),
node.children.length === 0
? node.hasDanglingSpaces && node.isDanglingSpaceSensitive
? line
: ""
: concat([
forceBreakContent(node) ? breakParent : "",
indent(
concat([
node.firstChild.type === "text" &&
node.firstChild.isWhiteSpaceSensitive &&
node.firstChild.isIndentationSensitive
? literalline
: node.firstChild.hasLeadingSpaces &&
node.firstChild.isLeadingSpaceSensitive
? line
: softline,
printChildren(path, options, print)
])
),
(node.next
? needsToBorrowPrevClosingTagEndMarker(node.next)
: needsToBorrowLastChildClosingTagEndMarker(node.parent))
? ""
: node.lastChild.hasTrailingSpaces &&
node.lastChild.isTrailingSpaceSensitive
? line
: softline
])
])
),
printClosingTag(node)
]);
case "text":
return fill(
[].concat(
printOpeningTagPrefix(node),
node.isWhiteSpaceSensitive
? node.isIndentationSensitive
? replaceNewlines(
node.data.replace(/^\s*?\n|\n\s*?$/g, ""),
literalline
)
: replaceNewlines(
dedentString(node.data.replace(/^\s*?\n|\n\s*?$/g, "")),
hardline
)
: join(line, node.data.split(/\s+/)).parts,
printClosingTagSuffix(node)
)
);
case "comment":
case "directive": {
const data = getCommentData(node);
return concat([
group(
concat([
printOpeningTagStart(node),
data.trim().length === 0
? ""
: concat([
indent(
concat([
node.prev &&
needsToBorrowNextOpeningTagStartMarker(node.prev)
? breakParent
: "",
node.type === "directive" ? " " : line,
concat(replaceNewlines(data, hardline))
])
),
node.type === "directive"
? ""
: (node.next
? needsToBorrowPrevClosingTagEndMarker(node.next)
: needsToBorrowLastChildClosingTagEndMarker(node.parent))
? " "
: line
])
])
),
printClosingTagEnd(node)
]);
}
case "attribute":
return concat([
node.key,
node.value === null
? ""
: concat([
'="',
concat(
replaceNewlines(node.value.replace(/"/g, "&quot;"), literalline)
),
'"'
])
]);
case "yaml":
case "toml":
return node.raw;
default:
throw new Error(`Unexpected node type ${node.type}`);
}
}
const selfClosing = isVoid || n.selfClosing;
function printChildren(path, options, print) {
const node = path.getValue();
// Don't break self-closing elements with no attributes
if (selfClosing && !n.attributes.length) {
return concat(["<", n.name, " />"]);
if (forceBreakChildren(node)) {
return concat([
breakParent,
concat(
path.map(childPath => {
const childNode = childPath.getValue();
const prevBetweenLine = !childNode.prev
? ""
: printBetweenLine(childNode.prev, childNode);
return concat([
!prevBetweenLine
? ""
: concat([
prevBetweenLine,
forceNextEmptyLine(childNode.prev) ||
childNode.prev.endLocation.line + 1 <
childNode.startLocation.line
? hardline
: ""
]),
print(childPath)
]);
}, "children")
)
]);
}
// Don't break up opening elements with a single long text attribute
if (n.attributes && n.attributes.length === 1 && n.attributes[0].value) {
return group(
concat([
"<",
n.name,
" ",
concat(path.map(print, "attributes")),
selfClosing ? " />" : ">"
])
const parts = [];
path.map((childPath, childIndex) => {
const childNode = childPath.getValue();
if (childIndex !== 0) {
const prevBetweenLine = printBetweenLine(childNode.prev, childNode);
if (prevBetweenLine) {
if (
forceNextEmptyLine(childNode.prev) ||
childNode.prev.endLocation.line + 1 < childNode.startLocation.line
) {
parts.push(hardline, hardline);
} else {
parts.push(prevBetweenLine);
}
}
}
Array.prototype.push.apply(
parts,
childNode.type === "text" ? print(childPath).parts : [print(childPath)]
);
}
}, "children");
return group(
concat([
"<",
n.name,
indent(
concat(path.map(attr => concat([line, print(attr)]), "attributes"))
),
selfClosing ? concat([line, "/>"]) : concat([softline, ">"])
])
);
return fill(parts);
function printBetweenLine(prevNode, nextNode) {
return (needsToBorrowNextOpeningTagStartMarker(prevNode) &&
/**
* 123<a
* ~
* ><b>
*/
(nextNode.firstChild ||
/**
* 123<br />
* ~
*/
(nextNode.type === "tag" &&
nextNode.isSelfClosing &&
nextNode.attributes.length === 0))) ||
/**
* <img
* src="long"
* ~
* />123
*/
(prevNode.type === "tag" &&
prevNode.isSelfClosing &&
needsToBorrowPrevClosingTagEndMarker(nextNode))
? ""
: !nextNode.isLeadingSpaceSensitive ||
preferHardlineAsLeadingSpaces(nextNode) ||
/**
* Want to write us a letter? Use our<a
* ><b><a>mailing address</a></b></a
* ~
* >.
*/
(needsToBorrowPrevClosingTagEndMarker(nextNode) &&
prevNode.lastChild &&
needsToBorrowParentClosingTagStartMarker(prevNode.lastChild) &&
prevNode.lastChild.lastChild &&
needsToBorrowParentClosingTagStartMarker(
prevNode.lastChild.lastChild
))
? hardline
: nextNode.hasLeadingSpaces
? line
: softline;
}
}
function printOpeningTag(path, options, print) {
const node = path.getValue();
return concat([
printOpeningTagStart(node),
!node.attributes || node.attributes.length === 0
? node.isSelfClosing
? /**
* <br />
* ^
*/
" "
: ""
: group(
concat([
node.prev && needsToBorrowNextOpeningTagStartMarker(node.prev)
? /**
* 123<a
* attr
* >
*/
breakParent
: "",
indent(concat([line, join(line, path.map(print, "attributes"))])),
node.firstChild &&
needsToBorrowParentOpeningTagEndMarker(node.firstChild)
? /**
* 123<a
* attr
* ~
* >456
*/
""
: node.isSelfClosing
? line
: softline
])
),
node.isSelfClosing ? "" : printOpeningTagEnd(node)
]);
}
function printOpeningTagStart(node) {
return node.prev && needsToBorrowNextOpeningTagStartMarker(node.prev)
? ""
: concat([printOpeningTagPrefix(node), printOpeningTagStartMarker(node)]);
}
function printOpeningTagEnd(node) {
return node.firstChild &&
needsToBorrowParentOpeningTagEndMarker(node.firstChild)
? ""
: printOpeningTagEndMarker(node);
}
function printClosingTag(node) {
return concat(["</", node.name, ">"]);
return concat([
node.isSelfClosing ? "" : printClosingTagStart(node),
printClosingTagEnd(node)
]);
}
function printChildren(path, print, options) {
const parts = [];
function printClosingTagStart(node) {
return node.lastChild &&
needsToBorrowParentClosingTagStartMarker(node.lastChild)
? ""
: concat([printClosingTagPrefix(node), printClosingTagStartMarker(node)]);
}
path.map(childPath => {
const child = childPath.getValue();
function printClosingTagEnd(node) {
return (node.next
? needsToBorrowPrevClosingTagEndMarker(node.next)
: needsToBorrowLastChildClosingTagEndMarker(node.parent))
? ""
: concat([printClosingTagEndMarker(node), printClosingTagSuffix(node)]);
}
parts.push(print(childPath));
function needsToBorrowNextOpeningTagStartMarker(node) {
/**
* 123<p
* ^^
* >
*/
return (
node.next &&
node.type === "text" &&
node.isTrailingSpaceSensitive &&
!node.hasTrailingSpaces
);
}
if (child.type !== "text" && child.type !== "directive") {
parts.push(hardline);
}
function needsToBorrowParentOpeningTagEndMarker(node) {
/**
* <p
* >123
* ^
*
* <p
* ><a
* ^
*/
return !node.prev && node.isLeadingSpaceSensitive && !node.hasLeadingSpaces;
}
if (isNextLineEmpty(options.originalText, childPath.getValue(), options)) {
parts.push(hardline);
}
}, "children");
function needsToBorrowPrevClosingTagEndMarker(node) {
/**
* <p></p
* >123
* ^
*
* <p></p
* ><a
* ^
*/
return node.prev && node.isLeadingSpaceSensitive && !node.hasLeadingSpaces;
}
return parts;
function needsToBorrowLastChildClosingTagEndMarker(node) {
/**
* <p
* ><a></a
* ></p
* ^
* >
*/
return (
node.lastChild &&
node.lastChild.isTrailingSpaceSensitive &&
!node.lastChild.hasTrailingSpaces &&
getLastDescendant(node.lastChild).type !== "text"
);
}
function needsToBorrowParentClosingTagStartMarker(node) {
/**
* <p>
* 123</p
* ^^^
* >
*
* 123</b
* ></a
* ^^^
* >
*/
return (
!node.next &&
!node.hasTrailingSpaces &&
node.isTrailingSpaceSensitive &&
getLastDescendant(node).type === "text"
);
}
function printOpeningTagPrefix(node) {
return needsToBorrowParentOpeningTagEndMarker(node)
? printOpeningTagEndMarker(node.parent)
: needsToBorrowPrevClosingTagEndMarker(node)
? printClosingTagEndMarker(node.prev)
: "";
}
function printClosingTagPrefix(node) {
return needsToBorrowLastChildClosingTagEndMarker(node)
? printClosingTagEndMarker(node.lastChild)
: "";
}
function printClosingTagSuffix(node) {
return needsToBorrowParentClosingTagStartMarker(node)
? printClosingTagStartMarker(node.parent)
: needsToBorrowNextOpeningTagStartMarker(node)
? printOpeningTagStartMarker(node.next)
: "";
}
function printOpeningTagStartMarker(node) {
switch (node.type) {
case "comment":
return "<!--";
case "ieConditionalComment":
return `<!--[if ${node.condition}`;
default:
return `<${node.name}`;
}
}
function printOpeningTagEndMarker(node) {
assert(!node.isSelfClosing);
switch (node.type) {
case "ieConditionalComment":
return "]>";
default:
return `>`;
}
}
function printClosingTagStartMarker(node) {
assert(!node.isSelfClosing);
switch (node.type) {
case "ieConditionalComment":
return "<!";
default:
return `</${node.name}`;
}
}
function printClosingTagEndMarker(node) {
switch (node.type) {
case "comment":
return "-->";
case "ieConditionalComment":
return `[endif]-->`;
case "tag":
if (node.isSelfClosing) {
return "/>";
}
// fall through
default:
return ">";
}
}
module.exports = {
preprocess,
print: genericPrint,
massageAstNode: clean,
embed,

View File

@ -1,5 +1,17 @@
"use strict";
const {
builders: { concat },
utils: { mapDoc }
} = require("../doc");
const {
CSS_DISPLAY_TAGS,
CSS_DISPLAY_DEFAULT,
CSS_WHITE_SPACE_TAGS,
CSS_WHITE_SPACE_DEFAULT
} = require("./constants.evaluate");
const htmlTagNames = require("html-tag-names");
const htmlElementAttributes = require("html-element-attributes");
@ -58,64 +70,402 @@ function mapObject(object, fn) {
function hasPrettierIgnore(path) {
const node = path.getValue();
if (isWhitespaceOnlyText(node) || node.type === "attribute") {
if (node.type === "attribute") {
return false;
}
const parentNode = path.getParentNode();
if (!parentNode) {
return false;
}
const index = path.getName();
if (typeof index !== "number" || index === 0) {
return false;
}
const prevNode = parentNode.children[index - 1];
if (isPrettierIgnore(prevNode)) {
return true;
}
if (!isWhitespaceOnlyText(prevNode)) {
return false;
}
const prevPrevNode = parentNode.children[index - 2];
return prevPrevNode && isPrettierIgnore(prevPrevNode);
return isPrettierIgnore(prevNode);
}
function isPrettierIgnore(node) {
return node.type === "comment" && node.data.trim() === "prettier-ignore";
}
function isWhitespaceOnlyText(node) {
return node.type === "text" && node.data.trim().length === 0;
function isTag(node) {
return node.type === "tag";
}
function isPreTagNode(node) {
return node.type === "tag" && node.name === "pre";
function isScriptLikeTag(node) {
return isTag(node) && (node.name === "script" || node.name === "style");
}
function isTextAreaTagNode(node) {
return node.type === "tag" && node.name === "textarea";
function isFrontMatterNode(node) {
return node.type === "yaml" || node.type === "toml";
}
function isScriptTagNode(node) {
return node.type === "script" || node.type === "style";
function isLeadingSpaceSensitiveNode(node, { prev, parent }) {
if (isFrontMatterNode(node)) {
return false;
}
if (!parent || parent.cssDisplay === "none") {
return false;
}
if (
!prev &&
(parent.type === "root" ||
isScriptLikeTag(parent) ||
isBlockLikeCssDisplay(parent.cssDisplay))
) {
return false;
}
if (prev && isBlockLikeCssDisplay(prev.cssDisplay)) {
return false;
}
return true;
}
function isTrailingSpaceSensitiveNode(node, { next, parent }) {
if (isFrontMatterNode(node)) {
return false;
}
if (!parent || parent.cssDisplay === "none") {
return false;
}
if (
!next &&
(parent.type === "root" ||
isScriptLikeTag(parent) ||
isBlockLikeCssDisplay(parent.cssDisplay))
) {
return false;
}
if (next && isBlockLikeCssDisplay(next.cssDisplay)) {
return false;
}
return true;
}
function isDanglingSpaceSensitiveNode(node) {
return !isBlockLikeCssDisplay(node.cssDisplay);
}
/**
* @param {unknown} node
* @param {(node: unknown, stack: Array<string | object>)} fn
* @param {unknown=} parent
*/
function mapNode(node, fn, stack = []) {
const newNode = Object.assign({}, node);
if (newNode.children) {
newNode.children = newNode.children.map((child, childIndex) =>
mapNode(child, fn, [childIndex, node].concat(stack))
);
}
return fn(newNode, stack);
}
function getPrevNode(stack) {
const [index, parent] = stack;
if (typeof index !== "number" || index === 0) {
return null;
}
return parent.children[index - 1];
}
function replaceNewlines(text, replacement) {
return text
.split(/(\n)/g)
.map((data, index) => (index % 2 === 1 ? replacement : data));
}
function replaceDocNewlines(doc, replacement) {
return mapDoc(
doc,
currentDoc =>
typeof currentDoc === "string" && currentDoc.includes("\n")
? concat(replaceNewlines(currentDoc, replacement))
: currentDoc
);
}
function forceNextEmptyLine(node) {
return isFrontMatterNode(node);
}
/** firstChild leadingSpaces and lastChild trailingSpaces */
function forceBreakContent(node) {
return (
forceBreakChildren(node) ||
(isTag(node) &&
node.children.length !== 0 &&
(["body", "template"].indexOf(node.name) !== -1 ||
node.children.some(child => hasNonTextChild(child))))
);
}
/** spaces between children */
function forceBreakChildren(node) {
return (
isTag(node) &&
node.children.length !== 0 &&
(["html", "head", "ul", "ol", "select"].indexOf(node.name) !== -1 ||
(node.cssDisplay.startsWith("table") && node.cssDisplay !== "table-cell"))
);
}
function preferHardlineAsLeadingSpaces(node) {
return (
preferHardlineAsSurroundingSpaces(node) ||
(node.prev && preferHardlineAsTrailingSpaces(node.prev))
);
}
function preferHardlineAsTrailingSpaces(node) {
return (
preferHardlineAsSurroundingSpaces(node) ||
(isTag(node) && node.name === "br")
);
}
function preferHardlineAsSurroundingSpaces(node) {
switch (node.type) {
case "ieConditionalComment":
case "comment":
case "directive":
return true;
case "tag":
return ["script", "select"].indexOf(node.name) !== -1;
}
return false;
}
function getLastDescendant(node) {
return node.lastChild ? getLastDescendant(node.lastChild) : node;
}
function hasNonTextChild(node) {
return node.children && node.children.some(child => child.type !== "text");
}
function inferScriptParser(node) {
if (
node.name === "script" &&
((!node.attribs.lang && !node.attribs.type) ||
node.attribs.type === "text/javascript" ||
node.attribs.type === "text/babel" ||
node.attribs.type === "application/javascript")
) {
return "babylon";
}
if (
node.name === "script" &&
(node.attribs.type === "application/x-typescript" ||
node.attribs.lang === "ts")
) {
return "typescript";
}
if (node.name === "style") {
return "css";
}
return null;
}
/**
* firstChild leadingSpaces, lastChild trailingSpaces, and danglingSpaces are insensitive
*/
function isBlockLikeCssDisplay(cssDisplay) {
return cssDisplay === "block" || cssDisplay.startsWith("table");
}
function getNodeCssStyleDisplay(node, prevNode, options) {
switch (getNodeCssStyleWhiteSpace(node)) {
case "pre":
case "pre-wrap":
// textarea-like
return "block";
}
if (prevNode && prevNode.type === "comment") {
// <!-- display: block -->
const match = prevNode.data.match(/^\s*display:\s*([a-z]+)\s*$/);
if (match) {
return match[1];
}
}
switch (options.htmlWhitespaceSensitivity) {
case "strict":
return "inline";
case "ignore":
return "block";
default:
return (
(isTag(node) && CSS_DISPLAY_TAGS[node.name]) || CSS_DISPLAY_DEFAULT
);
}
}
function getNodeCssStyleWhiteSpace(node) {
return (
(isTag(node) && CSS_WHITE_SPACE_TAGS[node.name]) || CSS_WHITE_SPACE_DEFAULT
);
}
function getCommentData(node) {
const rightTrimmedData = node.data.trimRight();
const hasLeadingEmptyLine = /^[^\S\n]*?\n/.test(node.data);
if (hasLeadingEmptyLine) {
/**
* <!--
* 123
* 456
* -->
*/
return dedentString(rightTrimmedData.replace(/^\s*\n/, ""));
}
/**
* <!-- 123 -->
*
* <!-- 123
* -->
*
* <!-- 123
*
* -->
*/
if (!rightTrimmedData.includes("\n")) {
return rightTrimmedData.trimLeft();
}
const firstNewlineIndex = rightTrimmedData.indexOf("\n");
const dataWithoutLeadingLine = rightTrimmedData.slice(firstNewlineIndex + 1);
const minIndentationForDataWithoutLeadingLine = getMinIndentation(
dataWithoutLeadingLine
);
const commentDataStartColumn = node.startLocation.column + "<!--".length;
/**
* <!-- 123
* 456 -->
*/
if (minIndentationForDataWithoutLeadingLine >= commentDataStartColumn) {
return dedentString(
" ".repeat(commentDataStartColumn) + "\n" + rightTrimmedData
);
}
const leadingLineData = rightTrimmedData.slice(0, firstNewlineIndex);
/**
* <!-- 123
* 456 -->
*/
return (
leadingLineData.trim() +
"\n" +
dedentString(
dataWithoutLeadingLine,
minIndentationForDataWithoutLeadingLine
)
);
}
function getMinIndentation(text) {
let minIndentation = Infinity;
for (const lineText of text.split("\n")) {
if (/\S/.test(lineText[0])) {
return 0;
}
const indentation = lineText.match(/^\s*/)[0].length;
if (lineText.length === indentation) {
continue;
}
if (indentation < minIndentation) {
minIndentation = indentation;
}
}
return minIndentation === Infinity ? 0 : minIndentation;
}
function dedentString(text, minIndent = getMinIndentation(text)) {
return minIndent === 0
? text
: text
.split("\n")
.map(lineText => lineText.slice(minIndent))
.join("\n");
}
function normalizeParts(parts) {
const newParts = [];
for (const part of parts) {
if (!part) {
continue;
}
if (
newParts.length !== 0 &&
typeof newParts[newParts.length - 1] === "string" &&
typeof part === "string"
) {
newParts.push(newParts.pop() + part);
continue;
}
newParts.push(part);
}
return newParts;
}
module.exports = {
HTML_ELEMENT_ATTRIBUTES,
HTML_TAGS,
VOID_TAGS,
dedentString,
forceBreakChildren,
forceBreakContent,
forceNextEmptyLine,
getCommentData,
getLastDescendant,
getNodeCssStyleDisplay,
getNodeCssStyleWhiteSpace,
getPrevNode,
hasPrettierIgnore,
isPreTagNode,
isScriptTagNode,
isTextAreaTagNode,
isWhitespaceOnlyText
inferScriptParser,
isDanglingSpaceSensitiveNode,
isFrontMatterNode,
isLeadingSpaceSensitiveNode,
isScriptLikeTag,
isTrailingSpaceSensitiveNode,
mapNode,
normalizeParts,
preferHardlineAsLeadingSpaces,
preferHardlineAsTrailingSpaces,
replaceDocNewlines,
replaceNewlines
};

View File

@ -61,6 +61,22 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS.">
</article>
<X>
</X>
<X a="1">
</X>
<X a="1" b="2">
</X>
<X a="1" b="2" c="3">
</X>
<p
class="
foo
bar
baz
"
>
</p>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<input name="address" maxlength="200" />
<input name="address" maxlength="200" />
@ -96,8 +112,7 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS."
data-columns="3"
data-index-number="12314"
data-parent="cars"
>
</article>
></article>
<article
id="electriccars"
data-columns="3"
@ -119,15 +134,21 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS."
data-columns="3"
data-index-number="12314"
data-parent="cars"
>
</article>
></article>
<article
id="electriccars"
data-columns="3"
data-index-number="12314"
data-parent="cars"
>
</article>
></article>
<X> </X> <X a="1"> </X> <X a="1" b="2"> </X> <X a="1" b="2" c="3"> </X>
<p
class="
foo
bar
baz
"
></p>
`;
@ -151,42 +172,36 @@ exports[`boolean.html - html-verify 1`] = `
<div lang=""></div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<button type="submit">This is valid.</button>
<button type="submit" disabled>
This is valid.
</button>
<button type="submit" disabled="">
This is valid.
</button>
<button type="submit" disabled="disabled">
This is valid.
</button>
<button type="submit" disabled="true">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="true">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="true">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="false">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="false">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="false">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="hahah">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="hahah">
This is valid. This will be disabled.
</button>
<button type="submit" disabled="hahah">
This is valid. This will be disabled.
</button>
<button type="submit" disabled>This is valid.</button>
<button type="submit" disabled="">This is valid.</button>
<button type="submit" disabled="disabled">This is valid.</button>
<button type="submit" disabled="true"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="true"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="true"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="false"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="false"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="false"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="hahah"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="hahah"
>This is valid. This will be disabled.</button
>
<button type="submit" disabled="hahah"
>This is valid. This will be disabled.</button
>
<input type="checkbox" checked disabled name="cheese" />
<input type="checkbox" checked="checked" disabled="disabled" name="cheese" />
<input type="checkbox" checked="" disabled="" name="cheese" />
@ -208,6 +223,13 @@ exports[`dobule-quotes.html - html-verify 1`] = `
`;
exports[`duplicate.html - html-verify 1`] = `
<a href="1" href="2">123</a>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<a href="1" href="2">123</a>
`;
exports[`single-quotes.html - html-verify 1`] = `
<img src="test.png" alt='John "ShotGun" Nelson'>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -58,3 +58,19 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS.">
</article>
<X>
</X>
<X a="1">
</X>
<X a="1" b="2">
</X>
<X a="1" b="2" c="3">
</X>
<p
class="
foo
bar
baz
"
>
</p>

View File

@ -0,0 +1 @@
<a href="1" href="2">123</a>

View File

@ -3,7 +3,7 @@
exports[`comment.html - html-verify 1`] = `
<!--hello world-->
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!--hello world-->
<!-- hello world -->
`;
@ -107,9 +107,9 @@ exports[`form.html - html-verify 1`] = `
aria-describedby="emailHelp"
placeholder="Enter email"
/>
<small id="emailHelp" class="form-text text-muted">
We'll never share your email with anyone else.
</small>
<small id="emailHelp" class="form-text text-muted"
>We'll never share your email with anyone else.</small
>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
@ -152,9 +152,10 @@ exports[`form.html - html-verify 1`] = `
id="exampleInputFile"
aria-describedby="fileHelp"
/>
<small id="fileHelp" class="form-text text-muted">
This is some placeholder block-level help text for the above input. It's a bit lighter and easily wraps to a new line.
</small>
<small id="fileHelp" class="form-text text-muted"
>This is some placeholder block-level help text for the above input. It's
a bit lighter and easily wraps to a new line.</small
>
</div>
<fieldset class="form-group">
<legend>Radio buttons</legend>
@ -180,7 +181,8 @@ exports[`form.html - html-verify 1`] = `
id="optionsRadios2"
value="option2"
/>
Option two can be something else and selecting it will deselect option one
Option two can be something else and selecting it will deselect option
one
</label>
</div>
<div class="form-check disabled">
@ -199,13 +201,10 @@ exports[`form.html - html-verify 1`] = `
</fieldset>
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" class="form-check-input" />
Check me out
<input type="checkbox" class="form-check-input" /> Check me out
</label>
</div>
<button type="submit" class="btn btn-primary">
Submit
</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
`;
@ -336,7 +335,11 @@ exports[`html5-boilerplate.html - html-verify 1`] = `
<body>
<!--[if lte IE 9]>
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
<p class="browserupgrade">
You are using an <strong>outdated</strong> browser. Please
<a href="https://browsehappy.com/">upgrade your browser</a> to improve
your experience and security.
</p>
<![endif]-->
<!-- Add your site or application content here -->
@ -348,21 +351,21 @@ exports[`html5-boilerplate.html - html-verify 1`] = `
crossorigin="anonymous"
></script>
<script>
window.jQuery ||
document.write('<script src="js/vendor/jquery-3.3.1.min.js"></script>');
window.jQuery ||
document.write('<script src="js/vendor/jquery-3.3.1.min.js"></script>');
</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
<!-- Google Analytics: change UA-XXXXX-Y to be your site's ID. -->
<script>
window.ga = function() {
ga.q.push(arguments);
};
ga.q = [];
ga.l = +new Date();
ga("create", "UA-XXXXX-Y", "auto");
ga("send", "pageview");
window.ga = function() {
ga.q.push(arguments);
};
ga.q = [];
ga.l = +new Date();
ga("create", "UA-XXXXX-Y", "auto");
ga("send", "pageview");
</script>
<script
src="https://www.google-analytics.com/analytics.js"

View File

@ -31,13 +31,13 @@ exports[`case.html - html-verify 1`] = `
This is HTML5 Boilerplate.
</p>
<script>
window.ga = function() {
ga.q.push(arguments);
};
ga.q = [];
ga.l = +new Date();
ga("create", "UA-XXXXX-Y", "auto");
ga("send", "pageview");
window.ga = function() {
ga.q.push(arguments);
};
ga.q = [];
ga.l = +new Date();
ga("create", "UA-XXXXX-Y", "auto");
ga("send", "pageview");
</script>
<script
src="https://www.google-analytics.com/analytics.js"

View File

@ -1,5 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`before-text.html - html-verify 1`] = `
<!-- hello -->
123
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!-- hello -->
123
`;
exports[`before-text.html - html-verify 2`] = `
<!-- hello -->
123
~
<!--
hello
-->
123
`;
exports[`before-text.html - html-verify 3`] = `
<!-- hello -->
123
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!-- hello -->
123
`;
exports[`conditional.html - html-verify 1`] = `
<!DOCTYPE html>
<html>
@ -17,11 +52,90 @@ exports[`conditional.html - html-verify 1`] = `
<!DOCTYPE html>
<html>
<body>
<!--[if IE 5]>This is IE 5<br /><![endif]-->
<!--[if IE 6]>This is IE 6<br /><![endif]-->
<!--[if IE 7]>This is IE 7<br /><![endif]-->
<!--[if IE 8]>This is IE 8<br /><![endif]-->
<!--[if IE 9]>This is IE 9<br /><![endif]-->
</body>
</html>
`;
exports[`conditional.html - html-verify 2`] = `
<!DOCTYPE html>
<html>
<body>
<!--[if IE 5]>This is IE 5<br><![endif]-->
<!--[if IE 6]>This is IE 6<br><![endif]-->
<!--[if IE 7]>This is IE 7<br><![endif]-->
<!--[if IE 8]>This is IE 8<br><![endif]-->
<!--[if IE 9]>This is IE 9<br><![endif]-->
</body>
</html>
~
<!DOCTYPE html>
<html>
<body>
<!--[if IE 5
]>This
is
IE
5<br
/><![endif]-->
<!--[if IE 6
]>This
is
IE
6<br
/><![endif]-->
<!--[if IE 7
]>This
is
IE
7<br
/><![endif]-->
<!--[if IE 8
]>This
is
IE
8<br
/><![endif]-->
<!--[if IE 9
]>This
is
IE
9<br
/><![endif]-->
</body>
</html>
`;
exports[`conditional.html - html-verify 3`] = `
<!DOCTYPE html>
<html>
<body>
<!--[if IE 5]>This is IE 5<br><![endif]-->
<!--[if IE 6]>This is IE 6<br><![endif]-->
<!--[if IE 7]>This is IE 7<br><![endif]-->
<!--[if IE 8]>This is IE 8<br><![endif]-->
<!--[if IE 9]>This is IE 9<br><![endif]-->
</body>
</html>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!DOCTYPE html>
<html>
<body>
<!--[if IE 5]>This is IE 5<br /><![endif]-->
<!--[if IE 6]>This is IE 6<br /><![endif]-->
<!--[if IE 7]>This is IE 7<br /><![endif]-->
<!--[if IE 8]>This is IE 8<br /><![endif]-->
<!--[if IE 9]>This is IE 9<br /><![endif]-->
</body>
</html>
@ -50,17 +164,104 @@ exports[`for_debugging.html - html-verify 1`] = `
<!DOCTYPE html>
<html>
<body>
<!-- Do not display this at the moment
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
</body>
</html>
`;
exports[`for_debugging.html - html-verify 2`] = `
<!DOCTYPE html>
<html>
<body>
<!-- Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!-- Do not display this at the moment
<!-- Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!-- Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
</body>
</html>
~
<!DOCTYPE html>
<html>
<body>
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
</body>
</html>
`;
exports[`for_debugging.html - html-verify 3`] = `
<!DOCTYPE html>
<html>
<body>
<!-- Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!-- Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!-- Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
</body>
</html>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!DOCTYPE html>
<html>
<body>
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
<!--
Do not display this at the moment
<img border="0" src="pic_trulli.jpg" alt="Trulli">
-->
</body>
</html>
@ -84,13 +285,348 @@ exports[`hidden.html - html-verify 1`] = `
<!DOCTYPE html>
<html>
<body>
<!--This is a comment-->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<p>This is a paragraph.</p>
<!-- Comments are not displayed in the browser -->
</body>
</html>
`;
exports[`hidden.html - html-verify 2`] = `
<!DOCTYPE html>
<html>
<body>
<!--This is a comment-->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<p>This is a paragraph.</p>
<!-- Comments are not displayed in the browser -->
</body>
</html>
~
<!DOCTYPE html>
<html>
<body>
<!--
This is a comment
-->
<!--
This is a comment
-->
<!--
This is a comment
-->
<!--
This is a comment
-->
<p>
This
is
a
paragraph.
</p>
<!--
Comments are not displayed in the browser
-->
</body>
</html>
`;
exports[`hidden.html - html-verify 3`] = `
<!DOCTYPE html>
<html>
<body>
<!--This is a comment-->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<p>This is a paragraph.</p>
<!-- Comments are not displayed in the browser -->
</body>
</html>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!DOCTYPE html>
<html>
<body>
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<!-- This is a comment -->
<p>This is a paragraph.</p>
<!-- Comments are not displayed in the browser -->
</body>
</html>
`;
exports[`surrounding-empty-line.html - html-verify 1`] = `
<ul><!-- 123
--><li>First</li><!-- 123
456
789
--><li>Second</li><!--
123
456
789
--><li>Second</li><!--
123
456
789
--></ul>
<span><!--
--><span>a</span><!--
--><span>b</span><!--
--></span>
<span><!-- 1
--><span>a</span><!-- 2
--><span>b</span><!-- 3
--></span>
<span><!--
1 --><span>a</span><!--
2 --><span>b</span><!--
3 --></span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<ul>
<!-- 123
--><li>First</li
><!--
123
456
789
--><li>Second</li
><!--
123
456
789
--><li>Second</li
><!--
123
456
789
-->
</ul>
<span
><!--
--><span>a</span
><!--
--><span>b</span
><!--
--></span>
<span
><!-- 1
--><span>a</span
><!-- 2
--><span>b</span
><!-- 3
--></span>
<span
><!-- 1
--><span>a</span
><!-- 2
--><span>b</span
><!-- 3
--></span>
`;
exports[`surrounding-empty-line.html - html-verify 2`] = `
<ul><!-- 123
--><li>First</li><!-- 123
456
789
--><li>Second</li><!--
123
456
789
--><li>Second</li><!--
123
456
789
--></ul>
<span><!--
--><span>a</span><!--
--><span>b</span><!--
--></span>
<span><!-- 1
--><span>a</span><!-- 2
--><span>b</span><!-- 3
--></span>
<span><!--
1 --><span>a</span><!--
2 --><span>b</span><!--
3 --></span>
~
<ul>
<!--
123
--><li
>First</li
><!--
123
456
789
--><li
>Second</li
><!--
123
456
789
--><li
>Second</li
><!--
123
456
789
-->
</ul>
<span
><!--
--><span
>a</span
><!--
--><span
>b</span
><!--
--></span>
<span
><!--
1
--><span
>a</span
><!--
2
--><span
>b</span
><!--
3
--></span>
<span
><!--
1
--><span
>a</span
><!--
2
--><span
>b</span
><!--
3
--></span>
`;
exports[`surrounding-empty-line.html - html-verify 3`] = `
<ul><!-- 123
--><li>First</li><!-- 123
456
789
--><li>Second</li><!--
123
456
789
--><li>Second</li><!--
123
456
789
--></ul>
<span><!--
--><span>a</span><!--
--><span>b</span><!--
--></span>
<span><!-- 1
--><span>a</span><!-- 2
--><span>b</span><!-- 3
--></span>
<span><!--
1 --><span>a</span><!--
2 --><span>b</span><!--
3 --></span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<ul>
<!-- 123
--><li>First</li
><!--
123
456
789
--><li>Second</li
><!--
123
456
789
--><li>Second</li
><!--
123
456
789
-->
</ul>
<span
><!--
--><span>a</span
><!--
--><span>b</span
><!--
--></span>
<span
><!-- 1
--><span>a</span
><!-- 2
--><span>b</span
><!-- 3
--></span>
<span
><!-- 1
--><span>a</span
><!-- 2
--><span>b</span
><!-- 3
--></span>
`;

View File

@ -0,0 +1,3 @@
<!-- hello -->
123

View File

@ -1 +1,3 @@
run_spec(__dirname, ["html"]);
run_spec(__dirname, ["html"], { printWidth: 1 });
run_spec(__dirname, ["html"], { printWidth: 999 });

View File

@ -0,0 +1,35 @@
<ul><!-- 123
--><li>First</li><!-- 123
456
789
--><li>Second</li><!--
123
456
789
--><li>Second</li><!--
123
456
789
--></ul>
<span><!--
--><span>a</span><!--
--><span>b</span><!--
--></span>
<span><!-- 1
--><span>a</span><!-- 2
--><span>b</span><!-- 3
--></span>
<span><!--
1 --><span>a</span><!--
2 --><span>b</span><!--
3 --></span>

View File

@ -27,21 +27,21 @@ exports[`less.html - html-verify 1`] = `
</style>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<style type="text/less">
@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;
@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;
#header {
color: @light-blue;
}
#header {
color: @light-blue;
}
</style>
<style lang="less">
@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;
@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;
#header {
color: @light-blue;
}
#header {
color: @light-blue;
}
</style>
`;
@ -56,17 +56,17 @@ exports[`postcss.html - html-verify 1`] = `
</style>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<style type="text/css">
body {
background: navy;
color: yellow;
}
body {
background: navy;
color: yellow;
}
</style>
<style lang="postcss">
body {
background: navy;
color: yellow;
}
body {
background: navy;
color: yellow;
}
</style>
`;
@ -93,23 +93,23 @@ exports[`scss.html - html-verify 1`] = `
</style>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<style type="text/x-scss">
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
body {
font: 100% $font-stack;
color: $primary-color;
}
</style>
<style lang="scss">
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
body {
font: 100% $font-stack;
color: $primary-color;
}
</style>
`;
@ -135,15 +135,15 @@ exports[`simple.html - html-verify 1`] = `
<head>
<title>Sample styled page</title>
<style>
a {
color: red;
}
a {
color: red;
}
</style>
<style>
body {
background: navy;
color: yellow;
}
body {
background: navy;
color: yellow;
}
</style>
</head>
<body>
@ -165,16 +165,16 @@ exports[`single-style.html - html-verify 1`] = `
</style>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<style>
a {
color: red;
}
a {
color: red;
}
</style>
<style>
h1 {
font-size: 120%;
font-family: Verdana, Arial, Helvetica, sans-serif;
color: #333366;
}
h1 {
font-size: 120%;
font-family: Verdana, Arial, Helvetica, sans-serif;
color: #333366;
}
</style>
`;

View File

@ -152,9 +152,7 @@ exports[`xhtml1.1.html - html-verify 1`] = `
Bar<br />
Foo
</p>
<p align="center">
<em>String</em>
</p>
<p align="center"><em>String</em></p>
<br />
<hr />
</body>

View File

@ -28,22 +28,22 @@ exports[`js.html - html-verify 1`] = `
</script>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<script type="text/javascript">
var message = "Alert!";
var message = "Alert!";
alert(message);
alert(message);
</script>
<script type="application/javascript">
var message = "Alert!";
var message = "Alert!";
alert(message);
alert(message);
</script>
<script>
var message = "Alert!";
var message = "Alert!";
alert(message);
alert(message);
</script>
<script type="text/babel">
const someJS = "this should be formatted";
const someJS = "this should be formatted";
</script>
`;
@ -71,12 +71,12 @@ exports[`simple.html - html-verify 1`] = `
<head>
<title>Sample styled page</title>
<script>
alert("test");
alert("test");
</script>
<script>
var message = "Alert!";
var message = "Alert!";
alert(message);
alert(message);
</script>
</head>
<body>
@ -94,10 +94,10 @@ exports[`single-script.html - html-verify 1`] = `
</script>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<script>
alert("test");
alert("test");
</script>
<script>
document.getElementById("demo").innerHTML = "Hello JavaScript!";
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
`;
@ -109,8 +109,8 @@ exports[`something-else.html - html-verify 1`] = `
</script>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<script type="text/template">
<div>
</div>
<div>
</div>
</script>
`;
@ -196,54 +196,54 @@ exports[`typescript.html - html-verify 1`] = `
</script>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<script type="application/x-typescript">
class Student {
fullName: string;
constructor(
public firstName: string,
public middleInitial: string,
public lastName: string
) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
class Student {
fullName: string;
constructor(
public firstName: string,
public middleInitial: string,
public lastName: string
) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
}
interface Person {
firstName: string;
lastName: string;
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
let user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
document.body.innerHTML = greeter(user);
</script>
<script lang="ts">
class Student {
fullName: string;
constructor(
public firstName: string,
public middleInitial: string,
public lastName: string
) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
class Student {
fullName: string;
constructor(
public firstName: string,
public middleInitial: string,
public lastName: string
) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
}
interface Person {
firstName: string;
lastName: string;
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
let user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
document.body.innerHTML = greeter(user);
</script>
<script lang="tsx">
class CommentBox extends React.Component<{ url: string, pollInterval: number}, CommentData> {

View File

@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`display.html - html-verify 1`] = `
<div>
<!-- display: inline -->
<p>Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long</p>
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div>
<!-- display: inline -->
<p
>Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long
Long Long Long</p
>
</div>
`;

View File

@ -0,0 +1,4 @@
<div>
<!-- display: inline -->
<p>Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long</p>
</div>

View File

@ -0,0 +1 @@
run_spec(__dirname, ["html"]);

View File

@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`standalone-end-marker.html - html-verify 1`] = `
<div></div
>
<span></span>
<div></div
>
<span></span>
<div></div
>
<span></span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div></div>
<span></span>
<div></div>
<span></span>
<div></div>
<span></span>
`;

View File

@ -0,0 +1 @@
run_spec(__dirname, ["html"]);

View File

@ -0,0 +1,12 @@
<div></div
>
<span></span>
<div></div
>
<span></span>
<div></div
>
<span></span>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
<div>
aaaaaaaaaa
<a
href="longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong"
>bbbbbbbbbb</a
>
cccccccccc
</div>
<div>
aaaaaaaaaa
<a
href="longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong"
>bbbbbbbbbb</a
>cccccccccc
</div>

View File

@ -1 +1,5 @@
run_spec(__dirname, ["html"]);
run_spec(__dirname, ["html"], { printWidth: 1 });
run_spec(__dirname, ["html"], { printWidth: 999 });
run_spec(__dirname, ["html"], { htmlWhitespaceSensitivity: "strict" });
run_spec(__dirname, ["html"], { htmlWhitespaceSensitivity: "ignore" });

View File

@ -0,0 +1,29 @@
<p
>Want to write us a letter? Use our<a
><b
><a>mailing address</a></b
></a
>.</p
>
<p
>Want to write us a letter? Use our<a
href="contacts.html#Mailing_address"
><b
><a>mailing address</a></b
></a
>.</p
>
<p
>Want to write us a letter? Use our<a
href="contacts.html#Mailing_address"
href1="contacts.html#Mailing_address"
href2="contacts.html#Mailing_address"
href3="contacts.html#Mailing_address"
href4="contacts.html#Mailing_address"
><b
><a>mailing address</a></b
></a
>.</p
>

View File

@ -47,3 +47,15 @@
<div>string</div>
</div>
<ul
>123<li
class="foo"
id="bar"
>First</li
>456<li
class="baz"
>Second</li
>789</ul
>
<span>*<b>200</b></span>
<img src="longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong" />123

View File

@ -0,0 +1,170 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`break-tags.html - html-verify 1`] = `
<a>Lorem</a>, ispum dolor sit <strong>amet</strong>.
<div><a>Lorem</a>, ispum dolor sit <strong>amet</strong>.</div>
<div><div><a>Lorem</a>, ispum dolor sit <strong>amet</strong>.</div></div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<a>Lorem</a>, ispum dolor sit <strong>amet</strong>.
<div><a>Lorem</a>, ispum dolor sit <strong>amet</strong>.</div>
<div>
<div><a>Lorem</a>, ispum dolor sit <strong>amet</strong>.</div>
</div>
`;
exports[`display-none.html - html-verify 1`] = `
<!DOCTYPE html><HTML CLASS="no-js mY-ClAsS"><HEAD><META CHARSET="utf-8"><TITLE>My tITlE</TITLE><META NAME="description" content="My CoNtEnT"></HEAD></HTML>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!DOCTYPE html>
<html class="no-js mY-ClAsS">
<head>
<meta charset="utf-8" />
<title>My tITlE</title>
<meta name="description" content="My CoNtEnT" />
</head>
</html>
`;
exports[`fill.html - html-verify 1`] = `
<p>
<img
src="/images/pansies.jpg"
alt="about fedco bottom image"
style="float: left;"
/><strong>We are a cooperative</strong>, one of the few seed companies so organized
in the United States. Because we do not have an individual owner or beneficiary,
profit is not our primary goal. Consumers own 60% of the cooperative and worker
members 40%. Consumer and worker members share proportionately in the cooperative&#8217;s
profits through our annual patronage dividends.
</p>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<p>
<img
src="/images/pansies.jpg"
alt="about fedco bottom image"
style="float: left;"
/><strong>We are a cooperative</strong>, one of the few seed companies so
organized in the United States. Because we do not have an individual owner or
beneficiary, profit is not our primary goal. Consumers own 60% of the
cooperative and worker members 40%. Consumer and worker members share
proportionately in the cooperative&#8217;s profits through our annual
patronage dividends.
</p>
`;
exports[`inline-leading-trailing-spaces.html - html-verify 1`] = `
<span> 321 </span>
<span> <a>321</a> </span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<span> 321 </span>
<span> <a>321</a> </span>
`;
exports[`inline-nodes.html - html-verify 1`] = `
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue
vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim.
Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh.
Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui.
Sed eu scelerisque neque. Donec <b>maximus</b> rhoncus pellentesque. Aenean purus turpis, vehicula
euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos. Donec in ornare velit.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue
vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim.
Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh.
Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui.
Sed eu scelerisque neque. Donec <a href="#"><b>maximus</b></a> rhoncus pellentesque. Aenean purus turpis, vehicula
euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos. Donec in ornare velit.</p>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa
vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet
urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed
ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse
vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque
neque. Donec <b>maximus</b> rhoncus pellentesque. Aenean purus turpis,
vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec
in ornare velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa
vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet
urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed
ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse
vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque
neque. Donec <a href="#"><b>maximus</b></a> rhoncus pellentesque. Aenean purus
turpis, vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec
in ornare velit.
</p>
`;
exports[`table.html - html-verify 1`] = `
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
</table>
<table><thead><tr><th>A</th><th>B</th><th>C</th></tr></thead></table>
<table> <thead> <tr> <th> A </th> <th> B </th> <th> C </th> </tr> </thead> </table>
<table>
<thead>
<tr>
</tr>
</thead>
</table>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr></tr>
</thead>
</table>
`;

View File

@ -0,0 +1,3 @@
<a>Lorem</a>, ispum dolor sit <strong>amet</strong>.
<div><a>Lorem</a>, ispum dolor sit <strong>amet</strong>.</div>
<div><div><a>Lorem</a>, ispum dolor sit <strong>amet</strong>.</div></div>

View File

@ -0,0 +1 @@
<!DOCTYPE html><HTML CLASS="no-js mY-ClAsS"><HEAD><META CHARSET="utf-8"><TITLE>My tITlE</TITLE><META NAME="description" content="My CoNtEnT"></HEAD></HTML>

View File

@ -0,0 +1,11 @@
<p>
<img
src="/images/pansies.jpg"
alt="about fedco bottom image"
style="float: left;"
/><strong>We are a cooperative</strong>, one of the few seed companies so organized
in the United States. Because we do not have an individual owner or beneficiary,
profit is not our primary goal. Consumers own 60% of the cooperative and worker
members 40%. Consumer and worker members share proportionately in the cooperative&#8217;s
profits through our annual patronage dividends.
</p>

View File

@ -0,0 +1,3 @@
<span> 321 </span>
<span> <a>321</a> </span>

View File

@ -0,0 +1,15 @@
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue
vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim.
Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh.
Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui.
Sed eu scelerisque neque. Donec <b>maximus</b> rhoncus pellentesque. Aenean purus turpis, vehicula
euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos. Donec in ornare velit.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue
vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim.
Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh.
Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui.
Sed eu scelerisque neque. Donec <a href="#"><b>maximus</b></a> rhoncus pellentesque. Aenean purus turpis, vehicula
euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos. Donec in ornare velit.</p>

View File

@ -0,0 +1 @@
run_spec(__dirname, ["html"]);

View File

@ -0,0 +1,20 @@
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
</table>
<table><thead><tr><th>A</th><th>B</th><th>C</th></tr></thead></table>
<table> <thead> <tr> <th> A </th> <th> B </th> <th> C </th> </tr> </thead> </table>
<table>
<thead>
<tr>
</tr>
</thead>
</table>

View File

@ -18,9 +18,9 @@ exports[`html-with-css-style.html - html-verify 1`] = `
<html lang="en">
<head>
<style>
blink {
display: none;
}
blink {
display: none;
}
</style>
</head>
<body></body>

View File

@ -17,7 +17,7 @@ exports[`html-with-js-script.html - html-verify 1`] = `
<html lang="en">
<head>
<script type="text/javascript">
hello("world");
hello("world");
</script>
</head>
<body></body>

View File

@ -26,11 +26,11 @@ exports[`html-with-ts-script.html - html-verify 1`] = `
<html lang="en">
<head>
<script lang="ts">
type X = { [K in keyof Y]: Partial<K> };
type X = { [K in keyof Y]: Partial<K> };
class Foo<T> {
constructor(private foo: keyof Apple) {}
}
class Foo<T> {
constructor(private foo: keyof Apple) {}
}
</script>
</head>
<body></body>

View File

@ -60,6 +60,9 @@ Format options:
Include parentheses around a sole arrow function parameter.
Defaults to avoid.
--no-bracket-spacing Do not print spaces between brackets.
--html-whitespace-sensitivity <css|strict|ignore>
How to handle whitespaces in HTML.
Defaults to css.
--jsx-bracket-same-line Put > on the last line instead of at a new line.
Defaults to false.
--parser <flow|babylon|typescript|css|less|scss|json|json5|json-stringify|graphql|markdown|mdx|vue|yaml|html>
@ -197,6 +200,9 @@ Format options:
Include parentheses around a sole arrow function parameter.
Defaults to avoid.
--no-bracket-spacing Do not print spaces between brackets.
--html-whitespace-sensitivity <css|strict|ignore>
How to handle whitespaces in HTML.
Defaults to css.
--jsx-bracket-same-line Put > on the last line instead of at a new line.
Defaults to false.
--parser <flow|babylon|typescript|css|less|scss|json|json5|json-stringify|graphql|markdown|mdx|vue|yaml|html>

View File

@ -138,6 +138,25 @@ exports[`show detailed usage with --help help (stdout) 1`] = `
exports[`show detailed usage with --help help (write) 1`] = `Array []`;
exports[`show detailed usage with --help html-whitespace-sensitivity (stderr) 1`] = `""`;
exports[`show detailed usage with --help html-whitespace-sensitivity (stdout) 1`] = `
"--html-whitespace-sensitivity <css|strict|ignore>
How to handle whitespaces in HTML.
Valid options:
css Respect the default value of CSS display property.
strict Whitespaces are considered sensitive.
ignore Whitespaces are considered insensitive.
Default: css
"
`;
exports[`show detailed usage with --help html-whitespace-sensitivity (write) 1`] = `Array []`;
exports[`show detailed usage with --help ignore-path (stderr) 1`] = `""`;
exports[`show detailed usage with --help ignore-path (stdout) 1`] = `

View File

@ -13,11 +13,11 @@ exports[` 1`] = `
--no-bracket-spacing Do not print spaces between brackets.
+ --foo-option <bar|baz> foo description
+ Defaults to bar.
--html-whitespace-sensitivity <css|strict|ignore>
How to handle whitespaces in HTML.
Defaults to css.
--jsx-bracket-same-line Put > on the last line instead of at a new line.
Defaults to false.
--parser <flow|babylon|typescript|css|less|scss|json|json5|json-stringify|graphql|markdown|mdx|vue|yaml|html>
Which parser to use.
--print-width <int> The line length where Prettier will try wrap."
Defaults to false."
`;
exports[`show detailed external option with \`--help foo-option\` (stderr) 1`] = `""`;

View File

@ -48,6 +48,30 @@ This option cannot be used with --range-start and --range-end.",
"description": "Specify the input filepath. This will be used to do parser inference.",
"type": "string",
},
"htmlWhitespaceSensitivity": Object {
"default": "css",
"description": "How to handle whitespaces in HTML.",
"oneOf": Array [
Object {
"description": "Respect the default value of CSS display property.",
"enum": Array [
"css",
],
},
Object {
"description": "Whitespaces are considered sensitive.",
"enum": Array [
"strict",
],
},
Object {
"description": "Whitespaces are considered insensitive.",
"enum": Array [
"ignore",
],
},
],
},
"insertPragma": Object {
"default": false,
"description": "Insert @format pragma into file's first docblock comment.",

View File

@ -479,7 +479,27 @@ exports[`API getSupportInfo() with version 1.8.2 -> undefined 1`] = `
\\"type\\": \\"boolean\\",
},
\\"cursorOffset\\": Object {
@@ -73,16 +99,30 @@
@@ -56,10 +82,19 @@
},
\\"filepath\\": Object {
\\"default\\": undefined,
\\"type\\": \\"path\\",
},
+ \\"htmlWhitespaceSensitivity\\": Object {
+ \\"choices\\": Array [
+ \\"css\\",
+ \\"strict\\",
+ \\"ignore\\",
+ ],
+ \\"default\\": \\"css\\",
+ \\"type\\": \\"choice\\",
+ },
\\"insertPragma\\": Object {
\\"default\\": false,
\\"type\\": \\"boolean\\",
},
\\"jsxBracketSameLine\\": Object {
@@ -73,16 +108,30 @@
\\"typescript\\",
\\"css\\",
\\"less\\",
@ -511,7 +531,7 @@ exports[`API getSupportInfo() with version 1.8.2 -> undefined 1`] = `
\\"range\\": Object {
\\"end\\": Infinity,
\\"start\\": 0,
@@ -90,14 +130,15 @@
@@ -90,14 +139,15 @@
},
\\"type\\": \\"int\\",
},
@ -931,6 +951,29 @@ exports[`CLI --support-info (stdout) 1`] = `
\\"since\\": \\"1.4.0\\",
\\"type\\": \\"path\\"
},
{
\\"category\\": \\"HTML\\",
\\"choices\\": [
{
\\"description\\": \\"Respect the default value of CSS display property.\\",
\\"value\\": \\"css\\"
},
{
\\"description\\": \\"Whitespaces are considered sensitive.\\",
\\"value\\": \\"strict\\"
},
{
\\"description\\": \\"Whitespaces are considered insensitive.\\",
\\"value\\": \\"ignore\\"
}
],
\\"default\\": \\"css\\",
\\"description\\": \\"How to handle whitespaces in HTML.\\",
\\"name\\": \\"htmlWhitespaceSensitivity\\",
\\"pluginDefaults\\": {},
\\"since\\": \\"1.15.0\\",
\\"type\\": \\"choice\\"
},
{
\\"category\\": \\"Special\\",
\\"default\\": false,

View File

@ -20,6 +20,7 @@ const CATEGORIES_ORDER = [
"Common",
"JavaScript",
"Markdown",
"HTML",
"Special"
];
const ENABLED_OPTIONS = [
@ -34,6 +35,7 @@ const ENABLED_OPTIONS = [
"arrowParens",
"trailingComma",
"proseWrap",
"htmlWhitespaceSensitivity",
"insertPragma",
"requirePragma"
];

View File

@ -3184,6 +3184,10 @@ html-encoding-sniffer@^1.0.2:
dependencies:
whatwg-encoding "^1.0.1"
html-styles@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/html-styles/-/html-styles-1.0.0.tgz#a18061fd651f99c6b75c45c8e0549a3bc3e01a75"
html-tag-names@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/html-tag-names/-/html-tag-names-1.1.2.tgz#f65168964c5a9c82675efda882875dcb2a875c22"
@ -4392,7 +4396,7 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lines-and-columns@^1.1.6:
lines-and-columns@1.1.6, lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=