diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f68dac5..eff38bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,7 @@ jobs: - name: Build, Test & Coverage run: | npm run build + npm run test:types npm run test:coverage - name: Coveralls uses: coverallsapp/github-action@master diff --git a/.gitignore b/.gitignore index a983fdb..502d450 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ coverage # Dotenv files .env.local .env.*.local + +# Vitest typecheck +*-temp.json diff --git a/package-lock.json b/package-lock.json index 20aef02..4a6bb20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "tsx": "^3.10.1", "typescript": "^4.8.4", "vite-tsconfig-paths": "^3.5.1", - "vitest": "^0.24.1" + "vitest": "^0.27.3" }, "engines": { "node": "^14.16.0 || ^16.10.0 || ^18.0.0" @@ -1345,9 +1345,9 @@ "dev": true }, "node_modules/@types/chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, "node_modules/@types/chai-subset": { @@ -1613,10 +1613,62 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@vitest/coverage-istanbul/node_modules/vitest": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.24.1.tgz", + "integrity": "sha512-NKkK1xnDIOOr42pKBfGQQl6b6IWdFVBpG6ZS1T+nUlJuqcOiZ7lxjVwHy9wrtTYpJ0BWww9y6bSGYXubD29Nag==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.3", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "chai": "^4.3.6", + "debug": "^4.3.4", + "local-pkg": "^0.4.2", + "strip-literal": "^0.4.2", + "tinybench": "^2.3.0", + "tinypool": "^0.3.0", + "tinyspy": "^1.0.2", + "vite": "^3.0.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.16.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2051,14 +2103,14 @@ } }, "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -2546,15 +2598,15 @@ } }, "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, "dependencies": { "type-detect": "^4.0.0" }, "engines": { - "node": ">=0.12" + "node": ">=6" } }, "node_modules/deep-extend": { @@ -4916,6 +4968,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -5682,6 +5740,24 @@ "node": ">= 6" } }, + "node_modules/mlly": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz", + "integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==", + "dev": true, + "dependencies": { + "acorn": "^8.8.1", + "pathe": "^1.0.0", + "pkg-types": "^1.0.1", + "ufo": "^1.0.1" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", + "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "dev": true + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -8698,6 +8774,12 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-0.2.0.tgz", + "integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==", + "dev": true + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -8826,6 +8908,23 @@ "node": ">=4" } }, + "node_modules/pkg-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz", + "integrity": "sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.0.0", + "pathe": "^1.0.0" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", + "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "dev": true + }, "node_modules/postcss": { "version": "8.4.17", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz", @@ -9586,6 +9685,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -9827,6 +9932,18 @@ "node": ">= 6" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.1.tgz", + "integrity": "sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==", + "dev": true + }, "node_modules/stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -10235,9 +10352,9 @@ } }, "node_modules/tinybench": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.0.tgz", - "integrity": "sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.1.tgz", + "integrity": "sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==", "dev": true }, "node_modules/tinypool": { @@ -10555,6 +10672,12 @@ "node": ">=4.2.0" } }, + "node_modules/ufo": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz", + "integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==", + "dev": true + }, "node_modules/uglify-js": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", @@ -10714,6 +10837,31 @@ } } }, + "node_modules/vite-node": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.27.3.tgz", + "integrity": "sha512-eyJYOO64o5HIp8poc4bJX+ZNBwMZeI3f6/JdiUmJgW02Mt7LnoCtDMRVmLaY9S05SIsjGe339ZK4uo2wQ+bF9g==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.1.0", + "pathe": "^0.2.0", + "picocolors": "^1.0.0", + "source-map": "^0.6.1", + "source-map-support": "^0.5.21", + "vite": "^3.0.0 || ^4.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.16.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/vite-tsconfig-paths": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-3.5.1.tgz", @@ -10759,22 +10907,30 @@ } }, "node_modules/vitest": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.24.1.tgz", - "integrity": "sha512-NKkK1xnDIOOr42pKBfGQQl6b6IWdFVBpG6ZS1T+nUlJuqcOiZ7lxjVwHy9wrtTYpJ0BWww9y6bSGYXubD29Nag==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.27.3.tgz", + "integrity": "sha512-Ld3UVgRVhJUtqvQ3dW89GxiApFAgBsWJZBCWzK+gA3w2yG68csXlGZZ4WDJURf+8ecNfgrScga6xY+8YSOpiMg==", "dev": true, "dependencies": { - "@types/chai": "^4.3.3", + "@types/chai": "^4.3.4", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "chai": "^4.3.6", + "acorn": "^8.8.1", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", "debug": "^4.3.4", "local-pkg": "^0.4.2", - "strip-literal": "^0.4.2", - "tinybench": "^2.3.0", + "picocolors": "^1.0.0", + "source-map": "^0.6.1", + "std-env": "^3.3.1", + "strip-literal": "^1.0.0", + "tinybench": "^2.3.1", "tinypool": "^0.3.0", "tinyspy": "^1.0.2", - "vite": "^3.0.0" + "vite": "^3.0.0 || ^4.0.0", + "vite-node": "0.27.3", + "why-is-node-running": "^2.2.2" }, "bin": { "vitest": "vitest.mjs" @@ -10810,6 +10966,18 @@ } } }, + "node_modules/vitest/node_modules/strip-literal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.0.tgz", + "integrity": "sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==", + "dev": true, + "dependencies": { + "acorn": "^8.8.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -10857,6 +11025,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -12062,9 +12246,9 @@ "dev": true }, "@types/chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, "@types/chai-subset": { @@ -12236,12 +12420,33 @@ "istanbul-reports": "^3.1.5", "test-exclude": "^6.0.0", "vitest": "0.24.1" + }, + "dependencies": { + "vitest": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.24.1.tgz", + "integrity": "sha512-NKkK1xnDIOOr42pKBfGQQl6b6IWdFVBpG6ZS1T+nUlJuqcOiZ7lxjVwHy9wrtTYpJ0BWww9y6bSGYXubD29Nag==", + "dev": true, + "requires": { + "@types/chai": "^4.3.3", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "chai": "^4.3.6", + "debug": "^4.3.4", + "local-pkg": "^0.4.2", + "strip-literal": "^0.4.2", + "tinybench": "^2.3.0", + "tinypool": "^0.3.0", + "tinyspy": "^1.0.2", + "vite": "^3.0.0" + } + } } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-jsx": { @@ -12547,14 +12752,14 @@ } }, "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -12923,9 +13128,9 @@ } }, "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -14577,6 +14782,12 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -15128,6 +15339,26 @@ "kind-of": "^6.0.3" } }, + "mlly": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz", + "integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==", + "dev": true, + "requires": { + "acorn": "^8.8.1", + "pathe": "^1.0.0", + "pkg-types": "^1.0.1", + "ufo": "^1.0.1" + }, + "dependencies": { + "pathe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", + "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "dev": true + } + } + }, "modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -17234,6 +17465,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pathe": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-0.2.0.tgz", + "integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==", + "dev": true + }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -17325,6 +17562,25 @@ } } }, + "pkg-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz", + "integrity": "sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==", + "dev": true, + "requires": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.0.0", + "pathe": "^1.0.0" + }, + "dependencies": { + "pathe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", + "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "dev": true + } + } + }, "postcss": { "version": "8.4.17", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz", @@ -17867,6 +18123,12 @@ "object-inspect": "^1.9.0" } }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -18066,6 +18328,18 @@ } } }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "std-env": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.1.tgz", + "integrity": "sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==", + "dev": true + }, "stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -18370,9 +18644,9 @@ } }, "tinybench": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.0.tgz", - "integrity": "sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.1.tgz", + "integrity": "sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==", "dev": true }, "tinypool": { @@ -18597,6 +18871,12 @@ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true }, + "ufo": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz", + "integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==", + "dev": true + }, "uglify-js": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", @@ -18708,6 +18988,22 @@ } } }, + "vite-node": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.27.3.tgz", + "integrity": "sha512-eyJYOO64o5HIp8poc4bJX+ZNBwMZeI3f6/JdiUmJgW02Mt7LnoCtDMRVmLaY9S05SIsjGe339ZK4uo2wQ+bF9g==", + "dev": true, + "requires": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.1.0", + "pathe": "^0.2.0", + "picocolors": "^1.0.0", + "source-map": "^0.6.1", + "source-map-support": "^0.5.21", + "vite": "^3.0.0 || ^4.0.0" + } + }, "vite-tsconfig-paths": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-3.5.1.tgz", @@ -18734,22 +19030,41 @@ } }, "vitest": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.24.1.tgz", - "integrity": "sha512-NKkK1xnDIOOr42pKBfGQQl6b6IWdFVBpG6ZS1T+nUlJuqcOiZ7lxjVwHy9wrtTYpJ0BWww9y6bSGYXubD29Nag==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.27.3.tgz", + "integrity": "sha512-Ld3UVgRVhJUtqvQ3dW89GxiApFAgBsWJZBCWzK+gA3w2yG68csXlGZZ4WDJURf+8ecNfgrScga6xY+8YSOpiMg==", "dev": true, "requires": { - "@types/chai": "^4.3.3", + "@types/chai": "^4.3.4", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "chai": "^4.3.6", + "acorn": "^8.8.1", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", "debug": "^4.3.4", "local-pkg": "^0.4.2", - "strip-literal": "^0.4.2", - "tinybench": "^2.3.0", + "picocolors": "^1.0.0", + "source-map": "^0.6.1", + "std-env": "^3.3.1", + "strip-literal": "^1.0.0", + "tinybench": "^2.3.1", "tinypool": "^0.3.0", "tinyspy": "^1.0.2", - "vite": "^3.0.0" + "vite": "^3.0.0 || ^4.0.0", + "vite-node": "0.27.3", + "why-is-node-running": "^2.2.2" + }, + "dependencies": { + "strip-literal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.0.tgz", + "integrity": "sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==", + "dev": true, + "requires": { + "acorn": "^8.8.1" + } + } } }, "webidl-conversions": { @@ -18790,6 +19105,16 @@ "is-symbol": "^1.0.3" } }, + "why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index a51cf15..b84dd39 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "release": "npx semantic-release", "release:dry": "npx semantic-release --dry-run", "test": "vitest", + "test:types": "vitest typecheck", "test:coverage": "vitest --coverage" }, "repository": { @@ -98,7 +99,7 @@ "tsx": "^3.10.1", "typescript": "^4.8.4", "vite-tsconfig-paths": "^3.5.1", - "vitest": "^0.24.1" + "vitest": "^0.27.3" }, "commitlint": { "extends": [ diff --git a/src/__tests__/@helpers/index.ts b/src/__tests__/@helpers/index.ts index b2607d8..5fdb54f 100644 --- a/src/__tests__/@helpers/index.ts +++ b/src/__tests__/@helpers/index.ts @@ -112,4 +112,4 @@ export const expectedParsers = [ 'whole' ] as const -export { describe, expect, it } from 'vitest' +export { describe, expect, it, assertType, expectTypeOf } from 'vitest' diff --git a/src/__tests__/combinators.spec-d.ts b/src/__tests__/combinators.spec-d.ts new file mode 100644 index 0000000..3940c1f --- /dev/null +++ b/src/__tests__/combinators.spec-d.ts @@ -0,0 +1,224 @@ +import * as c from '@combinators' +import { describe, expectTypeOf, it } from '@testing' +import { Parser, SucceedingParser } from '@types' + +type UnknownParser = Parser + +type StringParser = Parser +type NumberParser = Parser +type StringOrNumberParser = Parser + +type StringParsers = Parser +type NumberParsers = Parser + +describe('attempt', () => { + const { attempt } = c + + it('attempt should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('chain', () => { + const { chainl } = c + + it('chainl should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('choice', () => { + const { choice } = c + + it('choice should have correct inferred signature', () => { + expectTypeOf>().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf< + Parser + >() + expectTypeOf>().returns.toMatchTypeOf< + Parser + >() + }) +}) + +describe('error', () => { + const { error } = c + + it('error should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('lookahead', () => { + const { lookahead } = c + + it('lookahead should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('many', () => { + const { many, many1 } = c + + it('many should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf>() + expectTypeOf>().returns.toMatchTypeOf>() + }) + + it('many1 should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('map', () => { + const { map } = c + + it('map should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('optional', () => { + const { optional } = c + + it('optional should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf>() + expectTypeOf>().returns.toMatchTypeOf>() + }) +}) + +describe('sepBy', () => { + const { sepBy, sepBy1 } = c + + it('sepBy should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) + + it('sepBy1 should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('sequence', () => { + const { sequence } = c + + it('sequence should have correct inferred signature (from tuple)', () => { + expectTypeOf>().returns.toMatchTypeOf< + Parser<[string, string]> + >() + + expectTypeOf>().returns.toMatchTypeOf< + Parser<[string, number]> + >() + + // Fix this ? + expectTypeOf>().returns.toMatchTypeOf< + Parser + >() + }) + + it('should have correct inferred signature (from array)', () => { + expectTypeOf>>().returns.toMatchTypeOf() + expectTypeOf>>().returns.toMatchTypeOf() + }) +}) + +describe('take', () => { + const { takeLeft, takeMid, takeRight, takeSides } = c + + it('takeLeft should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) + + it('takeRight should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) + + it('takeMid should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) + + it('takeSides should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf< + Parser<[string, string]> + >() + expectTypeOf>().returns.toMatchTypeOf< + Parser<[number, number]> + >() + }) +}) + +describe('until', () => { + const { takeUntil, skipUntil } = c + + it('takeUntil should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf< + Parser<[string[], number]> + >() + expectTypeOf>().returns.toMatchTypeOf< + Parser<[number[], string]> + >() + }) + + it('skipUntil should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('when', () => { + const { when } = c + + it('when should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) diff --git a/src/__tests__/parsers.spec-d.ts b/src/__tests__/parsers.spec-d.ts new file mode 100644 index 0000000..b3e96d6 --- /dev/null +++ b/src/__tests__/parsers.spec-d.ts @@ -0,0 +1,211 @@ +import * as p from '@parsers' +import { describe, expectTypeOf, it } from '@testing' +import { Parser, SucceedingParser, Result, Success } from '@types' + +type UnknownParser = Parser +type NullParser = Parser + +type StringParser = Parser +type NumberParser = Parser + +describe('any', () => { + const { any } = p + + it('any should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('defer', () => { + const { defer } = p + + it('defer should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + expectTypeOf>().returns.toMatchTypeOf() + }) +}) + +describe('eof', () => { + const { eof } = p + + it('eof should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('eol', () => { + const { eol } = p + + it('eol should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('letter', () => { + const { letter, letters } = p + + it('letter should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('letters should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('noneOf', () => { + const { noneOf } = p + + it('noneOf should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('nothing', () => { + const { nothing } = p + + it('nothing should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('numbers', () => { + const { binary, float, hex, integer, octal, whole } = p + + it('binary should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('float should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('hex should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('integer should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('octal should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('whole should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('oneOf', () => { + const { oneOf } = p + + it('oneOf should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('regexp', () => { + const { regexp } = p + + it('regexp should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('rest', () => { + const { rest } = p + + it('rest should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf>() + expectTypeOf().returns.toMatchTypeOf>() + expectTypeOf().returns.not.toMatchTypeOf>() + }) +}) + +describe('run', () => { + const { run } = p + + it('run should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf<{ with: (input: string) => Result }>() + expectTypeOf>().returns.toMatchTypeOf<{ + with: (input: string) => Result + }>() + expectTypeOf>().returns.not.toMatchTypeOf<{ + with: (input: string) => Result + }>() + }) +}) + +describe('string', () => { + const { string, ustring } = p + + it('string should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) + + it('ustring should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) + +describe('tryRun', () => { + const { tryRun } = p + + it('tryRun should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf<{ + with: (input: string) => Success + }>() + expectTypeOf>().returns.toMatchTypeOf<{ + with: (input: string) => Success + }>() + expectTypeOf>().returns.not.toMatchTypeOf<{ + with: (input: string) => Success + }>() + }) +}) + +describe('whitespace', () => { + const { whitespace } = p + + it('whitespace should have correct inferred signature', () => { + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.toMatchTypeOf() + expectTypeOf().returns.not.toMatchTypeOf() + }) +}) diff --git a/src/__tests__/types.spec-d.ts b/src/__tests__/types.spec-d.ts new file mode 100644 index 0000000..e2c5ffe --- /dev/null +++ b/src/__tests__/types.spec-d.ts @@ -0,0 +1,145 @@ +import { describe, expectTypeOf, it } from '@testing' +import * as t from '@types' + +describe('library', () => { + it('`Parser` should correctly infer right signature', () => { + type NumberParser = t.Parser + expectTypeOf().toBeObject() + expectTypeOf().not.toBeAny() + expectTypeOf() + .toHaveProperty('parse') + .toMatchTypeOf< + | ((input: string, pos: number) => t.Success) + | ((input: string, pos: number) => t.Result) + | ((input: string, pos: number) => t.Failure) + >() + }) + + it('`SucceedingParser` should correctly infer right signature', () => { + type SucceedingNumberParser = t.SucceedingParser + + expectTypeOf().toBeObject() + expectTypeOf().not.toBeAny() + expectTypeOf() + .toHaveProperty('parse') + .returns.toMatchTypeOf>() + }) + + it('`UnsafeParser` should correctly infer right signature', () => { + type UnsafeNumberParser = t.UnsafeParser + + expectTypeOf().toBeObject() + expectTypeOf().not.toBeAny() + expectTypeOf() + .toHaveProperty('parse') + .returns.toMatchTypeOf>() + }) + + it('`Result` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf< + | { + readonly isOk: true + readonly span: t.Span + readonly pos: number + readonly value: number + } + | t.Failure + >() + }) + + it('`Success` should correctly infer right signature', () => { + type SuccessWithNumber = t.Success + + expectTypeOf().not.toBeAny() + expectTypeOf().toMatchTypeOf<{ + readonly span: t.Span + readonly pos: number + readonly value: number + }>() + }) +}) + +describe('internal utilities', () => { + type NumberAndString = number | string + type ParserNumberAndString = t.Parser | t.Parser + type ParsersTuple = [t.Parser, t.Parser] + + it('`UnionToIntersection` should correctly infer right signature', () => { + expectTypeOf>().toBeNever() + + expectTypeOf>().toBeNumber() + expectTypeOf>().not.toBeAny() + expectTypeOf>().not.toBeUnknown() + + expectTypeOf>().toMatchTypeOf< + t.FailingParser & + t.SucceedingParser & + t.UnsafeParser & + t.SucceedingParser & + t.UnsafeParser + >() + }) + + it('`UnionToTuplePreserving` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf<[number]>() + expectTypeOf>().toMatchTypeOf<[number, string]>() + }) + + it('`UnwrapParserTuple` should correctly infer right signature', () => { + expectTypeOf]>>().toMatchTypeOf<[string]>() + + expectTypeOf>().toMatchTypeOf<[string, number]>() + expectTypeOf, t.Parser]>>().toMatchTypeOf< + [string, string] + >() + }) + + it('`TupleToUnion` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf() + expectTypeOf>().not.toBeAny() + expectTypeOf>().not.toBeUnknown() + + expectTypeOf>().toMatchTypeOf() + expectTypeOf>().not.toBeAny() + expectTypeOf>().not.toBeUnknown() + }) +}) + +describe('utilities', () => { + type ParsersTuple = [t.Parser, t.Parser, t.Parser] + type NumberParsersArray = Array> + type TupleResult = [string, number, boolean] + type ParsersUnion = t.Parser | t.Parser | t.Parser + + it('`ToTuple` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf() + + expectTypeOf>().toMatchTypeOf<[]>() + }) + + it('`ToTupleOrArray` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf() + expectTypeOf>().toMatchTypeOf() + + expectTypeOf>().not.toMatchTypeOf() + }) + + it('`ToUnion` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf() + + expectTypeOf>().toMatchTypeOf() + expectTypeOf>().not.toMatchTypeOf() + }) + + it('`UnwrapUnion` should correctly infer right signature', () => { + expectTypeOf>().toMatchTypeOf< + [number, string, boolean] | [string, number, boolean] + >() + expectTypeOf | t.Parser>>().toMatchTypeOf<[string]>() + }) + + it('`ToParser` should correctly infer right signature', () => { + expectTypeOf>>().toMatchTypeOf>() + expectTypeOf>().toMatchTypeOf>() + }) +}) diff --git a/src/combinators/sequence.ts b/src/combinators/sequence.ts index ec2486b..23595d7 100644 --- a/src/combinators/sequence.ts +++ b/src/combinators/sequence.ts @@ -1,4 +1,4 @@ -import type { Parser, ToTuple } from '@types' +import type { Parser, ToTuple, ToTupleOrArray } from '@types' /** * Applies `ps` parsers in order, until *all* of them succeed. @@ -8,6 +8,7 @@ import type { Parser, ToTuple } from '@types' * @returns Tuple of values returned by `ps` parsers */ export function sequence>>(...ps: T): Parser> +export function sequence>>(...ps: T): Parser> export function sequence(...ps: Array>): Parser> { return { parse(input, pos) { diff --git a/src/types.ts b/src/types.ts index db3f04e..7b1c411 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,104 +1,2 @@ -/** Represents some range in the source input we are parsing or parsed. */ -export type Span = [start: number, end: number] - -/** Parsers of this type always succeed, e.g. `many` and `sepBy`. */ -export interface SucceedingParser { - parse(input: string, pos: number): Success -} - -/** Parsers of this type always fail. */ -export interface FailingParser { - parse(input: string, pos: number): Failure -} - -/** Parsers of this type may fail. */ -export interface UnsafeParser { - parse(input: string, pos: number): Result -} - -/** Parser interface that all parsers and combinators consume and resolve to. */ -export type Parser = FailingParser | SucceedingParser | UnsafeParser - -/** Represents failed execution. */ -export type Failure = { - readonly isOk: false - readonly span: Span - readonly pos: number - readonly expected: string -} - -/** Represents successful execution. */ -export type Success = { - readonly isOk: true - readonly span: Span - readonly pos: number - readonly value: T -} - -/** Interface describing the result of parsers and combinators execution. */ -export type Result = Success | Failure - -// Utility types. - -/** - * Given a tuple of `Parser`s, recursively extracts inner `T`s into a tuple. - * - * @example - * - * ```ts - * type U = [Parser, Parser, Parser] - * type R = ToTuple // type R = [string, number, boolean] - * ``` - */ -export type ToTuple = T extends [Parser, ...infer Tail] - ? [Head, ...ToTuple] - : [] - -/** - * Given a tuple of `Parser`s, recursively extracts inner `T`s into a union. - * - * @example - * - * ```ts - * type U = [Parser, Parser, Parser] - * type R = ToUnion // type R = string | number | boolean - * - * type U = Array> - * type R = ToUnion // type R = number - * ``` - */ -export type ToUnion = T extends Array> - ? Inner - : T extends [Parser, ...infer Tail] - ? Head | ToUnion - : never - -/** - * Given a union of `Parser`s, recursively extracts their inner `T`s into a tuple. - * - * @example - * - * ```ts - * type U = Parser | Parser | Parser - * type R = UnwrapUnion // type R = [string, number, boolean] - * ``` - */ -export type UnwrapUnion = T extends Parser | Parser - ? [Head, ...UnwrapUnion] - : [] - -/** - * Given a union of `Parser`s, folds it into a single`Parser` with a union of inner `T`s. - * In other words, it folds `Parser | Parser | ...` into `Parser`. - * - * Note: Technically, the result will be `SafeParser | UnsafeParser`, - * but no worries, it's the definition of the `Parser`. - * - * @example - * - * ```ts - * type U = Parser | Parser | Parser - * type R = ToParser // type R = Parser - * ``` - */ -export type ToParser = UnwrapUnion extends [infer R] ? Parser : Parser +export * from '@lib/types/library' +export * from '@lib/types/utility' diff --git a/src/types/library.ts b/src/types/library.ts new file mode 100644 index 0000000..923764f --- /dev/null +++ b/src/types/library.ts @@ -0,0 +1,39 @@ +/** Represents some range in the source input we are parsing or parsed. */ +export type Span = [start: number, end: number] + +/** Parsers of this type always succeed, e.g. `many` and `sepBy`. */ +export interface SucceedingParser { + parse(input: string, pos: number): Success +} + +/** Parsers of this type always fail. */ +export interface FailingParser { + parse(input: string, pos: number): Failure +} + +/** Parsers of this type may fail. */ +export interface UnsafeParser { + parse(input: string, pos: number): Result +} + +/** Parser interface that all parsers and combinators consume and resolve to. */ +export type Parser = FailingParser | SucceedingParser | UnsafeParser + +/** Represents failed execution. */ +export type Failure = { + readonly isOk: false + readonly span: Span + readonly pos: number + readonly expected: string +} + +/** Represents successful execution. */ +export type Success = { + readonly isOk: true + readonly span: Span + readonly pos: number + readonly value: T +} + +/** Interface describing the result of parsers and combinators execution. */ +export type Result = Success | Failure diff --git a/src/types/utility.ts b/src/types/utility.ts new file mode 100644 index 0000000..d3c5d59 --- /dev/null +++ b/src/types/utility.ts @@ -0,0 +1,107 @@ +import { Parser } from './library' + +/** @internal */ +export type UnionToIntersection = (U extends never ? never : (arg: U) => never) extends ( + arg: infer I +) => void + ? I + : never + +/** @internal */ +export type UnionToTuplePreserving = UnionToIntersection< + T extends never ? never : (t: T) => T +> extends (_: never) => infer W + ? [...UnionToTuplePreserving>, W] + : [] + +/** @internal */ +export type UnwrapParserTuple = T extends [Parser, ...infer Tail] + ? [Head, ...UnwrapParserTuple] + : [] + +/** @internal */ +export type TupleToUnion = T extends [infer Head, ...infer Rest] + ? Head | TupleToUnion + : never + +/** + * Given a tuple of `Parser`s, recursively extracts inner `T`s into a tuple. + * + * @example + * + * ```ts + * type U = [Parser, Parser, Parser] + * type R = ToTuple // type R = [string, number, boolean] + * ``` + */ +export type ToTuple = T extends [Parser, ...infer Tail] + ? [Head, ...ToTuple] + : [] + +/** + * Given a an array or a tuple of `Parser`s, recursively extracts inner `T`s into a tuple or array. + * + * @example + * + * ```ts + * type U = [Parser, Parser, Parser] + * type R = ToTuple // type R = [string, number, boolean] + * + * type A = Parser> + * type T = ToTupleOrArray // type T = string[] + * ``` + */ +export type ToTupleOrArray = T extends Array> + ? Inner extends unknown + ? T extends [Parser, ...infer Tail] + ? [Head, ...ToTuple] + : Inner[] + : [] + : [] + +/** + * Given a tuple of `Parser`s, recursively extracts inner `T`s into a union. + * + * @example + * + * ```ts + * type U = [Parser, Parser, Parser] + * type R = ToUnion // type R = string | number | boolean + * + * type U = Array> + * type R = ToUnion // type R = number + * ``` + */ +export type ToUnion = T extends Array> + ? Inner + : T extends [Parser, ...infer Tail] + ? Head | ToUnion + : never + +/** + * Given a union of `Parser`s, recursively extracts their inner `T`s into a tuple. + * + * @example + * + * ```ts + * type U = Parser | Parser | Parser + * type R = UnwrapUnion // type R = [string, number, boolean] + * ``` + */ +export type UnwrapUnion = UnwrapParserTuple> + +/** + * Given a union of `Parser`s, folds it into a single`Parser` with a union of inner `T`s. + * In other words, it folds `Parser | Parser | ...` into `Parser`. + * + * Note: Technically, the result will be `SafeParser | UnsafeParser`, + * but no worries, it's the definition of the `Parser`. + * + * @example + * + * ```ts + * type U = Parser | Parser | Parser + * type R = ToParser // type R = Parser + * ``` + */ +export type ToParser = UnwrapUnion extends infer R ? Parser> : Parser diff --git a/vite.config.ts b/vite.config.ts index 506bd2c..351d851 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -9,6 +9,10 @@ export default defineConfig({ provider: 'istanbul', reporter: ['text', 'lcov'], include: ['src/**/*.ts'] + }, + typecheck: { + include: ['src/**/*.spec-d.ts'], + ignoreSourceErrors: true } } })