From 363a20e1c182784de7664d8c782137daf5548312 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 1 Dec 2025 08:37:51 +0800 Subject: [PATCH] Improve `getPreferredQuote` (#18366) --- benchmarks/get-preferred-quote.js | 30 +++++++++++++++++++++ src/utilities/get-preferred-quote.js | 40 +++++++++++++++++++++------- 2 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 benchmarks/get-preferred-quote.js diff --git a/benchmarks/get-preferred-quote.js b/benchmarks/get-preferred-quote.js new file mode 100644 index 000000000..6497ac8f7 --- /dev/null +++ b/benchmarks/get-preferred-quote.js @@ -0,0 +1,30 @@ +import assert from "node:assert/strict"; +// import * as prettierDevBundled from "../dist/prettier/index.mjs"; +import * as prettierProduction from "../node_modules/prettier/index.mjs"; +import * as prettierDevelopment from "../src/index.js"; +import { runBenchmark } from "./utilities.js"; + +assert.notEqual(prettierProduction.version, prettierDevelopment.version); + +for (const size of [1, 1e1, 1e2, 1e3, 1e4, 1e5]) { + const text = Array.from({ length: size }, () => + Math.random() > 0.5 ? " " : Math.random() > 0.5 ? "'" : '"', + ).join(""); + const run = (prettier) => prettier.util.getPreferredQuote(text, "'"); + const expected = run(prettierProduction); + + await runBenchmark( + { + name: `getPreferredQuote (${size} characters)`, + assert: (result) => assert.deepEqual(result, expected), + }, + [ + { name: "Development", prettier: prettierDevelopment }, + // { name: "Development(Bundled)", prettier: prettierDevBundled }, + { name: "Production", prettier: prettierProduction }, + ].map(({ name, prettier }) => ({ + name, + implementation: () => run(prettier), + })), + ); +} diff --git a/src/utilities/get-preferred-quote.js b/src/utilities/get-preferred-quote.js index ec1a85ff7..dbf2ebac4 100644 --- a/src/utilities/get-preferred-quote.js +++ b/src/utilities/get-preferred-quote.js @@ -5,31 +5,51 @@ const SINGLE_QUOTE = "'"; const DOUBLE_QUOTE = '"'; +const SINGLE_QUOTE_DATA = Object.freeze({ + character: SINGLE_QUOTE, + codePoint: 39, +}); +const DOUBLE_QUOTE_DATA = Object.freeze({ + character: DOUBLE_QUOTE, + codePoint: 34, +}); + +const SINGLE_QUOTE_SETTINGS = Object.freeze({ + preferred: SINGLE_QUOTE_DATA, + alternate: DOUBLE_QUOTE_DATA, +}); +const DOUBLE_QUOTE_SETTINGS = Object.freeze({ + preferred: DOUBLE_QUOTE_DATA, + alternate: SINGLE_QUOTE_DATA, +}); + /** - * * @param {string} text * @param {Quote | boolean} preferredQuoteOrPreferSingleQuote * @returns {Quote} */ function getPreferredQuote(text, preferredQuoteOrPreferSingleQuote) { - const preferred = + const { preferred, alternate } = preferredQuoteOrPreferSingleQuote === true || preferredQuoteOrPreferSingleQuote === SINGLE_QUOTE - ? SINGLE_QUOTE - : DOUBLE_QUOTE; - const alternate = preferred === SINGLE_QUOTE ? DOUBLE_QUOTE : SINGLE_QUOTE; - + ? SINGLE_QUOTE_SETTINGS + : DOUBLE_QUOTE_SETTINGS; + const { length } = text; let preferredQuoteCount = 0; let alternateQuoteCount = 0; - for (const character of text) { - if (character === preferred) { + + // `for..of` loop is known slower + for (let index = 0; index < length; index++) { + const codePoint = text.charCodeAt(index); + if (codePoint === preferred.codePoint) { preferredQuoteCount++; - } else if (character === alternate) { + } else if (codePoint === alternate.codePoint) { alternateQuoteCount++; } } - return preferredQuoteCount > alternateQuoteCount ? alternate : preferred; + return (preferredQuoteCount > alternateQuoteCount ? alternate : preferred) + .character; } export default getPreferredQuote;