Skip to content

Commit

Permalink
feat(cli): add --generator option to generate cmd (#16452)
Browse files Browse the repository at this point in the history
Co-authored-by: Joël Galeran <Jolg42@users.noreply.github.com>
Co-authored-by: Jan Piotrowski <piotrowski+github@gmail.com>
Co-authored-by: Alexey Orlenko <alex@aqrln.net>
  • Loading branch information
4 people committed Feb 3, 2023
1 parent f4a7eba commit 1ea1511
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 1 deletion.
3 changes: 3 additions & 0 deletions packages/cli/src/Generate.ts
Expand Up @@ -103,6 +103,7 @@ ${chalk.bold('Examples')}
'--watch': Boolean,
'--schema': String,
'--data-proxy': Boolean,
'--generator': [String],
// Only used for checkpoint information
'--postinstall': String,
'--telemetry-information': String,
Expand Down Expand Up @@ -139,6 +140,7 @@ ${chalk.bold('Examples')}
version: enginesVersion,
cliVersion: pkg.version,
dataProxy: !!args['--data-proxy'] || !!process.env.PRISMA_GENERATE_DATAPROXY,
generatorNames: args['--generator'],
})

if (!generators || generators.length === 0) {
Expand Down Expand Up @@ -289,6 +291,7 @@ Please run \`${getCommandWithExecutor('prisma generate')}\` to see the errors.`)
version: enginesVersion,
cliVersion: pkg.version,
dataProxy: !!args['--data-proxy'] || !!process.env.PRISMA_GENERATE_DATAPROXY,
generatorNames: args['--generator'],
})

if (!generatorsWatch || generatorsWatch.length === 0) {
Expand Down
37 changes: 37 additions & 0 deletions packages/cli/src/__tests__/commands/Generate.test.ts
Expand Up @@ -152,6 +152,43 @@ describe('--schema from parent directory', () => {
const result = Generate.new().parse([`--schema=${absoluteSchemaPath}`])
await expect(result).rejects.toThrow(`Provided --schema at ${absoluteSchemaPath} doesn't exist.`)
})

it('--generator: should work - valid generator names', async () => {
ctx.fixture('example-project')
const result = await Generate.new().parse([
'--schema=./prisma/multiple-generator.prisma',
'--generator=client',
'--generator=client_3',
])
const output = stripAnsi(replaceEngineType(result))

expect(output).toMatchSnapshot()
})

it('--generator: should fail - single invalid generator name', async () => {
ctx.fixture('example-project')

await expect(
Generate.new().parse([
'--schema=./prisma/multiple-generator.prisma',
'--generator=client',
'--generator=invalid_client',
]),
).rejects.toMatchSnapshot()
})

it('--generator: should fail - multiple invalid generator names', async () => {
ctx.fixture('example-project')

await expect(
Generate.new().parse([
'--schema=./prisma/multiple-generator.prisma',
'--generator=client',
'--generator=invalid_client',
'--generator=invalid_client_2',
]),
).rejects.toMatchSnapshot()
})
})

function replaceEngineType(result: string | Error) {
Expand Down
@@ -1,5 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`--schema from parent directory --generator: should fail - multiple invalid generator names 1`] = `The generators invalid_client, invalid_client_2 specified via --generator do not exist in your Prisma schema`;

exports[`--schema from parent directory --generator: should fail - single invalid generator name 1`] = `The generator invalid_client specified via --generator does not exist in your Prisma schema`;

exports[`--schema from parent directory --generator: should work - valid generator names 1`] = `
✔ Generated Prisma Client (0.0.0 | TEST_ENGINE_TYPE) to ./generated/client in XXXms
✔ Generated Prisma Client (0.0.0 | TEST_ENGINE_TYPE) to ./generated/client_3 in XXXms
You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client
\`\`\`
import { PrismaClient } from './generated/client'
const prisma = new PrismaClient()
\`\`\`
`;

