UNPKG

prettier-plugin-solidity

Version:

A Prettier Plugin for automatically formatting your Solidity code.

93 lines 4.2 kB
import { VersionExpressionSets as SlangVersionExpressionSets } from '@nomicfoundation/slang/ast'; import { NonterminalKind, Query } from '@nomicfoundation/slang/cst'; import { Parser } from '@nomicfoundation/slang/parser'; import { LanguageFacts } from '@nomicfoundation/slang/utils'; import { maxSatisfying, minSatisfying, minor, major, satisfies, validRange } from 'semver'; import { VersionExpressionSets } from '../slang-nodes/VersionExpressionSets.js'; const supportedVersions = LanguageFacts.allVersions(); const milestoneVersions = Array.from(supportedVersions.reduce((minorRanges, version) => { minorRanges.add(`^${major(version)}.${minor(version)}.0`); return minorRanges; }, new Set())) .reverse() .reduce((versions, range) => { versions.push(maxSatisfying(supportedVersions, range)); versions.push(minSatisfying(supportedVersions, range)); return versions; }, []); const queries = [ Query.create('[VersionPragma @versionRanges [VersionExpressionSets]]') ]; let parser; export function createParser(text, options) { const compiler = maxSatisfying(supportedVersions, options.compiler); if (compiler) { if (!parser || parser.languageVersion !== compiler) { parser = Parser.create(compiler); } return [parser, parser.parseNonterminal(NonterminalKind.SourceUnit, text)]; } let isCachedParser = false; if (parser) { isCachedParser = true; } else { parser = Parser.create(milestoneVersions[0]); } let parseOutput; let inferredRanges = []; try { parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); inferredRanges = tryToCollectPragmas(parseOutput, parser, isCachedParser); } catch (_a) { for (let i = isCachedParser ? 0 : 1; i <= milestoneVersions.length; i += 1) { try { const version = milestoneVersions[i]; parser = Parser.create(version); parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); inferredRanges = tryToCollectPragmas(parseOutput, parser); break; } catch (_b) { continue; } } } const satisfyingVersions = inferredRanges.reduce((versions, inferredRange) => { if (!validRange(inferredRange)) { throw new Error(`Couldn't infer any version from the ranges in the pragmas${options.filepath ? ` for file ${options.filepath}` : ''}`); } return versions.filter((supportedVersion) => satisfies(supportedVersion, inferredRange)); }, supportedVersions); const inferredVersion = satisfyingVersions.length > 0 ? satisfyingVersions[satisfyingVersions.length - 1] : supportedVersions[supportedVersions.length - 1]; if (inferredVersion !== parser.languageVersion) { parser = Parser.create(inferredVersion); parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); } // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion return [parser, parseOutput]; } function tryToCollectPragmas(parseOutput, parser, isCachedParser = false) { const matches = parseOutput.createTreeCursor().query(queries); const ranges = []; let match; while ((match = matches.next())) { const versionRange = new SlangVersionExpressionSets(match.captures.versionRanges[0].node.asNonterminalNode()); ranges.push( // Replace all comments that could be in the expression with whitespace new VersionExpressionSets(versionRange).comments.reduce((range, comment) => range.replace(comment.value, ' '), versionRange.cst.unparse())); } if (ranges.length === 0) { // If we didn't find pragmas but succeeded parsing the source we keep it. if (!isCachedParser && parseOutput.isValid()) { return [parser.languageVersion]; } // Otherwise we throw. throw new Error(`Using version ${parser.languageVersion} did not find any pragma statement and does not parse without errors.`); } return ranges; } //# sourceMappingURL=create-parser.js.map