[vue] Introduce proper support for Vue Single File Components (SFC) (#3563)
There's a lot of demand for vue sfc (#2097). This introduces partial support for them: all the html is printed as is, except for the script and style tags which are printed using prettier. I believe that this should cover a lot of the use cases while being simple to support and if we want we can extend to more in the future. I copy pasted the html parser used by vue (it's just a single 400 lines file) so that we don't run the chancesof conflicts. I'm also very conservative: I only print the style and script at the top level and for the lang attributes we support. I expect this to be landable as is and provide value, review welcome :)master
parent
3ef89b5fa1
commit
c40b061b80
|
@ -7,6 +7,6 @@ Prettier attempts to support all JavaScript language features, including non-sta
|
|||
|
||||
All of JSX and Flow syntax is supported. In fact, the test suite in `tests/flow` _is_ the entire Flow test suite and they all pass.
|
||||
|
||||
Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), [GraphQL](http://graphql.org/), and [Markdown](http://commonmark.org).
|
||||
Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [Vue](https://vuejs.org/), [JSON](http://json.org/), [GraphQL](http://graphql.org/), and [Markdown](http://commonmark.org).
|
||||
|
||||
The minimum version of TypeScript supported is 2.1.3 as it introduces the ability to have leading `|` for type definitions which prettier outputs.
|
||||
|
|
|
@ -14,7 +14,8 @@ const parsers = [
|
|||
"graphql",
|
||||
"postcss",
|
||||
"parse5",
|
||||
"markdown"
|
||||
"markdown",
|
||||
"vue"
|
||||
];
|
||||
|
||||
function pipe(string) {
|
||||
|
|
|
@ -15,7 +15,8 @@ const parsers = [
|
|||
"graphql",
|
||||
"postcss",
|
||||
"parse5",
|
||||
"markdown"
|
||||
"markdown",
|
||||
"vue"
|
||||
];
|
||||
|
||||
process.env.PATH += path.delimiter + path.join(rootDir, "node_modules", ".bin");
|
||||
|
|
|
@ -232,7 +232,8 @@ const detailedOptions = normalizeDetailedOptions({
|
|||
"scss",
|
||||
"json",
|
||||
"graphql",
|
||||
"markdown"
|
||||
"markdown",
|
||||
"vue"
|
||||
],
|
||||
description: "Which parser to use.",
|
||||
getter: (value, argv) => (argv["flow-parser"] ? "flow" : value)
|
||||
|
|
|
@ -20,6 +20,8 @@ function printSubtree(path, print, options) {
|
|||
return fromBabylonFlowOrTypeScript(path, print, options);
|
||||
case "markdown":
|
||||
return fromMarkdown(path, print, options);
|
||||
case "vue":
|
||||
return fromVue(path, print, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +40,51 @@ function parseAndPrint(text, partialNextOptions, parentOptions) {
|
|||
return require("./printer").printAstToDoc(ast, nextOptions);
|
||||
}
|
||||
|
||||
function fromVue(path, print, options) {
|
||||
const node = path.getValue();
|
||||
const parent = path.getParentNode();
|
||||
if (!parent || parent.tag !== "root") {
|
||||
return null;
|
||||
}
|
||||
|
||||
let parser;
|
||||
|
||||
if (node.tag === "style") {
|
||||
const langAttr = node.attrs.find(attr => attr.name === "lang");
|
||||
if (!langAttr) {
|
||||
parser = "css";
|
||||
} else if (langAttr.value === "scss") {
|
||||
parser = "scss";
|
||||
} else if (langAttr.value === "less") {
|
||||
parser = "less";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.tag === "script") {
|
||||
const langAttr = node.attrs.find(attr => attr.name === "lang");
|
||||
if (!langAttr) {
|
||||
parser = "babylon";
|
||||
} else if (langAttr.value === "ts") {
|
||||
parser = "typescript";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return concat([
|
||||
options.originalText.slice(node.start, node.contentStart),
|
||||
hardline,
|
||||
parseAndPrint(
|
||||
options.originalText.slice(node.contentStart, node.contentEnd),
|
||||
parser,
|
||||
options
|
||||
),
|
||||
options.originalText.slice(node.contentEnd, node.end)
|
||||
]);
|
||||
}
|
||||
|
||||
function fromMarkdown(path, print, options) {
|
||||
const node = path.getValue();
|
||||
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
"use strict";
|
||||
|
||||
/*!
|
||||
* Extracted from vue codebase
|
||||
* https://github.com/vuejs/vue/blob/cfd73c2386623341fdbb3ac636c4baf84ea89c2c/src/compiler/parser/html-parser.js
|
||||
* HTML Parser By John Resig (ejohn.org)
|
||||
* Modified by Juriy "kangax" Zaytsev
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* Make a map and return a function for checking if a key
|
||||
* is in that map.
|
||||
*/
|
||||
function makeMap(str, expectsLowerCase) {
|
||||
const map = Object.create(null);
|
||||
const list = str.split(",");
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
map[list[i]] = true;
|
||||
}
|
||||
return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val];
|
||||
}
|
||||
|
||||
/**
|
||||
* Always return false.
|
||||
*/
|
||||
const no = () => false;
|
||||
|
||||
// HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3
|
||||
// Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
|
||||
const isNonPhrasingTag = makeMap(
|
||||
"address,article,aside,base,blockquote,body,caption,col,colgroup,dd," +
|
||||
"details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form," +
|
||||
"h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta," +
|
||||
"optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead," +
|
||||
"title,tr,track"
|
||||
);
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
const attribute = /^\s*([^\s"'<>/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
|
||||
// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
|
||||
// but for Vue templates we can enforce a simple charset
|
||||
const ncname = "[a-zA-Z_][\\w\\-\\.]*";
|
||||
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
|
||||
const startTagOpen = new RegExp(`^<${qnameCapture}`);
|
||||
const startTagClose = /^\s*(\/?)>/;
|
||||
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
|
||||
const doctype = /^<!DOCTYPE [^>]+>/i;
|
||||
const comment = /^<!--/;
|
||||
const conditionalComment = /^<!\[/;
|
||||
|
||||
let IS_REGEX_CAPTURING_BROKEN = false;
|
||||
"x".replace(/x(.)?/g, (m, g) => {
|
||||
IS_REGEX_CAPTURING_BROKEN = g === "";
|
||||
});
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
const isPlainTextElement = makeMap("script,style,textarea", true);
|
||||
const reCache = {};
|
||||
|
||||
const decodingMap = {
|
||||
"<": "<",
|
||||
">": ">",
|
||||
""": '"',
|
||||
"&": "&",
|
||||
" ": "\n",
|
||||
"	": "\t"
|
||||
};
|
||||
const encodedAttr = /&(?:lt|gt|quot|amp);/g;
|
||||
const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g;
|
||||
|
||||
// #5992
|
||||
const isIgnoreNewlineTag = makeMap("pre,textarea", true);
|
||||
const shouldIgnoreFirstNewline = (tag, html) =>
|
||||
tag && isIgnoreNewlineTag(tag) && html[0] === "\n";
|
||||
|
||||
function decodeAttr(value, shouldDecodeNewlines) {
|
||||
const re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr;
|
||||
return value.replace(re, match => decodingMap[match]);
|
||||
}
|
||||
|
||||
function parseHTML(html, options) {
|
||||
const stack = [];
|
||||
const expectHTML = options.expectHTML;
|
||||
const isUnaryTag = options.isUnaryTag || no;
|
||||
const canBeLeftOpenTag = options.canBeLeftOpenTag || no;
|
||||
let index = 0;
|
||||
let last;
|
||||
let lastTag;
|
||||
while (html) {
|
||||
last = html;
|
||||
// Make sure we're not in a plaintext content element like script/style
|
||||
if (!lastTag || !isPlainTextElement(lastTag)) {
|
||||
let textEnd = html.indexOf("<");
|
||||
if (textEnd === 0) {
|
||||
// Comment:
|
||||
if (comment.test(html)) {
|
||||
const commentEnd = html.indexOf("-->");
|
||||
|
||||
if (commentEnd >= 0) {
|
||||
if (options.shouldKeepComment) {
|
||||
options.comment(html.substring(4, commentEnd));
|
||||
}
|
||||
advance(commentEnd + 3);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
|
||||
if (conditionalComment.test(html)) {
|
||||
const conditionalEnd = html.indexOf("]>");
|
||||
|
||||
if (conditionalEnd >= 0) {
|
||||
advance(conditionalEnd + 2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Doctype:
|
||||
const doctypeMatch = html.match(doctype);
|
||||
if (doctypeMatch) {
|
||||
advance(doctypeMatch[0].length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// End tag:
|
||||
const endTagMatch = html.match(endTag);
|
||||
if (endTagMatch) {
|
||||
const curIndex = index;
|
||||
advance(endTagMatch[0].length);
|
||||
parseEndTag(endTagMatch[1], curIndex, index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start tag:
|
||||
const startTagMatch = parseStartTag();
|
||||
if (startTagMatch) {
|
||||
handleStartTag(startTagMatch);
|
||||
if (shouldIgnoreFirstNewline(lastTag, html)) {
|
||||
advance(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let text;
|
||||
let rest;
|
||||
let next;
|
||||
if (textEnd >= 0) {
|
||||
rest = html.slice(textEnd);
|
||||
while (
|
||||
!endTag.test(rest) &&
|
||||
!startTagOpen.test(rest) &&
|
||||
!comment.test(rest) &&
|
||||
!conditionalComment.test(rest)
|
||||
) {
|
||||
// < in plain text, be forgiving and treat it as text
|
||||
next = rest.indexOf("<", 1);
|
||||
if (next < 0) {
|
||||
break;
|
||||
}
|
||||
textEnd += next;
|
||||
rest = html.slice(textEnd);
|
||||
}
|
||||
text = html.substring(0, textEnd);
|
||||
advance(textEnd);
|
||||
}
|
||||
|
||||
if (textEnd < 0) {
|
||||
text = html;
|
||||
html = "";
|
||||
}
|
||||
|
||||
if (options.chars && text) {
|
||||
options.chars(text);
|
||||
}
|
||||
} else {
|
||||
let endTagLength = 0;
|
||||
const stackedTag = lastTag.toLowerCase();
|
||||
const reStackedTag =
|
||||
reCache[stackedTag] ||
|
||||
(reCache[stackedTag] = new RegExp(
|
||||
"([\\s\\S]*?)(</" + stackedTag + "[^>]*>)",
|
||||
"i"
|
||||
));
|
||||
const rest = html.replace(reStackedTag, (all, text, endTag) => {
|
||||
endTagLength = endTag.length;
|
||||
if (!isPlainTextElement(stackedTag) && stackedTag !== "noscript") {
|
||||
text = text
|
||||
.replace(/<!--([\s\S]*?)-->/g, "$1")
|
||||
.replace(/<!\[CDATA\[([\s\S]*?)]]>/g, "$1");
|
||||
}
|
||||
if (shouldIgnoreFirstNewline(stackedTag, text)) {
|
||||
text = text.slice(1);
|
||||
}
|
||||
if (options.chars) {
|
||||
options.chars(text);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
index += html.length - rest.length;
|
||||
html = rest;
|
||||
parseEndTag(stackedTag, index - endTagLength, index);
|
||||
}
|
||||
|
||||
if (html === last) {
|
||||
options.chars && options.chars(html);
|
||||
if (
|
||||
process.env.NODE_ENV !== "production" &&
|
||||
!stack.length &&
|
||||
options.warn
|
||||
) {
|
||||
options.warn(`Mal-formatted tag at end of template: "${html}"`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function advance(n) {
|
||||
index += n;
|
||||
html = html.substring(n);
|
||||
}
|
||||
|
||||
function parseStartTag() {
|
||||
const start = html.match(startTagOpen);
|
||||
if (start) {
|
||||
const match = {
|
||||
tagName: start[1],
|
||||
attrs: [],
|
||||
start: index
|
||||
};
|
||||
advance(start[0].length);
|
||||
let end;
|
||||
let attr;
|
||||
while (
|
||||
!(end = html.match(startTagClose)) &&
|
||||
(attr = html.match(attribute))
|
||||
) {
|
||||
advance(attr[0].length);
|
||||
match.attrs.push(attr);
|
||||
}
|
||||
if (end) {
|
||||
match.unarySlash = end[1];
|
||||
advance(end[0].length);
|
||||
match.end = index;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleStartTag(match) {
|
||||
const tagName = match.tagName;
|
||||
const unarySlash = match.unarySlash;
|
||||
|
||||
if (expectHTML) {
|
||||
if (lastTag === "p" && isNonPhrasingTag(tagName)) {
|
||||
parseEndTag(lastTag);
|
||||
}
|
||||
if (canBeLeftOpenTag(tagName) && lastTag === tagName) {
|
||||
parseEndTag(tagName);
|
||||
}
|
||||
}
|
||||
|
||||
const unary = isUnaryTag(tagName) || !!unarySlash;
|
||||
|
||||
const l = match.attrs.length;
|
||||
const attrs = new Array(l);
|
||||
for (let i = 0; i < l; i++) {
|
||||
const args = match.attrs[i];
|
||||
// hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
|
||||
if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {
|
||||
if (args[3] === "") {
|
||||
delete args[3];
|
||||
}
|
||||
if (args[4] === "") {
|
||||
delete args[4];
|
||||
}
|
||||
if (args[5] === "") {
|
||||
delete args[5];
|
||||
}
|
||||
}
|
||||
const value = args[3] || args[4] || args[5] || "";
|
||||
const shouldDecodeNewlines =
|
||||
tagName === "a" && args[1] === "href"
|
||||
? options.shouldDecodeNewlinesForHref
|
||||
: options.shouldDecodeNewlines;
|
||||
attrs[i] = {
|
||||
name: args[1],
|
||||
value: decodeAttr(value, shouldDecodeNewlines)
|
||||
};
|
||||
}
|
||||
|
||||
if (!unary) {
|
||||
stack.push({
|
||||
tag: tagName,
|
||||
lowerCasedTag: tagName.toLowerCase(),
|
||||
attrs: attrs
|
||||
});
|
||||
lastTag = tagName;
|
||||
}
|
||||
|
||||
if (options.start) {
|
||||
options.start(tagName, attrs, unary, match.start, match.end);
|
||||
}
|
||||
}
|
||||
|
||||
function parseEndTag(tagName, start, end) {
|
||||
let pos;
|
||||
let lowerCasedTagName;
|
||||
if (start == null) {
|
||||
start = index;
|
||||
}
|
||||
if (end == null) {
|
||||
end = index;
|
||||
}
|
||||
|
||||
if (tagName) {
|
||||
lowerCasedTagName = tagName.toLowerCase();
|
||||
}
|
||||
|
||||
// Find the closest opened tag of the same type
|
||||
if (tagName) {
|
||||
for (pos = stack.length - 1; pos >= 0; pos--) {
|
||||
if (stack[pos].lowerCasedTag === lowerCasedTagName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If no tag name is provided, clean shop
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
if (pos >= 0) {
|
||||
// Close all the open elements, up the stack
|
||||
for (let i = stack.length - 1; i >= pos; i--) {
|
||||
if (
|
||||
process.env.NODE_ENV !== "production" &&
|
||||
(i > pos || !tagName) &&
|
||||
options.warn
|
||||
) {
|
||||
options.warn(`tag <${stack[i].tag}> has no matching end tag.`);
|
||||
}
|
||||
if (options.end) {
|
||||
options.end(stack[i].tag, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
lastTag = pos && stack[pos - 1].tag;
|
||||
} else if (lowerCasedTagName === "br") {
|
||||
if (options.start) {
|
||||
options.start(tagName, [], true, start, end);
|
||||
}
|
||||
} else if (lowerCasedTagName === "p") {
|
||||
if (options.start) {
|
||||
options.start(tagName, [], false, start, end);
|
||||
}
|
||||
if (options.end) {
|
||||
options.end(tagName, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parse(text /*, parsers, opts*/) {
|
||||
const rootObj = {
|
||||
tag: "root",
|
||||
attrs: [],
|
||||
unary: false,
|
||||
start: 0,
|
||||
contentStart: 0,
|
||||
contentEnd: text.length,
|
||||
end: text.length,
|
||||
children: [],
|
||||
comments: []
|
||||
};
|
||||
const objStack = [rootObj];
|
||||
let obj = rootObj;
|
||||
parseHTML(text, {
|
||||
start: function(tag, attrs, unary, start, end) {
|
||||
const newObj = {
|
||||
tag,
|
||||
attrs,
|
||||
unary,
|
||||
start,
|
||||
contentStart: end,
|
||||
children: []
|
||||
};
|
||||
obj.children.push(newObj);
|
||||
objStack.push(newObj);
|
||||
obj = newObj;
|
||||
},
|
||||
end: function(tag, start, end) {
|
||||
objStack.pop();
|
||||
obj.contentEnd = start;
|
||||
obj.end = end;
|
||||
obj = objStack[objStack.length - 1];
|
||||
}
|
||||
});
|
||||
return rootObj;
|
||||
}
|
||||
|
||||
module.exports = parse;
|
|
@ -33,6 +33,9 @@ const parsers = {
|
|||
},
|
||||
get markdown() {
|
||||
return eval("require")("./parser-markdown");
|
||||
},
|
||||
get vue() {
|
||||
return eval("require")("./parser-vue");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
"use strict";
|
||||
|
||||
const docBuilders = require("./doc-builders");
|
||||
const concat = docBuilders.concat;
|
||||
|
||||
function genericPrint(path, options, print) {
|
||||
const n = path.getValue();
|
||||
const res = [];
|
||||
let index = n.start;
|
||||
|
||||
path.each(childPath => {
|
||||
const child = childPath.getValue();
|
||||
res.push(options.originalText.slice(index, child.start));
|
||||
res.push(childPath.call(print));
|
||||
index = child.end;
|
||||
}, "children");
|
||||
res.push(options.originalText.slice(index, n.end));
|
||||
|
||||
return concat(res);
|
||||
}
|
||||
|
||||
module.exports = genericPrint;
|
|
@ -56,6 +56,8 @@ function getPrintFunction(options) {
|
|||
return require("./printer-graphql");
|
||||
case "parse5":
|
||||
return require("./printer-htmlparser2");
|
||||
case "vue":
|
||||
return require("./printer-vue");
|
||||
case "css":
|
||||
case "less":
|
||||
case "scss":
|
||||
|
|
|
@ -173,6 +173,19 @@ const supportTable = [
|
|||
linguistLanguageId: 222,
|
||||
vscodeLanguageIds: ["markdown"]
|
||||
},
|
||||
{
|
||||
name: "Vue",
|
||||
since: "1.10.0",
|
||||
parsers: ["vue"],
|
||||
group: "HTML",
|
||||
tmScope: "text.html.vue",
|
||||
aceMode: "html",
|
||||
codemirrorMode: "htmlmixed",
|
||||
codemirrorMimeType: "text/html",
|
||||
extensions: [".vue"],
|
||||
linguistLanguageId: 146,
|
||||
vscodeLanguageIds: ["vue"]
|
||||
},
|
||||
{
|
||||
name: "HTML",
|
||||
since: undefined, // unreleased
|
||||
|
|
|
@ -9,18 +9,16 @@ exports[`template-bind.vue 1`] = `
|
|||
</template>
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
<template>
|
||||
|
||||
<div v-bind:id="'list-' + id" />
|
||||
|
||||
<div v-bind:id="rawId | formatId" />
|
||||
|
||||
<div v-bind:id="ok ? 'YES' : 'NO'" />
|
||||
|
||||
<button @click="foo(arg, 'string')" />
|
||||
|
||||
<div v-bind:id=" 'list-' + id ">
|
||||
</div>
|
||||
<div v-bind:id=" rawId | formatId ">
|
||||
</div>
|
||||
<div v-bind:id=" ok ? 'YES' : 'NO' ">
|
||||
</div>
|
||||
<button @click=" foo ( arg, 'string' ) ">
|
||||
</button>
|
||||
</template>
|
||||
`;
|
||||
|
||||
|
@ -35,24 +33,23 @@ exports[`template-class.vue 1`] = `
|
|||
</h2>
|
||||
</template>
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
<template>
|
||||
|
||||
<h2
|
||||
class="title"
|
||||
:class="{ 'issue-realtime-pre-pulse': preAnimation,
|
||||
'issue-realtime-trigger-pulse': pulseAnimation}"
|
||||
v-html="titleHtml"
|
||||
>
|
||||
|
||||
<h2 class="title"
|
||||
:class="{
|
||||
'issue-realtime-pre-pulse': preAnimation,
|
||||
'issue-realtime-trigger-pulse': pulseAnimation
|
||||
}"
|
||||
v-html="titleHtml">
|
||||
</h2>
|
||||
</template>
|
||||
`;
|
||||
|
||||
exports[`vue-component.vue 1`] = `
|
||||
<template >
|
||||
<h1 >{{greeting}} world</h1 >
|
||||
<h1 >{{greeting}} world</h1 >
|
||||
<script>kikoo ( ) </script>
|
||||
</template >
|
||||
|
||||
<script>
|
||||
|
@ -68,28 +65,26 @@ p { font-size : 2em ; text-align : center ; }
|
|||
|
||||
</style >
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
<template >
|
||||
|
||||
|
||||
<template>
|
||||
|
||||
<h1>{{greeting}} world</h1>
|
||||
</template>
|
||||
<h1 >
|
||||
{{greeting}} world</h1 >
|
||||
<script>
|
||||
kikoo ( ) </script>
|
||||
</template >
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
data: function() {
|
||||
return {
|
||||
greeting: "Hello"
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module . exports =
|
||||
{data : function () {return {
|
||||
greeting: "Hello"
|
||||
}}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p {
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<style scoped >
|
||||
|
||||
p { font-size : 2em ; text-align : center ; }
|
||||
|
||||
</style >
|
||||
`;
|
||||
|
|
|
@ -1 +1 @@
|
|||
run_spec(__dirname, ["parse5"]);
|
||||
run_spec(__dirname, ["vue"]);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template >
|
||||
<h1 >{{greeting}} world</h1 >
|
||||
<h1 >{{greeting}} world</h1 >
|
||||
<script>kikoo ( ) </script>
|
||||
</template >
|
||||
|
||||
<script>
|
||||
|
|
|
@ -265,7 +265,7 @@ exports[`show detailed usage with --help no-semi (write) 1`] = `Array []`;
|
|||
exports[`show detailed usage with --help parser (stderr) 1`] = `""`;
|
||||
|
||||
exports[`show detailed usage with --help parser (stdout) 1`] = `
|
||||
"--parser <flow|babylon|typescript|css|less|scss|json|graphql|markdown>
|
||||
"--parser <flow|babylon|typescript|css|less|scss|json|graphql|markdown|vue>
|
||||
|
||||
Which parser to use.
|
||||
|
||||
|
@ -280,6 +280,7 @@ Valid options:
|
|||
json
|
||||
graphql
|
||||
markdown
|
||||
vue
|
||||
|
||||
Default: babylon
|
||||
"
|
||||
|
@ -521,7 +522,7 @@ Format options:
|
|||
--no-bracket-spacing Do not print spaces between brackets.
|
||||
--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|graphql|markdown>
|
||||
--parser <flow|babylon|typescript|css|less|scss|json|graphql|markdown|vue>
|
||||
Which parser to use.
|
||||
Defaults to babylon.
|
||||
--print-width <int> The line length where Prettier will try wrap.
|
||||
|
@ -615,7 +616,7 @@ exports[`show warning with --help not-found (typo) (stderr) 1`] = `
|
|||
`;
|
||||
|
||||
exports[`show warning with --help not-found (typo) (stdout) 1`] = `
|
||||
"--parser <flow|babylon|typescript|css|less|scss|json|graphql|markdown>
|
||||
"--parser <flow|babylon|typescript|css|less|scss|json|graphql|markdown|vue>
|
||||
|
||||
Which parser to use.
|
||||
|
||||
|
@ -630,6 +631,7 @@ Valid options:
|
|||
json
|
||||
graphql
|
||||
markdown
|
||||
vue
|
||||
|
||||
Default: babylon
|
||||
"
|
||||
|
@ -660,7 +662,7 @@ Format options:
|
|||
--no-bracket-spacing Do not print spaces between brackets.
|
||||
--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|graphql|markdown>
|
||||
--parser <flow|babylon|typescript|css|less|scss|json|graphql|markdown|vue>
|
||||
Which parser to use.
|
||||
Defaults to babylon.
|
||||
--print-width <int> The line length where Prettier will try wrap.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
- "[JSX](https://facebook.github.io/jsx/)"
|
||||
- "[Flow](https://flow.org/)"
|
||||
- "[TypeScript](https://www.typescriptlang.org/)"
|
||||
- "[Vue](https://vuejs.org/)"
|
||||
- "[JSON](http://json.org/)"
|
||||
- name: CSS
|
||||
image: "/images/languages/css-128px.png"
|
||||
|
|
Loading…
Reference in New Issue