prisma-laravel-migrate 0.0.7 → 0.0.9
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 +481 -298
- package/dist/diff-writer/backupPath.js +14 -0
- package/dist/diff-writer/writer.js +37 -0
- package/dist/generator/migrator/index.js +3 -3
- package/dist/generator/modeler/generator.js +57 -34
- package/dist/generator/modeler/index.js +7 -4
- package/dist/generator/utils.js +14 -34
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,299 +1,482 @@
|
|
|
1
|
-
# Prisma Laravel Migrate
|
|
2
|
-
|
|
3
|
-
A generator plugin that translates your **Prisma schema** into Laravel‑ready
|
|
4
|
-
**Database Migrations**, **Eloquent Models**, and **Enum classes**.
|
|
5
|
-
Built in strict TypeScript with fully‑customisable stubs, grouping, and
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 📦 Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install prisma-laravel-migrate --save-dev
|
|
13
|
-
# requires the Prisma CLI in your project
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## 🛠️ Prisma Generator Setup
|
|
19
|
-
|
|
20
|
-
Add both generator blocks to **`schema.prisma`**:
|
|
21
|
-
|
|
22
|
-
```prisma
|
|
23
|
-
generator migrate {
|
|
24
|
-
provider
|
|
25
|
-
stubDir
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
noEmit
|
|
31
|
-
groups
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
generator modeler {
|
|
35
|
-
provider = "prisma-laravel-models"
|
|
36
|
-
stubDir = "./prisma/stubs"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
noEmit = false
|
|
43
|
-
groups = "./prisma/group-stubs.js"
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Field Reference
|
|
48
|
-
|
|
49
|
-
| Key | Notes |
|
|
50
|
-
| --- | --- |
|
|
51
|
-
| `outputDir / output` | Destination folder (`outputDir`
|
|
52
|
-
| `outputEnumDir` | (modeler) directory for
|
|
53
|
-
| `stubDir` | Root
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
prisma
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
{
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
${model.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
${
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
${model.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
1
|
+
# Prisma Laravel Migrate
|
|
2
|
+
|
|
3
|
+
A generator plugin that translates your **Prisma schema** into Laravel‑ready
|
|
4
|
+
**Database Migrations**, **Eloquent Models**, and **Enum classes**.
|
|
5
|
+
Built in strict TypeScript with fully‑customisable stubs, grouping, and smart merge updates.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📦 Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install prisma-laravel-migrate --save-dev
|
|
13
|
+
# requires the Prisma CLI in your project
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🛠️ Prisma Generator Setup
|
|
19
|
+
|
|
20
|
+
Add both generator blocks to **`schema.prisma`**:
|
|
21
|
+
|
|
22
|
+
```prisma
|
|
23
|
+
generator migrate {
|
|
24
|
+
provider = "prisma-laravel-migrate"
|
|
25
|
+
stubDir = "./prisma/stubs"
|
|
26
|
+
|
|
27
|
+
output = "database/migrations" // fallback
|
|
28
|
+
outputDir = "database/migrations" // takes precedence
|
|
29
|
+
|
|
30
|
+
noEmit = false // skip writing if true
|
|
31
|
+
groups = "./prisma/group-stubs.js"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
generator modeler {
|
|
35
|
+
provider = "prisma-laravel-models"
|
|
36
|
+
stubDir = "./prisma/stubs"
|
|
37
|
+
|
|
38
|
+
output = "app/Models"
|
|
39
|
+
outputDir = "app/Models" // overrides output
|
|
40
|
+
outputEnumDir = "app/Enums"
|
|
41
|
+
|
|
42
|
+
noEmit = false
|
|
43
|
+
groups = "./prisma/group-stubs.js"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Field Reference
|
|
48
|
+
|
|
49
|
+
| Key | Notes |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `outputDir / output` | Destination folder (`outputDir` overrides `output`). |
|
|
52
|
+
| `outputEnumDir` | (modeler) directory for generated enum classes. |
|
|
53
|
+
| `stubDir` | Root stub folder (`migration/`, `model/`, `enum/`). |
|
|
54
|
+
| `groups` | JS module that maps stub files to table groups. |
|
|
55
|
+
| `noEmit` | If `true`, generator parses but **writes no** files (dry‑run). |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🔀 `groups` – Stub Grouping
|
|
60
|
+
|
|
61
|
+
```prisma
|
|
62
|
+
generator migrate {
|
|
63
|
+
provider = "prisma-laravel-migrate"
|
|
64
|
+
stubDir = "./prisma/stubs"
|
|
65
|
+
groups = "./prisma/group-stubs.js"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`prisma/group-stubs.js`
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
module.exports = [
|
|
73
|
+
{
|
|
74
|
+
stubFile: "auth.stub", // stubs/migration/auth.stub
|
|
75
|
+
tables: ["users","accounts","password_resets"]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
stubFile: "billing.stub", // stubs/migration/billing.stub
|
|
79
|
+
tables: ["invoices","transactions"]
|
|
80
|
+
}
|
|
81
|
+
];
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Resolution order**
|
|
85
|
+
|
|
86
|
+
1. `stubs/<type>/<table>.stub` (table‑specific)
|
|
87
|
+
2. Matching group stub (`stubFile`)
|
|
88
|
+
3. `stubs/<type>/index.stub` (default)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 📁 Stub Folder Layout
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
prisma/stubs/
|
|
96
|
+
├── migration/index.stub
|
|
97
|
+
├── model/index.stub
|
|
98
|
+
├── model/simple-model.stub
|
|
99
|
+
└── enum/index.stub
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Add table‑specific overrides at
|
|
103
|
+
`stubs/<type>/<table>.stub` (e.g. `stubs/model/users.stub`).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🔧 CLI Commands
|
|
108
|
+
|
|
109
|
+
| Command | Purpose |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `init` | Inject generator blocks & scaffold stub folders |
|
|
112
|
+
| `customize` | Create per-table stub overrides |
|
|
113
|
+
| `gen` | Run `prisma generate` then Laravel generators |
|
|
114
|
+
|
|
115
|
+
### init
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx prisma-laravel-cli init --schema=prisma/schema.prisma
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### customize
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx prisma-laravel-cli customize -t migration,model -n users,accounts --force
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Flag | Description |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `-t, --type` | **Required.** Stub types (`migration`, `model`, `enum`). `enum` may not mix. |
|
|
130
|
+
| `-n, --names` | **Required.** Table or enum names (`users,accounts`). |
|
|
131
|
+
| `--force` | Overwrite existing stub files. |
|
|
132
|
+
| `--config` | Alternate CLI config path. |
|
|
133
|
+
|
|
134
|
+
### gen
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx prisma-laravel-cli gen --config=prisma/laravel.config.js
|
|
138
|
+
# skip prisma generate step
|
|
139
|
+
npx prisma-laravel-cli gen --config=prisma/laravel.config.js --skipGenerate
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`prisma/laravel.config.js`
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
module.exports = {
|
|
146
|
+
migrator: {
|
|
147
|
+
outputDir: "database/migrations",
|
|
148
|
+
stubDir: "prisma/stubs",
|
|
149
|
+
groups: "./prisma/group-stubs.js"
|
|
150
|
+
},
|
|
151
|
+
modeler: {
|
|
152
|
+
outputDir: "app/Models",
|
|
153
|
+
outputEnumDir: "app/Enums",
|
|
154
|
+
stubDir: "prisma/stubs",
|
|
155
|
+
groups: "./prisma/group-stubs.js"
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 🔄 How updates are applied
|
|
163
|
+
|
|
164
|
+
1. Generator builds a **full new file** from your schema & stubs.
|
|
165
|
+
2. Performs a **git‑style 3‑way merge** (using `node-diff3`):
|
|
166
|
+
- **base** = last generator output (`.prisma-laravel/backups/...`)
|
|
167
|
+
- **ours** = file on disk (user edits)
|
|
168
|
+
- **theirs** = freshly generated file
|
|
169
|
+
3. Non‑conflicting changes merge automatically; conflicts are wrapped with
|
|
170
|
+
`<<<<<<<`, `=======`, `>>>>>>>`.
|
|
171
|
+
4. New `use …;` imports are merged, duplicates skipped.
|
|
172
|
+
5. Baseline copy is updated in the backups folder.
|
|
173
|
+
|
|
174
|
+
Delete the marker block **and** set `noEmit = true` to stop updates for a file.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## ✨ Stub Customisation Notes
|
|
179
|
+
|
|
180
|
+
Stubs are **JavaScript template literals**. Escape \` and \${ } if you want them literally.
|
|
181
|
+
|
|
182
|
+
> **Fully custom model stubs**
|
|
183
|
+
> If you remove the `${content}` placeholder **and** the marker block, the
|
|
184
|
+
> generator leaves the file untouched.
|
|
185
|
+
> Keep the markers if you want automated updates but customised surroundings.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 📑 Default Stub Templates
|
|
190
|
+
|
|
191
|
+
<details>
|
|
192
|
+
<summary>Enum <code>index.stub</code></summary>
|
|
193
|
+
|
|
194
|
+
```php
|
|
195
|
+
<?php
|
|
196
|
+
|
|
197
|
+
namespace App\\Enums;
|
|
198
|
+
|
|
199
|
+
enum ${enumDef.name}: string
|
|
200
|
+
{
|
|
201
|
+
// <prisma-laravel:start>
|
|
202
|
+
${enumDef.values.map(v => ` case ${v} = '${v}';`).join('\\n')}
|
|
203
|
+
// <prisma-laravel:end>
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
</details>
|
|
208
|
+
|
|
209
|
+
<details>
|
|
210
|
+
<summary>Migration <code>index.stub</code></summary>
|
|
211
|
+
|
|
212
|
+
```php
|
|
213
|
+
<?php
|
|
214
|
+
|
|
215
|
+
use Illuminate\\Database\\Migrations\\Migration;
|
|
216
|
+
use Illuminate\\Database\\Schema\\Blueprint;
|
|
217
|
+
use Illuminate\\Support\\Facades\\Schema;
|
|
218
|
+
|
|
219
|
+
return new class extends Migration
|
|
220
|
+
{
|
|
221
|
+
public function up(): void
|
|
222
|
+
{
|
|
223
|
+
Schema::create('${tableName}', function (Blueprint $table) {
|
|
224
|
+
// <prisma-laravel:start>
|
|
225
|
+
${columns}
|
|
226
|
+
// <prisma-laravel:end>
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public function down(): void
|
|
231
|
+
{
|
|
232
|
+
Schema::dropIfExists('${tableName}');
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
</details>
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 🏗️ Complex Model Stub Example
|
|
242
|
+
|
|
243
|
+
<details>
|
|
244
|
+
<summary>Expand stub</summary>
|
|
245
|
+
|
|
246
|
+
```php
|
|
247
|
+
<?php
|
|
248
|
+
|
|
249
|
+
namespace App\\Models;
|
|
250
|
+
|
|
251
|
+
${model.imports}
|
|
252
|
+
use Illuminate\\Database\\Eloquent\\Model;
|
|
253
|
+
use Illuminate\\Database\\Eloquent\\Relations\\{ BelongsTo, HasMany, BelongsToMany };
|
|
254
|
+
|
|
255
|
+
class ${model.className} extends Model
|
|
256
|
+
{
|
|
257
|
+
protected $table = '${model.tableName}';
|
|
258
|
+
|
|
259
|
+
/* Mass Assignment */
|
|
260
|
+
protected $fillable = [
|
|
261
|
+
${model.properties.filter(p => p.fillable).map(p => ` '${p.name}',`).join('\\n')}
|
|
262
|
+
];
|
|
263
|
+
protected $guarded = [
|
|
264
|
+
${(model.guarded ?? []).map(n => ` '${n}',`).join('\\n')}
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
/* Hidden & Casts */
|
|
268
|
+
protected $hidden = [
|
|
269
|
+
${model.properties.filter(p => p.hidden).map(p => ` '${p.name}',`).join('\\n')}
|
|
270
|
+
];
|
|
271
|
+
protected $casts = [
|
|
272
|
+
${model.properties.filter(p => p.cast).map(p => ` '${p.name}' => '${p.cast}',`).join('\\n')}
|
|
273
|
+
${model.properties.filter(p => p.enumRef).map(p => ` '${p.name}' => ${p.enumRef}::class,`).join('\\n')}
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
/* Interfaces metadata */
|
|
277
|
+
public array $interfaces = [
|
|
278
|
+
${Object.entries(model.interfaces).map(([k,i]) =>
|
|
279
|
+
` '${k}' => {${i.import ? ` import: '${i.import}',` : ''} type: '${i.type}' },`).join('\\n')}
|
|
280
|
+
];
|
|
281
|
+
// This structure is useful for packages like fumeapp/modeltyper which
|
|
282
|
+
// read interface metadata to build TypeScript helpers.
|
|
283
|
+
|
|
284
|
+
/* Relationships */
|
|
285
|
+
${model.relations.map(r => {
|
|
286
|
+
const args = [r.modelClass, r.foreignKey ? `'${r.foreignKey}'` : '', r.localKey ? `'${r.localKey}'` : ''].filter(Boolean).join(', ');
|
|
287
|
+
return ` public function ${r.name}(): ${r.type.charAt(0).toUpperCase()+r.type.slice(1)}\\n {\\n return $this->${r.type}(${args});\\n }`;
|
|
288
|
+
}).join('\\n\\n')}
|
|
289
|
+
|
|
290
|
+
// <prisma-laravel:start>
|
|
291
|
+
${content}
|
|
292
|
+
// <prisma-laravel:end>
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
</details>
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 🚀 Enum Casting
|
|
301
|
+
|
|
302
|
+
```php
|
|
303
|
+
protected $casts = [
|
|
304
|
+
'status' => StatusEnum::class,
|
|
305
|
+
];
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
## 🧩 Custom Migration Rules
|
|
312
|
+
|
|
313
|
+
Point the generator’s `rules` field to a JS file exporting an **array** of
|
|
314
|
+
objects that implement the `Rule` interface:
|
|
315
|
+
|
|
316
|
+
```prisma
|
|
317
|
+
generator migrate {
|
|
318
|
+
provider = "prisma-laravel-migration"
|
|
319
|
+
stubDir = "./prisma/stubs"
|
|
320
|
+
rules = "./prisma/custom-rules.js"
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`prisma/custom-rules.js`
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
/** @type {import('prisma-laravel-migrate').Rule[]} */
|
|
328
|
+
module.exports = [
|
|
329
|
+
{
|
|
330
|
+
// Always add an `archived` boolean column defaulting to false
|
|
331
|
+
test(def) {
|
|
332
|
+
return def.name === "archived" && def.migrationType === "boolean";
|
|
333
|
+
},
|
|
334
|
+
render() {
|
|
335
|
+
return {
|
|
336
|
+
column: "archived",
|
|
337
|
+
snippet: ["$table->boolean('archived')->default(false);"],
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
// add more Rule objects...
|
|
342
|
+
];
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Rule execution order**
|
|
346
|
+
|
|
347
|
+
1. Built‑in rules
|
|
348
|
+
2. Custom rules (executed in array order)
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### ColumnDefinition quick reference
|
|
353
|
+
|
|
354
|
+
`ColumnDefinition` extends Prisma’s `DMMF.Field`, so all raw Prisma
|
|
355
|
+
properties remain accessible.
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
import { DMMF } from "@prisma/generator-helper";
|
|
359
|
+
|
|
360
|
+
export interface ColumnDefinition extends DMMF.Field {
|
|
361
|
+
migrationType: MigrationType; // e.g. "unsignedBigInteger"
|
|
362
|
+
args?: string[];
|
|
363
|
+
nullable?: boolean;
|
|
364
|
+
unsigned?: boolean;
|
|
365
|
+
|
|
366
|
+
hasDefaultValue: boolean;
|
|
367
|
+
default?: string | number | boolean | null;
|
|
368
|
+
|
|
369
|
+
relationship?: {
|
|
370
|
+
on: string;
|
|
371
|
+
references?: string;
|
|
372
|
+
onDelete?: string;
|
|
373
|
+
onUpdate?: string;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
ignore?: boolean;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Common checks inside a rule:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
def.kind // "scalar" | "enum" | "object"
|
|
384
|
+
def.type // original Prisma scalar
|
|
385
|
+
def.migrationType // mapped Laravel builder name
|
|
386
|
+
def.isId
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
📝 **Prisma DMMF docs:**
|
|
390
|
+
https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
### 📝 Comment-Directives in `schema.prisma`
|
|
396
|
+
|
|
397
|
+
Attach these `@` directives either to a **field** (inline or `///` above) **or**
|
|
398
|
+
to the **model** (curly‑brace syntax) to control what the generator writes into
|
|
399
|
+
your Eloquent model.
|
|
400
|
+
|
|
401
|
+
| Directive | Where you can put it | Effect in generated PHP |
|
|
402
|
+
| --- | --- | --- |
|
|
403
|
+
| `@fillable` | Field **or** `@fillable{...}` on model | Adds column(s) to `$fillable` |
|
|
404
|
+
| `@hidden` | Field **or** `@hidden{...}` on model | Adds column(s) to `$hidden` |
|
|
405
|
+
| `@guarded` | Field **or** `@guarded{...}` on model | Adds column(s) to `$guarded` |
|
|
406
|
+
| `@cast{...}` | Field only | Adds custom entry to `$casts` |
|
|
407
|
+
| `@type{ import:'…', type:'…' }` | Field only | Adds entry to `$interfaces` metadata |
|
|
408
|
+
| `@ignore` | Relation field | Skips generating the relationship method |
|
|
409
|
+
| `@with` (no args) | Relation field | Adds that single relation to `$with` |
|
|
410
|
+
| `@with(rel1,rel2,…)` | Model only | Adds listed relations to `$with` |
|
|
411
|
+
|
|
412
|
+
> **Syntax options**
|
|
413
|
+
> • Inline:
|
|
414
|
+
> `balance Decimal /// @fillable @cast{decimal:2}`
|
|
415
|
+
> • Block above field:
|
|
416
|
+
> `/// @hidden`
|
|
417
|
+
> • Model list:
|
|
418
|
+
> `/// @fillable{name,balance}`
|
|
419
|
+
> • Model eager‑load:
|
|
420
|
+
> `/// @with(posts,roles)`
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
#### Example
|
|
425
|
+
|
|
426
|
+
```prisma
|
|
427
|
+
/// @fillable{name,balance}
|
|
428
|
+
/// @hidden{secretToken}
|
|
429
|
+
model Account {
|
|
430
|
+
id Int @id @default(autoincrement())
|
|
431
|
+
|
|
432
|
+
balance Decimal @default(0.0) /// @cast{decimal:2}
|
|
433
|
+
|
|
434
|
+
nickname String /// @fillable @hidden
|
|
435
|
+
|
|
436
|
+
profile Json? /// @type{ import:'@types/forms', type:'ProfileDTO' }
|
|
437
|
+
|
|
438
|
+
company Company? @relation(fields:[companyId], references:[id]) /// @ignore
|
|
439
|
+
companyId Int?
|
|
440
|
+
|
|
441
|
+
posts Post[] /// @with
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/// @with(posts,comments)
|
|
445
|
+
model User {
|
|
446
|
+
id Int @id @default(autoincrement())
|
|
447
|
+
email String
|
|
448
|
+
posts Post[]
|
|
449
|
+
comments Comment[]
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Generated output**
|
|
454
|
+
|
|
455
|
+
```php
|
|
456
|
+
protected $fillable = ['name','balance','nickname'];
|
|
457
|
+
protected $hidden = ['secretToken','nickname'];
|
|
458
|
+
protected $casts = ['balance' => 'decimal:2'];
|
|
459
|
+
|
|
460
|
+
public array $interfaces = [
|
|
461
|
+
'profile' => { import: '@types/forms', type: 'ProfileDTO' },
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
protected $with = ['posts','comments'];
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
`@ignore` prevents the `company()` relation method.
|
|
468
|
+
Combine multiple inline directives; they’re processed left‑to‑right.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## 💡 Tips
|
|
473
|
+
|
|
474
|
+
- Combine `migration` & `model` in one customize command when table names align.
|
|
475
|
+
- Use `noEmit: true` for dry‑runs or CI validation.
|
|
476
|
+
- Escape template chars in stub files.
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## 📜 License
|
|
481
|
+
|
|
299
482
|
MIT — Happy scaffolding! 🎉
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { mkdirSync, existsSync } from "fs";
|
|
3
|
+
/** project-level hidden folder */
|
|
4
|
+
const BACKUP_ROOT = path.resolve(process.cwd(), ".prisma-laravel", "backups");
|
|
5
|
+
/** Returns `<root>/.prisma-laravel/backups/<relative-to-cwd>.bak` */
|
|
6
|
+
export function backupPathFor(targetFile) {
|
|
7
|
+
const rel = path.relative(process.cwd(), targetFile);
|
|
8
|
+
const full = path.join(BACKUP_ROOT, rel + ".bak");
|
|
9
|
+
const dir = path.dirname(full);
|
|
10
|
+
if (!existsSync(dir))
|
|
11
|
+
mkdirSync(dir, { recursive: true });
|
|
12
|
+
return full;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=backupPath.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// writeWithMerge.ts
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import * as diff3 from "node-diff3";
|
|
4
|
+
import { backupPathFor } from "./backupPath.js";
|
|
5
|
+
/**
|
|
6
|
+
* Git-style 3-way merge writer.
|
|
7
|
+
* @param filePath Destination file
|
|
8
|
+
* @param newContent Freshly generated FULL text
|
|
9
|
+
* @param overwrite Skip write if false and file exists
|
|
10
|
+
*/
|
|
11
|
+
export function writeWithMerge(filePath, newContent, overwrite = true) {
|
|
12
|
+
if (!overwrite && existsSync(filePath))
|
|
13
|
+
return;
|
|
14
|
+
const bakPath = backupPathFor(filePath);
|
|
15
|
+
const base = existsSync(bakPath) ? readFileSync(bakPath, "utf-8") : null;
|
|
16
|
+
/* initial write */
|
|
17
|
+
if (!existsSync(filePath)) {
|
|
18
|
+
writeFileSync(filePath, newContent, "utf-8");
|
|
19
|
+
writeFileSync(bakPath, newContent, "utf-8");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const mine = readFileSync(filePath, "utf-8");
|
|
23
|
+
if (mine === newContent)
|
|
24
|
+
return; // already up-to-date
|
|
25
|
+
/* no baseline yet → save current as baseline, overwrite */
|
|
26
|
+
if (!base) {
|
|
27
|
+
writeFileSync(bakPath, mine, "utf-8");
|
|
28
|
+
writeFileSync(filePath, newContent, "utf-8");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
/* diff3 merge */
|
|
32
|
+
const merged = diff3
|
|
33
|
+
.merge(mine.split(/\r?\n/), base.split(/\r?\n/), newContent.split(/\r?\n/), { stringSeparator: "\n" }).result.join("\n");
|
|
34
|
+
writeFileSync(filePath, merged, "utf-8");
|
|
35
|
+
writeFileSync(bakPath, newContent, "utf-8"); // update baseline
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=writer.js.map
|
|
@@ -2,9 +2,9 @@ import { existsSync, mkdirSync, readdirSync } from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { PrismaToLaravelMigrationGenerator } from "./PrismaToLaravelMigrationGenerator.js";
|
|
4
4
|
import { StubMigrationPrinter } from "../../printer/migrations.js";
|
|
5
|
-
import { writeWithMarkers } from "../../generator/utils.js";
|
|
6
5
|
import { fileURLToPath } from "url";
|
|
7
6
|
import { sortMigrations } from "./sort.js";
|
|
7
|
+
import { writeWithMerge } from "diff-writer/writer.js";
|
|
8
8
|
export async function generateLaravelSchema(options) {
|
|
9
9
|
const { dmmf, generator } = options;
|
|
10
10
|
// 0) Pull config values (all come in as strings)
|
|
@@ -75,9 +75,9 @@ export async function generateLaravelSchema(options) {
|
|
|
75
75
|
: `${timestamp}_create_${mig.tableName}_table.php`;
|
|
76
76
|
const filePath = path.join(baseOut, fileName);
|
|
77
77
|
// 3) Extract full & generated parts from your printer
|
|
78
|
-
const { fullContent: content
|
|
78
|
+
const { fullContent: content } = printer.printMigration(mig);
|
|
79
79
|
// 4) Write with markers as before
|
|
80
|
-
|
|
80
|
+
writeWithMerge(filePath, content, cfg.overwriteExisting ?? false);
|
|
81
81
|
});
|
|
82
82
|
return migrations;
|
|
83
83
|
}
|
|
@@ -13,58 +13,80 @@ export class PrismaToLaravelModelGenerator {
|
|
|
13
13
|
values: e.values.map((v) => v.name),
|
|
14
14
|
}));
|
|
15
15
|
// 2) Build each ModelDefinition
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
// 2) Build each ModelDefinition
|
|
17
|
+
const models = this.dmmf.datamodel.models.map(model => {
|
|
18
|
+
/* ── 2.1 Model-level directives ──────────────────────────────── */
|
|
19
19
|
const modelDoc = model.documentation ?? "";
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
//
|
|
29
|
-
const
|
|
20
|
+
// helper → get list from @tag{a,b,c}
|
|
21
|
+
const listFrom = (doc, tag) => {
|
|
22
|
+
const m = doc.match(new RegExp(`@${tag}\\{([^}]+)\\}`));
|
|
23
|
+
return m ? m[1].split(",").map(s => s.trim()).filter(Boolean) : [];
|
|
24
|
+
};
|
|
25
|
+
const modelFillable = listFrom(modelDoc, "fillable");
|
|
26
|
+
const modelHidden = listFrom(modelDoc, "hidden");
|
|
27
|
+
const modelGuarded = listFrom(modelDoc, "guarded");
|
|
28
|
+
// model-level eager-loads @with(rel1,rel2)
|
|
29
|
+
const modelWith = (() => {
|
|
30
|
+
const m = modelDoc.match(/@with([^)]+)/);
|
|
31
|
+
return m ? m[1].split(",").map(s => s.trim()).filter(Boolean) : [];
|
|
32
|
+
})();
|
|
33
|
+
/* ── 2.2 Field processing ────────────────────────────────────── */
|
|
34
|
+
const withList = [...modelWith]; // eager-load bucket
|
|
35
|
+
const guardedSet = new Set(modelGuarded);
|
|
36
|
+
const fillableSet = new Set(modelFillable);
|
|
37
|
+
const hiddenSet = new Set(modelHidden);
|
|
38
|
+
const properties = model.fields.map(field => {
|
|
30
39
|
const doc = field.documentation ?? "";
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
40
|
+
// quick helper for boolean flags
|
|
41
|
+
const flag = (tag) => new RegExp(`@${tag}\\b`).test(doc);
|
|
42
|
+
const fillable = flag("fillable") || fillableSet.has(field.name);
|
|
43
|
+
const hidden = flag("hidden") || hiddenSet.has(field.name);
|
|
44
|
+
const guarded = flag("guarded") || guardedSet.has(field.name);
|
|
45
|
+
const ignore = flag("ignore");
|
|
46
|
+
// eager-load relation field
|
|
47
|
+
if (flag("with") && !withList.includes(field.name)) {
|
|
48
|
+
withList.push(field.name);
|
|
49
|
+
}
|
|
50
|
+
// custom cast
|
|
35
51
|
const castMatch = doc.match(/@cast\{([^}]+)\}/);
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
const cast = castMatch ? castMatch[1].trim() : undefined;
|
|
53
|
+
// @type{ import:'x', type:'Y' }
|
|
54
|
+
const typeMatch = doc.match(/@type\{\s*(?:import\s*:\s*'([^']+)')?\s*,?\s*type\s*:\s*'([^']+)'\s*\}/);
|
|
38
55
|
const typeAnnotation = typeMatch
|
|
39
56
|
? { import: typeMatch[1], type: typeMatch[2] }
|
|
40
57
|
: undefined;
|
|
41
|
-
const enumMeta = enums.find(
|
|
58
|
+
const enumMeta = enums.find(e => e.name === field.type);
|
|
42
59
|
const phpType = enumMeta
|
|
43
|
-
?
|
|
60
|
+
? enumMeta.name
|
|
44
61
|
: this.mapPrismaToPhpType(field.type);
|
|
45
|
-
// with checking
|
|
46
|
-
if (doc.includes("@with"))
|
|
47
|
-
withList.push(field.name);
|
|
48
62
|
return {
|
|
49
63
|
name: field.name,
|
|
50
64
|
phpType,
|
|
51
65
|
fillable,
|
|
52
66
|
hidden,
|
|
53
67
|
ignore,
|
|
68
|
+
guarded,
|
|
54
69
|
cast,
|
|
55
70
|
enumRef: enumMeta?.name,
|
|
56
71
|
typeAnnotation,
|
|
57
72
|
};
|
|
58
73
|
});
|
|
59
|
-
|
|
74
|
+
/* ── 2.3 Laravel $guarded array (union model + field) ─────────── */
|
|
75
|
+
const guarded = guardedSet.size || properties.some(p => p.guarded)
|
|
76
|
+
? [
|
|
77
|
+
...guardedSet,
|
|
78
|
+
...properties.filter(p => p.guarded).map(p => p.name),
|
|
79
|
+
]
|
|
80
|
+
: undefined;
|
|
81
|
+
/* ── 2.4 Relations (unchanged except @ignore honoured) ────────── */
|
|
60
82
|
const relations = model.fields
|
|
61
|
-
.filter(
|
|
83
|
+
.filter(f => f.kind === "object" &&
|
|
62
84
|
f.relationName &&
|
|
63
85
|
!/@ignore\b/.test(f.documentation ?? ""))
|
|
64
|
-
.map(
|
|
65
|
-
const relatedModel = this.dmmf.datamodel.models.find(
|
|
86
|
+
.map(f => {
|
|
87
|
+
const relatedModel = this.dmmf.datamodel.models.find(m => m.name === f.type);
|
|
66
88
|
const relatedTable = relatedModel.dbName ?? f.type;
|
|
67
|
-
const thisTable =
|
|
89
|
+
const thisTable = model.dbName ?? model.name;
|
|
68
90
|
const isImplicitM2M = f.isList && (f.relationFromFields?.length ?? 0) === 0;
|
|
69
91
|
const relType = isImplicitM2M
|
|
70
92
|
? "belongsToMany"
|
|
@@ -74,7 +96,7 @@ export class PrismaToLaravelModelGenerator {
|
|
|
74
96
|
let pivotTable;
|
|
75
97
|
if (relType === "belongsToMany") {
|
|
76
98
|
pivotTable = [thisTable, relatedTable]
|
|
77
|
-
.map(
|
|
99
|
+
.map(t => t.toLowerCase())
|
|
78
100
|
.sort()
|
|
79
101
|
.join("_");
|
|
80
102
|
}
|
|
@@ -87,22 +109,23 @@ export class PrismaToLaravelModelGenerator {
|
|
|
87
109
|
pivotTable,
|
|
88
110
|
};
|
|
89
111
|
});
|
|
90
|
-
|
|
112
|
+
/* ── 2.5 Interfaces from @type annotations ────────────────────── */
|
|
91
113
|
const interfaces = {};
|
|
92
114
|
for (const prop of properties) {
|
|
93
115
|
if (prop.typeAnnotation) {
|
|
94
116
|
interfaces[prop.name] = { ...prop.typeAnnotation };
|
|
95
117
|
}
|
|
96
118
|
}
|
|
119
|
+
/* ── 2.6 Final ModelDefinition ────────────────────────────────── */
|
|
97
120
|
return {
|
|
121
|
+
className: model.name,
|
|
122
|
+
tableName: model.dbName ?? model.name,
|
|
98
123
|
guarded,
|
|
99
|
-
className,
|
|
100
|
-
tableName,
|
|
101
124
|
properties,
|
|
102
125
|
relations,
|
|
103
126
|
enums,
|
|
104
127
|
interfaces,
|
|
105
|
-
with: withList.length ? withList : undefined
|
|
128
|
+
with: withList.length ? withList : undefined,
|
|
106
129
|
};
|
|
107
130
|
});
|
|
108
131
|
return { models, enums };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { buildModelContent
|
|
3
|
+
import { buildModelContent } from "../utils.js";
|
|
4
4
|
import { StubModelPrinter } from "../../printer/models.js";
|
|
5
5
|
import { PrismaToLaravelModelGenerator } from "./generator.js";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
+
import { writeWithMerge } from "diff-writer/writer.js";
|
|
7
8
|
export async function generateLaravelModels(options) {
|
|
8
9
|
const { dmmf, generator } = options;
|
|
9
10
|
// 0) Pull config values
|
|
@@ -51,7 +52,7 @@ export async function generateLaravelModels(options) {
|
|
|
51
52
|
// 2) Load stubs (allow overrides)
|
|
52
53
|
const modelStub = cfg.modelStubPath
|
|
53
54
|
? path.resolve(process.cwd(), cfg.modelStubPath)
|
|
54
|
-
: path.resolve(__dirname, "../../../stubs/
|
|
55
|
+
: path.resolve(__dirname, "../../../stubs/model.stub");
|
|
55
56
|
const enumStub = cfg.enumStubPath
|
|
56
57
|
? path.resolve(process.cwd(), cfg.enumStubPath)
|
|
57
58
|
: path.resolve(__dirname, "../../../stubs/enums.stub");
|
|
@@ -63,14 +64,16 @@ export async function generateLaravelModels(options) {
|
|
|
63
64
|
for (const enumDef of enums) {
|
|
64
65
|
const enumPhp = printer.printEnum(enumDef);
|
|
65
66
|
const enumFile = path.join(enumsDir, `${enumDef.name}.php`);
|
|
66
|
-
|
|
67
|
+
writeWithMerge(enumFile, enumPhp, cfg.overwriteExisting ?? false);
|
|
67
68
|
}
|
|
68
69
|
// 5) Write model files
|
|
69
70
|
for (const model of models) {
|
|
71
|
+
model.imports = model.properties.filter(item => item.enumRef).map(item => `use App\\Enums\\${item.enumRef};`);
|
|
72
|
+
//----
|
|
70
73
|
const content = buildModelContent(model);
|
|
71
74
|
const modelPhp = printer.printModel(model, enums, content);
|
|
72
75
|
const modelFile = path.join(modelsDir, `${model.className}.php`);
|
|
73
|
-
|
|
76
|
+
writeWithMerge(modelFile, modelPhp, cfg.overwriteExisting ?? false);
|
|
74
77
|
}
|
|
75
78
|
return { models, enums };
|
|
76
79
|
}
|
package/dist/generator/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NativeToMigrationTypeMap } from "./migrator/column-maps.js";
|
|
2
2
|
import { MigrationTypes } from "./migrator/migrationTypes.js";
|
|
3
|
-
import { existsSync
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
4
|
import path from "path";
|
|
5
5
|
/**
|
|
6
6
|
* Given a Prisma field default, return the PHP code fragment
|
|
@@ -105,9 +105,21 @@ export function buildModelContent(model) {
|
|
|
105
105
|
if (model.properties.some(p => p.cast || p.enumRef)) {
|
|
106
106
|
lines.push(`protected $casts = [\n${model.properties
|
|
107
107
|
.filter(p => p.cast || p.enumRef)
|
|
108
|
-
.map(p => ` '${p.name}' => ${p.enumRef ?
|
|
108
|
+
.map(p => ` '${p.name}' => ${p.enumRef ? `${p.enumRef}::class` : `'${p.cast}'`}`)
|
|
109
109
|
.join(",\n")}\n ];`);
|
|
110
110
|
}
|
|
111
|
+
// — Interfaces metadata slot —
|
|
112
|
+
if (model.interfaces && Object.keys(model.interfaces).length) {
|
|
113
|
+
lines.push(` public array $interfaces = [`);
|
|
114
|
+
for (const [key, info] of Object.entries(model.interfaces)) {
|
|
115
|
+
const parts = [];
|
|
116
|
+
if (info.import)
|
|
117
|
+
parts.push(`import: '${info.import}'`);
|
|
118
|
+
parts.push(`type: '${info.type}'`);
|
|
119
|
+
lines.push(` '${key}' => { ${parts.join(', ')} },`);
|
|
120
|
+
}
|
|
121
|
+
lines.push(` ];`);
|
|
122
|
+
}
|
|
111
123
|
// 4) Relations (unchanged)
|
|
112
124
|
for (const rel of model.relations) {
|
|
113
125
|
const args = [
|
|
@@ -131,38 +143,6 @@ export function buildModelContent(model) {
|
|
|
131
143
|
*/
|
|
132
144
|
export function formatStub(stub) {
|
|
133
145
|
return stub;
|
|
134
|
-
// escape backslashes first
|
|
135
|
-
// .replace(/\\/g, '\\\\')
|
|
136
|
-
// then escape any backticks
|
|
137
|
-
// .replace(/`/g, '\\`');
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Safely write or update a file by replacing the region between
|
|
141
|
-
* startMarker and endMarker if both exist, otherwise overwrite the whole file.
|
|
142
|
-
*
|
|
143
|
-
* @param filePath Path to the target file
|
|
144
|
-
* @param fullContent The full text to write if markers are missing
|
|
145
|
-
* @param generated The text to inject between the markers
|
|
146
|
-
* @param startMarker Literal string marking the region start
|
|
147
|
-
* @param endMarker Literal string marking the region end
|
|
148
|
-
* @param overwrite If false and file exists, do nothing
|
|
149
|
-
*/
|
|
150
|
-
export function writeWithMarkers(filePath, fullContent, generated, startMarker, endMarker, overwrite) {
|
|
151
|
-
// If the file exists but we're *not* overwriting, skip entirely
|
|
152
|
-
if (existsSync(filePath) && !overwrite)
|
|
153
|
-
return;
|
|
154
|
-
if (existsSync(filePath)) {
|
|
155
|
-
const existing = readFileSync(filePath, 'utf-8');
|
|
156
|
-
// If both markers are present, do an in‐place replace
|
|
157
|
-
if (existing.includes(startMarker) && existing.includes(endMarker)) {
|
|
158
|
-
const escaped = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
159
|
-
const re = new RegExp(`${escaped(startMarker)}[\\s\\S]*?${escaped(endMarker)}`, 'm');
|
|
160
|
-
const updated = existing.replace(re, `${startMarker}\n${generated}\n${endMarker}`);
|
|
161
|
-
return writeFileSync(filePath, updated, 'utf-8');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// Otherwise write the full content (which itself can include the markers)
|
|
165
|
-
writeFileSync(filePath, fullContent, 'utf-8');
|
|
166
146
|
}
|
|
167
147
|
export function resolveStub(cfg, type, tableName) {
|
|
168
148
|
if (!cfg.stubDir)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/global.ts","../src/index.ts","../src/test.ts","../src/cli/index.ts","../src/cli/models.index.ts","../src/generator/utils.ts","../src/generator/migrator/prismatolaravelmigrationgenerator.ts","../src/generator/migrator/actions-map.ts","../src/generator/migrator/column-definition-types.ts","../src/generator/migrator/column-definition.ts","../src/generator/migrator/column-maps.ts","../src/generator/migrator/index.ts","../src/generator/migrator/migrationtypes.ts","../src/generator/migrator/rule-definition.ts","../src/generator/migrator/rules.ts","../src/generator/migrator/sort.ts","../src/generator/modeler/generator.ts","../src/generator/modeler/index.ts","../src/generator/modeler/types.ts","../src/printer/migrations.ts","../src/printer/models.ts"],"version":"5.7.2"}
|
|
1
|
+
{"root":["../src/global.ts","../src/index.ts","../src/test.ts","../src/cli/index.ts","../src/cli/models.index.ts","../src/diff-writer/backuppath.ts","../src/diff-writer/writer.ts","../src/generator/utils.ts","../src/generator/migrator/prismatolaravelmigrationgenerator.ts","../src/generator/migrator/actions-map.ts","../src/generator/migrator/column-definition-types.ts","../src/generator/migrator/column-definition.ts","../src/generator/migrator/column-maps.ts","../src/generator/migrator/index.ts","../src/generator/migrator/migrationtypes.ts","../src/generator/migrator/rule-definition.ts","../src/generator/migrator/rules.ts","../src/generator/migrator/sort.ts","../src/generator/modeler/generator.ts","../src/generator/modeler/index.ts","../src/generator/modeler/types.ts","../src/printer/migrations.ts","../src/printer/models.ts"],"version":"5.7.2"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-laravel-migrate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Generate laravel migrations and/or models using prisma files",
|
|
5
5
|
"bin": {
|
|
6
6
|
"prisma-laravel-migrations": "./dist/cli/index.js",
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
"@types/node": "^24.0.3",
|
|
44
44
|
"change-case": "^5.4.4",
|
|
45
45
|
"dayjs": "^1.11.13",
|
|
46
|
+
"diff3": "^0.0.4",
|
|
46
47
|
"jest": "^30.0.2",
|
|
48
|
+
"node-diff3": "^3.1.2",
|
|
47
49
|
"pluralize": "^8.0.0",
|
|
48
50
|
"prettier": "^3.5.3",
|
|
49
51
|
"ts-node": "^10.9.2",
|