Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce drizzle.dateMode directive #50

Merged
merged 5 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/generator/src/lib/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ImportValue } from '../syntaxes/imports'
import type { Module } from '../syntaxes/module'
import type { FieldFunc } from './fields/createField'

type ParsableField = PrismaScalarField | PrismaEnumField
export type ParsableField = PrismaScalarField | PrismaEnumField

type DeclarationFunc = { imports: ImportValue[]; func: string }

Expand Down
20 changes: 1 addition & 19 deletions packages/generator/src/lib/adapter/fields/createField.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { DMMF } from '@prisma/generator-helper'
import { getDirective } from '~/lib/directive'
import { type ImportValue, namedImport } from '~/lib/syntaxes/imports'
import type { MakeRequired, ModifyType, Prettify } from '~/lib/types/utils'

Expand Down Expand Up @@ -58,25 +59,6 @@ export function createField(input: CreateFieldInput) {
}
}

/**
* e.g.
* Unknown
* - Input: just a doc
* - Returns: undefined
* When Exists
* - Input: drizzle.type viem::Address
* - Returns: viem:Address
*/
function getDirective(field: DMMF.Field, directive: string) {
if (field.documentation == null) return

return field.documentation
.split('\n')
.find((doc) => doc.startsWith(directive))
?.replaceAll(directive, '')
.trim()
}

