feat(html): add parse5/htmlparser2 printer (#2083)

master
Lucas Azzola 2017-06-10 12:22:59 +10:00 committed by Christopher Chedeau
parent f399e90bc2
commit d00956d51d
12 changed files with 203 additions and 15 deletions

View File

@ -98,6 +98,7 @@ function getParserOption() {
value === "babylon" ||
value === "typescript" ||
value === "postcss" ||
value === "parse5" ||
value === "graphql"
) {
return value;

View File

@ -29,6 +29,7 @@
"jest-validate": "20.0.3",
"minimist": "1.2.0",
"mkdirp": "^0.5.1",
"parse5": "3.0.2",
"postcss": "^6.0.1",
"postcss-less": "^1.0.0",
"postcss-media-query-parser": "0.2.3",

View File

@ -31,6 +31,8 @@ function normalize(options) {
if (/\.(css|less|scss)$/.test(filepath)) {
normalized.parser = "postcss";
} else if (/\.html$/.test(filepath)) {
normalized.parser = "parse5";
} else if (/\.(ts|tsx)$/.test(filepath)) {
normalized.parser = "typescript";
}

25
src/parser-parse5.js Normal file
View File

@ -0,0 +1,25 @@
"use strict";
// const createError = require("./parser-create-error");
function parse(text) {
// Inline the require to avoid loading all the JS if we don't use it
const parse5 = require("parse5");
try {
const ast = parse5.parse(text, {
treeAdapter: parse5.treeAdapters.htmlparser2
});
return ast;
} catch (error) {
// throw createError(error.message, {
// start: {
// line: error.locations[0].line,
// column: error.locations[0].column
// }
// } else {
throw error;
// }
}
}
module.exports = parse;

View File

@ -1,22 +1,25 @@
"use strict";
function parse(text, opts) {
let parseFunction;
if (opts.parser === "flow") {
parseFunction = eval("require")("./parser-flow");
} else if (opts.parser === "graphql") {
parseFunction = eval("require")("./parser-graphql");
} else if (opts.parser === "typescript") {
parseFunction = eval("require")("./parser-typescript");
} else if (opts.parser === "postcss") {
parseFunction = eval("require")("./parser-postcss");
} else {
parseFunction = eval("require")("./parser-babylon");
function getParseFunction(opts) {
switch (opts.parser) {
case "flow":
return eval("require")("./parser-flow");
case "graphql":
return eval("require")("./parser-graphql");
case "parse5":
return eval("require")("./parser-parse5");
case "postcss":
return eval("require")("./parser-postcss");
case "typescript":
return eval("require")("./parser-typescript");
default:
return eval("require")("./parser-babylon");
}
}
function parse(text, opts) {
try {
return parseFunction(text);
return getParseFunction(opts)(text);
} catch (error) {
const loc = error.loc;

100
src/printer-htmlparser2.js Normal file
View File

@ -0,0 +1,100 @@
"use strict";
const docBuilders = require("./doc-builders");
const concat = docBuilders.concat;
const join = docBuilders.join;
const hardline = docBuilders.hardline;
// const line = docBuilders.line;
const softline = docBuilders.softline;
const group = docBuilders.group;
const indent = docBuilders.indent;
// const ifBreak = docBuilders.ifBreak;
// http://w3c.github.io/html/single-page.html#void-elements
const voidTags = {
area: true,
base: true,
br: true,
col: true,
embed: true,
hr: true,
img: true,
input: true,
link: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true
};
function genericPrint(path, options, print) {
const n = path.getValue();
if (!n) {
return "";
}
if (typeof n === "string") {
return n;
}
switch (n.type) {
case "root": {
return concat(path.map(print, "children"));
}
case "directive": {
return concat(["<", n.data, ">", hardline]);
}
case "text": {
return n.data.replace(/\s+/g, " ").trim();
}
case "tag": {
const selfClose = voidTags[n.name] ? ">" : " />";
const children = [];
path.each(childPath => {
const child = childPath.getValue();
if (child.type !== "text") {
children.push(softline);
}
children.push(childPath.call(print));
}, "children");
return group(
concat([
"<",
n.name,
printAttributes(n.attribs),
n.children.length ? ">" : selfClose,
indent(concat(children)),
n.children.length ? concat([softline, "</", n.name, ">"]) : ""
])
);
}
case "comment": {
return concat(["<!-- ", n.data.trim(), " -->"]);
}
default:
throw new Error("unknown htmlparser2 type: " + n.type);
}
}
function printAttributes(attribs) {
const attributeKeys = Object.keys(attribs);
return concat([
attributeKeys.length ? " " : "",
join(
" ",
attributeKeys.map(name => {
if (attribs[name] === "") {
return name;
}
return concat([name, '="', attribs[name], '"']);
})
)
]);
}
module.exports = genericPrint;

View File

@ -52,6 +52,8 @@ function getPrintFunction(options) {
switch (options.parser) {
case "graphql":
return require("./printer-graphql");
case "parse5":
return require("./printer-htmlparser2");
case "postcss":
return require("./printer-postcss");
default:

View File

@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`hello-world.html 1`] = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- A comment -->
<h1>Hello World</h1>
</body>
</html>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body><!-- A comment --><h1>Hello World</h1></body>
</html>
`;

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- A comment -->
<h1>Hello World</h1>
</body>
</html>

View File

@ -0,0 +1 @@
run_spec(__dirname, { parser: "parse5" });

View File

@ -16,7 +16,7 @@ function run_spec(dirname, options, additionalParsers) {
fs.readdirSync(dirname).forEach(filename => {
const extension = extname(filename);
if (
/^\.([jt]sx?|css|graphql)$/.test(extension) &&
/^\.([jt]sx?|css|graphql|html)$/.test(extension) &&
filename !== "jsfmt.spec.js"
) {
const path = dirname + "/" + filename;

View File

@ -2,6 +2,10 @@
# yarn lockfile v1
"@types/node@^6.0.46":
version "6.0.78"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.78.tgz#5d4a3f579c1524e01ee21bf474e6fba09198f470"
abab@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
@ -2746,6 +2750,12 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
parse5@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.2.tgz#05eff57f0ef4577fb144a79f8b9a967a6cc44510"
dependencies:
"@types/node" "^6.0.46"
parse5@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"