diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index d4aa98b..f85dfd6 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -18,4 +18,5 @@ jobs: cache: "npm" - run: npm ci - run: npm run lint + - run: npm run build - run: npm test diff --git a/README.md b/README.md index cf03084..5988a95 100644 --- a/README.md +++ b/README.md @@ -46,22 +46,23 @@ Note that `bigint` types don't have a default `toJSON` method, so the above assu ## Options -| **Option** | **Type** | **Default** | **Description** | -| ----------------- | :----------------------------------------------------: | :---------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| output | `string` | `"interfaces.ts"` | The output location for the generated Typescript interfaces. | -| enumPrefix | `string` | `""` | Prefix to add to enum types. | -| enumSuffix | `string` | `""` | Suffix to add to enum types. | -| modelPrefix | `string` | `""` | Prefix to add to model types. | -| modelSuffix | `string` | `""` | Suffix to add to model types. | -| typePrefix | `string` | `""` | Prefix to add to [type](https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-composite-types) types (mongodb only). | -| typeSuffix | `string` | `""` | Suffix to add to [type](https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-composite-types) types (mongodb only). | -| enumType | `"stringUnion" \| "enum"` | `"stringUnion"` | Controls how enums are generated. `"enum"` will create Typescript enums, `"stringUnion"` will create union types with all the enum values. | -| dateType | `"Date" \| "string"` | `"Date"` | The type to use for DateTime model fields. | -| bigIntType | `"bigint" \| "string" \| "number"` | `"bigint"` | The type to use for BigInt model fields. | -| decimalType | `"Decimal" \| "string"` | `"Decimal"` | The type to use for Decimal model fields. Note that the `Decimal` type here is just an interface with a `getValue()` function. You will need to cast to an actual Decimal type if you want to use other methods. | -| bytesType | `"Buffer" \| "BufferObject" \| "string" \| "number[]"` | `"Buffer"` | The type to use for Bytes model fields. `BufferObject` is a type definition which matches the output of `Buffer.toJSON()`, which is called when running `JSON.stringify()` on a Buffer. | -| optionalRelations | `boolean` | `true` | Controls whether model relation fields are optional or not. If `true`, all model relation fields will use `?:` in the field definition. | -| prettier | `boolean` | `false` | Formats the output using Prettier. Setting this to `true` requires that the `prettier` package is available. [Prettier settings files](https://prettier.io/docs/en/configuration.html) will be respected. | +| **Option** | **Type** | **Default** | **Description** | +| ----------------- | :----------------------------------------------------: | :------------------------------------------------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| output | `string` | `"interfaces.ts"` | The output location for the generated Typescript interfaces. | +| enumPrefix | `string` | `""` | Prefix to add to enum types. | +| enumSuffix | `string` | `""` | Suffix to add to enum types. | +| modelPrefix | `string` | `""` | Prefix to add to model types. | +| modelSuffix | `string` | `""` | Suffix to add to model types. | +| typePrefix | `string` | `""` | Prefix to add to [type](https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-composite-types) types (mongodb only). | +| typeSuffix | `string` | `""` | Suffix to add to [type](https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-composite-types) types (mongodb only). | +| headerComment | `string` | `"This file was auto-generated by prisma-generator-typescript-interfaces"` | Sets the header comment added to the top of the generated file. Set this to an empty string to disable the header comment. Supports multiple lines with `"\n"`. | +| enumType | `"stringUnion" \| "enum"` | `"stringUnion"` | Controls how enums are generated. `"enum"` will create Typescript enums, `"stringUnion"` will create union types with all the enum values. | +| dateType | `"Date" \| "string"` | `"Date"` | The type to use for DateTime model fields. | +| bigIntType | `"bigint" \| "string" \| "number"` | `"bigint"` | The type to use for BigInt model fields. | +| decimalType | `"Decimal" \| "string"` | `"Decimal"` | The type to use for Decimal model fields. Note that the `Decimal` type here is just an interface with a `getValue()` function. You will need to cast to an actual Decimal type if you want to use other methods. | +| bytesType | `"Buffer" \| "BufferObject" \| "string" \| "number[]"` | `"Buffer"` | The type to use for Bytes model fields. `BufferObject` is a type definition which matches the output of `Buffer.toJSON()`, which is called when running `JSON.stringify()` on a Buffer. | +| optionalRelations | `boolean` | `true` | Controls whether model relation fields are optional or not. If `true`, all model relation fields will use `?:` in the field definition. | +| prettier | `boolean` | `false` | Formats the output using Prettier. Setting this to `true` requires that the `prettier` package is available. [Prettier settings files](https://prettier.io/docs/en/configuration.html) will be respected. | ## Example @@ -169,9 +170,11 @@ model Data { #### Output
-src/interfaces.ts +src/dto/interfaces.ts ```typescript +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Fruits = "Apple" | "Banana" | "Orange" | "Pear"; export interface RelationA { @@ -243,9 +246,11 @@ type JsonValue =
-src/json-interfaces.ts +src/dto/json-interfaces.ts ```typescript +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Fruits = "Apple" | "Banana" | "Orange" | "Pear"; export interface RelationAJson { @@ -326,20 +331,14 @@ All the code for this generator is contained within the `generator.ts` file. You ### Tests -You can run tests with `npm run test`. Tests are run using a custom script, see `test.ts` for details. You can add new tests by placing a Prisma schema and the expected output in a folder under the `tests` directory, you may want to look at the `tests/no-options` test as an example. +You can run tests with `npm run test`. Tests are run using a custom script, see `test.ts` for details. You can add new tests by placing a Prisma schema and the expected output in a folder under the `tests` directory, you may want to look at the `tests/options-behavior` test as an example. You can run specific tests by passing them as arguments to the test command: ``` -npm run test -- buffer-array-type mongo-types required-relations +npm run test -- options-behavior validation-errors ``` When a test fails, you can see the generated output in the `__TEST_TMP__` folder inside that test's directory. Compare this with the expected output to see why it failed. -By default the test runner will quit when it encounters it's first failure. If you want it to continue after failures, use the `-c` (or `--continue`) option: - -``` -npm run test -- -c -``` - Please ensure all tests are passing and that the code is properly linted (`npm run lint`) before submitting a PR, thanks! diff --git a/generator.ts b/generator.ts index 08c5870..016d9d0 100644 --- a/generator.ts +++ b/generator.ts @@ -9,6 +9,7 @@ interface Config { modelSuffix: string; typePrefix: string; typeSuffix: string; + headerComment: string; enumType: "stringUnion" | "enum"; dateType: "Date" | "string"; bigIntType: "bigint" | "string" | "number"; @@ -156,6 +157,7 @@ generatorHandler({ modelSuffix: "", typePrefix: "", typeSuffix: "", + headerComment: "This file was auto-generated by prisma-generator-typescript-interfaces", enumType: "stringUnion", dateType: "Date", bigIntType: "bigint", @@ -195,6 +197,14 @@ generatorHandler({ let ts = [...enumsTs, ...modelsTs, ...customTypesTs].join("\n\n") + "\n"; + if (config.headerComment) { + const headerContent = config.headerComment + .split("\n") + .map((line) => `// ${line}`) + .join("\n"); + ts = `${headerContent}\n\n${ts}`; + } + if (config.prettier) { // Prettier is imported inside this if so that it's not a required dependency let prettier: typeof import("prettier"); diff --git a/package.json b/package.json index be4e6b3..0baf4ff 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,18 @@ { "name": "prisma-generator-typescript-interfaces", - "version": "1.0.1", + "version": "1.1.0", "description": "Generate zero-dependency Typescript interfaces from Prisma schema", "main": "generator.js", "bin": { "prisma-generator-typescript-interfaces": "generator.js" }, "scripts": { - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "clean": "rimraf --glob *.js **/__TEST_TMP__", "generate": "prisma generate --schema=prisma/example.prisma", "lint": "prettier --check . && eslint . --ext .ts", "lint:fix": "prettier --write . && eslint . --ext .ts --fix", - "test": "npm run build && node test.js", + "test": "node --loader ts-node/esm test.ts", "prepare": "husky install" }, "files": [ diff --git a/test.ts b/test.ts index ea7cde4..6099922 100644 --- a/test.ts +++ b/test.ts @@ -4,11 +4,8 @@ * test folders). If a test is expected to fail, the expected error message should be put in an * "expected-error.txt" file in the relevant test folder. * - * You can run specific tests by passing the one(s) you want to run as arguments to this script: - * npm run test -- custom-output no-options prettier - * - * If you want to run all tests even if some fail, pass the --continue or -c flag: - * npm run test -- -c + * You can run specific tests by passing them as arguments to this script: + * npm run test -- options-behavior validation-errors */ import { exec } from "node:child_process"; @@ -18,13 +15,11 @@ import fs from "node:fs/promises"; import path from "node:path"; const TEMP_TEST_DIRNAME = "__TEST_TMP__"; -const BASE_REPLACE_REGEX = /^\/\/ ?#INSERT base\.([a-z]+)\.prisma$/gm; const RED = "\x1b[1;97;41m"; const GREEN = "\x1b[1;102;30m"; const RESET = "\x1b[0m"; const execAsync = promisify(exec); -const readFile = async (path: string) => fs.readFile(path, { encoding: "utf-8" }); const trimMultiLine = (s: string) => s .trim() @@ -32,81 +27,70 @@ const trimMultiLine = (s: string) => .map((l) => l.trim()) .join("\n"); -let testFilters = process.argv.slice(2); +async function readFile(path: string) { + try { + return await fs.readFile(path, { encoding: "utf-8" }); + } catch (e) { + if (e instanceof Error && "code" in e && e.code === "ENOENT") { + return null; // Return null when file is not found + } + throw e; + } +} -// Continue on errors if --continue or -c is passed -let continueOnError = false; -let hasErrors = false; -if (testFilters.some((f) => f === "--continue" || f === "-c")) { - continueOnError = true; - testFilters = testFilters.filter((f) => f !== "--continue" && f !== "-c"); +async function readDir(path: string) { + try { + return await fs.readdir(path, { withFileTypes: true }); + } catch (e) { + if (e instanceof Error && "code" in e && e.code === "ENOENT") { + return []; // Return empty array when dir not found + } + throw e; + } } -const testsEntries = await fs.readdir("tests", { withFileTypes: true }); -const tests = testsEntries +const testFilters = process.argv.slice(2); + +const tests = (await readDir("tests")) .filter((d) => d.isDirectory() && (!testFilters.length || testFilters.includes(d.name))) .map((d) => path.join(d.path, d.name)); -// Common schemas used by multiple tests -const baseSchemas = new Map( - await Promise.all( - testsEntries - .filter((f) => f.isFile() && /^base\.[a-z]+\.prisma$/.test(f.name)) - .map>((f) => - readFile(path.join(f.path, f.name)).then((c) => [f.name, c]), - ), - ), -); +if (!tests.length) { + console.error("No tests found!"); + process.exit(1); +} // Get the length of the longest test name, so we can pad the output const longestName = Math.max(...tests.map((t) => t.length)); -console.log("Running tests..."); +console.log("\nRunning tests..."); + +let hasErrors = false; for (const test of tests) { try { process.stdout.write(` ${test}${" ".repeat(longestName - test.length + 2)}`); - const schema = (await readFile(path.join(test, "schema.prisma"))).replaceAll( - BASE_REPLACE_REGEX, - (_, baseName) => { - const baseSchema = baseSchemas.get(`base.${baseName}.prisma`); - if (!baseSchema) { - throw new Error(`Unknown base schema: ${baseName}`); - } - return baseSchema; - }, - ); - - let expectedError: string | null; // Text of expected stderr after a non-zero exit code - let expectedFiles: Map | null; // Map of file name to expected contents. - - try { - expectedError = await readFile(path.join(test, "expected-error.txt")); - } catch { - expectedError = null; + const schema = await readFile(path.join(test, "schema.prisma")); + if (!schema) { + throw new Error(`Test ${test} has no schema.prisma!`); } - try { - expectedFiles = new Map( - await Promise.all<[string, string]>( - (await fs.readdir(path.join(test, "expected"))).map>( - async (f) => { - const contents = await readFile(path.join(test, "expected", f)); - return [f, contents]; - }, - ), - ), - ); - } catch { - expectedFiles = null; + let expectedError = await readFile(path.join(test, "expected-error.txt")); + + const expectedFiles: Map = new Map(); + for (const entry of await readDir(path.join(test, "expected"))) { + if (entry.isFile()) { + expectedFiles.set(entry.name, await readFile(path.join(test, "expected", entry.name))); + } } - if (!expectedError && !expectedFiles) { - throw new Error(`Test ${test} has no expected output!`); + if (expectedFiles.size === 0 && !expectedError) { + throw new Error(`Test ${test} has no expected files or errors!`); } const testDir = path.join(test, TEMP_TEST_DIRNAME); + await rimraf(testDir); // Ensure test dir is clean before running await fs.mkdir(testDir, { recursive: true }); await fs.writeFile(path.join(testDir, "schema.prisma"), schema); @@ -128,28 +112,47 @@ for (const test of tests) { throw new Error("Expected error did not occur!"); } - if (expectedFiles) { - for (const [filename, contents] of expectedFiles.entries()) { - const filePath = path.join(testDir, filename); - const actualContents = await readFile(filePath); - if (actualContents !== contents) { - throw new Error( - `Generated ${filename} does not match expected contents! Check the output in ${filePath}`, - ); - } + const errors: string[] = []; + const uncheckedFileNames = new Set(expectedFiles.keys()); + + for (const entry of await readDir(testDir)) { + if (!entry.isFile() || entry.name === "schema.prisma") { + continue; } + + uncheckedFileNames.delete(entry.name); + + const filePath = path.join(testDir, entry.name); + const fileContents = await readFile(filePath); + const expectedContents = expectedFiles.get(entry.name); + + if (!expectedContents) { + errors.push(`Unexpected file ${entry.name} in test output! See ${filePath}`); + continue; + } + + if (fileContents !== expectedContents) { + errors.push( + `Generated ${entry.name} does not match expected contents! Check the output in ${filePath}`, + ); + } + } + + for (const file of uncheckedFileNames) { + errors.push(`Expected file ${file} was not generated!`); + } + + if (errors.length) { + throw new Error("Errors:\n" + errors.map((e) => ` - ${e}`).join("\n")); } process.stdout.write(GREEN + " PASS " + RESET + "\n"); - await rimraf(testDir); + await rimraf(testDir); // Clean up test dir on success } catch (e) { process.stdout.write(RED + " FAIL " + RESET + "\n\n"); console.error((e as Error).message, "\n"); hasErrors = true; - if (!continueOnError) { - process.exit(1); - } } } diff --git a/tests/base.mongo.prisma b/tests/base.mongo.prisma deleted file mode 100644 index 9d39f40..0000000 --- a/tests/base.mongo.prisma +++ /dev/null @@ -1,45 +0,0 @@ -// We need to use mongo to test composite types -datasource db { - provider = "mongodb" - url = "" -} - -enum Gender { - Male - Female - Other -} - -enum PhotoType { - Selfie - Profile - Tagged -} - -model Person { - id Int @id @map("_id") - name String - gender Gender - addressId Int - address Address @relation(fields: [addressId], references: [id]) - photos Photo[] - tags Tag? -} - -model Address { - id Int @id @map("_id") - addresText String - people Person[] -} - -type Photo { - height Int - Width Int - url String - type PhotoType -} - -type Tag { - id Int - name String -} diff --git a/tests/base.postgres.prisma b/tests/base.postgres.prisma deleted file mode 100644 index c480d79..0000000 --- a/tests/base.postgres.prisma +++ /dev/null @@ -1,79 +0,0 @@ -// Common schema used by tests. The tests insert different generators into it -datasource db { - provider = "postgresql" - url = "" -} - -enum Gender { - Male - Female - Other -} - -enum DataTest { - Apple - Banana - Orange - Pear -} - -model Person { - id Int @id @default(autoincrement()) - name String - age Int - email String? - gender Gender - addressId Int - address Address @relation(fields: [addressId], references: [id]) - friends Person[] @relation("Friends") - friendsOf Person[] @relation("Friends") - data Data? -} - -model Address { - id Int @id - streetNumber Int - streetName String - city String - isBilling Boolean - people Person[] -} - -model Data { - id String @id @default(uuid()) @db.Uuid - stringField String - booleanField Boolean - intField Int - bigIntField BigInt - floatField Float - decimalField Decimal - dateField DateTime - jsonField Json - bytesField Bytes - enumField DataTest - - optionalStringField String? - optionalBooleanField Boolean? - optionalIntField Int? - optionalBigIntField BigInt? - optionalFloatField Float? - optionalDecimalField Decimal? - optionalDateField DateTime? - optionalJsonField Json? - optionalBytesField Bytes? - optionalEnumField DataTest? - - stringArrayField String[] - booleanArrayField Boolean[] - intArrayField Int[] - bigIntArrayField BigInt[] - floatArrayField Float[] - decimalArrayField Decimal[] - dateArrayField DateTime[] - jsonArrayField Json[] - bytesArrayField Bytes[] - enumArrayField DataTest[] - - personId Int @unique - person Person @relation(fields: [personId], references: [id]) -} diff --git a/tests/bigint-number-type/schema.prisma b/tests/bigint-number-type/schema.prisma deleted file mode 100644 index bddcca3..0000000 --- a/tests/bigint-number-type/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - bigIntType = "number" -} - -// #INSERT base.postgres.prisma diff --git a/tests/buffer-array-type/schema.prisma b/tests/buffer-array-type/schema.prisma deleted file mode 100644 index 80b7cc2..0000000 --- a/tests/buffer-array-type/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - bytesType = "number[]" -} - -// #INSERT base.postgres.prisma diff --git a/tests/buffer-object-type/schema.prisma b/tests/buffer-object-type/schema.prisma deleted file mode 100644 index cec0bc0..0000000 --- a/tests/buffer-object-type/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - bytesType = "BufferObject" -} - -// #INSERT base.postgres.prisma diff --git a/tests/custom-output/schema.prisma b/tests/custom-output/schema.prisma deleted file mode 100644 index 7b63736..0000000 --- a/tests/custom-output/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - output = "generated.ts" -} - -// #INSERT base.postgres.prisma diff --git a/tests/enums-prefix-suffix/schema.prisma b/tests/enums-prefix-suffix/schema.prisma deleted file mode 100644 index d1b5eb5..0000000 --- a/tests/enums-prefix-suffix/schema.prisma +++ /dev/null @@ -1,8 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - enumType = "enum" - enumPrefix = "Enum" - enumSuffix = "Union" -} - -// #INSERT base.postgres.prisma diff --git a/tests/enums/schema.prisma b/tests/enums/schema.prisma deleted file mode 100644 index 395a805..0000000 --- a/tests/enums/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - enumType = "enum" -} - -// #INSERT base.postgres.prisma diff --git a/tests/mongo-types/expected/interfaces.ts b/tests/mongo-types/expected/mongoTypes.ts similarity index 83% rename from tests/mongo-types/expected/interfaces.ts rename to tests/mongo-types/expected/mongoTypes.ts index ed19d41..71d0455 100644 --- a/tests/mongo-types/expected/interfaces.ts +++ b/tests/mongo-types/expected/mongoTypes.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type PhotoType = "Selfie" | "Profile" | "Tagged"; @@ -14,7 +16,7 @@ export interface Person { export interface Address { id: number; - addresText: string; + addressText: string; people?: Person[]; } diff --git a/tests/mongo-types/expected/prefixSuffix.ts b/tests/mongo-types/expected/prefixSuffix.ts new file mode 100644 index 0000000..bdd30dd --- /dev/null +++ b/tests/mongo-types/expected/prefixSuffix.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + +export type Gender = "Male" | "Female" | "Other"; + +export type PhotoType = "Selfie" | "Profile" | "Tagged"; + +export interface Person { + id: number; + name: string; + gender: Gender; + addressId: number; + address?: Address; + photos: typePhotoType[]; + tags: typeTagType | null; +} + +export interface Address { + id: number; + addressText: string; + people?: Person[]; +} + +export interface typePhotoType { + height: number; + Width: number; + url: string; + type: PhotoType; +} + +export interface typeTagType { + id: number; + name: string; +} diff --git a/tests/mongo-types/schema.prisma b/tests/mongo-types/schema.prisma index 53df48a..7454334 100644 --- a/tests/mongo-types/schema.prisma +++ b/tests/mongo-types/schema.prisma @@ -1,5 +1,67 @@ -generator typescriptInterfaces { - provider = "node generator.js" +// Test against mongodb schema to allow testing composite types +// https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-composite-types + +// ======================== +// Generator Tests +// ======================== + +generator mongoTypes { + provider = "node --loader ts-node/esm generator.ts" + output = "mongoTypes.ts" +} + +generator prefixSuffix { + provider = "node --loader ts-node/esm generator.ts" + output = "prefixSuffix.ts" + typePrefix = "type" + typeSuffix = "Type" +} + +// ======================== +// Prisma Schema +// ======================== + +datasource db { + provider = "mongodb" + url = "" +} + +enum Gender { + Male + Female + Other } -// #INSERT base.mongo.prisma +enum PhotoType { + Selfie + Profile + Tagged +} + +model Person { + id Int @id @map("_id") + name String + gender Gender + addressId Int + address Address @relation(fields: [addressId], references: [id]) + photos Photo[] + tags Tag? +} + +model Address { + id Int @id @map("_id") + addressText String + people Person[] +} + +type Photo { + height Int + Width Int + url String + type PhotoType +} + +type Tag { + id Int + name String +} diff --git a/tests/no-custom-types/expected/interfaces.ts b/tests/no-custom-types/expected/interfaces.ts index d0d56ed..7ad5d2a 100644 --- a/tests/no-custom-types/expected/interfaces.ts +++ b/tests/no-custom-types/expected/interfaces.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export interface User { id: string; name: string; diff --git a/tests/no-custom-types/schema.prisma b/tests/no-custom-types/schema.prisma index e5c1014..39f1a90 100644 --- a/tests/no-custom-types/schema.prisma +++ b/tests/no-custom-types/schema.prisma @@ -1,13 +1,23 @@ +// Test against a basic Prisma schema which does not use any types requiring custom type definitions +// in the output (like Decimal or Json), to ensure that such definitions are omitted. + +// ======================== +// Generator Tests +// ======================== + generator typescriptInterfaces { - provider = "node generator.js" + provider = "node --loader ts-node/esm generator.ts" } +// ======================== +// Prisma Schema +// ======================== + datasource db { provider = "postgresql" url = "" } -// Basic model which does not use custom types such as Json or Decimal model User { id String @id @default(cuid()) name String diff --git a/tests/no-options/schema.prisma b/tests/no-options/schema.prisma deleted file mode 100644 index fc8316b..0000000 --- a/tests/no-options/schema.prisma +++ /dev/null @@ -1,5 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" -} - -// #INSERT base.postgres.prisma diff --git a/tests/bigint-number-type/expected/interfaces.ts b/tests/options-behavior/expected/bigintNumber.ts similarity index 95% rename from tests/bigint-number-type/expected/interfaces.ts rename to tests/options-behavior/expected/bigintNumber.ts index 29ef1e2..af5e3ce 100644 --- a/tests/bigint-number-type/expected/interfaces.ts +++ b/tests/options-behavior/expected/bigintNumber.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/buffer-object-type/expected/interfaces.ts b/tests/options-behavior/expected/bytesBufferObject.ts similarity index 95% rename from tests/buffer-object-type/expected/interfaces.ts rename to tests/options-behavior/expected/bytesBufferObject.ts index 53c1ff4..19cb80e 100644 --- a/tests/buffer-object-type/expected/interfaces.ts +++ b/tests/options-behavior/expected/bytesBufferObject.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/buffer-array-type/expected/interfaces.ts b/tests/options-behavior/expected/bytesNumberArray.ts similarity index 95% rename from tests/buffer-array-type/expected/interfaces.ts rename to tests/options-behavior/expected/bytesNumberArray.ts index 977085a..6360b18 100644 --- a/tests/buffer-array-type/expected/interfaces.ts +++ b/tests/options-behavior/expected/bytesNumberArray.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/no-options/expected/interfaces.ts b/tests/options-behavior/expected/customHeader.ts similarity index 98% rename from tests/no-options/expected/interfaces.ts rename to tests/options-behavior/expected/customHeader.ts index 4ce581e..2211dd1 100644 --- a/tests/no-options/expected/interfaces.ts +++ b/tests/options-behavior/expected/customHeader.ts @@ -1,3 +1,5 @@ +// This is a custom header + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/options-behavior/expected/customOutput.ts b/tests/options-behavior/expected/customOutput.ts new file mode 100644 index 0000000..9401122 --- /dev/null +++ b/tests/options-behavior/expected/customOutput.ts @@ -0,0 +1,67 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + +export type Gender = "Male" | "Female" | "Other"; + +export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; + +export interface Person { + id: number; + name: string; + age: number; + email: string | null; + gender: Gender; + addressId: number; + address?: Address; + friends?: Person[]; + friendsOf?: Person[]; + data?: Data | null; +} + +export interface Address { + id: number; + streetNumber: number; + streetName: string; + city: string; + isBilling: boolean; + people?: Person[]; +} + +export interface Data { + id: string; + stringField: string; + booleanField: boolean; + intField: number; + bigIntField: bigint; + floatField: number; + decimalField: Decimal; + dateField: Date; + jsonField: JsonValue; + bytesField: Buffer; + enumField: DataTest; + optionalStringField: string | null; + optionalBooleanField: boolean | null; + optionalIntField: number | null; + optionalBigIntField: bigint | null; + optionalFloatField: number | null; + optionalDecimalField: Decimal | null; + optionalDateField: Date | null; + optionalJsonField: JsonValue | null; + optionalBytesField: Buffer | null; + optionalEnumField: DataTest | null; + stringArrayField: string[]; + booleanArrayField: boolean[]; + intArrayField: number[]; + bigIntArrayField: bigint[]; + floatArrayField: number[]; + decimalArrayField: Decimal[]; + dateArrayField: Date[]; + jsonArrayField: JsonValue[]; + bytesArrayField: Buffer[]; + enumArrayField: DataTest[]; + personId: number; + person?: Person; +} + +type Decimal = { valueOf(): string }; + +type JsonValue = string | number | boolean | { [key in string]?: JsonValue } | Array | null; diff --git a/tests/enums/expected/interfaces.ts b/tests/options-behavior/expected/enumType.ts similarity index 95% rename from tests/enums/expected/interfaces.ts rename to tests/options-behavior/expected/enumType.ts index 1f86d2d..6f0e4eb 100644 --- a/tests/enums/expected/interfaces.ts +++ b/tests/options-behavior/expected/enumType.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export enum Gender { Male = "Male", Female = "Female", diff --git a/tests/enums-prefix-suffix/expected/interfaces.ts b/tests/options-behavior/expected/enumTypeSuffixPrefix.ts similarity index 85% rename from tests/enums-prefix-suffix/expected/interfaces.ts rename to tests/options-behavior/expected/enumTypeSuffixPrefix.ts index 7b031b1..9f1b5e1 100644 --- a/tests/enums-prefix-suffix/expected/interfaces.ts +++ b/tests/options-behavior/expected/enumTypeSuffixPrefix.ts @@ -1,10 +1,12 @@ -export enum EnumGenderUnion { +// This file was auto-generated by prisma-generator-typescript-interfaces + +export enum eGenderEnum { Male = "Male", Female = "Female", Other = "Other" } -export enum EnumDataTestUnion { +export enum eDataTestEnum { Apple = "Apple", Banana = "Banana", Orange = "Orange", @@ -16,7 +18,7 @@ export interface Person { name: string; age: number; email: string | null; - gender: EnumGenderUnion; + gender: eGenderEnum; addressId: number; address?: Address; friends?: Person[]; @@ -44,7 +46,7 @@ export interface Data { dateField: Date; jsonField: JsonValue; bytesField: Buffer; - enumField: EnumDataTestUnion; + enumField: eDataTestEnum; optionalStringField: string | null; optionalBooleanField: boolean | null; optionalIntField: number | null; @@ -54,7 +56,7 @@ export interface Data { optionalDateField: Date | null; optionalJsonField: JsonValue | null; optionalBytesField: Buffer | null; - optionalEnumField: EnumDataTestUnion | null; + optionalEnumField: eDataTestEnum | null; stringArrayField: string[]; booleanArrayField: boolean[]; intArrayField: number[]; @@ -64,7 +66,7 @@ export interface Data { dateArrayField: Date[]; jsonArrayField: JsonValue[]; bytesArrayField: Buffer[]; - enumArrayField: EnumDataTestUnion[]; + enumArrayField: eDataTestEnum[]; personId: number; person?: Person; } diff --git a/tests/prettier/expected/interfaces.ts b/tests/options-behavior/expected/formatPrettier.ts similarity index 95% rename from tests/prettier/expected/interfaces.ts rename to tests/options-behavior/expected/formatPrettier.ts index eee3254..fb3a0aa 100644 --- a/tests/prettier/expected/interfaces.ts +++ b/tests/options-behavior/expected/formatPrettier.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/options-behavior/expected/interfaces.ts b/tests/options-behavior/expected/interfaces.ts new file mode 100644 index 0000000..9401122 --- /dev/null +++ b/tests/options-behavior/expected/interfaces.ts @@ -0,0 +1,67 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + +export type Gender = "Male" | "Female" | "Other"; + +export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; + +export interface Person { + id: number; + name: string; + age: number; + email: string | null; + gender: Gender; + addressId: number; + address?: Address; + friends?: Person[]; + friendsOf?: Person[]; + data?: Data | null; +} + +export interface Address { + id: number; + streetNumber: number; + streetName: string; + city: string; + isBilling: boolean; + people?: Person[]; +} + +export interface Data { + id: string; + stringField: string; + booleanField: boolean; + intField: number; + bigIntField: bigint; + floatField: number; + decimalField: Decimal; + dateField: Date; + jsonField: JsonValue; + bytesField: Buffer; + enumField: DataTest; + optionalStringField: string | null; + optionalBooleanField: boolean | null; + optionalIntField: number | null; + optionalBigIntField: bigint | null; + optionalFloatField: number | null; + optionalDecimalField: Decimal | null; + optionalDateField: Date | null; + optionalJsonField: JsonValue | null; + optionalBytesField: Buffer | null; + optionalEnumField: DataTest | null; + stringArrayField: string[]; + booleanArrayField: boolean[]; + intArrayField: number[]; + bigIntArrayField: bigint[]; + floatArrayField: number[]; + decimalArrayField: Decimal[]; + dateArrayField: Date[]; + jsonArrayField: JsonValue[]; + bytesArrayField: Buffer[]; + enumArrayField: DataTest[]; + personId: number; + person?: Person; +} + +type Decimal = { valueOf(): string }; + +type JsonValue = string | number | boolean | { [key in string]?: JsonValue } | Array | null; diff --git a/tests/options-behavior/expected/multilineHeader.ts b/tests/options-behavior/expected/multilineHeader.ts new file mode 100644 index 0000000..880900f --- /dev/null +++ b/tests/options-behavior/expected/multilineHeader.ts @@ -0,0 +1,70 @@ +// This is a multiline header +// with a second line +// and a third line indented +// and emoji 🎉 + +export type Gender = "Male" | "Female" | "Other"; + +export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; + +export interface Person { + id: number; + name: string; + age: number; + email: string | null; + gender: Gender; + addressId: number; + address?: Address; + friends?: Person[]; + friendsOf?: Person[]; + data?: Data | null; +} + +export interface Address { + id: number; + streetNumber: number; + streetName: string; + city: string; + isBilling: boolean; + people?: Person[]; +} + +export interface Data { + id: string; + stringField: string; + booleanField: boolean; + intField: number; + bigIntField: bigint; + floatField: number; + decimalField: Decimal; + dateField: Date; + jsonField: JsonValue; + bytesField: Buffer; + enumField: DataTest; + optionalStringField: string | null; + optionalBooleanField: boolean | null; + optionalIntField: number | null; + optionalBigIntField: bigint | null; + optionalFloatField: number | null; + optionalDecimalField: Decimal | null; + optionalDateField: Date | null; + optionalJsonField: JsonValue | null; + optionalBytesField: Buffer | null; + optionalEnumField: DataTest | null; + stringArrayField: string[]; + booleanArrayField: boolean[]; + intArrayField: number[]; + bigIntArrayField: bigint[]; + floatArrayField: number[]; + decimalArrayField: Decimal[]; + dateArrayField: Date[]; + jsonArrayField: JsonValue[]; + bytesArrayField: Buffer[]; + enumArrayField: DataTest[]; + personId: number; + person?: Person; +} + +type Decimal = { valueOf(): string }; + +type JsonValue = string | number | boolean | { [key in string]?: JsonValue } | Array | null; diff --git a/tests/custom-output/expected/generated.ts b/tests/options-behavior/expected/noHeader.ts similarity index 100% rename from tests/custom-output/expected/generated.ts rename to tests/options-behavior/expected/noHeader.ts diff --git a/tests/options-behavior/expected/prefixSuffix.ts b/tests/options-behavior/expected/prefixSuffix.ts new file mode 100644 index 0000000..c9a1ad6 --- /dev/null +++ b/tests/options-behavior/expected/prefixSuffix.ts @@ -0,0 +1,67 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + +export type enumGenderEnum = "Male" | "Female" | "Other"; + +export type enumDataTestEnum = "Apple" | "Banana" | "Orange" | "Pear"; + +export interface modelPersonModel { + id: number; + name: string; + age: number; + email: string | null; + gender: enumGenderEnum; + addressId: number; + address?: modelAddressModel; + friends?: modelPersonModel[]; + friendsOf?: modelPersonModel[]; + data?: modelDataModel | null; +} + +export interface modelAddressModel { + id: number; + streetNumber: number; + streetName: string; + city: string; + isBilling: boolean; + people?: modelPersonModel[]; +} + +export interface modelDataModel { + id: string; + stringField: string; + booleanField: boolean; + intField: number; + bigIntField: bigint; + floatField: number; + decimalField: Decimal; + dateField: Date; + jsonField: JsonValue; + bytesField: Buffer; + enumField: enumDataTestEnum; + optionalStringField: string | null; + optionalBooleanField: boolean | null; + optionalIntField: number | null; + optionalBigIntField: bigint | null; + optionalFloatField: number | null; + optionalDecimalField: Decimal | null; + optionalDateField: Date | null; + optionalJsonField: JsonValue | null; + optionalBytesField: Buffer | null; + optionalEnumField: enumDataTestEnum | null; + stringArrayField: string[]; + booleanArrayField: boolean[]; + intArrayField: number[]; + bigIntArrayField: bigint[]; + floatArrayField: number[]; + decimalArrayField: Decimal[]; + dateArrayField: Date[]; + jsonArrayField: JsonValue[]; + bytesArrayField: Buffer[]; + enumArrayField: enumDataTestEnum[]; + personId: number; + person?: modelPersonModel; +} + +type Decimal = { valueOf(): string }; + +type JsonValue = string | number | boolean | { [key in string]?: JsonValue } | Array | null; diff --git a/tests/required-relations/expected/interfaces.ts b/tests/options-behavior/expected/requiredRelations.ts similarity index 95% rename from tests/required-relations/expected/interfaces.ts rename to tests/options-behavior/expected/requiredRelations.ts index 8b800af..da46a8a 100644 --- a/tests/required-relations/expected/interfaces.ts +++ b/tests/options-behavior/expected/requiredRelations.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/string-types/expected/interfaces.ts b/tests/options-behavior/expected/stringTypes.ts similarity index 95% rename from tests/string-types/expected/interfaces.ts rename to tests/options-behavior/expected/stringTypes.ts index 55b42b8..bbf0f7f 100644 --- a/tests/string-types/expected/interfaces.ts +++ b/tests/options-behavior/expected/stringTypes.ts @@ -1,3 +1,5 @@ +// This file was auto-generated by prisma-generator-typescript-interfaces + export type Gender = "Male" | "Female" | "Other"; export type DataTest = "Apple" | "Banana" | "Orange" | "Pear"; diff --git a/tests/options-behavior/schema.prisma b/tests/options-behavior/schema.prisma new file mode 100644 index 0000000..ff48ee4 --- /dev/null +++ b/tests/options-behavior/schema.prisma @@ -0,0 +1,177 @@ +// These tests cover most of the generator's options + +// ======================== +// Generator Tests +// ======================== + +generator noOptions { + provider = "node --loader ts-node/esm generator.ts" +} + +generator customOutput { + provider = "node --loader ts-node/esm generator.ts" + output = "customOutput.ts" +} + +generator customHeader { + provider = "node --loader ts-node/esm generator.ts" + output = "customHeader.ts" + headerComment = "This is a custom header" +} + +generator noHeader { + provider = "node --loader ts-node/esm generator.ts" + output = "noHeader.ts" + headerComment = "" +} + +generator multilineHeader { + provider = "node --loader ts-node/esm generator.ts" + output = "multilineHeader.ts" + headerComment = "This is a multiline header\nwith a second line\n and a third line indented\nand emoji 🎉" +} + +generator formatPrettier { + provider = "node --loader ts-node/esm generator.ts" + output = "formatPrettier.ts" + prettier = true +} + +generator requiredRelations { + provider = "node --loader ts-node/esm generator.ts" + output = "requiredRelations.ts" + optionalRelations = false +} + +generator prefixSuffix { + provider = "node --loader ts-node/esm generator.ts" + output = "prefixSuffix.ts" + enumPrefix = "enum" + enumSuffix = "Enum" + modelPrefix = "model" + modelSuffix = "Model" +} + +generator stringTypes { + provider = "node --loader ts-node/esm generator.ts" + output = "stringTypes.ts" + dateType = "string" + bigIntType = "string" + decimalType = "string" + bytesType = "string" +} + +generator bytesNumberArray { + provider = "node --loader ts-node/esm generator.ts" + output = "bytesNumberArray.ts" + bytesType = "number[]" +} + +generator bytesBufferObject { + provider = "node --loader ts-node/esm generator.ts" + output = "bytesBufferObject.ts" + bytesType = "BufferObject" +} + +generator bigintNumber { + provider = "node --loader ts-node/esm generator.ts" + output = "bigintNumber.ts" + bigIntType = "number" +} + +generator enumType { + provider = "node --loader ts-node/esm generator.ts" + output = "enumType.ts" + enumType = "enum" +} + +generator enumTypeSuffixPrefix { + provider = "node --loader ts-node/esm generator.ts" + output = "enumTypeSuffixPrefix.ts" + enumType = "enum" + enumPrefix = "e" + enumSuffix = "Enum" +} + +// ======================== +// Prisma Schema +// ======================== + +datasource db { + provider = "postgresql" + url = "" +} + +enum Gender { + Male + Female + Other +} + +enum DataTest { + Apple + Banana + Orange + Pear +} + +model Person { + id Int @id @default(autoincrement()) + name String + age Int + email String? + gender Gender + addressId Int + address Address @relation(fields: [addressId], references: [id]) + friends Person[] @relation("Friends") + friendsOf Person[] @relation("Friends") + data Data? +} + +model Address { + id Int @id + streetNumber Int + streetName String + city String + isBilling Boolean + people Person[] +} + +model Data { + id String @id @default(uuid()) @db.Uuid + stringField String + booleanField Boolean + intField Int + bigIntField BigInt + floatField Float + decimalField Decimal + dateField DateTime + jsonField Json + bytesField Bytes + enumField DataTest + + optionalStringField String? + optionalBooleanField Boolean? + optionalIntField Int? + optionalBigIntField BigInt? + optionalFloatField Float? + optionalDecimalField Decimal? + optionalDateField DateTime? + optionalJsonField Json? + optionalBytesField Bytes? + optionalEnumField DataTest? + + stringArrayField String[] + booleanArrayField Boolean[] + intArrayField Int[] + bigIntArrayField BigInt[] + floatArrayField Float[] + decimalArrayField Decimal[] + dateArrayField DateTime[] + jsonArrayField Json[] + bytesArrayField Bytes[] + enumArrayField DataTest[] + + personId Int @unique + person Person @relation(fields: [personId], references: [id]) +} diff --git a/tests/prefix-suffix/expected/interfaces.ts b/tests/prefix-suffix/expected/interfaces.ts deleted file mode 100644 index 24cc369..0000000 --- a/tests/prefix-suffix/expected/interfaces.ts +++ /dev/null @@ -1,31 +0,0 @@ -export type enumGenderEnum = "Male" | "Female" | "Other"; - -export type enumPhotoTypeEnum = "Selfie" | "Profile" | "Tagged"; - -export interface modelPersonModel { - id: number; - name: string; - gender: enumGenderEnum; - addressId: number; - address?: modelAddressModel; - photos: typePhotoType[]; - tags: typeTagType | null; -} - -export interface modelAddressModel { - id: number; - addresText: string; - people?: modelPersonModel[]; -} - -export interface typePhotoType { - height: number; - Width: number; - url: string; - type: enumPhotoTypeEnum; -} - -export interface typeTagType { - id: number; - name: string; -} diff --git a/tests/prefix-suffix/schema.prisma b/tests/prefix-suffix/schema.prisma deleted file mode 100644 index b251a3e..0000000 --- a/tests/prefix-suffix/schema.prisma +++ /dev/null @@ -1,11 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - enumPrefix = "enum" - enumSuffix = "Enum" - modelPrefix = "model" - modelSuffix = "Model" - typePrefix = "type" - typeSuffix = "Type" -} - -// #INSERT base.mongo.prisma diff --git a/tests/prettier/schema.prisma b/tests/prettier/schema.prisma deleted file mode 100644 index f990b4d..0000000 --- a/tests/prettier/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - prettier = true -} - -// #INSERT base.postgres.prisma diff --git a/tests/required-relations/schema.prisma b/tests/required-relations/schema.prisma deleted file mode 100644 index e06ebd4..0000000 --- a/tests/required-relations/schema.prisma +++ /dev/null @@ -1,6 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - optionalRelations = false -} - -// #INSERT base.postgres.prisma diff --git a/tests/string-types/schema.prisma b/tests/string-types/schema.prisma deleted file mode 100644 index 73dd734..0000000 --- a/tests/string-types/schema.prisma +++ /dev/null @@ -1,9 +0,0 @@ -generator typescriptInterfaces { - provider = "node generator.js" - dateType = "string" - bigIntType = "string" - decimalType = "string" - bytesType = "string" -} - -// #INSERT base.postgres.prisma diff --git a/tests/validation-errors/schema.prisma b/tests/validation-errors/schema.prisma index d3cd542..611433c 100644 --- a/tests/validation-errors/schema.prisma +++ b/tests/validation-errors/schema.prisma @@ -1,5 +1,9 @@ +// ======================== +// Generator Tests +// ======================== + generator typescriptInterfaces { - provider = "node generator.js" + provider = "node --loader ts-node/esm generator.ts" enumType = "incorrect" dateType = "wrong" bigIntType = "values" @@ -8,4 +12,15 @@ generator typescriptInterfaces { prettier = "bad" } -// #INSERT base.postgres.prisma +// ======================== +// Prisma Schema +// ======================== + +datasource db { + provider = "postgresql" + url = "" +} + +model User { + id Int @id +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..e36f08e --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["generator.ts"] +} diff --git a/tsconfig.json b/tsconfig.json index 449d1f8..4bda6c0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,5 +5,8 @@ "module": "NodeNext", "moduleResolution": "NodeNext" }, - "include": ["generator.ts", "test.ts"] + "include": ["**/*.ts"], + "ts-node": { + "transpileOnly": true + } }