From 3db5f9d3ff6b1d7d386f12f33fa3fcb0ee586156 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 23 Dec 2023 19:14:48 -0300 Subject: [PATCH 1/2] Migration to typescript Step 1 --- .c8rc | 9 +- .eslintignore | 5 +- .eslintrc | 37 +- .prettierignore | 2 +- jest.config.js | 5 +- package-lock.json | 499 +++++++++++++++- package.json | 40 +- src/binary-operator-printers/types.d.ts | 9 + src/{clean.js => clean.ts} | 2 +- src/comments/handler.js | 31 +- ...js => handleContractDefinitionComments.ts} | 15 +- ...js => handleModifierInvocationComments.ts} | 15 +- src/comments/handlers/{index.js => index.ts} | 1 - src/comments/handlers/types.d.ts | 9 + src/comments/index.js | 2 +- src/comments/{printer.js => printer.ts} | 17 +- src/common/backward-compatibility.js | 4 +- ...{printer-helpers.js => printer-helpers.ts} | 61 +- src/common/types.d.ts | 28 + src/common/{util.js => util.ts} | 51 +- src/index.js | 49 +- src/loc.js | 16 - .../{AssemblyBlock.js => AssemblyBlock.ts} | 6 +- .../{AssemblyCall.js => AssemblyCall.ts} | 7 +- src/nodes/{AssemblyFor.js => AssemblyFor.ts} | 4 +- src/nodes/AssemblyFunctionDefinition.js | 6 +- src/nodes/AssemblyLocalDefinition.js | 20 +- src/nodes/AssemblyStackAssignment.js | 3 +- .../{AssemblySwitch.js => AssemblySwitch.ts} | 4 +- src/nodes/{Block.js => Block.ts} | 4 +- src/nodes/{CatchClause.js => CatchClause.ts} | 11 +- src/nodes/{Conditional.js => Conditional.ts} | 19 +- ...actDefinition.js => ContractDefinition.ts} | 24 +- src/nodes/CustomErrorDefinition.js | 6 +- ...oWhileStatement.js => DoWhileStatement.ts} | 11 +- src/nodes/ElementaryTypeName.js | 2 +- .../{EnumDefinition.js => EnumDefinition.ts} | 4 +- src/nodes/EventDefinition.js | 10 +- src/nodes/ExpressionStatement.js | 28 +- src/nodes/ForStatement.js | 2 +- .../{FunctionCall.js => FunctionCall.ts} | 26 +- src/nodes/FunctionDefinition.js | 117 ---- src/nodes/FunctionDefinition.ts | 139 +++++ ...unctionTypeName.js => FunctionTypeName.ts} | 19 +- src/nodes/HexLiteral.js | 2 +- src/nodes/IfStatement.js | 2 +- ...{ImportDirective.js => ImportDirective.ts} | 16 +- src/nodes/{IndexAccess.js => IndexAccess.ts} | 16 +- src/nodes/InheritanceSpecifier.js | 2 +- ...tatement.js => InlineAssemblyStatement.ts} | 9 +- src/nodes/LabelDefinition.js | 2 +- src/nodes/{Mapping.js => Mapping.ts} | 13 +- .../{MemberAccess.js => MemberAccess.ts} | 23 +- src/nodes/ModifierDefinition.js | 25 +- ...ierInvocation.js => ModifierInvocation.ts} | 21 +- .../{NameValueList.js => NameValueList.ts} | 8 +- src/nodes/NumberLiteral.js | 2 +- src/nodes/PragmaDirective.js | 2 +- ...{ReturnStatement.js => ReturnStatement.ts} | 12 +- src/nodes/{SourceUnit.js => SourceUnit.ts} | 4 +- src/nodes/StateVariableDeclaration.js | 2 +- .../{StringLiteral.js => StringLiteral.ts} | 6 +- ...tructDefinition.js => StructDefinition.ts} | 11 +- src/nodes/TryStatement.js | 35 +- src/nodes/TupleExpression.js | 16 +- src/nodes/TypeDefinition.js | 2 +- .../{UnaryOperation.js => UnaryOperation.ts} | 5 +- src/nodes/UncheckedStatement.js | 6 +- src/nodes/UsingForDeclaration.js | 4 +- src/nodes/VariableDeclaration.js | 2 +- src/nodes/VariableDeclarationStatement.js | 2 +- .../{WhileStatement.js => WhileStatement.ts} | 11 +- src/nodes/index.js | 52 +- src/nodes/types.d.ts | 10 + src/{options.js => options.ts} | 8 +- src/{parser.js => parser.ts} | 101 ++-- .../language-js/{comments.js => comments.ts} | 546 +++++++++--------- src/printer.js | 2 +- src/types.d.ts | 44 ++ tests/config/.prettierrc | 7 +- .../config/{setup.js => format-test-setup.js} | 0 tests/config/format-test.js | 30 +- tests/config/require-standalone.cjs | 4 +- tests/config/utils/compile-contract.js | 2 +- tests/config/utils/create-snapshot.js | 4 +- .../utils/stringify-options-for-title.js | 2 +- tests/config/utils/visualize-range.js | 2 +- .../binary-operator-printers/index.test.js | 4 +- tests/unit/comments/printer.test.js | 9 +- tsconfig.json | 17 + webpack.config.js | 16 + 91 files changed, 1630 insertions(+), 870 deletions(-) create mode 100644 src/binary-operator-printers/types.d.ts rename src/{clean.js => clean.ts} (79%) rename src/comments/handlers/{handleContractDefinitionComments.js => handleContractDefinitionComments.ts} (85%) rename src/comments/handlers/{handleModifierInvocationComments.js => handleModifierInvocationComments.ts} (88%) rename src/comments/handlers/{index.js => index.ts} (88%) create mode 100644 src/comments/handlers/types.d.ts rename src/comments/{printer.js => printer.ts} (72%) rename src/common/{printer-helpers.js => printer-helpers.ts} (61%) create mode 100644 src/common/types.d.ts rename src/common/{util.js => util.ts} (50%) delete mode 100644 src/loc.js rename src/nodes/{AssemblyBlock.js => AssemblyBlock.ts} (63%) rename src/nodes/{AssemblyCall.js => AssemblyCall.ts} (51%) rename src/nodes/{AssemblyFor.js => AssemblyFor.ts} (58%) rename src/nodes/{AssemblySwitch.js => AssemblySwitch.ts} (52%) rename src/nodes/{Block.js => Block.ts} (77%) rename src/nodes/{CatchClause.js => CatchClause.ts} (50%) rename src/nodes/{Conditional.js => Conditional.ts} (83%) rename src/nodes/{ContractDefinition.js => ContractDefinition.ts} (55%) rename src/nodes/{DoWhileStatement.js => DoWhileStatement.ts} (55%) rename src/nodes/{EnumDefinition.js => EnumDefinition.ts} (63%) rename src/nodes/{FunctionCall.js => FunctionCall.ts} (64%) delete mode 100644 src/nodes/FunctionDefinition.js create mode 100644 src/nodes/FunctionDefinition.ts rename src/nodes/{FunctionTypeName.js => FunctionTypeName.ts} (59%) rename src/nodes/{ImportDirective.js => ImportDirective.ts} (77%) rename src/nodes/{IndexAccess.js => IndexAccess.ts} (59%) rename src/nodes/{InlineAssemblyStatement.js => InlineAssemblyStatement.ts} (55%) rename src/nodes/{Mapping.js => Mapping.ts} (50%) rename src/nodes/{MemberAccess.js => MemberAccess.ts} (82%) rename src/nodes/{ModifierInvocation.js => ModifierInvocation.ts} (55%) rename src/nodes/{NameValueList.js => NameValueList.ts} (55%) rename src/nodes/{ReturnStatement.js => ReturnStatement.ts} (55%) rename src/nodes/{SourceUnit.js => SourceUnit.ts} (60%) rename src/nodes/{StringLiteral.js => StringLiteral.ts} (59%) rename src/nodes/{StructDefinition.js => StructDefinition.ts} (54%) rename src/nodes/{UnaryOperation.js => UnaryOperation.ts} (55%) rename src/nodes/{WhileStatement.js => WhileStatement.ts} (54%) create mode 100644 src/nodes/types.d.ts rename src/{options.js => options.ts} (94%) rename src/{parser.js => parser.ts} (64%) rename src/prettier-comments/language-js/{comments.js => comments.ts} (55%) create mode 100644 src/types.d.ts rename tests/config/{setup.js => format-test-setup.js} (100%) create mode 100644 tsconfig.json diff --git a/.c8rc b/.c8rc index d1a055fdd..8b132d628 100644 --- a/.c8rc +++ b/.c8rc @@ -1,13 +1,14 @@ { "check-coverage": true, - "branches": 99, - "lines": 100, + "branches": 97, + "lines": 99, "functions": 100, - "statements": 100, + "statements": 99, "exclude": ["/node_modules/", "/src/prettier-comments/"], "include": [ "src/**/*.js", - "!src/prettier-comments/**/*.js", + "src/**/*.ts", + "!src/prettier-comments/**/*.ts", "!src/common/backward-compatibility.js" ], "reporter": ["lcov", "text"], diff --git a/.eslintignore b/.eslintignore index 77cd3b23c..11a6e6694 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,8 @@ coverage/**/*.js -dist/**/*.js +dist/**/*.cjs +tests/**/*.snap tests/format/**/*.sol tests/format/Markdown/Markdown.md tests/format/RespectDefaultOptions/respect-default-options.js tests/config/**/*.*js -src/prettier-comments/**/*.js +src/prettier-comments/**/*.ts diff --git a/.eslintrc b/.eslintrc index eb3058129..4345855ad 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,16 +2,33 @@ "env": { "jest": true }, - "extends": ["airbnb-base", "prettier"], "globals": { - "run_spec": false + "run_spec": "readonly" }, - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "rules": { - "import/extensions": "off", - "import/prefer-default-export": "off" - } + "overrides": [ + { + "files": ["**/*.ts"], + "extends": ["prettier", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "parserOptions": { + "project": ["tsconfig.json"] + }, + "rules": { + "@typescript-eslint/explicit-function-return-type": "error" + } + }, + { + "files": ["**/*.*js"], + "extends": ["airbnb-base", "prettier"], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "rules": { + "import/extensions": "off", + "import/prefer-default-export": "off" + } + } + ] } diff --git a/.prettierignore b/.prettierignore index e09b9fd06..7bc82e792 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,4 +3,4 @@ dist/**/*.js tests/format/**/*.sol tests/format/Markdown/Markdown.md tests/format/RespectDefaultOptions/respect-default-options.js -src/prettier-comments/**/*.js +src/prettier-comments/**/*.ts diff --git a/jest.config.js b/jest.config.js index 79f72f497..dd7f0f5fa 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,6 @@ const TEST_STANDALONE = Boolean(process.env.TEST_STANDALONE); const testMatch = [ '/tests/format/**/jsfmt.spec.js', - '/tests/unit/**/*.test.js' ]; @@ -11,12 +10,11 @@ if (TEST_STANDALONE) { export default { runner: 'jest-light-runner', - setupFiles: ['/tests/config/setup.js'], + setupFiles: ['/tests/config/format-test-setup.js'], snapshotSerializers: [ 'jest-snapshot-serializer-raw', 'jest-snapshot-serializer-ansi' ], - testEnvironment: 'node', // ignore console warnings in TEST_STANDALONE silent: TEST_STANDALONE, testPathIgnorePatterns: TEST_STANDALONE @@ -27,7 +25,6 @@ export default { ] : [], testMatch, - transform: {}, watchPlugins: [ 'jest-watch-typeahead/filename', 'jest-watch-typeahead/testname' diff --git a/package-lock.json b/package-lock.json index f9b18aaa9..ed9dbe077 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,28 +10,33 @@ "license": "MIT", "dependencies": { "@solidity-parser/parser": "^0.18.0", - "semver": "^7.5.4" + "semver": "^7.6.0" }, "devDependencies": { - "@babel/code-frame": "^7.22.10", - "c8": "^9.0.0", + "@babel/code-frame": "^7.23.5", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "c8": "^9.1.0", "cross-env": "^7.0.3", - "eslint": "^8.47.0", + "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.28.1", - "esm-utils": "^4.1.2", - "esmock": "^2.3.8", - "jest": "^29.6.3", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "esm-utils": "^4.2.1", + "esmock": "^2.6.4", + "jest": "^29.7.0", "jest-light-runner": "^0.6.0", "jest-snapshot-serializer-ansi": "^2.1.0", "jest-snapshot-serializer-raw": "^2.0.0", "jest-watch-typeahead": "^2.2.2", - "lines-and-columns": "^2.0.3", - "prettier": "^3.1.1", + "lines-and-columns": "^2.0.4", + "prettier": "^3.2.5", "proxyquire": "^2.1.3", "solc": "^0.8.25", - "webpack": "^5.88.2", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "typescript": "^5.4.2", + "webpack": "^5.90.3", "webpack-cli": "^5.1.4" }, "engines": { @@ -692,6 +697,28 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -1349,6 +1376,30 @@ "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -1450,9 +1501,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/json5": { @@ -1467,6 +1518,12 @@ "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", "dev": true }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -1488,6 +1545,220 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/type-utils": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1726,6 +1997,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1836,6 +2116,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1874,6 +2160,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.findlastindex": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", @@ -2407,6 +2702,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -2666,6 +2967,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -2675,6 +2985,18 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3636,6 +3958,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -5882,6 +6224,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -6314,6 +6662,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7289,6 +7646,90 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -7405,6 +7846,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -7477,6 +7931,12 @@ "url": "https://github.com/fisker/url-or-path?sponsor=1" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -7791,6 +8251,15 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 20a4fe27e..20faae9de 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,10 @@ "lint": "npm run eslint && npm run prettier -- --list-different", "lint:fix": "npm run eslint -- --fix && npm run prettier -- --write", "prepublishOnly": "npm run build", - "prettier": "prettier './*.{js,json,md,yml}' '{src,tests}/**/*.js'", - "test": "NODE_OPTIONS=--loader=esmock jest", - "test:all": "cross-env FULL_TEST=1 NODE_OPTIONS=--loader=esmock c8 jest", - "test:standalone": "cross-env TEST_STANDALONE=1 FULL_TEST=1 jest" + "prettier": "prettier './*.{ts,js,cjs,json,md,yml}' '{src,tests}/**/*.{ts,js,cjs}'", + "test": "NODE_OPTIONS=\"--loader=ts-node/esm --loader=esmock\" jest", + "test:all": "cross-env FULL_TEST=1 NODE_OPTIONS=\"--loader=ts-node/esm --loader=esmock\" c8 jest", + "test:standalone": "cross-env NODE_OPTIONS=\"--loader=ts-node/esm\" TEST_STANDALONE=1 FULL_TEST=1 jest" }, "files": [ "src", @@ -87,30 +87,35 @@ "node": ">=16" }, "devDependencies": { - "@babel/code-frame": "^7.22.10", - "c8": "^9.0.0", + "@babel/code-frame": "^7.23.5", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "c8": "^9.1.0", "cross-env": "^7.0.3", - "eslint": "^8.47.0", + "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.28.1", - "esm-utils": "^4.1.2", - "esmock": "^2.3.8", - "jest": "^29.6.3", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "esm-utils": "^4.2.1", + "esmock": "^2.6.4", + "jest": "^29.7.0", "jest-light-runner": "^0.6.0", "jest-snapshot-serializer-ansi": "^2.1.0", "jest-snapshot-serializer-raw": "^2.0.0", "jest-watch-typeahead": "^2.2.2", - "lines-and-columns": "^2.0.3", - "prettier": "^3.1.1", + "lines-and-columns": "^2.0.4", + "prettier": "^3.2.5", "proxyquire": "^2.1.3", "solc": "^0.8.25", - "webpack": "^5.88.2", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "typescript": "^5.4.2", + "webpack": "^5.90.3", "webpack-cli": "^5.1.4" }, "dependencies": { "@solidity-parser/parser": "^0.18.0", - "semver": "^7.5.4" + "semver": "^7.6.0" }, "peerDependencies": { "prettier": ">=2.3.0" @@ -118,8 +123,7 @@ "browserslist": { "production": [ ">0.5%", - "not ie 11", - "not safari 5.1", + "not dead", "not op_mini all" ], "development": [ diff --git a/src/binary-operator-printers/types.d.ts b/src/binary-operator-printers/types.d.ts new file mode 100644 index 000000000..dbbf99250 --- /dev/null +++ b/src/binary-operator-printers/types.d.ts @@ -0,0 +1,9 @@ +import type { + BinaryOperation, + BinOp +} from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from '../nodes/types'; + +interface BinaryOperationPrinter extends NodePrinter { + operators: BinOp[]; +} diff --git a/src/clean.js b/src/clean.ts similarity index 79% rename from src/clean.js rename to src/clean.ts index 6ce7c3d55..280b14184 100644 --- a/src/clean.js +++ b/src/clean.ts @@ -1,6 +1,6 @@ // Prettier offers a clean way to define ignored properties. const ignoredProperties = new Set(['loc', 'range', 'comments']); -function clean(/* ast, newObj, parent */) {} +function clean(/* ast, newObj, parent */): void {} clean.ignoredProperties = ignoredProperties; export default clean; diff --git a/src/comments/handler.js b/src/comments/handler.js index 208a189c1..f0e00d3de 100644 --- a/src/comments/handler.js +++ b/src/comments/handler.js @@ -2,8 +2,8 @@ import { handleOwnLineComment, handleEndOfLineComment, handleRemainingComment -} from '../prettier-comments/language-js/comments.js'; -import handlers from './handlers/index.js'; +} from '../prettier-comments/language-js/comments.ts'; +import handlers from './handlers/index.ts'; export function solidityHandleOwnLineComment( comment, @@ -18,13 +18,12 @@ export function solidityHandleOwnLineComment( precedingNode, enclosingNode, followingNode, - comment, - options + comment }; if ( handlers.some((handler) => handler(handlerArguments)) || - handleOwnLineComment(comment, text, options, ast, isLastComment) + handleOwnLineComment(comment, text, ast, isLastComment) ) { return true; } @@ -44,13 +43,12 @@ export function solidityHandleEndOfLineComment( precedingNode, enclosingNode, followingNode, - comment, - options + comment }; if ( handlers.some((handler) => handler(handlerArguments)) || - handleEndOfLineComment(comment, text, options, ast, isLastComment) + handleEndOfLineComment(comment, text, ast, isLastComment) ) { return true; } @@ -70,13 +68,12 @@ export function solidityHandleRemainingComment( precedingNode, enclosingNode, followingNode, - comment, - options + comment }; if ( handlers.some((handler) => handler(handlerArguments)) || - handleRemainingComment(comment, text, options, ast, isLastComment) + handleRemainingComment(comment, text, ast, isLastComment) ) { return true; } @@ -86,3 +83,15 @@ export function solidityHandleRemainingComment( export function isBlockComment(comment) { return comment.type === 'BlockComment'; } + +function isNodeOrComment(node) { + return node.type !== undefined; +} + +export function canAttachComment(node) { + return ( + isNodeOrComment(node) && + !isBlockComment(node) && + node.type !== 'LineComment' + ); +} diff --git a/src/comments/handlers/handleContractDefinitionComments.js b/src/comments/handlers/handleContractDefinitionComments.ts similarity index 85% rename from src/comments/handlers/handleContractDefinitionComments.js rename to src/comments/handlers/handleContractDefinitionComments.ts index 67d5638b9..a5de76db7 100644 --- a/src/comments/handlers/handleContractDefinitionComments.js +++ b/src/comments/handlers/handleContractDefinitionComments.ts @@ -1,16 +1,17 @@ import { util } from 'prettier'; import { getNextNonSpaceNonCommentCharacter } from '../../common/backward-compatibility.js'; +import { locEnd } from '../../common/util.js'; +import type { HandlerArguments } from './types'; const { addLeadingComment, addTrailingComment, addDanglingComment } = util; -function handleContractDefinitionComments({ +export default function handleContractDefinitionComments({ text, precedingNode, enclosingNode, followingNode, - comment, - options -}) { + comment +}: HandlerArguments): boolean { if (!enclosingNode || enclosingNode.type !== 'ContractDefinition') { return false; } @@ -23,7 +24,7 @@ function handleContractDefinitionComments({ const nextCharacter = getNextNonSpaceNonCommentCharacter( text, comment, - options.locEnd + locEnd ); // The comment is behind the start of the Block `{}` or behind a base contract @@ -45,11 +46,9 @@ function handleContractDefinitionComments({ // When the contract is empty and contain comments. // Most likely disabling a linter rule. if (enclosingNode.subNodes.length === 0) { - addDanglingComment(enclosingNode, comment); + addDanglingComment(enclosingNode, comment, false); return true; } return false; } - -export default handleContractDefinitionComments; diff --git a/src/comments/handlers/handleModifierInvocationComments.js b/src/comments/handlers/handleModifierInvocationComments.ts similarity index 88% rename from src/comments/handlers/handleModifierInvocationComments.js rename to src/comments/handlers/handleModifierInvocationComments.ts index f9034ff5b..00cd515c5 100644 --- a/src/comments/handlers/handleModifierInvocationComments.js +++ b/src/comments/handlers/handleModifierInvocationComments.ts @@ -1,15 +1,16 @@ import { util } from 'prettier'; import { getNextNonSpaceNonCommentCharacter } from '../../common/backward-compatibility.js'; +import { locEnd } from '../../common/util.js'; +import type { HandlerArguments } from './types'; const { addLeadingComment, addTrailingComment, addDanglingComment } = util; -function handleModifierInvocationComments({ +export default function handleModifierInvocationComments({ text, precedingNode, enclosingNode, - comment, - options -}) { + comment +}: HandlerArguments): boolean { if (!enclosingNode || enclosingNode.type !== 'ModifierInvocation') { return false; } @@ -22,7 +23,7 @@ function handleModifierInvocationComments({ const nextCharacter = getNextNonSpaceNonCommentCharacter( text, comment, - options.locEnd + locEnd ); // The comment is behind the start of the Parentheses `()` @@ -57,11 +58,9 @@ function handleModifierInvocationComments({ // comment, we assume there's an explicit reason for it to be placed there // so we respect it here. // function a() public modifier(/* block comment */) - addDanglingComment(enclosingNode, comment); + addDanglingComment(enclosingNode, comment, false); return true; } return false; } - -export default handleModifierInvocationComments; diff --git a/src/comments/handlers/index.js b/src/comments/handlers/index.ts similarity index 88% rename from src/comments/handlers/index.js rename to src/comments/handlers/index.ts index fa67eab16..571ee7128 100644 --- a/src/comments/handlers/index.js +++ b/src/comments/handlers/index.ts @@ -1,4 +1,3 @@ -/* eslint-disable global-require */ import handleContractDefinitionComments from './handleContractDefinitionComments.js'; import handleModifierInvocationComments from './handleModifierInvocationComments.js'; diff --git a/src/comments/handlers/types.d.ts b/src/comments/handlers/types.d.ts new file mode 100644 index 000000000..7c28d8686 --- /dev/null +++ b/src/comments/handlers/types.d.ts @@ -0,0 +1,9 @@ +import type { ASTNode, Comment } from '@solidity-parser/parser/src/ast-types'; + +interface HandlerArguments { + text: string; + precedingNode?: ASTNode; + enclosingNode?: ASTNode; + followingNode?: ASTNode; + comment: Comment; +} diff --git a/src/comments/index.js b/src/comments/index.js index 22efb5a89..a1f2c22a1 100644 --- a/src/comments/index.js +++ b/src/comments/index.js @@ -1,2 +1,2 @@ export * from './handler.js'; -export * from './printer.js'; +export * from './printer.ts'; diff --git a/src/comments/printer.js b/src/comments/printer.ts similarity index 72% rename from src/comments/printer.js rename to src/comments/printer.ts index ee84ee9e0..203fff757 100644 --- a/src/comments/printer.js +++ b/src/comments/printer.ts @@ -1,8 +1,12 @@ import { doc, util } from 'prettier'; +import { getNode } from '../common/backward-compatibility.js'; +import { locStart } from '../common/util.js'; +import type { Comment } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; const { hardline, join } = doc.builders; -function isIndentableBlockComment(comment) { +function isIndentableBlockComment(comment: Comment): boolean { // If the comment has multiple lines and every line starts with a star // we can fix the indentation of each line. The stars in the `/*` and // `*/` delimiters are not included in the comment value, so add them @@ -11,7 +15,7 @@ function isIndentableBlockComment(comment) { return lines.length > 1 && lines.every((line) => line.trim()[0] === '*'); } -function printIndentableBlockComment(comment) { +function printIndentableBlockComment(comment: Comment): Doc { const lines = comment.value.split('\n'); return [ @@ -28,8 +32,11 @@ function printIndentableBlockComment(comment) { ]; } -export function printComment(commentPath, options) { - const comment = commentPath.getValue(); +export function printComment( + commentPath: AstPath, + options: ParserOptions +): Doc { + const comment = getNode(commentPath) as Comment; switch (comment.type) { case 'BlockComment': { @@ -40,7 +47,7 @@ export function printComment(commentPath, options) { // interleaved. See https://github.com/prettier/prettier/issues/4412 if ( comment.trailing && - !util.hasNewline(options.originalText, options.locStart(comment), { + !util.hasNewline(options.originalText, locStart(comment), { backwards: true }) ) { diff --git a/src/common/backward-compatibility.js b/src/common/backward-compatibility.js index a8ae3d512..ec5e9a378 100644 --- a/src/common/backward-compatibility.js +++ b/src/common/backward-compatibility.js @@ -1,5 +1,5 @@ import { util } from 'prettier'; -import { prettierVersionSatisfies } from './util.js'; +import { prettierVersionSatisfies } from './util.ts'; export const isPrettier2 = prettierVersionSatisfies('^2.3.0'); @@ -27,6 +27,8 @@ export function getNextNonSpaceNonCommentCharacter(text, node, locEnd) { : util.getNextNonSpaceNonCommentCharacter(text, locEnd(node)); // V3 exposes this function directly } +export const getNode = (path) => (isPrettier2 ? path.getValue() : path.node); // V3 deprecated `getValue` + export function isLast(path, key, index) { return isPrettier2 ? index === path.getParentNode()[key].length - 1 diff --git a/src/common/printer-helpers.js b/src/common/printer-helpers.ts similarity index 61% rename from src/common/printer-helpers.js rename to src/common/printer-helpers.ts index 1401e1aac..7f7fbd84b 100644 --- a/src/common/printer-helpers.js +++ b/src/common/printer-helpers.ts @@ -1,27 +1,38 @@ import { doc } from 'prettier'; import { + getNode, isLast, isNextLineEmpty, isPrettier2 } from './backward-compatibility.js'; +import { printComment } from '../comments/printer.js'; +import { locEnd } from './util.js'; +import type { ASTNode, Comment } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { DocV2, PrintSeparatedOptions } from './types'; const { group, indent, join, line, softline, hardline } = doc.builders; -export const printComments = (node, path, options, filter = () => true) => { - if (!node.comments) return ''; +export const printComments = ( + node: ASTNode, + path: AstPath, + options: ParserOptions, + filter: (comment: Comment) => boolean = (): true => true +): Doc[] => { + if (!node.comments) return []; const document = join( line, path .map((commentPath) => { - const comment = commentPath.getValue(); + const comment = getNode(commentPath) as Comment; if (comment.trailing || comment.leading || comment.printed) { - return null; + return ''; } if (!filter(comment)) { - return null; + return ''; } comment.printed = true; - return options.printer.printComment(commentPath, options); + return printComment(commentPath, options); }, 'comments') .filter(Boolean) ); @@ -31,15 +42,20 @@ export const printComments = (node, path, options, filter = () => true) => { // Mocking the behaviour will introduce a lot of maintenance in the tests. /* c8 ignore start */ return isPrettier2 - ? document.parts // Prettier V2 + ? (document as DocV2).parts // Prettier V2 : document; // Prettier V3 /* c8 ignore stop */ }; -export function printPreservingEmptyLines(path, key, options, print) { - const parts = []; +export function printPreservingEmptyLines( + path: AstPath, + key: string, + options: ParserOptions, + print: (path: AstPath) => Doc +): Doc[] { + const parts: Doc[] = []; path.each((childPath, index) => { - const node = childPath.getValue(); + const node = getNode(childPath) as ASTNode; const nodeType = node.type; if ( @@ -57,7 +73,7 @@ export function printPreservingEmptyLines(path, key, options, print) { // Only attempt to append an empty line if `node` is not the last item if ( !isLast(childPath, key, index) && - isNextLineEmpty(options.originalText, options.locEnd(node) + 1) + isNextLineEmpty(options.originalText, locEnd(node) + 1) ) { // Append an empty line if the original text already had an one after // the current `node` @@ -71,25 +87,30 @@ export function printPreservingEmptyLines(path, key, options, print) { // This function will add an indentation to the `item` and separate it from the // rest of the `doc` in most cases by a `softline`. export const printSeparatedItem = ( - item, + item: Doc, { firstSeparator = softline, lastSeparator = firstSeparator, grouped = true - } = {} -) => { - const document = [indent([firstSeparator, item]), lastSeparator]; - return grouped ? group(document) : document; -}; + }: PrintSeparatedOptions = {} +): Doc => + grouped + ? group([indent([firstSeparator, item]), lastSeparator]) + : [indent([firstSeparator, item]), lastSeparator]; // This function will add an indentation to the `list` and separate it from the // rest of the `doc` in most cases by a `softline`. // the list itself will be printed with a separator that in most cases is a // comma (,) and a `line` export const printSeparatedList = ( - list, - { firstSeparator, separator = [',', line], lastSeparator, grouped } = {} -) => + list: Doc[], + { + firstSeparator, + separator = [',', line], + lastSeparator, + grouped + }: PrintSeparatedOptions = {} +): Doc => printSeparatedItem(join(separator, list), { firstSeparator, lastSeparator, diff --git a/src/common/types.d.ts b/src/common/types.d.ts new file mode 100644 index 000000000..8352516fc --- /dev/null +++ b/src/common/types.d.ts @@ -0,0 +1,28 @@ +import type { ASTNode, Comment } from '@solidity-parser/parser/src/ast-types'; +import type { Doc, util } from 'prettier'; + +declare namespace utilV2Functions { + function getNextNonSpaceNonCommentCharacterIndex( + text: string, + node: ASTNode | Comment, + locEnd: (node: ASTNode) => number + ): number | false; + + function isNextLineEmptyAfterIndex(text: string, startIndex: number): boolean; +} + +type utilV2 = typeof util & typeof utilV2Functions; + +type DocV2 = Doc[] & { parts: Doc[] }; + +interface QuoteRegex { + quote: util.Quote; + regex: RegExp; +} + +interface PrintSeparatedOptions { + firstSeparator?: Doc; + separator?: Doc; + lastSeparator?: Doc; + grouped?: boolean; +} diff --git a/src/common/util.js b/src/common/util.ts similarity index 50% rename from src/common/util.js rename to src/common/util.ts index 608d72272..781079a6b 100644 --- a/src/common/util.js +++ b/src/common/util.ts @@ -1,12 +1,19 @@ import { util, version } from 'prettier'; import satisfies from 'semver/functions/satisfies.js'; +import type { ASTNode, Comment } from '@solidity-parser/parser/src/ast-types'; +import type { Doc, doc, ParserOptions } from 'prettier'; +import type { QuoteRegex } from './types'; -export const prettierVersionSatisfies = (range) => satisfies(version, range); +export const prettierVersionSatisfies = (range: string): boolean => + satisfies(version, range); -export function printString(rawContent, options) { - const double = { quote: '"', regex: /"/g }; - const single = { quote: "'", regex: /'/g }; +const double: QuoteRegex = { quote: '"', regex: /"/g }; +const single: QuoteRegex = { quote: "'", regex: /'/g }; +export function printString( + rawContent: string, + options: ParserOptions +): string { const preferred = options.singleQuote ? single : double; const alternate = preferred === single ? double : single; @@ -21,8 +28,8 @@ export function printString(rawContent, options) { rawContent.includes(preferred.quote) || rawContent.includes(alternate.quote) ) { - const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length; - const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length; + const numPreferredQuotes = (rawContent.match(preferred.regex) ?? []).length; + const numAlternateQuotes = (rawContent.match(alternate.regex) ?? []).length; shouldUseAlternateQuote = numPreferredQuotes > numAlternateQuotes; } @@ -38,9 +45,33 @@ export function printString(rawContent, options) { return util.makeString(rawContent, enclosingQuote); } -export function hasNodeIgnoreComment(node) { - return ( - node?.comments?.length > 0 && - node.comments.some((comment) => comment.value.trim() === 'prettier-ignore') +export function hasNodeIgnoreComment(node: ASTNode): boolean { + return Boolean( + node.comments?.some( + (comment: Comment) => comment.value.trim() === 'prettier-ignore' + ) ); } + +// see: https://github.com/prettier/prettier/blob/main/src/language-js/loc.js +function getRange(index: number, node: ASTNode | Comment): number { + if (node.range) { + return node.range[index]; + } + if (node.type === 'ExpressionStatement' && node.expression?.range) { + return node.expression.range[index]; + } + return 0; +} + +export function locEnd(node: ASTNode | Comment): number { + return getRange(1, node); +} + +export function locStart(node: ASTNode | Comment): number { + return getRange(0, node); +} + +export function isLabel(doc: Doc): doc is doc.builders.Label { + return (doc as doc.builders.DocCommand).type === 'label'; +} diff --git a/src/index.js b/src/index.js index 9d3bfe3cf..d5f03ebfa 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,15 @@ import * as comments from './comments/index.js'; -import massageAstNode from './clean.js'; -import loc from './loc.js'; -import options from './options.js'; -import parse from './parser.js'; +import massageAstNode from './clean.ts'; +import { locEnd, locStart } from './common/util.ts'; +import options from './options.ts'; +import parse from './parser.ts'; import print from './printer.js'; +const astFormat = 'solidity-ast'; +const parserName = 'solidity-parse'; + // https://prettier.io/docs/en/plugins.html#languages -// https://github.com/ikatyang/linguist-languages/blob/master/data/Solidity.json +// https://github.com/github-linguist/linguist/blob/master/lib/linguist/languages.yml const languages = [ { linguistLanguageId: 237469032, @@ -16,35 +19,29 @@ const languages = [ aceMode: 'text', tmScope: 'source.solidity', extensions: ['.sol'], - parsers: ['solidity-parse'], + parsers: [parserName], vscodeLanguageIds: ['solidity'] } ]; // https://prettier.io/docs/en/plugins.html#parsers -const parser = { astFormat: 'solidity-ast', parse, ...loc }; -const parsers = { - 'solidity-parse': parser -}; - -const canAttachComment = (node) => - node.type && node.type !== 'BlockComment' && node.type !== 'LineComment'; +const parser = { astFormat, locEnd, locStart, parse }; +const parsers = { [parserName]: parser }; // https://prettier.io/docs/en/plugins.html#printers -const printers = { - 'solidity-ast': { - canAttachComment, - handleComments: { - ownLine: comments.solidityHandleOwnLineComment, - endOfLine: comments.solidityHandleEndOfLineComment, - remaining: comments.solidityHandleRemainingComment - }, - isBlockComment: comments.isBlockComment, - massageAstNode, - print, - printComment: comments.printComment - } +const printer = { + canAttachComment: comments.canAttachComment, + handleComments: { + ownLine: comments.solidityHandleOwnLineComment, + endOfLine: comments.solidityHandleEndOfLineComment, + remaining: comments.solidityHandleRemainingComment + }, + isBlockComment: comments.isBlockComment, + massageAstNode, + print, + printComment: comments.printComment }; +const printers = { [astFormat]: printer }; // https://prettier.io/docs/en/plugins.html#defaultoptions const defaultOptions = { diff --git a/src/loc.js b/src/loc.js deleted file mode 100644 index 4f7ab03ba..000000000 --- a/src/loc.js +++ /dev/null @@ -1,16 +0,0 @@ -// see: https://github.com/prettier/prettier/blob/main/src/language-js/loc.js - -function getRange(index, node) { - if (node.range) { - return node.range[index]; - } - if (node.expression?.range) { - return node.expression.range[index]; - } - return null; -} - -export default { - locEnd: (node) => getRange(1, node), - locStart: (node) => getRange(0, node) -}; diff --git a/src/nodes/AssemblyBlock.js b/src/nodes/AssemblyBlock.ts similarity index 63% rename from src/nodes/AssemblyBlock.js rename to src/nodes/AssemblyBlock.ts index 680790711..f741e2724 100644 --- a/src/nodes/AssemblyBlock.js +++ b/src/nodes/AssemblyBlock.ts @@ -4,11 +4,13 @@ import { printPreservingEmptyLines, printSeparatedItem } from '../common/printer-helpers.js'; +import type { AssemblyBlock as IAssemblyBlock } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { hardline } = doc.builders; -export const AssemblyBlock = { - print: ({ node, options, path, print }) => [ +export const AssemblyBlock: NodePrinter = { + print: ({ node, path, print, options }) => [ '{', printSeparatedItem( [ diff --git a/src/nodes/AssemblyCall.js b/src/nodes/AssemblyCall.ts similarity index 51% rename from src/nodes/AssemblyCall.js rename to src/nodes/AssemblyCall.ts index b7973b8b6..00c252648 100644 --- a/src/nodes/AssemblyCall.js +++ b/src/nodes/AssemblyCall.ts @@ -1,9 +1,12 @@ import { printSeparatedList } from '../common/printer-helpers.js'; +import { locEnd } from '../common/util.js'; +import type { AssemblyCall as IAssemblyCall } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; -export const AssemblyCall = { +export const AssemblyCall: NodePrinter = { print: ({ node, path, print, options }) => node.arguments.length === 0 && - options.originalText.charAt(options.locEnd(node)) !== ')' + options.originalText.charAt(locEnd(node)) !== ')' ? node.functionName : [ node.functionName, diff --git a/src/nodes/AssemblyFor.js b/src/nodes/AssemblyFor.ts similarity index 58% rename from src/nodes/AssemblyFor.js rename to src/nodes/AssemblyFor.ts index 291382fd1..764b441db 100644 --- a/src/nodes/AssemblyFor.js +++ b/src/nodes/AssemblyFor.ts @@ -1,8 +1,10 @@ import { doc } from 'prettier'; +import type { AssemblyFor as IAssemblyFor } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { join } = doc.builders; -export const AssemblyFor = { +export const AssemblyFor: NodePrinter = { print: ({ path, print }) => join(' ', [ 'for', diff --git a/src/nodes/AssemblyFunctionDefinition.js b/src/nodes/AssemblyFunctionDefinition.js index be5d93039..633064bfb 100644 --- a/src/nodes/AssemblyFunctionDefinition.js +++ b/src/nodes/AssemblyFunctionDefinition.js @@ -2,15 +2,13 @@ import { doc } from 'prettier'; import { printSeparatedItem, printSeparatedList -} from '../common/printer-helpers.js'; +} from '../common/printer-helpers.ts'; const { line } = doc.builders; export const AssemblyFunctionDefinition = { print: ({ node, path, print }) => [ - 'function ', - node.name, - '(', + `function ${node.name}(`, printSeparatedList(path.map(print, 'arguments')), ')', node.returnArguments.length === 0 diff --git a/src/nodes/AssemblyLocalDefinition.js b/src/nodes/AssemblyLocalDefinition.js index 9e2c17e76..a3617375d 100644 --- a/src/nodes/AssemblyLocalDefinition.js +++ b/src/nodes/AssemblyLocalDefinition.js @@ -1,20 +1,12 @@ import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const { line } = doc.builders; export const AssemblyLocalDefinition = { - print: ({ node, path, print }) => { - const parts = [ - 'let', - printSeparatedList(path.map(print, 'names'), { firstSeparator: line }) - ]; - - if (node.expression !== null) { - parts.push(':= '); - parts.push(path.call(print, 'expression')); - } - - return parts; - } + print: ({ node, path, print }) => [ + 'let', + printSeparatedList(path.map(print, 'names'), { firstSeparator: line }), + node.expression ? [':= ', path.call(print, 'expression')] : '' + ] }; diff --git a/src/nodes/AssemblyStackAssignment.js b/src/nodes/AssemblyStackAssignment.js index 32ada6c70..cbc39c7c8 100644 --- a/src/nodes/AssemblyStackAssignment.js +++ b/src/nodes/AssemblyStackAssignment.js @@ -1,7 +1,6 @@ export const AssemblyStackAssignment = { print: ({ node, path, print }) => [ path.call(print, 'expression'), - ' =: ', - node.name + ` =: ${node.name}` ] }; diff --git a/src/nodes/AssemblySwitch.js b/src/nodes/AssemblySwitch.ts similarity index 52% rename from src/nodes/AssemblySwitch.js rename to src/nodes/AssemblySwitch.ts index eccb91a0e..1591272d0 100644 --- a/src/nodes/AssemblySwitch.js +++ b/src/nodes/AssemblySwitch.ts @@ -1,8 +1,10 @@ import { doc } from 'prettier'; +import type { AssemblySwitch as IAssemblySwitch } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { hardline, join } = doc.builders; -export const AssemblySwitch = { +export const AssemblySwitch: NodePrinter = { print: ({ path, print }) => [ 'switch ', path.call(print, 'expression'), diff --git a/src/nodes/Block.js b/src/nodes/Block.ts similarity index 77% rename from src/nodes/Block.js rename to src/nodes/Block.ts index 3045fc249..b6028c8c6 100644 --- a/src/nodes/Block.js +++ b/src/nodes/Block.ts @@ -3,10 +3,12 @@ import { printComments, printPreservingEmptyLines } from '../common/printer-helpers.js'; +import type { Block as IBlock } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { hardline, indent } = doc.builders; -export const Block = { +export const Block: NodePrinter = { print: ({ node, options, path, print }) => // if block is empty, just return the pair of braces node.statements.length === 0 && !node.comments diff --git a/src/nodes/CatchClause.js b/src/nodes/CatchClause.ts similarity index 50% rename from src/nodes/CatchClause.js rename to src/nodes/CatchClause.ts index a29c065e9..1fe17a7cc 100644 --- a/src/nodes/CatchClause.js +++ b/src/nodes/CatchClause.ts @@ -1,6 +1,13 @@ import { printSeparatedList } from '../common/printer-helpers.js'; +import type { CatchClause as ICatchClause } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc } from 'prettier'; +import type { NodePrinter } from './types'; -const parameters = (node, path, print) => +const parameters = ( + node: ICatchClause, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => node.parameters ? [ node.kind || '', @@ -10,7 +17,7 @@ const parameters = (node, path, print) => ] : ''; -export const CatchClause = { +export const CatchClause: NodePrinter = { print: ({ node, path, print }) => [ 'catch ', parameters(node, path, print), diff --git a/src/nodes/Conditional.js b/src/nodes/Conditional.ts similarity index 83% rename from src/nodes/Conditional.js rename to src/nodes/Conditional.ts index b6f74eb02..52b5b24fe 100644 --- a/src/nodes/Conditional.js +++ b/src/nodes/Conditional.ts @@ -1,10 +1,18 @@ import { doc } from 'prettier'; import { printSeparatedItem } from '../common/printer-helpers.js'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { Conditional as IConditional } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { group, hardline, ifBreak, indent, line, softline } = doc.builders; let groupIndex = 0; -const experimentalTernaries = (node, path, print, options) => { +const experimentalTernaries = ( + node: IConditional, + path: AstPath, + print: (path: AstPath) => Doc, + options: ParserOptions +): Doc => { const parent = path.getParentNode(); const isNested = parent.type === 'Conditional'; const isNestedAsTrueExpression = isNested && parent.trueExpression === node; @@ -30,7 +38,7 @@ const experimentalTernaries = (node, path, print, options) => { const conditionAndTrueExpressionGroup = group( [conditionDoc, trueExpressionDoc], - { id: `Conditional.trueExpressionDoc-${groupIndex}` } + { id: Symbol(`Conditional.trueExpressionDoc-${groupIndex}`) } ); groupIndex += 1; @@ -65,7 +73,10 @@ const experimentalTernaries = (node, path, print, options) => { : document; }; -const traditionalTernaries = (path, print) => +const traditionalTernaries = ( + path: AstPath, + print: (path: AstPath) => Doc +): Doc => group([ path.call(print, 'condition'), indent([ @@ -80,7 +91,7 @@ const traditionalTernaries = (path, print) => ]) ]); -export const Conditional = { +export const Conditional: NodePrinter = { print: ({ node, path, print, options }) => options.experimentalTernaries ? experimentalTernaries(node, path, print, options) diff --git a/src/nodes/ContractDefinition.js b/src/nodes/ContractDefinition.ts similarity index 55% rename from src/nodes/ContractDefinition.js rename to src/nodes/ContractDefinition.ts index 43e784dda..6450acbdb 100644 --- a/src/nodes/ContractDefinition.js +++ b/src/nodes/ContractDefinition.ts @@ -5,10 +5,17 @@ import { printSeparatedItem, printSeparatedList } from '../common/printer-helpers.js'; +import type { ContractDefinition as IContractDefinition } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { NodePrinter } from './types'; const { group, line, hardline } = doc.builders; -const inheritance = (node, path, print) => +const inheritance = ( + node: IContractDefinition, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => node.baseContracts.length > 0 ? [ ' is', @@ -18,9 +25,14 @@ const inheritance = (node, path, print) => ] : line; -const body = (node, path, options, print) => { +const body = ( + node: IContractDefinition, + path: AstPath, + options: ParserOptions, + print: (path: AstPath) => Doc +): Doc => { const comments = printComments(node, path, options); - return node.subNodes.length > 0 || comments?.length + return node.subNodes.length > 0 || comments.length ? printSeparatedItem( [printPreservingEmptyLines(path, 'subNodes', options, print), comments], { firstSeparator: hardline, grouped: false } @@ -28,12 +40,10 @@ const body = (node, path, options, print) => { : ''; }; -export const ContractDefinition = { +export const ContractDefinition: NodePrinter = { print: ({ node, options, path, print }) => [ group([ - node.kind === 'abstract' ? 'abstract contract' : node.kind, - ' ', - node.name, + `${node.kind}${node.kind === 'abstract' ? ' contract ' : ' '}${node.name}`, inheritance(node, path, print), '{' ]), diff --git a/src/nodes/CustomErrorDefinition.js b/src/nodes/CustomErrorDefinition.js index a504932a0..cc66c00b9 100644 --- a/src/nodes/CustomErrorDefinition.js +++ b/src/nodes/CustomErrorDefinition.js @@ -1,4 +1,4 @@ -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const parameters = (node, path, print) => node.parameters?.length > 0 @@ -7,9 +7,7 @@ const parameters = (node, path, print) => export const CustomErrorDefinition = { print: ({ node, path, print }) => [ - 'error ', - node.name, - '(', + `error ${node.name}(`, parameters(node, path, print), ');' ] diff --git a/src/nodes/DoWhileStatement.js b/src/nodes/DoWhileStatement.ts similarity index 55% rename from src/nodes/DoWhileStatement.js rename to src/nodes/DoWhileStatement.ts index af0f86884..e404a6b46 100644 --- a/src/nodes/DoWhileStatement.js +++ b/src/nodes/DoWhileStatement.ts @@ -1,14 +1,21 @@ import { doc } from 'prettier'; import { printSeparatedItem } from '../common/printer-helpers.js'; +import type { DoWhileStatement as IDoWhileStatement } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indent, line } = doc.builders; -const printBody = (node, path, print) => +const printBody = ( + node: IDoWhileStatement, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => node.body.type === 'Block' ? [' ', path.call(print, 'body'), ' '] : group([indent([line, path.call(print, 'body')]), line]); -export const DoWhileStatement = { +export const DoWhileStatement: NodePrinter = { print: ({ node, path, print }) => [ 'do', printBody(node, path, print), diff --git a/src/nodes/ElementaryTypeName.js b/src/nodes/ElementaryTypeName.js index 0da2029fb..8d63600e8 100644 --- a/src/nodes/ElementaryTypeName.js +++ b/src/nodes/ElementaryTypeName.js @@ -1,5 +1,5 @@ const stateMutability = (node) => - node.stateMutability?.length > 0 ? [' ', node.stateMutability] : ''; + node.stateMutability?.length > 0 ? ` ${node.stateMutability}` : ''; export const ElementaryTypeName = { print: ({ node }) => [node.name, stateMutability(node)] diff --git a/src/nodes/EnumDefinition.js b/src/nodes/EnumDefinition.ts similarity index 63% rename from src/nodes/EnumDefinition.js rename to src/nodes/EnumDefinition.ts index c8cd0363f..19f43e723 100644 --- a/src/nodes/EnumDefinition.js +++ b/src/nodes/EnumDefinition.ts @@ -1,9 +1,11 @@ import { doc } from 'prettier'; import { printSeparatedList } from '../common/printer-helpers.js'; +import type { EnumDefinition as IEnumDefinition } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { group, hardline } = doc.builders; -export const EnumDefinition = { +export const EnumDefinition: NodePrinter = { print: ({ node, path, print }) => group([ 'enum ', diff --git a/src/nodes/EventDefinition.js b/src/nodes/EventDefinition.js index 569382cef..d600f3751 100644 --- a/src/nodes/EventDefinition.js +++ b/src/nodes/EventDefinition.js @@ -1,4 +1,4 @@ -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const parameters = (node, path, print) => node.parameters?.length > 0 @@ -7,12 +7,8 @@ const parameters = (node, path, print) => export const EventDefinition = { print: ({ node, path, print }) => [ - 'event ', - node.name, - '(', + `event ${node.name}(`, parameters(node, path, print), - ')', - node.isAnonymous ? ' anonymous' : '', - ';' + `)${node.isAnonymous ? ' anonymous' : ''};` ] }; diff --git a/src/nodes/ExpressionStatement.js b/src/nodes/ExpressionStatement.js index 6f40afe7d..145190c9a 100644 --- a/src/nodes/ExpressionStatement.js +++ b/src/nodes/ExpressionStatement.js @@ -1,27 +1,19 @@ import { doc } from 'prettier'; -import { printComments } from '../common/printer-helpers.js'; +import { printComments } from '../common/printer-helpers.ts'; const { hardline } = doc.builders; export const ExpressionStatement = { print: ({ node, options, path, print }) => { - const parts = []; + const comments = + path.getParentNode().type === 'IfStatement' + ? printComments(node, path, options) + : []; - const parent = path.getParentNode(); - - if (parent.type === 'IfStatement') { - if (node.comments?.length) { - const comments = printComments(node, path, options); - if (comments?.length) { - parts.push(comments); - parts.push(hardline); - } - } - } - - parts.push(path.call(print, 'expression')); - parts.push(node.omitSemicolon ? '' : ';'); - - return parts; + return [ + comments.length ? [comments, hardline] : '', + path.call(print, 'expression'), + node.omitSemicolon ? '' : ';' + ]; } }; diff --git a/src/nodes/ForStatement.js b/src/nodes/ForStatement.js index 609a92a7a..5a24d9930 100644 --- a/src/nodes/ForStatement.js +++ b/src/nodes/ForStatement.js @@ -1,5 +1,5 @@ import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const { group, indent, line } = doc.builders; diff --git a/src/nodes/FunctionCall.js b/src/nodes/FunctionCall.ts similarity index 64% rename from src/nodes/FunctionCall.js rename to src/nodes/FunctionCall.ts index f33d98b43..83e492ebb 100644 --- a/src/nodes/FunctionCall.js +++ b/src/nodes/FunctionCall.ts @@ -1,9 +1,17 @@ import { doc } from 'prettier'; import { printSeparatedList } from '../common/printer-helpers.js'; +import { isLabel } from '../common/util.js'; +import type { FunctionCall as IFunctionCall } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indentIfBreak, label, line, softline } = doc.builders; -const printObject = (path, print, options) => { +const printObject = ( + path: AstPath, + print: (path: AstPath) => Doc, + options: ParserOptions +): Doc => { const identifiers = path.map(print, 'identifiers'); return [ '{', @@ -19,19 +27,19 @@ const printObject = (path, print, options) => { ]; }; -const printArguments = (path, print) => +const printArguments = (path: AstPath, print: (path: AstPath) => Doc): Doc => printSeparatedList(path.map(print, 'arguments'), { lastSeparator: [softline, ')'] }); let groupIndex = 0; -export const FunctionCall = { +export const FunctionCall: NodePrinter = { print: ({ node, path, print, options }) => { let expressionDoc = path.call(print, 'expression'); - let argumentsDoc = ')'; + let argumentsDoc: Doc = ')'; - if (node.arguments?.length > 0) { - if (node.identifiers?.length > 0) { + if (node.arguments.length > 0) { + if (node.identifiers.length > 0) { argumentsDoc = printObject(path, print, options); } else { argumentsDoc = printArguments(path, print); @@ -40,15 +48,15 @@ export const FunctionCall = { // If we are at the end of a MemberAccessChain we should indent the // arguments accordingly. - if (expressionDoc.label === 'MemberAccessChain') { + if (isLabel(expressionDoc) && expressionDoc.label === 'MemberAccessChain') { expressionDoc = group(expressionDoc.contents, { - id: `FunctionCall.expression-${groupIndex}` + id: Symbol(`FunctionCall.expression-${groupIndex}`) }); groupIndex += 1; argumentsDoc = indentIfBreak(argumentsDoc, { - groupId: expressionDoc.id + groupId: expressionDoc.id! }); // We wrap the expression in a label in case there is an IndexAccess or // a FunctionCall following this IndexAccess. diff --git a/src/nodes/FunctionDefinition.js b/src/nodes/FunctionDefinition.js deleted file mode 100644 index f6e31955e..000000000 --- a/src/nodes/FunctionDefinition.js +++ /dev/null @@ -1,117 +0,0 @@ -import { doc } from 'prettier'; -import { getNextNonSpaceNonCommentCharacter } from '../common/backward-compatibility.js'; -import { - printComments, - printSeparatedItem, - printSeparatedList -} from '../common/printer-helpers.js'; - -const { dedent, group, indent, join, line } = doc.builders; - -const functionName = (node, options) => { - if (node.isConstructor && !node.name) return 'constructor'; - if (node.name) return `function ${node.name}`; - if (node.isReceiveEther) return 'receive'; - // The parser doesn't give us any information about the keyword used for the - // fallback. - // Using the originalText is the next best option. - // A neat idea would be to rely on the pragma and enforce it but for the - // moment this will do. - const names = { fallback: 'fallback', function: 'function' }; - const name = options.originalText.slice( - options.locStart(node), - options.locStart(node) + 8 - ); - return names[name]; -}; - -const parameters = (parametersType, node, path, print, options) => { - if (node[parametersType]?.length > 0) { - return printSeparatedList(path.map(print, parametersType), { - grouped: false - }); - } - if (node.comments?.length > 0) { - // we add a check to see if the comment is inside the parentheses - const parameterComments = printComments( - node, - path, - options, - (comment) => - getNextNonSpaceNonCommentCharacter( - options.originalText, - comment, - options.locEnd - ) === ')' - ); - return parameterComments.length > 0 - ? printSeparatedItem(parameterComments) - : ''; - } - return ''; -}; - -const visibility = (node) => - node.visibility && node.visibility !== 'default' - ? [line, node.visibility] - : ''; - -const virtual = (node) => (node.isVirtual ? [line, 'virtual'] : ''); - -const override = (node, path, print) => { - if (!node.override) return ''; - if (node.override.length === 0) return [line, 'override']; - return [ - line, - 'override(', - printSeparatedList(path.map(print, 'override')), - ')' - ]; -}; - -const stateMutability = (node) => - node.stateMutability ? [line, node.stateMutability] : ''; - -const modifiers = (node, path, print) => - node.modifiers.length > 0 - ? [line, join(line, path.map(print, 'modifiers'))] - : ''; - -const returnParameters = (node, path, print, options) => - node.returnParameters - ? [ - line, - 'returns (', - group(parameters('returnParameters', node, path, print, options)), - ')' - ] - : ''; - -const signatureEnd = (node) => (node.body ? dedent(line) : ';'); - -const body = (node, path, print) => (node.body ? path.call(print, 'body') : ''); - -export const FunctionDefinition = { - print: ({ node, path, print, options }) => [ - group([ - functionName(node, options), - '(', - parameters('parameters', node, path, print, options), - ')', - indent( - group([ - // TODO: sort comments for modifiers and return parameters - printComments(node, path, options), - visibility(node), - stateMutability(node), - virtual(node), - override(node, path, print), - modifiers(node, path, print), - returnParameters(node, path, print, options), - signatureEnd(node) - ]) - ) - ]), - body(node, path, print) - ] -}; diff --git a/src/nodes/FunctionDefinition.ts b/src/nodes/FunctionDefinition.ts new file mode 100644 index 000000000..99da390ec --- /dev/null +++ b/src/nodes/FunctionDefinition.ts @@ -0,0 +1,139 @@ +import { doc } from 'prettier'; +import { getNextNonSpaceNonCommentCharacter } from '../common/backward-compatibility.js'; +import { + printComments, + printSeparatedItem, + printSeparatedList +} from '../common/printer-helpers.js'; +import { locEnd, locStart } from '../common/util.js'; +import type { FunctionDefinition as IFunctionDefinition } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { NodePrinter } from './types'; + +const { dedent, group, indent, join, line } = doc.builders; + +const keywords = { fallback: 'fallback', function: 'function' }; + +const functionName = ( + node: IFunctionDefinition, + options: ParserOptions +): string => { + if (node.isConstructor && !node.name) return 'constructor'; + if (node.name) return `function ${node.name}`; + if (node.isReceiveEther) return 'receive'; + // The parser doesn't give us any information about the keyword used for the + // fallback. + // Using the originalText is the next best option. + // A neat idea would be to rely on the pragma and enforce it but for the + // moment this will do. + const nodeStart = locStart(node); + const keyword = options.originalText.slice( + nodeStart, + nodeStart + 8 + ) as keyof typeof keywords; + return keywords[keyword]; +}; + +const parameters = ( + parametersType: 'parameters' | 'returnParameters', + node: IFunctionDefinition, + path: AstPath, + print: (path: AstPath) => Doc, + options: ParserOptions +): Doc => { + if (node[parametersType]!.length > 0) { + return printSeparatedList(path.map(print, parametersType), { + grouped: false + }); + } + // we add a check to see if the comment is inside the parentheses + const parameterComments = printComments( + node, + path, + options, + (comment) => + getNextNonSpaceNonCommentCharacter( + options.originalText, + comment, + locEnd + ) === ')' + ); + return parameterComments.length > 0 + ? printSeparatedItem(parameterComments) + : ''; +}; + +const visibility = (node: IFunctionDefinition): Doc => + node.visibility !== 'default' ? [line, node.visibility] : ''; + +const virtual = (node: IFunctionDefinition): Doc => + node.isVirtual ? [line, 'virtual'] : ''; + +const override = ( + node: IFunctionDefinition, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => { + if (!node.override) return ''; + if (node.override.length === 0) return [line, 'override']; + return [ + line, + 'override(', + printSeparatedList(path.map(print, 'override')), + ')' + ]; +}; + +const stateMutability = (node: IFunctionDefinition): Doc => + node.stateMutability ? [line, node.stateMutability] : ''; + +const modifiers = ( + node: IFunctionDefinition, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => + node.modifiers.length > 0 + ? [line, join(line, path.map(print, 'modifiers'))] + : ''; + +const returnParameters = ( + node: IFunctionDefinition, + path: AstPath, + print: (path: AstPath) => Doc, + options: ParserOptions +): Doc => + node.returnParameters + ? [ + line, + 'returns (', + group(parameters('returnParameters', node, path, print, options)), + ')' + ] + : ''; + +const signatureEnd = (node: IFunctionDefinition): Doc => + node.body ? dedent(line) : ';'; + +export const FunctionDefinition: NodePrinter = { + print: ({ node, path, print, options }) => [ + group([ + `${functionName(node, options)}(`, + parameters('parameters', node, path, print, options), + ')', + group( + indent([ + // TODO: sort comments for modifiers and return parameters + printComments(node, path, options), + visibility(node), + stateMutability(node), + virtual(node), + override(node, path, print), + modifiers(node, path, print), + returnParameters(node, path, print, options), + signatureEnd(node) + ]) + ) + ]), + path.call(print, 'body') + ] +}; diff --git a/src/nodes/FunctionTypeName.js b/src/nodes/FunctionTypeName.ts similarity index 59% rename from src/nodes/FunctionTypeName.js rename to src/nodes/FunctionTypeName.ts index 688117fa5..e86b05d5d 100644 --- a/src/nodes/FunctionTypeName.js +++ b/src/nodes/FunctionTypeName.ts @@ -1,9 +1,16 @@ import { doc } from 'prettier'; import { printSeparatedList } from '../common/printer-helpers.js'; +import type { FunctionTypeName as IFunctionTypeName } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indent, line } = doc.builders; -const returnTypes = (node, path, print) => +const returnTypes = ( + node: IFunctionTypeName, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => node.returnTypes.length > 0 ? [ line, @@ -13,23 +20,23 @@ const returnTypes = (node, path, print) => ] : ''; -const visibility = (node) => +const visibility = (node: IFunctionTypeName): Doc => node.visibility && node.visibility !== 'default' ? [line, node.visibility] : ''; -const stateMutability = (node) => +const stateMutability = (node: IFunctionTypeName): Doc => node.stateMutability && node.stateMutability !== 'default' ? [line, node.stateMutability] : ''; -export const FunctionTypeName = { +export const FunctionTypeName: NodePrinter = { print: ({ node, path, print }) => [ 'function(', printSeparatedList(path.map(print, 'parameterTypes')), ')', - indent( - group([ + group( + indent([ visibility(node), stateMutability(node), returnTypes(node, path, print) diff --git a/src/nodes/HexLiteral.js b/src/nodes/HexLiteral.js index 368d7001a..5dd9c8052 100644 --- a/src/nodes/HexLiteral.js +++ b/src/nodes/HexLiteral.js @@ -1,5 +1,5 @@ import { doc } from 'prettier'; -import { printString } from '../common/util.js'; +import { printString } from '../common/util.ts'; const { join, line } = doc.builders; diff --git a/src/nodes/IfStatement.js b/src/nodes/IfStatement.js index 1d9dbc5dc..bf5919128 100644 --- a/src/nodes/IfStatement.js +++ b/src/nodes/IfStatement.js @@ -2,7 +2,7 @@ import { doc } from 'prettier'; import { printComments, printSeparatedItem -} from '../common/printer-helpers.js'; +} from '../common/printer-helpers.ts'; const { group, hardline, indent, line } = doc.builders; diff --git a/src/nodes/ImportDirective.js b/src/nodes/ImportDirective.ts similarity index 77% rename from src/nodes/ImportDirective.js rename to src/nodes/ImportDirective.ts index 225530ff6..50f0062bd 100644 --- a/src/nodes/ImportDirective.js +++ b/src/nodes/ImportDirective.ts @@ -3,25 +3,28 @@ import coerce from 'semver/functions/coerce.js'; import satisfies from 'semver/functions/satisfies.js'; import { printSeparatedList } from '../common/printer-helpers.js'; import { printString } from '../common/util.js'; +import type { ImportDirective as IImportDirective } from '@solidity-parser/parser/src/ast-types'; +import type { Doc } from 'prettier'; +import type { NodePrinter } from './types'; const { group, line, softline } = doc.builders; -export const ImportDirective = { +export const ImportDirective: NodePrinter = { print: ({ node, options }) => { const importPath = printString(node.path, options); - let document; + let document: Doc; if (node.unitAlias) { // import "./Foo.sol" as Foo; - document = [importPath, ' as ', node.unitAlias]; + document = `${importPath} as ${node.unitAlias}`; } else if (node.symbolAliases) { // import { Foo, Bar as Qux } from "./Foo.sol"; const compiler = coerce(options.compiler); const symbolAliases = node.symbolAliases.map(([a, b]) => b ? `${a} as ${b}` : a ); - let firstSeparator; - let separator; + let firstSeparator: Doc; + let separator: Doc; if (compiler && satisfies(compiler, '>=0.7.4')) { // if the compiler exists and is greater than or equal to 0.7.4 we will @@ -38,8 +41,7 @@ export const ImportDirective = { document = [ '{', printSeparatedList(symbolAliases, { firstSeparator, separator }), - '} from ', - importPath + `} from ${importPath}` ]; } else { // import "./Foo.sol"; diff --git a/src/nodes/IndexAccess.js b/src/nodes/IndexAccess.ts similarity index 59% rename from src/nodes/IndexAccess.js rename to src/nodes/IndexAccess.ts index 6838775e7..98cac8876 100644 --- a/src/nodes/IndexAccess.js +++ b/src/nodes/IndexAccess.ts @@ -1,12 +1,16 @@ import { doc } from 'prettier'; +import { isLabel } from '../common/util.js'; +import type { IndexAccess as IIndexAccess } from '@solidity-parser/parser/src/ast-types'; +import type { Doc } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indent, indentIfBreak, label, softline } = doc.builders; let groupIndex = 0; -export const IndexAccess = { +export const IndexAccess: NodePrinter = { print: ({ path, print }) => { let baseDoc = path.call(print, 'base'); - let indexDoc = group([ + let indexDoc: Doc = group([ indent([softline, path.call(print, 'index')]), softline, ']' @@ -14,16 +18,14 @@ export const IndexAccess = { // If we are at the end of a MemberAccessChain we should indent the // arguments accordingly. - if (baseDoc.label === 'MemberAccessChain') { + if (isLabel(baseDoc) && baseDoc.label === 'MemberAccessChain') { baseDoc = group(baseDoc.contents, { - id: `IndexAccess.base-${groupIndex}` + id: Symbol(`IndexAccess.base-${groupIndex}`) }); groupIndex += 1; - indexDoc = indentIfBreak(indexDoc, { - groupId: baseDoc.id - }); + indexDoc = indentIfBreak(indexDoc, { groupId: baseDoc.id! }); // We wrap the expression in a label in case there is an IndexAccess or // a FunctionCall following this IndexAccess. return label('MemberAccessChain', [baseDoc, '[', indexDoc]); diff --git a/src/nodes/InheritanceSpecifier.js b/src/nodes/InheritanceSpecifier.js index 6227ac8cb..0f752b83a 100644 --- a/src/nodes/InheritanceSpecifier.js +++ b/src/nodes/InheritanceSpecifier.js @@ -1,4 +1,4 @@ -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const printArguments = (node, path, print) => node.arguments?.length diff --git a/src/nodes/InlineAssemblyStatement.js b/src/nodes/InlineAssemblyStatement.ts similarity index 55% rename from src/nodes/InlineAssemblyStatement.js rename to src/nodes/InlineAssemblyStatement.ts index 7d09c9d9e..81a866b4f 100644 --- a/src/nodes/InlineAssemblyStatement.js +++ b/src/nodes/InlineAssemblyStatement.ts @@ -1,12 +1,13 @@ // @TODO: add support for assembly language specifier import { printString } from '../common/util.js'; import { printSeparatedList } from '../common/printer-helpers.js'; +import type { InlineAssemblyStatement as IInlineAssemblyStatement } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; -export const InlineAssemblyStatement = { +export const InlineAssemblyStatement: NodePrinter = { print: ({ node, path, print, options }) => [ - 'assembly ', - node.language ? `${printString(node.language, options)} ` : '', - node.flags?.length > 0 + `assembly ${node.language ? `${printString(node.language, options)} ` : ''}`, + node.flags.length > 0 ? [ '(', printSeparatedList( diff --git a/src/nodes/LabelDefinition.js b/src/nodes/LabelDefinition.js index bff22d2c2..6b825211e 100644 --- a/src/nodes/LabelDefinition.js +++ b/src/nodes/LabelDefinition.js @@ -3,5 +3,5 @@ import { doc } from 'prettier'; const { dedent, line } = doc.builders; export const LabelDefinition = { - print: ({ node }) => [dedent(line), node.name, ':'] + print: ({ node }) => [dedent(line), `${node.name}:`] }; diff --git a/src/nodes/Mapping.js b/src/nodes/Mapping.ts similarity index 50% rename from src/nodes/Mapping.js rename to src/nodes/Mapping.ts index 03c619489..f9ab17b3d 100644 --- a/src/nodes/Mapping.js +++ b/src/nodes/Mapping.ts @@ -1,4 +1,13 @@ -const namedParameter = (prefix, node, path, print) => +import type { Mapping as IMapping } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc } from 'prettier'; +import type { NodePrinter } from './types'; + +const namedParameter = ( + prefix: 'key' | 'value', + node: IMapping, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => node[`${prefix}Name`] ? [ path.call(print, `${prefix}Type`), @@ -7,7 +16,7 @@ const namedParameter = (prefix, node, path, print) => ] : path.call(print, `${prefix}Type`); -export const Mapping = { +export const Mapping: NodePrinter = { print: ({ node, path, print }) => [ 'mapping(', namedParameter('key', node, path, print), diff --git a/src/nodes/MemberAccess.js b/src/nodes/MemberAccess.ts similarity index 82% rename from src/nodes/MemberAccess.js rename to src/nodes/MemberAccess.ts index 028daa630..9b7aff797 100644 --- a/src/nodes/MemberAccess.js +++ b/src/nodes/MemberAccess.ts @@ -1,11 +1,18 @@ import { doc } from 'prettier'; +import { isLabel } from '../common/util.js'; +import type { + ASTNode, + MemberAccess as IMemberAccess +} from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indent, label, softline } = doc.builders; -const isEndOfChain = (node, path) => { +const isEndOfChain = (node: IMemberAccess, path: AstPath): boolean => { let i = 0; - let currentNode = node; - let parentNode = path.getParentNode(i); + let currentNode: ASTNode = node; + let parentNode: ASTNode | null = path.getParentNode(i); while ( parentNode && [ @@ -91,9 +98,9 @@ const isEndOfChain = (node, path) => { * @returns a processed doc[] with the proper grouping and indentation ready to * be printed. */ -const processChain = (chain) => { +const processChain = (chain: Doc[]): Doc => { const firstSeparatorIndex = chain.findIndex( - (element) => element.label === 'separator' + (element) => isLabel(element) && element.label === 'separator' ); // The doc[] before the first separator const firstExpression = chain.slice(0, firstSeparatorIndex); @@ -102,17 +109,17 @@ const processChain = (chain) => { // We wrap the expression in a label in case there is an IndexAccess or // a FunctionCall following this MemberAccess. - return label('MemberAccessChain', group([firstExpression, restOfChain])); + return label('MemberAccessChain', [firstExpression, restOfChain]); }; -export const MemberAccess = { +export const MemberAccess: NodePrinter = { print: ({ node, path, print }) => { let expressionDoc = path.call(print, 'expression'); if (Array.isArray(expressionDoc)) { expressionDoc = expressionDoc.flat(); } - const document = [ + const document: Doc[] = [ expressionDoc, label('separator', [softline, '.']), node.memberName diff --git a/src/nodes/ModifierDefinition.js b/src/nodes/ModifierDefinition.js index 296f2bbe4..4b41392fa 100644 --- a/src/nodes/ModifierDefinition.js +++ b/src/nodes/ModifierDefinition.js @@ -1,20 +1,19 @@ import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const { group, hardline, indent, line } = doc.builders; const modifierParameters = (node, path, print) => { if (node.parameters?.length > 0) { + // To keep consistency any list of parameters will split if it's longer than 2. + // For more information see: + // https://github.com/prettier-solidity/prettier-plugin-solidity/issues/256 + const shouldBreak = node.parameters.length > 2; return [ '(', printSeparatedList(path.map(print, 'parameters'), { - separator: [ - ',', - // To keep consistency any list of parameters will split if it's longer than 2. - // For more information see: - // https://github.com/prettier-solidity/prettier-plugin-solidity/issues/256 - node.parameters.length > 2 ? hardline : line - ] + separator: [',', shouldBreak ? hardline : line], + grouped: !shouldBreak }), ')' ]; @@ -36,16 +35,12 @@ const override = (node, path, print) => { ]; }; -const body = (node, path, print) => { - if (!node.body) return ';'; - if (node.isVirtual) return group([' ', path.call(print, 'body')]); - return [' ', path.call(print, 'body')]; -}; +const body = (node, path, print) => + node.body ? [' ', path.call(print, 'body')] : ';'; export const ModifierDefinition = { print: ({ node, path, print }) => [ - 'modifier ', - node.name, + `modifier ${node.name}`, modifierParameters(node, path, print), group(indent([virtual(node), override(node, path, print)])), body(node, path, print) diff --git a/src/nodes/ModifierInvocation.js b/src/nodes/ModifierInvocation.ts similarity index 55% rename from src/nodes/ModifierInvocation.js rename to src/nodes/ModifierInvocation.ts index afe36e918..248426ea3 100644 --- a/src/nodes/ModifierInvocation.js +++ b/src/nodes/ModifierInvocation.ts @@ -2,8 +2,16 @@ import { printComments, printSeparatedList } from '../common/printer-helpers.js'; +import type { ModifierInvocation as IModifierInvocation } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { NodePrinter } from './types'; -const modifierArguments = (node, path, print, options) => { +const modifierArguments = ( + node: IModifierInvocation, + path: AstPath, + print: (path: AstPath) => Doc, + options: ParserOptions +): Doc => { if (node.arguments) { // We always print parentheses at this stage because the parser already // stripped them in FunctionDefinitions that are not a constructor. @@ -12,21 +20,18 @@ const modifierArguments = (node, path, print, options) => { : '()'; } - if ( - node.comments?.some( - (comment) => !comment.leading && !comment.trailing && !comment.printed - ) - ) { + const comments = printComments(node, path, options); + if (comments.length) { // We print parentheses here because the comment is supposed to be a block // comment inside empty parentheses. // modifier(/* comment */) - return ['(', printComments(node, path, options), ')']; + return ['(', comments, ')']; } return ''; }; -export const ModifierInvocation = { +export const ModifierInvocation: NodePrinter = { print: ({ node, path, print, options }) => [ node.name, modifierArguments(node, path, print, options) diff --git a/src/nodes/NameValueList.js b/src/nodes/NameValueList.ts similarity index 55% rename from src/nodes/NameValueList.js rename to src/nodes/NameValueList.ts index 0c97ace30..da92d468a 100644 --- a/src/nodes/NameValueList.js +++ b/src/nodes/NameValueList.ts @@ -1,16 +1,16 @@ import { doc } from 'prettier'; import { printSeparatedList } from '../common/printer-helpers.js'; +import type { NameValueList as INameValueList } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { line, softline } = doc.builders; -export const NameValueList = { +export const NameValueList: NodePrinter = { print: ({ node, path, print, options }) => printSeparatedList( path .map(print, 'arguments') .map((argument, index) => [node.names[index], ': ', argument]), - { - firstSeparator: options.bracketSpacing ? line : softline - } + { firstSeparator: options.bracketSpacing ? line : softline } ) }; diff --git a/src/nodes/NumberLiteral.js b/src/nodes/NumberLiteral.js index d86c8c621..d0351faf4 100644 --- a/src/nodes/NumberLiteral.js +++ b/src/nodes/NumberLiteral.js @@ -1,6 +1,6 @@ export const NumberLiteral = { print: ({ node }) => node.subdenomination - ? [node.number, ' ', node.subdenomination] + ? `${node.number} ${node.subdenomination}` : node.number }; diff --git a/src/nodes/PragmaDirective.js b/src/nodes/PragmaDirective.js index a05c5da8a..7c6fa71f7 100644 --- a/src/nodes/PragmaDirective.js +++ b/src/nodes/PragmaDirective.js @@ -1,3 +1,3 @@ export const PragmaDirective = { - print: ({ node }) => ['pragma ', node.name, ' ', node.value, ';'] + print: ({ node }) => `pragma ${node.name} ${node.value};` }; diff --git a/src/nodes/ReturnStatement.js b/src/nodes/ReturnStatement.ts similarity index 55% rename from src/nodes/ReturnStatement.js rename to src/nodes/ReturnStatement.ts index fd8f1ad86..a0645b635 100644 --- a/src/nodes/ReturnStatement.js +++ b/src/nodes/ReturnStatement.ts @@ -1,8 +1,16 @@ import { doc } from 'prettier'; +import type { ReturnStatement as IReturnStatement } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc, ParserOptions } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indent, line } = doc.builders; -const expression = (node, path, print, options) => { +const expression = ( + node: IReturnStatement, + path: AstPath, + print: (path: AstPath) => Doc, + options: ParserOptions +): Doc => { if (node.expression) { return node.expression.type === 'TupleExpression' || (options.experimentalTernaries && node.expression.type === 'Conditional') @@ -12,7 +20,7 @@ const expression = (node, path, print, options) => { return ''; }; -export const ReturnStatement = { +export const ReturnStatement: NodePrinter = { print: ({ node, path, print, options }) => [ 'return', expression(node, path, print, options), diff --git a/src/nodes/SourceUnit.js b/src/nodes/SourceUnit.ts similarity index 60% rename from src/nodes/SourceUnit.js rename to src/nodes/SourceUnit.ts index 413f97393..7c3fbaa46 100644 --- a/src/nodes/SourceUnit.js +++ b/src/nodes/SourceUnit.ts @@ -1,9 +1,11 @@ import { doc } from 'prettier'; import { printPreservingEmptyLines } from '../common/printer-helpers.js'; +import type { SourceUnit as ISourceUnit } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { line } = doc.builders; -export const SourceUnit = { +export const SourceUnit: NodePrinter = { print: ({ options, path, print }) => [ printPreservingEmptyLines(path, 'children', options, print), options.parentParser ? '' : line diff --git a/src/nodes/StateVariableDeclaration.js b/src/nodes/StateVariableDeclaration.js index e9c6fb07a..1d4e30dd8 100644 --- a/src/nodes/StateVariableDeclaration.js +++ b/src/nodes/StateVariableDeclaration.js @@ -16,7 +16,7 @@ const initialValue = (node, path, print) => { export const StateVariableDeclaration = { print: ({ node, path, print }) => [ - ...path.map(print, 'variables'), + path.map(print, 'variables'), initialValue(node, path, print), ';' ] diff --git a/src/nodes/StringLiteral.js b/src/nodes/StringLiteral.ts similarity index 59% rename from src/nodes/StringLiteral.js rename to src/nodes/StringLiteral.ts index 112e498b4..9bf686fe0 100644 --- a/src/nodes/StringLiteral.js +++ b/src/nodes/StringLiteral.ts @@ -1,15 +1,17 @@ import { doc } from 'prettier'; import { printString } from '../common/util.js'; +import type { StringLiteral as IStringLiteral } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { join, hardline } = doc.builders; -export const StringLiteral = { +export const StringLiteral: NodePrinter = { print: ({ node, options }) => { const list = node.parts.map( (part, index) => // node.isUnicode is an array of the same length as node.parts // that indicates if that string fragment has the unicode prefix - (node.isUnicode[index] ? 'unicode' : '') + printString(part, options) + `${node.isUnicode[index] ? 'unicode' : ''}${printString(part, options)}` ); return join(hardline, list); diff --git a/src/nodes/StructDefinition.js b/src/nodes/StructDefinition.ts similarity index 54% rename from src/nodes/StructDefinition.js rename to src/nodes/StructDefinition.ts index 858eb6366..7ad8aa351 100644 --- a/src/nodes/StructDefinition.js +++ b/src/nodes/StructDefinition.ts @@ -1,18 +1,19 @@ import { doc } from 'prettier'; import { printSeparatedList } from '../common/printer-helpers.js'; +import type { StructDefinition as IStructDefinition } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; const { hardline } = doc.builders; -export const StructDefinition = { +export const StructDefinition: NodePrinter = { print: ({ node, path, print }) => [ - 'struct ', - node.name, - ' {', + `struct ${node.name} {`, node.members.length > 0 ? printSeparatedList(path.map(print, 'members'), { firstSeparator: hardline, separator: [';', hardline], - lastSeparator: [';', hardline] + lastSeparator: [';', hardline], + grouped: false }) : '', '}' diff --git a/src/nodes/TryStatement.js b/src/nodes/TryStatement.js index 44688d713..197d79994 100644 --- a/src/nodes/TryStatement.js +++ b/src/nodes/TryStatement.js @@ -2,7 +2,7 @@ import { doc } from 'prettier'; import { printSeparatedItem, printSeparatedList -} from '../common/printer-helpers.js'; +} from '../common/printer-helpers.ts'; const { join, line } = doc.builders; @@ -11,30 +11,19 @@ const returnParameters = (node, path, print) => ? [ 'returns (', printSeparatedList(path.map(print, 'returnParameters')), - ')' + ') ' ] : ''; export const TryStatement = { - print: ({ node, path, print }) => { - let parts = [ - 'try', - printSeparatedItem(path.call(print, 'expression'), { - firstSeparator: line - }) - ]; - - const formattedReturnParameters = returnParameters(node, path, print); - if (formattedReturnParameters) { - parts = parts.concat([formattedReturnParameters, ' ']); - } - - parts = parts.concat([ - path.call(print, 'body'), - ' ', - join(' ', path.map(print, 'catchClauses')) - ]); - - return parts; - } + print: ({ node, path, print }) => [ + 'try', + printSeparatedItem(path.call(print, 'expression'), { + firstSeparator: line + }), + returnParameters(node, path, print), + path.call(print, 'body'), + ' ', + join(' ', path.map(print, 'catchClauses')) + ] }; diff --git a/src/nodes/TupleExpression.js b/src/nodes/TupleExpression.js index 587bc4e34..5cd6e540c 100644 --- a/src/nodes/TupleExpression.js +++ b/src/nodes/TupleExpression.js @@ -1,7 +1,4 @@ -import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; - -const { group } = doc.builders; +import { printSeparatedList } from '../common/printer-helpers.ts'; const contents = (node, path, print) => node.components?.length === 1 && node.components[0].type === 'BinaryOperation' @@ -9,10 +6,9 @@ const contents = (node, path, print) => : printSeparatedList(path.map(print, 'components')); export const TupleExpression = { - print: ({ node, path, print }) => - group([ - node.isArray ? '[' : '(', - contents(node, path, print), - node.isArray ? ']' : ')' - ]) + print: ({ node, path, print }) => [ + node.isArray ? '[' : '(', + contents(node, path, print), + node.isArray ? ']' : ')' + ] }; diff --git a/src/nodes/TypeDefinition.js b/src/nodes/TypeDefinition.js index 99ac8a43c..cd1ef5e7f 100644 --- a/src/nodes/TypeDefinition.js +++ b/src/nodes/TypeDefinition.js @@ -1,3 +1,3 @@ export const TypeDefinition = { - print: ({ node }) => ['type ', node.name, ' is ', node.definition.name, ';'] + print: ({ node }) => `type ${node.name} is ${node.definition.name};` }; diff --git a/src/nodes/UnaryOperation.js b/src/nodes/UnaryOperation.ts similarity index 55% rename from src/nodes/UnaryOperation.js rename to src/nodes/UnaryOperation.ts index 05dc3809c..c84d40043 100644 --- a/src/nodes/UnaryOperation.js +++ b/src/nodes/UnaryOperation.ts @@ -1,4 +1,7 @@ -export const UnaryOperation = { +import type { UnaryOperation as IUnaryOperation } from '@solidity-parser/parser/src/ast-types'; +import type { NodePrinter } from './types'; + +export const UnaryOperation: NodePrinter = { print: ({ node, path, print }) => node.isPrefix ? [ diff --git a/src/nodes/UncheckedStatement.js b/src/nodes/UncheckedStatement.js index 4d8ca31a5..c6bfd3b9b 100644 --- a/src/nodes/UncheckedStatement.js +++ b/src/nodes/UncheckedStatement.js @@ -1,7 +1,3 @@ -import { doc } from 'prettier'; - -const { group } = doc.builders; - export const UncheckedStatement = { - print: ({ path, print }) => group(['unchecked ', path.call(print, 'block')]) + print: ({ path, print }) => ['unchecked ', path.call(print, 'block')] }; diff --git a/src/nodes/UsingForDeclaration.js b/src/nodes/UsingForDeclaration.js index e4e48d2ec..f209fc714 100644 --- a/src/nodes/UsingForDeclaration.js +++ b/src/nodes/UsingForDeclaration.js @@ -1,5 +1,5 @@ import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const { line, softline } = doc.builders; @@ -12,7 +12,7 @@ export const UsingForDeclaration = { printSeparatedList( node.functions.map((functionName, i) => node.operators[i] - ? [functionName, ' as ', node.operators[i]] + ? `${functionName} as ${node.operators[i]}` : functionName ), { diff --git a/src/nodes/VariableDeclaration.js b/src/nodes/VariableDeclaration.js index 50bb764dc..cc1a292d7 100644 --- a/src/nodes/VariableDeclaration.js +++ b/src/nodes/VariableDeclaration.js @@ -1,5 +1,5 @@ import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const { group, indent, line } = doc.builders; diff --git a/src/nodes/VariableDeclarationStatement.js b/src/nodes/VariableDeclarationStatement.js index 7f078bf1a..2c25c5136 100644 --- a/src/nodes/VariableDeclarationStatement.js +++ b/src/nodes/VariableDeclarationStatement.js @@ -1,5 +1,5 @@ import { doc } from 'prettier'; -import { printSeparatedList } from '../common/printer-helpers.js'; +import { printSeparatedList } from '../common/printer-helpers.ts'; const { group, indentIfBreak } = doc.builders; diff --git a/src/nodes/WhileStatement.js b/src/nodes/WhileStatement.ts similarity index 54% rename from src/nodes/WhileStatement.js rename to src/nodes/WhileStatement.ts index 778b618e3..d8f2c821e 100644 --- a/src/nodes/WhileStatement.js +++ b/src/nodes/WhileStatement.ts @@ -1,14 +1,21 @@ import { doc } from 'prettier'; import { printSeparatedItem } from '../common/printer-helpers.js'; +import type { WhileStatement as IWhileStatement } from '@solidity-parser/parser/src/ast-types'; +import type { AstPath, Doc } from 'prettier'; +import type { NodePrinter } from './types'; const { group, indent, line } = doc.builders; -const printBody = (node, path, print) => +const printBody = ( + node: IWhileStatement, + path: AstPath, + print: (path: AstPath) => Doc +): Doc => node.body.type === 'Block' ? [' ', path.call(print, 'body')] : group(indent([line, path.call(print, 'body')])); -export const WhileStatement = { +export const WhileStatement: NodePrinter = { print: ({ node, path, print }) => [ 'while (', printSeparatedItem(path.call(print, 'condition')), diff --git a/src/nodes/index.js b/src/nodes/index.js index 179299865..3e7abc988 100644 --- a/src/nodes/index.js +++ b/src/nodes/index.js @@ -1,72 +1,72 @@ export * from './ArrayTypeName.js'; export * from './AssemblyAssignment.js'; -export * from './AssemblyBlock.js'; -export * from './AssemblyCall.js'; +export * from './AssemblyBlock.ts'; +export * from './AssemblyCall.ts'; export * from './AssemblyCase.js'; -export * from './AssemblyFor.js'; +export * from './AssemblyFor.ts'; export * from './AssemblyFunctionDefinition.js'; export * from './AssemblyIf.js'; export * from './AssemblyLocalDefinition.js'; export * from './AssemblyMemberAccess.js'; export * from './AssemblyStackAssignment.js'; -export * from './AssemblySwitch.js'; +export * from './AssemblySwitch.ts'; export * from './BinaryOperation.js'; -export * from './Block.js'; +export * from './Block.ts'; export * from './BooleanLiteral.js'; export * from './Break.js'; export * from './BreakStatement.js'; -export * from './CatchClause.js'; -export * from './Conditional.js'; +export * from './CatchClause.ts'; +export * from './Conditional.ts'; export * from './Continue.js'; export * from './ContinueStatement.js'; -export * from './ContractDefinition.js'; +export * from './ContractDefinition.ts'; export * from './CustomErrorDefinition.js'; export * from './DecimalNumber.js'; -export * from './DoWhileStatement.js'; +export * from './DoWhileStatement.ts'; export * from './ElementaryTypeName.js'; export * from './EmitStatement.js'; -export * from './EnumDefinition.js'; +export * from './EnumDefinition.ts'; export * from './EnumValue.js'; export * from './EventDefinition.js'; export * from './ExpressionStatement.js'; export * from './FileLevelConstant.js'; export * from './ForStatement.js'; -export * from './FunctionCall.js'; -export * from './FunctionDefinition.js'; -export * from './FunctionTypeName.js'; +export * from './FunctionCall.ts'; +export * from './FunctionDefinition.ts'; +export * from './FunctionTypeName.ts'; export * from './HexLiteral.js'; export * from './HexNumber.js'; export * from './Identifier.js'; export * from './IfStatement.js'; -export * from './ImportDirective.js'; -export * from './IndexAccess.js'; +export * from './ImportDirective.ts'; +export * from './IndexAccess.ts'; export * from './IndexRangeAccess.js'; export * from './InheritanceSpecifier.js'; -export * from './InlineAssemblyStatement.js'; +export * from './InlineAssemblyStatement.ts'; export * from './LabelDefinition.js'; -export * from './Mapping.js'; -export * from './MemberAccess.js'; +export * from './Mapping.ts'; +export * from './MemberAccess.ts'; export * from './ModifierDefinition.js'; -export * from './ModifierInvocation.js'; +export * from './ModifierInvocation.ts'; export * from './NameValueExpression.js'; -export * from './NameValueList.js'; +export * from './NameValueList.ts'; export * from './NewExpression.js'; export * from './NumberLiteral.js'; export * from './PragmaDirective.js'; -export * from './ReturnStatement.js'; +export * from './ReturnStatement.ts'; export * from './RevertStatement.js'; -export * from './SourceUnit.js'; +export * from './SourceUnit.ts'; export * from './StateVariableDeclaration.js'; -export * from './StringLiteral.js'; -export * from './StructDefinition.js'; +export * from './StringLiteral.ts'; +export * from './StructDefinition.ts'; export * from './ThrowStatement.js'; export * from './TryStatement.js'; export * from './TupleExpression.js'; export * from './TypeDefinition.js'; -export * from './UnaryOperation.js'; +export * from './UnaryOperation.ts'; export * from './UncheckedStatement.js'; export * from './UserDefinedTypeName.js'; export * from './UsingForDeclaration.js'; export * from './VariableDeclaration.js'; export * from './VariableDeclarationStatement.js'; -export * from './WhileStatement.js'; +export * from './WhileStatement.ts'; diff --git a/src/nodes/types.d.ts b/src/nodes/types.d.ts new file mode 100644 index 000000000..6af01b35a --- /dev/null +++ b/src/nodes/types.d.ts @@ -0,0 +1,10 @@ +import type { AstPath, Doc, ParserOptions } from 'prettier'; + +interface NodePrinter { + print(arg: { + node: T; + options: ParserOptions; + path: AstPath; + print: (path: AstPath) => Doc; + }): Doc; +} diff --git a/src/options.js b/src/options.ts similarity index 94% rename from src/options.js rename to src/options.ts index 34fa5a44d..cf8a4e49c 100644 --- a/src/options.js +++ b/src/options.ts @@ -1,11 +1,12 @@ +import type { SupportOptions } from 'prettier'; + const CATEGORY_GLOBAL = 'Global'; const CATEGORY_COMMON = 'Common'; const CATEGORY_JAVASCRIPT = 'JavaScript'; const CATEGORY_SOLIDITY = 'Solidity'; -const options = { +const options: SupportOptions = { printWidth: { - since: '0.0.0', category: CATEGORY_GLOBAL, type: 'int', default: 80, @@ -20,14 +21,12 @@ const options = { range: { start: 0, end: Number.POSITIVE_INFINITY, step: 1 } }, useTabs: { - since: '1.0.0', category: CATEGORY_GLOBAL, type: 'boolean', default: false, description: 'Indent with tabs instead of spaces.' }, bracketSpacing: { - since: '0.0.0', category: CATEGORY_COMMON, type: 'boolean', default: true, @@ -35,7 +34,6 @@ const options = { oppositeDescription: 'Do not print spaces between brackets.' }, singleQuote: { - since: '0.0.0', category: CATEGORY_COMMON, type: 'boolean', default: false, diff --git a/src/parser.js b/src/parser.ts similarity index 64% rename from src/parser.js rename to src/parser.ts index e2a119245..cd4fde117 100644 --- a/src/parser.js +++ b/src/parser.ts @@ -2,37 +2,49 @@ import parser from '@solidity-parser/parser'; import coerce from 'semver/functions/coerce.js'; import satisfies from 'semver/functions/satisfies.js'; +import type { + BinOp, + BinaryOperation, + Expression, + ForStatement, + SourceUnit +} from '@solidity-parser/parser/src/ast-types'; +import type { Parser, ParserOptions } from 'prettier'; -const tryHug = (node, operators) => { +const tryHug = (node: Expression, operators: BinOp[]): Expression => { if (node.type === 'BinaryOperation' && operators.includes(node.operator)) - return { - type: 'TupleExpression', - components: [node], - isArray: false - }; + return { type: 'TupleExpression', components: [node], isArray: false }; return node; }; -function parse(text, _parsers, options = _parsers) { +export default function parse( + text: string, + _parsers: Parser[] | ParserOptions, + options = _parsers as ParserOptions +): SourceUnit { const compiler = coerce(options.compiler); - const parsed = parser.parse(text, { loc: true, range: true, comments: true }); + const parsed: SourceUnit = parser.parse(text, { + loc: true, + range: true, + comments: true + }); parser.visit(parsed, { - PragmaDirective(ctx) { - // if the pragma is not for solidity we leave. - if (ctx.name !== 'solidity') return; - // if the compiler option has not been provided we leave. - if (!compiler) return; - // we make a check against each pragma directive in the document. - if (!satisfies(compiler, ctx.value)) { - // @TODO: investigate the best way to warn that would apply to - // different editors. - // eslint-disable-next-line no-console - console.warn( - `[prettier-solidity] The compiler option is set to '${options.compiler}', which does not satisfy 'pragma solidity ${ctx.value}'.` - ); - } - }, + PragmaDirective: !compiler + ? undefined // if the compiler option has not been provided we don't define it. + : (ctx): void => { + // if the pragma is not for solidity we leave. + if (ctx.name !== 'solidity') return; + // if the compiler option has not been provided we leave. + if (!satisfies(compiler, ctx.value)) { + // @TODO: investigate the best way to warn that would apply to + // different editors. + // eslint-disable-next-line no-console + console.warn( + `[prettier-solidity] The compiler option is set to '${compiler}', which does not satisfy 'pragma solidity ${ctx.value}'.` + ); + } + }, ModifierDefinition(ctx) { if (!ctx.parameters) { ctx.parameters = []; @@ -48,32 +60,33 @@ function parse(text, _parsers, options = _parsers) { }); } }, - ForStatement(ctx) { + ForStatement(ctx: ForStatement) { if (ctx.initExpression) { ctx.initExpression.omitSemicolon = true; } ctx.loopExpression.omitSemicolon = true; }, HexLiteral(ctx) { - 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; - } - } + ctx.value = options.singleQuote + ? `hex'${ctx.value.slice(4, -1)}'` + : `hex"${ctx.value.slice(4, -1)}"`; }, + // TODO: while the behaviour is not stable, it should be behind the + // experimentalTernaries flag. + Conditional: !options.experimentalTernaries + ? undefined + : (ctx): void => { + // 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[0] as Expression; + } + }, BinaryOperation(ctx) { switch (ctx.operator) { case '+': @@ -107,7 +120,7 @@ function parse(text, _parsers, options = _parsers) { ) { // the parser organizes the a**b**c as a**(b**c) so we need to // restructure it. - const left = { + const left: BinaryOperation = { type: 'BinaryOperation', operator: '**', left: ctx.left, @@ -189,5 +202,3 @@ function parse(text, _parsers, options = _parsers) { return parsed; } - -export default parse; diff --git a/src/prettier-comments/language-js/comments.js b/src/prettier-comments/language-js/comments.ts similarity index 55% rename from src/prettier-comments/language-js/comments.js rename to src/prettier-comments/language-js/comments.ts index 7b0003828..56d8ae254 100644 --- a/src/prettier-comments/language-js/comments.js +++ b/src/prettier-comments/language-js/comments.ts @@ -1,5 +1,11 @@ -import { util } from "prettier"; -import { getNextNonSpaceNonCommentCharacter } from "../../common/backward-compatibility.js"; +import { util } from 'prettier'; +import { + getNextNonSpaceNonCommentCharacter, + getNextNonSpaceNonCommentCharacterIndex +} from '../../common/backward-compatibility.js'; +import { locEnd, locStart } from '../../common/util.js'; +import type { Comment, SourceUnit } from '@solidity-parser/parser/src/ast-types'; +import type { AST } from 'prettier'; const { addLeadingComment, @@ -7,9 +13,14 @@ const { addDanglingComment, hasNewline, hasNewlineInRange -} = util - -export function handleOwnLineComment(comment, text, options, ast, isLastComment) { +} = util; + +export function handleOwnLineComment( + comment: Comment, + text: string, + ast: SourceUnit, + isLastComment: boolean +): boolean { const { precedingNode, enclosingNode, followingNode } = comment; if ( handleLastFunctionArgComments( @@ -17,8 +28,7 @@ export function handleOwnLineComment(comment, text, options, ast, isLastComment) precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleMemberExpressionComments(enclosingNode, followingNode, comment) || handleIfStatementComments( @@ -26,16 +36,14 @@ export function handleOwnLineComment(comment, text, options, ast, isLastComment) precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleWhileComments( text, precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleTryStatementComments( enclosingNode, @@ -57,24 +65,22 @@ export function handleOwnLineComment(comment, text, options, ast, isLastComment) text, enclosingNode, precedingNode, - comment, - options + comment ) || handleAssignmentPatternComments(enclosingNode, comment) || - handleMethodNameComments( - text, - enclosingNode, - precedingNode, - comment, - options - ) + handleMethodNameComments(text, enclosingNode, precedingNode, comment) ) { return true; } return false; } -export function handleEndOfLineComment(comment, text, options, ast, isLastComment) { +export function handleEndOfLineComment( + comment: Comment, + text: string, + ast: SourceUnit, + isLastComment: boolean +): boolean { const { precedingNode, enclosingNode, followingNode } = comment; if ( handleLastFunctionArgComments( @@ -82,16 +88,14 @@ export function handleEndOfLineComment(comment, text, options, ast, isLastCommen precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleConditionalExpressionComments( enclosingNode, precedingNode, followingNode, comment, - text, - options + text ) || handleImportSpecifierComments(enclosingNode, comment) || handleIfStatementComments( @@ -99,16 +103,14 @@ export function handleEndOfLineComment(comment, text, options, ast, isLastCommen precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleWhileComments( text, precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleTryStatementComments( enclosingNode, @@ -129,7 +131,12 @@ export function handleEndOfLineComment(comment, text, options, ast, isLastCommen return false; } -export function handleRemainingComment(comment, text, options, ast, isLastComment) { +export function handleRemainingComment( + comment: Comment, + text: string, + ast: SourceUnit, + isLastComment: boolean +): boolean { const { precedingNode, enclosingNode, followingNode } = comment; if ( @@ -138,35 +145,21 @@ export function handleRemainingComment(comment, text, options, ast, isLastCommen precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleWhileComments( text, precedingNode, enclosingNode, followingNode, - comment, - options + comment ) || handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) || - handleCommentInEmptyParens(text, enclosingNode, comment, options) || - handleMethodNameComments( - text, - enclosingNode, - precedingNode, - comment, - options - ) || + handleCommentInEmptyParens(text, enclosingNode, comment) || + handleMethodNameComments(text, enclosingNode, precedingNode, comment) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) || - handleCommentAfterArrowParams(text, enclosingNode, comment, options) || - handleFunctionNameComments( - text, - enclosingNode, - precedingNode, - comment, - options - ) || + handleCommentAfterArrowParams(text, enclosingNode, comment) || + handleFunctionNameComments(text, enclosingNode, precedingNode, comment) || handleTSMappedTypeComments( text, enclosingNode, @@ -181,22 +174,22 @@ export function handleRemainingComment(comment, text, options, ast, isLastCommen return false; } -function addBlockStatementFirstComment(node, comment) { +function addBlockStatementFirstComment(node: AST, comment: Comment): void { if (!node.body) { - addDanglingComment(node, comment); + addDanglingComment(node, comment, false); return; } - const body = node.body.filter(n => n.type !== "EmptyStatement"); + const body = node.body.filter((n: AST) => n.type !== 'EmptyStatement'); if (body.length === 0) { - addDanglingComment(node, comment); + addDanglingComment(node, comment, false); } else { addLeadingComment(body[0], comment); } } -function addBlockOrNotComment(node, comment) { - if (node.type === "BlockStatement") { +function addBlockOrNotComment(node: AST, comment: Comment): void { + if (node.type === 'BlockStatement') { addBlockStatementFirstComment(node, comment); } else { addLeadingComment(node, comment); @@ -220,16 +213,15 @@ function addBlockOrNotComment(node, comment) { // ... // } function handleIfStatementComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment, - options -) { + text: string, + precedingNode: AST, + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( !enclosingNode || - enclosingNode.type !== "IfStatement" || + enclosingNode.type !== 'IfStatement' || !followingNode ) { return false; @@ -243,9 +235,9 @@ function handleIfStatementComments( const nextCharacter = getNextNonSpaceNonCommentCharacter( text, comment, - options.locEnd + locEnd ); - if (nextCharacter === ")") { + if (nextCharacter === ')') { addTrailingComment(precedingNode, comment); return true; } @@ -257,20 +249,20 @@ function handleIfStatementComments( precedingNode === enclosingNode.trueBody && followingNode === enclosingNode.falseBody ) { - if (precedingNode.type === "ExpressionStatement") { + if (precedingNode.type === 'ExpressionStatement') { addTrailingComment(precedingNode, comment); } else { - addDanglingComment(enclosingNode, comment); + addDanglingComment(enclosingNode, comment, false); } return true; } - if (followingNode.type === "ExpressionStatement") { + if (followingNode.type === 'ExpressionStatement') { addBlockStatementFirstComment(followingNode, comment); return true; } - if (followingNode.type === "IfStatement") { + if (followingNode.type === 'IfStatement') { addBlockOrNotComment(followingNode.trueBody, comment); return true; } @@ -289,16 +281,15 @@ function handleIfStatementComments( } function handleWhileComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment, - options -) { + text: string, + precedingNode: AST, + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( !enclosingNode || - enclosingNode.type !== "WhileStatement" || + enclosingNode.type !== 'WhileStatement' || !followingNode ) { return false; @@ -312,14 +303,14 @@ function handleWhileComments( const nextCharacter = getNextNonSpaceNonCommentCharacter( text, comment, - options.locEnd + locEnd ); - if (nextCharacter === ")") { + if (nextCharacter === ')') { addTrailingComment(precedingNode, comment); return true; } - if (followingNode.type === "BlockStatement") { + if (followingNode.type === 'BlockStatement') { addBlockStatementFirstComment(followingNode, comment); return true; } @@ -329,36 +320,36 @@ function handleWhileComments( // Same as IfStatement but for TryStatement function handleTryStatementComments( - enclosingNode, - precedingNode, - followingNode, - comment -) { + enclosingNode: AST, + precedingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( !enclosingNode || - (enclosingNode.type !== "TryStatement" && - enclosingNode.type !== "CatchClause") || + (enclosingNode.type !== 'TryStatement' && + enclosingNode.type !== 'CatchClause') || !followingNode ) { return false; } - if (enclosingNode.type === "CatchClause" && precedingNode) { + if (enclosingNode.type === 'CatchClause' && precedingNode) { addTrailingComment(precedingNode, comment); return true; } - if (followingNode.type === "BlockStatement") { + if (followingNode.type === 'BlockStatement') { addBlockStatementFirstComment(followingNode, comment); return true; } - if (followingNode.type === "TryStatement") { + if (followingNode.type === 'TryStatement') { addBlockOrNotComment(followingNode.finalizer, comment); return true; } - if (followingNode.type === "CatchClause") { + if (followingNode.type === 'CatchClause') { addBlockOrNotComment(followingNode.body, comment); return true; } @@ -366,12 +357,16 @@ function handleTryStatementComments( return false; } -function handleMemberExpressionComments(enclosingNode, followingNode, comment) { +function handleMemberExpressionComments( + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - enclosingNode.type === "MemberExpression" && + enclosingNode.type === 'MemberExpression' && followingNode && - followingNode.type === "Identifier" + followingNode.type === 'Identifier' ) { addLeadingComment(enclosingNode, comment); return true; @@ -381,25 +376,20 @@ function handleMemberExpressionComments(enclosingNode, followingNode, comment) { } function handleConditionalExpressionComments( - enclosingNode, - precedingNode, - followingNode, - comment, - text, - options -) { + enclosingNode: AST, + precedingNode: AST, + followingNode: AST, + comment: Comment, + text: string +): boolean { const isSameLineAsPrecedingNode = precedingNode && - !hasNewlineInRange( - text, - options.locEnd(precedingNode), - options.locStart(comment) - ); + !hasNewlineInRange(text, locEnd(precedingNode), locStart(comment)); if ( (!precedingNode || !isSameLineAsPrecedingNode) && enclosingNode && - enclosingNode.type === "ConditionalExpression" && + enclosingNode.type === 'ConditionalExpression' && followingNode ) { addLeadingComment(followingNode, comment); @@ -408,14 +398,18 @@ function handleConditionalExpressionComments( return false; } -function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { +function handleObjectPropertyAssignment( + enclosingNode: AST, + precedingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - (enclosingNode.type === "ObjectProperty" || - enclosingNode.type === "Property") && + (enclosingNode.type === 'ObjectProperty' || + enclosingNode.type === 'Property') && enclosingNode.shorthand && enclosingNode.key === precedingNode && - enclosingNode.value.type === "AssignmentPattern" + enclosingNode.value.type === 'AssignmentPattern' ) { addTrailingComment(enclosingNode.value.left, comment); return true; @@ -424,17 +418,18 @@ function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { } function handleClassComments( - enclosingNode, - precedingNode, - followingNode, - comment -) { + enclosingNode: AST, + precedingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - (enclosingNode.type === "ClassDeclaration" || - enclosingNode.type === "ClassExpression") && - (enclosingNode.decorators && enclosingNode.decorators.length > 0) && - !(followingNode && followingNode.type === "Decorator") + (enclosingNode.type === 'ClassDeclaration' || + enclosingNode.type === 'ClassExpression') && + enclosingNode.decorators && + enclosingNode.decorators.length > 0 && + !(followingNode && followingNode.type === 'Decorator') ) { if (!enclosingNode.decorators || enclosingNode.decorators.length === 0) { addLeadingComment(enclosingNode, comment); @@ -450,29 +445,24 @@ function handleClassComments( } function handleMethodNameComments( - text, - enclosingNode, - precedingNode, - comment, - options -) { + text: string, + enclosingNode: AST, + precedingNode: AST, + comment: Comment +): boolean { // This is only needed for estree parsers (flow, typescript) to attach // after a method name: // obj = { fn /*comment*/() {} }; if ( enclosingNode && precedingNode && - (enclosingNode.type === "Property" || - enclosingNode.type === "MethodDefinition") && - precedingNode.type === "Identifier" && + (enclosingNode.type === 'Property' || + enclosingNode.type === 'MethodDefinition') && + precedingNode.type === 'Identifier' && enclosingNode.key === precedingNode && // special Property case: { key: /*comment*/(value) }; // comment should be attached to value instead of key - getNextNonSpaceNonCommentCharacter( - text, - precedingNode, - options.locEnd - ) !== ":" + getNextNonSpaceNonCommentCharacter(text, precedingNode, locEnd) !== ':' ) { addTrailingComment(precedingNode, comment); return true; @@ -483,12 +473,12 @@ function handleMethodNameComments( if ( precedingNode && enclosingNode && - precedingNode.type === "Decorator" && - (enclosingNode.type === "ClassMethod" || - enclosingNode.type === "ClassProperty" || - enclosingNode.type === "TSAbstractClassProperty" || - enclosingNode.type === "TSAbstractMethodDefinition" || - enclosingNode.type === "MethodDefinition") + precedingNode.type === 'Decorator' && + (enclosingNode.type === 'ClassMethod' || + enclosingNode.type === 'ClassProperty' || + enclosingNode.type === 'TSAbstractClassProperty' || + enclosingNode.type === 'TSAbstractMethodDefinition' || + enclosingNode.type === 'MethodDefinition') ) { addTrailingComment(precedingNode, comment); return true; @@ -498,30 +488,23 @@ function handleMethodNameComments( } function handleFunctionNameComments( - text, - enclosingNode, - precedingNode, - comment, - options -) { - if ( - getNextNonSpaceNonCommentCharacter( - text, - comment, - options.locEnd - ) !== "(" - ) { + text: string, + enclosingNode: AST, + precedingNode: AST, + comment: Comment +): boolean { + if (getNextNonSpaceNonCommentCharacter(text, comment, locEnd) !== '(') { return false; } if ( precedingNode && enclosingNode && - (enclosingNode.type === "FunctionDeclaration" || - enclosingNode.type === "FunctionExpression" || - enclosingNode.type === "ClassMethod" || - enclosingNode.type === "MethodDefinition" || - enclosingNode.type === "ObjectMethod") + (enclosingNode.type === 'FunctionDeclaration' || + enclosingNode.type === 'FunctionExpression' || + enclosingNode.type === 'ClassMethod' || + enclosingNode.type === 'MethodDefinition' || + enclosingNode.type === 'ObjectMethod') ) { addTrailingComment(precedingNode, comment); return true; @@ -529,32 +512,30 @@ function handleFunctionNameComments( return false; } -function handleCommentAfterArrowParams(text, enclosingNode, comment, options) { - if (!(enclosingNode && enclosingNode.type === "ArrowFunctionExpression")) { +function handleCommentAfterArrowParams( + text: string, + enclosingNode: AST, + comment: Comment, +): boolean { + if (!(enclosingNode && enclosingNode.type === 'ArrowFunctionExpression')) { return false; } - const index = getNextNonSpaceNonCommentCharacterIndex( - text, - comment, - options - ); - if (text.substr(index, 2) === "=>") { - addDanglingComment(enclosingNode, comment); + const index = getNextNonSpaceNonCommentCharacterIndex(text, comment, locEnd); + if (index && text.substr(index, 2) === '=>') { + addDanglingComment(enclosingNode, comment, false); return true; } return false; } -function handleCommentInEmptyParens(text, enclosingNode, comment, options) { - if ( - getNextNonSpaceNonCommentCharacter( - text, - comment, - options.locEnd - ) !== ")" - ) { +function handleCommentInEmptyParens( + text: string, + enclosingNode: AST, + comment: Comment +): boolean { + if (getNextNonSpaceNonCommentCharacter(text, comment, locEnd) !== ')') { return false; } @@ -562,48 +543,47 @@ function handleCommentInEmptyParens(text, enclosingNode, comment, options) { // i.e. a function without any argument. if ( enclosingNode && - (((enclosingNode.type === "FunctionDeclaration" || - enclosingNode.type === "FunctionExpression" || - (enclosingNode.type === "ArrowFunctionExpression" && - (enclosingNode.body.type !== "CallExpression" || + (((enclosingNode.type === 'FunctionDeclaration' || + enclosingNode.type === 'FunctionExpression' || + (enclosingNode.type === 'ArrowFunctionExpression' && + (enclosingNode.body.type !== 'CallExpression' || enclosingNode.body.arguments.length === 0)) || - enclosingNode.type === "ClassMethod" || - enclosingNode.type === "ObjectMethod") && + enclosingNode.type === 'ClassMethod' || + enclosingNode.type === 'ObjectMethod') && enclosingNode.params.length === 0) || - ((enclosingNode.type === "CallExpression" || - enclosingNode.type === "NewExpression") && + ((enclosingNode.type === 'CallExpression' || + enclosingNode.type === 'NewExpression') && enclosingNode.arguments.length === 0)) ) { - addDanglingComment(enclosingNode, comment); + addDanglingComment(enclosingNode, comment, false); return true; } if ( enclosingNode && - (enclosingNode.type === "MethodDefinition" && - enclosingNode.value.params.length === 0) + enclosingNode.type === 'MethodDefinition' && + enclosingNode.value.params.length === 0 ) { - addDanglingComment(enclosingNode.value, comment); + addDanglingComment(enclosingNode.value, comment, false); return true; } return false; } function handleLastFunctionArgComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment, - options -) { + text: string, + precedingNode: AST, + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { // Type definitions functions if ( precedingNode && - precedingNode.type === "FunctionTypeParam" && + precedingNode.type === 'FunctionTypeParam' && enclosingNode && - enclosingNode.type === "FunctionTypeAnnotation" && + enclosingNode.type === 'FunctionTypeAnnotation' && followingNode && - followingNode.type !== "FunctionTypeParam" + followingNode.type !== 'FunctionTypeParam' ) { addTrailingComment(precedingNode, comment); return true; @@ -612,19 +592,15 @@ function handleLastFunctionArgComments( // Real functions if ( precedingNode && - (precedingNode.type === "Identifier" || - precedingNode.type === "AssignmentPattern") && + (precedingNode.type === 'Identifier' || + precedingNode.type === 'AssignmentPattern') && enclosingNode && - (enclosingNode.type === "ArrowFunctionExpression" || - enclosingNode.type === "FunctionExpression" || - enclosingNode.type === "FunctionDeclaration" || - enclosingNode.type === "ObjectMethod" || - enclosingNode.type === "ClassMethod") && - getNextNonSpaceNonCommentCharacter( - text, - comment, - options.locEnd - ) === ")" + (enclosingNode.type === 'ArrowFunctionExpression' || + enclosingNode.type === 'FunctionExpression' || + enclosingNode.type === 'FunctionDeclaration' || + enclosingNode.type === 'ObjectMethod' || + enclosingNode.type === 'ClassMethod') && + getNextNonSpaceNonCommentCharacter(text, comment, locEnd) === ')' ) { addTrailingComment(precedingNode, comment); return true; @@ -632,27 +608,36 @@ function handleLastFunctionArgComments( return false; } -function handleImportSpecifierComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "ImportSpecifier") { +function handleImportSpecifierComments( + enclosingNode: AST, + comment: Comment +): boolean { + if (enclosingNode && enclosingNode.type === 'ImportSpecifier') { addLeadingComment(enclosingNode, comment); return true; } return false; } -function handleLabeledStatementComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "LabeledStatement") { +function handleLabeledStatementComments( + enclosingNode: AST, + comment: Comment +): boolean { + if (enclosingNode && enclosingNode.type === 'LabeledStatement') { addLeadingComment(enclosingNode, comment); return true; } return false; } -function handleBreakAndContinueStatementComments(enclosingNode, comment) { +function handleBreakAndContinueStatementComments( + enclosingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - (enclosingNode.type === "ContinueStatement" || - enclosingNode.type === "BreakStatement") && + (enclosingNode.type === 'ContinueStatement' || + enclosingNode.type === 'BreakStatement') && !enclosingNode.label ) { addTrailingComment(enclosingNode, comment); @@ -661,10 +646,14 @@ function handleBreakAndContinueStatementComments(enclosingNode, comment) { return false; } -function handleCallExpressionComments(precedingNode, enclosingNode, comment) { +function handleCallExpressionComments( + precedingNode: AST, + enclosingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - enclosingNode.type === "CallExpression" && + enclosingNode.type === 'CallExpression' && precedingNode && enclosingNode.callee === precedingNode && enclosingNode.arguments.length > 0 @@ -676,15 +665,15 @@ function handleCallExpressionComments(precedingNode, enclosingNode, comment) { } function handleUnionTypeComments( - precedingNode, - enclosingNode, - followingNode, - comment -) { + precedingNode: AST, + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - (enclosingNode.type === "UnionTypeAnnotation" || - enclosingNode.type === "TSUnionType") + (enclosingNode.type === 'UnionTypeAnnotation' || + enclosingNode.type === 'TSUnionType') ) { addTrailingComment(precedingNode, comment); return true; @@ -692,11 +681,11 @@ function handleUnionTypeComments( return false; } -function handlePropertyComments(enclosingNode, comment) { +function handlePropertyComments(enclosingNode: AST, comment: Comment): boolean { if ( enclosingNode && - (enclosingNode.type === "Property" || - enclosingNode.type === "ObjectProperty") + (enclosingNode.type === 'Property' || + enclosingNode.type === 'ObjectProperty') ) { addLeadingComment(enclosingNode, comment); return true; @@ -704,24 +693,29 @@ function handlePropertyComments(enclosingNode, comment) { return false; } -function handleOnlyComments(enclosingNode, ast, comment, isLastComment) { +function handleOnlyComments( + enclosingNode: AST, + ast: SourceUnit, + comment: Comment, + isLastComment: boolean +): boolean { // With Flow the enclosingNode is undefined so use the AST instead. - if (ast && ast.body && ast.body.length === 0) { + if (ast && ast.children && ast.children.length === 0) { if (isLastComment) { - addDanglingComment(ast, comment); + addDanglingComment(ast, comment, false); } else { addLeadingComment(ast, comment); } return true; } else if ( enclosingNode && - enclosingNode.type === "Program" && + enclosingNode.type === 'Program' && enclosingNode.body.length === 0 && enclosingNode.directives && enclosingNode.directives.length === 0 ) { if (isLastComment) { - addDanglingComment(enclosingNode, comment); + addDanglingComment(enclosingNode, comment, false); } else { addLeadingComment(enclosingNode, comment); } @@ -730,11 +724,15 @@ function handleOnlyComments(enclosingNode, ast, comment, isLastComment) { return false; } -function handleForComments(enclosingNode, precedingNode, comment) { +function handleForComments( + enclosingNode: AST, + precedingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - (enclosingNode.type === "ForInStatement" || - enclosingNode.type === "ForOfStatement") + (enclosingNode.type === 'ForInStatement' || + enclosingNode.type === 'ForOfStatement') ) { addLeadingComment(enclosingNode, comment); return true; @@ -743,18 +741,17 @@ function handleForComments(enclosingNode, precedingNode, comment) { } function handleImportDeclarationComments( - text, - enclosingNode, - precedingNode, - comment, - options -) { + text: string, + enclosingNode: AST, + precedingNode: AST, + comment: Comment +): boolean { if ( precedingNode && - precedingNode.type === "ImportSpecifier" && + precedingNode.type === 'ImportSpecifier' && enclosingNode && - enclosingNode.type === "ImportDeclaration" && - hasNewline(text, options.locEnd(comment)) + enclosingNode.type === 'ImportDeclaration' && + hasNewline(text, locEnd(comment)) ) { addTrailingComment(precedingNode, comment); return true; @@ -762,16 +759,23 @@ function handleImportDeclarationComments( return false; } -function handleAssignmentPatternComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "AssignmentPattern") { +function handleAssignmentPatternComments( + enclosingNode: AST, + comment: Comment +): boolean { + if (enclosingNode && enclosingNode.type === 'AssignmentPattern') { addLeadingComment(enclosingNode, comment); return true; } return false; } -function handleTypeAliasComments(enclosingNode, followingNode, comment) { - if (enclosingNode && enclosingNode.type === "TypeAlias") { +function handleTypeAliasComments( + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { + if (enclosingNode && enclosingNode.type === 'TypeAlias') { addLeadingComment(enclosingNode, comment); return true; } @@ -779,19 +783,19 @@ function handleTypeAliasComments(enclosingNode, followingNode, comment) { } function handleVariableDeclaratorComments( - enclosingNode, - followingNode, - comment -) { + enclosingNode: AST, + followingNode: AST, + comment: Comment +): boolean { if ( enclosingNode && - (enclosingNode.type === "VariableDeclarator" || - enclosingNode.type === "AssignmentExpression") && + (enclosingNode.type === 'VariableDeclarator' || + enclosingNode.type === 'AssignmentExpression') && followingNode && - (followingNode.type === "ObjectExpression" || - followingNode.type === "ArrayExpression" || - followingNode.type === "TemplateLiteral" || - followingNode.type === "TaggedTemplateExpression") + (followingNode.type === 'ObjectExpression' || + followingNode.type === 'ArrayExpression' || + followingNode.type === 'TemplateLiteral' || + followingNode.type === 'TaggedTemplateExpression') ) { addLeadingComment(followingNode, comment); return true; @@ -800,19 +804,19 @@ function handleVariableDeclaratorComments( } function handleTSMappedTypeComments( - text, - enclosingNode, - precedingNode, - followingNode, - comment -) { - if (!enclosingNode || enclosingNode.type !== "TSMappedType") { + text: string, + enclosingNode: AST, + precedingNode: AST, + followingNode: AST, + comment: Comment +): boolean { + if (!enclosingNode || enclosingNode.type !== 'TSMappedType') { return false; } if ( followingNode && - followingNode.type === "TSTypeParameter" && + followingNode.type === 'TSTypeParameter' && followingNode.name ) { addLeadingComment(followingNode.name, comment); @@ -821,7 +825,7 @@ function handleTSMappedTypeComments( if ( precedingNode && - precedingNode.type === "TSTypeParameter" && + precedingNode.type === 'TSTypeParameter' && precedingNode.constraint ) { addTrailingComment(precedingNode.constraint, comment); @@ -830,7 +834,3 @@ function handleTSMappedTypeComments( return false; } - -export function isBlockComment(comment) { - return comment.type === "Block" || comment.type === "CommentBlock"; -} diff --git a/src/printer.js b/src/printer.js index e725b1de4..ed74821cf 100644 --- a/src/printer.js +++ b/src/printer.js @@ -2,7 +2,7 @@ import * as nodes from './nodes/index.js'; import { hasNodeIgnoreComment, prettierVersionSatisfies -} from './common/util.js'; +} from './common/util.ts'; import ignoreComments from './comments/ignore.js'; let checked = false; diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 000000000..c1208fd79 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,44 @@ +import { ASTNode } from '@solidity-parser/parser/src/ast-types'; + +// Adding our own options to prettier's `ParserOptions` interface. +declare module 'prettier' { + interface ParserOptions { + compiler: string; + experimentalTernaries: boolean; + } +} + +declare module '@solidity-parser/parser/src/ast-types' { + // Adding the properties that prettier expects from the `Comment` interface. + interface Comment { + leading?: boolean; + trailing?: boolean; + printed?: boolean; + precedingNode?: ASTNode; + enclosingNode?: ASTNode; + followingNode?: ASTNode; + } + + export interface BlockComment extends Comment { + type: 'BlockComment'; + } + + export interface LineComment extends Comment { + type: 'LineComment'; + } + + // All `ASTNode`s can have `comments` + interface BaseASTNode { + comments?: Comment[]; + } + + // `ForStatement`'s `initExpression` and `loopExpression` need the + // `omitSemicolon` property + interface ExpressionStatement { + omitSemicolon?: boolean; + } + + interface VariableDeclarationStatement { + omitSemicolon?: boolean; + } +} diff --git a/tests/config/.prettierrc b/tests/config/.prettierrc index befe74aca..f3bcd4c90 100644 --- a/tests/config/.prettierrc +++ b/tests/config/.prettierrc @@ -1,5 +1,4 @@ { - "singleQuote": false, - "trailingComma": "es5" - } - \ No newline at end of file + "singleQuote": false, + "trailingComma": "all" +} diff --git a/tests/config/setup.js b/tests/config/format-test-setup.js similarity index 100% rename from tests/config/setup.js rename to tests/config/format-test-setup.js diff --git a/tests/config/format-test.js b/tests/config/format-test.js index 7c3a3ed74..54dca47de 100644 --- a/tests/config/format-test.js +++ b/tests/config/format-test.js @@ -25,7 +25,7 @@ const unstableTests = new Map( ? fixture : [fixture]; return [path.join(__dirname, "../format/", file), isUnstable]; - }) + }), ); // Here we add files that will not have the same AST after being formatted. @@ -35,7 +35,7 @@ const unstableAstTests = new Map( ? fixture : [fixture]; return [path.join(__dirname, "../format/", file), isAstUnstable]; - }) + }), ); const testsWithAstChanges = new Map( @@ -59,7 +59,7 @@ const testsWithAstChanges = new Map( ? fixture : [fixture]; return [path.join(__dirname, "../format/", file), compareBytecode]; - }) + }), ); const isUnstable = (filename, options) => { @@ -109,7 +109,7 @@ const shouldThrowOnFormat = (filename, options) => { const isTestDirectory = (dirname, name) => (dirname + path.sep).startsWith( - path.join(__dirname, "../format", name) + path.sep + path.join(__dirname, "../format", name) + path.sep, ); function runSpec(fixtures, parsers, options) { @@ -121,7 +121,7 @@ function runSpec(fixtures, parsers, options) { // `IS_PARSER_INFERENCE_TESTS` mean to test `inferParser` on `standalone` const IS_PARSER_INFERENCE_TESTS = isTestDirectory( dirname, - "misc/parser-inference" + "misc/parser-inference", ); // `IS_ERROR_TESTS` mean to watch errors like: @@ -197,7 +197,7 @@ function runSpec(fixtures, parsers, options) { }; const shouldThrowOnMainParserFormat = shouldThrowOnFormat( name, - formatOptions + formatOptions, ); let mainParserFormatResult; @@ -266,13 +266,13 @@ async function runTest({ // Make sure output has consistent EOL expect(formatResult.eolVisualizedOutput).toEqual( - visualizeEndOfLine(consistentEndOfLine(formatResult.outputWithCursor)) + visualizeEndOfLine(consistentEndOfLine(formatResult.outputWithCursor)), ); // The result is assert to equals to `output` if (typeof output === "string") { expect(formatResult.eolVisualizedOutput).toEqual( - visualizeEndOfLine(output) + visualizeEndOfLine(output), ); return; } @@ -283,7 +283,7 @@ async function runTest({ parsers, formatOptions, CURSOR_PLACEHOLDER, - }) + }), ).toMatchSnapshot(); if (!FULL_TEST) { @@ -299,7 +299,7 @@ async function runTest({ const { eolVisualizedOutput: firstOutput, output } = formatResult; const { eolVisualizedOutput: secondOutput } = await format( output, - formatOptions + formatOptions, ); if (isUnstableTest) { // To keep eye on failed tests, this assert never supposed to pass, @@ -327,14 +327,14 @@ async function runTest({ for (const eol of ["\r\n", "\r"]) { const { eolVisualizedOutput: output } = await format( code.replace(/\n/g, eol), - formatOptions + formatOptions, ); // Only if `endOfLine: "auto"` the result will be different const expected = formatOptions.endOfLine === "auto" ? visualizeEndOfLine( // All `code` use `LF`, so the `eol` of result is always `LF` - formatResult.outputWithCursor.replace(/\n/g, eol) + formatResult.outputWithCursor.replace(/\n/g, eol), ) : formatResult.eolVisualizedOutput; expect(output).toEqual(expected); @@ -344,7 +344,7 @@ async function runTest({ if (code.charAt(0) !== BOM) { const { eolVisualizedOutput: output } = await format( BOM + code, - formatOptions + formatOptions, ); const expected = BOM + formatResult.eolVisualizedOutput; expect(output).toEqual(expected); @@ -427,14 +427,14 @@ const insertCursor = (text, cursorOffset) => async function format(originalText, originalOptions) { const { text: input, options } = replacePlaceholders( originalText, - originalOptions + originalOptions, ); const inputWithCursor = insertCursor(input, options.cursorOffset); const prettier = await getPrettier(); const { formatted: output, cursorOffset } = await prettier.formatWithCursor( input, - options + options, ); const outputWithCursor = insertCursor(output, cursorOffset); const eolVisualizedOutput = visualizeEndOfLine(outputWithCursor); diff --git a/tests/config/require-standalone.cjs b/tests/config/require-standalone.cjs index 2f2c10bbc..8b9d52d3a 100644 --- a/tests/config/require-standalone.cjs +++ b/tests/config/require-standalone.cjs @@ -35,7 +35,7 @@ module.exports = { }; prettier.formatWithCursor($$$input, options); `, - { $$$input: input, $$$options: options, ...sandbox } + { $$$input: input, $$$options: options, ...sandbox }, ); }, @@ -57,7 +57,7 @@ module.exports = { $$$options: options, $$$devOptions: devOptions, ...sandbox, - } + }, ); }, }, diff --git a/tests/config/utils/compile-contract.js b/tests/config/utils/compile-contract.js index 7a0474f8f..194eb0980 100644 --- a/tests/config/utils/compile-contract.js +++ b/tests/config/utils/compile-contract.js @@ -40,7 +40,7 @@ async function compileContract(filename, content) { ...byteCodes, [contractName]: compiledContracts[contractName].evm.bytecode.object, }), - {} + {}, ); } diff --git a/tests/config/utils/create-snapshot.js b/tests/config/utils/create-snapshot.js index 5b122939a..ed5f2ea91 100644 --- a/tests/config/utils/create-snapshot.js +++ b/tests/config/utils/create-snapshot.js @@ -48,7 +48,7 @@ function printWidthIndicator(printWidth, offset) { function createSnapshot( formatResult, - { parsers, formatOptions, CURSOR_PLACEHOLDER } + { parsers, formatOptions, CURSOR_PLACEHOLDER }, ) { let { inputWithCursor: input, @@ -89,7 +89,7 @@ function createSnapshot( printSeparator("output"), output, printSeparator(), - ].join("\n") + ].join("\n"), ); } diff --git a/tests/config/utils/stringify-options-for-title.js b/tests/config/utils/stringify-options-for-title.js index 46921dc54..d25c671d5 100644 --- a/tests/config/utils/stringify-options-for-title.js +++ b/tests/config/utils/stringify-options-for-title.js @@ -4,7 +4,7 @@ function stringifyOptions(options) { ? undefined : value === Number.POSITIVE_INFINITY ? "Infinity" - : value + : value, ); return string === "{}" ? "" : string; diff --git a/tests/config/utils/visualize-range.js b/tests/config/utils/visualize-range.js index 6d3762f7f..eb7232dc1 100644 --- a/tests/config/utils/visualize-range.js +++ b/tests/config/utils/visualize-range.js @@ -32,7 +32,7 @@ const visualizeRange = (text, { rangeStart = 0, rangeEnd = text.length }) => locationForRange(text, rangeStart, rangeEnd), rangeStart > rangeEnd ? { ...codeFrameColumnsOptions, message: "[Reversed range]" } - : codeFrameColumnsOptions + : codeFrameColumnsOptions, ); export default visualizeRange; diff --git a/tests/unit/binary-operator-printers/index.test.js b/tests/unit/binary-operator-printers/index.test.js index e802c8981..cf3c40559 100644 --- a/tests/unit/binary-operator-printers/index.test.js +++ b/tests/unit/binary-operator-printers/index.test.js @@ -1,5 +1,5 @@ -import * as binaryOperatorPrinters from '../../../src/binary-operator-printers/index.js'; +import * as printers from '../../../src/binary-operator-printers/index.js'; test('binary operators printers to match snapshot', () => { - expect(Object.keys(binaryOperatorPrinters)).toMatchSnapshot(); + expect(Object.keys(printers)).toMatchSnapshot(); }); diff --git a/tests/unit/comments/printer.test.js b/tests/unit/comments/printer.test.js index b4a28d747..c00663078 100644 --- a/tests/unit/comments/printer.test.js +++ b/tests/unit/comments/printer.test.js @@ -1,11 +1,8 @@ -import { printComment } from '../../../src/comments/printer.js'; -import loc from '../../../src/loc.js'; +import { printComment } from '../../../src/comments/printer.ts'; test('given an unknown comment type then printComment function should throw', () => { - const mockCommentPath = { - getValue: () => ({ type: 'UnknownComment', range: [0, 1] }) - }; - const mockOptions = { ...loc, originalText: 'foo' }; + const mockCommentPath = { node: { type: 'UnknownComment', range: [0, 1] } }; + const mockOptions = { originalText: 'foo' }; expect(() => { printComment(mockCommentPath, mockOptions); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..d74332c94 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "allowJs": true, + "outDir": "./dist/", + "noImplicitAny": true, + "sourceMap": true, + "target": "es6", + "module": "es2022", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "files": true + }, + "include": ["./src/**/*"] +} diff --git a/webpack.config.js b/webpack.config.js index ab7bcdbd0..c30eada67 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -27,6 +27,22 @@ export default (webpackEnv) => { bail: isEnvProduction, devtool: 'source-map', + resolve: { + extensions: ['.ts', '.js'], + extensionAlias: { + '.js': ['.js', '.ts'] + } + }, + + module: { + rules: [ + { + test: /\.ts$/, + use: 'ts-loader', + exclude: /node_modules/ + } + ] + }, optimization: { minimize: isEnvProduction }, From b5a2c3c9ee42456a10511c16e551213f1507ce59 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 30 Dec 2023 01:14:06 -0300 Subject: [PATCH 2/2] removing ts-node from test:standalone --- .github/workflows/CI.yml | 2 +- jest.config.js | 12 +++++++----- package.json | 2 +- tests/config/get-prettier.js | 2 +- tests/config/require-prettier.cjs | 5 ----- 5 files changed, 10 insertions(+), 13 deletions(-) delete mode 100644 tests/config/require-prettier.cjs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ecd108073..4d0d28fda 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -59,7 +59,7 @@ jobs: - name: Build test app run: npm run build:test - name: Run standalone tests - run: npm run test:standalone tests/format tests/integration tests/unit/prettier-version + run: npm run test:standalone test_linux: name: Test on Linux with Node ${{ matrix.node }} diff --git a/jest.config.js b/jest.config.js index dd7f0f5fa..de1dc5c4c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,11 +1,13 @@ const TEST_STANDALONE = Boolean(process.env.TEST_STANDALONE); -const testMatch = [ - '/tests/format/**/jsfmt.spec.js', - '/tests/unit/**/*.test.js' -]; +const testMatch = ['/tests/format/**/jsfmt.spec.js']; if (TEST_STANDALONE) { - testMatch.push('/tests/integration/**/*.test.js'); + testMatch.push( + '/tests/integration/**/*.test.js', + '/tests/unit/prettier-version/**/*.test.js' + ); +} else { + testMatch.push('/tests/unit/**/*.test.js'); } export default { diff --git a/package.json b/package.json index 20faae9de..e86d91014 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "prettier": "prettier './*.{ts,js,cjs,json,md,yml}' '{src,tests}/**/*.{ts,js,cjs}'", "test": "NODE_OPTIONS=\"--loader=ts-node/esm --loader=esmock\" jest", "test:all": "cross-env FULL_TEST=1 NODE_OPTIONS=\"--loader=ts-node/esm --loader=esmock\" c8 jest", - "test:standalone": "cross-env NODE_OPTIONS=\"--loader=ts-node/esm\" TEST_STANDALONE=1 FULL_TEST=1 jest" + "test:standalone": "cross-env TEST_STANDALONE=1 FULL_TEST=1 jest" }, "files": [ "src", diff --git a/tests/config/get-prettier.js b/tests/config/get-prettier.js index 5d72040c0..6a7446292 100644 --- a/tests/config/get-prettier.js +++ b/tests/config/get-prettier.js @@ -1,7 +1,7 @@ function getPrettierInternal() { const entry = process.env.TEST_STANDALONE ? new URL("./require-standalone.cjs", import.meta.url) - : new URL("./require-prettier.cjs", import.meta.url); + : "prettier"; return import(entry).then((module) => module.default); } diff --git a/tests/config/require-prettier.cjs b/tests/config/require-prettier.cjs deleted file mode 100644 index f0fa1e6d4..000000000 --- a/tests/config/require-prettier.cjs +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -const prettier = require("prettier"); - -module.exports = prettier;