diff --git a/README.md b/README.md index 0b3a5cc..2c6c2c9 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ or create a minimal `bsconfig.json` like that: } ``` -Otherwise, you will only see issues coming from the +Otherwise, you will only see issues coming from the [brighterscript](https://github.com/rokucommunity/brighterscript) compiler. ### Command line interface (CLI) @@ -98,7 +98,7 @@ skip ignored files when running the extra linting rules. *Note: it won't remove issues reported by the compiler itself!* -Format should follow "glob search" rules, as implemented in +Format should follow "glob search" rules, as implemented in [minimatch](https://www.npmjs.com/package/minimatch) module. Examples: @@ -128,6 +128,7 @@ Default rules: "condition-style": "no-group", "named-function-style": "auto", "anon-function-style": "auto", + "aa-comma-style": "no-dangling", "no-print": "off", "type-annotations": "off", @@ -172,6 +173,13 @@ Default rules: - `auto`: use `sub` for `Void` functions, otherwise use `function` (**default**) - `off`: no not validate +- `aa-comma-style`: validation of commas in Associative Array (AA) literals + + - `always`: enforce the presence of commas, always + - `no-dangling`: enforce the presence of commas but don't leave one dangling (**default**) + - `never`: enforce that optional commas aren't used + - `off`: do not validate + - `no-print`: prevent usage of `print` statements in code (`error | warn | info | off`) ### Strictness rules @@ -236,6 +244,7 @@ Running `bslint` with `--fix` parameter will attempt to fix common code-style is - Using wrong `sub` or `function` keyword, - Using/missing the optional `then` keyword, - Using/missing parenthesis around `if/while` conditions. +- Adding/removing Associative Array (AA) literals' commas where needed. - Case sensitivity (align with first occurence) ## Usage checking (approximative) diff --git a/package-lock.json b/package-lock.json index b9282d0..978254b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -436,9 +436,9 @@ } }, "@xml-tools/parser": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@xml-tools/parser/-/parser-1.0.10.tgz", - "integrity": "sha512-9oRb68wEKT+MRB7e2GwTiKicRKVXKzquBDGgH6YcGafvnSYXorWi2oaTVtbv2109RlGiQSnoXaQFUXCnHwFS7Q==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@xml-tools/parser/-/parser-1.0.11.tgz", + "integrity": "sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==", "requires": { "chevrotain": "7.1.1" }, @@ -509,6 +509,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -523,53 +524,6 @@ "default-require-extensions": "^3.0.0" } }, - "archiver": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", - "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==", - "requires": { - "archiver-utils": "^2.1.0", - "async": "^2.6.3", - "buffer-crc32": "^0.2.1", - "glob": "^7.1.4", - "readable-stream": "^3.4.0", - "tar-stream": "^2.1.0", - "zip-stream": "^2.1.2" - } - }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", @@ -633,14 +587,6 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -661,11 +607,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -679,16 +620,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -707,9 +638,9 @@ } }, "brighterscript": { - "version": "0.39.1", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.39.1.tgz", - "integrity": "sha512-SFPKhOO66ELY6T0whrmJIPCmIpxMnwYItHOPjDJHFxpcZwcjs2AxWPQjTvYeqZDwVeBiJGNeaRC8Dg2l5PQfYQ==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.40.1.tgz", + "integrity": "sha512-OF9scfjtwE2vKd7cqVEdPBjPqAQyxawxQQiBshIZMJl4WIyTZcLNCNvMYm25u9j1yv0ZePqHxFdbeKNZXOy2Tw==", "requires": { "@rokucommunity/bslib": "^0.1.1", "@xml-tools/parser": "^1.0.7", @@ -731,7 +662,7 @@ "moment": "^2.23.0", "p-settle": "^2.1.0", "parse-ms": "^2.1.0", - "roku-deploy": "^3.2.4", + "roku-deploy": "^3.4.2", "serialize-error": "^7.0.1", "source-map": "^0.7.3", "vscode-languageserver": "7.0.0", @@ -826,9 +757,9 @@ } }, "yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" } } }, @@ -838,20 +769,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -925,25 +842,42 @@ } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } } } }, @@ -995,33 +929,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "compress-commons": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", - "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==", - "requires": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^3.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^2.3.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1054,23 +961,6 @@ "request": "^2.88.2" } }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "requires": { - "buffer": "^5.1.0" - } - }, - "crc32-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", - "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", - "requires": { - "crc": "^3.4.4", - "readable-stream": "^3.4.0" - } - }, "cross-platform-clear-console": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cross-platform-clear-console/-/cross-platform-clear-console-2.3.0.tgz", @@ -1186,14 +1076,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -1631,11 +1513,6 @@ "integrity": "sha512-Xu2Qh8yqYuDhQGOhD5iJGninErSfI9A3FrriD3tjUgV5VbJFeH8vfgZ9HnC6jWN80QDVNQK5vmxRAmEAp7Mevw==", "dev": true }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -1718,6 +1595,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -1813,17 +1691,17 @@ "sshpk": "^1.7.0" } }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -2204,28 +2082,15 @@ "verror": "1.10.0" } }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "jszip": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", + "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", "requires": { - "readable-stream": "^2.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" } }, "lcov-parse": { @@ -2244,6 +2109,14 @@ "type-check": "~0.4.0" } }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -2255,22 +2128,8 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "lodash.flattendeep": { "version": "4.4.0", @@ -2278,16 +2137,6 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, "log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", @@ -2309,9 +2158,9 @@ "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" }, "luxon": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz", - "integrity": "sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA==" + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" }, "make-dir": { "version": "3.1.0", @@ -2766,6 +2615,11 @@ "release-zalgo": "^1.0.0" } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2905,19 +2759,23 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } @@ -3004,16 +2862,20 @@ } }, "roku-deploy": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.3.0.tgz", - "integrity": "sha512-85XAgFH7AZylxIiECHbfydNEYeUUEEs/iq/zl3G3gqgM+x3fT/JnnkqrwgdFdLBj+aJVR3ZlyhuUWjDh1WxMbw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.4.2.tgz", + "integrity": "sha512-Tg/K5M9/gvH5bzzs3g2bUxrcPiwKigKdnpbq5yiTjMnAtDJM6u7Ug4PbcnnoH3qJ3IJqODCW7Kq1iIH7sWmqBg==", "requires": { - "archiver": "^3.0.0", + "chalk": "^2.4.2", "dateformat": "^3.0.3", + "eventemitter3": "^4.0.7", "fs-extra": "^7.0.1", "glob": "^7.1.6", "jsonc-parser": "^2.3.0", + "jszip": "^3.6.0", "minimatch": "^3.0.4", + "moment": "^2.29.1", + "parse-ms": "^2.1.0", "path": "^0.12.7", "request": "^2.88.0", "xml2js": "^0.4.23" @@ -3087,6 +2949,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3304,18 +3171,6 @@ } } }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -3855,16 +3710,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true - }, - "zip-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", - "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==", - "requires": { - "archiver-utils": "^2.1.0", - "compress-commons": "^2.1.1", - "readable-stream": "^3.4.0" - } } } } diff --git a/package.json b/package.json index 7993851..c336cc6 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "license": "ISC", "homepage": "https://github.com/rokucommunity/bslint", "dependencies": { - "brighterscript": "^0.39.1", + "brighterscript": "^0.40.1", "fs-extra": "^10.0.0", "jsonc-parser": "^2.3.0", "minimatch": "^3.0.4", diff --git a/src/index.ts b/src/index.ts index bae805e..3df1f23 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export type RuleInlineIf = 'never' | 'no-then' | 'then' | 'off'; export type RuleBlockIf = 'no-then' | 'then' | 'off'; export type RuleCondition = 'no-group' | 'group' | 'off'; export type RuleFunction = 'no-function' | 'no-sub' | 'auto' | 'off'; +export type RuleAAComma = 'always' | 'no-dangling' | 'never' | 'off'; export type RuleTypeAnnotations = 'all' | 'return' | 'args' | 'off'; export type BsLintConfig = Pick & { @@ -31,6 +32,7 @@ export type BsLintConfig = Pick ({ + severity: DiagnosticSeverity.Error, + code: CodeStyleError.AACommaFound, + source: 'bslint', + message: `Remove optional comma`, + range + }), + addAAComma: (range: Range) => ({ + severity: DiagnosticSeverity.Error, + code: CodeStyleError.AACommaMissing, + source: 'bslint', + message: `Add comma after the expression`, + range }) }; diff --git a/src/plugins/codeStyle/index.spec.ts b/src/plugins/codeStyle/index.spec.ts index eeb4535..a0f94a2 100644 --- a/src/plugins/codeStyle/index.spec.ts +++ b/src/plugins/codeStyle/index.spec.ts @@ -1,8 +1,8 @@ import * as fs from 'fs'; import { expect } from 'chai'; -import { BsDiagnostic, Program } from 'brighterscript'; +import { AALiteralExpression, AssignmentStatement, BsDiagnostic, ParseMode, Parser, Program } from 'brighterscript'; import Linter from '../../Linter'; -import CodeStyle from './index'; +import CodeStyle, { collectWrappingAAMembersIndexes } from './index'; import { createContext, PluginWrapperContext } from '../../util'; function pad(n: number) { @@ -374,6 +374,106 @@ describe('codeStyle', () => { expect(actual).deep.equal(expected); }); + describe('AA style', () => { + it('collects wrapping AA members indexes', () => { + const { statements } = Parser.parse(` + aa = {} + aa = { + p1: 1 + } + aa = { + p1: 1, + p2: 2 + } + aa = { 'comment + p1: 1, + 'comment + p2: 2 + 'comment + } + aa = { + p1: 1, 'comment + p2: 2 'comment + } + aa = { p1: 1 } + aa = { p1: 1, p2: 2 } + aa = { p1: 1, p2: 2, } + `, { mode: ParseMode.BrightScript }).ast; + const indexes = statements.map(s => { + const assign = s as AssignmentStatement; + const value: AALiteralExpression = assign.value as AALiteralExpression; + return collectWrappingAAMembersIndexes(value); + }); + expect(indexes).to.deep.equal([ + [], + [0], + [0, 1], + [1, 3], + [0, 2], + [0], + [1], + [1] + ]); + }); + + it('enforce aa comma, never', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/aa-style.brs'], + rules: { + 'aa-comma-style': 'never' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `03:LINT3013:Remove optional comma`, + `04:LINT3013:Remove optional comma`, + `11:LINT3013:Remove optional comma`, + `12:LINT3013:Remove optional comma`, + `13:LINT3013:Remove optional comma`, + `31:LINT3013:Remove optional comma` + ]; + expect(actual).deep.equal(expected); + }); + + it('enforce aa comma, no dangling', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/aa-style.brs'], + rules: { + 'aa-comma-style': 'no-dangling' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `13:LINT3013:Remove optional comma`, + `19:LINT3014:Add comma after the expression`, + `20:LINT3014:Add comma after the expression`, + `31:LINT3013:Remove optional comma` + ]; + expect(actual).deep.equal(expected); + }); + + it('enforce aa comma, always', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/aa-style.brs'], + rules: { + 'aa-comma-style': 'always' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `05:LINT3014:Add comma after the expression`, + `19:LINT3014:Add comma after the expression`, + `20:LINT3014:Add comma after the expression`, + `21:LINT3014:Add comma after the expression`, + `27:LINT3014:Add comma after the expression` + ]; + expect(actual).deep.equal(expected); + }); + }); + describe('fix', () => { beforeEach(() => { fs.copyFileSync( @@ -384,11 +484,16 @@ describe('codeStyle', () => { `${project1.rootDir}/source/if-style.brs`, `${project1.rootDir}/source/if-style-temp.brs` ); + fs.copyFileSync( + `${project1.rootDir}/source/aa-style.brs`, + `${project1.rootDir}/source/aa-style-temp.brs` + ); }); afterEach(() => { fs.unlinkSync(`${project1.rootDir}/source/function-style-temp.brs`); fs.unlinkSync(`${project1.rootDir}/source/if-style-temp.brs`); + fs.unlinkSync(`${project1.rootDir}/source/aa-style-temp.brs`); }); it('replaces `sub` with `function`', async () => { @@ -542,5 +647,80 @@ describe('codeStyle', () => { const expectedSrc = fs.readFileSync(`${project1.rootDir}/source/if-style-group.brs`).toString(); expect(actualSrc).to.equal(expectedSrc); }); + + it('remove optional aa comma', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/aa-style-temp.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'no-print': 'off', + 'aa-comma-style': 'never' + }, + fix: true + }); + const actual = fmtDiagnostics(diagnostics); + const expected = []; + expect(actual).deep.equal(expected); + + expect(lintContext.pendingFixes.size).equals(1); + await lintContext.applyFixes(); + expect(lintContext.pendingFixes.size).equals(0); + + const actualSrc = fs.readFileSync(`${project1.rootDir}/source/aa-style-temp.brs`).toString(); + const expectedSrc = fs.readFileSync(`${project1.rootDir}/source/aa-style-nocomma.brs`).toString(); + expect(actualSrc).to.equal(expectedSrc); + }); + + it('add missing aa comma, always', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/aa-style-temp.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'no-print': 'off', + 'aa-comma-style': 'always' + }, + fix: true + }); + const actual = fmtDiagnostics(diagnostics); + const expected = []; + expect(actual).deep.equal(expected); + + expect(lintContext.pendingFixes.size).equals(1); + await lintContext.applyFixes(); + expect(lintContext.pendingFixes.size).equals(0); + + const actualSrc = fs.readFileSync(`${project1.rootDir}/source/aa-style-temp.brs`).toString(); + const expectedSrc = fs.readFileSync(`${project1.rootDir}/source/aa-style-always.brs`).toString(); + expect(actualSrc).to.equal(expectedSrc); + }); + + it('add missing aa comma, no dangling', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/aa-style-temp.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'no-print': 'off', + 'aa-comma-style': 'no-dangling' + }, + fix: true + }); + const actual = fmtDiagnostics(diagnostics); + const expected = []; + expect(actual).deep.equal(expected); + + expect(lintContext.pendingFixes.size).equals(1); + await lintContext.applyFixes(); + expect(lintContext.pendingFixes.size).equals(0); + + const actualSrc = fs.readFileSync(`${project1.rootDir}/source/aa-style-temp.brs`).toString(); + const expectedSrc = fs.readFileSync(`${project1.rootDir}/source/aa-style-nodangling.brs`).toString(); + expect(actualSrc).to.equal(expectedSrc); + }); }); }); diff --git a/src/plugins/codeStyle/index.ts b/src/plugins/codeStyle/index.ts index ae91ef8..797d6d7 100644 --- a/src/plugins/codeStyle/index.ts +++ b/src/plugins/codeStyle/index.ts @@ -1,4 +1,5 @@ -import { BscFile, BsDiagnostic, createVisitor, FunctionExpression, isBrsFile, isGroupingExpression, TokenKind, WalkMode, CancellationTokenSource, DiagnosticSeverity, OnGetCodeActionsEvent } from 'brighterscript'; +import { BscFile, BsDiagnostic, createVisitor, FunctionExpression, isBrsFile, isGroupingExpression, TokenKind, WalkMode, CancellationTokenSource, DiagnosticSeverity, OnGetCodeActionsEvent, isCommentStatement, AALiteralExpression, AAMemberExpression } from 'brighterscript'; +import { RuleAAComma } from '../..'; import { addFixesToEvent } from '../../textEdit'; import { PluginContext } from '../../util'; import { messages } from './diagnosticMessages'; @@ -23,7 +24,7 @@ export default class CodeStyle { const diagnostics: (Omit)[] = []; const { severity, fix } = this.lintContext; - const { inlineIfStyle, blockIfStyle, conditionStyle, noPrint } = severity; + const { inlineIfStyle, blockIfStyle, conditionStyle, noPrint, aaCommaStyle } = severity; const validatePrint = noPrint !== DiagnosticSeverity.Hint; const validateInlineIf = inlineIfStyle !== 'off'; const disallowInlineIf = inlineIfStyle === 'never'; @@ -32,6 +33,8 @@ export default class CodeStyle { const requireBlockIfThen = blockIfStyle === 'then'; const validateCondition = conditionStyle !== 'off'; const requireConditionGroup = conditionStyle === 'group'; + const validateAAStyle = aaCommaStyle !== 'off'; + const walkExpressions = validateAAStyle; file.ast.walk(createVisitor({ IfStatement: s => { @@ -77,8 +80,13 @@ export default class CodeStyle { if (validatePrint) { diagnostics.push(messages.noPrint(s.tokens.print.range, noPrint)); } + }, + AALiteralExpression: e => { + if (validateAAStyle) { + this.validateAAStyle(e, aaCommaStyle, diagnostics); + } } - }), { walkMode: WalkMode.visitStatementsRecursive }); + }), { walkMode: walkExpressions ? WalkMode.visitAllRecursive : WalkMode.visitStatementsRecursive }); // validate function style (`function` or `sub`) for (const fun of file.parser.references.functionExpressions) { @@ -100,6 +108,22 @@ export default class CodeStyle { file.addDiagnostics(bsDiagnostics); } + validateAAStyle(aa: AALiteralExpression, aaCommaStyle: RuleAAComma, diagnostics: (Omit)[]) { + const indexes = collectWrappingAAMembersIndexes(aa); + const last = indexes.length - 1; + indexes.forEach((index, i) => { + const member = aa.elements[index] as AAMemberExpression; + const hasComma = !!member.commaToken; + if (aaCommaStyle === 'never' || (i === last && aaCommaStyle === 'no-dangling')) { + if (hasComma) { + diagnostics.push(messages.removeAAComma(member.commaToken.range)); + } + } else if (!hasComma) { + diagnostics.push(messages.addAAComma(member.value.range)); + } + }); + } + validateFunctionStyle(fun: FunctionExpression, diagnostics: (Omit)[]) { const { severity } = this.lintContext; const { namedFunctionStyle, anonFunctionStyle, typeAnnotations } = severity; @@ -167,3 +191,28 @@ export default class CodeStyle { return hasReturnedValue; } } + +/** + * Collect indexes of non-inline AA members + */ +export function collectWrappingAAMembersIndexes(aa: AALiteralExpression): number[] { + const indexes: number[] = []; + const { elements } = aa; + const lastIndex = elements.length - 1; + for (let i = 0; i < lastIndex; i++) { + const e = elements[i]; + if (isCommentStatement(e)) { + continue; + } + const ne = elements[i + 1]; + const hasNL = isCommentStatement(ne) || ne.range.start.line > e.range.end.line; + if (hasNL) { + indexes.push(i); + } + } + const last = elements[lastIndex]; + if (last && !isCommentStatement(last)) { + indexes.push(lastIndex); + } + return indexes; +} diff --git a/src/plugins/codeStyle/styleFixes.ts b/src/plugins/codeStyle/styleFixes.ts index f795935..a85fac4 100644 --- a/src/plugins/codeStyle/styleFixes.ts +++ b/src/plugins/codeStyle/styleFixes.ts @@ -32,11 +32,35 @@ export function getFixes(diagnostic: BsDiagnostic): ChangeEntry { return removeConditionGroup(diagnostic); case CodeStyleError.ConditionGroupMissing: return addConditionGroup(diagnostic); + case CodeStyleError.AACommaFound: + return removeAAComma(diagnostic); + case CodeStyleError.AACommaMissing: + return addAAComma(diagnostic); default: return null; } } +function addAAComma(diagnostic: BsDiagnostic) { + const { range } = diagnostic; + return { + diagnostic, + changes: [ + insertText(range.end, ',') + ] + }; +} + +function removeAAComma(diagnostic: BsDiagnostic) { + const { range } = diagnostic; + return { + diagnostic, + changes: [ + replaceText(range, '') + ] + }; +} + function addConditionGroup(diagnostic: BsDiagnostic) { const stat: IfStatement | WhileStatement = diagnostic.data; const { start, end } = stat.condition.range; diff --git a/src/util.ts b/src/util.ts index c8919f9..477ce7a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -25,6 +25,7 @@ export function getDefaultRules(): BsLintConfig['rules'] { 'condition-style': 'no-group', 'named-function-style': 'auto', 'anon-function-style': 'auto', + 'aa-comma-style': 'no-dangling', 'type-annotations': 'off', 'no-print': 'off' }; @@ -150,6 +151,7 @@ function rulesToSeverity(rules: BsLintConfig['rules']) { conditionStyle: rules['condition-style'], namedFunctionStyle: rules['named-function-style'], anonFunctionStyle: rules['anon-function-style'], + aaCommaStyle: rules['aa-comma-style'], typeAnnotations: rules['type-annotations'], noPrint: ruleToSeverity(rules['no-print']) }; diff --git a/test/project1/source/aa-style-always.brs b/test/project1/source/aa-style-always.brs new file mode 100644 index 0000000..0725ffb --- /dev/null +++ b/test/project1/source/aa-style-always.brs @@ -0,0 +1,32 @@ +sub syle1() + a = { + a1: 1, + a2: 2, + a3: 3, + } +end sub + +sub syle2() + a = { + a1: 1, + a2: 2, 'comment + a3: 3, + } +end sub + +sub syle3() + a = { + "a1": 1, + "a2": 2, + "a3": 3, + 'comment + } +end sub + +sub style4() + a = { a1: 1, a2: 2, a3: 3, } +end sub + +sub style5() + a = { a1: 1, a2: 2, a3: 3, } +end sub diff --git a/test/project1/source/aa-style-nocomma.brs b/test/project1/source/aa-style-nocomma.brs new file mode 100644 index 0000000..f3625ed --- /dev/null +++ b/test/project1/source/aa-style-nocomma.brs @@ -0,0 +1,32 @@ +sub syle1() + a = { + a1: 1 + a2: 2 + a3: 3 + } +end sub + +sub syle2() + a = { + a1: 1 + a2: 2 'comment + a3: 3 + } +end sub + +sub syle3() + a = { + "a1": 1 + "a2": 2 + "a3": 3 + 'comment + } +end sub + +sub style4() + a = { a1: 1, a2: 2, a3: 3 } +end sub + +sub style5() + a = { a1: 1, a2: 2, a3: 3 } +end sub diff --git a/test/project1/source/aa-style-nodangling.brs b/test/project1/source/aa-style-nodangling.brs new file mode 100644 index 0000000..f749f14 --- /dev/null +++ b/test/project1/source/aa-style-nodangling.brs @@ -0,0 +1,32 @@ +sub syle1() + a = { + a1: 1, + a2: 2, + a3: 3 + } +end sub + +sub syle2() + a = { + a1: 1, + a2: 2, 'comment + a3: 3 + } +end sub + +sub syle3() + a = { + "a1": 1, + "a2": 2, + "a3": 3 + 'comment + } +end sub + +sub style4() + a = { a1: 1, a2: 2, a3: 3 } +end sub + +sub style5() + a = { a1: 1, a2: 2, a3: 3 } +end sub diff --git a/test/project1/source/aa-style.brs b/test/project1/source/aa-style.brs new file mode 100644 index 0000000..c465360 --- /dev/null +++ b/test/project1/source/aa-style.brs @@ -0,0 +1,32 @@ +sub syle1() + a = { + a1: 1, + a2: 2, + a3: 3 + } +end sub + +sub syle2() + a = { + a1: 1, + a2: 2, 'comment + a3: 3, + } +end sub + +sub syle3() + a = { + "a1": 1 + "a2": 2 + "a3": 3 + 'comment + } +end sub + +sub style4() + a = { a1: 1, a2: 2, a3: 3 } +end sub + +sub style5() + a = { a1: 1, a2: 2, a3: 3, } +end sub