Skip to content

Commit

Permalink
Merge pull request #47 from farreldarian/produce-single-output
Browse files Browse the repository at this point in the history
Produce single output
  • Loading branch information
fdarian authored Apr 9, 2024
2 parents fe02569 + fac3152 commit 2f15dac
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 36 deletions.
96 changes: 72 additions & 24 deletions packages/generator/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,26 @@ import {
type ModelModule,
generateModelModules,
} from './lib/adapter/modules/model'
import {
type RelationalModule,
generateRelationalModules,
} from './lib/adapter/modules/relational'
import { generateImplicitModules } from './lib/adapter/modules/relational'
import type { RelationalModuleSet } from './lib/adapter/modules/relational'
import { generateSchemaModules as generateSchemaModule } from './lib/adapter/modules/relational'
import type { BaseGeneratedModules } from './lib/adapter/modules/sets/base-generated-modules'
import { logger } from './lib/logger'
import {
type ImportValue,
type NamedImport,
namedImport,
} from './lib/syntaxes/imports'
import type { Module } from './lib/syntaxes/module'
import { type Module, createModule } from './lib/syntaxes/module'
import {
isRelationalQueryEnabled,
initializeGenerator,
getGenerator,
initializeGenerator,
isRelationalQueryEnabled,
} from './shared/generator-context'
import {
generateRelationalModules,
type RelationalModule,
} from './lib/adapter/modules/relational'
import type { BaseGeneratedModules } from './lib/adapter/modules/sets/base-generated-modules'
import { generateImplicitModules } from './lib/adapter/modules/relational'
import type { RelationalModuleSet } from './lib/adapter/modules/relational'
import { generateSchemaModules as generateSchemaModule } from './lib/adapter/modules/relational'

const { version } = require('../package.json')