exports[`using cli should work with a custom output dir 1`] = `
Prisma schema loaded from prisma/schema.prisma
Expand Down
@@ -0,0 +1,44 @@
generator client {
provider = "prisma-client-js"
output = "../generated/client"
}

generator client_2 {
provider = "prisma-client-js"
output = "../generated/client_2"
}

generator client_3 {
provider = "prisma-client-js"
output = "../generated/client_3"
}

datasource db {
provider = "sqlite"
url = "file:dev.db"
}

model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}

model Profile {
id Int @id @default(autoincrement())
bio String?
user User @relation(fields: [userId], references: [id])
userId Int @unique
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
}
Expand Up @@ -618,6 +618,38 @@ describe('getGenerators', () => {
generators.forEach((g) => g.stop())
})

test('filter generator names', async () => {
const aliases = {
'predefined-generator-1': {
generatorPath: generatorPath,
outputPath: __dirname,
},
'predefined-generator-2': {
generatorPath: generatorPath,
outputPath: __dirname,
},
'predefined-generator-3': {
generatorPath: generatorPath,
outputPath: __dirname,
},
}

const generators = await getGenerators({
schemaPath: path.join(__dirname, 'multiple-generators-schema.prisma'),
dataProxy: false,
providerAliases: aliases,
generatorNames: ['client_1', 'client_3'],
})

expect(generators).toHaveLength(2)
expect(generators[0].config.name).toEqual('client_1')
expect(generators[0].getProvider()).toEqual('predefined-generator-1')
expect(generators[1].config.name).toEqual('client_3')
expect(generators[1].getProvider()).toEqual('predefined-generator-3')

generators.forEach((g) => g.stop())
})

test('fail on platforms', async () => {
const aliases = {
'predefined-generator': {
Expand Down Expand Up @@ -805,4 +837,36 @@ describe('getGenerators', () => {
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`)
expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`)
})

test('fail if generator not found', async () => {
expect.assertions(1)

const aliases = {
'predefined-generator-1': {
generatorPath: generatorPath,
outputPath: __dirname,
},
'predefined-generator-2': {
generatorPath: generatorPath,
outputPath: __dirname,
},
'predefined-generator-3': {
generatorPath: generatorPath,
outputPath: __dirname,
},
}

try {
await getGenerators({
schemaPath: path.join(__dirname, 'multiple-generators-schema.prisma'),
dataProxy: false,
providerAliases: aliases,
generatorNames: ['client_1', 'invalid_generator'],
})
} catch (e) {
expect(stripAnsi(e.message)).toMatchInlineSnapshot(
`"The generator invalid_generator specified via --generator does not exist in your Prisma schema"`,
)
}
})
})
@@ -0,0 +1,21 @@
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}

generator client_1 {
provider = "predefined-generator-1"
}

generator client_2 {
provider = "predefined-generator-2"
}

generator client_3 {
provider = "predefined-generator-3"
}

model User {
id Int @id
name String
}
24 changes: 23 additions & 1 deletion packages/internals/src/get-generators/getGenerators.ts
Expand Up @@ -51,6 +51,7 @@ export type GetGeneratorOptions = {
skipDownload?: boolean
binaryPathsOverride?: BinaryPathsOverride
dataProxy: boolean
generatorNames?: string[]
}
/**
* Makes sure that all generators have the binaries they deserve and returns a
Expand All @@ -71,6 +72,7 @@ export async function getGenerators(options: GetGeneratorOptions): Promise<Gener
skipDownload,
binaryPathsOverride,
dataProxy,
generatorNames = [],
} = options

if (!schemaPath) {
Expand Down Expand Up @@ -143,7 +145,7 @@ export async function getGenerators(options: GetGeneratorOptions): Promise<Gener

checkFeatureFlags(config, options)

const generatorConfigs = overrideGenerators || config.generators
const generatorConfigs = filterGenerators(overrideGenerators || config.generators, generatorNames)

await validateGenerators(generatorConfigs)

Expand Down Expand Up @@ -495,3 +497,23 @@ In case you want to fix this, you can provide ${chalk.greenBright(
}
}
}

function filterGenerators(generators: GeneratorConfig[], generatorNames: string[]) {
if (generatorNames.length < 1) {
return generators
}

const filtered = generators.filter((generator) => generatorNames.includes(generator.name))

if (filtered.length !== generatorNames.length) {
const missings = generatorNames.filter((name) => filtered.find((generator) => generator.name === name) == null)
const isSingular = missings.length <= 1
throw new Error(
`The ${isSingular ? 'generator' : 'generators'} ${chalk.bold(missings.join(', '))} specified via ${chalk.bold(
'--generator',
)} ${isSingular ? 'does' : 'do'} not exist in your Prisma schema`,
)
}

return filtered
}

0 comments on commit 1ea1511

Please sign in to comment.