function getCustomType(field: DMMF.Field) {
const directive = getDirective(field, 'drizzle.type')
if (directive == null) return
Expand Down
4 changes: 2 additions & 2 deletions packages/generator/src/lib/adapter/providers/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { camelCase, kebabCase } from 'lodash'
import { getDbName } from '~/lib/prisma-helpers/getDbName'
import { namedImport } from '~/lib/syntaxes/imports'
import { createModule } from '~/lib/syntaxes/module'
import { getGenerator } from '~/shared/generator-context'
import { getDateMode } from '~/shared/date-mode'
import { createAdapter } from '../adapter'
import { createField, hasDefault, isDefaultFunc } from '../fields/createField'

Expand Down Expand Up @@ -97,7 +97,7 @@ export const mysqlAdapter = createAdapter({
return createField({
field,
imports: [namedImport(['datetime'], coreModule)],
func: `datetime('${getDbName(field)}', { mode: '${getGenerator().dateMode}', fsp: 3 })`, // biome-ignore format: keep one line
func: `datetime('${getDbName(field)}', { mode: '${getDateMode(field)}', fsp: 3 })`, // biome-ignore format: keep one line
// https://github.com/drizzle-team/drizzle-orm/issues/921
onDefault: (field) => {
if (
Expand Down
4 changes: 2 additions & 2 deletions packages/generator/src/lib/adapter/providers/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { camelCase, kebabCase } from 'lodash'
import { getDbName } from '~/lib/prisma-helpers/getDbName'
import { namedImport } from '~/lib/syntaxes/imports'
import { createModule } from '~/lib/syntaxes/module'
import { getGenerator } from '~/shared/generator-context'
import { getDateMode } from '~/shared/date-mode'
import { createAdapter } from '../adapter'
import {
type CreateFieldInput,
Expand Down Expand Up @@ -105,7 +105,7 @@ export const postgresAdapter = createAdapter({
return createField({
field,
imports: [namedImport(['timestamp'], coreModule)],
func: `timestamp('${getDbName(field)}', { mode: '${getGenerator().dateMode}', precision: 3 })`, // biome-ignore format: keep one line
func: `timestamp('${getDbName(field)}', { mode: '${getDateMode(field)}', precision: 3 })`, // biome-ignore format: keep one line
})
},
// https://orm.drizzle.team/docs/column-types/pg/#decimal
Expand Down
4 changes: 2 additions & 2 deletions packages/generator/src/lib/adapter/providers/sqlite.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getDbName } from '~/lib/prisma-helpers/getDbName'
import { namedImport } from '~/lib/syntaxes/imports'
import { createModule } from '~/lib/syntaxes/module'
import { getGenerator } from '~/shared/generator-context'
import { getDateMode } from '~/shared/date-mode'
import { createAdapter } from '../adapter'
import { createField, hasDefault, isDefaultFunc } from '../fields/createField'

Expand Down Expand Up @@ -100,7 +100,7 @@ export const sqliteAdapter = createAdapter({
// Prisma: https://arc.net/l/quote/grwnsumx
// Drizzle: https://arc.net/l/quote/fpupjigo
DateTime(field) {
if (getGenerator().dateMode !== 'date') {
if (getDateMode(field) !== 'date') {
throw new Error('Only dateMode `date` is supported for sqlite')
}

Expand Down
7 changes: 2 additions & 5 deletions packages/generator/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
object,
optional,
safeParse,
union,
} from 'valibot'
import { DateMode } from '~/shared/date-mode'
import { ModuleResolution } from '~/shared/generator-context/module-resolution'
import { BooleanInStr, withDefault } from './valibot-schema'

Expand All @@ -17,10 +17,7 @@ const Config = object({
moduleResolution: optional(ModuleResolution),
verbose: optional(BooleanInStr),
formatter: optional(literal('prettier')),
dateMode: withDefault(
optional(union([literal('string'), literal('date')])),
'date'
),
dateMode: optional(DateMode),
})
export type Config = Output<typeof Config>

Expand Down
20 changes: 20 additions & 0 deletions packages/generator/src/lib/directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { DMMF } from '@prisma/generator-helper'

/**
* e.g.
* Unknown
* - Input: just a doc
* - Returns: undefined
* When Exists
* - Input: drizzle.type viem::Address
* - Returns: viem:Address
*/
export function getDirective(field: DMMF.Field, directive: string) {
if (field.documentation == null) return

return field.documentation
.split('\n')
.find((doc) => doc.startsWith(directive))
?.replaceAll(directive, '')
.trim()
}
13 changes: 13 additions & 0 deletions packages/generator/src/shared/date-mode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { literal, parse, union } from 'valibot'
import type { ParsableField } from '~/lib/adapter/adapter'
import { getDirective } from '~/lib/directive'
import { getGenerator } from './generator-context'

export const DateMode = union([literal('string'), literal('date')])

export function getDateMode(field: ParsableField) {
const directive = getDirective(field, 'drizzle.dateMode')
if (directive) return parse(DateMode, directive)

return getGenerator().config.dateMode ?? 'date'
}
2 changes: 0 additions & 2 deletions packages/generator/src/shared/generator-context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ type Output = {
type Generator = {
moduleResolution?: string
output: Output
dateMode: Config['dateMode']
//
dmmf: GeneratorOptions['dmmf']
config: Config
Expand All @@ -27,7 +26,6 @@ export function initializeGenerator(options: GeneratorOptions) {
const context: Generator = {
moduleResolution: config.moduleResolution ?? resolveModuleResolution(),
output,
dateMode: config.dateMode,
//
dmmf: options.dmmf,
config,
Expand Down
74 changes: 74 additions & 0 deletions packages/usage/tests/configure-date-mode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { $ } from 'bun'
import { type TempDirectory, createTempHandler } from './utils/temp'

const tempHandler = createTempHandler()

afterAll(async () => {
await tempHandler.cleanup()
})

test('global config', async () => {
const temp = await tempHandler.prepare()

await Bun.write(
getSchemaPath(temp),
`datasource db {
provider = "postgresql"
url = env("PG_DATABASE_URL")
}

generator drizzle {
provider = "prisma-generator-drizzle"
dateMode = "string"
output = "drizzle.ts"
}

model User {
id Int @id
date DateTime
}`
)
await $`bun prisma generate --schema ${getSchemaPath(temp)}`.quiet()

const output = await Bun.file(`${temp.basePath}/drizzle.ts`).text()
expect(output).toContain(
"date: timestamp('date', { mode: 'string', precision: 3 })"
)
})

test.only('field-level config', async () => {
const temp = await tempHandler.prepare()

await Bun.write(
getSchemaPath(temp),
`datasource db {
provider = "postgresql"
url = env("PG_DATABASE_URL")
}

generator drizzle {
provider = "prisma-generator-drizzle"
output = "drizzle.ts"
}

model User {
id Int @id
normalDate DateTime
/// drizzle.dateMode string
stringDate DateTime
}`
)
await $`bun prisma generate --schema ${getSchemaPath(temp)}`.quiet()

const output = await Bun.file(`${temp.basePath}/drizzle.ts`).text()
expect(output).toContain(
"normalDate: timestamp('normalDate', { mode: 'date', precision: 3 })"
)
expect(output).toContain(
"stringDate: timestamp('stringDate', { mode: 'string', precision: 3 })"
)
})

function getSchemaPath(temp: TempDirectory) {
return `${temp.basePath}/schema.prisma`
}
48 changes: 24 additions & 24 deletions packages/usage/tests/single-file-output.test.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,54 @@
import fs from 'node:fs'
import { createId } from '@paralleldrive/cuid2'
import { $ } from 'bun'
import { createTempHandler } from './utils/temp'

const tempHandler = createTempHandler()

afterAll(() => {
fs.rmSync('.temp', { recursive: true, force: true })
tempHandler.cleanup()
})

test('generates drizzle.ts', async () => {
const name = createId()
writeSchemaWithOutput(name, 'drizzle.ts')
await $`bun prisma generate --schema .temp/${name}/schema.prisma`.quiet()
const temp = await tempHandler.prepare()
writeSchemaWithOutput(temp.name, 'drizzle.ts')
await $`bun prisma generate --schema .temp/${temp.name}/schema.prisma`.quiet()

expect(fs.existsSync(`.temp/${name}/drizzle.ts`)).toBe(true)
expect(fs.existsSync(`.temp/${temp.name}/drizzle.ts`)).toBe(true)
})

test('generates ./drizzle.ts', async () => {
const name = createId()
writeSchemaWithOutput(name, './drizzle.ts')
await $`bun prisma generate --schema .temp/${name}/schema.prisma`.quiet()
const temp = await tempHandler.prepare()
writeSchemaWithOutput(temp.name, './drizzle.ts')
await $`bun prisma generate --schema .temp/${temp.name}/schema.prisma`.quiet()

expect(fs.existsSync(`.temp/${name}/drizzle.ts`)).toBe(true)
expect(fs.existsSync(`.temp/${temp.name}/drizzle.ts`)).toBe(true)
})

test('generates sub/drizzle.ts', async () => {
const name = createId()
writeSchemaWithOutput(name, 'sub/drizzle.ts')
await $`bun prisma generate --schema .temp/${name}/schema.prisma`.quiet()
const temp = await tempHandler.prepare()
writeSchemaWithOutput(temp.name, 'sub/drizzle.ts')
await $`bun prisma generate --schema .temp/${temp.name}/schema.prisma`.quiet()

expect(fs.existsSync(`.temp/${name}/sub/drizzle.ts`)).toBe(true)
expect(fs.existsSync(`.temp/${temp.name}/sub/drizzle.ts`)).toBe(true)
})

test('generates ./sub/drizzle.ts', async () => {
const name = createId()
writeSchemaWithOutput(name, './sub/drizzle.ts')
await $`bun prisma generate --schema .temp/${name}/schema.prisma`.quiet()
const temp = await tempHandler.prepare()
writeSchemaWithOutput(temp.name, './sub/drizzle.ts')
await $`bun prisma generate --schema .temp/${temp.name}/schema.prisma`.quiet()

expect(fs.existsSync(`.temp/${name}/sub/drizzle.ts`)).toBe(true)
expect(fs.existsSync(`.temp/${temp.name}/sub/drizzle.ts`)).toBe(true)
})

test('generates ./sub/multi/drizzle.ts', async () => {
const name = createId()
writeSchemaWithOutput(name, './sub/multi/drizzle.ts')
await $`bun prisma generate --schema .temp/${name}/schema.prisma`.quiet()
const temp = await tempHandler.prepare()
writeSchemaWithOutput(temp.name, './sub/multi/drizzle.ts')
await $`bun prisma generate --schema .temp/${temp.name}/schema.prisma`.quiet()

expect(fs.existsSync(`.temp/${name}/sub/multi/drizzle.ts`)).toBe(true)
expect(fs.existsSync(`.temp/${temp.name}/sub/multi/drizzle.ts`)).toBe(true)
})

function writeSchemaWithOutput(name: string, output: string) {
fs.mkdirSync(`.temp/${name}`, { recursive: true })

const schema = fs
.readFileSync('./prisma/schema.prisma', { encoding: 'utf-8' })
.replace(
Expand Down
26 changes: 26 additions & 0 deletions packages/usage/tests/utils/temp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import fs from 'node:fs/promises'
import { createId } from '@paralleldrive/cuid2'

export type TempDirectory = { name: string; basePath: string }

export function createTempHandler() {
const temps: string[] = []
return {
async prepare(): Promise<TempDirectory> {
const name = createId()
await fs.mkdir(`.temp/${name}`, { recursive: true })

return {
name,
get basePath() {
return `.temp/${name}`
},
}
},
async cleanup() {
await Promise.all(
temps.map((name) => fs.rmdir(`.temp/${name}`, { recursive: true }))
)
},
}
}