prettier-plugin-solidity
Advanced tools
Comparing version 1.2.0 to 1.3.1
{ | ||
"name": "prettier-plugin-solidity", | ||
"version": "1.2.0", | ||
"version": "1.3.1", | ||
"description": "A Prettier Plugin for automatically formatting your Solidity code.", | ||
@@ -101,3 +101,3 @@ "type": "module", | ||
"jest": "^29.6.3", | ||
"jest-light-runner": "^0.5.0", | ||
"jest-light-runner": "^0.6.0", | ||
"jest-snapshot-serializer-ansi": "^2.1.0", | ||
@@ -107,5 +107,5 @@ "jest-snapshot-serializer-raw": "^2.0.0", | ||
"lines-and-columns": "^2.0.3", | ||
"prettier": "^3.0.2", | ||
"prettier": "^3.1.1", | ||
"proxyquire": "^2.1.3", | ||
"solc": "^0.8.21", | ||
"solc": "^0.8.23-fixed", | ||
"webpack": "^5.88.2", | ||
@@ -115,5 +115,5 @@ "webpack-cli": "^5.1.4" | ||
"dependencies": { | ||
"@solidity-parser/parser": "^0.16.2", | ||
"@solidity-parser/parser": "^0.17.0", | ||
"semver": "^7.5.4", | ||
"solidity-comments-extractor": "^0.0.7" | ||
"solidity-comments-extractor": "^0.0.8" | ||
}, | ||
@@ -120,0 +120,0 @@ "peerDependencies": { |
@@ -209,2 +209,17 @@ # prettier-plugin-solidity | ||
### Experimental Ternaries | ||
_Added in v1.3.0_ | ||
Mimicking prettier's [new ternary formatting](https://prettier.io/blog/2023/11/13/curious-ternaries) for the community to try. | ||
Valid options: | ||
- `true` - Use curious ternaries, with the question mark after the condition. | ||
- `false` - Retain the default behavior of ternaries; keep question marks on the same line as the consequent. | ||
| Default | CLI Override | API Override | | ||
| ------- | -------------------------- | ------------------------------- | | ||
| false | `--experimental-ternaries` | `experimentalTernaries: <bool>` | | ||
## Integrations | ||
@@ -211,0 +226,0 @@ |
@@ -8,3 +8,3 @@ import { doc } from 'prettier'; | ||
const indentIfNecessaryBuilder = (path) => (document) => { | ||
const indentIfNecessaryBuilder = (path, options) => (document) => { | ||
let node = path.getNode(); | ||
@@ -16,2 +16,8 @@ for (let i = 0; ; i += 1) { | ||
if (parentNode.type === 'WhileStatement') return document; | ||
if ( | ||
options.experimentalTernaries && | ||
parentNode.type === 'Conditional' && | ||
parentNode.condition === node | ||
) | ||
return document; | ||
if (parentNode.type !== 'BinaryOperation') return indent(document); | ||
@@ -25,5 +31,5 @@ if (node === parentNode.right) return document; | ||
match: (op) => ['&&', '||'].includes(op), | ||
print: (node, path, print) => { | ||
print: (node, path, print, options) => { | ||
const groupIfNecessary = groupIfNecessaryBuilder(path); | ||
const indentIfNecessary = indentIfNecessaryBuilder(path); | ||
const indentIfNecessary = indentIfNecessaryBuilder(path, options); | ||
@@ -30,0 +36,0 @@ const right = [node.operator, line, path.call(print, 'right')]; |
@@ -30,9 +30,6 @@ import { util } from 'prettier'; | ||
// The comment is behind the start of the Block `{}` or behind a base contract | ||
if ( | ||
(followingNode && followingNode.type === 'InheritanceSpecifier') || | ||
nextCharacter === '{' | ||
) { | ||
if (followingNode?.type === 'InheritanceSpecifier' || nextCharacter === '{') { | ||
// In this scenario the comment belongs to a base contract. | ||
// contract A is B, /* comment for B */ C /* comment for C */ {} | ||
if (precedingNode && precedingNode.type === 'InheritanceSpecifier') { | ||
if (precedingNode?.type === 'InheritanceSpecifier') { | ||
addTrailingComment(precedingNode, comment); | ||
@@ -39,0 +36,0 @@ return true; |
@@ -30,6 +30,2 @@ import { util } from 'prettier'; | ||
export function isFirst(path, _, index) { | ||
return isPrettier2 ? index === 0 : path.isFirst; | ||
} | ||
export function isLast(path, key, index) { | ||
@@ -40,9 +36,1 @@ return isPrettier2 | ||
} | ||
export function previous(path, key, index) { | ||
return isPrettier2 ? path.getParentNode()[key][index - 1] : path.previous; | ||
} | ||
export function next(path, key, index) { | ||
return isPrettier2 ? path.getParentNode()[key][index + 1] : path.next; | ||
} |
import { doc } from 'prettier'; | ||
import { | ||
isFirst, | ||
isLast, | ||
isNextLineEmpty, | ||
isPrettier2, | ||
next, | ||
previous | ||
isPrettier2 | ||
} from './backward-compatibility.js'; | ||
@@ -27,3 +24,3 @@ | ||
comment.printed = true; | ||
return options.printer.printComment(commentPath); | ||
return options.printer.printComment(commentPath, options); | ||
}, 'comments') | ||
@@ -43,18 +40,2 @@ .filter(Boolean) | ||
const shouldHaveEmptyLine = (node, checkForLeading) => | ||
Boolean( | ||
// if node is not FunctionDefinition, it should have an empty line | ||
node.type !== 'FunctionDefinition' || | ||
// if FunctionDefinition is not abstract, it should have an empty line | ||
node.body || | ||
// if FunctionDefinition has the comment we are looking for (trailing or | ||
// leading), it should have an empty line | ||
node.comments?.some((comment) => checkForLeading && comment.leading) | ||
); | ||
const separatingLine = (firstNode, secondNode) => | ||
shouldHaveEmptyLine(firstNode, false) || shouldHaveEmptyLine(secondNode, true) | ||
? hardline | ||
: ''; | ||
export function printPreservingEmptyLines(path, key, options, print) { | ||
@@ -76,34 +57,12 @@ const parts = []; | ||
// Only attempt to prepend an empty line if `node` is not the first item | ||
// and an empty line hasn't already been appended after the previous `node` | ||
if ( | ||
!isFirst(childPath, key, index) && | ||
parts[parts.length - 2] !== hardline | ||
) { | ||
if (nodeType === 'FunctionDefinition') { | ||
// Prepend FunctionDefinition with an empty line if there should be a | ||
// separation with the previous `node` | ||
parts.push(separatingLine(previous(childPath, key, index), node)); | ||
} else if (nodeType === 'ContractDefinition') { | ||
// Prepend ContractDefinition with an empty line | ||
parts.push(hardline); | ||
} | ||
} | ||
parts.push(print(childPath)); | ||
// Only attempt to append an empty line if `node` is not the last item | ||
if (!isLast(childPath, key, index)) { | ||
if (isNextLineEmpty(options.originalText, options.locEnd(node) + 1)) { | ||
// Append an empty line if the original text already had an one after | ||
// the current `node` | ||
parts.push(hardline); | ||
} else if (nodeType === 'FunctionDefinition') { | ||
// Append FunctionDefinition with an empty line if there should be a | ||
// separation with the next `node` | ||
parts.push(separatingLine(node, next(childPath, key, index))); | ||
} else if (nodeType === 'ContractDefinition') { | ||
// Append ContractDefinition with an empty line | ||
parts.push(hardline); | ||
} | ||
if ( | ||
!isLast(childPath, key, index) && | ||
isNextLineEmpty(options.originalText, options.locEnd(node) + 1) | ||
) { | ||
// Append an empty line if the original text already had an one after | ||
// the current `node` | ||
parts.push(hardline); | ||
} | ||
@@ -110,0 +69,0 @@ }, key); |
@@ -43,7 +43,5 @@ import { util, version } from 'prettier'; | ||
return ( | ||
node && | ||
node.comments && | ||
node.comments.length > 0 && | ||
node?.comments?.length > 0 && | ||
node.comments.some((comment) => comment.value.trim() === 'prettier-ignore') | ||
); | ||
} |
@@ -7,3 +7,3 @@ // see: https://github.com/prettier/prettier/blob/main/src/language-js/loc.js | ||
} | ||
if (node.expression && node.expression.range) { | ||
if (node.expression?.range) { | ||
return node.expression.range[index]; | ||
@@ -10,0 +10,0 @@ } |
import { doc } from 'prettier'; | ||
import { printSeparatedItem } from '../common/printer-helpers.js'; | ||
const { group, indent, line } = doc.builders; | ||
const { group, hardline, ifBreak, indent, line, softline } = doc.builders; | ||
let groupIndex = 0; | ||
const experimentalTernaries = (node, path, print, options) => { | ||
const parent = path.getParentNode(); | ||
const isNested = parent.type === 'Conditional'; | ||
const isNestedAsTrueExpression = isNested && parent.trueExpression === node; | ||
const falseExpressionIsNested = node.falseExpression.type === 'Conditional'; | ||
// If the `condition` breaks into multiple lines, we add parentheses, | ||
// unless it already is a `TupleExpression`. | ||
const condition = path.call(print, 'condition'); | ||
const conditionDoc = group([ | ||
node.condition.type === 'TupleExpression' | ||
? condition | ||
: ifBreak(['(', printSeparatedItem(condition), ')'], condition), | ||
' ?' | ||
]); | ||
// To switch between "case-style" and "curious" ternaries we force a new line | ||
// before a nested `trueExpression` if the current `Conditional` is also a | ||
// `trueExpression`. | ||
const trueExpressionDoc = indent([ | ||
isNestedAsTrueExpression ? hardline : line, | ||
path.call(print, 'trueExpression') | ||
]); | ||
const conditionAndTrueExpressionGroup = group( | ||
[conditionDoc, trueExpressionDoc], | ||
{ id: `Conditional.trueExpressionDoc-${groupIndex}` } | ||
); | ||
groupIndex += 1; | ||
// For the odd case of `tabWidth` of 1 or 0 we initiate `fillTab` as a single | ||
// space. | ||
let fillTab = ' '; | ||
if ( | ||
!falseExpressionIsNested && // avoid processing if it's not needed | ||
(options.tabWidth > 2 || options.useTabs) | ||
) { | ||
fillTab = options.useTabs ? '\t' : ' '.repeat(options.tabWidth - 1); | ||
} | ||
// A nested `falseExpression` is always printed in a new line. | ||
const falseExpression = path.call(print, 'falseExpression'); | ||
const falseExpressionDoc = [ | ||
isNested ? hardline : line, | ||
':', | ||
falseExpressionIsNested | ||
? [' ', falseExpression] | ||
: ifBreak([fillTab, indent(falseExpression)], [' ', falseExpression], { | ||
// We only add `fillTab` if we are sure the trueExpression is indented | ||
groupId: conditionAndTrueExpressionGroup.id | ||
}) | ||
]; | ||
const document = group([conditionAndTrueExpressionGroup, falseExpressionDoc]); | ||
return parent.type === 'VariableDeclarationStatement' | ||
? indent([softline, document]) | ||
: document; | ||
}; | ||
const traditionalTernaries = (path, print) => | ||
group([ | ||
path.call(print, 'condition'), | ||
indent([ | ||
// Nested trueExpression and falseExpression are always printed in a new | ||
// line | ||
path.getParentNode().type === 'Conditional' ? hardline : line, | ||
'? ', | ||
path.call(print, 'trueExpression'), | ||
line, | ||
': ', | ||
path.call(print, 'falseExpression') | ||
]) | ||
]); | ||
export const Conditional = { | ||
print: ({ path, print }) => | ||
group([ | ||
path.call(print, 'condition'), | ||
indent([ | ||
line, | ||
'? ', | ||
path.call(print, 'trueExpression'), | ||
line, | ||
': ', | ||
path.call(print, 'falseExpression') | ||
]) | ||
]) | ||
print: ({ node, path, print, options }) => | ||
options.experimentalTernaries | ||
? experimentalTernaries(node, path, print, options) | ||
: traditionalTernaries(path, print) | ||
}; |
@@ -23,3 +23,3 @@ import { doc } from 'prettier'; | ||
const comments = printComments(node, path, options); | ||
return node.subNodes.length > 0 || (comments && comments.length) | ||
return node.subNodes.length > 0 || comments?.length | ||
? printSeparatedItem( | ||
@@ -26,0 +26,0 @@ [printPreservingEmptyLines(path, 'subNodes', options, print), comments], |
import { printSeparatedList } from '../common/printer-helpers.js'; | ||
const parameters = (node, path, print) => | ||
node.parameters && node.parameters.length > 0 | ||
node.parameters?.length > 0 | ||
? printSeparatedList(path.map(print, 'parameters')) | ||
@@ -6,0 +6,0 @@ : ''; |
const stateMutability = (node) => | ||
node.stateMutability && node.stateMutability.length > 0 | ||
? [' ', node.stateMutability] | ||
: ''; | ||
node.stateMutability?.length > 0 ? [' ', node.stateMutability] : ''; | ||
@@ -6,0 +4,0 @@ export const ElementaryTypeName = { |
import { printSeparatedList } from '../common/printer-helpers.js'; | ||
const parameters = (node, path, print) => | ||
node.parameters && node.parameters.length > 0 | ||
node.parameters?.length > 0 | ||
? printSeparatedList(path.map(print, 'parameters')) | ||
@@ -6,0 +6,0 @@ : ''; |
@@ -13,5 +13,5 @@ import { doc } from 'prettier'; | ||
if (parent.type === 'IfStatement') { | ||
if (node.comments && node.comments.length) { | ||
if (node.comments?.length) { | ||
const comments = printComments(node, path, options); | ||
if (comments && comments.length) { | ||
if (comments?.length) { | ||
parts.push(comments); | ||
@@ -18,0 +18,0 @@ parts.push(hardline); |
@@ -33,4 +33,4 @@ import { doc } from 'prettier'; | ||
if (node.arguments && node.arguments.length > 0) { | ||
if (node.identifiers && node.identifiers.length > 0) { | ||
if (node.arguments?.length > 0) { | ||
if (node.identifiers?.length > 0) { | ||
argumentsDoc = printObject(path, print, options); | ||
@@ -37,0 +37,0 @@ } else { |
@@ -29,3 +29,3 @@ import { doc } from 'prettier'; | ||
const parameters = (parametersType, node, path, print, options) => { | ||
if (node[parametersType] && node[parametersType].length > 0) { | ||
if (node[parametersType]?.length > 0) { | ||
return printSeparatedList(path.map(print, parametersType), { | ||
@@ -35,3 +35,3 @@ grouped: false | ||
} | ||
if (node.comments && node.comments.length > 0) { | ||
if (node.comments?.length > 0) { | ||
// we add a check to see if the comment is inside the parentheses | ||
@@ -38,0 +38,0 @@ const parameterComments = printComments( |
import { printSeparatedList } from '../common/printer-helpers.js'; | ||
const printArguments = (node, path, print) => | ||
node.arguments && node.arguments.length | ||
node.arguments?.length | ||
? ['(', printSeparatedList(path.map(print, 'arguments')), ')'] | ||
@@ -6,0 +6,0 @@ : ''; |
@@ -9,3 +9,3 @@ // @TODO: add support for assembly language specifier | ||
node.language ? `${printString(node.language, options)} ` : '', | ||
node.flags && node.flags.length > 0 | ||
node.flags?.length > 0 | ||
? [ | ||
@@ -12,0 +12,0 @@ '(', |
@@ -7,3 +7,3 @@ import { doc } from 'prettier'; | ||
const modifierParameters = (node, path, print) => { | ||
if (node.parameters && node.parameters.length > 0) { | ||
if (node.parameters?.length > 0) { | ||
return [ | ||
@@ -10,0 +10,0 @@ '(', |
@@ -16,4 +16,3 @@ import { | ||
if ( | ||
node.comments && | ||
node.comments.some( | ||
node.comments?.some( | ||
(comment) => !comment.leading && !comment.trailing && !comment.printed | ||
@@ -20,0 +19,0 @@ ) |
@@ -5,5 +5,6 @@ import { doc } from 'prettier'; | ||
const expression = (node, path, print) => { | ||
const expression = (node, path, print, options) => { | ||
if (node.expression) { | ||
return node.expression.type === 'TupleExpression' | ||
return node.expression.type === 'TupleExpression' || | ||
(options.experimentalTernaries && node.expression.type === 'Conditional') | ||
? [' ', path.call(print, 'expression')] | ||
@@ -16,7 +17,7 @@ : group(indent([line, path.call(print, 'expression')])); | ||
export const ReturnStatement = { | ||
print: ({ node, path, print }) => [ | ||
print: ({ node, path, print, options }) => [ | ||
'return', | ||
expression(node, path, print), | ||
expression(node, path, print, options), | ||
';' | ||
] | ||
}; |
@@ -7,7 +7,5 @@ import { doc } from 'prettier'; | ||
const contents = (node, path, print) => | ||
node.components && | ||
node.components.length === 1 && | ||
node.components[0].type === 'BinaryOperation' | ||
node.components?.length === 1 && node.components[0].type === 'BinaryOperation' | ||
? path.map(print, 'components') | ||
: [printSeparatedList(path.map(print, 'components'))]; | ||
: printSeparatedList(path.map(print, 'components')); | ||
@@ -18,5 +16,5 @@ export const TupleExpression = { | ||
node.isArray ? '[' : '(', | ||
...contents(node, path, print), | ||
contents(node, path, print), | ||
node.isArray ? ']' : ')' | ||
]) | ||
}; |
@@ -9,3 +9,3 @@ import { doc } from 'prettier'; | ||
'using ', | ||
node.functions && node.functions.length | ||
node.functions?.length | ||
? [ | ||
@@ -12,0 +12,0 @@ '{', |
@@ -16,3 +16,3 @@ import { doc } from 'prettier'; | ||
const startsWithVar = | ||
node.variables.filter((x) => x && x.typeName).length === 0; | ||
node.variables.filter((x) => x?.typeName).length === 0; | ||
@@ -19,0 +19,0 @@ const declarationDoc = group( |
const CATEGORY_GLOBAL = 'Global'; | ||
const CATEGORY_COMMON = 'Common'; | ||
const CATEGORY_JAVASCRIPT = 'JavaScript'; | ||
const CATEGORY_SOLIDITY = 'Solidity'; | ||
@@ -43,2 +44,11 @@ | ||
}, | ||
experimentalTernaries: { | ||
category: CATEGORY_JAVASCRIPT, | ||
type: 'boolean', | ||
default: false, | ||
description: | ||
'Use curious ternaries, with the question mark after the condition.', | ||
oppositeDescription: | ||
'Default behavior of ternaries; keep question marks on the same line as the consequent.' | ||
}, | ||
compiler: { | ||
@@ -45,0 +55,0 @@ category: CATEGORY_SOLIDITY, |
@@ -46,3 +46,3 @@ import extractComments from 'solidity-comments-extractor'; | ||
ctx.modifiers.forEach((modifier) => { | ||
if (modifier.arguments && modifier.arguments.length === 0) { | ||
if (modifier.arguments?.length === 0) { | ||
// eslint-disable-next-line no-param-reassign | ||
@@ -61,6 +61,21 @@ modifier.arguments = null; | ||
HexLiteral(ctx) { | ||
ctx.value = options.singleQuote | ||
? `hex'${ctx.value.slice(4, -1)}'` | ||
: `hex"${ctx.value.slice(4, -1)}"`; | ||
const value = ctx.value.slice(4, -1); | ||
ctx.value = options.singleQuote ? `hex'${value}'` : `hex"${value}"`; | ||
}, | ||
Conditional(ctx) { | ||
// TODO: while the behaviour is not stable, it should be behind the | ||
// experimentalTernaries flag. | ||
if (options.experimentalTernaries) { | ||
// We can remove parentheses only because we are sure that the | ||
// `condition` must be a single `bool` value. | ||
while ( | ||
ctx.condition.type === 'TupleExpression' && | ||
!ctx.condition.isArray && | ||
ctx.condition.components.length === 1 && | ||
ctx.condition.components[0].type !== 'Conditional' | ||
) { | ||
[ctx.condition] = ctx.condition.components; | ||
} | ||
} | ||
}, | ||
BinaryOperation(ctx) { | ||
@@ -83,30 +98,30 @@ switch (ctx.operator) { | ||
case '**': | ||
// If the compiler has not been given as an option using we leave a**b**c. | ||
// If the compiler has not been given as an option using we leave | ||
// a**b**c. | ||
if (!compiler) break; | ||
if (satisfies(compiler, '<0.8.0')) { | ||
// If the compiler is less than 0.8.0 then a**b**c is formatted as | ||
// (a**b)**c. | ||
ctx.left = tryHug(ctx.left, ['**']); | ||
if (satisfies(compiler, '>=0.8.0')) { | ||
// If the compiler is greater than or equal to 0.8.0 then a**b**c | ||
// is formatted as a**(b**c). | ||
ctx.right = tryHug(ctx.right, ['**']); | ||
break; | ||
} | ||
if ( | ||
ctx.left.type === 'BinaryOperation' && | ||
ctx.left.operator === '**' | ||
ctx.right.type === 'BinaryOperation' && | ||
ctx.right.operator === '**' | ||
) { | ||
// the parser still organizes the a**b**c as (a**b)**c so we need | ||
// to restructure it. | ||
ctx.right = { | ||
// the parser organizes the a**b**c as a**(b**c) so we need to | ||
// restructure it. | ||
const left = { | ||
type: 'BinaryOperation', | ||
operator: '**', | ||
left: ctx.left, | ||
right: ctx.right.left | ||
}; | ||
ctx.left = { | ||
type: 'TupleExpression', | ||
components: [ | ||
{ | ||
type: 'BinaryOperation', | ||
operator: '**', | ||
left: ctx.left.right, | ||
right: ctx.right | ||
} | ||
], | ||
components: [left], | ||
isArray: false | ||
}; | ||
ctx.left = ctx.left.left; | ||
ctx.right = ctx.right.right; | ||
} | ||
@@ -113,0 +128,0 @@ break; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
408
3
0
2017928
103
4771
+ Added@solidity-parser/parser@0.17.0(transitive)
+ Addedsolidity-comments-extractor@0.0.8(transitive)
- Removed@solidity-parser/parser@0.16.2(transitive)
- Removedantlr4ts@0.5.0-dev(transitive)
- Removedbuffer-from@1.1.2(transitive)
- Removedsolidity-comments-extractor@0.0.7(transitive)
- Removedsource-map@0.6.1(transitive)
- Removedsource-map-support@0.5.21(transitive)