Mirror: The spec-compliant minimum of client-side GraphQL.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add tests and enforce test coverage (#8)

* Set up coverage provider

* Add test for multiline block string

* Fix up Int/Float detection

* Add test for non-block strings

* Add tests for object values

* Reformat parse tests

* Add tests for parsing arguments

* Add tests for directives

* Add test for aliased field

* Add tests for list values

* Add tests for invalid type references

* Add tests for inline fragments

* Add tests for variable definitions

* Remove redundant type parse check

* Add tests for fragment definitions

* Remove redundant EOF check

* Remove redundant null check in parseType

* Add basic call tests

* Update GraphQLError and add tests

* Apply lints

* Add tests for printString and printBlockString

* Add additional printer tests

* Add missing test cases for visitor

* Add changeset

* Update snapshots

* Apply lints

authored by

Phil Pluckthun and committed by
GitHub
14905376 6a29a9d7

+769 -172
+5
.changeset/cold-llamas-grin.md
··· 1 + --- 2 + '@0no-co/graphql.web': patch 3 + --- 4 + 5 + Fix float pattern and int/float decision in value parsing.
+5
.changeset/fifty-carrots-serve.md
··· 1 + --- 2 + '@0no-co/graphql.web': patch 3 + --- 4 + 5 + Remove redundant code paths from `visit` and parser.
+1 -1
.github/workflows/ci.yml
··· 51 51 run: pnpm run lint 52 52 53 53 - name: Unit Tests 54 - run: pnpm run test 54 + run: pnpm run test --run 55 55 56 56 - name: Build 57 57 run: pnpm run build
+1
.gitignore
··· 5 5 .rts2_cache* 6 6 .husky 7 7 dist/ 8 + coverage/ 8 9 package-lock.json 9 10 .DS_Store
+2 -7
benchmark/suite.js
··· 4 4 const graphql16 = require('graphql16'); 5 5 const graphql17 = require('graphql17'); 6 6 7 - const kitchenSink = 8 - fs.readFileSync('../src/__tests__/kitchen_sink.graphql', { encoding: 'utf8' }); 7 + const kitchenSink = fs.readFileSync('../src/__tests__/kitchen_sink.graphql', { encoding: 'utf8' }); 9 8 const document = require('../src/__tests__/kitchen_sink.json'); 10 9 11 10 suite('parse kitchen sink query', () => { ··· 77 76 function formatNode(node) { 78 77 if (!node.selectionSet) return node; 79 78 for (const selection of node.selectionSet.selections) 80 - if ( 81 - selection.kind === 'Field' && 82 - selection.name.value === '__typename' && 83 - !selection.alias 84 - ) 79 + if (selection.kind === 'Field' && selection.name.value === '__typename' && !selection.alias) 85 80 return node; 86 81 87 82 return {
+3 -2
package.json
··· 29 29 "client-side graphql" 30 30 ], 31 31 "scripts": { 32 - "test": "vitest run", 32 + "test": "vitest", 33 33 "check": "tsc", 34 34 "lint": "eslint --ext=js,ts .", 35 35 "build": "rollup -c scripts/rollup.config.mjs", ··· 75 75 "@rollup/plugin-terser": "^0.4.0", 76 76 "@typescript-eslint/eslint-plugin": "^5.55.0", 77 77 "@typescript-eslint/parser": "^5.55.0", 78 + "@vitest/coverage-c8": "^0.29.7", 78 79 "dotenv": "^16.0.3", 79 80 "eslint": "^8.36.0", 80 81 "eslint-config-prettier": "^8.7.0", ··· 91 92 "rollup-plugin-dts": "^5.3.0", 92 93 "terser": "^5.16.6", 93 94 "typescript": "^5.0.2", 94 - "vitest": "^0.29.3" 95 + "vitest": "^0.29.7" 95 96 }, 96 97 "publishConfig": { 97 98 "access": "public"
+170 -25
pnpm-lock.yaml
··· 13 13 '@rollup/plugin-terser': ^0.4.0 14 14 '@typescript-eslint/eslint-plugin': ^5.55.0 15 15 '@typescript-eslint/parser': ^5.55.0 16 + '@vitest/coverage-c8': ^0.29.7 16 17 dotenv: ^16.0.3 17 18 eslint: ^8.36.0 18 19 eslint-config-prettier: ^8.7.0 ··· 29 30 rollup-plugin-dts: ^5.3.0 30 31 terser: ^5.16.6 31 32 typescript: ^5.0.2 32 - vitest: ^0.29.3 33 + vitest: ^0.29.7 33 34 devDependencies: 34 35 '@changesets/cli': 2.26.0 35 36 '@changesets/get-github-info': 0.5.2 ··· 40 41 '@rollup/plugin-terser': 0.4.0_rollup@3.19.1 41 42 '@typescript-eslint/eslint-plugin': 5.55.0_qsnvknysi52qtaxqdyqyohkcku 42 43 '@typescript-eslint/parser': 5.55.0_j4766f7ecgqbon3u7zlxn5zszu 44 + '@vitest/coverage-c8': 0.29.7_vitest@0.29.7 43 45 dotenv: 16.0.3 44 46 eslint: 8.36.0 45 47 eslint-config-prettier: 8.7.0_eslint@8.36.0 ··· 56 58 rollup-plugin-dts: 5.3.0_7iejawhbqmte5pthjozf4tfuqy 57 59 terser: 5.16.6 58 60 typescript: 5.0.2 59 - vitest: 0.29.3_terser@5.16.6 61 + vitest: 0.29.7_terser@5.16.6 60 62 61 63 benchmark: 62 64 specifiers: ··· 98 100 engines: {node: '>=6.9.0'} 99 101 dependencies: 100 102 regenerator-runtime: 0.13.11 103 + dev: true 104 + 105 + /@bcoe/v8-coverage/0.2.3: 106 + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} 101 107 dev: true 102 108 103 109 /@changesets/apply-release-plan/6.1.3: ··· 548 554 resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} 549 555 dev: true 550 556 557 + /@istanbuljs/schema/0.1.3: 558 + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 559 + engines: {node: '>=8'} 560 + dev: true 561 + 551 562 /@jridgewell/gen-mapping/0.3.2: 552 563 resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} 553 564 engines: {node: '>=6.0.0'} ··· 760 771 ci-info: 3.8.0 761 772 dev: true 762 773 774 + /@types/istanbul-lib-coverage/2.0.4: 775 + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} 776 + dev: true 777 + 763 778 /@types/json-schema/7.0.11: 764 779 resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} 765 780 dev: true ··· 926 941 eslint-visitor-keys: 3.3.0 927 942 dev: true 928 943 929 - /@vitest/expect/0.29.3: 930 - resolution: {integrity: sha512-z/0JqBqqrdtrT/wzxNrWC76EpkOHdl+SvuNGxWulLaoluygntYyG5wJul5u/rQs5875zfFz/F+JaDf90SkLUIg==} 944 + /@vitest/coverage-c8/0.29.7_vitest@0.29.7: 945 + resolution: {integrity: sha512-TSubtP9JFBuI/wuApxwknHe40VDkX8hFbBak0OXj4/jCeXrEu5B5GPWcxzyk9YvzXgCaDvoiZV79I7AvhNI9YQ==} 946 + peerDependencies: 947 + vitest: '>=0.29.0 <1' 948 + dependencies: 949 + c8: 7.13.0 950 + picocolors: 1.0.0 951 + std-env: 3.3.2 952 + vitest: 0.29.7_terser@5.16.6 953 + dev: true 954 + 955 + /@vitest/expect/0.29.7: 956 + resolution: {integrity: sha512-UtG0tW0DP6b3N8aw7PHmweKDsvPv4wjGvrVZW7OSxaFg76ShtVdMiMcUkZJgCE8QWUmhwaM0aQhbbVLo4F4pkA==} 931 957 dependencies: 932 - '@vitest/spy': 0.29.3 933 - '@vitest/utils': 0.29.3 958 + '@vitest/spy': 0.29.7 959 + '@vitest/utils': 0.29.7 934 960 chai: 4.3.7 935 961 dev: true 936 962 937 - /@vitest/runner/0.29.3: 938 - resolution: {integrity: sha512-XLi8ctbvOWhUWmuvBUSIBf8POEDH4zCh6bOuVxm/KGfARpgmVF1ku+vVNvyq85va+7qXxtl+MFmzyXQ2xzhAvw==} 963 + /@vitest/runner/0.29.7: 964 + resolution: {integrity: sha512-Yt0+csM945+odOx4rjZSjibQfl2ymxqVsmYz6sO2fiO5RGPYDFCo60JF6tLL9pz4G/kjY4irUxadeB1XT+H1jg==} 939 965 dependencies: 940 - '@vitest/utils': 0.29.3 966 + '@vitest/utils': 0.29.7 941 967 p-limit: 4.0.0 942 968 pathe: 1.1.0 943 969 dev: true 944 970 945 - /@vitest/spy/0.29.3: 946 - resolution: {integrity: sha512-LLpCb1oOCOZcBm0/Oxbr1DQTuKLRBsSIHyLYof7z4QVE8/v8NcZKdORjMUq645fcfX55+nLXwU/1AQ+c2rND+w==} 971 + /@vitest/spy/0.29.7: 972 + resolution: {integrity: sha512-IalL0iO6A6Xz8hthR8sctk6ZS//zVBX48EiNwQguYACdgdei9ZhwMaBFV70mpmeYAFCRAm+DpoFHM5470Im78A==} 947 973 dependencies: 948 974 tinyspy: 1.1.1 949 975 dev: true 950 976 951 - /@vitest/utils/0.29.3: 952 - resolution: {integrity: sha512-hg4Ff8AM1GtUnLpUJlNMxrf9f4lZr/xRJjh3uJ0QFP+vjaW82HAxKrmeBmLnhc8Os2eRf+f+VBu4ts7TafPPkA==} 977 + /@vitest/utils/0.29.7: 978 + resolution: {integrity: sha512-vNgGadp2eE5XKCXtZXL5UyNEDn68npSct75OC9AlELenSK0DiV1Mb9tfkwJHKjRb69iek+e79iipoJx8+s3SdA==} 953 979 dependencies: 954 980 cli-truncate: 3.1.0 955 981 diff: 5.1.0 ··· 1193 1219 engines: {node: '>=6'} 1194 1220 dev: true 1195 1221 1222 + /c8/7.13.0: 1223 + resolution: {integrity: sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==} 1224 + engines: {node: '>=10.12.0'} 1225 + hasBin: true 1226 + dependencies: 1227 + '@bcoe/v8-coverage': 0.2.3 1228 + '@istanbuljs/schema': 0.1.3 1229 + find-up: 5.0.0 1230 + foreground-child: 2.0.0 1231 + istanbul-lib-coverage: 3.2.0 1232 + istanbul-lib-report: 3.0.0 1233 + istanbul-reports: 3.1.5 1234 + rimraf: 3.0.2 1235 + test-exclude: 6.0.0 1236 + v8-to-istanbul: 9.1.0 1237 + yargs: 16.2.0 1238 + yargs-parser: 20.2.9 1239 + dev: true 1240 + 1196 1241 /cac/6.7.14: 1197 1242 resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 1198 1243 engines: {node: '>=8'} ··· 1315 1360 wrap-ansi: 6.2.0 1316 1361 dev: true 1317 1362 1363 + /cliui/7.0.4: 1364 + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} 1365 + dependencies: 1366 + string-width: 4.2.3 1367 + strip-ansi: 6.0.1 1368 + wrap-ansi: 7.0.0 1369 + dev: true 1370 + 1318 1371 /cliui/8.0.1: 1319 1372 resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 1320 1373 engines: {node: '>=12'} ··· 1375 1428 1376 1429 /concat-map/0.0.1: 1377 1430 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 1431 + dev: true 1432 + 1433 + /convert-source-map/1.9.0: 1434 + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} 1378 1435 dev: true 1379 1436 1380 1437 /cosmiconfig/7.1.0: ··· 1945 2002 is-callable: 1.2.7 1946 2003 dev: true 1947 2004 2005 + /foreground-child/2.0.0: 2006 + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} 2007 + engines: {node: '>=8.0.0'} 2008 + dependencies: 2009 + cross-spawn: 7.0.3 2010 + signal-exit: 3.0.7 2011 + dev: true 2012 + 1948 2013 /fs-extra/7.0.1: 1949 2014 resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} 1950 2015 engines: {node: '>=6 <7 || >=8'} ··· 2184 2249 2185 2250 /hosted-git-info/2.8.9: 2186 2251 resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} 2252 + dev: true 2253 + 2254 + /html-escaper/2.0.2: 2255 + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 2187 2256 dev: true 2188 2257 2189 2258 /human-id/1.0.2: ··· 2446 2515 resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 2447 2516 dev: true 2448 2517 2518 + /istanbul-lib-coverage/3.2.0: 2519 + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} 2520 + engines: {node: '>=8'} 2521 + dev: true 2522 + 2523 + /istanbul-lib-report/3.0.0: 2524 + resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} 2525 + engines: {node: '>=8'} 2526 + dependencies: 2527 + istanbul-lib-coverage: 3.2.0 2528 + make-dir: 3.1.0 2529 + supports-color: 7.2.0 2530 + dev: true 2531 + 2532 + /istanbul-reports/3.1.5: 2533 + resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} 2534 + engines: {node: '>=8'} 2535 + dependencies: 2536 + html-escaper: 2.0.2 2537 + istanbul-lib-report: 3.0.0 2538 + dev: true 2539 + 2449 2540 /jju/1.4.0: 2450 2541 resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} 2451 2542 dev: true ··· 2679 2770 '@jridgewell/sourcemap-codec': 1.4.14 2680 2771 dev: true 2681 2772 2773 + /make-dir/3.1.0: 2774 + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} 2775 + engines: {node: '>=8'} 2776 + dependencies: 2777 + semver: 6.3.0 2778 + dev: true 2779 + 2682 2780 /map-obj/1.0.1: 2683 2781 resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} 2684 2782 engines: {node: '>=0.10.0'} ··· 3466 3564 hasBin: true 3467 3565 dev: true 3468 3566 3567 + /semver/6.3.0: 3568 + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} 3569 + hasBin: true 3570 + dev: true 3571 + 3469 3572 /semver/7.3.8: 3470 3573 resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} 3471 3574 engines: {node: '>=10'} ··· 3791 3894 source-map-support: 0.5.21 3792 3895 dev: true 3793 3896 3897 + /test-exclude/6.0.0: 3898 + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} 3899 + engines: {node: '>=8'} 3900 + dependencies: 3901 + '@istanbuljs/schema': 0.1.3 3902 + glob: 7.2.3 3903 + minimatch: 3.1.2 3904 + dev: true 3905 + 3794 3906 /text-table/0.2.0: 3795 3907 resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} 3796 3908 dev: true ··· 3816 3928 resolution: {integrity: sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==} 3817 3929 dev: true 3818 3930 3819 - /tinypool/0.3.1: 3820 - resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} 3931 + /tinypool/0.4.0: 3932 + resolution: {integrity: sha512-2ksntHOKf893wSAH4z/+JbPpi92esw8Gn9N2deXX+B0EO92hexAVI9GIZZPx7P5aYo5KULfeOSt3kMOmSOy6uA==} 3821 3933 engines: {node: '>=14.0.0'} 3822 3934 dev: true 3823 3935 ··· 3983 4095 punycode: 2.3.0 3984 4096 dev: true 3985 4097 4098 + /v8-to-istanbul/9.1.0: 4099 + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} 4100 + engines: {node: '>=10.12.0'} 4101 + dependencies: 4102 + '@jridgewell/trace-mapping': 0.3.17 4103 + '@types/istanbul-lib-coverage': 2.0.4 4104 + convert-source-map: 1.9.0 4105 + dev: true 4106 + 3986 4107 /validate-npm-package-license/3.0.4: 3987 4108 resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} 3988 4109 dependencies: ··· 3990 4111 spdx-expression-parse: 3.0.1 3991 4112 dev: true 3992 4113 3993 - /vite-node/0.29.3_67ayhxtn77ihpqz7ip4pro4g64: 3994 - resolution: {integrity: sha512-QYzYSA4Yt2IiduEjYbccfZQfxKp+T1Do8/HEpSX/G5WIECTFKJADwLs9c94aQH4o0A+UtCKU61lj1m5KvbxxQA==} 4114 + /vite-node/0.29.7_67ayhxtn77ihpqz7ip4pro4g64: 4115 + resolution: {integrity: sha512-PakCZLvz37yFfUPWBnLa1OYHPCGm5v4pmRrTcFN4V/N/T3I6tyP3z07S//9w+DdeL7vVd0VSeyMZuAh+449ZWw==} 3995 4116 engines: {node: '>=v14.16.0'} 3996 4117 hasBin: true 3997 4118 dependencies: ··· 4046 4167 fsevents: 2.3.2 4047 4168 dev: true 4048 4169 4049 - /vitest/0.29.3_terser@5.16.6: 4050 - resolution: {integrity: sha512-muMsbXnZsrzDGiyqf/09BKQsGeUxxlyLeLK/sFFM4EXdURPQRv8y7dco32DXaRORYP0bvyN19C835dT23mL0ow==} 4170 + /vitest/0.29.7_terser@5.16.6: 4171 + resolution: {integrity: sha512-aWinOSOu4jwTuZHkb+cCyrqQ116Q9TXaJrNKTHudKBknIpR0VplzeaOUuDF9jeZcrbtQKZQt6yrtd+eakbaxHg==} 4051 4172 engines: {node: '>=v14.16.0'} 4052 4173 hasBin: true 4053 4174 peerDependencies: ··· 4056 4177 '@vitest/ui': '*' 4057 4178 happy-dom: '*' 4058 4179 jsdom: '*' 4180 + safaridriver: '*' 4181 + webdriverio: '*' 4059 4182 peerDependenciesMeta: 4060 4183 '@edge-runtime/vm': 4061 4184 optional: true ··· 4067 4190 optional: true 4068 4191 jsdom: 4069 4192 optional: true 4193 + safaridriver: 4194 + optional: true 4195 + webdriverio: 4196 + optional: true 4070 4197 dependencies: 4071 4198 '@types/chai': 4.3.4 4072 4199 '@types/chai-subset': 1.3.3 4073 4200 '@types/node': 18.15.3 4074 - '@vitest/expect': 0.29.3 4075 - '@vitest/runner': 0.29.3 4076 - '@vitest/spy': 0.29.3 4077 - '@vitest/utils': 0.29.3 4201 + '@vitest/expect': 0.29.7 4202 + '@vitest/runner': 0.29.7 4203 + '@vitest/spy': 0.29.7 4204 + '@vitest/utils': 0.29.7 4078 4205 acorn: 8.8.2 4079 4206 acorn-walk: 8.2.0 4080 4207 cac: 6.7.14 ··· 4087 4214 std-env: 3.3.2 4088 4215 strip-literal: 1.0.1 4089 4216 tinybench: 2.4.0 4090 - tinypool: 0.3.1 4217 + tinypool: 0.4.0 4091 4218 tinyspy: 1.1.1 4092 4219 vite: 4.2.0_67ayhxtn77ihpqz7ip4pro4g64 4093 - vite-node: 0.29.3_67ayhxtn77ihpqz7ip4pro4g64 4220 + vite-node: 0.29.7_67ayhxtn77ihpqz7ip4pro4g64 4094 4221 why-is-node-running: 2.2.2 4095 4222 transitivePeerDependencies: 4096 4223 - less ··· 4242 4369 decamelize: 1.2.0 4243 4370 dev: true 4244 4371 4372 + /yargs-parser/20.2.9: 4373 + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} 4374 + engines: {node: '>=10'} 4375 + dev: true 4376 + 4245 4377 /yargs-parser/21.1.1: 4246 4378 resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 4247 4379 engines: {node: '>=12'} ··· 4262 4394 which-module: 2.0.0 4263 4395 y18n: 4.0.3 4264 4396 yargs-parser: 18.1.3 4397 + dev: true 4398 + 4399 + /yargs/16.2.0: 4400 + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} 4401 + engines: {node: '>=10'} 4402 + dependencies: 4403 + cliui: 7.0.4 4404 + escalade: 3.1.1 4405 + get-caller-file: 2.0.5 4406 + require-directory: 2.1.1 4407 + string-width: 4.2.3 4408 + y18n: 5.0.8 4409 + yargs-parser: 20.2.9 4265 4410 dev: true 4266 4411 4267 4412 /yargs/17.7.1:
+1 -1
src/__tests__/__snapshots__/parser.test.ts.snap
··· 1 1 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 2 3 - exports[`print > prints the kitchen sink document like graphql.js does 1`] = ` 3 + exports[`parse > parses the kitchen sink document like graphql.js does 1`] = ` 4 4 { 5 5 "definitions": [ 6 6 {
+53
src/__tests__/error.test.ts
··· 1 + import { describe, it, expect } from 'vitest'; 2 + 3 + import { Kind } from '../kind'; 4 + import { GraphQLError as graphql_GraphQLError } from 'graphql'; 5 + import { GraphQLError } from '../error'; 6 + 7 + describe('GraphQLError', () => { 8 + it('sorts input arguments into properties', () => { 9 + const inputs = ['message', [], { body: '' }, [], [], new Error(), {}] as const; 10 + 11 + const error = new GraphQLError(...inputs); 12 + expect(error).toMatchInlineSnapshot('[GraphQLError: message]'); 13 + expect(error).toEqual(new (graphql_GraphQLError as any)(...inputs)); 14 + }); 15 + 16 + it('normalizes incoming nodes to arrays', () => { 17 + const error = new GraphQLError('message', { kind: Kind.NULL }); 18 + expect(error.nodes).toEqual([{ kind: Kind.NULL }]); 19 + }); 20 + 21 + it('allows toJSON and toString calls', () => { 22 + const error = new GraphQLError('message'); 23 + expect(error.toString()).toEqual('message'); 24 + expect(error.toJSON()).toEqual({ 25 + name: 'GraphQLError', 26 + message: 'message', 27 + extensions: {}, 28 + locations: undefined, 29 + nodes: undefined, 30 + originalError: undefined, 31 + path: undefined, 32 + positions: undefined, 33 + source: undefined, 34 + }); 35 + }); 36 + 37 + it('normalizes extensions as expected', () => { 38 + const inputs = (extensions: any, originalError = new Error()) => 39 + ['message', [], { body: '' }, [], [], originalError, extensions] as const; 40 + 41 + expect(new GraphQLError(...inputs(undefined)).extensions).toEqual({}); 42 + expect(new GraphQLError(...inputs({ test: true })).extensions).toEqual({ test: true }); 43 + 44 + expect( 45 + new GraphQLError(...inputs({ test: true }, { extensions: { override: true } } as any)) 46 + .extensions 47 + ).toEqual({ test: true }); 48 + 49 + expect( 50 + new GraphQLError(...inputs(undefined, { extensions: { override: true } } as any)).extensions 51 + ).toEqual({ override: true }); 52 + }); 53 + });
+390 -108
src/__tests__/parser.test.ts
··· 5 5 import { parse, parseType, parseValue } from '../parser'; 6 6 import { Kind } from '../kind'; 7 7 8 - describe('print', () => { 9 - it('prints the kitchen sink document like graphql.js does', () => { 8 + describe('parse', () => { 9 + it('parses the kitchen sink document like graphql.js does', () => { 10 10 const sink = readFileSync(__dirname + '/../../benchmark/kitchen_sink.graphql', { 11 11 encoding: 'utf8', 12 12 }); ··· 15 15 expect(doc).toEqual(graphql_parse(sink, { noLocation: true })); 16 16 }); 17 17 18 - it('parse provides errors', () => { 18 + it('parses basic documents', () => { 19 19 expect(() => parse('{')).toThrow(); 20 + expect(() => parse('{}x ')).toThrow(); 21 + expect(() => parse('{ field }')).not.toThrow(); 22 + expect(() => parse({ body: '{ field }' })).not.toThrow(); 20 23 }); 21 24 22 25 it('parses variable inline values', () => { ··· 99 102 }).not.toThrow(); 100 103 }); 101 104 105 + it('parses fragment definitions', () => { 106 + expect(() => parse('fragment { test }')).toThrow(); 107 + expect(() => parse('fragment name { test }')).toThrow(); 108 + expect(() => parse('fragment name on name')).toThrow(); 109 + expect(() => parse('fragment Name on Type { field }')).not.toThrow(); 110 + }); 111 + 112 + it('parses fields', () => { 113 + expect(() => parse('{ field: }')).toThrow(); 114 + expect(() => parse('{ alias: field() }')).toThrow(); 115 + 116 + expect(parse('{ alias: field { child } }').definitions[0]).toHaveProperty( 117 + 'selectionSet.selections.0', 118 + { 119 + kind: Kind.FIELD, 120 + directives: [], 121 + arguments: [], 122 + alias: { 123 + kind: Kind.NAME, 124 + value: 'alias', 125 + }, 126 + name: { 127 + kind: Kind.NAME, 128 + value: 'field', 129 + }, 130 + selectionSet: { 131 + kind: Kind.SELECTION_SET, 132 + selections: [ 133 + { 134 + kind: Kind.FIELD, 135 + directives: [], 136 + arguments: [], 137 + name: { 138 + kind: Kind.NAME, 139 + value: 'child', 140 + }, 141 + }, 142 + ], 143 + }, 144 + } 145 + ); 146 + }); 147 + 148 + it('parses arguments', () => { 149 + expect(() => parse('{ field() }')).toThrow(); 150 + expect(() => parse('{ field(name) }')).toThrow(); 151 + expect(() => parse('{ field(name:) }')).toThrow(); 152 + expect(() => parse('{ field(name: null }')).toThrow(); 153 + 154 + expect(parse('{ field(name: null) }').definitions[0]).toMatchObject({ 155 + kind: Kind.OPERATION_DEFINITION, 156 + selectionSet: { 157 + kind: Kind.SELECTION_SET, 158 + selections: [ 159 + { 160 + kind: Kind.FIELD, 161 + name: { 162 + kind: Kind.NAME, 163 + value: 'field', 164 + }, 165 + arguments: [ 166 + { 167 + kind: Kind.ARGUMENT, 168 + name: { 169 + kind: Kind.NAME, 170 + value: 'name', 171 + }, 172 + value: { 173 + kind: Kind.NULL, 174 + }, 175 + }, 176 + ], 177 + }, 178 + ], 179 + }, 180 + }); 181 + }); 182 + 183 + it('parses directives', () => { 184 + expect(() => parse('{ field @ }')).toThrow(); 185 + expect(() => parse('{ field @(test: null) }')).toThrow(); 186 + 187 + expect(parse('{ field @test(name: null) }')).toHaveProperty( 188 + 'definitions.0.selectionSet.selections.0.directives.0', 189 + { 190 + kind: Kind.DIRECTIVE, 191 + name: { 192 + kind: Kind.NAME, 193 + value: 'test', 194 + }, 195 + arguments: [ 196 + { 197 + kind: Kind.ARGUMENT, 198 + name: { 199 + kind: Kind.NAME, 200 + value: 'name', 201 + }, 202 + value: { 203 + kind: Kind.NULL, 204 + }, 205 + }, 206 + ], 207 + } 208 + ); 209 + }); 210 + 211 + it('parses inline fragments', () => { 212 + expect(() => parse('{ ... on Test }')).toThrow(); 213 + expect(() => parse('{ ... {} }')).toThrow(); 214 + expect(() => parse('{ ... }')).toThrow(); 215 + 216 + expect(parse('{ ... on Test { field } }')).toHaveProperty( 217 + 'definitions.0.selectionSet.selections.0', 218 + { 219 + kind: Kind.INLINE_FRAGMENT, 220 + directives: [], 221 + typeCondition: { 222 + kind: Kind.NAMED_TYPE, 223 + name: { 224 + kind: Kind.NAME, 225 + value: 'Test', 226 + }, 227 + }, 228 + selectionSet: expect.any(Object), 229 + } 230 + ); 231 + 232 + expect(parse('{ ... { field } }')).toHaveProperty('definitions.0.selectionSet.selections.0', { 233 + kind: Kind.INLINE_FRAGMENT, 234 + directives: [], 235 + typeCondition: undefined, 236 + selectionSet: expect.any(Object), 237 + }); 238 + }); 239 + 240 + it('parses variable definitions', () => { 241 + expect(() => parse('query ( { test }')).toThrow(); 242 + expect(() => parse('query ($var) { test }')).toThrow(); 243 + expect(() => parse('query ($var:) { test }')).toThrow(); 244 + expect(() => parse('query ($var: Int =) { test }')).toThrow(); 245 + 246 + expect(parse('query ($var: Int = 1) { test }').definitions[0]).toMatchObject({ 247 + kind: Kind.OPERATION_DEFINITION, 248 + operation: 'query', 249 + directives: [], 250 + selectionSet: expect.any(Object), 251 + variableDefinitions: [ 252 + { 253 + kind: Kind.VARIABLE_DEFINITION, 254 + type: { 255 + kind: Kind.NAMED_TYPE, 256 + name: { 257 + kind: Kind.NAME, 258 + value: 'Int', 259 + }, 260 + }, 261 + variable: { 262 + kind: Kind.VARIABLE, 263 + name: { 264 + kind: Kind.NAME, 265 + value: 'var', 266 + }, 267 + }, 268 + defaultValue: { 269 + kind: Kind.INT, 270 + value: '1', 271 + }, 272 + }, 273 + ], 274 + }); 275 + }); 276 + 102 277 it('creates ast', () => { 103 278 const result = parse(` 104 279 { ··· 235 410 const result = parse('{ id }', { noLocation: true }); 236 411 expect('loc' in result).toBe(false); 237 412 }); 413 + }); 238 414 239 - describe('parseValue', () => { 240 - it('parses null value', () => { 241 - const result = parseValue('null'); 242 - expect(result).toEqual({ kind: Kind.NULL }); 415 + describe('parseValue', () => { 416 + it('parses basic values', () => { 417 + expect(() => parseValue('')).toThrow(); 418 + expect(parseValue('null')).toEqual({ kind: Kind.NULL }); 419 + expect(parseValue({ body: 'null' })).toEqual({ kind: Kind.NULL }); 420 + }); 421 + 422 + it('parses list values', () => { 423 + const result = parseValue('[123 "abc"]'); 424 + expect(result).toEqual({ 425 + kind: Kind.LIST, 426 + values: [ 427 + { 428 + kind: Kind.INT, 429 + value: '123', 430 + }, 431 + { 432 + kind: Kind.STRING, 433 + value: 'abc', 434 + block: false, 435 + }, 436 + ], 243 437 }); 438 + }); 244 439 245 - it('parses list values', () => { 246 - const result = parseValue('[123 "abc"]'); 247 - expect(result).toEqual({ 248 - kind: Kind.LIST, 249 - values: [ 250 - { 251 - kind: Kind.INT, 252 - value: '123', 253 - }, 254 - { 255 - kind: Kind.STRING, 256 - value: 'abc', 257 - block: false, 258 - }, 259 - ], 260 - }); 440 + it('parses integers', () => { 441 + expect(parseValue('12')).toEqual({ 442 + kind: Kind.INT, 443 + value: '12', 261 444 }); 262 445 263 - it('parses block strings', () => { 264 - const result = parseValue('["""long""" "short"]'); 265 - expect(result).toEqual({ 266 - kind: Kind.LIST, 267 - values: [ 268 - { 269 - kind: Kind.STRING, 270 - value: 'long', 271 - block: true, 446 + expect(parseValue('-12')).toEqual({ 447 + kind: Kind.INT, 448 + value: '-12', 449 + }); 450 + }); 451 + 452 + it('parses floats', () => { 453 + expect(parseValue('12e2')).toEqual({ 454 + kind: Kind.FLOAT, 455 + value: '12e2', 456 + }); 457 + 458 + expect(parseValue('0.2E3')).toEqual({ 459 + kind: Kind.FLOAT, 460 + value: '0.2E3', 461 + }); 462 + 463 + expect(parseValue('-1.2e+3')).toEqual({ 464 + kind: Kind.FLOAT, 465 + value: '-1.2e+3', 466 + }); 467 + }); 468 + 469 + it('parses strings', () => { 470 + expect(parseValue('"test"')).toEqual({ 471 + kind: Kind.STRING, 472 + value: 'test', 473 + block: false, 474 + }); 475 + 476 + expect(parseValue('"\\t\\t"')).toEqual({ 477 + kind: Kind.STRING, 478 + value: '\t\t', 479 + block: false, 480 + }); 481 + }); 482 + 483 + it('parses objects', () => { 484 + expect(parseValue('{}')).toEqual({ 485 + kind: Kind.OBJECT, 486 + fields: [], 487 + }); 488 + 489 + expect(() => parseValue('{name}')).toThrow(); 490 + expect(() => parseValue('{name:}')).toThrow(); 491 + expect(() => parseValue('{name:null')).toThrow(); 492 + 493 + expect(parseValue('{name:null}')).toEqual({ 494 + kind: Kind.OBJECT, 495 + fields: [ 496 + { 497 + kind: Kind.OBJECT_FIELD, 498 + name: { 499 + kind: Kind.NAME, 500 + value: 'name', 272 501 }, 273 - { 274 - kind: Kind.STRING, 275 - value: 'short', 276 - block: false, 502 + value: { 503 + kind: Kind.NULL, 277 504 }, 278 - ], 279 - }); 505 + }, 506 + ], 507 + }); 508 + }); 509 + 510 + it('parses lists', () => { 511 + expect(parseValue('[]')).toEqual({ 512 + kind: Kind.LIST, 513 + values: [], 514 + }); 515 + 516 + expect(() => parseValue('[')).toThrow(); 517 + expect(() => parseValue('[null')).toThrow(); 518 + 519 + expect(parseValue('[null]')).toEqual({ 520 + kind: Kind.LIST, 521 + values: [ 522 + { 523 + kind: Kind.NULL, 524 + }, 525 + ], 280 526 }); 527 + }); 281 528 282 - it('allows variables', () => { 283 - const result = parseValue('{ field: $var }'); 284 - expect(result).toEqual({ 285 - kind: Kind.OBJECT, 286 - fields: [ 287 - { 288 - kind: Kind.OBJECT_FIELD, 529 + it('parses block strings', () => { 530 + expect(parseValue('["""long""" "short"]')).toEqual({ 531 + kind: Kind.LIST, 532 + values: [ 533 + { 534 + kind: Kind.STRING, 535 + value: 'long', 536 + block: true, 537 + }, 538 + { 539 + kind: Kind.STRING, 540 + value: 'short', 541 + block: false, 542 + }, 543 + ], 544 + }); 545 + 546 + expect(parseValue('"""\n\n first\n second\n"""')).toEqual({ 547 + kind: Kind.STRING, 548 + value: 'first\nsecond', 549 + block: true, 550 + }); 551 + }); 552 + 553 + it('allows variables', () => { 554 + const result = parseValue('{ field: $var }'); 555 + expect(result).toEqual({ 556 + kind: Kind.OBJECT, 557 + fields: [ 558 + { 559 + kind: Kind.OBJECT_FIELD, 560 + name: { 561 + kind: Kind.NAME, 562 + value: 'field', 563 + }, 564 + value: { 565 + kind: Kind.VARIABLE, 289 566 name: { 290 567 kind: Kind.NAME, 291 - value: 'field', 292 - }, 293 - value: { 294 - kind: Kind.VARIABLE, 295 - name: { 296 - kind: Kind.NAME, 297 - value: 'var', 298 - }, 568 + value: 'var', 299 569 }, 300 570 }, 301 - ], 302 - }); 571 + }, 572 + ], 303 573 }); 574 + }); 304 575 305 - it('correct message for incomplete variable', () => { 306 - expect(() => { 307 - return parseValue('$'); 308 - }).toThrow(); 576 + it('correct message for incomplete variable', () => { 577 + expect(() => { 578 + return parseValue('$'); 579 + }).toThrow(); 580 + }); 581 + 582 + it('correct message for unexpected token', () => { 583 + expect(() => { 584 + return parseValue(':'); 585 + }).toThrow(); 586 + }); 587 + }); 588 + 589 + describe('parseType', () => { 590 + it('parses basic types', () => { 591 + expect(() => parseType('')).toThrow(); 592 + expect(() => parseType('Type')).not.toThrow(); 593 + expect(() => parseType({ body: 'Type' })).not.toThrow(); 594 + }); 595 + 596 + it('throws on invalid inputs', () => { 597 + expect(() => parseType('!')).toThrow(); 598 + expect(() => parseType('[String')).toThrow(); 599 + expect(() => parseType('[String!')).toThrow(); 600 + }); 601 + 602 + it('parses well known types', () => { 603 + const result = parseType('String'); 604 + expect(result).toEqual({ 605 + kind: Kind.NAMED_TYPE, 606 + name: { 607 + kind: Kind.NAME, 608 + value: 'String', 609 + }, 309 610 }); 611 + }); 310 612 311 - it('correct message for unexpected token', () => { 312 - expect(() => { 313 - return parseValue(':'); 314 - }).toThrow(); 613 + it('parses custom types', () => { 614 + const result = parseType('MyType'); 615 + expect(result).toEqual({ 616 + kind: Kind.NAMED_TYPE, 617 + name: { 618 + kind: Kind.NAME, 619 + value: 'MyType', 620 + }, 315 621 }); 316 622 }); 317 623 318 - describe('parseType', () => { 319 - it('parses well known types', () => { 320 - const result = parseType('String'); 321 - expect(result).toEqual({ 624 + it('parses list types', () => { 625 + const result = parseType('[MyType]'); 626 + expect(result).toEqual({ 627 + kind: Kind.LIST_TYPE, 628 + type: { 322 629 kind: Kind.NAMED_TYPE, 323 630 name: { 324 631 kind: Kind.NAME, 325 - value: 'String', 632 + value: 'MyType', 326 633 }, 327 - }); 634 + }, 328 635 }); 636 + }); 329 637 330 - it('parses custom types', () => { 331 - const result = parseType('MyType'); 332 - expect(result).toEqual({ 638 + it('parses non-null types', () => { 639 + const result = parseType('MyType!'); 640 + expect(result).toEqual({ 641 + kind: Kind.NON_NULL_TYPE, 642 + type: { 333 643 kind: Kind.NAMED_TYPE, 334 644 name: { 335 645 kind: Kind.NAME, 336 646 value: 'MyType', 337 647 }, 338 - }); 339 - }); 340 - 341 - it('parses list types', () => { 342 - const result = parseType('[MyType]'); 343 - expect(result).toEqual({ 344 - kind: Kind.LIST_TYPE, 345 - type: { 346 - kind: Kind.NAMED_TYPE, 347 - name: { 348 - kind: Kind.NAME, 349 - value: 'MyType', 350 - }, 351 - }, 352 - }); 648 + }, 353 649 }); 650 + }); 354 651 355 - it('parses non-null types', () => { 356 - const result = parseType('MyType!'); 357 - expect(result).toEqual({ 652 + it('parses nested types', () => { 653 + const result = parseType('[MyType!]'); 654 + expect(result).toEqual({ 655 + kind: Kind.LIST_TYPE, 656 + type: { 358 657 kind: Kind.NON_NULL_TYPE, 359 658 type: { 360 659 kind: Kind.NAMED_TYPE, ··· 363 662 value: 'MyType', 364 663 }, 365 664 }, 366 - }); 367 - }); 368 - 369 - it('parses nested types', () => { 370 - const result = parseType('[MyType!]'); 371 - expect(result).toEqual({ 372 - kind: Kind.LIST_TYPE, 373 - type: { 374 - kind: Kind.NON_NULL_TYPE, 375 - type: { 376 - kind: Kind.NAMED_TYPE, 377 - name: { 378 - kind: Kind.NAME, 379 - value: 'MyType', 380 - }, 381 - }, 382 - }, 383 - }); 665 + }, 384 666 }); 385 667 }); 386 668 });
+79 -6
src/__tests__/printer.test.ts
··· 2 2 import { readFileSync } from 'fs'; 3 3 4 4 import { parse, print as graphql_print } from 'graphql'; 5 - import { print } from '../printer'; 5 + import { print, printString, printBlockString } from '../printer'; 6 6 7 7 function dedentString(string) { 8 8 const trimmedStr = string ··· 26 26 return dedentString(str); 27 27 } 28 28 29 + describe('printString', () => { 30 + it('prints strings as expected', () => { 31 + expect(printString('test')).toEqual('"test"'); 32 + expect(printString('\n')).toEqual('"\\n"'); 33 + }); 34 + }); 35 + 36 + describe('printBlockString', () => { 37 + it('prints block strings as expected', () => { 38 + expect(printBlockString('test')).toEqual('"""\ntest\n"""'); 39 + expect(printBlockString('\n')).toEqual('"""\n\n\n"""'); 40 + expect(printBlockString('"""')).toEqual('"""\n\\"""\n"""'); 41 + }); 42 + }); 43 + 29 44 describe('print', () => { 30 45 it('prints the kitchen sink document like graphql.js does', () => { 31 46 const sink = JSON.parse(readFileSync(__dirname + '/kitchen_sink.json', { encoding: 'utf8' })); ··· 35 50 }); 36 51 37 52 it('prints minimal ast', () => { 38 - const ast = { 39 - kind: 'Field', 40 - name: { kind: 'Name', value: 'foo' }, 41 - }; 42 - expect(print(ast as any)).toBe('foo'); 53 + expect( 54 + print({ 55 + kind: 'Field', 56 + name: { kind: 'Name', value: 'foo' }, 57 + } as any) 58 + ).toBe('foo'); 59 + 60 + expect( 61 + print({ 62 + kind: 'Name', 63 + value: 'foo', 64 + } as any) 65 + ).toBe('foo'); 66 + 67 + expect( 68 + print({ 69 + kind: 'Document', 70 + definitions: [], 71 + } as any) 72 + ).toBe(''); 73 + }); 74 + 75 + it('prints integers and floats', () => { 76 + expect( 77 + print({ 78 + kind: 'IntValue', 79 + value: '12', 80 + } as any) 81 + ).toBe('12'); 82 + 83 + expect( 84 + print({ 85 + kind: 'FloatValue', 86 + value: '12e2', 87 + } as any) 88 + ).toBe('12e2'); 89 + }); 90 + 91 + it('prints lists of values', () => { 92 + expect( 93 + print({ 94 + kind: 'ListValue', 95 + values: [{ kind: 'NullValue' }], 96 + } as any) 97 + ).toBe('[null]'); 98 + }); 99 + 100 + it('prints types', () => { 101 + expect( 102 + print({ 103 + kind: 'ListType', 104 + type: { 105 + kind: 'NonNullType', 106 + type: { 107 + kind: 'NamedType', 108 + name: { 109 + kind: 'Name', 110 + value: 'Type', 111 + }, 112 + }, 113 + }, 114 + } as any) 115 + ).toBe('[Type!]'); 43 116 }); 44 117 45 118 // NOTE: The shim won't throw for invalid AST nodes
+27
src/__tests__/visitor.test.ts
··· 50 50 expect(() => visit(ast, {})).not.toThrow(); 51 51 }); 52 52 53 + it('handles noop visitor', () => { 54 + const ast = parse('{ a, b }', { noLocation: true }); 55 + expect(() => 56 + visit(ast, { 57 + enter() { 58 + /*noop*/ 59 + }, 60 + }) 61 + ).not.toThrow(); 62 + 63 + expect(() => 64 + visit(ast, { 65 + enter(node) { 66 + return node; 67 + }, 68 + }) 69 + ).not.toThrow(); 70 + 71 + expect(() => 72 + visit(ast, { 73 + enter() { 74 + throw new Error(); 75 + }, 76 + }) 77 + ).toThrow(); 78 + }); 79 + 53 80 it('validates path argument', () => { 54 81 const visited: any[] = []; 55 82
+1 -1
src/error.ts
··· 42 42 } 43 43 44 44 toJSON(): any { 45 - return { ...this }; 45 + return { ...this, message: this.message }; 46 46 } 47 47 48 48 toString() {
+17 -18
src/parser.ts
··· 25 25 } 26 26 27 27 const leadingRe = / +(?=[^\s])/y; 28 - export function blockString(string: string) { 28 + function blockString(string: string) { 29 29 const lines = string.split('\n'); 30 30 let out = ''; 31 31 let commonIndent = 0; ··· 78 78 79 79 const constRe = /null|true|false/y; 80 80 const variableRe = /\$[_\w][_\d\w]*/y; 81 - const intRe = /[-]?\d+/y; 82 - const floatRe = /(?:[-]?\d+)?(?:\.\d+)(?:[eE][+-]?\d+)?/y; 81 + const intRe = /-?\d+/y; 82 + const floatPartRe = /(?:\.\d+)?(?:[eE][+-]?\d+)?/y; 83 83 const complexStringRe = /\\/g; 84 84 const blockStringRe = /"""(?:[\s\S]+(?="""))?"""/y; 85 85 const stringRe = /"(?:[^"\r\n]+)?"/y; ··· 108 108 value: match.slice(1), 109 109 }, 110 110 }; 111 - } else if ((match = advance(floatRe))) { 112 - out = { 113 - kind: 'FloatValue' as Kind.FLOAT, 114 - value: match, 115 - }; 116 111 } else if ((match = advance(intRe))) { 117 - out = { 118 - kind: 'IntValue' as Kind.INT, 119 - value: match, 120 - }; 112 + const intPart = match; 113 + if ((match = advance(floatPartRe))) { 114 + out = { 115 + kind: 'FloatValue' as Kind.FLOAT, 116 + value: intPart + match, 117 + }; 118 + } else { 119 + out = { 120 + kind: 'IntValue' as Kind.INT, 121 + value: intPart, 122 + }; 123 + } 121 124 } else if ((match = advance(nameRe))) { 122 125 out = { 123 126 kind: 'EnumValue' as Kind.ENUM, ··· 255 258 } 256 259 } 257 260 258 - function type(): ast.TypeNode | undefined { 261 + function type(): ast.TypeNode { 259 262 let match: ast.NameNode | ast.TypeNode | undefined; 260 263 ignored(); 261 264 if (input.charCodeAt(idx) === 91 /*'['*/) { ··· 360 363 ignored(); 361 364 if (input.charCodeAt(idx++) !== 58 /*':'*/) throw error('VariableDefinition'); 362 365 const _type = type(); 363 - if (!_type) throw error('VariableDefinition'); 364 366 let _defaultValue: ast.ValueNode | undefined; 365 367 if (input.charCodeAt(idx) === 61 /*'='*/) { 366 368 idx++; ··· 441 443 ignored(); 442 444 const definitions: ast.ExecutableDefinitionNode[] = []; 443 445 while ((match = fragmentDefinition() || operationDefinition())) definitions.push(match); 444 - if (idx !== input.length) throw error('Document'); 445 446 return { 446 447 kind: 'Document' as Kind.DOCUMENT, 447 448 definitions, ··· 479 480 ): ast.TypeNode { 480 481 input = typeof string.body === 'string' ? string.body : string; 481 482 idx = 0; 482 - const _type = type(); 483 - if (!_type) throw error('TypeNode'); 484 - return _type; 483 + return type(); 485 484 }
+1 -3
src/visitor.ts
··· 48 48 result = traverse(value[index], index, value); 49 49 path.pop(); 50 50 ancestors.pop(); 51 - if (result === undefined) { 52 - newValue.push(value[index]); 53 - } else if (result === null) { 51 + if (result == null) { 54 52 hasEdited = true; 55 53 } else { 56 54 hasEdited = hasEdited || result !== value[index];
+13
vitest.config.ts
··· 1 + import { defineConfig } from 'vitest/config'; 2 + 3 + export default defineConfig({ 4 + test: { 5 + coverage: { 6 + enabled: true, 7 + provider: 'c8', 8 + 100: true, 9 + }, 10 + globals: false, 11 + clearMocks: true, 12 + }, 13 + });