diff --git a/src/language-html/printer-html.js b/src/language-html/printer-html.js
index d0ba2f0b6..1cab5dea6 100644
--- a/src/language-html/printer-html.js
+++ b/src/language-html/printer-html.js
@@ -20,6 +20,7 @@ const {
softline
} = builders;
const {
+ countChars,
countParents,
dedentString,
forceBreakChildren,
@@ -36,7 +37,8 @@ const {
replaceDocNewlines,
replaceNewlines,
shouldNotPrintClosingTag,
- shouldPreserveContent
+ shouldPreserveContent,
+ unescapeQuoteEntities
} = require("./utils");
const preprocess = require("./preprocess");
const assert = require("assert");
@@ -325,19 +327,31 @@ function genericPrint(path, options, print) {
printClosingTagSuffix(node, options)
]);
}
- case "attribute":
+ case "attribute": {
+ if (node.value === null) {
+ return node.rawName;
+ }
+ const value = unescapeQuoteEntities(node.value);
+ const singleQuoteCount = countChars(value, "'");
+ const doubleQuoteCount = countChars(value, '"');
+ const quote = singleQuoteCount < doubleQuoteCount ? "'" : '"';
return concat([
node.rawName,
- node.value === null
- ? ""
- : concat([
- '="',
- concat(
- replaceNewlines(node.value.replace(/"/g, """), literalline)
- ),
- '"'
- ])
+ concat([
+ "=",
+ quote,
+ concat(
+ replaceNewlines(
+ quote === '"'
+ ? value.replace(/"/g, """)
+ : value.replace(/'/g, "'"),
+ literalline
+ )
+ ),
+ quote
+ ])
]);
+ }
case "yaml":
case "toml":
return node.raw;
@@ -892,8 +906,7 @@ function getTextValueParts(node, value = node.value) {
function printEmbeddedAttributeValue(node, originalTextToDoc, options) {
const isKeyMatched = patterns =>
new RegExp(patterns.join("|")).test(node.fullName);
- const getValue = () =>
- node.value.replace(/"/g, '"').replace(/'/g, "'");
+ const getValue = () => unescapeQuoteEntities(node.value);
let shouldHug = false;
diff --git a/src/language-html/utils.js b/src/language-html/utils.js
index 8435940dc..1c9940e78 100644
--- a/src/language-html/utils.js
+++ b/src/language-html/utils.js
@@ -602,10 +602,25 @@ function shouldNotPrintClosingTag(node, options) {
);
}
+function countChars(text, char) {
+ let counter = 0;
+ for (let i = 0; i < text.length; i++) {
+ if (text[i] === char) {
+ counter++;
+ }
+ }
+ return counter;
+}
+
+function unescapeQuoteEntities(text) {
+ return text.replace(/'/g, "'").replace(/"/g, '"');
+}
+
module.exports = {
HTML_ELEMENT_ATTRIBUTES,
HTML_TAGS,
canHaveInterpolation,
+ countChars,
countParents,
dedentString,
forceBreakChildren,
@@ -633,5 +648,6 @@ module.exports = {
replaceDocNewlines,
replaceNewlines,
shouldNotPrintClosingTag,
- shouldPreserveContent
+ shouldPreserveContent,
+ unescapeQuoteEntities
};
diff --git a/tests/html_attributes/__snapshots__/jsfmt.spec.js.snap b/tests/html_attributes/__snapshots__/jsfmt.spec.js.snap
index 33f99bd5f..1a114d12b 100644
--- a/tests/html_attributes/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/html_attributes/__snapshots__/jsfmt.spec.js.snap
@@ -277,7 +277,29 @@ printWidth: 80
=====================================output=====================================
-
+
+
+================================================================================
+`;
+
+exports[`smart-quotes.html 1`] = `
+====================================options=====================================
+parsers: ["html"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+