Skip to content

Instantly share code, notes, and snippets.

@antoniopresto
Last active July 2, 2025 00:30
Show Gist options
  • Save antoniopresto/2fce7711c76df230810730f04ef81cb8 to your computer and use it in GitHub Desktop.
Save antoniopresto/2fce7711c76df230810730f04ef81cb8 to your computer and use it in GitHub Desktop.
prettier-plugin-array-line-break.js - For non-empty arrays, force them to break into multiple lines
/**
* @prettier
*/
import { doc } from 'prettier';
import { printers as estreePrinters } from 'prettier/plugins/estree';
const originalEstreePrinter = estreePrinters.estree;
const { group, indent, line, join, ifBreak } = doc.builders;
const DEFAULT_THRESHOLD = 1;
/**
* Creates a wrapped printer that applies custom formatting rules.
* Prettier uses the estree printer for JS, TS, and JSON by default.
* @param {object} originalPrinter - The original Prettier printer to wrap.
* @returns {object} The new printer with the overridden print function.
*/
function createWrappedPrinter(originalPrinter) {
return {
...originalPrinter,
print(path, options = {}, print) {
const node = path.getValue();
// Determine the threshold for breaking lines from options, with fallbacks.
const multilineWrapThreshold = asInt(
options.multilineWrapThreshold,
DEFAULT_THRESHOLD,
);
const objectWrapThreshold = asInt(
options.objectWrapThreshold,
multilineWrapThreshold,
);
const arrayWrapThreshold = asInt(
options.arrayWrapThreshold,
objectWrapThreshold,
);
// Determine if a trailing comma should be added.
const trailingComma =
options.trailingComma === 'all' || options.trailingComma === 'es5'
? ifBreak(',', '')
: '';
// Handle ArrayExpression nodes.
// If the number of elements exceeds the threshold, break it into multiple lines.
if (
node?.type === 'ArrayExpression' &&
node.elements.length > arrayWrapThreshold
) {
return group(
[
'[',
indent([
line,
join(
[
',',
line,
],
path.map(print, 'elements'),
),
trailingComma,
]),
line,
']',
],
{
shouldBreak: true,
},
);
}
// Handle ObjectExpression nodes.
// If the number of properties exceeds the threshold, break it into multiple lines.
if (
node?.type === 'ObjectExpression' &&
node.properties.length > objectWrapThreshold
) {
const objectDoc = group(
[
'{',
indent([
line,
join(
[
',',
line,
],
path.map(print, 'properties'),
),
trailingComma,
]),
line,
'}',
],
{
shouldBreak: true,
},
);
// Special handling for objects returned by arrow functions,
// which need to be wrapped in parentheses, e.g., `() => ({ a:1, b:2 })`.
const parentNode = path.getParentNode();
if (
parentNode?.type === 'ArrowFunctionExpression' &&
parentNode.body === node
) {
return [
'(',
objectDoc,
')',
];
}
return objectDoc;
}
// Handle TSTypeLiteral nodes (for TypeScript type definitions).
// If the number of members exceeds the threshold, break it into multiple lines.
if (
node?.type === 'TSTypeLiteral' &&
node.members.length > objectWrapThreshold
) {
return group(
[
'{',
indent([
line,
join(
[
',',
line,
],
path.map(print, 'members'),
),
trailingComma,
]),
line,
'}',
],
{
shouldBreak: true,
},
);
}
// For all other nodes, use the original printer.
return originalPrinter.print(path, options, print);
},
};
}
/**
* Helper function to parse an integer from a value, with a default fallback.
* @param {*} value - The value to parse.
* @param {number} defaultValue - The value to return if parsing fails.
* @returns {number} The parsed integer or the default value.
*/
function asInt(value, defaultValue) {
const num = parseInt(value, 10);
if (!isNaN(num) && num >= 0) {
return num;
}
return defaultValue;
}
export const printers = {
estree: createWrappedPrinter(originalEstreePrinter),
};
import * as arrayPlugin from './plugins/prettier-plugin-array-line-break.js';
/** @type {import("prettier").Config} */
const config = {
semi: true,
singleQuote: true,
printWidth: 80,
trailingComma: 'all',
tabWidth: 2,
plugins: [
arrayPlugin,
'prettier-plugin-organize-imports',
],
};
export default config;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment