Last active
July 2, 2025 00:30
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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), | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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