prettierx
Version:
prettierX - a less opinionated fork of the Prettier code formatter
234 lines (200 loc) • 6.68 kB
JavaScript
;
// [prettierx] support internal error message below
const { outdent } = require("outdent");
const { printComments } = require("../../main/comments");
const { printString, printNumber } = require("../../common/util");
const {
isNumericLiteral,
isSimpleNumber,
isStringLiteral,
isStringPropSafeToUnquote,
rawText,
} = require("../utils");
const { printAssignment } = require("./assignment");
const needsQuoteProps = new WeakMap();
function printPropertyKey(path, options, print) {
const node = path.getNode();
if (node.computed) {
// [prettierx] --computed-property-spacing option support (...)
const computedPropertySpace = options.computedPropertySpacing ? " " : "";
// [prettierx] --computed-property-spacing option support (...)
return [
"[",
computedPropertySpace,
print("key"),
computedPropertySpace,
"]",
];
}
const parent = path.getParentNode();
const { key } = node;
// flow has `Identifier` key, other parsers use `PrivateIdentifier` (ESTree) or `PrivateName`
if (node.type === "ClassPrivateProperty" && key.type === "Identifier") {
return ["#", print("key")];
}
if (options.quoteProps === "consistent" && !needsQuoteProps.has(parent)) {
const objectHasStringProp = (
parent.properties ||
parent.body ||
parent.members
).some(
(prop) =>
!prop.computed &&
prop.key &&
isStringLiteral(prop.key) &&
!isStringPropSafeToUnquote(prop, options)
);
needsQuoteProps.set(parent, objectHasStringProp);
}
if (
(key.type === "Identifier" ||
(isNumericLiteral(key) &&
isSimpleNumber(printNumber(rawText(key))) &&
// Avoid converting 999999999999999999999 to 1e+21, 0.99999999999999999 to 1 and 1.0 to 1.
String(key.value) === printNumber(rawText(key)) &&
// [prettierx] support __typescript_estree parser option for testing
// Quoting number keys is safe in JS and Flow, but not in TypeScript (as
// mentioned in `isStringPropSafeToUnquote`).
!(
options.parser === "typescript" ||
options.parser === "babel-ts" ||
options.parser === "__typescript_estree"
))) &&
(options.parser === "json" ||
(options.quoteProps === "consistent" && needsQuoteProps.get(parent)))
) {
// a -> "a"
// 1 -> "1"
// 1.5 -> "1.5"
const prop = printString(
JSON.stringify(
key.type === "Identifier" ? key.name : key.value.toString()
),
options
);
return path.call((keyPath) => printComments(keyPath, prop, options), "key");
}
if (
isStringPropSafeToUnquote(node, options) &&
(options.quoteProps === "as-needed" ||
(options.quoteProps === "consistent" && !needsQuoteProps.get(parent)))
) {
// 'a' -> a
// '1' -> 1
// '1.5' -> 1.5
return path.call(
(keyPath) =>
printComments(
keyPath,
/^\d/.test(key.value) ? printNumber(key.value) : key.value,
options
),
"key"
);
}
return print("key");
}
// [prettierx] support --align-object-properties & --computed-property-spacing
function getPropertyPadding(options, path) {
if (!options.alignObjectProperties) {
return "";
}
if (/json/.test(options.parser)) {
return "";
}
const node = path.getValue();
const { type } = node;
// grandparent node:
const parentObject = path.getParentNode(1);
const { locStart, locEnd } = options;
// THIS IS A HACK:
const shouldBreak = options.originalText
.slice(locStart(parentObject), locEnd(parentObject))
.match(/{\s*(\/.*)?\n/);
if (!shouldBreak) {
return "";
}
// *should* always resolve to a (whole) number
// (unless it throws, which is NOT EXPECTED)
const nameLength =
type === "Identifier"
? node.name.length
: node.raw
? node.raw.length
: node.extra.raw
? node.extra.raw.length
: (() => {
// STOP HERE with an internal error
// (alternative is to simply assume zero-length)
throw new Error(outdent`
INTERNAL ERROR NOT EXPECTED: could not find length for a node
issue with reproduction would be appreciated in:
https://github.com/brodybits/prettierx/issues
`);
})();
// [prettierx] --computed-property-spacing option support
const computedPropertyOverhead = options.computedPropertySpacing ? 4 : 2;
const { properties } = parentObject;
const lengths = properties.map((property) => {
if (!property.key) {
return 0;
}
return (
property.key.loc.end.column -
property.key.loc.start.column +
(property.computed ? computedPropertyOverhead : 0)
);
});
const maxLength = Math.max.apply(null, lengths);
const padLength = maxLength - nameLength + 1;
const padding = " ".repeat(padLength);
return padding;
}
function printProperty(path, options, print) {
const node = path.getValue();
if (node.shorthand) {
return print("value");
}
// [prettierx] for --align-object-properties option
const propertyPadding = path.call(
getPropertyPadding.bind(null, options),
"key"
);
// [prettierx] FUTURE TBD: it should be possible to refactor the code
// to use the same printPropertyKey call for both
// computed & non-computed properties
// [prettierx] --computed-property-spacing option support
const computedPropertySpace = options.computedPropertySpacing ? " " : "";
// [prettierx] calculate this overhead in case it is needed,
// with --computed-property-spacing option support:
const computedPropertyOverhead = options.computedPropertySpacing ? 4 : 2;
// [prettierx] compose left part, for --align-object-properties option
const propertyLeftPart = node.computed
? [
// [prettierx] computed property key,
// with padding as needed for alignment
"[",
// [prettierx] --computed-property-spacing option support (...)
computedPropertySpace,
print("key"),
// [prettierx] --computed-property-spacing option support (...)
computedPropertySpace,
"]",
propertyPadding.slice(computedPropertyOverhead),
]
: [
// [prettierx] normal property key, for --align-object-properties option
printPropertyKey(path, options, print),
propertyPadding,
];
return printAssignment(
path,
options,
print,
// [prettierx] optional property alignment for --align-object-properties
propertyLeftPart,
":",
"value"
);
}
module.exports = { printProperty, printPropertyKey };