Refactor and use getPreferredQuote in both jsx and handlebars printers (#11569)

* raw => rawContent

* Refactor getPreferredQuote

Exposing { quote, regex, escaped } from the getPreferredQuote return
value allows us to simplify a bit the code in the printer.

* Use the common/util function

And remove chooseEnclosingQuote

* Move favoriteQuote

* Fix lint

* Remove enclosing quotes _before_ unescape
This commit is contained in:
Cyrille authored and GitHub committed 2021-09-24 06:32:32 +09:00
1 parent 737d436a95
commit 2f9663b6fa
3 files changed
+31 -57

No files matched your search

+10 -16
View File
@@ -343,24 +343,21 @@ function getIndentSize(value, tabWidth) {
/**
*
* @param {string} raw
* @param {string} rawContent
* @param {Quote} preferredQuote
* @returns {Quote}
* @returns {{ quote: Quote, regex: RegExp, escaped: string }}
*/
function getPreferredQuote(raw, preferredQuote) {
// `rawContent` is the string exactly like it appeared in the input source
// code, without its enclosing quotes.
const rawContent = raw.slice(1, -1);
/** @type {{ quote: '"', regex: RegExp }} */
const double = { quote: '"', regex: /"/g };
/** @type {{ quote: "'", regex: RegExp }} */
const single = { quote: "'", regex: /'/g };
function getPreferredQuote(rawContent, preferredQuote) {
/** @type {{ quote: '"', regex: RegExp, escaped: """ }} */
const double = { quote: '"', regex: /"/g, escaped: """ };
/** @type {{ quote: "'", regex: RegExp, escaped: "'" }} */
const single = { quote: "'", regex: /'/g, escaped: "'" };
const preferred = preferredQuote === "'" ? single : double;
const alternate = preferred === single ? double : single;
let result = preferred.quote;
let result = preferred;
// If `rawContent` contains at least one of the quote preferred for enclosing
// the string, we might want to enclose with the alternate quote instead, to
@@ -372,10 +369,7 @@ function getPreferredQuote(raw, preferredQuote) {
const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length;
const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length;
result =
numPreferredQuotes > numAlternateQuotes
? alternate.quote
: preferred.quote;
result = numPreferredQuotes > numAlternateQuotes ? alternate : preferred;
}
return result;
@@ -395,7 +389,7 @@ function printString(raw, options) {
? '"'
: options.__isInHtmlAttribute
? "'"
: getPreferredQuote(raw, options.singleQuote ? "'" : '"');
: getPreferredQuote(rawContent, options.singleQuote ? "'" : '"').quote;
// It might sound unnecessary to use `makeString` even if the string already
// is enclosed with `enclosingQuote`, but it isn't. The string could contain
+13 -36
View File
@@ -14,7 +14,7 @@ const {
},
utils: { getDocParts, replaceTextEndOfLine },
} = require("../document/index.js");
const { isNonEmptyArray } = require("../common/util.js");
const { getPreferredQuote, isNonEmptyArray } = require("../common/util.js");
const { locStart, locEnd } = require("./loc.js");
const clean = require("./clean.js");
const {
@@ -47,6 +47,8 @@ function print(path, options, print) {
return options.originalText.slice(locStart(node), locEnd(node));
}
const favoriteQuote = options.singleQuote ? "'" : '"';
switch (node.type) {
case "Block":
case "Program":
@@ -153,14 +155,14 @@ function print(path, options, print) {
// Let's assume quotes inside the content of text nodes are already
// properly escaped with entities, otherwise the parse wouldn't have parsed them.
const quote = isText
? chooseEnclosingQuote(options, node.value.chars).quote
? getPreferredQuote(node.value.chars, favoriteQuote).quote
: node.value.type === "ConcatStatement"
? chooseEnclosingQuote(
options,
? getPreferredQuote(
node.value.parts
.filter((part) => part.type === "TextNode")
.map((part) => part.chars)
.join("")
.join(""),
favoriteQuote
).quote
: "";
@@ -397,7 +399,7 @@ function print(path, options, print) {
return ["<!--", node.value, "-->"];
}
case "StringLiteral": {
return printStringLiteral(node.value, options);
return printStringLiteral(node.value, favoriteQuote);
}
case "NumberLiteral": {
return String(node.value);
@@ -691,6 +693,8 @@ function generateHardlines(number = 0) {
/* StringLiteral print helpers */
/** @typedef {import("../common/util").Quote} Quote */
/**
* Prints a string literal with the correct surrounding quotes based on
* `options.singleQuote` and the number of escaped quotes contained in
@@ -698,40 +702,13 @@ function generateHardlines(number = 0) {
* in `common/util`, but has differences because of the way escaped characters
* are treated in hbs string literals.
* @param {string} stringLiteral - the string literal value
* @param {object} options - the prettier options object
* @param {Quote} favoriteQuote - the user's preferred quote: `'` or `"`
*/
function printStringLiteral(stringLiteral, options) {
const { quote, regex } = chooseEnclosingQuote(options, stringLiteral);
function printStringLiteral(stringLiteral, favoriteQuote) {
const { quote, regex } = getPreferredQuote(stringLiteral, favoriteQuote);
return [quote, stringLiteral.replace(regex, `\\${quote}`), quote];
}
function chooseEnclosingQuote(options, stringLiteral) {
const double = { quote: '"', regex: /"/g };
const single = { quote: "'", regex: /'/g };
const preferred = options.singleQuote ? single : double;
const alternate = preferred === single ? double : single;
let shouldUseAlternateQuote = false;
// If `stringLiteral` contains at least one of the quote preferred for
// enclosing the string, we might want to enclose with the alternate quote
// instead, to minimize the number of escaped quotes.
if (
stringLiteral.includes(preferred.quote) ||
stringLiteral.includes(alternate.quote)
) {
const numPreferredQuotes = (stringLiteral.match(preferred.regex) || [])
.length;
const numAlternateQuotes = (stringLiteral.match(alternate.regex) || [])
.length;
shouldUseAlternateQuote = numPreferredQuotes > numAlternateQuotes;
}
return shouldUseAlternateQuote ? alternate : preferred;
}
/* SubExpression print helpers */
function printSubExpressionPathAndParams(path, print) {
+8 -5
View File
@@ -479,14 +479,17 @@ function printJsxAttribute(path, options, print) {
let res;
if (isStringLiteral(node.value)) {
const raw = rawText(node.value);
// Unescape all quotes so we get an accurate preferred quote
let final = raw.replace(/&apos;/g, "'").replace(/&quot;/g, '"');
const quote = getPreferredQuote(
// Remove enclosing quotes and unescape
// all quotes so we get an accurate preferred quote
let final = raw
.slice(1, -1)
.replace(/&apos;/g, "'")
.replace(/&quot;/g, '"');
const { escaped, quote, regex } = getPreferredQuote(
final,
options.jsxSingleQuote ? "'" : '"'
);
const escape = quote === "'" ? "&apos;" : "&quot;";
final = final.slice(1, -1).replace(new RegExp(quote, "g"), escape);
final = final.replace(regex, escaped);
res = [quote, final, quote];
} else {
res = print("value");