diff --git a/docs/package-lock.json b/docs/package-lock.json index d2b68a1..28ba3e9 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -25,7 +25,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwindcss": "^3.4.3", - "typescript": "^5.4.5" + "typescript": "^5.5.3" }, "devDependencies": { "@types/js-beautify": "^1.14.3" @@ -7359,9 +7359,9 @@ "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==" }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/docs/package.json b/docs/package.json index 5550e7f..124a55f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -27,7 +27,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwindcss": "^3.4.3", - "typescript": "^5.4.5" + "typescript": "^5.5.3" }, "devDependencies": { "@types/js-beautify": "^1.14.3" diff --git a/package-lock.json b/package-lock.json index 6c4f27e..d1810c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "ts-runtime-checks", - "version": "0.6.0", + "version": "0.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ts-runtime-checks", - "version": "0.6.0", + "version": "0.6.1", "license": "MIT", "devDependencies": { "@types/chai": "^4.3.0", "@types/diff": "^5.0.2", "@types/mocha": "^9.1.0", "@types/node": "^20.3.0", - "@types/ts-expose-internals": "npm:ts-expose-internals@^5.4.3", + "@types/ts-expose-internals": "npm:ts-expose-internals@^5.5.3", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "chai": "^4.3.6", @@ -23,8 +23,8 @@ "mitata": "^0.1.6", "mocha": "^9.2.2", "prettier": "^3.2.5", - "ts-patch": "^3.1.1", - "typescript": "^5.4.3" + "ts-patch": "^3.2.1", + "typescript": "^5.5.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -173,9 +173,9 @@ }, "node_modules/@types/ts-expose-internals": { "name": "ts-expose-internals", - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/ts-expose-internals/-/ts-expose-internals-5.4.3.tgz", - "integrity": "sha512-DHaFZ4MUIm23TB4FdvnfOdWJWXcs9AhMINcgogd4QlMzd6uQdgplzS1Sv7pYMLL+TEeo6zX1p/Msv2bl1s3prQ==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/ts-expose-internals/-/ts-expose-internals-5.5.3.tgz", + "integrity": "sha512-/HejRD2f0jpndM0ftNiqjHkQBK3A/8z+hE3hwe9rQba4cMG+gKsEpgnjQxNTGaMPwxuBzR9uKrNi+bJTjsBi7g==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -2158,9 +2158,9 @@ } }, "node_modules/ts-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/ts-patch/-/ts-patch-3.1.1.tgz", - "integrity": "sha512-ReGYz9jQYC80PFafBx25TC0UI9cSgmUBtpT+WIy8IrhpLVzEHf430k03XQYOMldQMyZDBbzn5fBPELgtIl65cA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ts-patch/-/ts-patch-3.2.1.tgz", + "integrity": "sha512-hlR43v+GUIUy8/ZGFP1DquEqPh7PFKQdDMTAmYt671kCCA6AkDQMoeFaFmZ7ObPLYOmpMgyKUqL1C+coFMf30w==", "dev": true, "dependencies": { "chalk": "^4.1.2", @@ -2230,9 +2230,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2502,9 +2502,9 @@ "dev": true }, "@types/ts-expose-internals": { - "version": "npm:ts-expose-internals@5.4.3", - "resolved": "https://registry.npmjs.org/ts-expose-internals/-/ts-expose-internals-5.4.3.tgz", - "integrity": "sha512-DHaFZ4MUIm23TB4FdvnfOdWJWXcs9AhMINcgogd4QlMzd6uQdgplzS1Sv7pYMLL+TEeo6zX1p/Msv2bl1s3prQ==", + "version": "npm:ts-expose-internals@5.5.3", + "resolved": "https://registry.npmjs.org/ts-expose-internals/-/ts-expose-internals-5.5.3.tgz", + "integrity": "sha512-/HejRD2f0jpndM0ftNiqjHkQBK3A/8z+hE3hwe9rQba4cMG+gKsEpgnjQxNTGaMPwxuBzR9uKrNi+bJTjsBi7g==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -3907,9 +3907,9 @@ } }, "ts-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/ts-patch/-/ts-patch-3.1.1.tgz", - "integrity": "sha512-ReGYz9jQYC80PFafBx25TC0UI9cSgmUBtpT+WIy8IrhpLVzEHf430k03XQYOMldQMyZDBbzn5fBPELgtIl65cA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ts-patch/-/ts-patch-3.2.1.tgz", + "integrity": "sha512-hlR43v+GUIUy8/ZGFP1DquEqPh7PFKQdDMTAmYt671kCCA6AkDQMoeFaFmZ7ObPLYOmpMgyKUqL1C+coFMf30w==", "dev": true, "requires": { "chalk": "^4.1.2", @@ -3957,9 +3957,9 @@ "dev": true }, "typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index 8976e15..f64a17f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-runtime-checks", - "version": "0.6.0", + "version": "0.6.1", "description": "A typescript transformer which automatically generates validation code from your types.", "main": "dist/index.js", "scripts": { @@ -33,7 +33,7 @@ "@types/diff": "^5.0.2", "@types/mocha": "^9.1.0", "@types/node": "^20.3.0", - "@types/ts-expose-internals": "npm:ts-expose-internals@^5.4.3", + "@types/ts-expose-internals": "npm:ts-expose-internals@^5.5.3", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "chai": "^4.3.6", @@ -43,7 +43,7 @@ "mitata": "^0.1.6", "mocha": "^9.2.2", "prettier": "^3.2.5", - "ts-patch": "^3.1.1", - "typescript": "^5.4.3" + "ts-patch": "^3.2.1", + "typescript": "^5.5.3" } } diff --git a/src/gen/expressionUtils.ts b/src/gen/expressionUtils.ts index 73549b8..1adf8df 100644 --- a/src/gen/expressionUtils.ts +++ b/src/gen/expressionUtils.ts @@ -212,7 +212,6 @@ export function _val(val: unknown) : ts.Expression { } export function _arr(array: Array) : ts.Expression { - console.log(array.map(val => _val(val))); return ts.factory.createArrayLiteralExpression(array.map(val => _val(val))); } diff --git a/src/transformer.ts b/src/transformer.ts index 9f6f12f..7a5685c 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -1,7 +1,7 @@ import ts from "typescript"; import * as Block from "./block"; import {FnCallFn, Functions, MarkerCallData, MarkerFn, Markers} from "./markers"; -import {TransformerError, getResolvedTypesFromCallSig, hasBit, importSymbol, resolveAsChain} from "./utils"; +import {TransformerError, cloneNodeWithoutOriginal, getResolvedTypesFromCallSig, hasBit, importSymbol, resolveAsChain} from "./utils"; import {UNDEFINED, _var} from "./gen/expressionUtils"; import {CodeReference, ResolveTypeData, Validator, genValidator} from "./gen/validators"; import {ValidationResultType, createContext, fullValidate} from "./gen/nodes"; @@ -279,13 +279,16 @@ export class Transformer { const result = ts.createSourceFile("expr", str, ts.ScriptTarget.ESNext, false, ts.ScriptKind.JS); const firstStmt = result.statements[0]; if (!firstStmt || !ts.isExpressionStatement(firstStmt)) return UNDEFINED; + //console.dir(firstStmt, { depth: 3}); const visitor = (node: ts.Node): ts.Node => { if (ts.isIdentifier(node)) { - if (replacements && replacements[node.text] && typeof replacements[node.text] === "object") return replacements[node.text] as ts.Expression; + if (replacements && replacements[node.text] && typeof replacements[node.text] === "object") return ts.factory.cloneNode(replacements[node.text] as ts.Expression); return ts.factory.createIdentifier(node.text); - } else if (replacements && ts.isCallExpression(node) && ts.isIdentifier(node.expression) && replacements[node.expression.text] && typeof replacements[node.expression.text] === "function") + } + else if (replacements && ts.isCallExpression(node) && ts.isIdentifier(node.expression) && replacements[node.expression.text] && typeof replacements[node.expression.text] === "function") { return (replacements[node.expression.text] as (...args: ts.Expression[]) => ts.Node)(...node.arguments); - return ts.visitEachChild(ts.factory.cloneNode(node), visitor, this.ctx); + } + return ts.visitEachChild(cloneNodeWithoutOriginal(node), visitor, this.ctx); }; return ts.visitNode(firstStmt.expression, visitor) as ts.Expression; } diff --git a/src/utils.ts b/src/utils.ts index dfb511b..1e444bd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -225,3 +225,9 @@ export function addArticle(msg: string): string { if (firstLetter === "a" || firstLetter === "e" || firstLetter === "o" || firstLetter === "i" || firstLetter === "u") return `an ${msg}`; return `a ${msg}`; } + +export function cloneNodeWithoutOriginal(node: ts.Node) : ts.Node { + const cloned = ts.factory.cloneNode(node); + delete cloned.original; + return cloned; +} \ No newline at end of file diff --git a/tests/integrated/utilities/check.test.ts b/tests/integrated/utilities/check.test.ts index 3aa75fc..d9da011 100644 --- a/tests/integrated/utilities/check.test.ts +++ b/tests/integrated/utilities/check.test.ts @@ -1,7 +1,10 @@ -import type { Assert, Max, MaxLen, Min, Check } from "../../../dist/index"; +import type { Assert, Max, MaxLen, Min, Check, MinLen } from "../../../dist/index"; import { call } from "../../utils"; import { expect } from "chai"; +type StartsWith = Check<`$self.startsWith("${T}")`, `to start with "${T}"`, "startsWith", T>; +type AmountOfProps = Check<`Object.keys($self).length === ${T}`, `to have exactly ${T} keys`>; + describe("Check", () => { describe("Assert", () => { function test(a: Assert & Max<15>>) { @@ -14,8 +17,6 @@ describe("Check", () => { expect(call(test, 3)).to.throw("Expected a to be a number, to be greater than 5 & to be less than 15"); }); - type AmountOfProps = Check<`Object.keys($self).length === ${T}`, `to have exactly ${T} keys`>; - function test1(a: Assert<{ one: (string & MaxLen<6>)[], two: { a: string, b: number, c: boolean} & AmountOfProps<3> @@ -37,6 +38,27 @@ describe("Check", () => { two: { a: "a", b: 123, c: true, d: false } })()).to.equal(false); }); + + function test2(a: Assert | number & Min<3>, false>) { + return true; + } + + it("Conditional checks between different types", () => { + expect(call(test2, "abcdef")()).to.equal(true); + expect(call(test2, 5)()).to.equal(true); + expect(call(test2, "ab")()).to.equal(false); + expect(call(test2, 1)()).to.equal(false); + }); + function test3(a: Assert | StartsWith<"a">), false>) { + return true; + } + + it("Conditional checks between the same type", () => { + expect(call(test3, "de")()).to.equal(true); + expect(call(test3, "abcdef")()).to.equal(true); + expect(call(test3, "efeew3e")()).to.equal(false); + expect(call(test3, 123)()).to.equal(false); + }); }); }); \ No newline at end of file diff --git a/tests/snapshots/artifacts/utilities_check.test.js b/tests/snapshots/artifacts/utilities_check.test.js index e6e3b80..29c7d5d 100644 --- a/tests/snapshots/artifacts/utilities_check.test.js +++ b/tests/snapshots/artifacts/utilities_check.test.js @@ -40,5 +40,27 @@ describe("Check", () => { two: { a: "a", b: 123, c: true, d: false } })()).to.equal(false); }); + function test2(a) { + if ((typeof a !== "string" || a.length < 3) && (typeof a !== "number" || a < 3)) + return false; + return true; + } + it("Conditional checks between different types", () => { + (0, chai_1.expect)((0, utils_1.call)(test2, "abcdef")()).to.equal(true); + (0, chai_1.expect)((0, utils_1.call)(test2, 5)()).to.equal(true); + (0, chai_1.expect)((0, utils_1.call)(test2, "ab")()).to.equal(false); + (0, chai_1.expect)((0, utils_1.call)(test2, 1)()).to.equal(false); + }); + function test3(a) { + if (typeof a !== "string" || a.length > 3 && !a.startsWith("a")) + return false; + return true; + } + it("Conditional checks between the same type", () => { + (0, chai_1.expect)((0, utils_1.call)(test3, "de")()).to.equal(true); + (0, chai_1.expect)((0, utils_1.call)(test3, "abcdef")()).to.equal(true); + (0, chai_1.expect)((0, utils_1.call)(test3, "efeew3e")()).to.equal(false); + (0, chai_1.expect)((0, utils_1.call)(test3, 123)()).to.equal(false); + }); }); });