@effectify/prisma 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +17 -0
- package/dist/src/commands/generate-effect.d.ts +2 -0
- package/dist/src/commands/generate-effect.js +73 -0
- package/dist/src/commands/generate-sql-schema.d.ts +2 -0
- package/dist/src/commands/generate-sql-schema.js +72 -0
- package/dist/src/commands/init.d.ts +4 -0
- package/dist/src/commands/init.js +102 -0
- package/dist/src/effect-prisma.d.ts +2 -0
- package/dist/src/effect-prisma.js +1771 -0
- package/dist/src/generators/prisma-effect-generator.d.ts +1 -0
- package/dist/src/generators/prisma-effect-generator.js +446 -0
- package/dist/src/generators/sql-schema-generator.d.ts +1 -0
- package/dist/src/generators/sql-schema-generator.js +58 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +53 -0
- package/prisma/dev.db +0 -0
- package/prisma/generated/client.d.ts +1 -0
- package/prisma/generated/client.js +5 -0
- package/prisma/generated/default.d.ts +1 -0
- package/prisma/generated/default.js +5 -0
- package/prisma/generated/edge.d.ts +1 -0
- package/prisma/generated/edge.js +141 -0
- package/prisma/generated/effect/index.ts +397 -0
- package/prisma/generated/effect/prisma-repository.ts +954 -0
- package/prisma/generated/effect/prisma-schema.ts +94 -0
- package/prisma/generated/effect/schemas/enums.ts +6 -0
- package/prisma/generated/effect/schemas/index.ts +2 -0
- package/prisma/generated/effect/schemas/types.ts +40 -0
- package/prisma/generated/index-browser.js +172 -0
- package/prisma/generated/index.d.ts +2360 -0
- package/prisma/generated/index.js +141 -0
- package/prisma/generated/package.json +144 -0
- package/prisma/generated/query_compiler_bg.js +2 -0
- package/prisma/generated/query_compiler_bg.wasm +0 -0
- package/prisma/generated/query_compiler_bg.wasm-base64.js +2 -0
- package/prisma/generated/runtime/client.d.ts +3180 -0
- package/prisma/generated/runtime/client.js +86 -0
- package/prisma/generated/runtime/index-browser.d.ts +87 -0
- package/prisma/generated/runtime/index-browser.js +6 -0
- package/prisma/generated/runtime/wasm-compiler-edge.js +76 -0
- package/prisma/generated/schema.prisma +31 -0
- package/prisma/generated/wasm-edge-light-loader.mjs +5 -0
- package/prisma/generated/wasm-worker-loader.mjs +5 -0
- package/prisma/migrations/20250721164420_init/migration.sql +9 -0
- package/prisma/migrations/20250721191716_dumb/migration.sql +49 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +31 -0
- package/prisma.config.ts +8 -0
- package/project.json +48 -0
- package/scripts/cleanup-tests.ts +26 -0
- package/scripts/generate-test-files.ts +93 -0
- package/setup-tests.ts +10 -0
- package/src/cli.tsx +23 -0
- package/src/commands/generate-effect.ts +109 -0
- package/src/commands/generate-sql-schema.ts +109 -0
- package/src/commands/init.ts +155 -0
- package/src/effect-prisma.ts +1826 -0
- package/src/generators/prisma-effect-generator.ts +496 -0
- package/src/generators/sql-schema-generator.ts +75 -0
- package/test/prisma-model.test.ts +340 -0
- package/test/utils.ts +10 -0
- package/tsconfig.json +20 -0
- package/tsconfig.lib.json +24 -0
- package/tsconfig.spec.json +15 -0
- package/vitest.config.ts +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# @effectify/prisma
|
|
2
|
+
|
|
3
|
+
A powerful Prisma generator that creates **Effect** services and layers from your Prisma schema, enabling seamless integration of Prisma with the Effect ecosystem.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **Effect-Native Services**: Auto-generated Effect services for all your Prisma models.
|
|
8
|
+
- **Dependency Injection**: Ready-to-use Layers for easy testing and production setup.
|
|
9
|
+
- **Type-Safe Error Handling**: All Prisma errors are mapped to typed Effect errors (e.g., `PrismaUniqueConstraintError`).
|
|
10
|
+
- **Transaction Support**: Native Effect integration for Prisma transactions.
|
|
11
|
+
- **Schema Validation**: Integration with `@effect/schema` for runtime validation.
|
|
12
|
+
|
|
13
|
+
## 📦 Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @effectify/prisma prisma-effect-kysely
|
|
17
|
+
pnpm add effect @prisma/client
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 🛠️ Configuration
|
|
21
|
+
|
|
22
|
+
Add the generator to your `schema.prisma` file:
|
|
23
|
+
|
|
24
|
+
```prisma
|
|
25
|
+
generator client {
|
|
26
|
+
provider = "prisma-client-js"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
generator effect_schemas {
|
|
30
|
+
provider = "prisma-effect-kysely"
|
|
31
|
+
output = "./generated/effect/schemas"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
generator effect {
|
|
35
|
+
provider = "effect-prisma"
|
|
36
|
+
output = "./generated/effect"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
datasource db {
|
|
40
|
+
provider = "sqlite" // or postgresql, mysql, etc.
|
|
41
|
+
url = env("DATABASE_URL")
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## ⚙️ Usage
|
|
46
|
+
|
|
47
|
+
### 1. Generate the Code
|
|
48
|
+
|
|
49
|
+
Run the Prisma generator to create your Effect services:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm prisma generate
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Use the Generated Services
|
|
56
|
+
|
|
57
|
+
The generator creates a `Prisma` service for transactions and raw queries, and Model classes that you can use to create repositories.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { Effect, Layer } from "effect";
|
|
61
|
+
import { Prisma, UserModel } from "./generated/effect/index.js";
|
|
62
|
+
import * as PrismaRepository from "./generated/effect/prisma-repository.js";
|
|
63
|
+
|
|
64
|
+
// Define a program using the generated Prisma service
|
|
65
|
+
const program = Effect.gen(function* () {
|
|
66
|
+
// Create a repository for the User model
|
|
67
|
+
const userRepo = yield* PrismaRepository.make(UserModel, {
|
|
68
|
+
modelName: "user",
|
|
69
|
+
spanPrefix: "User",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Create a new user
|
|
73
|
+
const newUser = yield* userRepo.create({
|
|
74
|
+
data: {
|
|
75
|
+
email: "hello@effect.website",
|
|
76
|
+
name: "Effect User",
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Find the user
|
|
81
|
+
const user = yield* userRepo.findUnique({
|
|
82
|
+
where: { id: newUser.id },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return user;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Provide the Prisma layer
|
|
89
|
+
const MainLayer = Prisma.layer({
|
|
90
|
+
// Prisma Client options
|
|
91
|
+
log: ["query", "info", "warn", "error"],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Run the program
|
|
95
|
+
Effect.runPromise(program.pipe(Effect.provide(MainLayer)));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 🧪 Testing
|
|
99
|
+
|
|
100
|
+
The generated layers make testing easy by allowing you to provide alternative implementations or test databases.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { it } from "@effect/vitest";
|
|
104
|
+
import { Effect } from "effect";
|
|
105
|
+
import { Prisma, UserModel } from "./generated/effect/index.js";
|
|
106
|
+
import * as PrismaRepository from "./generated/effect/prisma-repository.js";
|
|
107
|
+
|
|
108
|
+
it.effect("should create a user", () =>
|
|
109
|
+
Effect.gen(function* () {
|
|
110
|
+
const userRepo = yield* PrismaRepository.make(UserModel, {
|
|
111
|
+
modelName: "user",
|
|
112
|
+
spanPrefix: "User",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const user = yield* userRepo.create({
|
|
116
|
+
data: { email: "test@example.com" },
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(user.email).toBe("test@example.com");
|
|
120
|
+
}).pipe(
|
|
121
|
+
Effect.provide(Prisma.layer()) // In tests, you might want to use a specific test DB url
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## ⚠️ Error Handling
|
|
127
|
+
|
|
128
|
+
All Prisma errors are mapped to specific tagged errors in Effect, allowing you to handle them precisely.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { Effect } from "effect";
|
|
132
|
+
import { Prisma, UserModel } from "./generated/effect/index.js";
|
|
133
|
+
import * as PrismaRepository from "./generated/effect/prisma-repository.js";
|
|
134
|
+
|
|
135
|
+
const createUser = (email: string) =>
|
|
136
|
+
Effect.gen(function* () {
|
|
137
|
+
const userRepo = yield* PrismaRepository.make(UserModel, {
|
|
138
|
+
modelName: "user",
|
|
139
|
+
spanPrefix: "User",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return yield* userRepo.create({
|
|
143
|
+
data: { email },
|
|
144
|
+
});
|
|
145
|
+
}).pipe(
|
|
146
|
+
Effect.catchTag("PrismaUniqueConstraintError", (error) =>
|
|
147
|
+
Effect.logError(`User with email ${email} already exists`)
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 📝 License
|
|
153
|
+
|
|
154
|
+
MIT
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import * as Command from '@effect/cli/Command';
|
|
3
|
+
import * as NodeContext from '@effect/platform-node/NodeContext';
|
|
4
|
+
import * as NodeRuntime from '@effect/platform-node/NodeRuntime';
|
|
5
|
+
import * as Console from 'effect/Console';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import { generateEffectCommand } from './commands/generate-effect.js';
|
|
8
|
+
import { generateSqlSchemaCommand } from './commands/generate-sql-schema.js';
|
|
9
|
+
import { initCommand } from './commands/init.js';
|
|
10
|
+
// Main CLI command
|
|
11
|
+
const prisma = Command.make('prisma', {}, () => Console.log('🚀 prisma CLI - Use --help to see available commands')).pipe(Command.withSubcommands([initCommand, generateEffectCommand, generateSqlSchemaCommand]));
|
|
12
|
+
// Run the CLI
|
|
13
|
+
const cli = Command.run(prisma, {
|
|
14
|
+
name: '@effectify/prisma CLI',
|
|
15
|
+
version: '0.1.0',
|
|
16
|
+
});
|
|
17
|
+
cli(process.argv).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as Command from '@effect/cli/Command';
|
|
2
|
+
import * as FileSystem from '@effect/platform/FileSystem';
|
|
3
|
+
import * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';
|
|
4
|
+
import * as NodePath from '@effect/platform-node/NodePath';
|
|
5
|
+
import * as Console from 'effect/Console';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Match from 'effect/Match';
|
|
8
|
+
// import { generateEffectPrisma } from '../generators/prisma-effect-generator.js'
|
|
9
|
+
// Check if file exists
|
|
10
|
+
const fileExists = (path) => Effect.gen(function* () {
|
|
11
|
+
const fs = yield* FileSystem.FileSystem;
|
|
12
|
+
return yield* fs.exists(path);
|
|
13
|
+
});
|
|
14
|
+
// Detect package manager using pattern matching
|
|
15
|
+
const detectPackageManager = () => Effect.gen(function* () {
|
|
16
|
+
const pnpmExists = yield* fileExists('pnpm-lock.yaml');
|
|
17
|
+
const bunExists = yield* fileExists('bun.lockb');
|
|
18
|
+
const npmExists = yield* fileExists('package-lock.json');
|
|
19
|
+
// Create a tuple to match against
|
|
20
|
+
const lockFiles = [pnpmExists, bunExists, npmExists];
|
|
21
|
+
return Match.value(lockFiles).pipe(Match.when([true, false, false], () => 'pnpm'), Match.when([false, true, false], () => 'bun'), Match.when([false, false, true], () => 'npm'), Match.orElse(() => 'npm'));
|
|
22
|
+
});
|
|
23
|
+
// Check if Prisma schema exists
|
|
24
|
+
const checkPrismaSchema = () => Effect.gen(function* () {
|
|
25
|
+
const schemaExists = yield* fileExists('prisma/schema.prisma');
|
|
26
|
+
if (!schemaExists) {
|
|
27
|
+
yield* Console.log('❌ Prisma schema not found.');
|
|
28
|
+
yield* Console.log('');
|
|
29
|
+
yield* Console.log('Please run the following command first:');
|
|
30
|
+
const packageManager = yield* detectPackageManager();
|
|
31
|
+
const initCommand = Match.value(packageManager).pipe(Match.when('pnpm', () => 'pnpm dlx prisma init'), Match.when('bun', () => 'bunx prisma init'), Match.when('npm', () => 'npx prisma init'), Match.exhaustive);
|
|
32
|
+
yield* Console.log(` ${initCommand}`);
|
|
33
|
+
yield* Effect.fail(new Error('Prisma schema not found'));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Execute the Effect generator
|
|
37
|
+
const generateEffectServices = () => Effect.gen(function* () {
|
|
38
|
+
// Check if we're being called by Prisma (no interactive output)
|
|
39
|
+
const isCalledByPrisma = process.env.PRISMA_GENERATOR_INVOCATION === 'true' ||
|
|
40
|
+
process.argv.includes('--generator') ||
|
|
41
|
+
!process.stdout.isTTY;
|
|
42
|
+
if (!isCalledByPrisma) {
|
|
43
|
+
yield* Console.log('🔧 Running Effect generator...');
|
|
44
|
+
}
|
|
45
|
+
// Check if Prisma schema exists
|
|
46
|
+
yield* checkPrismaSchema();
|
|
47
|
+
// Note: We don't need to check for local generator files
|
|
48
|
+
// since we're importing from our own package
|
|
49
|
+
if (!isCalledByPrisma) {
|
|
50
|
+
yield* Console.log('🚀 Executing Effect generator...');
|
|
51
|
+
}
|
|
52
|
+
// Execute the generator function
|
|
53
|
+
yield* Effect.tryPromise({
|
|
54
|
+
try: () => {
|
|
55
|
+
// Create mock options for the generator
|
|
56
|
+
// const mockOptions = {
|
|
57
|
+
// dmmf: { datamodel: { models: [] } }, // Empty models for now
|
|
58
|
+
// generator: { output: { value: 'src/generated/effect-prisma' } },
|
|
59
|
+
// }
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
// return generateEffectPrisma(mockOptions)
|
|
62
|
+
},
|
|
63
|
+
catch: (error) => new Error(`Generator execution failed: ${error}`),
|
|
64
|
+
});
|
|
65
|
+
if (!isCalledByPrisma) {
|
|
66
|
+
yield* Console.log('✅ Effect generator executed successfully!');
|
|
67
|
+
yield* Console.log('💡 Generated files are available in the configured output directory');
|
|
68
|
+
}
|
|
69
|
+
// Ensure the effect completes and exits
|
|
70
|
+
yield* Effect.sync(() => process.exit(0));
|
|
71
|
+
});
|
|
72
|
+
// Export the generate-effect command
|
|
73
|
+
export const generateEffectCommand = Command.make('generate-effect', {}, () => generateEffectServices().pipe(Effect.provide(NodeFileSystem.layer), Effect.provide(NodePath.layer)));
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as Command from '@effect/cli/Command';
|
|
2
|
+
import * as FileSystem from '@effect/platform/FileSystem';
|
|
3
|
+
import * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';
|
|
4
|
+
import * as NodePath from '@effect/platform-node/NodePath';
|
|
5
|
+
import * as Console from 'effect/Console';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Match from 'effect/Match';
|
|
8
|
+
// import { generateSqlSchema } from '../generators/sql-schema-generator.js'
|
|
9
|
+
// Check if file exists
|
|
10
|
+
const fileExists = (path) => Effect.gen(function* () {
|
|
11
|
+
const fs = yield* FileSystem.FileSystem;
|
|
12
|
+
return yield* fs.exists(path);
|
|
13
|
+
});
|
|
14
|
+
// Detect package manager using pattern matching
|
|
15
|
+
const detectPackageManager = () => Effect.gen(function* () {
|
|
16
|
+
const pnpmExists = yield* fileExists('pnpm-lock.yaml');
|
|
17
|
+
const bunExists = yield* fileExists('bun.lockb');
|
|
18
|
+
const npmExists = yield* fileExists('package-lock.json');
|
|
19
|
+
// Create a tuple to match against
|
|
20
|
+
const lockFiles = [pnpmExists, bunExists, npmExists];
|
|
21
|
+
return Match.value(lockFiles).pipe(Match.when([true, false, false], () => 'pnpm'), Match.when([false, true, false], () => 'bun'), Match.when([false, false, true], () => 'npm'), Match.orElse(() => 'npm'));
|
|
22
|
+
});
|
|
23
|
+
// Check if Prisma schema exists
|
|
24
|
+
const checkPrismaSchema = () => Effect.gen(function* () {
|
|
25
|
+
const schemaExists = yield* fileExists('prisma/schema.prisma');
|
|
26
|
+
if (!schemaExists) {
|
|
27
|
+
yield* Console.log('❌ Prisma schema not found.');
|
|
28
|
+
yield* Console.log('');
|
|
29
|
+
yield* Console.log('Please run the following command first:');
|
|
30
|
+
const packageManager = yield* detectPackageManager();
|
|
31
|
+
const initCommand = Match.value(packageManager).pipe(Match.when('pnpm', () => 'pnpm dlx prisma init'), Match.when('bun', () => 'bunx prisma init'), Match.when('npm', () => 'npx prisma init'), Match.exhaustive);
|
|
32
|
+
yield* Console.log(` ${initCommand}`);
|
|
33
|
+
yield* Effect.fail(new Error('Prisma schema not found'));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Execute the SQL schema generator
|
|
37
|
+
const generateSqlSchemaServices = () => Effect.gen(function* () {
|
|
38
|
+
// Check if we're being called by Prisma (no interactive output)
|
|
39
|
+
const isCalledByPrisma = process.env.PRISMA_GENERATOR_INVOCATION === 'true' ||
|
|
40
|
+
process.argv.includes('--generator') ||
|
|
41
|
+
!process.stdout.isTTY;
|
|
42
|
+
if (!isCalledByPrisma) {
|
|
43
|
+
yield* Console.log('🔧 Running SQL schema generator...');
|
|
44
|
+
}
|
|
45
|
+
// Check if Prisma schema exists
|
|
46
|
+
yield* checkPrismaSchema();
|
|
47
|
+
// Note: We don't need to check for local generator files
|
|
48
|
+
// since we're importing from our own package
|
|
49
|
+
if (!isCalledByPrisma) {
|
|
50
|
+
yield* Console.log('🚀 Executing SQL schema generator...');
|
|
51
|
+
}
|
|
52
|
+
// Execute the generator function
|
|
53
|
+
yield* Effect.tryPromise({
|
|
54
|
+
try: () => {
|
|
55
|
+
// Create mock options for the generator
|
|
56
|
+
// const mockOptions = {
|
|
57
|
+
// generator: { output: { value: 'src/generated' } },
|
|
58
|
+
// }
|
|
59
|
+
// return generateSqlSchema(mockOptions)
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
},
|
|
62
|
+
catch: (error) => new Error(`Generator execution failed: ${error}`),
|
|
63
|
+
});
|
|
64
|
+
if (!isCalledByPrisma) {
|
|
65
|
+
yield* Console.log('✅ SQL schema generator executed successfully!');
|
|
66
|
+
yield* Console.log('💡 Generated files are available in the configured output directory');
|
|
67
|
+
}
|
|
68
|
+
// Ensure the effect completes and exits
|
|
69
|
+
yield* Effect.sync(() => process.exit(0));
|
|
70
|
+
});
|
|
71
|
+
// Export the generate-sql-schema command
|
|
72
|
+
export const generateSqlSchemaCommand = Command.make('generate-sql-schema', {}, () => generateSqlSchemaServices().pipe(Effect.provide(NodeFileSystem.layer), Effect.provide(NodePath.layer)));
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as Command from '@effect/cli/Command';
|
|
2
|
+
import * as Options from '@effect/cli/Options';
|
|
3
|
+
import * as FileSystem from '@effect/platform/FileSystem';
|
|
4
|
+
import * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';
|
|
5
|
+
import * as NodePath from '@effect/platform-node/NodePath';
|
|
6
|
+
import * as Console from 'effect/Console';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Match from 'effect/Match';
|
|
9
|
+
// Options for the init command
|
|
10
|
+
const outputOption = Options.text('output').pipe(Options.withAlias('o'), Options.withDescription('Output directory path for generated files'), Options.withDefault('src'));
|
|
11
|
+
// Check if file exists
|
|
12
|
+
const fileExists = (path) => Effect.gen(function* () {
|
|
13
|
+
const fs = yield* FileSystem.FileSystem;
|
|
14
|
+
return yield* fs.exists(path);
|
|
15
|
+
});
|
|
16
|
+
// Read file content
|
|
17
|
+
const readFileContent = (path) => Effect.gen(function* () {
|
|
18
|
+
const fs = yield* FileSystem.FileSystem;
|
|
19
|
+
const content = yield* fs.readFileString(path);
|
|
20
|
+
return content;
|
|
21
|
+
});
|
|
22
|
+
// Write file content
|
|
23
|
+
const writeFileContent = (path, content) => Effect.gen(function* () {
|
|
24
|
+
const fs = yield* FileSystem.FileSystem;
|
|
25
|
+
yield* fs.writeFileString(path, content);
|
|
26
|
+
});
|
|
27
|
+
// Detect package manager using pattern matching
|
|
28
|
+
const detectPackageManager = () => Effect.gen(function* () {
|
|
29
|
+
const pnpmExists = yield* fileExists('pnpm-lock.yaml');
|
|
30
|
+
const bunExists = yield* fileExists('bun.lockb');
|
|
31
|
+
const npmExists = yield* fileExists('package-lock.json');
|
|
32
|
+
// Create a tuple to match against
|
|
33
|
+
const lockFiles = [pnpmExists, bunExists, npmExists];
|
|
34
|
+
return Match.value(lockFiles).pipe(Match.when([true, false, false], () => 'pnpm'), Match.when([false, true, false], () => 'bun'), Match.when([false, false, true], () => 'npm'), Match.orElse(() => 'npm'));
|
|
35
|
+
});
|
|
36
|
+
// Check if Prisma is already initialized
|
|
37
|
+
const checkPrismaSetup = () => Effect.gen(function* () {
|
|
38
|
+
const schemaExists = yield* fileExists('prisma/schema.prisma');
|
|
39
|
+
if (!schemaExists) {
|
|
40
|
+
const packageManager = yield* detectPackageManager();
|
|
41
|
+
yield* Console.log('❌ Prisma is not initialized in this project.');
|
|
42
|
+
yield* Console.log('');
|
|
43
|
+
yield* Console.log('Please run the following command first:');
|
|
44
|
+
const initCommand = Match.value(packageManager).pipe(Match.when('pnpm', () => 'pnpm dlx prisma init'), Match.when('bun', () => 'bunx prisma init'), Match.when('npm', () => 'npx prisma init'), Match.exhaustive);
|
|
45
|
+
yield* Console.log(` ${initCommand}`);
|
|
46
|
+
yield* Console.log('');
|
|
47
|
+
yield* Console.log('For more information, visit:');
|
|
48
|
+
yield* Console.log(' https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-prismaPostgres');
|
|
49
|
+
yield* Effect.fail(new Error('Prisma not initialized'));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// Initialize Prisma schema logic
|
|
53
|
+
const initializePrismaSchema = (options) => Effect.gen(function* () {
|
|
54
|
+
yield* Console.log('🔧 Configuring Prisma schema with Effect generators...');
|
|
55
|
+
yield* Console.log(`📁 Output path: ${options.output}`);
|
|
56
|
+
// Check if Prisma is already set up
|
|
57
|
+
yield* checkPrismaSetup();
|
|
58
|
+
const schemaPath = 'prisma/schema.prisma';
|
|
59
|
+
yield* Console.log('📄 Schema file already exists.');
|
|
60
|
+
// Read existing content and check if it has our generators
|
|
61
|
+
const existingContent = yield* readFileContent(schemaPath);
|
|
62
|
+
if (existingContent.includes('@effectify/prisma prisma generate-effect')) {
|
|
63
|
+
yield* Console.log('✅ Effect generators already configured!');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Add our generators to existing schema
|
|
67
|
+
yield* Console.log('🔧 Adding Effect generators to existing schema...');
|
|
68
|
+
const updatedContent = existingContent +
|
|
69
|
+
`
|
|
70
|
+
|
|
71
|
+
// Effect generators added by @effectify/prisma
|
|
72
|
+
generator effectServices {
|
|
73
|
+
provider = "@effectify/prisma generate-effect"
|
|
74
|
+
output = "../${options.output}/generated/effect-prisma"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
generator effect {
|
|
78
|
+
provider = "prisma-effect-kysely"
|
|
79
|
+
output = "../${options.output}/generated/effect-prisma"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
generator sqlSchema {
|
|
83
|
+
provider = "@effectify/prisma generate-sql-schema"
|
|
84
|
+
output = "../${options.output}/generated"
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
yield* writeFileContent(schemaPath, updatedContent);
|
|
88
|
+
yield* Console.log('✅ Effect generators added to existing schema!');
|
|
89
|
+
yield* Console.log('🎉 Prisma schema initialization completed!');
|
|
90
|
+
yield* Console.log('💡 Next steps:');
|
|
91
|
+
yield* Console.log(' 1. Set your DATABASE_URL environment variable');
|
|
92
|
+
yield* Console.log(' 2. Run: @effectify/prisma prisma generate-effect');
|
|
93
|
+
yield* Console.log(' 3. Run: @effectify/prisma prisma generate-sql-schema');
|
|
94
|
+
// Ensure the effect completes and exits
|
|
95
|
+
yield* Effect.sync(() => process.exit(0));
|
|
96
|
+
});
|
|
97
|
+
// Export the init command
|
|
98
|
+
export const initCommand = Command.make('init', {
|
|
99
|
+
output: outputOption,
|
|
100
|
+
}, ({ output }) => initializePrismaSchema({
|
|
101
|
+
output,
|
|
102
|
+
}).pipe(Effect.provide(NodeFileSystem.layer), Effect.provide(NodePath.layer)));
|