Extract common/util.js to utils/ (#14329)

This commit is contained in:
fisker Cheung authored and GitHub committed 2023-02-13 10:17:39 +08:00
1 parent 83124d7fd6
commit 282a4019c7
66 files changed
+496 -499

No files matched your search

-389
View File
@@ -1,389 +0,0 @@
import escapeStringRegexp from "escape-string-regexp";
import {
skip,
skipWhitespace,
skipSpaces,
skipToLineEnd,
skipEverythingButNewLine,
} from "../utils/text/skip.js";
import skipInlineComment from "../utils/text/skip-inline-comment.js";
import skipTrailingComment from "../utils/text/skip-trailing-comment.js";
import skipNewline from "../utils/text/skip-newline.js";
import getNextNonSpaceNonCommentCharacterIndex from "../utils/text/get-next-non-space-non-comment-character-index.js";
/**
* @typedef {{backwards?: boolean}} SkipOptions
*/
/**
* @param {string} text
* @param {number} startIndex
* @param {SkipOptions=} options
* @returns {boolean}
*/
function hasNewline(text, startIndex, options = {}) {
const idx = skipSpaces(
text,
options.backwards ? startIndex - 1 : startIndex,
options
);
const idx2 = skipNewline(text, idx, options);
return idx !== idx2;
}
/**
* @param {string} text
* @param {number} startIndex
* @param {number} endIndex
* @returns {boolean}
*/
function hasNewlineInRange(text, startIndex, endIndex) {
for (let i = startIndex; i < endIndex; ++i) {
if (text.charAt(i) === "\n") {
return true;
}
}
return false;
}
// Note: this function doesn't ignore leading comments unlike isNextLineEmpty
/**
* @param {string} text
* @param {number} startIndex
* @returns {boolean}
*/
function isPreviousLineEmpty(text, startIndex) {
/** @type {number | false} */
let idx = startIndex - 1;
idx = skipSpaces(text, idx, { backwards: true });
idx = skipNewline(text, idx, { backwards: true });
idx = skipSpaces(text, idx, { backwards: true });
const idx2 = skipNewline(text, idx, { backwards: true });
return idx !== idx2;
}
/**
* @param {string} text
* @param {number} startIndex
* @returns {boolean}
*/
function isNextLineEmpty(text, startIndex) {
/** @type {number | false} */
let oldIdx = null;
/** @type {number | false} */
let idx = startIndex;
while (idx !== oldIdx) {
// We need to skip all the potential trailing inline comments
oldIdx = idx;
idx = skipToLineEnd(text, idx);
idx = skipInlineComment(text, idx);
idx = skipSpaces(text, idx);
}
idx = skipTrailingComment(text, idx);
idx = skipNewline(text, idx);
return idx !== false && hasNewline(text, idx);
}
/**
* @param {string} text
* @param {number} startIndex
* @returns {string}
*/
function getNextNonSpaceNonCommentCharacter(text, startIndex) {
const index = getNextNonSpaceNonCommentCharacterIndex(text, startIndex);
return index === false ? "" : text.charAt(index);
}
// Not using, but it's public utils
/* c8 ignore start */
/**
* @param {string} text
* @param {number} startIndex
* @param {SkipOptions=} options
* @returns {boolean}
*/
function hasSpaces(text, startIndex, options = {}) {
const idx = skipSpaces(
text,
options.backwards ? startIndex - 1 : startIndex,
options
);
return idx !== startIndex;
}
/* c8 ignore stop */
/**
* @param {string} text
* @param {number} tabWidth
* @param {number=} startIndex
* @returns {number}
*/
function getAlignmentSize(text, tabWidth, startIndex = 0) {
let size = 0;
for (let i = startIndex; i < text.length; ++i) {
if (text[i] === "\t") {
// Tabs behave in a way that they are aligned to the nearest
// multiple of tabWidth:
// 0 -> 4, 1 -> 4, 2 -> 4, 3 -> 4
// 4 -> 8, 5 -> 8, 6 -> 8, 7 -> 8 ...
size = size + tabWidth - (size % tabWidth);
} else {
size++;
}
}
return size;
}
/**
* @param {string} value
* @param {number} tabWidth
* @returns {number}
*/
function getIndentSize(value, tabWidth) {
const lastNewlineIndex = value.lastIndexOf("\n");
/* c8 ignore next 3 */
if (lastNewlineIndex === -1) {
return 0;
}
return getAlignmentSize(
// All the leading whitespaces
value.slice(lastNewlineIndex + 1).match(/^[\t ]*/)[0],
tabWidth
);
}
/**
* @typedef {'"' | "'"} Quote
*/
/**
*
* @param {string} rawContent
* @param {Quote} preferredQuote
* @returns {{ quote: Quote, regex: RegExp, escaped: string }}
*/
function getPreferredQuote(rawContent, preferredQuote) {
/** @type {{ quote: '"', regex: RegExp, escaped: "&quot;" }} */
const double = { quote: '"', regex: /"/g, escaped: "&quot;" };
/** @type {{ quote: "'", regex: RegExp, escaped: "&apos;" }} */
const single = { quote: "'", regex: /'/g, escaped: "&apos;" };
const preferred = preferredQuote === "'" ? single : double;
const alternate = preferred === single ? double : single;
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
// minimize the number of escaped quotes.
if (
rawContent.includes(preferred.quote) ||
rawContent.includes(alternate.quote)
) {
const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length;
const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length;
result = numPreferredQuotes > numAlternateQuotes ? alternate : preferred;
}
return result;
}
function printString(raw, options) {
// `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} */
const enclosingQuote =
options.parser === "json" ||
(options.parser === "json5" &&
options.quoteProps === "preserve" &&
!options.singleQuote)
? '"'
: options.__isInHtmlAttribute
? "'"
: 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
// unnecessary escapes (such as in `"\'"`). Always using `makeString` makes
// sure that we consistently output the minimum amount of escaped quotes.
return makeString(
rawContent,
enclosingQuote,
!(
options.parser === "css" ||
options.parser === "less" ||
options.parser === "scss" ||
options.__embeddedInHtml
)
);
}
/**
* @param {string} rawText
* @param {Quote} enclosingQuote
* @param {boolean=} unescapeUnnecessaryEscapes
* @returns {string}
*/
function makeString(rawText, enclosingQuote, unescapeUnnecessaryEscapes) {
const otherQuote = enclosingQuote === '"' ? "'" : '"';
// Matches _any_ escape and unescaped quotes (both single and double).
const regex = /\\(.)|(["'])/gs;
// Escape and unescape single and double quotes as needed to be able to
// enclose `rawText` with `enclosingQuote`.
const raw = rawText.replaceAll(regex, (match, escaped, quote) => {
// If we matched an escape, and the escaped character is a quote of the
// other type than we intend to enclose the string with, there's no need for
// it to be escaped, so return it _without_ the backslash.
if (escaped === otherQuote) {
return escaped;
}
// If we matched an unescaped quote and it is of the _same_ type as we
// intend to enclose the string with, it must be escaped, so return it with
// a backslash.
if (quote === enclosingQuote) {
return "\\" + quote;
}
if (quote) {
return quote;
}
// Unescape any unnecessarily escaped character.
// Adapted from https://github.com/eslint/eslint/blob/de0b4ad7bd820ade41b1f606008bea68683dc11a/lib/rules/no-useless-escape.js#L27
return unescapeUnnecessaryEscapes &&
/^[^\n\r"'0-7\\bfnrt-vx\u2028\u2029]$/.test(escaped)
? escaped
: "\\" + escaped;
});
return enclosingQuote + raw + enclosingQuote;
}
function printNumber(rawNumber) {
return (
rawNumber
.toLowerCase()
// Remove unnecessary plus and zeroes from scientific notation.
.replace(/^([+-]?[\d.]+e)(?:\+|(-))?0*(?=\d)/, "$1$2")
// Remove unnecessary scientific notation (1e0).
.replace(/^([+-]?[\d.]+)e[+-]?0+$/, "$1")
// Make sure numbers always start with a digit.
.replace(/^([+-])?\./, "$10.")
// Remove extraneous trailing decimal zeroes.
.replace(/(\.\d+?)0+(?=e|$)/, "$1")
// Remove trailing dot.
.replace(/\.(?=e|$)/, "")
);
}
/**
* @param {string} text
* @param {string} searchString
* @returns {number}
*/
function getMaxContinuousCount(text, searchString) {
const results = text.match(
new RegExp(`(${escapeStringRegexp(searchString)})+`, "g")
);
if (results === null) {
return 0;
}
return results.reduce(
(maxCount, result) =>
Math.max(maxCount, result.length / searchString.length),
0
);
}
/**
* @param {string} text
* @param {string} searchString
* @returns {number}
*/
function getMinNotPresentContinuousCount(text, searchString) {
const matches = text.match(
new RegExp(`(${escapeStringRegexp(searchString)})+`, "g")
);
if (matches === null) {
return 0;
}
const countPresent = new Map();
let max = 0;
for (const match of matches) {
const count = match.length / searchString.length;
countPresent.set(count, true);
if (count > max) {
max = count;
}
}
for (let i = 1; i < max; i++) {
if (!countPresent.get(i)) {
return i;
}
}
return max + 1;
}
function isFrontMatterNode(node) {
return node?.type === "front-matter";
}
/**
* @param {string} description
* @returns {(node: any) => symbol}
*/
function createGroupIdMapper(description) {
const groupIds = new WeakMap();
return function (node) {
if (!groupIds.has(node)) {
groupIds.set(node, Symbol(description));
}
return groupIds.get(node);
};
}
export {
getMaxContinuousCount,
getMinNotPresentContinuousCount,
getNextNonSpaceNonCommentCharacterIndex,
getNextNonSpaceNonCommentCharacter,
skip,
skipWhitespace,
skipSpaces,
skipToLineEnd,
skipEverythingButNewLine,
skipInlineComment,
skipTrailingComment,
skipNewline,
isNextLineEmpty,
isPreviousLineEmpty,
hasNewline,
hasNewlineInRange,
hasSpaces,
getAlignmentSize,
getIndentSize,
getPreferredQuote,
printString,
printNumber,
makeString,
isFrontMatterNode,
createGroupIdMapper,
};
export { default as isNonEmptyArray } from "../utils/is-non-empty-array.js";
export { default as getStringWidth } from "../utils/get-string-width.js";
+1 -1
View File
@@ -39,7 +39,7 @@ for (const name of debugApiFunctionNames) {
prettier.__debug = debugApis;
if (process.env.NODE_ENV === "production") {
prettier.util = require("./common/util-shared.js");
prettier.util = require("./utils/public.js");
prettier.doc = require("./document/index.js");
} else {
Object.defineProperties(prettier, {
+2 -2
View File
@@ -20,7 +20,7 @@ import { hiddenDefaults as optionsHiddenDefaults } from "./main/options.js";
import normalizeOptions from "./main/options-normalizer.js";
import arrayify from "./utils/arrayify.js";
import partition from "./utils/partition.js";
import { isNonEmptyArray } from "./common/util.js";
import isNonEmptyArray from "./utils/is-non-empty-array.js";
const builtinPlugins = Object.values(languages);
@@ -110,6 +110,6 @@ export {
sharedWithCli as __internal,
debugApis as __debug,
};
export * as util from "./common/util-shared.js";
export * as util from "./utils/public.js";
export * as doc from "./document/index.js";
export { default as version } from "./main/version.evaluate.cjs";
+3 -3
View File
@@ -1,4 +1,4 @@
import { isFrontMatterNode } from "../common/util.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
const ignoredProperties = new Set([
"raw", // front-matter
@@ -12,7 +12,7 @@ const ignoredProperties = new Set([
]);
function clean(ast, newObj, parent) {
if (isFrontMatterNode(ast) && ast.lang === "yaml") {
if (isFrontMatter(ast) && ast.lang === "yaml") {
delete newObj.value;
}
@@ -25,7 +25,7 @@ function clean(ast, newObj, parent) {
// first non-front-matter comment
if (
parent.nodes[0] === ast ||
(isFrontMatterNode(parent.nodes[0]) && parent.nodes[1] === ast)
(isFrontMatter(parent.nodes[0]) && parent.nodes[1] === ast)
) {
/**
* something
+1 -1
View File
@@ -1,4 +1,4 @@
import { skipEverythingButNewLine } from "../utils/text/skip.js";
import { skipEverythingButNewLine } from "../utils/skip.js";
import isNonEmptyArray from "../utils/is-non-empty-array.js";
import lineColumnToIndex from "../utils/line-column-to-index.js";
+8 -10
View File
@@ -1,11 +1,9 @@
import {
printNumber,
printString,
hasNewline,
isFrontMatterNode,
isNextLineEmpty,
isNonEmptyArray,
} from "../common/util.js";
import printNumber from "../utils/print-number.js";
import printString from "../utils/print-string.js";
import hasNewline from "../utils/has-newline.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
import isNextLineEmpty from "../utils/is-next-line-empty.js";
import isNonEmptyArray from "../utils/is-non-empty-array.js";
import {
join,
line,
@@ -1100,7 +1098,7 @@ function printNodeSequence(path, options, print) {
!hasNewline(options.originalText, locStart(next), {
backwards: true,
}) &&
!isFrontMatterNode(node)) ||
!isFrontMatter(node)) ||
(next.type === "css-atrule" &&
next.name === "else" &&
node.type !== "css-comment")
@@ -1110,7 +1108,7 @@ function printNodeSequence(path, options, print) {
parts.push(options.__isHTMLStyleAttribute ? line : hardline);
if (
isNextLineEmpty(options.originalText, locEnd(node)) &&
!isFrontMatterNode(node)
!isFrontMatter(node)
) {
parts.push(hardline);
}
+2 -1
View File
@@ -7,7 +7,8 @@ import {
indent,
ifBreak,
} from "../document/builders.js";
import { isNextLineEmpty, isNonEmptyArray } from "../common/util.js";
import isNextLineEmpty from "../utils/is-next-line-empty.js";
import isNonEmptyArray from "../utils/is-non-empty-array.js";
import UnexpectedNodeError from "../utils/unexpected-node-error.js";
import { insertPragma } from "./pragma.js";
import { locStart, locEnd } from "./loc.js";
+3 -2
View File
@@ -10,7 +10,8 @@ import {
softline,
} from "../document/builders.js";
import { replaceEndOfLine } from "../document/utils.js";
import { getPreferredQuote, isNonEmptyArray } from "../common/util.js";
import getPreferredQuote from "../utils/get-preferred-quote.js";
import isNonEmptyArray from "../utils/is-non-empty-array.js";
import UnexpectedNodeError from "../utils/unexpected-node-error.js";
import { locStart, locEnd } from "./loc.js";
import clean from "./clean.js";
@@ -686,7 +687,7 @@ function generateHardlines(number = 0) {
/* StringLiteral print helpers */
/** @typedef {import("../common/util.js").Quote} Quote */
/** @typedef {import("../utils/get-preferred-quote.js").Quote} Quote */
/**
* Prints a string literal with the correct surrounding quotes based on
+2 -2
View File
@@ -1,4 +1,4 @@
import { isFrontMatterNode } from "../common/util.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
const ignoredProperties = new Set([
"sourceSpan",
@@ -18,7 +18,7 @@ function clean(ast, newNode) {
}
// may be formatted by multiparser
if (isFrontMatterNode(ast) || ast.type === "yaml" || ast.type === "toml") {
if (isFrontMatter(ast) || ast.type === "yaml" || ast.type === "toml") {
return null;
}
+1 -1
View File
@@ -3,7 +3,7 @@
*/
import assert from "node:assert";
import { isNonEmptyArray } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import {
indent,
join,
+4 -4
View File
@@ -2,7 +2,7 @@
* @typedef {import("../../common/ast-path.js").default} AstPath
*/
import { isFrontMatterNode } from "../../common/util.js";
import isFrontMatter from "../../utils/front-matter/is-front-matter.js";
import inferParserByLanguage from "../../utils/infer-parser-by-language.js";
import { line, hardline, join } from "../../document/builders.js";
import { replaceEndOfLine } from "../../document/utils.js";
@@ -144,7 +144,7 @@ function isLeadingSpaceSensitiveNode(node, options) {
return isLeadingSpaceSensitive;
function _isLeadingSpaceSensitiveNode() {
if (isFrontMatterNode(node)) {
if (isFrontMatter(node)) {
return false;
}
@@ -187,7 +187,7 @@ function isLeadingSpaceSensitiveNode(node, options) {
}
function isTrailingSpaceSensitiveNode(node, options) {
if (isFrontMatterNode(node)) {
if (isFrontMatter(node)) {
return false;
}
@@ -237,7 +237,7 @@ function isDanglingSpaceSensitiveNode(node) {
function forceNextEmptyLine(node) {
return (
isFrontMatterNode(node) ||
isFrontMatter(node) ||
(node.next &&
node.sourceSpan.end &&
node.sourceSpan.end.line + 1 < node.next.sourceSpan.start.line)
+5 -7
View File
@@ -1,10 +1,8 @@
import {
hasNewline,
getNextNonSpaceNonCommentCharacterIndex,
getNextNonSpaceNonCommentCharacter,
hasNewlineInRange,
isNonEmptyArray,
} from "../../common/util.js";
import hasNewline from "../../utils/has-newline.js";
import getNextNonSpaceNonCommentCharacter from "../../utils/get-next-non-space-non-comment-character.js";
import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js";
import hasNewlineInRange from "../../utils/has-newline-in-range.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import {
addLeadingComment,
addTrailingComment,
+1 -1
View File
@@ -1,4 +1,4 @@
import { isNonEmptyArray } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import { indent, hardline, softline } from "../../document/builders.js";
import { mapDoc, replaceEndOfLine, cleanDoc } from "../../document/utils.js";
import { printTemplateExpressions } from "../print/template-literal.js";
+1 -1
View File
@@ -1,6 +1,6 @@
import tryCombinations from "../../utils/try-combinations.js";
import getShebang from "../utils/get-shebang.js";
import getNextNonSpaceNonCommentCharacterIndex from "../../utils/text/get-next-non-space-non-comment-character-index.js";
import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js";
// JSON parsers are bundled here so we can reduce package size
import jsonParsers from "../../language-json/parser-json.js";
import createParser from "./utils/create-parser.js";
+1 -1
View File
@@ -8,7 +8,7 @@ import {
ifBreak,
fill,
} from "../../document/builders.js";
import { hasNewline } from "../../common/util.js";
import hasNewline from "../../utils/has-newline.js";
import {
shouldPrintComma,
hasComment,
+2 -1
View File
@@ -1,4 +1,5 @@
import { isNonEmptyArray, getStringWidth } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import getStringWidth from "../../utils/get-string-width.js";
import {
line,
group,
+1 -1
View File
@@ -1,5 +1,5 @@
import { printDanglingComments } from "../../main/comments/print.js";
import { isNonEmptyArray } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import { hardline, indent } from "../../document/builders.js";
import {
hasComment,
+2 -1
View File
@@ -1,4 +1,5 @@
import { isNonEmptyArray, createGroupIdMapper } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import createGroupIdMapper from "../../utils/create-group-id-mapper.js";
import {
printComments,
printDanglingComments,
+2 -1
View File
@@ -1,4 +1,5 @@
import { isNonEmptyArray, hasNewline } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import hasNewline from "../../utils/has-newline.js";
import {
line,
hardline,
+2 -1
View File
@@ -1,7 +1,8 @@
/** @typedef {import("../../document/builders.js").Doc} Doc */
import assert from "node:assert";
import { printString, printNumber } from "../../common/util.js";
import printString from "../../utils/print-string.js";
import printNumber from "../../utils/print-number.js";
import { replaceEndOfLine } from "../../document/utils.js";
import UnexpectedNodeError from "../../utils/unexpected-node-error.js";
import {
+1 -1
View File
@@ -1,4 +1,4 @@
import { getNextNonSpaceNonCommentCharacter } from "../../common/util.js";
import getNextNonSpaceNonCommentCharacter from "../../utils/get-next-non-space-non-comment-character.js";
import { printDanglingComments } from "../../main/comments/print.js";
import {
line,
+1 -1
View File
@@ -5,7 +5,7 @@ import {
printDanglingComments,
printCommentsSeparately,
} from "../../main/comments/print.js";
import { getNextNonSpaceNonCommentCharacterIndex } from "../../common/util.js";
import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js";
import {
line,
softline,
+1 -1
View File
@@ -1,4 +1,4 @@
import { isNonEmptyArray } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import { join, line, group, indent, ifBreak } from "../../document/builders.js";
import { hasComment, identity, CommentCheckFlags } from "../utils/index.js";
import { getTypeParametersGroupId } from "./type-parameters.js";
+1 -2
View File
@@ -16,8 +16,7 @@ import {
} from "../../document/builders.js";
import { willBreak, replaceEndOfLine } from "../../document/utils.js";
import UnexpectedNodeError from "../../utils/unexpected-node-error.js";
import { getPreferredQuote } from "../../common/util.js";
import getPreferredQuote from "../../utils/get-preferred-quote.js";
import {
isJsxElement,
rawText,
+2 -1
View File
@@ -1,4 +1,5 @@
import { printString, printNumber } from "../../common/util.js";
import printString from "../../utils/print-string.js";
import printNumber from "../../utils/print-number.js";
import { replaceEndOfLine } from "../../document/utils.js";
import { printDirective } from "./misc.js";
+2 -4
View File
@@ -1,8 +1,6 @@
import { printComments } from "../../main/comments/print.js";
import {
isNextLineEmpty as isNextLineEmptyAfterIndex,
getNextNonSpaceNonCommentCharacterIndex,
} from "../../common/util.js";
import isNextLineEmptyAfterIndex from "../../utils/is-next-line-empty.js";
import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js";
import pathNeedsParens from "../needs-parens.js";
import {
isCallExpression,
+1 -1
View File
@@ -1,4 +1,4 @@
import { isNonEmptyArray } from "../../common/util.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import UnexpectedNodeError from "../../utils/unexpected-node-error.js";
import {
softline,
+3 -5
View File
@@ -7,11 +7,9 @@ import {
ifBreak,
hardline,
} from "../../document/builders.js";
import {
hasNewlineInRange,
hasNewline,
isNonEmptyArray,
} from "../../common/util.js";
import hasNewlineInRange from "../../utils/has-newline-in-range.js";
import hasNewline from "../../utils/has-newline.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import {
shouldPrintComma,
hasComment,
+2 -1
View File
@@ -1,5 +1,6 @@
import { printComments } from "../../main/comments/print.js";
import { printString, printNumber } from "../../common/util.js";
import printString from "../../utils/print-string.js";
import printNumber from "../../utils/print-number.js";
import {
isNumericLiteral,
isSimpleNumber,
+2 -1
View File
@@ -1,4 +1,5 @@
import { getStringWidth, getIndentSize } from "../../common/util.js";
import getStringWidth from "../../utils/get-string-width.js";
import getIndentSize from "../../utils/get-indent-size.js";
import {
join,
hardline,
+1 -1
View File
@@ -1,4 +1,4 @@
import { hasNewlineInRange } from "../../common/util.js";
import hasNewlineInRange from "../../utils/has-newline-in-range.js";
import {
isJsxElement,
isCallExpression,
+1 -1
View File
@@ -18,7 +18,7 @@ import {
isObjectType,
getTypeScriptMappedTypeModifier,
} from "../utils/index.js";
import { createGroupIdMapper } from "../../common/util.js";
import createGroupIdMapper from "../../utils/create-group-id-mapper.js";
import {
printTypeAnnotationProperty,
shouldHugType,
+1 -1
View File
@@ -1,5 +1,5 @@
import { printDanglingComments } from "../../main/comments/print.js";
import { hasNewlineInRange } from "../../common/util.js";
import hasNewlineInRange from "../../utils/has-newline-in-range.js";
import {
join,
line,
+1 -1
View File
@@ -1,6 +1,6 @@
// TODO(azz): anything that imports from main shouldn't be in a `language-*` dir.
import { printDanglingComments } from "../main/comments/print.js";
import { hasNewline } from "../common/util.js";
import hasNewline from "../utils/has-newline.js";
import {
join,
line,
+4 -6
View File
@@ -1,10 +1,8 @@
import isEs5IdentifierName from "@prettier/is-es5-identifier-name";
import {
hasNewline,
isNonEmptyArray,
isNextLineEmpty as isNextLineEmptyAfterIndex,
getStringWidth,
} from "../../common/util.js";
import hasNewline from "../../utils/has-newline.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import isNextLineEmptyAfterIndex from "../../utils/is-next-line-empty.js";
import getStringWidth from "../../utils/get-string-width.js";
import { locStart, locEnd, hasSameLocStart } from "../loc.js";
import getVisitorKeys from "../traverse/get-visitor-keys.js";
import isBlockComment from "./is-block-comment.js";
+2 -2
View File
@@ -1,5 +1,5 @@
import collapseWhiteSpace from "collapse-white-space";
import { isFrontMatterNode } from "../common/util.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
import { startWithPragma } from "./pragma.js";
const ignoredProperties = new Set([
@@ -63,7 +63,7 @@ function clean(ast, newObj, parent) {
parent?.type === "root" &&
parent.children.length > 0 &&
(parent.children[0] === ast ||
(isFrontMatterNode(parent.children[0]) && parent.children[1] === ast)) &&
(isFrontMatter(parent.children[0]) && parent.children[1] === ast)) &&
ast.type === "html" &&
startWithPragma(ast.value)
) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { getMaxContinuousCount } from "../common/util.js";
import getMaxContinuousCount from "../utils/get-max-continuous-count.js";
import { hardline, markAsRoot } from "../document/builders.js";
import { replaceEndOfLine } from "../document/utils.js";
import printFrontMatter from "../utils/front-matter/print.js";
+3 -5
View File
@@ -1,9 +1,7 @@
import collapseWhiteSpace from "collapse-white-space";
import {
getMinNotPresentContinuousCount,
getMaxContinuousCount,
getStringWidth,
} from "../common/util.js";
import getMinNotPresentContinuousCount from "../utils/get-min-not-present-continuous-count.js";
import getMaxContinuousCount from "../utils/get-max-continuous-count.js";
import getStringWidth from "../utils/get-string-width.js";
import {
breakParent,
join,
+1 -1
View File
@@ -10,7 +10,7 @@ import {
lineSuffix,
} from "../document/builders.js";
import { replaceEndOfLine } from "../document/utils.js";
import { isPreviousLineEmpty } from "../common/util.js";
import isPreviousLineEmpty from "../utils/is-previous-line-empty.js";
import UnexpectedNodeError from "../utils/unexpected-node-error.js";
import { insertPragma, isPragma } from "./pragma.js";
import { locStart } from "./loc.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import { isNonEmptyArray } from "../common/util.js";
import isNonEmptyArray from "../utils/is-non-empty-array.js";
/**
* @param {any} value
+2 -1
View File
@@ -1,5 +1,6 @@
import assert from "node:assert";
import { hasNewline, isNonEmptyArray } from "../../common/util.js";
import hasNewline from "../../utils/has-newline.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
import createGetVisitorKeysFunction from "../create-get-visitor-keys-function.js";
import {
addLeadingComment,
+5 -7
View File
@@ -7,13 +7,11 @@ import {
join,
label,
} from "../../document/builders.js";
import {
hasNewline,
skipNewline,
skipSpaces,
isPreviousLineEmpty,
isNonEmptyArray,
} from "../../common/util.js";
import hasNewline from "../../utils/has-newline.js";
import skipNewline from "../../utils/skip-newline.js";
import { skipSpaces } from "../../utils/skip.js";
import isPreviousLineEmpty from "../../utils/is-previous-line-empty.js";
import isNonEmptyArray from "../../utils/is-non-empty-array.js";
/**
* @typedef {import("../../common/ast-path.js").default} AstPath
+1 -1
View File
@@ -2,7 +2,7 @@ import { diffArrays } from "diff";
import { printDocToString as printDocToStringWithoutNormalizeOptions } from "../document/printer.js";
import { printDocToDebug } from "../document/debug.js";
import { getAlignmentSize } from "../common/util.js";
import getAlignmentSize from "../utils/get-alignment-size.js";
import {
guessEndOfLine,
convertEndOfLineToChars,
+1 -1
View File
@@ -57,6 +57,6 @@ export {
getSupportInfo,
debugApis as __debug,
};
export * as util from "./common/util-shared.js";
export * as util from "./utils/public.js";
export * as doc from "./document/index.js";
export { default as version } from "./main/version.evaluate.cjs";
+15
View File
@@ -0,0 +1,15 @@
/**
* @param {string} description
* @returns {(node: any) => symbol}
*/
function createGroupIdMapper(description) {
const groupIds = new WeakMap();
return function (node) {
if (!groupIds.has(node)) {
groupIds.set(node, Symbol(description));
}
return groupIds.get(node);
};
}
export default createGroupIdMapper;
@@ -0,0 +1,5 @@
function isFrontMatter(node) {
return node?.type === "front-matter";
}
export default isFrontMatter;
+24
View File
@@ -0,0 +1,24 @@
/**
* @param {string} text
* @param {number} tabWidth
* @param {number=} startIndex
* @returns {number}
*/
function getAlignmentSize(text, tabWidth, startIndex = 0) {
let size = 0;
for (let i = startIndex; i < text.length; ++i) {
if (text[i] === "\t") {
// Tabs behave in a way that they are aligned to the nearest
// multiple of tabWidth:
// 0 -> 4, 1 -> 4, 2 -> 4, 3 -> 4
// 4 -> 8, 5 -> 8, 6 -> 8, 7 -> 8 ...
size = size + tabWidth - (size % tabWidth);
} else {
size++;
}
}
return size;
}
export default getAlignmentSize;
+22
View File
@@ -0,0 +1,22 @@
import getAlignmentSize from "./get-alignment-size.js";
/**
* @param {string} value
* @param {number} tabWidth
* @returns {number}
*/
function getIndentSize(value, tabWidth) {
const lastNewlineIndex = value.lastIndexOf("\n");
/* c8 ignore next 3 */
if (lastNewlineIndex === -1) {
return 0;
}
return getAlignmentSize(
// All the leading whitespaces
value.slice(lastNewlineIndex + 1).match(/^[\t ]*/)[0],
tabWidth
);
}
export default getIndentSize;
+24
View File
@@ -0,0 +1,24 @@
import escapeStringRegexp from "escape-string-regexp";
/**
* @param {string} text
* @param {string} searchString
* @returns {number}
*/
function getMaxContinuousCount(text, searchString) {
const results = text.match(
new RegExp(`(${escapeStringRegexp(searchString)})+`, "g")
);
if (results === null) {
return 0;
}
return results.reduce(
(maxCount, result) =>
Math.max(maxCount, result.length / searchString.length),
0
);
}
export default getMaxContinuousCount;
@@ -0,0 +1,37 @@
import escapeStringRegexp from "escape-string-regexp";
/**
* @param {string} text
* @param {string} searchString
* @returns {number}
*/
function getMinNotPresentContinuousCount(text, searchString) {
const matches = text.match(
new RegExp(`(${escapeStringRegexp(searchString)})+`, "g")
);
if (matches === null) {
return 0;
}
const countPresent = new Map();
let max = 0;
for (const match of matches) {
const count = match.length / searchString.length;
countPresent.set(count, true);
if (count > max) {
max = count;
}
}
for (let i = 1; i < max; i++) {
if (!countPresent.get(i)) {
return i;
}
}
return max + 1;
}
export default getMinNotPresentContinuousCount;
@@ -0,0 +1,13 @@
import getNextNonSpaceNonCommentCharacterIndex from "./get-next-non-space-non-comment-character-index.js";
/**
* @param {string} text
* @param {number} startIndex
* @returns {string}
*/
function getNextNonSpaceNonCommentCharacter(text, startIndex) {
const index = getNextNonSpaceNonCommentCharacterIndex(text, startIndex);
return index === false ? "" : text.charAt(index);
}
export default getNextNonSpaceNonCommentCharacter;
+39
View File
@@ -0,0 +1,39 @@
/**
* @typedef {'"' | "'"} Quote
*/
/**
*
* @param {string} rawContent
* @param {Quote} preferredQuote
* @returns {{ quote: Quote, regex: RegExp, escaped: string }}
*/
function getPreferredQuote(rawContent, preferredQuote) {
/** @type {{ quote: '"', regex: RegExp, escaped: "&quot;" }} */
const double = { quote: '"', regex: /"/g, escaped: "&quot;" };
/** @type {{ quote: "'", regex: RegExp, escaped: "&apos;" }} */
const single = { quote: "'", regex: /'/g, escaped: "&apos;" };
const preferred = preferredQuote === "'" ? single : double;
const alternate = preferred === single ? double : single;
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
// minimize the number of escaped quotes.
if (
rawContent.includes(preferred.quote) ||
rawContent.includes(alternate.quote)
) {
const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length;
const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length;
result = numPreferredQuotes > numAlternateQuotes ? alternate : preferred;
}
return result;
}
export default getPreferredQuote;
+16
View File
@@ -0,0 +1,16 @@
/**
* @param {string} text
* @param {number} startIndex
* @param {number} endIndex
* @returns {boolean}
*/
function hasNewlineInRange(text, startIndex, endIndex) {
for (let i = startIndex; i < endIndex; ++i) {
if (text.charAt(i) === "\n") {
return true;
}
}
return false;
}
export default hasNewlineInRange;
+22
View File
@@ -0,0 +1,22 @@
import { skipSpaces } from "./skip.js";
import skipNewline from "./skip-newline.js";
/** @typedef {import("./skip.js").SkipOptions} SkipOptions */
/**
* @param {string} text
* @param {number} startIndex
* @param {SkipOptions=} options
* @returns {boolean}
*/
function hasNewline(text, startIndex, options = {}) {
const idx = skipSpaces(
text,
options.backwards ? startIndex - 1 : startIndex,
options
);
const idx2 = skipNewline(text, idx, options);
return idx !== idx2;
}
export default hasNewline;
+23
View File
@@ -0,0 +1,23 @@
import { skipSpaces } from "./skip.js";
/** @typedef {import("./skip.js").SkipOptions} SkipOptions */
// Not using, but it's public utils
/* c8 ignore start */
/**
* @param {string} text
* @param {number} startIndex
* @param {SkipOptions=} options
* @returns {boolean}
*/
function hasSpaces(text, startIndex, options = {}) {
const idx = skipSpaces(
text,
options.backwards ? startIndex - 1 : startIndex,
options
);
return idx !== startIndex;
}
/* c8 ignore stop */
export default hasSpaces;
+29
View File
@@ -0,0 +1,29 @@
import skipNewline from "./skip-newline.js";
import { skipSpaces, skipToLineEnd } from "./skip.js";
import skipInlineComment from "./skip-inline-comment.js";
import skipTrailingComment from "./skip-trailing-comment.js";
import hasNewline from "./has-newline.js";
/**
* @param {string} text
* @param {number} startIndex
* @returns {boolean}
*/
function isNextLineEmpty(text, startIndex) {
/** @type {number | false} */
let oldIdx = null;
/** @type {number | false} */
let idx = startIndex;
while (idx !== oldIdx) {
// We need to skip all the potential trailing inline comments
oldIdx = idx;
idx = skipToLineEnd(text, idx);
idx = skipInlineComment(text, idx);
idx = skipSpaces(text, idx);
}
idx = skipTrailingComment(text, idx);
idx = skipNewline(text, idx);
return idx !== false && hasNewline(text, idx);
}
export default isNextLineEmpty;
+20
View File
@@ -0,0 +1,20 @@
import skipNewline from "./skip-newline.js";
import { skipSpaces } from "./skip.js";
// Note: this function doesn't ignore leading comments unlike isNextLineEmpty
/**
* @param {string} text
* @param {number} startIndex
* @returns {boolean}
*/
function isPreviousLineEmpty(text, startIndex) {
/** @type {number | false} */
let idx = startIndex - 1;
idx = skipSpaces(text, idx, { backwards: true });
idx = skipNewline(text, idx, { backwards: true });
idx = skipSpaces(text, idx, { backwards: true });
const idx2 = skipNewline(text, idx, { backwards: true });
return idx !== idx2;
}
export default isPreviousLineEmpty;
+47
View File
@@ -0,0 +1,47 @@
/** @typedef {import("./get-preferred-quote.js").Quote} Quote */
/**
* @param {string} rawText
* @param {Quote} enclosingQuote
* @param {boolean=} unescapeUnnecessaryEscapes
* @returns {string}
*/
function makeString(rawText, enclosingQuote, unescapeUnnecessaryEscapes) {
const otherQuote = enclosingQuote === '"' ? "'" : '"';
// Matches _any_ escape and unescaped quotes (both single and double).
const regex = /\\(.)|(["'])/gs;
// Escape and unescape single and double quotes as needed to be able to
// enclose `rawText` with `enclosingQuote`.
const raw = rawText.replaceAll(regex, (match, escaped, quote) => {
// If we matched an escape, and the escaped character is a quote of the
// other type than we intend to enclose the string with, there's no need for
// it to be escaped, so return it _without_ the backslash.
if (escaped === otherQuote) {
return escaped;
}
// If we matched an unescaped quote and it is of the _same_ type as we
// intend to enclose the string with, it must be escaped, so return it with
// a backslash.
if (quote === enclosingQuote) {
return "\\" + quote;
}
if (quote) {
return quote;
}
// Unescape any unnecessarily escaped character.
// Adapted from https://github.com/eslint/eslint/blob/de0b4ad7bd820ade41b1f606008bea68683dc11a/lib/rules/no-useless-escape.js#L27
return unescapeUnnecessaryEscapes &&
/^[^\n\r"'0-7\\bfnrt-vx\u2028\u2029]$/.test(escaped)
? escaped
: "\\" + escaped;
});
return enclosingQuote + raw + enclosingQuote;
}
export default makeString;
+18
View File
@@ -0,0 +1,18 @@
function printNumber(rawNumber) {
return (
rawNumber
.toLowerCase()
// Remove unnecessary plus and zeroes from scientific notation.
.replace(/^([+-]?[\d.]+e)(?:\+|(-))?0*(?=\d)/, "$1$2")
// Remove unnecessary scientific notation (1e0).
.replace(/^([+-]?[\d.]+)e[+-]?0+$/, "$1")
// Make sure numbers always start with a digit.
.replace(/^([+-])?\./, "$10.")
// Remove extraneous trailing decimal zeroes.
.replace(/(\.\d+?)0+(?=e|$)/, "$1")
// Remove trailing dot.
.replace(/\.(?=e|$)/, "")
);
}
export default printNumber;
+38
View File
@@ -0,0 +1,38 @@
import getPreferredQuote from "./get-preferred-quote.js";
import makeString from "./make-string.js";
/** @typedef {import("./get-preferred-quote.js").Quote} Quote */
function printString(raw, options) {
// `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} */
const enclosingQuote =
options.parser === "json" ||
(options.parser === "json5" &&
options.quoteProps === "preserve" &&
!options.singleQuote)
? '"'
: options.__isInHtmlAttribute
? "'"
: 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
// unnecessary escapes (such as in `"\'"`). Always using `makeString` makes
// sure that we consistently output the minimum amount of escaped quotes.
return makeString(
rawContent,
enclosingQuote,
!(
options.parser === "css" ||
options.parser === "less" ||
options.parser === "scss" ||
options.__embeddedInHtml
)
);
}
export default printString;
@@ -1,8 +1,6 @@
import {
getNextNonSpaceNonCommentCharacterIndex as getNextNonSpaceNonCommentCharacterIndexWithStartIndex,
isPreviousLineEmpty as isPreviousLineEmptyWithStartIndex,
isNextLineEmpty as isNextLineEmptyAfterIndex,
} from "./util.js";
import getNextNonSpaceNonCommentCharacterIndexWithStartIndex from "./get-next-non-space-non-comment-character-index.js";
import isPreviousLineEmptyWithStartIndex from "./is-previous-line-empty.js";
import isNextLineEmptyAfterIndex from "./is-next-line-empty.js";
// Legacy version of `getNextNonSpaceNonCommentCharacterIndex`
/**
@@ -84,29 +82,30 @@ export function isNextLineEmpty(text, startIndex) {
legacyIsNextLineEmpty(...arguments);
}
export { default as getMaxContinuousCount } from "./get-max-continuous-count.js";
export { default as getStringWidth } from "./get-string-width.js";
export { default as getAlignmentSize } from "./get-alignment-size.js";
export { default as getIndentSize } from "./get-indent-size.js";
export { default as skipNewline } from "./skip-newline.js";
export { default as skipInlineComment } from "./skip-inline-comment.js";
export { default as skipTrailingComment } from "./skip-trailing-comment.js";
export { default as hasNewline } from "./has-newline.js";
export { default as hasNewlineInRange } from "./has-newline-in-range.js";
export { default as hasSpaces } from "./has-spaces.js";
export { default as getNextNonSpaceNonCommentCharacter } from "./get-next-non-space-non-comment-character.js";
export { default as makeString } from "./make-string.js";
export {
getMaxContinuousCount,
getStringWidth,
getAlignmentSize,
getIndentSize,
skip,
skipWhitespace,
skipSpaces,
skipNewline,
skipToLineEnd,
skipEverythingButNewLine,
skipInlineComment,
skipTrailingComment,
hasNewline,
hasNewlineInRange,
hasSpaces,
getNextNonSpaceNonCommentCharacter,
makeString,
// Remove this in v4
isNextLineEmpty as isNextLineEmptyAfterIndex,
} from "./util.js";
} from "./skip.js";
export {
addLeadingComment,
addDanglingComment,
addTrailingComment,
} from "../main/comments/utils.js";
// TODO: Remove this in v4
export { isNextLineEmptyAfterIndex };
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.