From 7ea339ed69114002168fb92b5be10cd31135431f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:24:21 -0700 Subject: [PATCH 01/11] chore: Apply prettier to eslintrc.json --- .eslintrc.json | 70 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2439bd1..4b4a513 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,9 @@ { "root": true, - "plugins": ["simple-import-sort", "unused-imports"], + "plugins": [ + "simple-import-sort", + "unused-imports" + ], "parserOptions": { "sourceType": "module", "ecmaVersion": "latest" @@ -18,33 +21,68 @@ "ignoreRestSiblings": true } ], - "import/extensions": ["error", "ignorePackages"], - "import/no-duplicates": ["error", { "prefer-inline": true }], + "import/extensions": [ + "error", + "ignorePackages" + ], + "import/no-duplicates": [ + "error", + { + "prefer-inline": true + } + ], "import/no-relative-parent-imports": "error", "simple-import-sort/imports": [ "error", { "groups": [ - ["^\\u0000"], - ["^node:"], - ["^@?\\w"], - ["@seamapi/dbtypr"], - ["^lib/"], - ["^"], - ["^\\."] + [ + "^\\u0000" + ], + [ + "^node:" + ], + [ + "^@?\\w" + ], + [ + "@seamapi/dbtypr" + ], + [ + "^lib/" + ], + [ + "^" + ], + [ + "^\\." + ] ] } ], - "simple-import-sort/exports": "error" + "simple-import-sort/exports": "error", }, "overrides": [ { - "files": ["*.js", "*.mjs", "*.cjs"], - "extends": ["standard", "prettier"] + "files": [ + "*.js", + "*.mjs", + "*.cjs" + ], + "extends": [ + "standard", + "prettier" + ] }, { - "files": ["*.ts", "*.tsx"], - "extends": ["standard-with-typescript", "prettier"], + "files": [ + "*.ts", + "*.tsx" + ], + "extends": [ + "standard-with-typescript", + "prettier" + ], "parserOptions": { "project": "./tsconfig.json" }, @@ -60,4 +98,4 @@ } } ] -} +} \ No newline at end of file From 76d41c7328e6495eaf75dbdf1dee166f09812ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:25:44 -0700 Subject: [PATCH 02/11] chore: Apply Seam naming convention --- .eslintrc.json | 79 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 4b4a513..720fccc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -94,8 +94,81 @@ "fixStyle": "inline-type-imports" } ], - "@typescript-eslint/no-unused-vars": "off" - } - } + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": [ + "interface", + "typeAlias" + ], + "format": [ + "PascalCase" + ], + "leadingUnderscore": "forbid", + "trailingUnderscore": "forbid" + }, + { + "selector": [ + "classProperty", + "typeProperty", + "parameterProperty", + "variable", + "parameter" + ], + "types": [ + "function" + ], + "format": [ + "camelCase" + ] + }, + { + "selector": [ + "objectLiteralProperty" + ], + "format": null + }, + { + "selector": [ + "parameterProperty", + "variable", + "parameter" + ], + "types": [ + "boolean", + "string", + "number", + "array" + ], + "format": [ + "snake_case", + "UPPER_CASE" + ], + "leadingUnderscore": "allow", + "trailingUnderscore": "allow" + }, + { + "selector": [ + "function" + ], + "format": [ + "camelCase" + ] + }, + { + "selector": "default", + "format": [ + "camelCase", + "snake_case", + "UPPER_CASE", + "PascalCase" + ], + "leadingUnderscore": "allow", + "trailingUnderscore": "forbid" + } + ] + }, + }, ] } \ No newline at end of file From 2e2bc0aa2ddce726690c1593d5e045cbd94078c8 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 18 Apr 2025 21:34:15 +0000 Subject: [PATCH 03/11] ci: Format code --- .eslintrc.json | 121 ++++++++++++------------------------------------- 1 file changed, 28 insertions(+), 93 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 720fccc..01d9344 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,6 @@ { "root": true, - "plugins": [ - "simple-import-sort", - "unused-imports" - ], + "plugins": ["simple-import-sort", "unused-imports"], "parserOptions": { "sourceType": "module", "ecmaVersion": "latest" @@ -21,10 +18,7 @@ "ignoreRestSiblings": true } ], - "import/extensions": [ - "error", - "ignorePackages" - ], + "import/extensions": ["error", "ignorePackages"], "import/no-duplicates": [ "error", { @@ -36,53 +30,26 @@ "error", { "groups": [ - [ - "^\\u0000" - ], - [ - "^node:" - ], - [ - "^@?\\w" - ], - [ - "@seamapi/dbtypr" - ], - [ - "^lib/" - ], - [ - "^" - ], - [ - "^\\." - ] + ["^\\u0000"], + ["^node:"], + ["^@?\\w"], + ["@seamapi/dbtypr"], + ["^lib/"], + ["^"], + ["^\\."] ] } ], - "simple-import-sort/exports": "error", + "simple-import-sort/exports": "error" }, "overrides": [ { - "files": [ - "*.js", - "*.mjs", - "*.cjs" - ], - "extends": [ - "standard", - "prettier" - ] + "files": ["*.js", "*.mjs", "*.cjs"], + "extends": ["standard", "prettier"] }, { - "files": [ - "*.ts", - "*.tsx" - ], - "extends": [ - "standard-with-typescript", - "prettier" - ], + "files": ["*.ts", "*.tsx"], + "extends": ["standard-with-typescript", "prettier"], "parserOptions": { "project": "./tsconfig.json" }, @@ -98,13 +65,8 @@ "@typescript-eslint/naming-convention": [ "error", { - "selector": [ - "interface", - "typeAlias" - ], - "format": [ - "PascalCase" - ], + "selector": ["interface", "typeAlias"], + "format": ["PascalCase"], "leadingUnderscore": "forbid", "trailingUnderscore": "forbid" }, @@ -116,59 +78,32 @@ "variable", "parameter" ], - "types": [ - "function" - ], - "format": [ - "camelCase" - ] + "types": ["function"], + "format": ["camelCase"] }, { - "selector": [ - "objectLiteralProperty" - ], + "selector": ["objectLiteralProperty"], "format": null }, { - "selector": [ - "parameterProperty", - "variable", - "parameter" - ], - "types": [ - "boolean", - "string", - "number", - "array" - ], - "format": [ - "snake_case", - "UPPER_CASE" - ], + "selector": ["parameterProperty", "variable", "parameter"], + "types": ["boolean", "string", "number", "array"], + "format": ["snake_case", "UPPER_CASE"], "leadingUnderscore": "allow", "trailingUnderscore": "allow" }, { - "selector": [ - "function" - ], - "format": [ - "camelCase" - ] + "selector": ["function"], + "format": ["camelCase"] }, { "selector": "default", - "format": [ - "camelCase", - "snake_case", - "UPPER_CASE", - "PascalCase" - ], + "format": ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"], "leadingUnderscore": "allow", "trailingUnderscore": "forbid" } ] - }, - }, + } + } ] -} \ No newline at end of file +} From d8bec5b5dda04f4deeb8ef2528b97c493c6f1fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:34:22 -0700 Subject: [PATCH 04/11] feat: Add database typegen --- package-lock.json | 76 ++++-- package.json | 5 +- .../[schema_name]/[TableName]CustomTypes.ts | 61 +++++ .../generated/[schema_name]/[TableName].ts | 96 ++++++++ .../[schema_name]/[TableName]QueryTypes.ts | 72 ++++++ .../generated/[schema_name]/index.ts | 126 ++++++++++ src/lib/file-generators/generated/index.ts | 101 ++++++++ src/lib/file-generators/generated/knex.ts | 62 +++++ src/lib/file-generators/generated/kysely.ts | 80 ++++++ src/lib/file-generators/generated/utils.ts | 102 ++++++++ src/lib/generate-types.ts | 110 +++++++++ src/lib/index.ts | 2 +- src/lib/read-database-tree.ts | 230 ++++++++++++++++++ src/lib/todo.test.ts | 7 - src/lib/todo.ts | 1 - src/lib/types.ts | 34 +++ src/lib/utils.ts | 5 + test/todo.test.ts | 7 - 18 files changed, 1134 insertions(+), 43 deletions(-) create mode 100644 src/lib/file-generators/custom/[schema_name]/[TableName]CustomTypes.ts create mode 100644 src/lib/file-generators/generated/[schema_name]/[TableName].ts create mode 100644 src/lib/file-generators/generated/[schema_name]/[TableName]QueryTypes.ts create mode 100644 src/lib/file-generators/generated/[schema_name]/index.ts create mode 100644 src/lib/file-generators/generated/index.ts create mode 100644 src/lib/file-generators/generated/knex.ts create mode 100644 src/lib/file-generators/generated/kysely.ts create mode 100644 src/lib/file-generators/generated/utils.ts create mode 100644 src/lib/generate-types.ts create mode 100644 src/lib/read-database-tree.ts delete mode 100644 src/lib/todo.test.ts delete mode 100644 src/lib/todo.ts create mode 100644 src/lib/types.ts create mode 100644 src/lib/utils.ts delete mode 100644 test/todo.test.ts diff --git a/package-lock.json b/package-lock.json index 99dbf61..b2dfedb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "@seamapi/dbtypr", "version": "0.0.1", "license": "MIT", + "dependencies": { + "prettier": "^3.5.3", + "ts-morph": "^25.0.1" + }, "devDependencies": { "@types/node": "^20.8.10", "ava": "^6.0.1", @@ -20,7 +24,6 @@ "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-unused-imports": "^3.0.0", "landlubber": "^2.0.0", - "prettier": "^3.0.0", "tsc-alias": "^1.8.2", "tsup": "^8.0.1", "tsx": "^4.6.2", @@ -803,7 +806,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -817,7 +819,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -827,7 +828,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -1158,6 +1158,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ts-morph/common": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.26.1.tgz", + "integrity": "sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA==", + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1989,7 +2013,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -2047,7 +2070,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -2057,7 +2079,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -2507,6 +2528,11 @@ "node": ">=8" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==" + }, "node_modules/code-excerpt": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", @@ -3911,7 +3937,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3928,7 +3953,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -3972,7 +3996,6 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -4033,7 +4056,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -4963,7 +4985,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5023,7 +5044,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5050,7 +5070,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -5742,7 +5761,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -5752,7 +5770,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -5766,7 +5783,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -6361,6 +6377,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6624,11 +6645,9 @@ } }, "node_modules/prettier": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", - "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", - "dev": true, - "license": "MIT", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "bin": { "prettier": "bin/prettier.cjs" }, @@ -6707,7 +6726,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -6992,7 +7010,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -7059,7 +7076,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -8074,7 +8090,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -8133,6 +8148,15 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/ts-morph": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-25.0.1.tgz", + "integrity": "sha512-QJEiTdnz1YjrB3JFhd626gX4rKHDLSjSVMvGGG4v7ONc3RBwa0Eei98G9AT9uNFDMtV54JyuXsFeC+OH0n6bXQ==", + "dependencies": { + "@ts-morph/common": "~0.26.0", + "code-block-writer": "^13.0.3" + } + }, "node_modules/tsc-alias": { "version": "1.8.10", "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.10.tgz", diff --git a/package.json b/package.json index af13a32..3460f04 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,10 @@ "node": ">=18.12.0", "npm": ">= 9.0.0" }, + "dependencies": { + "prettier": "^3.5.3", + "ts-morph": "^25.0.1" + }, "devDependencies": { "@types/node": "^20.8.10", "ava": "^6.0.1", @@ -79,7 +83,6 @@ "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-unused-imports": "^3.0.0", "landlubber": "^2.0.0", - "prettier": "^3.0.0", "tsc-alias": "^1.8.2", "tsup": "^8.0.1", "tsx": "^4.6.2", diff --git a/src/lib/file-generators/custom/[schema_name]/[TableName]CustomTypes.ts b/src/lib/file-generators/custom/[schema_name]/[TableName]CustomTypes.ts new file mode 100644 index 0000000..f4c87ac --- /dev/null +++ b/src/lib/file-generators/custom/[schema_name]/[TableName]CustomTypes.ts @@ -0,0 +1,61 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema, Table } from '../../../types.js' +import { pascalCase } from '../../../utils.js' + +export const createCustomTypesFile = ( + args: { + project: Project + schema: Schema + table: Table + }, + config: Config, +): SourceFile => { + const { project, schema, table } = args + const { output_dir } = config + + const pascal_table_name = pascalCase(table.name) + + const statements: StatementedNodeStructure['statements'] = [ + { + kind: StructureKind.ImportDeclaration, + namedImports: ['SubsetOf'], + moduleSpecifier: '../../generated/utils', + isTypeOnly: true, + }, + { + kind: StructureKind.ImportDeclaration, + namedImports: [`Selectable${pascal_table_name}`], + moduleSpecifier: `../../generated/${schema.name}/${pascal_table_name}QueryTypes`, + isTypeOnly: true, + }, + (writer) => writer.blankLine(), + { + kind: StructureKind.TypeAlias, + name: `${pascal_table_name}CustomTypes`, + type: `SubsetOf`, + isExported: true, + }, + (writer) => writer.blankLine(), + { + kind: StructureKind.ExportAssignment, + isExportEquals: false, + expression: `${pascal_table_name}CustomTypes`, + }, + ] + + return project.createSourceFile( + `${output_dir}/custom/${schema.name}/${pascal_table_name}CustomTypes.ts`, + { + statements, + }, + { + overwrite: false, + }, + ) +} diff --git a/src/lib/file-generators/generated/[schema_name]/[TableName].ts b/src/lib/file-generators/generated/[schema_name]/[TableName].ts new file mode 100644 index 0000000..2343b07 --- /dev/null +++ b/src/lib/file-generators/generated/[schema_name]/[TableName].ts @@ -0,0 +1,96 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema, Table } from '../../../types.js' +import { pascalCase } from '../../../utils.js' + +export const createResultingTypeFile = ( + args: { + project: Project + schema: Schema + table: Table + }, + config: Config, +): SourceFile => { + const { project, schema, table } = args + const { output_dir } = config + + const pascal_table_name = pascalCase(table.name) + + const statements: StatementedNodeStructure['statements'] = [ + { + kind: StructureKind.ImportDeclaration, + moduleSpecifier: `./${pascal_table_name}QueryTypes`, + namedImports: [ + `Selectable${pascal_table_name}`, + `Insertable${pascal_table_name}`, + ], + isTypeOnly: true, + }, + ] + + if (table.is_affected_by_pgtui_bugs) { + statements.push({ + kind: StructureKind.ImportDeclaration, + moduleSpecifier: '../utils', + namedImports: ['ReproducePgtuiBugs'], + isTypeOnly: true, + }) + } + + if (table.is_customizable) { + statements.push( + { + kind: StructureKind.ImportDeclaration, + moduleSpecifier: '../utils', + namedImports: ['CustomizeDbType', 'CustomizeDbTypeInitializer'], + isTypeOnly: true, + }, + { + kind: StructureKind.ImportDeclaration, + moduleSpecifier: `../../custom/${schema.name}/${pascal_table_name}CustomTypes`, + defaultImport: `${pascal_table_name}CustomTypes`, + isTypeOnly: true, + }, + ) + } + + statements.push( + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + isExported: true, + name: pascal_table_name, + type: table.is_customizable + ? `CustomizeDbType` + : `Selectable${pascal_table_name}`, + }, + { + kind: StructureKind.TypeAlias, + isExported: true, + name: `${pascal_table_name}Initializer`, + type: table.is_customizable + ? `CustomizeDbTypeInitializer` + : `Insertable${pascal_table_name}`, + }, + ) + + if (table.is_affected_by_pgtui_bugs) { + statements.push({ + kind: StructureKind.TypeAlias, + isExported: true, + name: `${pascal_table_name}WithPgtuiBugs`, + type: `ReproducePgtuiBugs<${pascal_table_name}>`, + }) + } + + return project.createSourceFile( + `${output_dir}/generated/${schema.name}/${pascal_table_name}.ts`, + { statements }, + { overwrite: true }, + ) +} diff --git a/src/lib/file-generators/generated/[schema_name]/[TableName]QueryTypes.ts b/src/lib/file-generators/generated/[schema_name]/[TableName]QueryTypes.ts new file mode 100644 index 0000000..144218d --- /dev/null +++ b/src/lib/file-generators/generated/[schema_name]/[TableName]QueryTypes.ts @@ -0,0 +1,72 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema, Table } from '../../../types.js' +import { pascalCase } from '../../../utils.js' + +export const createQueryTypesFile = ( + args: { + project: Project + schema: Schema + table: Table + }, + config: Config, +): SourceFile => { + const { project, schema, table } = args + const { output_dir } = config + + const base_type_name = pascalCase(table.name) + + const statements: StatementedNodeStructure['statements'] = [] + + if (table.uses_zapatos_column_type) { + statements.push({ + kind: StructureKind.ImportDeclaration, + moduleSpecifier: 'zapatos/db', + namespaceImport: 'db', + isTypeOnly: true, + }) + } + + statements.push( + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + isExported: true, + name: `Selectable${base_type_name}`, + type: (writer) => + writer.inlineBlock(() => { + for (const column of Object.values(table.selectable_columns)) { + writer.writeLine( + `${column.name}${column.is_optional ? '?' : ''}: ${column.type};`, + ) + } + }), + }, + { + kind: StructureKind.TypeAlias, + isExported: true, + name: `Insertable${base_type_name}`, + type: (writer) => + writer.inlineBlock(() => { + for (const column of Object.values(table.insertable_columns)) { + writer.writeLine( + `${column.name}${column.is_optional ? '?' : ''}: ${column.type};`, + ) + } + }), + }, + ) + + return project.createSourceFile( + `${output_dir}/generated/${schema.name}/${base_type_name}QueryTypes.ts`, + { + statements, + }, + { overwrite: true }, + ) +} diff --git a/src/lib/file-generators/generated/[schema_name]/index.ts b/src/lib/file-generators/generated/[schema_name]/index.ts new file mode 100644 index 0000000..7803646 --- /dev/null +++ b/src/lib/file-generators/generated/[schema_name]/index.ts @@ -0,0 +1,126 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema } from '../../../types.js' +import { pascalCase } from '../../../utils.js' + +export const createSchemaGeneratedTypeIndexFile = ( + args: { + project: Project + schema: Schema + }, + config: Config, +): SourceFile => { + const { project, schema } = args + const { output_dir } = config + + const pascal_schema_name = pascalCase(schema.name) + + const tables = Object.values(schema.tables) + const pascal_table_names = tables + .map((table) => table.name) + .reduce>((acc, table_name) => { + acc[table_name] = pascalCase(table_name) + return acc + }, {}) + + const statements: StatementedNodeStructure['statements'] = [ + { + kind: StructureKind.ImportDeclaration, + moduleSpecifier: '../utils', + namedImports: ['KyselyTable'], + isTypeOnly: true, + }, + ] + + for (const table of tables) { + statements.push({ + kind: StructureKind.ImportDeclaration, + isTypeOnly: true, + moduleSpecifier: `./${pascal_table_names[table.name]}`, + namedImports: [ + pascal_table_names[table.name]!, + ...(table.is_affected_by_pgtui_bugs + ? [`${pascal_table_names[table.name]}WithPgtuiBugs`] + : []), + `${pascal_table_names[table.name]}Initializer`, + ], + }) + } + + statements.push( + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + isExported: true, + name: `${pascal_schema_name}Tables`, + type: `[ ${tables + .map((table) => `"${schema.name}.${table.name}"`) + .join(', ')} ]`, + }, + { + kind: StructureKind.TypeAlias, + isExported: true, + name: `${pascal_schema_name}Table`, + type: `${pascal_schema_name}Tables[number]`, + }, + (writer) => writer.newLine(), + ) + + if (config.generate_knex_types) { + statements.push({ + kind: StructureKind.Interface, + isExported: true, + name: 'KnexSchemaTypeMap', + properties: tables.map((table) => ({ + name: + schema.name === 'seam' + ? table.name + : `"${schema.name}.${table.name}"`, + type: table.is_affected_by_pgtui_bugs + ? `${pascal_table_names[table.name]}WithPgtuiBugs` + : pascal_table_names[table.name]!, + })), + }) + } + + statements.push( + { + kind: StructureKind.TypeAlias, + isExported: true, + name: 'KyselySchemaTypeMap', + type: `{ ${tables + .map( + (table) => + `"${schema.name}.${table.name}": KyselyTable<${ + pascal_table_names[table.name] + }, ${pascal_table_names[table.name]}Initializer>;`, + ) + .join(' ')} }`, + }, + (writer) => writer.newLine(), + { + kind: StructureKind.ExportDeclaration, + isTypeOnly: true, + namedExports: tables.flatMap((table) => [ + pascal_table_names[table.name]!, + ...(table.is_affected_by_pgtui_bugs + ? [`${pascal_table_names[table.name]}WithPgtuiBugs`] + : []), + `${pascal_table_names[table.name]}Initializer`, + ]), + }, + ) + + return project.createSourceFile( + `${output_dir}/generated/${schema.name}/index.ts`, + { + statements, + }, + { overwrite: true }, + ) +} diff --git a/src/lib/file-generators/generated/index.ts b/src/lib/file-generators/generated/index.ts new file mode 100644 index 0000000..a30d81b --- /dev/null +++ b/src/lib/file-generators/generated/index.ts @@ -0,0 +1,101 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema } from '../../types.js' +import { pascalCase } from '../../utils.js' + +export const createGeneratedIndexFile = ( + args: { + project: Project + schemas: Schema[] + }, + config: Config, +): SourceFile => { + const { project, schemas } = args + const { output_dir, main_schema = 'public' } = config + + const schema_import_statements: StatementedNodeStructure['statements'] = [] + const schema_export_statements: StatementedNodeStructure['statements'] = [] + const schemas_tables: string[] = [] + + for (const schema of schemas) { + if (Object.values(schema.tables).length === 0) continue + const pascal_schema_name = pascalCase(schema.name) + + schema_import_statements.push({ + kind: StructureKind.ImportDeclaration, + moduleSpecifier: `./${schema.name}`, + namedImports: [`${pascal_schema_name}Tables`], + }) + schemas_tables.push(`${pascal_schema_name}Tables`) + schema_export_statements.push({ + kind: StructureKind.ExportDeclaration, + moduleSpecifier: `./${schema.name}`, + ...(schema.name === main_schema + ? {} + : { + namespaceExport: schema.name, + }), + }) + } + + const statements: StatementedNodeStructure['statements'] = [ + ...schema_import_statements, + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + isExported: true, + name: 'DatabaseSchemas', + type: `[ ${schemas.map((schema) => `"${schema.name}"`).join(', ')} ]`, + }, + { + kind: StructureKind.TypeAlias, + isExported: true, + name: 'DatabaseTables', + type: `[ ${schemas_tables + .map((schema_tables) => `...${schema_tables}`) + .join(', ')} ]`, + }, + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + isExported: true, + name: 'DatabaseSchema', + type: 'DatabaseSchemas[number]', + }, + { + kind: StructureKind.TypeAlias, + isExported: true, + name: 'DatabaseTable', + type: 'DatabaseTables[number]', + }, + (writer) => writer.newLine(), + ...schema_export_statements, + (writer) => writer.newLine(), + { + kind: StructureKind.ExportDeclaration, + namespaceExport: 'kysely', + moduleSpecifier: './kysely', + }, + ] + + if (config.generate_knex_types) { + statements.push({ + kind: StructureKind.ExportDeclaration, + namespaceExport: 'knex', + moduleSpecifier: './knex', + }) + } + + return project.createSourceFile( + `${output_dir}/generated/index.ts`, + { + statements, + }, + { overwrite: true }, + ) +} diff --git a/src/lib/file-generators/generated/knex.ts b/src/lib/file-generators/generated/knex.ts new file mode 100644 index 0000000..6b4180a --- /dev/null +++ b/src/lib/file-generators/generated/knex.ts @@ -0,0 +1,62 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema } from '../../types.js' +import { pascalCase } from '../../utils.js' + +export const createKnexIndexFile = ( + args: { + project: Project + schemas: Schema[] + }, + config: Config, +): SourceFile => { + const { project, schemas } = args + const { output_dir } = config + + const statements: StatementedNodeStructure['statements'] = [] + const module_interface_statements: StatementedNodeStructure['statements'] = [] + + for (const schema of schemas) { + if (Object.values(schema.tables).length === 0) continue + + statements.push({ + kind: StructureKind.ImportDeclaration, + moduleSpecifier: `./${schema.name}`, + namedImports: [ + { + name: 'KnexSchemaTypeMap', + alias: `${pascalCase(schema.name)}TypeMap`, + }, + ], + }) + + module_interface_statements.push({ + kind: StructureKind.Interface, + name: 'Tables', + extends: [`${pascalCase(schema.name)}TypeMap`], + properties: [], + }) + } + + // Create the module declaration + statements.push({ + kind: StructureKind.Module, + name: `"knex/types/tables"`, + hasDeclareKeyword: true, + statements: module_interface_statements, + }) + + // Create the source file + return project.createSourceFile( + `${output_dir}/generated/knex.ts`, + { + statements, + }, + { overwrite: true }, + ) +} diff --git a/src/lib/file-generators/generated/kysely.ts b/src/lib/file-generators/generated/kysely.ts new file mode 100644 index 0000000..b16f1b5 --- /dev/null +++ b/src/lib/file-generators/generated/kysely.ts @@ -0,0 +1,80 @@ +import { + type Project, + type SourceFile, + type StatementedNodeStructure, + StructureKind, +} from 'ts-morph' + +import type { Config, Schema } from '../../types.js' +import { pascalCase } from '../../utils.js' + +export const createKyselyIndexFile = ( + args: { + project: Project + schemas: Schema[] + }, + config: Config, +): SourceFile => { + const { project, schemas } = args + const { output_dir } = config + + const type_references: string[] = [] + + const statements: StatementedNodeStructure['statements'] = [ + { + kind: StructureKind.ImportDeclaration, + moduleSpecifier: 'kysely', + namedImports: ['Kysely', 'Transaction'], + isTypeOnly: true, + }, + ] + + for (const schema of schemas) { + if (Object.values(schema.tables).length === 0) continue + + statements.push({ + kind: StructureKind.ImportDeclaration, + moduleSpecifier: `./${schema.name}`, + namedImports: [ + { + name: 'KyselySchemaTypeMap', + alias: `${pascalCase(schema.name)}TypeMap`, + }, + ], + isTypeOnly: true, + }) + + type_references.push(`${pascalCase(schema.name)}TypeMap`) + } + + statements.push( + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + name: 'KyselySchema', + isExported: true, + type: type_references.join(' & '), + }, + (writer) => writer.newLine(), + { + kind: StructureKind.TypeAlias, + name: 'KyselyDatabase', + isExported: true, + type: `Kysely`, + }, + { + kind: StructureKind.TypeAlias, + name: 'KyselyTransaction', + isExported: true, + type: `Transaction`, + }, + ) + + return project.createSourceFile( + `${output_dir}/generated/kysely.ts`, + { + statements, + }, + { overwrite: true }, + ) +} diff --git a/src/lib/file-generators/generated/utils.ts b/src/lib/file-generators/generated/utils.ts new file mode 100644 index 0000000..5d4e243 --- /dev/null +++ b/src/lib/file-generators/generated/utils.ts @@ -0,0 +1,102 @@ +import type { Project, SourceFile } from 'ts-morph' + +import type { Config } from '../../types.js' + +export const createGeneratedUtilsFile = ( + args: { + project: Project + }, + config: Config, +): SourceFile => { + const { project } = args + const { output_dir } = config + + const file_source = `import type { JSONValue } from "zapatos/db" +import { type ColumnType } from "kysely" + +type PartialWithNever = { + [P in keyof T as T[P] extends never ? never : P]?: T[P] +} & { + [P in keyof T as T[P] extends never ? P : never]: T[P] +} + +export type KyselyTable = { + [K in keyof Selectable]: ColumnType< + Selectable[K], + K extends keyof Initializer ? Initializer[K] : never, + K extends keyof PartialWithNever + ? PartialWithNever[K] + : never + > +} + +type KeysOfUnion = T extends T ? keyof T : never + +type NullableKeys = { + [K in keyof T]: null extends T[K] ? K : never +}[keyof T] + +/** + * Allows creating a type that is a subset of another type. + */ +export type SubsetOf< + Super, + Sub extends { + // The types of keys present in sub must be assignable to the types of the same keys in super + [K in keyof Super as K extends KeysOfUnion ? K : never]: Super[K] + } & { + // Keys that are nullable in super must stay nullable in sub + [K in keyof Sub as K extends NullableKeys + ? K + : never]: null extends Sub[K] ? any : null + } & { + // Keys in sub must exist in super + [K in keyof Sub as K extends keyof Super ? never : K]: never + } +> = Sub + +// Replaces the values of From with the values of To and ignores the rest of the properties of To +type ReplaceValueTypes = Omit & + Pick> + +// Works to make From properties optional but it would not work to make them mandatory as From[K] would include undefined +// TODO improve to keep unions (if possible) +type ReplaceKeyTypes = { + [K in keyof To as K extends keyof From ? K : never]: K extends keyof From + ? From[K] + : never +} & { + [K in keyof From as K extends keyof To ? never : K]: From[K] +} + +export type CustomizeDbType = ReplaceValueTypes< + DbType, + CustomProperties +> + +export type CustomizeDbTypeInitializer = + ReplaceValueTypes< + DbTypeInitializer, + ReplaceKeyTypes + > + +// TODO Migrating from pgtui to our custom types is a pain because of two bugs in pgtui. +// This type is a workaround to make the migration easier and should be removed later. +// To remove it, we need to fix a lot of type errors. It can be done per table thanks +// to a configuration option of generate-types. +export type ReproducePgtuiBugs = { + [K in keyof T]: K extends \`\${string}_id\` + ? string + : ReplaceJSONValueWithAny +} + +type ReplaceJSONValueWithAny = [JSONValue] extends [T] ? any : T` + + return project.createSourceFile( + `${output_dir}/generated/utils.ts`, + file_source, + { + overwrite: true, + }, + ) +} diff --git a/src/lib/generate-types.ts b/src/lib/generate-types.ts new file mode 100644 index 0000000..43a7f89 --- /dev/null +++ b/src/lib/generate-types.ts @@ -0,0 +1,110 @@ +import prettier from 'prettier' +import { Project, type SourceFile } from 'ts-morph' + +import { createCustomTypesFile } from './file-generators/custom/[schema_name]/[TableName]CustomTypes.js' +import { createResultingTypeFile } from './file-generators/generated/[schema_name]/[TableName].js' +import { createQueryTypesFile } from './file-generators/generated/[schema_name]/[TableName]QueryTypes.js' +import { createSchemaGeneratedTypeIndexFile } from './file-generators/generated/[schema_name]/index.js' +import { createGeneratedIndexFile } from './file-generators/generated/index.js' +import { createKnexIndexFile } from './file-generators/generated/knex.js' +import { createKyselyIndexFile } from './file-generators/generated/kysely.js' +import { createGeneratedUtilsFile } from './file-generators/generated/utils.js' +import { readDatabaseTree } from './read-database-tree.js' +import type { Config, DatabaseTree } from './types.js' + +/** + * Generate types based on a Zapatos database schema. + */ +export const generateTypes = async (config: Config) => { + const project = new Project() + const database_tree = readDatabaseTree(config) + + createGeneratedTypesFiles(project, database_tree, config) + createCustomTypesFiles(project, database_tree, config) + + await formatProjectFiles(project) + + project.saveSync() +} + +const createGeneratedTypesFiles = ( + project: Project, + database_tree: DatabaseTree, + config: Config, +) => { + const { + file_header = '// @generated\n// This file was automatically generated. DO NOT EDIT!\n\n', + } = config + + const schemas = Object.values(database_tree.schemas) + const generated_types_files: SourceFile[] = [ + createKyselyIndexFile({ project, schemas }, config), + createGeneratedIndexFile({ project, schemas }, config), + createGeneratedUtilsFile({ project }, config), + ] + + if (config.generate_knex_types) { + generated_types_files.push( + createKnexIndexFile({ project, schemas }, config), + ) + } + + for (const schema of schemas) { + for (const table of Object.values(schema.tables)) { + generated_types_files.push( + createQueryTypesFile({ project, schema, table }, config), + createResultingTypeFile({ project, schema, table }, config), + ) + } + if (Object.values(schema.tables).length > 0) { + generated_types_files.push( + createSchemaGeneratedTypeIndexFile({ project, schema }, config), + ) + } + } + + for (const source_file of generated_types_files) { + source_file.replaceWithText(`${file_header}${source_file.getFullText()}`) + } + + return generated_types_files +} + +const createCustomTypesFiles = ( + project: Project, + database_tree: DatabaseTree, + config: Config, +) => { + const custom_types_files: SourceFile[] = [] + for (const schema of Object.values(database_tree.schemas)) { + for (const table of Object.values(schema.tables)) { + if (!table.is_customizable) { + continue + } + try { + custom_types_files.push( + createCustomTypesFile({ project, schema, table }, config), + ) + } catch (error: any) { + if (error.message.includes('A source file already exists')) { + continue + } + throw error as Error + } + } + } + return custom_types_files +} + +const formatProjectFiles = async (project: Project) => { + for (const source_file of project.getSourceFiles()) { + const formatted_source = await prettier.format( + source_file.getFullText().replace(/;$/gm, ''), + { + semi: false, + parser: 'typescript', + }, + ) + source_file.replaceWithText(formatted_source) + } +} diff --git a/src/lib/index.ts b/src/lib/index.ts index 0cbac41..0552fe1 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1 +1 @@ -export { todo } from './todo.js' +export * from './generate-types.js' diff --git a/src/lib/read-database-tree.ts b/src/lib/read-database-tree.ts new file mode 100644 index 0000000..615ca88 --- /dev/null +++ b/src/lib/read-database-tree.ts @@ -0,0 +1,230 @@ +import { + type InterfaceDeclaration, + Project, + type PropertySignature, + SyntaxKind, +} from 'ts-morph' + +import type { Column, Config, DatabaseTree, Schema, Table } from './types.js' + +export const readDatabaseTree = (config: Config): DatabaseTree => { + const { zapatos_dir } = config + + const project = new Project() + + const zapatos_custom_types = readCustomZapatosTypes(config) + + const source_file = project.addSourceFileAtPath(`${zapatos_dir}/schema.d.ts`) + const zapatos_module = source_file.getModules()[0] + + if (!zapatos_module) { + throw new Error('Could not find the zapatos module in the schema file') + } + + const schemas: Record = {} + for (const schema_module of zapatos_module.getModules()) { + const schema_name = schema_module.getName() + const tables: Record = {} + + for (const table_module of schema_module.getModules()) { + const is_table = + table_module + .getStatementByKind(SyntaxKind.TypeAliasDeclaration) + ?.getName() === 'Table' + + if (!is_table) { + continue + } + const table_name = table_module.getName() + const is_customizable = isTableCustomizable( + { + schema_name, + table_name, + }, + config, + ) + const is_affected_by_pgtui_bugs = isTableAffectedByPgtuiBugs( + { + schema_name, + table_name, + }, + config, + ) + + const table = { + name: table_name, + selectable_columns: {}, + insertable_columns: {}, + is_customizable, + is_affected_by_pgtui_bugs, + uses_zapatos_column_type: false, + } + + table.selectable_columns = getColumns( + table_module.getInterfaceOrThrow('Selectable'), + table, + zapatos_custom_types, + ) + table.insertable_columns = getColumns( + table_module.getInterfaceOrThrow('Insertable'), + table, + zapatos_custom_types, + ) + + tables[table_name] = table + } + + schemas[schema_name] = { + name: schema_name, + tables, + } + } + + return { + schemas, + } +} + +const isTableCustomizable = ( + args: { schema_name: string; table_name: string }, + config: Config, +) => { + const { schema_name, table_name } = args + const { customizable_tables } = config + + const schema_customizable_tables = customizable_tables?.[schema_name] + return ( + schema_customizable_tables === 'all' || + (schema_customizable_tables?.includes(table_name) ?? false) + ) +} + +const isTableAffectedByPgtuiBugs = ( + args: { schema_name: string; table_name: string }, + config: Config, +) => { + const { schema_name, table_name } = args + const { reproduce_pgtui_bugs_for_tables } = config + + const pgtui_affected_tables = reproduce_pgtui_bugs_for_tables?.[schema_name] + return pgtui_affected_tables?.includes(table_name) ?? false +} + +const readCustomZapatosTypes = (config: Config) => { + const { zapatos_dir } = config + + const project = new Project() + const custom_zapatos_types: Record = {} + + const source_files = project.addSourceFilesAtPaths( + `${zapatos_dir}/custom/**/*.d.ts`, + ) + + for (const source_file of source_files) { + const zapatos_module = source_file.getModules()[0] + + if (!zapatos_module) { + throw new Error('Could not find the zapatos module in the schema file') + } + + for (const type_alias of zapatos_module.getTypeAliases()) { + custom_zapatos_types[type_alias.getName()] = type_alias + .getTypeNodeOrThrow() + .getText() + } + } + return custom_zapatos_types +} + +const getColumns = ( + table_interface: InterfaceDeclaration, + table: Table, + zapatos_custom_types: Record, +) => { + const columns: Record = {} + + for (const column of table_interface.getProperties()) { + const column_name = column.getName() + const column_type = customizeType(column, zapatos_custom_types) + + if (column_type.includes('db.')) { + table.uses_zapatos_column_type = true + } + + columns[column_name] = { + name: column_name, + type: column_type, + is_optional: column.hasQuestionToken(), + comments: [], + } + } + + return columns +} + +const customizeType = ( + column: PropertySignature, + zapatos_custom_types: Record, +) => { + const type_node = column.getTypeNodeOrThrow() + const is_union = type_node.getKind() === SyntaxKind.UnionType + + const type_members = is_union + ? type_node + .asKindOrThrow(SyntaxKind.UnionType) + .getTypeNodes() + .map((node) => node.getText()) + : [type_node.getText()] + + let customized_type_members = type_members + customized_type_members = removeZapatosSpecificTypes(customized_type_members) + customized_type_members = replaceSvixMessageStatusEnum( + customized_type_members, + ) + customized_type_members = replaceCustomZapatosTypesWithTheirDefinitions( + customized_type_members, + zapatos_custom_types, + ) + customized_type_members = replaceJSONValueWithNonNullableVersion( + customized_type_members, + ) + + return customized_type_members.join(' | ') +} + +const removeZapatosSpecificTypes = (type_members: string[]) => + type_members.filter( + (member) => + !( + member === 'db.DefaultType' || + member.startsWith('db.Parameter') || + member.startsWith('db.SQLFragment') + ), + ) + +const replaceCustomZapatosTypesWithTheirDefinitions = ( + type_members: string[], + zapatos_custom_types: Record, +) => + type_members.map((member) => + member.startsWith('c.Pg') + ? (zapatos_custom_types[member.slice(2)] ?? member) + : member, + ) + +/** + * For some reason Zapatos generates a type `db.JSONValue` which is nullable. + * This is an issue as it means non-nullable jsonb columns are not correctly + * represented in the generated types. + */ +const replaceJSONValueWithNonNullableVersion = (type_members: string[]) => + type_members.map((member) => + member === 'db.JSONValue' ? 'NonNullable' : member, + ) + +const replaceSvixMessageStatusEnum = (type_members: string[]) => + type_members.map((member) => + member === 'svix_message_status_enum' + ? `"no_svix_app" | "sent" | "unsent"` + : member, + ) diff --git a/src/lib/todo.test.ts b/src/lib/todo.test.ts deleted file mode 100644 index 4f4b033..0000000 --- a/src/lib/todo.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import test from 'ava' - -import { todo } from './todo.js' - -test('todo: returns argument', (t) => { - t.is(todo('todo'), 'todo', 'returns input') -}) diff --git a/src/lib/todo.ts b/src/lib/todo.ts deleted file mode 100644 index 5633fe7..0000000 --- a/src/lib/todo.ts +++ /dev/null @@ -1 +0,0 @@ -export const todo = (x: string): string => x diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..9aa9a9b --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,34 @@ +export interface Config { + zapatos_dir: string + output_dir: string + customizable_tables?: Record + reproduce_pgtui_bugs_for_tables?: Record + generate_knex_types?: boolean + main_schema?: string + file_header?: string +} + +export interface DatabaseTree { + schemas: Record +} + +export interface Schema { + name: string + tables: Record +} + +export interface Table { + name: string + selectable_columns: Record + insertable_columns: Record + is_customizable: boolean + is_affected_by_pgtui_bugs: boolean + uses_zapatos_column_type: boolean +} + +export interface Column { + name: string + type: string + is_optional: boolean + comments: string[] +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..67932a9 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,5 @@ +export const pascalCase = (str: string): string => { + return str.replace(/(_\w|^\w)/g, (match) => + match.replace("_", "").toUpperCase() + ) +} diff --git a/test/todo.test.ts b/test/todo.test.ts deleted file mode 100644 index b5cd626..0000000 --- a/test/todo.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import test from 'ava' - -import { todo } from '@seamapi/dbtypr' - -test('todo: returns argument', (t) => { - t.is(todo('todo'), 'todo', 'returns input') -}) From a75e0674b9bddaf89ec8df8d976f19eb63823e1b Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 18 Apr 2025 21:35:23 +0000 Subject: [PATCH 05/11] ci: Format code --- src/lib/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 67932a9..c36a52c 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,5 @@ export const pascalCase = (str: string): string => { return str.replace(/(_\w|^\w)/g, (match) => - match.replace("_", "").toUpperCase() + match.replace('_', '').toUpperCase(), ) } From 055850f884d385f2bd093f0701c93192395e00e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:44:33 -0700 Subject: [PATCH 06/11] chore: Linter stuff --- .eslintrc.json | 118 ++++++++++++++---- .../generated/[schema_name]/index.ts | 16 +-- src/lib/file-generators/generated/index.ts | 2 +- src/lib/generate-types.ts | 4 +- src/lib/read-database-tree.ts | 4 +- 5 files changed, 106 insertions(+), 38 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 01d9344..f8c89e7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,9 @@ { "root": true, - "plugins": ["simple-import-sort", "unused-imports"], + "plugins": [ + "simple-import-sort", + "unused-imports" + ], "parserOptions": { "sourceType": "module", "ecmaVersion": "latest" @@ -18,7 +21,10 @@ "ignoreRestSiblings": true } ], - "import/extensions": ["error", "ignorePackages"], + "import/extensions": [ + "error", + "ignorePackages" + ], "import/no-duplicates": [ "error", { @@ -30,26 +36,54 @@ "error", { "groups": [ - ["^\\u0000"], - ["^node:"], - ["^@?\\w"], - ["@seamapi/dbtypr"], - ["^lib/"], - ["^"], - ["^\\."] + [ + "^\\u0000" + ], + [ + "^node:" + ], + [ + "^@?\\w" + ], + [ + "@seamapi/dbtypr" + ], + [ + "^lib/" + ], + [ + "^" + ], + [ + "^\\." + ] ] } ], - "simple-import-sort/exports": "error" + "simple-import-sort/exports": "error", + "@typescript-eslint/explicit-function-return-type": "off" }, "overrides": [ { - "files": ["*.js", "*.mjs", "*.cjs"], - "extends": ["standard", "prettier"] + "files": [ + "*.js", + "*.mjs", + "*.cjs" + ], + "extends": [ + "standard", + "prettier" + ] }, { - "files": ["*.ts", "*.tsx"], - "extends": ["standard-with-typescript", "prettier"], + "files": [ + "*.ts", + "*.tsx" + ], + "extends": [ + "standard-with-typescript", + "prettier" + ], "parserOptions": { "project": "./tsconfig.json" }, @@ -65,8 +99,13 @@ "@typescript-eslint/naming-convention": [ "error", { - "selector": ["interface", "typeAlias"], - "format": ["PascalCase"], + "selector": [ + "interface", + "typeAlias" + ], + "format": [ + "PascalCase" + ], "leadingUnderscore": "forbid", "trailingUnderscore": "forbid" }, @@ -78,27 +117,54 @@ "variable", "parameter" ], - "types": ["function"], - "format": ["camelCase"] + "types": [ + "function" + ], + "format": [ + "camelCase" + ] }, { - "selector": ["objectLiteralProperty"], + "selector": [ + "objectLiteralProperty" + ], "format": null }, { - "selector": ["parameterProperty", "variable", "parameter"], - "types": ["boolean", "string", "number", "array"], - "format": ["snake_case", "UPPER_CASE"], + "selector": [ + "parameterProperty", + "variable", + "parameter" + ], + "types": [ + "boolean", + "string", + "number", + "array" + ], + "format": [ + "snake_case", + "UPPER_CASE" + ], "leadingUnderscore": "allow", "trailingUnderscore": "allow" }, { - "selector": ["function"], - "format": ["camelCase"] + "selector": [ + "function" + ], + "format": [ + "camelCase" + ] }, { "selector": "default", - "format": ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"], + "format": [ + "camelCase", + "snake_case", + "UPPER_CASE", + "PascalCase" + ], "leadingUnderscore": "allow", "trailingUnderscore": "forbid" } @@ -106,4 +172,4 @@ } } ] -} +} \ No newline at end of file diff --git a/src/lib/file-generators/generated/[schema_name]/index.ts b/src/lib/file-generators/generated/[schema_name]/index.ts index 7803646..20e2c08 100644 --- a/src/lib/file-generators/generated/[schema_name]/index.ts +++ b/src/lib/file-generators/generated/[schema_name]/index.ts @@ -38,16 +38,18 @@ export const createSchemaGeneratedTypeIndexFile = ( ] for (const table of tables) { + const pascal_table_name = pascal_table_names[table.name] ?? '' + statements.push({ kind: StructureKind.ImportDeclaration, isTypeOnly: true, - moduleSpecifier: `./${pascal_table_names[table.name]}`, + moduleSpecifier: `./${pascal_table_name}`, namedImports: [ - pascal_table_names[table.name]!, + pascal_table_name, ...(table.is_affected_by_pgtui_bugs - ? [`${pascal_table_names[table.name]}WithPgtuiBugs`] + ? [`${pascal_table_name}WithPgtuiBugs`] : []), - `${pascal_table_names[table.name]}Initializer`, + `${pascal_table_name}Initializer`, ], }) } @@ -71,7 +73,7 @@ export const createSchemaGeneratedTypeIndexFile = ( (writer) => writer.newLine(), ) - if (config.generate_knex_types) { + if (config.generate_knex_types === true) { statements.push({ kind: StructureKind.Interface, isExported: true, @@ -83,7 +85,7 @@ export const createSchemaGeneratedTypeIndexFile = ( : `"${schema.name}.${table.name}"`, type: table.is_affected_by_pgtui_bugs ? `${pascal_table_names[table.name]}WithPgtuiBugs` - : pascal_table_names[table.name]!, + : (pascal_table_names[table.name] ?? ''), })), }) } @@ -107,7 +109,7 @@ export const createSchemaGeneratedTypeIndexFile = ( kind: StructureKind.ExportDeclaration, isTypeOnly: true, namedExports: tables.flatMap((table) => [ - pascal_table_names[table.name]!, + pascal_table_names[table.name] ?? '', ...(table.is_affected_by_pgtui_bugs ? [`${pascal_table_names[table.name]}WithPgtuiBugs`] : []), diff --git a/src/lib/file-generators/generated/index.ts b/src/lib/file-generators/generated/index.ts index a30d81b..42ad872 100644 --- a/src/lib/file-generators/generated/index.ts +++ b/src/lib/file-generators/generated/index.ts @@ -83,7 +83,7 @@ export const createGeneratedIndexFile = ( }, ] - if (config.generate_knex_types) { + if (config.generate_knex_types === true) { statements.push({ kind: StructureKind.ExportDeclaration, namespaceExport: 'knex', diff --git a/src/lib/generate-types.ts b/src/lib/generate-types.ts index 43a7f89..f2310c5 100644 --- a/src/lib/generate-types.ts +++ b/src/lib/generate-types.ts @@ -43,7 +43,7 @@ const createGeneratedTypesFiles = ( createGeneratedUtilsFile({ project }, config), ] - if (config.generate_knex_types) { + if (config.generate_knex_types === true) { generated_types_files.push( createKnexIndexFile({ project, schemas }, config), ) @@ -86,7 +86,7 @@ const createCustomTypesFiles = ( createCustomTypesFile({ project, schema, table }, config), ) } catch (error: any) { - if (error.message.includes('A source file already exists')) { + if (error.message.includes('A source file already exists') === true) { continue } throw error as Error diff --git a/src/lib/read-database-tree.ts b/src/lib/read-database-tree.ts index 615ca88..b50a17b 100644 --- a/src/lib/read-database-tree.ts +++ b/src/lib/read-database-tree.ts @@ -17,7 +17,7 @@ export const readDatabaseTree = (config: Config): DatabaseTree => { const source_file = project.addSourceFileAtPath(`${zapatos_dir}/schema.d.ts`) const zapatos_module = source_file.getModules()[0] - if (!zapatos_module) { + if (zapatos_module === undefined) { throw new Error('Could not find the zapatos module in the schema file') } @@ -123,7 +123,7 @@ const readCustomZapatosTypes = (config: Config) => { for (const source_file of source_files) { const zapatos_module = source_file.getModules()[0] - if (!zapatos_module) { + if (zapatos_module === undefined) { throw new Error('Could not find the zapatos module in the schema file') } From 96f1868da36523ca5ec550ce673039b4eccea623 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 18 Apr 2025 21:45:02 +0000 Subject: [PATCH 07/11] ci: Format code --- .eslintrc.json | 115 +++++++++++-------------------------------------- 1 file changed, 25 insertions(+), 90 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index f8c89e7..10c2b25 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,6 @@ { "root": true, - "plugins": [ - "simple-import-sort", - "unused-imports" - ], + "plugins": ["simple-import-sort", "unused-imports"], "parserOptions": { "sourceType": "module", "ecmaVersion": "latest" @@ -21,10 +18,7 @@ "ignoreRestSiblings": true } ], - "import/extensions": [ - "error", - "ignorePackages" - ], + "import/extensions": ["error", "ignorePackages"], "import/no-duplicates": [ "error", { @@ -36,27 +30,13 @@ "error", { "groups": [ - [ - "^\\u0000" - ], - [ - "^node:" - ], - [ - "^@?\\w" - ], - [ - "@seamapi/dbtypr" - ], - [ - "^lib/" - ], - [ - "^" - ], - [ - "^\\." - ] + ["^\\u0000"], + ["^node:"], + ["^@?\\w"], + ["@seamapi/dbtypr"], + ["^lib/"], + ["^"], + ["^\\."] ] } ], @@ -65,25 +45,12 @@ }, "overrides": [ { - "files": [ - "*.js", - "*.mjs", - "*.cjs" - ], - "extends": [ - "standard", - "prettier" - ] + "files": ["*.js", "*.mjs", "*.cjs"], + "extends": ["standard", "prettier"] }, { - "files": [ - "*.ts", - "*.tsx" - ], - "extends": [ - "standard-with-typescript", - "prettier" - ], + "files": ["*.ts", "*.tsx"], + "extends": ["standard-with-typescript", "prettier"], "parserOptions": { "project": "./tsconfig.json" }, @@ -99,13 +66,8 @@ "@typescript-eslint/naming-convention": [ "error", { - "selector": [ - "interface", - "typeAlias" - ], - "format": [ - "PascalCase" - ], + "selector": ["interface", "typeAlias"], + "format": ["PascalCase"], "leadingUnderscore": "forbid", "trailingUnderscore": "forbid" }, @@ -117,54 +79,27 @@ "variable", "parameter" ], - "types": [ - "function" - ], - "format": [ - "camelCase" - ] + "types": ["function"], + "format": ["camelCase"] }, { - "selector": [ - "objectLiteralProperty" - ], + "selector": ["objectLiteralProperty"], "format": null }, { - "selector": [ - "parameterProperty", - "variable", - "parameter" - ], - "types": [ - "boolean", - "string", - "number", - "array" - ], - "format": [ - "snake_case", - "UPPER_CASE" - ], + "selector": ["parameterProperty", "variable", "parameter"], + "types": ["boolean", "string", "number", "array"], + "format": ["snake_case", "UPPER_CASE"], "leadingUnderscore": "allow", "trailingUnderscore": "allow" }, { - "selector": [ - "function" - ], - "format": [ - "camelCase" - ] + "selector": ["function"], + "format": ["camelCase"] }, { "selector": "default", - "format": [ - "camelCase", - "snake_case", - "UPPER_CASE", - "PascalCase" - ], + "format": ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"], "leadingUnderscore": "allow", "trailingUnderscore": "forbid" } @@ -172,4 +107,4 @@ } } ] -} \ No newline at end of file +} From d3c7ee0f8cb167c529a0c0531cd18651824f26dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:45:04 -0700 Subject: [PATCH 08/11] chore: Remove examples --- examples/index.ts | 9 --------- examples/todo.ts | 23 ----------------------- 2 files changed, 32 deletions(-) delete mode 100755 examples/index.ts delete mode 100644 examples/todo.ts diff --git a/examples/index.ts b/examples/index.ts deleted file mode 100755 index 7a1cc11..0000000 --- a/examples/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env tsx - -import landlubber from 'landlubber' - -import * as todo from './todo.js' - -const commands = [todo] - -await landlubber(commands).parse() diff --git a/examples/todo.ts b/examples/todo.ts deleted file mode 100644 index 168c6af..0000000 --- a/examples/todo.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Builder, Command, Describe, Handler } from 'landlubber' - -import { todo } from '@seamapi/dbtypr' - -interface Options { - x: string -} - -export const command: Command = 'todo x' - -export const describe: Describe = 'TODO' - -export const builder: Builder = { - x: { - type: 'string', - default: 'TODO', - describe: 'TODO', - }, -} - -export const handler: Handler = async ({ x, logger }) => { - logger.info({ data: todo(x) }, 'TODO') -} From 1d4bbdcd98cbb464e32b7268afd7d418c3a7c490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:48:04 -0700 Subject: [PATCH 09/11] chore: Lint rules --- .eslintrc.json | 119 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 10c2b25..f7c8c32 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,9 @@ { "root": true, - "plugins": ["simple-import-sort", "unused-imports"], + "plugins": [ + "simple-import-sort", + "unused-imports" + ], "parserOptions": { "sourceType": "module", "ecmaVersion": "latest" @@ -18,7 +21,10 @@ "ignoreRestSiblings": true } ], - "import/extensions": ["error", "ignorePackages"], + "import/extensions": [ + "error", + "ignorePackages" + ], "import/no-duplicates": [ "error", { @@ -30,27 +36,53 @@ "error", { "groups": [ - ["^\\u0000"], - ["^node:"], - ["^@?\\w"], - ["@seamapi/dbtypr"], - ["^lib/"], - ["^"], - ["^\\."] + [ + "^\\u0000" + ], + [ + "^node:" + ], + [ + "^@?\\w" + ], + [ + "@seamapi/dbtypr" + ], + [ + "^lib/" + ], + [ + "^" + ], + [ + "^\\." + ] ] } ], - "simple-import-sort/exports": "error", - "@typescript-eslint/explicit-function-return-type": "off" + "simple-import-sort/exports": "error" }, "overrides": [ { - "files": ["*.js", "*.mjs", "*.cjs"], - "extends": ["standard", "prettier"] + "files": [ + "*.js", + "*.mjs", + "*.cjs" + ], + "extends": [ + "standard", + "prettier" + ] }, { - "files": ["*.ts", "*.tsx"], - "extends": ["standard-with-typescript", "prettier"], + "files": [ + "*.ts", + "*.tsx" + ], + "extends": [ + "standard-with-typescript", + "prettier" + ], "parserOptions": { "project": "./tsconfig.json" }, @@ -63,11 +95,17 @@ } ], "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/naming-convention": [ "error", { - "selector": ["interface", "typeAlias"], - "format": ["PascalCase"], + "selector": [ + "interface", + "typeAlias" + ], + "format": [ + "PascalCase" + ], "leadingUnderscore": "forbid", "trailingUnderscore": "forbid" }, @@ -79,27 +117,54 @@ "variable", "parameter" ], - "types": ["function"], - "format": ["camelCase"] + "types": [ + "function" + ], + "format": [ + "camelCase" + ] }, { - "selector": ["objectLiteralProperty"], + "selector": [ + "objectLiteralProperty" + ], "format": null }, { - "selector": ["parameterProperty", "variable", "parameter"], - "types": ["boolean", "string", "number", "array"], - "format": ["snake_case", "UPPER_CASE"], + "selector": [ + "parameterProperty", + "variable", + "parameter" + ], + "types": [ + "boolean", + "string", + "number", + "array" + ], + "format": [ + "snake_case", + "UPPER_CASE" + ], "leadingUnderscore": "allow", "trailingUnderscore": "allow" }, { - "selector": ["function"], - "format": ["camelCase"] + "selector": [ + "function" + ], + "format": [ + "camelCase" + ] }, { "selector": "default", - "format": ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"], + "format": [ + "camelCase", + "snake_case", + "UPPER_CASE", + "PascalCase" + ], "leadingUnderscore": "allow", "trailingUnderscore": "forbid" } @@ -107,4 +172,4 @@ } } ] -} +} \ No newline at end of file From 0e8755408563b511d5f76d518a6fdda82450e296 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 18 Apr 2025 21:48:31 +0000 Subject: [PATCH 10/11] ci: Format code --- .eslintrc.json | 115 +++++++++++-------------------------------------- 1 file changed, 25 insertions(+), 90 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index f7c8c32..716bc92 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,6 @@ { "root": true, - "plugins": [ - "simple-import-sort", - "unused-imports" - ], + "plugins": ["simple-import-sort", "unused-imports"], "parserOptions": { "sourceType": "module", "ecmaVersion": "latest" @@ -21,10 +18,7 @@ "ignoreRestSiblings": true } ], - "import/extensions": [ - "error", - "ignorePackages" - ], + "import/extensions": ["error", "ignorePackages"], "import/no-duplicates": [ "error", { @@ -36,27 +30,13 @@ "error", { "groups": [ - [ - "^\\u0000" - ], - [ - "^node:" - ], - [ - "^@?\\w" - ], - [ - "@seamapi/dbtypr" - ], - [ - "^lib/" - ], - [ - "^" - ], - [ - "^\\." - ] + ["^\\u0000"], + ["^node:"], + ["^@?\\w"], + ["@seamapi/dbtypr"], + ["^lib/"], + ["^"], + ["^\\."] ] } ], @@ -64,25 +44,12 @@ }, "overrides": [ { - "files": [ - "*.js", - "*.mjs", - "*.cjs" - ], - "extends": [ - "standard", - "prettier" - ] + "files": ["*.js", "*.mjs", "*.cjs"], + "extends": ["standard", "prettier"] }, { - "files": [ - "*.ts", - "*.tsx" - ], - "extends": [ - "standard-with-typescript", - "prettier" - ], + "files": ["*.ts", "*.tsx"], + "extends": ["standard-with-typescript", "prettier"], "parserOptions": { "project": "./tsconfig.json" }, @@ -99,13 +66,8 @@ "@typescript-eslint/naming-convention": [ "error", { - "selector": [ - "interface", - "typeAlias" - ], - "format": [ - "PascalCase" - ], + "selector": ["interface", "typeAlias"], + "format": ["PascalCase"], "leadingUnderscore": "forbid", "trailingUnderscore": "forbid" }, @@ -117,54 +79,27 @@ "variable", "parameter" ], - "types": [ - "function" - ], - "format": [ - "camelCase" - ] + "types": ["function"], + "format": ["camelCase"] }, { - "selector": [ - "objectLiteralProperty" - ], + "selector": ["objectLiteralProperty"], "format": null }, { - "selector": [ - "parameterProperty", - "variable", - "parameter" - ], - "types": [ - "boolean", - "string", - "number", - "array" - ], - "format": [ - "snake_case", - "UPPER_CASE" - ], + "selector": ["parameterProperty", "variable", "parameter"], + "types": ["boolean", "string", "number", "array"], + "format": ["snake_case", "UPPER_CASE"], "leadingUnderscore": "allow", "trailingUnderscore": "allow" }, { - "selector": [ - "function" - ], - "format": [ - "camelCase" - ] + "selector": ["function"], + "format": ["camelCase"] }, { "selector": "default", - "format": [ - "camelCase", - "snake_case", - "UPPER_CASE", - "PascalCase" - ], + "format": ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"], "leadingUnderscore": "allow", "trailingUnderscore": "forbid" } @@ -172,4 +107,4 @@ } } ] -} \ No newline at end of file +} From 4262339a594c85739d7571c48ed6f20490aa4ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Pr=C3=A9?= Date: Fri, 18 Apr 2025 14:50:47 -0700 Subject: [PATCH 11/11] test: Add default test --- test/index.test.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/index.test.ts diff --git a/test/index.test.ts b/test/index.test.ts new file mode 100644 index 0000000..69e3f9f --- /dev/null +++ b/test/index.test.ts @@ -0,0 +1,5 @@ +import test from 'ava' + +test('todo', (t) => { + t.true(true) +})