Expand All @@ -60,18 +60,21 @@ generatorHandler({

if (isRelationalQueryEnabled()) {
const relational = generateRelationalModules(modules.models)
const implicit = generateImplicitModules(adapter, relational)
const schema = generateSchemaModule({
...modules,
relational: relational,
implicitModels: implicit.models,
implicitRelational: implicit.relational,
})

modules.schema = schema
modules.relational = relational

const implicit = generateImplicitModules(adapter, relational)
modules.implicitModels = implicit.models
modules.implicitRelational = implicit.relational

if (!getGenerator().output.isSingleFile) {
const schema = generateSchemaModule({
...modules,
relational: relational,
implicitModels: implicit.models,
implicitRelational: implicit.relational,
})
modules.schema = schema
}
}

writeModules(modules)
Expand Down Expand Up @@ -122,17 +125,62 @@ export function reduceImports(imports: ImportValue[]) {
}

function writeModules(modules: GeneratedModules) {
const basePath = getGenerator().outputBasePath
const outputPath = getGenerator().output.path

fs.existsSync(basePath) && fs.rmSync(basePath, { recursive: true })
fs.mkdirSync(basePath, { recursive: true })
if (getGenerator().output.isSingleFile) {
if (hasSubFolder(outputPath)) {
fs.mkdirSync(getParentPath(outputPath), { recursive: true })
}

fs.writeFileSync(outputPath, createDrizzleModule(modules).code)
return
}

fs.existsSync(outputPath) && fs.rmSync(outputPath, { recursive: true })
fs.mkdirSync(outputPath, { recursive: true })

for (const module of flattenModules(modules)) {
const writeLocation = path.join(basePath, `${module.name}.ts`)
const writeLocation = path.join(outputPath, `${module.name}.ts`)
fs.writeFileSync(writeLocation, module.code)
}
}

function getParentPath(output: string) {
return output.split('/').slice(0, -1).join('/')
}

function hasSubFolder(output: string) {
return output.split('/').length > 1
}

/**
* A single file output module where it contains
* all schema definitions and its relations
*/
function createDrizzleModule(modules: GeneratedModules) {
return createModule({
name: getSingleOutputFileName(),
declarations: flattenModules(modules).flatMap((module) =>
module.declarations.map((declaration) => {
return {
...declaration,
imports: declaration.imports.filter(
(i) => !i.module.startsWith('./')
),
}
})
),
})
}

function getSingleOutputFileName() {
const lastSegment = getGenerator().output.path.split('/').at(-1)
if (lastSegment == null) {
throw new Error('Last segment is undefined')
}
return lastSegment.replace('.ts', '')
}

/**
* @dev Importing the adapter dynamically so `getGeneratorContext()` won't
* be called before initialization (`onGenerate`)
Expand Down
5 changes: 2 additions & 3 deletions packages/generator/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ import {
type Output,
type SchemaIssues,
flatten,
literal,
object,
optional,
safeParse,
literal,
} from 'valibot'
import { ModuleResolution } from '~/shared/generator-context/module-resolution'
import { withDefault } from './valibot-schema'
import { BooleanInStr } from './valibot-schema'
import { BooleanInStr, withDefault } from './valibot-schema'

const Config = object({
relationalQuery: withDefault(optional(BooleanInStr), true),
Expand Down
26 changes: 22 additions & 4 deletions packages/generator/src/shared/generator-context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import type { GeneratorOptions } from '@prisma/generator-helper'
import { type Config, parseConfig } from '~/lib/config'
import { resolveModuleResolution } from './module-resolution'

type Output = {
isSingleFile: boolean
path: string
}

type Generator = {
outputBasePath: string
moduleResolution?: string
output: Output
//
dmmf: GeneratorOptions['dmmf']
config: Config
Expand All @@ -16,10 +21,11 @@ let generator_: Generator | undefined

export function initializeGenerator(options: GeneratorOptions) {
const config = parseConfig(options.generator.config)
const output = getOutputConfig(options)

const context: Generator = {
moduleResolution: config.moduleResolution ?? resolveModuleResolution(),
outputBasePath: getBasePath(options),
output,
//
dmmf: options.dmmf,
config,
Expand All @@ -29,10 +35,22 @@ export function initializeGenerator(options: GeneratorOptions) {
return context
}

function getBasePath(options: GeneratorOptions) {
function getOutputConfig(options: GeneratorOptions): Output {
const basePath = options.generator.output?.value
if (!basePath) throw new Error('No output path specified')
return basePath

const isSingleFile = basePath.endsWith('.ts')
if (isSingleFile) {
return {
isSingleFile: true,
path: basePath,
}
}

return {
isSingleFile: false,
path: basePath,
}
}

// #endregion
Expand Down
3 changes: 3 additions & 0 deletions packages/usage/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ prisma/mysql/schema.prisma
prisma/sqlite/schema.prisma
prisma/sqlite/test.db
prisma/sqlite/test.db-journal

# Generated on-the-fly
.temp
6 changes: 3 additions & 3 deletions packages/usage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"start": "bun run src/index.ts",
"test": "bun test --preload tests/setup.ts",
"generate": "prisma generate",
"pushreset:postgres": "DATABASE_URL=$PG_DATABASE_URL bun prisma db push --schema prisma/schema.prisma --force-reset --accept-data-loss",
"pushreset:sqlite": "DATABASE_URL=$MYSQL_DATABASE_URL bun prisma db push --schema prisma/mysql/schema.prisma --force-reset --accept-data-loss",
"pushreset:mysql": "bun prisma db push --schema prisma/sqlite/schema.prisma --force-reset --accept-data-loss",
"pushreset:postgres": "bun prisma db push --schema prisma/schema.prisma --force-reset --accept-data-loss",
"pushreset:mysql": "bun prisma db push --schema prisma/mysql/schema.prisma --force-reset --accept-data-loss",
"pushreset:sqlite": "bun prisma db push --schema prisma/sqlite/schema.prisma --force-reset --accept-data-loss",
"clone:sqlite": "bun run scripts/cloneSqlite.ts",
"clone:mysql": "bun run scripts/cloneMysql.ts"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/usage/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ generator drizzle {

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
url = env("PG_DATABASE_URL")
}

model SelfReference {
Expand Down
1 change: 1 addition & 0 deletions packages/usage/scripts/cloneMysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const TARGET_PATH = './prisma/mysql/schema.prisma'

const schema = (await Bun.file(BASE_PATH).text())
.replace('postgresql', 'mysql')
.replace('PG_DATABASE_URL', 'MYSQL_DATABASE_URL')
.replace(/(\/\/ start -mysql\n)[\s\S]*?(\n\/\/ end -mysql)/g, '')
.replace(/^.*-mysql.*/gm, '')

Expand Down
2 changes: 1 addition & 1 deletion packages/usage/scripts/cloneSqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const TARGET_PATH = './prisma/sqlite/schema.prisma'

const schema = (await Bun.file(BASE_PATH).text())
.replace('postgresql', 'sqlite')
.replace('env("DATABASE_URL")', '"file:./test.db"')
.replace('env("PG_DATABASE_URL")', '"file:./test.db"')
.replace(/(\/\/ start -sqlite\n)[\s\S]*?(\n\/\/ end -sqlite)/g, '')
.replace(/^.*-sqlite.*/gm, '')

Expand Down
59 changes: 59 additions & 0 deletions packages/usage/tests/single-file-output.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import fs from 'node:fs'
import { createId } from '@paralleldrive/cuid2'
import { $ } from 'bun'

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

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

expect(fs.existsSync(`.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()

expect(fs.existsSync(`.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()

expect(fs.existsSync(`.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()

expect(fs.existsSync(`.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()

expect(fs.existsSync(`.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(
'generator drizzle {\n provider = "bunx prisma-generator-drizzle"\n}',
`generator drizzle {\n provider = "bunx prisma-generator-drizzle"\n output = "${output}"\n}`
)
fs.writeFileSync(`.temp/${name}/schema.prisma`, schema)
}

0 comments on commit 2f15dac

Please sign in to comment.