Format <script> content in glimmer templates (#20012)
Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
1 parent
968c1a62c8
commit
2529b92818
14 files changed
+357
-28
No files matched your search
@@ -0,0 +1,21 @@
|
|||||||
|
#### Support embedded `<script>` (#20012 by @kovsu)
|
||||||
|
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
```hbs
|
||||||
|
{{! Input }}
|
||||||
|
<script>
|
||||||
|
const a = 1;
|
||||||
|
console.log( a );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{! Prettier stable }}
|
||||||
|
<script>
|
||||||
|
const a = 1; console.log( a );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{! Prettier main }}
|
||||||
|
<script>
|
||||||
|
const a = 1;
|
||||||
|
console.log(a);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
@@ -1,4 +1,40 @@
|
|||||||
import { isPlainTextStyleElement } from "./utilities.js";
|
import { isPlainTextStyleOrScriptElement } from "./utilities.js";
|
||||||
|
|
||||||
|
function getAttribute(node, name) {
|
||||||
|
return node.attributes.find(
|
||||||
|
(attribute) => attribute.type === "AttrNode" && attribute.name === name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTextValue(attribute) {
|
||||||
|
return attribute.value.type === "TextNode"
|
||||||
|
? attribute.value.chars
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScriptTextToDocOptions(node) {
|
||||||
|
if (getAttribute(node, "src")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeAttribute = getAttribute(node, "type");
|
||||||
|
const type = typeAttribute ? getTextValue(typeAttribute) : undefined;
|
||||||
|
|
||||||
|
if (!typeAttribute || type === "module" || type === "text/javascript") {
|
||||||
|
return {
|
||||||
|
parser: "babel",
|
||||||
|
__embeddedInHtml: true,
|
||||||
|
__babelSourceType: type === "module" ? "module" : "script",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStyleTextToDocOptions(node) {
|
||||||
|
const langAttribute = getAttribute(node, "lang");
|
||||||
|
if (!langAttribute || getTextValue(langAttribute) === "css") {
|
||||||
|
return { parser: "css" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function embed(path /* , options*/) {
|
function embed(path /* , options*/) {
|
||||||
const { node } = path;
|
const { node } = path;
|
||||||
@@ -9,30 +45,27 @@ function embed(path /* , options*/) {
|
|||||||
|
|
||||||
const { parent } = path;
|
const { parent } = path;
|
||||||
|
|
||||||
if (!(isPlainTextStyleElement(parent) && parent.children[0] === node)) {
|
if (!(
|
||||||
|
isPlainTextStyleOrScriptElement(parent) && parent.children[0] === node
|
||||||
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const languageAttribute = parent.attributes.find(
|
const textToDocOptions =
|
||||||
(attribute) => attribute.type === "AttrNode" && attribute.name === "lang",
|
parent.tag === "style"
|
||||||
);
|
? getStyleTextToDocOptions(parent)
|
||||||
if (
|
: getScriptTextToDocOptions(parent);
|
||||||
languageAttribute &&
|
|
||||||
!(
|
if (!textToDocOptions) {
|
||||||
languageAttribute.value.type === "TextNode" &&
|
|
||||||
languageAttribute.value.chars === "css"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return async (textToDoc) => {
|
return async (textToDoc) => {
|
||||||
const context = node.chars;
|
const content = node.chars;
|
||||||
if (!context.trim()) {
|
if (!content.trim()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const doc = await textToDoc(context, { parser: "css" });
|
return await textToDoc(content, textToDocOptions);
|
||||||
return doc;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import htmlWhitespace from "../../utilities/html-whitespace.js";
|
import htmlWhitespace from "../../utilities/html-whitespace.js";
|
||||||
import { isPlainTextStyleElement } from "../utilities.js";
|
import { isPlainTextStyleOrScriptElement } from "../utilities.js";
|
||||||
|
|
||||||
function massageAstNode(original, cloned, parent) {
|
function massageAstNode(original, cloned, parent) {
|
||||||
// (Glimmer/HTML) ignore TextNode
|
// (Glimmer/HTML) ignore TextNode
|
||||||
@@ -9,8 +9,11 @@ function massageAstNode(original, cloned, parent) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS will be formatted
|
// CSS/JS will be formatted
|
||||||
if (isPlainTextStyleElement(parent) && parent.children[0] === original) {
|
if (
|
||||||
|
isPlainTextStyleOrScriptElement(parent) &&
|
||||||
|
parent.children[0] === original
|
||||||
|
) {
|
||||||
cloned.chars = "";
|
cloned.chars = "";
|
||||||
} else {
|
} else {
|
||||||
cloned.chars = htmlWhitespace.split(trimmed).join(" ");
|
cloned.chars = htmlWhitespace.split(trimmed).join(" ");
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { locEnd, locStart } from "./loc.js";
|
|||||||
import { massageAstNode } from "./massage-ast/index.js";
|
import { massageAstNode } from "./massage-ast/index.js";
|
||||||
import {
|
import {
|
||||||
hasPrettierIgnore,
|
hasPrettierIgnore,
|
||||||
isPlainTextStyleElement,
|
isPlainTextStyleOrScriptElement,
|
||||||
isVoidElement,
|
isVoidElement,
|
||||||
isWhitespaceNode,
|
isWhitespaceNode,
|
||||||
} from "./utilities.js";
|
} from "./utilities.js";
|
||||||
@@ -60,11 +60,11 @@ function print(path, options, print) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const endingTag = ["</", node.tag, ">"];
|
const endingTag = ["</", node.tag, ">"];
|
||||||
const isPlainStyle = isPlainTextStyleElement(node);
|
const isPlainStyleOrScript = isPlainTextStyleOrScriptElement(node);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
node.children.length === 0 ||
|
node.children.length === 0 ||
|
||||||
((!isWhitespaceSensitive || isPlainStyle) &&
|
((!isWhitespaceSensitive || isPlainStyleOrScript) &&
|
||||||
node.children.every((node) => isWhitespaceNode(node)))
|
node.children.every((node) => isWhitespaceNode(node)))
|
||||||
) {
|
) {
|
||||||
return [startingTag, endingTag];
|
return [startingTag, endingTag];
|
||||||
@@ -72,7 +72,7 @@ function print(path, options, print) {
|
|||||||
|
|
||||||
const parts = path.map(print, "children");
|
const parts = path.map(print, "children");
|
||||||
|
|
||||||
if (isPlainStyle || !isWhitespaceSensitive) {
|
if (!isWhitespaceSensitive || isPlainStyleOrScript) {
|
||||||
return [startingTag, indent([softline, ...parts]), softline, endingTag];
|
return [startingTag, indent([softline, ...parts]), softline, endingTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,8 @@ function print(path, options, print) {
|
|||||||
case "TextNode": {
|
case "TextNode": {
|
||||||
// Don't format content:
|
// Don't format content:
|
||||||
// 1. in `<pre>`,
|
// 1. in `<pre>`,
|
||||||
// 2. in `<style>`
|
// 2. in `<style>`,
|
||||||
|
// 3. in `<script>`
|
||||||
|
|
||||||
let text = node.chars;
|
let text = node.chars;
|
||||||
|
|
||||||
@@ -173,7 +174,10 @@ function print(path, options, print) {
|
|||||||
return replaceEndOfLine(text);
|
return replaceEndOfLine(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlainTextStyleElement(parent)) {
|
if (
|
||||||
|
isPlainTextStyleOrScriptElement(parent) &&
|
||||||
|
parent.children[0] === node
|
||||||
|
) {
|
||||||
text = text.replaceAll(/^\n+/g, "");
|
text = text.replaceAll(/^\n+/g, "");
|
||||||
text = htmlWhitespace.trimEnd(text);
|
text = htmlWhitespace.trimEnd(text);
|
||||||
text = htmlWhitespace.dedentString(text);
|
text = htmlWhitespace.dedentString(text);
|
||||||
|
|||||||
@@ -42,8 +42,10 @@ function isPlainTextElement(node) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPlainTextStyleElement(node) {
|
function isPlainTextStyleOrScriptElement(node) {
|
||||||
return isPlainTextElement(node) && node.tag === "style";
|
return (
|
||||||
|
isPlainTextElement(node) && (node.tag === "style" || node.tag === "script")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPrettierIgnoreNode(node) {
|
function isPrettierIgnoreNode(node) {
|
||||||
@@ -67,7 +69,7 @@ function hasPrettierIgnore(path) {
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
hasPrettierIgnore,
|
hasPrettierIgnore,
|
||||||
isPlainTextStyleElement,
|
isPlainTextStyleOrScriptElement,
|
||||||
isVoidElement,
|
isVoidElement,
|
||||||
isWhitespaceNode,
|
isWhitespaceNode,
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
|
exports[`script-tag.hbs format 1`] = `
|
||||||
|
====================================options=====================================
|
||||||
|
parsers: ["glimmer"]
|
||||||
|
printWidth: 80 (default) |
|
||||||
|
=====================================input======================================
|
||||||
|
<script></script>
|
||||||
|
|
||||||
|
<script> </script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import a from "a" ;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Should not support TLA in script without \`[type="module"]\`
|
||||||
|
await shouldNotFormat ( a );
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
|
// TLA
|
||||||
|
await shouldFormat ( a );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
<!--Should not support HTML comment in \`script[type="module"]\`
|
||||||
|
shouldNotFormat ( a );
|
||||||
|
-->
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
<!--html comment
|
||||||
|
shouldFormat ( a );
|
||||||
|
-->
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const template = html\`<div><script>shouldFormat ( )<\\/script></div>\`;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
=====================================output=====================================
|
||||||
|
<script></script>
|
||||||
|
|
||||||
|
<script></script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import a from "a";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Should not support TLA in script without \`[type="module"]\`
|
||||||
|
await shouldNotFormat ( a );
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
|
// TLA
|
||||||
|
await shouldFormat(a);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
<!--Should not support HTML comment in \`script[type="module"]\`
|
||||||
|
shouldNotFormat ( a );
|
||||||
|
-->
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
<!--html comment
|
||||||
|
shouldFormat(a);
|
||||||
|
-->
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const template = html\`<div>
|
||||||
|
<script>
|
||||||
|
shouldFormat();
|
||||||
|
<\\/script>
|
||||||
|
</div>\`;
|
||||||
|
</script>
|
||||||
|
================================================================================
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`type-attribute.hbs format 1`] = `
|
||||||
|
====================================options=====================================
|
||||||
|
parsers: ["glimmer"]
|
||||||
|
printWidth: 80 (default) |
|
||||||
|
=====================================input======================================
|
||||||
|
<script>
|
||||||
|
shouldFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
shouldFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type>
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script type="">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script type="unknown">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{ "should" : "not format" }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
=====================================output=====================================
|
||||||
|
<script>
|
||||||
|
shouldFormat();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
shouldFormat();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type>
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script type="">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script type="unknown">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{ "should" : "not format" }
|
||||||
|
</script>
|
||||||
|
================================================================================
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`with-mustache.hbs format 1`] = `
|
||||||
|
====================================options=====================================
|
||||||
|
parsers: ["glimmer"]
|
||||||
|
printWidth: 80 (default) |
|
||||||
|
=====================================input======================================
|
||||||
|
<script>
|
||||||
|
shouldNotFormat( {{foo}} )
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
shouldNotFormat( '{{user.name}}' )
|
||||||
|
{{#if debug}}shouldNotFormat( 'debug' ){{/if}}
|
||||||
|
</script>
|
||||||
|
<script>{{foo}}</script>
|
||||||
|
|
||||||
|
=====================================output=====================================
|
||||||
|
<script>
|
||||||
|
shouldNotFormat(
|
||||||
|
{{foo}}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
shouldNotFormat( '{{user.name}}' )
|
||||||
|
{{#if debug}}shouldNotFormat( 'debug' ){{/if}}
|
||||||
|
</script>
|
||||||
|
<script>{{foo}}</script>
|
||||||
|
================================================================================
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`with-src.hbs format 1`] = `
|
||||||
|
====================================options=====================================
|
||||||
|
parsers: ["glimmer"]
|
||||||
|
printWidth: 80 (default) |
|
||||||
|
=====================================input======================================
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script src="main.js">shouldNotFormat ( );</script>
|
||||||
|
<script src>shouldNotFormat ( );</script>
|
||||||
|
<script src="">shouldNotFormat ( );</script>
|
||||||
|
|
||||||
|
=====================================output=====================================
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script src="main.js">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script src>
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script src="">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
================================================================================
|
||||||
|
`;
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
|
exports[`test.hbs - {"embeddedLanguageFormatting":"off"} format 1`] = `
|
||||||
|
====================================options=====================================
|
||||||
|
embeddedLanguageFormatting: "off"
|
||||||
|
parsers: ["glimmer"]
|
||||||
|
printWidth: 80 (default) |
|
||||||
|
=====================================input======================================
|
||||||
|
<script>
|
||||||
|
call ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
=====================================output=====================================
|
||||||
|
<script>call ( );</script>
|
||||||
|
================================================================================
|
||||||
|
`;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
runFormatTest(import.meta, ["glimmer"], { embeddedLanguageFormatting: "off" });
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<script>
|
||||||
|
call ( );
|
||||||
|
</script>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
runFormatTest(import.meta, ["glimmer"]);
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<script></script>
|
||||||
|
|
||||||
|
<script> </script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import a from "a" ;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Should not support TLA in script without `[type="module"]`
|
||||||
|
await shouldNotFormat ( a );
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
|
// TLA
|
||||||
|
await shouldFormat ( a );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
<!--Should not support HTML comment in `script[type="module"]`
|
||||||
|
shouldNotFormat ( a );
|
||||||
|
-->
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
<!--html comment
|
||||||
|
shouldFormat ( a );
|
||||||
|
-->
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const template = html`<div><script>shouldFormat ( )<\/script></div>`;
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<script>
|
||||||
|
shouldFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
shouldFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type>
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script type="">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
<script type="unknown">
|
||||||
|
shouldNotFormat ( );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{ "should" : "not format" }
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
shouldNotFormat( {{foo}} )
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
shouldNotFormat( '{{user.name}}' )
|
||||||
|
{{#if debug}}shouldNotFormat( 'debug' ){{/if}}
|
||||||
|
</script>
|
||||||
|
<script>{{foo}}</script>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<script src="main.js"></script>
|
||||||
|
<script src="main.js">shouldNotFormat ( );</script>
|
||||||
|
<script src>shouldNotFormat ( );</script>
|
||||||
|
<script src="">shouldNotFormat ( );</script>
|
||||||
Reference in new issue
Block a user