Support handlebars (#3534)

* feat(glimmer): initial HTML/Handlebars/Glimmer support

* feat(glimmer): support un-escaped mustache expressions

* fix(glimmer): add 'as |foo|' syntax

* fix(glimmer): support {{foo bar}} syntax

* feat(glimmer): implement all AST types

* feat(glimmer): implement if/else if/else

* test(glimmer): ignore leading/trailing whitespace from AST_COMPARE

* fix(glimmer): do not use trailing slash for void self-closing elements

* chore(build): disable html tests until we can sort out the glimmer dependency

* feat(html): complete rebase on parse5 addition

* Prettier Support For Glimmer VM/Handlebars

Polished Support for Sub Expressions
Added test cases for glimmer primitives
Added support for concat statements
Attempted to make element nodes work
Attempted block statements
Element Nodes are OK
Added support for block elements that are not else-if related
Added support for Else/If
Cleaning up
Rebase
Switch node 4 syntax
Update build

* Removed dead code/partials

* Added new lines to end of test files.

* Rebase after plugin change

* Added ignores to code coverage
master
Robert Webb 2017-12-29 12:14:31 -05:00 committed by Christopher Chedeau
parent 6a75f66731
commit 833666a394
35 changed files with 1346 additions and 6 deletions

View File

@ -15,6 +15,7 @@
},
"dependencies": {
"@babel/code-frame": "7.0.0-beta.35",
"@glimmer/syntax": "0.30.3",
"babylon": "7.0.0-beta.34",
"camelcase": "4.1.0",
"chalk": "2.1.0",

View File

@ -37,6 +37,11 @@ for (const parser of parsers) {
shell.exec(
`rollup -c scripts/build/rollup.parser.config.js --environment parser:${parser}`
);
if (parser.endsWith("glimmer")) {
shell.exec(
`node_modules/babel-cli/bin/babel.js dist/parser-glimmer.js --out-file dist/parser-glimmer.js --presets=es2015`
);
}
}
shell.echo("\nsrc/language-css/parser-postcss.js → dist/parser-postcss.js");

View File

@ -5,6 +5,7 @@ module.exports = [
"language-js/parser-flow",
"language-js/parser-typescript",
"language-graphql/parser-graphql",
"language-handlebars/parser-glimmer",
"language-css/parser-postcss",
"language-html/parser-parse5",
"language-markdown/parser-markdown",

View File

@ -30,7 +30,24 @@ export default Object.assign(baseConfig, {
: {},
json(),
resolve({ preferBuiltins: true }),
commonjs(),
commonjs(
parser.endsWith("glimmer")
? {
namedExports: {
"node_modules/handlebars/lib/index.js": ["parse"],
"node_modules/simple-html-tokenizer/dist/simple-html-tokenizer.js": [
"EntityParser",
"HTML5NamedCharRefs",
"EventedTokenizer"
],
"node_modules/@glimmer/syntax/dist/modules/index.js": "default",
"node_modules/@glimmer/syntax/dist/modules/es2017/index.js":
"default"
},
ignore: ["source-map"]
}
: {}
),
{
transformBundle(code) {
const result = uglify.minify(code, {});

View File

@ -234,6 +234,7 @@ const detailedOptions = normalizeDetailedOptions({
"less",
"scss",
"json",
// "glimmer",
"graphql",
"markdown",
"vue"

View File

@ -94,6 +94,14 @@ function massageAST(ast, parent) {
newObj.value = newObj.value.replace(/ /g, "");
}
// (Glimmer/HTML) ignore TextNode whitespace
if (ast.type === "TextNode") {
if (ast.chars.replace(/\s+/, "") === "") {
return undefined;
}
newObj.chars = ast.chars.replace(/^\s+/, "").replace(/\s+$/, "");
}
if (
(ast.type === "value-word" && ast.isColor && ast.isHex) ||
ast.type === "media-feature" ||

View File

@ -8,6 +8,7 @@ function loadPlugins(options) {
const internalPlugins = [
require("../language-js"),
require("../language-css"),
require("../language-handlebars"),
require("../language-graphql"),
require("../language-markdown"),
require("../language-html"),

View File

@ -0,0 +1,37 @@
"use strict";
const printer = require("./printer-glimmer");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const languages = [
{
type: "markup",
group: "HTML",
aliases: ["hbs", "htmlbars"],
extensions: [".handlebars", ".hbs"],
tm_scope: "text.html.handlebars",
ace_mode: "handlebars",
language_id: 155
}
];
const parsers = {
glimmer: {
get parse() {
return eval("require")("./parser-glimmer");
},
astFormat: "glimmer"
}
};
const printers = {
glimmer: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -0,0 +1,45 @@
"use strict";
const createError = require("../common/parser-create-error");
function removeEmptyNodes(node) {
return (
node.type !== "TextNode" ||
(node.type === "TextNode" &&
node.chars.replace(/^\s+/, "").replace(/\s+$/, "") !== "")
);
}
function removeWhiteSpace() {
return {
visitor: {
Program(node) {
node.body = node.body.filter(removeEmptyNodes);
},
ElementNode(node) {
node.children = node.children.filter(removeEmptyNodes);
}
}
};
}
function parse(text) {
try {
const glimmer = require("@glimmer/syntax").preprocess;
return glimmer(text, {
plugins: {
ast: [removeWhiteSpace]
}
});
/* istanbul ignore next */
} catch (error) {
const matches = error.message.match(/on line (\d+)/);
if (matches) {
throw createError(error.message, {
start: { line: +matches[1], column: 0 },
end: { line: +matches[1], column: 80 }
});
} else {
throw error;
}
}
}
module.exports = parse;

View File

@ -0,0 +1,271 @@
"use strict";
const docBuilders = require("../doc/doc-builders");
const concat = docBuilders.concat;
const join = docBuilders.join;
const softline = docBuilders.softline;
const hardline = docBuilders.hardline;
const line = docBuilders.line;
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",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr"
];
// Formatter based on @glimmerjs/syntax's built-in test formatter:
// https://github.com/glimmerjs/glimmer-vm/blob/master/packages/%40glimmer/syntax/lib/generation/print.ts
function print(path, options, print) {
const n = path.getValue();
/* istanbul ignore if*/
if (!n) {
return "";
}
switch (n.type) {
case "Program": {
return group(
join(softline, path.map(print, "body").filter(text => text !== ""))
);
}
case "ElementNode": {
const isVoid = voidTags.indexOf(n.tag) !== -1;
const closeTag = isVoid ? concat([" />", softline]) : ">";
const hasChildren = n.children.length > 0;
const getParams = (path, print) =>
indent(
concat([
n.attributes.length ? line : "",
join(line, path.map(print, "attributes")),
n.modifiers.length ? line : "",
join(line, path.map(print, "modifiers")),
n.comments.length ? line : "",
join(line, path.map(print, "comments"))
])
);
// The problem here is that I want to not break at all if the children
// would not break but I need to force an indent, so I use a hardline.
/**
* What happens now:
* <div>
* Hello
* </div>
* ==>
* <div>Hello</div>
* This is due to me using hasChildren to decide to put the hardline in.
* I would rather use a {DOES THE WHOLE THING NEED TO BREAK}
*/
return concat([
group(
concat([
"<",
n.tag,
getParams(path, print),
ifBreak(softline, ""),
closeTag
])
),
group(
concat([
indent(join(softline, [""].concat(path.map(print, "children")))),
ifBreak(hasChildren ? hardline : "", ""),
!isVoid ? concat(["</", n.tag, ">"]) : ""
])
)
]);
}
case "BlockStatement": {
const pp = path.getParentNode(1);
const isElseIf = pp && pp.inverse && pp.inverse.body[0] === n;
const hasElseIf =
n.inverse &&
n.inverse.body[0] &&
n.inverse.body[0].type === "BlockStatement";
const indentElse = hasElseIf ? a => a : indent;
if (n.inverse) {
return concat([
isElseIf
? concat(["{{else ", printPathParams(path, print), "}}"])
: printOpenBlock(path, print),
indent(concat([hardline, path.call(print, "program")])),
n.inverse && !hasElseIf ? concat([hardline, "{{else}}"]) : "",
n.inverse
? indentElse(concat([hardline, path.call(print, "inverse")]))
: "",
isElseIf ? "" : concat([hardline, printCloseBlock(path, print)])
]);
}
/**
* I want this boolean to be: if params are going to cause a break,
* not that it has params.
*/
const hasParams = n.params.length > 0 || n.hash.pairs.length > 0;
const hasChildren = n.program.body.length > 0;
return concat([
printOpenBlock(path, print),
group(
concat([
indent(concat([softline, path.call(print, "program")])),
hasParams && hasChildren ? hardline : "",
printCloseBlock(path, print)
])
)
]);
}
case "ElementModifierStatement":
case "MustacheStatement": {
const pp = path.getParentNode(1);
const isConcat = pp && pp.type === "ConcatStatement";
return group(
concat([
/*n.escaped ? "{{{" : */ "{{",
printPathParams(path, print),
isConcat ? "" : softline,
/*.escaped ? "}}}" :*/ "}}"
])
);
}
case "SubExpression": {
return group(
concat([
"(",
printPath(path, print),
indent(concat([line, group(join(line, getParams(path, print)))])),
softline,
")"
])
);
}
case "AttrNode": {
const quote = n.value.type === "TextNode" ? '"' : "";
return concat([n.name, "=", quote, path.call(print, "value"), quote]);
}
case "ConcatStatement": {
return concat([
'"',
group(
indent(
join(
softline,
path
.map(partPath => print(partPath), "parts")
.filter(a => a !== "")
)
)
),
'"'
]);
}
case "Hash": {
return concat([join(line, path.map(print, "pairs"))]);
}
case "HashPair": {
return concat([n.key, "=", path.call(print, "value")]);
}
case "TextNode": {
return n.chars.replace(/^\s+/, "").replace(/\s+$/, "");
}
case "MustacheCommentStatement": {
const dashes = n.value.indexOf("}}") > -1 ? "--" : "";
return concat(["{{!", dashes, n.value, dashes, "}}"]);
}
case "PathExpression": {
return n.original;
}
case "BooleanLiteral": {
return String(n.value);
}
case "CommentStatement": {
return concat(["<!--", n.value, "-->"]);
}
case "StringLiteral": {
return `"${n.value}"`;
}
case "NumberLiteral": {
return String(n.value);
}
case "UndefinedLiteral": {
return "undefined";
}
case "NullLiteral": {
return "null";
}
/* istanbul ignore next */
default:
throw new Error("unknown glimmer type: " + JSON.stringify(n.type));
}
}
function printPath(path, print) {
return path.call(print, "path");
}
function getParams(path, print) {
const node = path.getValue();
let parts = [];
if (node.params.length > 0) {
parts = parts.concat(path.map(print, "params"));
}
if (node.hash && node.hash.pairs.length > 0) {
parts.push(path.call(print, "hash"));
}
return parts;
}
function printPathParams(path, print) {
let parts = [];
parts.push(printPath(path, print));
parts = parts.concat(getParams(path, print));
return indent(group(join(line, parts)));
}
function printBlockParams(path) {
const block = path.getValue();
if (!block.program || !block.program.blockParams.length) {
return "";
}
return concat([" as |", block.program.blockParams.join(" "), "|"]);
}
function printOpenBlock(path, print) {
return group(
concat([
"{{#",
printPathParams(path, print),
printBlockParams(path, print),
softline,
"}}"
])
);
}
function printCloseBlock(path, print) {
return concat(["{{/", path.call(print, "path"), "}}"]);
}
module.exports = { print };

View File

@ -31,8 +31,6 @@ function parse(text /*, parsers, opts*/) {
}
function tryParseTypeScript(text, jsx) {
// While we are working on typescript, we are putting it in devDependencies
// so it shouldn't be picked up by static analysis
const parser = require("typescript-eslint-parser");
return parser.parse(text, {
loc: true,
@ -48,8 +46,7 @@ function tryParseTypeScript(text, jsx) {
}
/**
* Use a naive regular expression until we address
* https://github.com/prettier/prettier/issues/1538
* Use a naive regular expression to detect JSX
*/
function isProbablyJsx(text) {
return new RegExp(

View File

@ -52,3 +52,55 @@ exports[`html-fragment.html 1`] = `
<textarea />
`;
exports[`more-html.html 1`] = `
<html>
<body>
<a href="#">Anchor</a>
<div hidden class="foo" id=bar></div>
</body>
</html>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<html>
<head />
<body>
<a href="#">Anchor</a>
<div hidden
class="foo"
id="bar" />
</body>
</html>
`;
exports[`void-elements.html 1`] = `
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="code-guide.css">
</head>
<body></body>
</html>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet"
href="code-guide.css">
</head>
<body>
</body>
</html>
`;

View File

@ -0,0 +1,6 @@
<html>
<body>
<a href="#">Anchor</a>
<div hidden class="foo" id=bar></div>
</body>
</html>

View File

@ -0,0 +1,7 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="code-guide.css">
</head>
<body></body>
</html>

View File

@ -0,0 +1,493 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`block-statement.hbs 1`] = `
{{#block param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars helloWorldParam key=here}}
{{/block}}
{{#block param param param param param param param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block param param param param param param param hashKey=HashValue hashKey=hashValue}}
Hello
{{/block}}
{{#block param param param param param param param param param param param param param}}
Hello
{{/block}}
{{#block hashKey=HashValue hashKey=hashValue hashKey=HashValue hashKey=hashValue hashKey=HashValue}}
Hello
{{/block}}
{{#block}}
{{#block}}
hello
{{/block}}
{{/block}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{#block param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block
almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars
helloWorldParam
key=here
}}{{/block}}
{{#block
param
param
param
param
param
param
param
hashKey=hashValue as |blockParam|
}}
Hello
{{/block}}
{{#block
param
param
param
param
param
param
param
hashKey=HashValue
hashKey=hashValue
}}
Hello
{{/block}}
{{#block
param
param
param
param
param
param
param
param
param
param
param
param
param
}}
Hello
{{/block}}
{{#block
hashKey=HashValue
hashKey=hashValue
hashKey=HashValue
hashKey=hashValue
hashKey=HashValue
}}
Hello
{{/block}}
{{#block}}{{#block}}hello{{/block}}{{/block}}
`;
exports[`component.hbs 1`] = `
<user-greeting @name="Ricardo" @greeting="Olá" />
{{@greeting}}, {{@name}}!
<button onclick={{action next}}>Next</button>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<user-greeting @name="Ricardo" @greeting="Olá"></user-greeting>
{{@greeting}}
,
{{@name}}
!
<button onclick={{action next}}>
Next
</button>
`;
exports[`concat-statement.hbs 1`] = `
<div class="hello {{if goodbye true}}">
Hello
</div>
<div class="hello {{if goodbye true}} {{if goodbye false}} {{if goodbye true}} {{if goodbye false}} {{if goodbye true}}">
Hello
</div>
<a href="/{{url}}/{{url}}"></a>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="hello{{if goodbye true}}">
Hello
</div>
<div
class="hello
{{if goodbye true}}
{{if goodbye false}}
{{if goodbye true}}
{{if goodbye false}}
{{if goodbye true}}"
>
Hello
</div>
<a href="/{{url}}/{{url}}"></a>
`;
exports[`element-modifier-statement.hbs 1`] = `
<div {{hello param hash=key}} {{goodbye param}}>
Hello
</div>
<div {{hello param param param param param param param param param param param param}}>
Hello
</div>
<div {{hello hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div>
<div {{hello param param param param hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div {{hello param hash=key}} {{goodbye param}}>
Hello
</div>
<div
{{hello
param
param
param
param
param
param
param
param
param
param
param
param
}}
>
Hello
</div>
<div
{{hello
hashPair=value
hashPair=value
hashPair=value
hashPair=value
hashPair=value
}}
>
Hello
</div>
<div
{{hello
param
param
param
param
hashPair=value
hashPair=value
hashPair=value
hashPair=value
hashPair=value
}}
>
Hello
</div>
`;
exports[`element-node.hbs 1`] = `
<div class="attribute" {{modifier}} {{! comment}}>
Hello
</div>
<div>
Hello
</div>
<div>
hi
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
<div>
{{#block}}
{{hello}}
{{/block}}
</div>
<div>
{{hello}}
</div>
<div></div>
<img />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="attribute" {{modifier}} {{! comment}}>
Hello
</div>
<div>
Hello
</div>
<div>
hi
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
<div>
{{#block}}{{hello}}{{/block}}
</div>
<div>
{{hello}}
</div>
<div></div>
<img />
`;
exports[`else-if.hbs 1`] = `
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else}}
hello
{{#if f}}
g
{{/if}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else if e}}
f
{{else if g}}
h
{{else}}
j
{{/if}}
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
<div>
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else}}
hello
{{#if f}}
g
{{/if}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else if e}}
f
{{else if g}}
h
{{else}}
j
{{/if}}
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
<div>
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
</div>
`;
exports[`literals.hbs 1`] = `
{{mustache true}}
{{mustache 5}}
{{mustache undefined}}
{{mustache null}}
<!-- hello world -->
{{! Mustache Comment}}
{{!-- Mustache Comment }} --}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{mustache true}}
{{mustache 5}}
{{mustache undefined}}
{{mustache null}}
<!-- hello world -->
{{! Mustache Comment}}
{{!-- Mustache Comment }} --}}
`;
exports[`loop.hbs 1`] = `
<ul>
{{#each speakers key="@index" as |speaker|}}
<li>{{speaker}}</li>
{{/each}}
</ul>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<ul>
{{#each speakers key="@index" as |speaker|}}
<li>
{{speaker}}
</li>
{{/each}}
</ul>
`;
exports[`sub-expressions.hbs 1`] = `
<div
{{mustache
(concat
(helper param hashPair=Value)
(largeNameHelper param param param param hashPair=value hashPair=value hashPair=Value)
hashPair=(helper param param param param param param hashPair=value hashPair=value hashPair=value)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
></div>
{{#block
(concat
(helper param hashPair=Value)
(largeNameHelper param param param param hashPair=value hashPair=value hashPair=Value)
hashPair=(helper param param param param param param hashPair=value hashPair=value hashPair=value)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
{{/block}}
{{foobar-sub-component/foobar-foo
hook="stringLiteral"
foo=
(t
(concat "stringLiteral" (get blockParam "stringLiteral") hash=hash hash=hash)
foo=(simple-helper (hash hashKey=blockParam.foo assignParam=blockParam.bar))
)
}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div
{{mustache
(concat
(helper param hashPair=Value)
(largeNameHelper
param param param param hashPair=value hashPair=value hashPair=Value
)
hashPair=(helper
param
param
param
param
param
param
hashPair=value
hashPair=value
hashPair=value
)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
></div>
{{#block
(concat
(helper param hashPair=Value)
(largeNameHelper
param param param param hashPair=value hashPair=value hashPair=Value
)
hashPair=(helper
param
param
param
param
param
param
hashPair=value
hashPair=value
hashPair=value
)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}{{/block}}
{{foobar-sub-component/foobar-foo
hook="stringLiteral"
foo=(t
(concat
"stringLiteral" (get blockParam "stringLiteral") hash=hash hash=hash
)
foo=(simple-helper (hash hashKey=blockParam.foo assignParam=blockParam.bar))
)
}}
`;

View File

@ -0,0 +1,28 @@
{{#block param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars helloWorldParam key=here}}
{{/block}}
{{#block param param param param param param param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block param param param param param param param hashKey=HashValue hashKey=hashValue}}
Hello
{{/block}}
{{#block param param param param param param param param param param param param param}}
Hello
{{/block}}
{{#block hashKey=HashValue hashKey=hashValue hashKey=HashValue hashKey=hashValue hashKey=HashValue}}
Hello
{{/block}}
{{#block}}
{{#block}}
hello
{{/block}}
{{/block}}

View File

@ -0,0 +1,4 @@
<user-greeting @name="Ricardo" @greeting="Olá" />
{{@greeting}}, {{@name}}!
<button onclick={{action next}}>Next</button>

View File

@ -0,0 +1,9 @@
<div class="hello {{if goodbye true}}">
Hello
</div>
<div class="hello {{if goodbye true}} {{if goodbye false}} {{if goodbye true}} {{if goodbye false}} {{if goodbye true}}">
Hello
</div>
<a href="/{{url}}/{{url}}"></a>

View File

@ -0,0 +1,15 @@
<div {{hello param hash=key}} {{goodbye param}}>
Hello
</div>
<div {{hello param param param param param param param param param param param param}}>
Hello
</div>
<div {{hello hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div>
<div {{hello param param param param hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div>

View File

@ -0,0 +1,36 @@
<div class="attribute" {{modifier}} {{! comment}}>
Hello
</div>
<div>
Hello
</div>
<div>
hi
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
<div>
{{#block}}
{{hello}}
{{/block}}
</div>
<div>
{{hello}}
</div>
<div></div>
<img />

View File

@ -0,0 +1,53 @@
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else}}
hello
{{#if f}}
g
{{/if}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else if e}}
f
{{else if g}}
h
{{else}}
j
{{/if}}
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
<div>
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
</div>

View File

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

View File

@ -0,0 +1,7 @@
{{mustache true}}
{{mustache 5}}
{{mustache undefined}}
{{mustache null}}
<!-- hello world -->
{{! Mustache Comment}}
{{!-- Mustache Comment }} --}}

View File

@ -0,0 +1,5 @@
<ul>
{{#each speakers key="@index" as |speaker|}}
<li>{{speaker}}</li>
{{/each}}
</ul>

View File

@ -0,0 +1,30 @@
<div
{{mustache
(concat
(helper param hashPair=Value)
(largeNameHelper param param param param hashPair=value hashPair=value hashPair=Value)
hashPair=(helper param param param param param param hashPair=value hashPair=value hashPair=value)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
></div>
{{#block
(concat
(helper param hashPair=Value)
(largeNameHelper param param param param hashPair=value hashPair=value hashPair=Value)
hashPair=(helper param param param param param param hashPair=value hashPair=value hashPair=value)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
{{/block}}
{{foobar-sub-component/foobar-foo
hook="stringLiteral"
foo=
(t
(concat "stringLiteral" (get blockParam "stringLiteral") hash=hash hash=hash)
foo=(simple-helper (hash hashKey=blockParam.foo assignParam=blockParam.bar))
)
}}

View File

@ -0,0 +1,131 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`basic-handlebars.hbs 1`] = `
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
</div>
</script>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>
{{title}}
</h1>
<div class="body">
{{body}}
</div>
</div>
</script>
`;
exports[`comments.hbs 1`] = `
<div class="entry">
{{! This comment will not be in the output }}
{{!-- This comment as }} and will not be in the output --}}
<!-- This comment will be in the output -->
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="entry">
{{! This comment will not be in the output }}
{{!-- This comment as }} and will not be in the output --}}
<!-- This comment will be in the output -->
</div>
`;
exports[`each.hbs 1`] = `
<div id="comments">
{{#each comments}}
<h2><a href="/posts/{{permalink}}#{{id}}">{{title}}</a></h2>
<div>{{body}}</div>
{{/each}}
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div id="comments">
{{#each comments}}
<h2>
<a href="/posts/{{permalink}}#{{id}}">
{{title}}
</a>
</h2>
<div>
{{body}}
</div>
{{/each}}
</div>
`;
exports[`if.hbs 1`] = `
{{#if title}}
{{permalink}}
{{/if}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{#if title}}
{{permalink}}
{{/if}}
`;
exports[`if-else.hbs 1`] = `
<h1>
{{#if isAtWork}}
Ship that code!
{{else if isReading}}
You can finish War and Peace eventually...
{{else}}
Go to bed!
{{/if}}
</h1>
<h2>
{{#if a}}
A
{{else}}
B
{{/if}}
</h2>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<h1>
{{#if isAtWork}}
Ship that code!
{{else if isReading}}
You can finish War and Peace eventually...
{{else}}
Go to bed!
{{/if}}
</h1>
<h2>
{{#if a}}
A
{{else}}
B
{{/if}}
</h2>
`;
exports[`nested-path.hbs 1`] = `
<div class="entry">
<h1>{{title}}</h1>
<h2>By {{author.name}}</h2>
<div class="body">
{{body}}
</div>
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="entry">
<h1>
{{title}}
</h1>
<h2>
By
{{author.name}}
</h2>
<div class="body">
{{body}}
</div>
</div>
`;

View File

@ -0,0 +1,8 @@
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
</div>
</script>

View File

@ -0,0 +1,5 @@
<div class="entry">
{{! This comment will not be in the output }}
{{!-- This comment as }} and will not be in the output --}}
<!-- This comment will be in the output -->
</div>

View File

@ -0,0 +1,6 @@
<div id="comments">
{{#each comments}}
<h2><a href="/posts/{{permalink}}#{{id}}">{{title}}</a></h2>
<div>{{body}}</div>
{{/each}}
</div>

View File

@ -0,0 +1,18 @@
<h1>
{{#if isAtWork}}
Ship that code!
{{else if isReading}}
You can finish War and Peace eventually...
{{else}}
Go to bed!
{{/if}}
</h1>
<h2>
{{#if a}}
A
{{else}}
B
{{/if}}
</h2>

View File

@ -0,0 +1,3 @@
{{#if title}}
{{permalink}}
{{/if}}

View File

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

View File

@ -0,0 +1,8 @@
<div class="entry">
<h1>{{title}}</h1>
<h2>By {{author.name}}</h2>
<div class="body">
{{body}}
</div>
</div>

View File

@ -12,6 +12,7 @@ toolbox.precache([
"lib/parser-typescript.js",
"lib/parser-postcss.js",
"lib/parser-flow.js",
"lib/parser-glimmer.js",
"lib/parser-graphql.js",
"lib/parser-markdown.js",
"markdown.js",

View File

@ -10,6 +10,31 @@
esutils "^2.0.2"
js-tokens "^3.0.0"
"@glimmer/interfaces@^0.30.3":
version "0.30.3"
resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.30.3.tgz#ea53e6b945ae3cc14588e655626ad9c6ed90a9f9"
dependencies:
"@glimmer/wire-format" "^0.30.3"
"@glimmer/syntax@0.30.3":
version "0.30.3"
resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.30.3.tgz#31ca56d00b4f7c63af13aeb70b5d58a9b250f02e"
dependencies:
"@glimmer/interfaces" "^0.30.3"
"@glimmer/util" "^0.30.3"
handlebars "^4.0.6"
simple-html-tokenizer "^0.4.1"
"@glimmer/util@^0.30.3":
version "0.30.3"
resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.30.3.tgz#c771fce2d766c380ae50d3ad8045eff224637e8c"
"@glimmer/wire-format@^0.30.3":
version "0.30.3"
resolved "https://registry.yarnpkg.com/@glimmer/wire-format/-/wire-format-0.30.3.tgz#32629d99f3107b4c940cf84607e417686e55b94d"
dependencies:
"@glimmer/util" "^0.30.3"
"@types/node@*":
version "8.0.47"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.47.tgz#968e596f91acd59069054558a00708c445ca30c2"
@ -1982,7 +2007,7 @@ growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
handlebars@^4.0.3:
handlebars@^4.0.3, handlebars@^4.0.6:
version "4.0.10"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f"
dependencies:
@ -4054,6 +4079,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
simple-html-tokenizer@^0.4.1:
version "0.4.3"
resolved "https://registry.yarnpkg.com/simple-html-tokenizer/-/simple-html-tokenizer-0.4.3.tgz#9b00b766e30058b4bb377c0d4f97566a13ab1be1"
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"