prisma-laravel-migrate 3.0.5 → 3.0.6

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 CHANGED
@@ -1,8 +1,12 @@
1
1
  # Prisma Laravel Migrate
2
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.
3
+ A generator plugin that translates your **Prisma schema** into Laravel‑ready
4
+
5
+ * **Database Migrations**
6
+ * **Eloquent Models & PHP Enums**
7
+ * **TypeScript model & enum types**
8
+
9
+ Built in strict TypeScript with fully‑customisable stubs, grouping, smart merge updates, and an optional TS types generator.
6
10
 
7
11
  ---
8
12
 
@@ -14,7 +18,8 @@ npm install prisma-laravel-migrate --save-dev
14
18
  ```
15
19
 
16
20
  ---
17
- 🛠️ Configuration layers
21
+
22
+ ## 🛠️ Configuration layers
18
23
 
19
24
  The generators read options in **three tiers (highest → lowest)**:
20
25
 
@@ -43,54 +48,97 @@ module.exports = {
43
48
  output: {
44
49
  migrations: "database/migrations",
45
50
  models: "app/Models",
46
- enums: "app/Enums"
51
+ enums: "app/Enums",
47
52
  },
48
53
 
49
54
  /* -- per‑generator overrides ------------------------ */
50
55
  migrate: {
51
56
  groups: "./prisma/migrate-groups.js",
52
- rules : "./prisma/custom-rules.js"
57
+ rules : "./prisma/custom-rules.js",
53
58
  },
54
59
 
55
60
  modeler: {
56
61
  groups: [
57
- { stubFile: "audit.stub", tables: ["logs","audit_trails"] }
62
+ { stubFile: "audit.stub", tables: ["logs","audit_trails"] },
58
63
  ],
59
64
  outputEnumDir: "app/Enums",
60
65
  overwriteExisting: true,
61
- allowedPivotExtraFields: ["scope"]
62
- }
66
+ allowedPivotExtraFields: ["scope"],
67
+ // use awobaz/compoships for multi-column relations
68
+ awobaz: true,
69
+ },
70
+
71
+ /**
72
+ * NEW: TypeScript generator (models + enums → TS types)
73
+ */
74
+ types: {
75
+ // Where generated TS goes (default: "resources/ts/prisma")
76
+ outputDir: "resources/ts/prisma",
77
+
78
+ // Emit .d.ts instead of .ts when true
79
+ declaration: false,
80
+
81
+ // "interface" | "type" for model declarations
82
+ shape: "interface",
83
+
84
+ // Optional module wrapper for the main types file
85
+ // declare module "database/prisma" { ... }
86
+ moduleName: "database/prisma",
87
+
88
+ // Example scalar mapping overrides
89
+ scalarMap: {
90
+ // BigInt: "bigint",
91
+ // Decimal: "string",
92
+ // Json: "unknown",
93
+ },
94
+
95
+ // If true: nullable fields become optional properties
96
+ nullableAsOptional: false,
97
+
98
+ // If true: lists become ReadonlyArray<T>
99
+ readonlyArrays: false,
100
+
101
+ // Name decoration for interfaces/types
102
+ namePrefix: "",
103
+ nameSuffix: "",
104
+ },
63
105
  };
64
106
  ```
65
107
 
66
- >`Updated! I added a new section in the canvas—“Related Generator Options (Formatting & Compoships)”—covering:
67
- prettier (per-generator): what it does, defaults, config snippets, and a typings excerpt.
68
- modeler.awobaz: what enabling Compoships changes, config example, and a reminder to composer require it.`
108
+ > There is also a small section in the codebase labelled
109
+ > **“Related Generator Options (Formatting & Compoships)”** covering:
110
+ >
111
+ > * `prettier` (per‑generator): enables formatting via Prettier for
112
+ > PHP/TS outputs during merge.
113
+ > * `modeler.awobaz`: whether to emit Compoships‑aware relationships
114
+ > (requires `awobaz/compoships` in your Laravel app).
115
+
116
+ ---
69
117
 
70
118
  <details>
71
119
  <summary>Type reference</summary>
72
120
 
73
121
  ```ts
74
122
  export interface Rule {
75
- test(
76
- def: ColumnDefinition,
77
- allDefs: ColumnDefinition[],
78
- dmmf: DMMF.Document
79
- ): boolean;
80
- render(
81
- def: ColumnDefinition,
82
- allDefs: ColumnDefinition[],
83
- dmmf: DMMF.Document
84
- ): Render;
123
+ test(
124
+ def: ColumnDefinition,
125
+ allDefs: ColumnDefinition[],
126
+ dmmf: DMMF.Document,
127
+ ): boolean;
128
+ render(
129
+ def: ColumnDefinition,
130
+ allDefs: ColumnDefinition[],
131
+ dmmf: DMMF.Document,
132
+ ): Render;
85
133
  }
86
134
 
87
135
  /* ------------------------------------------------------------
88
136
  * Re-usable stub-group description
89
137
  * ---------------------------------------------------------- */
90
138
  export interface StubGroupConfig extends FlexibleStubGroup {
91
- /** Path relative to stubDir/<type>/ (e.g. "auth.stub") */
92
- stubFile: string;
93
- tables: string[]; // ["users","accounts",…] or enum names
139
+ /** Path relative to stubDir/<type>/ (e.g. "auth.stub") */
140
+ stubFile: string;
141
+ tables: string[]; // ["users","accounts",…] or enum names
94
142
  }
95
143
 
96
144
  /**
@@ -98,20 +146,20 @@ export interface StubGroupConfig extends FlexibleStubGroup {
98
146
  * Supply EITHER `tables` *or* (`include` / `exclude` / `pattern`).
99
147
  */
100
148
  interface FlexibleStubGroup {
101
- /** Path relative to stubDir/<type>/, e.g. "auth.stub" */
102
- stubFile: string;
149
+ /** Path relative to stubDir/<type>/, e.g. "auth.stub" */
150
+ stubFile: string;
103
151
 
104
- /** Old style - explicit white-list */
105
- tables?: string[];
152
+ /** Old style - explicit white-list */
153
+ tables?: string[];
106
154
 
107
- /** New style – include list ( '*' means “all tables” ) */
108
- include?: string[] | '*';
155
+ /** New style – include list ( '*' means “all tables” ) */
156
+ include?: string[] | '*';
109
157
 
110
- /** New style – blacklist applied after include / pattern */
111
- exclude?: string[];
158
+ /** New style – blacklist applied after include / pattern */
159
+ exclude?: string[];
112
160
 
113
- /** New style – RegExp OR minimatch glob(s) */
114
- pattern?: RegExp | string | Array<RegExp | string>;
161
+ /** New style – RegExp OR minimatch glob(s) */
162
+ pattern?: RegExp | string | Array<RegExp | string>;
115
163
  }
116
164
 
117
165
 
@@ -119,81 +167,124 @@ interface FlexibleStubGroup {
119
167
  * Per-generator overrides (migration / modeler)
120
168
  * ---------------------------------------------------------- */
121
169
  export interface LaravelGeneratorConfig {
122
- tablePrefix?: string;
123
- tableSuffix?: string;
124
-
125
- /** Override stubDir only for this generator */
126
- stubDir?: string;
127
-
128
- /** Where the generated PHP goes (overrides block) */
129
- outputDir?: string;
130
-
131
- overwriteExisting?: boolean;
132
- /** Allow formatting with prettier */
133
- prettier?: boolean;
134
- /**
135
- * Stub grouping:
136
- * • string – path to a JS module exporting StubGroupConfig[]
137
- * • array – the group definitions themselves
138
- */
139
- groups?: string | StubGroupConfig[];
140
-
141
- /** Skip file emission for *this* generator only */
142
- noEmit?: boolean;
143
-
144
- /**Default namespace for local imports */
145
- namespace?: "App\\"
170
+ tablePrefix?: string;
171
+ tableSuffix?: string;
172
+
173
+ /** Override stubDir only for this generator */
174
+ stubDir?: string;
175
+
176
+ /** Where the generated PHP goes (overrides block) */
177
+ outputDir?: string;
178
+
179
+ overwriteExisting?: boolean;
180
+ /** Allow formatting with prettier */
181
+ prettier?: boolean;
182
+ /**
183
+ * Stub grouping:
184
+ * • string – path to a JS module exporting StubGroupConfig[]
185
+ * • array – the group definitions themselves
186
+ */
187
+ groups?: string | StubGroupConfig[];
188
+
189
+ /** Skip file emission for *this* generator only */
190
+ noEmit?: boolean;
191
+
192
+ /** Default namespace for local imports */
193
+ namespace?: "App\\";
146
194
  }
147
195
 
148
196
  /* ------------------------------------------------------------
149
197
  * Top-level shared config (visible to all generators)
150
198
  * ---------------------------------------------------------- */
151
199
  export interface LaravelSharedConfig {
152
- /** Table name decoration */
153
- tablePrefix?: string;
154
- tableSuffix?: string;
155
-
156
- /** Default stub root (migration/, model/, enum/) */
157
- stubDir?: string;
158
-
159
- /** Global “don’t write files” switch */
160
- noEmit?: boolean;
161
-
162
- /** Override default output folders */
163
- output?: {
164
- migrations?: string;
165
- models?: string;
166
- enums?: string;
167
- };
168
-
169
- /** Per-generator fine-tuning */
170
- migrate?: Partial<MigratorConfigOverride>;
171
- modeler?: Partial<ModelConfigOverride>;
200
+ /** Table name decoration */
201
+ tablePrefix?: string;
202
+ tableSuffix?: string;
203
+
204
+ /** Default stub root (migration/, model/, enum/, ts/) */
205
+ stubDir?: string;
206
+
207
+ /** Global “don’t write files” switch */
208
+ noEmit?: boolean;
209
+
210
+ /** Override default output folders (PHP side) */
211
+ output?: {
212
+ migrations?: string;
213
+ models?: string;
214
+ enums?: string;
215
+ };
216
+
217
+ /** Per-generator fine-tuning */
218
+ migrate?: Partial<MigratorConfigOverride>;
219
+ modeler?: Partial<ModelConfigOverride>;
220
+
221
+ /** NEW: TypeScript generator overrides */
222
+ types?: Partial<TypesConfigOverride>;
172
223
  }
173
224
 
174
225
 
175
226
  /* --- Migrator-specific extra keys ---------------------------------------- */
176
227
  export interface MigratorConfigOverride extends LaravelGeneratorConfig {
177
- /**
178
- * Custom migration rules:
179
- * • string – path to JS module exporting Rule[]
180
- * • Rule[] – rules array inline
181
- */
182
- rules?: string | Rule[];
183
- stubPath?: string;
184
- defaultMaps?: DefaultMaps
228
+ /**
229
+ * Custom migration rules:
230
+ * • string – path to JS module exporting Rule[]
231
+ * • Rule[] – rules array inline
232
+ */
233
+ rules?: string | Rule[];
234
+ stubPath?: string;
235
+ defaultMaps?: DefaultMaps;
185
236
  }
186
237
 
187
238
 
188
239
  export interface ModelConfigOverride extends LaravelGeneratorConfig {
189
- modelStubPath?: string;
190
- enumStubPath?: string;
191
- /** Extra folder for enums (modeler only) */
192
- outputEnumDir?: string;
193
- /** use awobaz/compoships */
194
- awobaz?: boolean;
195
- /** Extra fields allowed on pivot models */
196
- allowedPivotExtraFields?: string[];
240
+ modelStubPath?: string;
241
+ enumStubPath?: string;
242
+ /** Extra folder for enums (modeler only) */
243
+ outputEnumDir?: string;
244
+ /** use awobaz/compoships */
245
+ awobaz?: boolean;
246
+ /** Extra fields allowed on pivot models */
247
+ allowedPivotExtraFields?: string[];
248
+ }
249
+
250
+ /* ------------------------------------------------------------
251
+ * TypeScript generator overrides
252
+ * ---------------------------------------------------------- */
253
+ export interface TypesConfigOverride extends LaravelGeneratorConfig {
254
+ /** Where generated TS types should be written, e.g. "resources/ts/prisma" */
255
+ outputDir?: string;
256
+
257
+ /** Emit `.d.ts` declaration files instead of `.ts` source files. */
258
+ declaration?: boolean; // default: false → .ts
259
+
260
+ /** Use `interface` or `type` for model declarations. */
261
+ shape?: "interface" | "type"; // default: "interface"
262
+
263
+ /**
264
+ * Map Prisma scalar types → TypeScript types.
265
+ * Keys are Prisma scalar names ("Int", "BigInt", "Decimal", "Json", "DateTime", etc.).
266
+ */
267
+ scalarMap?: Record<string, string>;
268
+
269
+ /** If true, nullable fields become optional: `foo?: string` instead of `foo: string | null`. */
270
+ nullableAsOptional?: boolean;
271
+
272
+ /** If true, lists are emitted as `ReadonlyArray<T>` instead of `T[]`. */
273
+ readonlyArrays?: boolean;
274
+
275
+ /** Optional name decoration for generated types/interfaces. */
276
+ namePrefix?: string;
277
+ nameSuffix?: string;
278
+
279
+ /**
280
+ * Optional root module/namespace hint.
281
+ * When set, the main types file is wrapped in:
282
+ * declare module "…" { ... }
283
+ */
284
+ moduleName?: string;
285
+
286
+ /** Where models should import enums from (default: "./enums"). */
287
+ enumImportFrom?: string;
197
288
  }
198
289
  ```
199
290
 
@@ -214,35 +305,47 @@ generator migrate {
214
305
  generator modeler {
215
306
  provider = "prisma-laravel-models"
216
307
  stubDir = "./prisma/stubs"
308
+ }
309
+
310
+ // NEW: TypeScript types generator
311
+ generator types {
312
+ provider = "prisma-laravel-types"
313
+ stubDir = "./prisma/stubs"
314
+ }
217
315
  ```
218
316
 
219
317
  ### Field Reference
220
318
 
221
- | Key | Notes |
222
- | ---------------------- | --------------------------------------------------------------------------------------------------------- |
223
- | `outputDir / output` | Destination folder (`outputDir` overrides `output`). |
224
- | `outputEnumDir` | (modeler) directory for generated enum classes. |
225
- | `stubDir` | Root stub folder (`migration/`, `model/`, `enum/`). |
226
- | `tablePrefix` | String prepended to every generated **physical** table name. |
227
- | `tableSuffix` | String appended to every generated **physical** table name. |
228
- | `groups` | JS module *or* inline array that maps stub files to table groups. |
229
- | `noEmit` | If `true`, generator parses and validates but **does not write** any files (dry-run / CI mode). |
319
+ | Key | Notes | |
320
+ | -------------------- | ----------------------------------------------------------------------------------------------- | ------ |
321
+ | `outputDir / output` | Destination folder (`outputDir` overrides `output`) — PHP and TS. | |
322
+ | `outputEnumDir` | (modeler) directory for generated PHP enum classes. | |
323
+ | `stubDir` | Root stub folder (`migration/`, `model/`, `enum/`, `ts/`). | |
324
+ | `tablePrefix` | String prepended to every generated **physical** table name. | |
325
+ | `tableSuffix` | String appended to every generated **physical** table name. | |
326
+ | `groups` | JS module *or* inline array that maps stub files to table/model/enum/TS groups. | |
327
+ | `noEmit` | If `true`, generator parses and validates but **does not write** any files (dry-run / CI mode). | |
328
+ | `prettier` | Per-generator toggle for formatting (PHP or TS) before 3‑way merge. | |
329
+ | `awobaz` | (modeler) Emit Compoships‑aware relationships; requires `awobaz/compoships`. | |
330
+ | `scalarMap` | (types) Map Prisma scalars → TS type names. | |
331
+ | `nullableAsOptional` | (types) Emit `foo?: T` for nullable fields instead of `foo: T | null`. |
332
+ | `readonlyArrays` | (types) Emit `ReadonlyArray<T>` instead of `T[]`. | |
333
+ | `moduleName` | (types) Wrap main types output in `declare module "…" {}`. | |
230
334
 
231
335
  ---
232
336
 
233
-
234
337
  ## 🔀 `groups` – Stub Grouping (v2)
235
338
 
236
- A **group** links one stub file to _many_ tables (or enums).
339
+ A **group** links one stub file to *many* tables (or enums / TS variants).
237
340
  From v2 you can keep the old **`tables: [...]`** list **or** use the new, more
238
341
  expressive selectors:
239
342
 
240
- | Key | Type / Example | Meaning |
241
- |--------------|----------------------------------------|--------------------------------------------------------------|
242
- | `tables` | `["users","accounts"]` | Classic explicit list – **exact names** only |
243
- | `include` | `["audit_*","logs"]` **or** `"*"` | White‑list using globs or `'*'` (all) |
244
- | `exclude` | `["*_archive","failed_jobs"]` | Black‑list applied *after* include / pattern |
245
- | `pattern` | `/^temp_/` or `"report_*"` | `RegExp` **or** glob(s) – match if **any** hits |
343
+ | Key | Type / Example | Meaning |
344
+ | --------- | --------------------------------- | ----------------------------------------------- |
345
+ | `tables` | `["users","accounts"]` | Classic explicit list – **exact names** only |
346
+ | `include` | `["audit_*","logs"]` **or** `"*"` | White‑list using globs or `'*'` (all) |
347
+ | `exclude` | `["*_archive","failed_jobs"]` | Black‑list applied *after* include / pattern |
348
+ | `pattern` | `/^temp_/` or `"report_*"` | `RegExp` **or** glob(s) – match if **any** hits |
246
349
 
247
350
  > Only one of **`tables`** **or** the new selector trio is required.
248
351
 
@@ -264,34 +367,36 @@ module.exports = [
264
367
  // 1. legacy explicit list
265
368
  {
266
369
  stubFile: "auth.stub", // stubs/migration/auth.stub
267
- tables: ["users","accounts","password_resets"]
370
+ tables: ["users","accounts","password_resets"],
268
371
  },
269
372
 
270
373
  // 2. regex + blacklist
271
374
  {
272
375
  stubFile: "audit.stub",
273
376
  pattern : /^audit_/,
274
- exclude : ["audit_archive"]
377
+ exclude : ["audit_archive"],
275
378
  },
276
379
 
277
380
  // 3. catch‑all
278
381
  {
279
382
  stubFile: "catch-all.stub",
280
383
  include : "*",
281
- exclude : ["failed_jobs","migrations"]
282
- }
384
+ exclude : ["failed_jobs","migrations"],
385
+ },
283
386
  ];
284
387
  ```
285
388
 
286
389
  ### Resolution order
287
390
 
288
- 1. **Table‑specific** — `stubs/<type>/<table>.stub`
289
- 2. **First matching group** (objects are checked top‑to‑bottom)
391
+ 1. **Table‑specific** — `stubs/<type>/<table>.stub`
392
+ 2. **First matching group** (objects are checked top‑to‑bottom)
290
393
  3. **Default** — `stubs/<type>/index.stub`
291
394
 
292
395
  > If a group’s `stubFile` does not exist it is skipped, so you may leave unused
293
396
  > groups in place without breaking the build.
294
397
 
398
+ ---
399
+
295
400
  ## 📁 Stub Folder Layout
296
401
 
297
402
  ```text
@@ -299,21 +404,26 @@ prisma/stubs/
299
404
  ├── migration/index.stub
300
405
  ├── model/index.stub
301
406
  ├── model/simple-model.stub
302
- └── enum/index.stub
407
+ ├── enum/index.stub
408
+ └── ts/index.stub
303
409
  ```
304
410
 
305
- Add table‑specific overrides at
306
- `stubs/<type>/<table>.stub` (e.g. `stubs/model/users.stub`).
411
+ Add table‑specific overrides at
412
+ `stubs/<type>/<table>.stub` (e.g. `stubs/model/users.stub`, `stubs/ts/Account.stub`).
413
+
414
+ * `migration/` and `model/` stubs are **PHP** template literals.
415
+ * `enum/` stubs are **PHP** enum templates.
416
+ * `ts/` stubs are **JavaScript template literals** evaluated by the TS printer.
307
417
 
308
418
  ---
309
419
 
310
420
  ## 🔧 CLI Commands
311
421
 
312
- | Command | Purpose |
313
- | --- | --- |
314
- | `init` | Inject generator blocks & scaffold stub folders |
315
- | `customize` | Create per-table stub overrides |
316
- | `gen` | Run `prisma generate` then Laravel generators |
422
+ | Command | Purpose |
423
+ | ----------- | -------------------------------------------------- |
424
+ | `init` | Inject generator blocks & scaffold stubs |
425
+ | `customize` | Create per-table stub overrides |
426
+ | `gen` | Run `prisma generate` then Laravel + TS generators |
317
427
 
318
428
  ### init
319
429
 
@@ -324,15 +434,18 @@ npx prisma-laravel-cli init --schema=prisma/schema.prisma
324
434
  ### customize
325
435
 
326
436
  ```bash
327
- npx prisma-laravel-cli customize -t migration,model -n users,accounts --force
437
+ npx prisma-laravel-cli customize \
438
+ -t migration,model,ts \
439
+ -n users,accounts \
440
+ --force
328
441
  ```
329
442
 
330
- | Flag | Description |
331
- | --- | --- |
332
- | `-t, --type` | **Required.** Stub types (`migration`, `model`, `enum`). `enum` may not mix. |
333
- | `-n, --names` | **Required.** Table or enum names (`users,accounts`). |
334
- | `--force` | Overwrite existing stub files. |
335
- | `--config` | Alternate CLI config path. |
443
+ | Flag | Description |
444
+ | ------------- | ---------------------------------------------------------------------------------------------- |
445
+ | `-t, --types` | **Required.** Stub types (`migration`, `model`, `enum`, `ts`). `enum` may not mix with others. |
446
+ | `-n, --names` | **Required.** Table or enum names (`users,accounts`). |
447
+ | `--force` | Overwrite existing stub files. |
448
+ | `--config` | Alternate CLI config path. |
336
449
 
337
450
  ### gen
338
451
 
@@ -346,17 +459,22 @@ npx prisma-laravel-cli gen --config=prisma/laravel.config.js --skipGenerate
346
459
 
347
460
  ```js
348
461
  module.exports = {
349
- migrator: {
462
+ migrate: {
350
463
  outputDir: "database/migrations",
351
464
  stubDir: "prisma/stubs",
352
- groups: "./prisma/group-stubs.js"
465
+ groups: "./prisma/group-stubs.js",
353
466
  },
354
467
  modeler: {
355
468
  outputDir: "app/Models",
356
469
  outputEnumDir: "app/Enums",
357
470
  stubDir: "prisma/stubs",
358
- groups: "./prisma/group-stubs.js"
359
- }
471
+ groups: "./prisma/group-stubs.js",
472
+ },
473
+ types: {
474
+ outputDir: "resources/ts/prisma",
475
+ stubDir: "prisma/stubs",
476
+ moduleName: "database/prisma",
477
+ },
360
478
  };
361
479
  ```
362
480
 
@@ -366,56 +484,67 @@ module.exports = {
366
484
 
367
485
  1. Generator builds a **full new file** from your schema & stubs.
368
486
  2. Performs a **git‑style 3‑way merge** (using `node-diff3`):
369
- - **base** = last generator output (`.prisma-laravel/backups/...`)
370
- - **ours** = file on disk (user edits)
371
- - **theirs** = freshly generated file
372
- 3. Non‑conflicting changes merge automatically; conflicts are wrapped with
373
- `<<<<<<<`, `=======`, `>>>>>>>`.
374
- 4. New `use …;` imports are merged, duplicates skipped.
487
+
488
+ * **base** = last generator output (`.prisma-laravel/backups/...`)
489
+ * **ours** = file on disk (user edits)
490
+ * **theirs** = freshly generated file
491
+ 3. Non‑conflicting changes merge automatically; conflicts are wrapped with
492
+ `<<<<<<<`, `=======`, `>>>>>>>`.
493
+ 4. New imports are merged, duplicates skipped.
375
494
  5. Baseline copy is updated in the backups folder.
376
495
 
496
+ The same merge pipeline is used for:
497
+
498
+ * PHP migrations (`type: "migrator"` in the diff writer),
499
+ * PHP models/enums (`type: "model"`),
500
+ * TypeScript outputs (`type: "ts"`, using the `typescript` Prettier parser).
501
+
377
502
  Delete the marker block **and** set `noEmit = true` to stop updates for a file.
378
503
 
379
504
  ---
380
505
 
381
506
  ## ✨ Stub Customisation Notes
382
507
 
383
- Stubs are **JavaScript template literals**. Escape \` and \${ } if you want them literally.
508
+ Stubs are **JavaScript template literals**. Escape ` and ${ } if you want them literally.
384
509
 
385
- > **Fully custom model stubs**
510
+ > **Fully custom model stubs**
386
511
  > If you remove the `${content}` placeholder **and** the marker block, the
387
- > generator leaves the file untouched.
512
+ > generator leaves the file untouched.
388
513
  > Keep the markers if you want automated updates but customised surroundings.
389
514
 
390
515
  ---
391
516
 
392
517
  ## 📑 Default Stub Templates
393
518
 
519
+ ### PHP Enum `index.stub`
520
+
394
521
  <details>
395
- <summary>Enum <code>index.stub</code></summary>
522
+ <summary>Enum <code>index.stub</code> (PHP)</summary>
396
523
 
397
524
  ```php
398
525
  <?php
399
526
 
400
- namespace App\\Enums;
527
+ namespace App\Enums;
401
528
 
402
529
  enum ${enumDef.name}: string
403
530
  {
404
- ${enumDef.values.map(v => ` case ${v} = '${v}';`).join('\\n')}
531
+ ${enumDef.values.map(v => ` case ${v} = '${v}';`).join('\n')}
405
532
  }
406
533
  ```
407
534
 
408
535
  </details>
409
536
 
537
+ ### PHP Migration `index.stub`
538
+
410
539
  <details>
411
540
  <summary>Migration <code>index.stub</code></summary>
412
541
 
413
542
  ```php
414
543
  <?php
415
544
 
416
- use Illuminate\\Database\\Migrations\\Migration;
417
- use Illuminate\\Database\\Schema\\Blueprint;
418
- use Illuminate\\Support\\Facades\\Schema;
545
+ use Illuminate\Database\Migrations\Migration;
546
+ use Illuminate\Database\Schema\Blueprint;
547
+ use Illuminate\Support\Facades\Schema;
419
548
 
420
549
  return new class extends Migration
421
550
  {
@@ -435,9 +564,35 @@ return new class extends Migration
435
564
 
436
565
  </details>
437
566
 
567
+ ### TypeScript module `index.stub`
568
+
569
+ <details>
570
+ <summary>TS <code>ts/index.stub</code> (module-level)</summary>
571
+
572
+ ```ts
573
+ ${imports}
574
+
575
+ ${body}
576
+ ```
577
+
578
+ * `imports` → merged import block for all non-stubbed models (including enums).
579
+ * `body` → full `declare module "…" { … }` wrapper generated by the TS printer.
580
+ * `content` (exposed to the stub function) is just the inner interfaces/types
581
+ without the `declare module` wrapper, if you need it for advanced patterns.
582
+
583
+ Per-model TS stubs under `stubs/ts/<Model>.stub` receive:
584
+
585
+ ```ts
586
+ (model, imports, content, body, moduleName) => string
587
+ ```
588
+
589
+ and are free to ignore or wrap the default `body` however they like.
590
+
591
+ </details>
592
+
438
593
  ---
439
594
 
440
- ## 🏗️ Complex Model Stub Example
595
+ ## 🏗️ Complex Model Stub Example (PHP)
441
596
 
442
597
  <details>
443
598
  <summary>Expand stub</summary>
@@ -445,11 +600,11 @@ return new class extends Migration
445
600
  ```php
446
601
  <?php
447
602
 
448
- namespace App\\Models;
603
+ namespace App\Models;
449
604
 
450
605
  ${model.imports}
451
- use Illuminate\\Database\\Eloquent\\Model;
452
- use Illuminate\\Database\\Eloquent\\Relations\\{ BelongsTo, HasMany, BelongsToMany };
606
+ use Illuminate\Database\Eloquent\Model;
607
+ use Illuminate\Database\Eloquent\Relations\{ BelongsTo, HasMany, BelongsToMany };
453
608
 
454
609
  class ${model.className} extends Model
455
610
  {
@@ -457,25 +612,25 @@ class ${model.className} extends Model
457
612
 
458
613
  /* Mass Assignment */
459
614
  protected $fillable = [
460
- ${model.properties.filter(p => p.fillable).map(p => ` '${p.name}',`).join('\\n')}
615
+ ${model.properties.filter(p => p.fillable).map(p => ` '${p.name}',`).join('\n')}
461
616
  ];
462
617
  protected $guarded = [
463
- ${(model.guarded ?? []).map(n => ` '${n}',`).join('\\n')}
618
+ ${(model.guarded ?? []).map(n => ` '${n}',`).join('\n')}
464
619
  ];
465
620
 
466
621
  /* Hidden & Casts */
467
622
  protected $hidden = [
468
- ${model.properties.filter(p => p.hidden).map(p => ` '${p.name}',`).join('\\n')}
623
+ ${model.properties.filter(p => p.hidden).map(p => ` '${p.name}',`).join('\n')}
469
624
  ];
470
625
  protected $casts = [
471
- ${model.properties.filter(p => p.cast).map(p => ` '${p.name}' => '${p.cast}',`).join('\\n')}
472
- ${model.properties.filter(p => p.enumRef).map(p => ` '${p.name}' => ${p.enumRef}::class,`).join('\\n')}
626
+ ${model.properties.filter(p => p.cast).map(p => ` '${p.name}' => '${p.cast}',`).join('\n')}
627
+ ${model.properties.filter(p => p.enumRef).map(p => ` '${p.name}' => ${p.enumRef}::class,`).join('\n')}
473
628
  ];
474
629
 
475
630
  /* Interfaces metadata */
476
631
  public array $interfaces = [
477
632
  ${Object.entries(model.interfaces).map(([k,i]) =>
478
- ` '${k}' => {${i.import ? ` import: '${i.import}',` : ''} type: '${i.type}' },`).join('\\n')}
633
+ ` '${k}' => {${i.import ? ` import: '${i.import}',` : ''} type: '${i.type}' },`).join('\n')}
479
634
  ];
480
635
  // This structure is useful for packages like fumeapp/modeltyper which
481
636
  // read interface metadata to build TypeScript helpers.
@@ -483,9 +638,9 @@ ${Object.entries(model.interfaces).map(([k,i]) =>
483
638
  /* Relationships */
484
639
  ${model.relations.map(r => {
485
640
  const args = [r.modelClass, r.foreignKey ? `'${r.foreignKey}'` : '', r.localKey ? `'${r.localKey}'` : ''].filter(Boolean).join(', ');
486
- return ` public function ${r.name}(): ${r.type.charAt(0).toUpperCase()+r.type.slice(1)}\\n {\\n return $this->${r.type}(${args});\\n }`;
487
- }).join('\\n\\n')}
488
- // Or
641
+ return ` public function ${r.name}(): ${r.type.charAt(0).toUpperCase()+r.type.slice(1)}\n {\n return $this->${r.type}(${args});\n }`;
642
+ }).join('\n\n')}
643
+ // Or
489
644
  ${relationships}
490
645
 
491
646
  ${content}
@@ -506,7 +661,6 @@ protected $casts = [
506
661
 
507
662
  ---
508
663
 
509
-
510
664
  ## 🧩 Custom Migration Rules
511
665
 
512
666
  Point the generator’s `rules` field to a JS file exporting an **array** of
@@ -543,7 +697,7 @@ module.exports = [
543
697
 
544
698
  **Rule execution order**
545
699
 
546
- 1. Built‑in rules
700
+ 1. Built‑in rules
547
701
  2. Custom rules (executed in array order)
548
702
 
549
703
  ---
@@ -568,7 +722,7 @@ export interface ColumnDefinition extends DMMF.Field {
568
722
  relationship?: {
569
723
  on: string;
570
724
  references?: string[];
571
- fields: string[]
725
+ fields: string[];
572
726
  onDelete?: string;
573
727
  onUpdate?: string;
574
728
  };
@@ -586,9 +740,8 @@ def.migrationType // mapped Laravel builder name
586
740
  def.isId
587
741
  ```
588
742
 
589
- 📝 **Prisma DMMF docs:**
590
- https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md
591
-
743
+ 📝 **Prisma DMMF docs:**
744
+ [https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md](https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md)
592
745
 
593
746
  ---
594
747
 
@@ -603,6 +756,13 @@ You can attach them either:
603
756
 
604
757
  > This document focuses on the **new & updated directives** and how they interact with existing ones like `@fillable`, `@hidden`, `@guarded`, `@cast{…}`, `@type{…}`, `@with`, `@trait:…`, `@extend:…`, `@implements:…`, `@observer:…`, `@factory:…`, `@touch{…}`, `@appends{…}`.
605
758
 
759
+ The same directives are visible to the **PHP generators** (migrator + modeler)
760
+ **and** to the **TS types generator**, which uses them to:
761
+
762
+ * apply `@type{…}` overrides for TS field types/imports,
763
+ * honour `@with` when deciding whether relation properties are optional, and
764
+ * strip directive noise from generated JSDoc/docblocks.
765
+
606
766
  ---
607
767
 
608
768
  ## What’s New
@@ -615,21 +775,21 @@ You can attach them either:
615
775
 
616
776
  ## Summary of Directives
617
777
 
618
- | Directive | Scope | Purpose |
619
- | ------------------------------------------------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------- |
620
- | `@fillable` | Field **or** `@fillable{…}` on model | Adds field(s) to `$fillable`. |
621
- | `@hidden` | Field **or** `@hidden{…}` on model | Adds field(s) to `$hidden`. |
622
- | `@guarded` | Field **or** `@guarded{…}` on model | Adds field(s) to `$guarded`. |
623
- | `@cast{…}` | Field | Adds a cast entry to `$casts`. |
624
- | `@type{ import:'…', type:'…' }` | Field | Exposes a PHP/interface type hint for downstream tooling. |
625
- | `@with` / `@with(a,b,…)` | Field / Model | Marks relations to eager-load via `$with`. |
626
- | `@trait:…` `@extend:…` `@implements:…` `@observer:…` `@factory:…` `@touch{…}` `@appends{…}` | Model | Class customization & extras (traits, parents, observers, factories, touches, appends). |
627
- | `@local` | Relation Field | Skip generating that **specific relation method** on the model. Replaces `@ignore`. |
628
- | `@silent` | Model / Enum | Do **not** emit files for this entity (model + migration / enum). |
629
- | `@morph(…)` | Model | Declare owner-side polymorphic relations; child-side `morphTo` is auto-detected. |
630
- | `@pivot` / `@pivot(a,b,…)` | Pivot **model** and/or scalar **fields**| Explicitly mark extra pivot columns to include in generated `withPivot(…)` chains. |
631
- | `@withTimestamps` | Pivot **model** / relation **field** | Instructs the generator to append `->withTimestamps()` on the relation definition. |
632
- | `@pivotAlias(name)` | Pivot **model** | Sets the pivot attribute alias; generator should add `->as('name')` on the relation. |
778
+ | Directive | Scope | Purpose |
779
+ | ------------------------------------------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------- |
780
+ | `@fillable` | Field **or** `@fillable{…}` on model | Adds field(s) to `$fillable`. |
781
+ | `@hidden` | Field **or** `@hidden{…}` on model | Adds field(s) to `$hidden`. |
782
+ | `@guarded` | Field **or** `@guarded{…}` on model | Adds field(s) to `$guarded`. |
783
+ | `@cast{…}` | Field | Adds a cast entry to `$casts`. |
784
+ | `@type{ import:'…', type:'…' }` | Field | Exposes a PHP/interface/TS type hint for downstream tooling. |
785
+ | `@with` / `@with(a,b,…)` | Field / Model | Marks relations to eager-load via `$with` and as non‑optional in TS types. |
786
+ | `@trait:…` `@extend:…` `@implements:…` `@observer:…` `@factory:…` `@touch{…}` `@appends{…}` | Model | Class customization & extras (traits, parents, observers, factories, touches, appends). |
787
+ | `@local` | Relation Field | Skip generating that **specific relation method** on the model. Replaces `@ignore`. |
788
+ | `@silent` | Model / Enum | Do **not** emit files for this entity (model + migration / enum). |
789
+ | `@morph(…)` | Model | Declare owner-side polymorphic relations; child-side `morphTo` is auto-detected. |
790
+ | `@pivot` / `@pivot(a,b,…)` | Pivot **model** and/or scalar **fields** | Explicitly mark extra pivot columns to include in generated `withPivot(…)` chains. |
791
+ | `@withTimestamps` | Pivot **model** / relation **field** | Instructs the generator to append `->withTimestamps()` on the relation definition. |
792
+ | `@pivotAlias(name)` | Pivot **model** | Sets the pivot attribute alias; generator should add `->as('name')` on the relation. |
633
793
 
634
794
  > **Syntax options**
635
795
  > • Inline: `balance Decimal /// @fillable @cast{decimal:2}`
@@ -646,10 +806,12 @@ You can attach them either:
646
806
  > `/// @touch{company,profile}`
647
807
  > `/// @appends{full_name,age}`
648
808
 
649
- > **Note:** Directives like `@fillable`, `@hidden`, `@guarded`, `@with`, `@touch`, and `@appends` now support **all of the following syntaxes**:
650
- > - `/// @fillable{name,balance}`
651
- > - `/// @fillable(name,balance)`
652
- > - `/// @fillable: name,balance`
809
+ > **Note:** Directives like `@fillable`, `@hidden`, `@guarded`, `@with`, `@touch`, and `@appends` support **all** of the following syntaxes:
810
+ >
811
+ > * `/// @fillable{name,balance}`
812
+ > * `/// @fillable(name,balance)`
813
+ > * `/// @fillable: name,balance`
814
+
653
815
  ---
654
816
 
655
817
  ## `@local` — Skip a Single Relation Method (replaces `@ignore`)
@@ -672,7 +834,7 @@ model Account {
672
834
  }
673
835
  ```
674
836
 
675
- **Effect:** the `user()` method is *not* written to `Account.php`. If scope includes Migrator, the migration also skips generating the FK/constraint. Other methods remain unaffected.
837
+ **Effect:** the `user()` method is *not* written to `Account.php`. If scope includes Migrator, the migration also skips generating the FK/constraint. Other methods remain unaffected. TS types still include the `user?: User | null` navigation property unless you override it with `@type`.
676
838
 
677
839
  ---
678
840
 
@@ -696,11 +858,10 @@ model AuditTrail {
696
858
  }
697
859
  ```
698
860
 
699
- **Effect:** no Eloquent model and/or no migration file are emitted for `AuditTrail` depending on the scope. For enums, no PHP enum is emitted.
861
+ **Effect:** no Eloquent model and/or no migration file are emitted for `AuditTrail` depending on the scope. For enums, no PHP enum is emitted. The TS generator can still expose types for `AuditTrail` if you wish (or you can add an additional TS-side filter later).
700
862
 
701
863
  ---
702
864
 
703
-
704
865
  ## Polymorphic Relations
705
866
 
706
867
  ### Auto-Detected Child Side: `morphTo`
@@ -716,6 +877,16 @@ public function commentable()
716
877
 
717
878
  No directive is required for `morphTo`.
718
879
 
880
+ On the TS side, the generator also detects these pairs and adds a
881
+ corresponding **appended property** like:
882
+
883
+ ```ts
884
+ commentable?: any;
885
+ ```
886
+
887
+ which you can refine via `@appends{commentable: Comment}` or project‑specific
888
+ TS overrides.
889
+
719
890
  ### Owner Side via `@morph(…)`
720
891
 
721
892
  Add one or more `@morph(...)` directives to the **owner model’s** docblock to generate Laravel morph methods. Parentheses and quotes inside `raw:"…"` are supported.
@@ -758,8 +929,8 @@ model User {
758
929
  /// Owner: Video → morphToMany(Tag::class, 'taggable', 'taggables')
759
930
  /// @morph(name: taggable, type: to many, model: Tag, table:"taggables")
760
931
  model Video {
761
- id Int @id @default(autoincrement())
762
- url String
932
+ id Int @id @default(autoincrement())
933
+ url String
763
934
  }
764
935
  ```
765
936
 
@@ -809,7 +980,7 @@ model Image {
809
980
  }
810
981
  ```
811
982
 
812
- **C) Polymorphic M\:N (`morphToMany` / `morphedByMany`)**
983
+ **C) Polymorphic M:N (`morphToMany` / `morphedByMany`)**
813
984
 
814
985
  ```prisma
815
986
  /// @morph(name: taggable, type: to many, model: Tag, table: "taggables")
@@ -863,64 +1034,53 @@ model Activity {
863
1034
  }
864
1035
  ```
865
1036
 
866
-
867
-
868
- #### Other Examples
1037
+ ---
869
1038
 
870
-
1039
+ ## Other Examples
871
1040
 
872
1041
  ```prisma
873
-
874
1042
  /// @fillable{name,balance}
875
1043
  /// @hidden{secretToken}
876
1044
  model Account {
877
-   id        Int      @id @default(autoincrement())
878
-   balance   Decimal  @default(0.0) /// @cast{decimal:2}
879
-   nickname  String   /// @fillable @hidden
880
-   profile   Json?    /// @type{ import:'@types/forms', type:'ProfileDTO' }
881
-   company   Company? @relation(fields:[companyId], references:[id]) /// @local
882
-
883
-   companyId Int?
884
-   posts     Post[]   /// @with
1045
+ id Int @id @default(autoincrement())
1046
+ balance Decimal @default(0.0) /// @cast{decimal:2}
1047
+ nickname String /// @fillable @hidden
1048
+ profile Json? /// @type{ import:'@types/forms', type:'ProfileDTO' }
1049
+ company Company? @relation(fields:[companyId], references:[id]) /// @local
1050
+
1051
+ companyId Int?
1052
+ posts Post[] /// @with
885
1053
  }
886
1054
 
887
-
888
-
889
1055
  /// @with(posts,comments)
890
-
891
1056
  model User {
892
-   id       Int      @id @default(autoincrement())
893
-   email    String
894
-   posts    Post[]
895
-   comments Comment[]
1057
+ id Int @id @default(autoincrement())
1058
+ email String
1059
+ posts Post[]
1060
+ comments Comment[]
896
1061
  }
897
-
898
1062
  ```
899
1063
 
900
- **Generated output**
1064
+ **Generated (PHP, simplified)**
901
1065
 
902
1066
  ```php
903
-
904
1067
  protected $fillable = ['name','balance','nickname'];
905
- protected $hidden   = ['secretToken','nickname'];
906
- protected $casts    = ['balance' => 'decimal:2'];
1068
+ protected $hidden = ['secretToken','nickname'];
1069
+ protected $casts = ['balance' => 'decimal:2'];
907
1070
 
908
1071
  public array $interfaces = [
909
-     'profile' => { import: '@types/forms', type: 'ProfileDTO' },
1072
+ 'profile' => { import: '@types/forms', type: 'ProfileDTO' },
910
1073
  ];
911
1074
 
912
1075
  protected $with = ['posts'];
913
-
914
1076
  ```
915
1077
 
916
- `@local` prevents the `company()` relation method.  
917
-
1078
+ `@local` prevents the `company()` relation method.
918
1079
  Combine multiple inline directives; they’re processed left‑to‑right.
919
1080
 
920
- #### Example: Combined Directives
1081
+ ### Example: Combined Directives
921
1082
 
922
1083
  ```prisma
923
-
924
1084
  /// @fillable{name,balance}
925
1085
  /// @hidden{secretToken}
926
1086
  /// @guarded{password,apiToken}
@@ -932,22 +1092,20 @@ Combine multiple inline directives; they’re processed left‑to‑right.
932
1092
  /// @touch{company}
933
1093
  /// @appends{full_name}
934
1094
  model User {
935
-   id        Int      @id @default(autoincrement())
936
-   email     String   @unique                      /// @hidden @fillable
937
-   password  String                                 /// @hidden @guarded
938
-   balance   Decimal @default(0.0) /// @cast{decimal:2}
939
-   profile   Json?    /// @type{ import:'@types/forms', type:'ProfileDTO' }
940
-   company   Company? @relation(fields:[companyId], references:[id]) /// @local
941
-   companyId Int?
942
-   posts     Post[]   /// @with
1095
+ id Int @id @default(autoincrement())
1096
+ email String @unique /// @hidden @fillable
1097
+ password String /// @hidden @guarded
1098
+ balance Decimal @default(0.0) /// @cast{decimal:2}
1099
+ profile Json? /// @type{ import:'@types/forms', type:'ProfileDTO' }
1100
+ company Company? @relation(fields:[companyId], references:[id]) /// @local
1101
+ companyId Int?
1102
+ posts Post[] /// @with
943
1103
  }
944
-
945
1104
  ```
946
1105
 
947
1106
  **Generated output (simplified)**
948
1107
 
949
1108
  ```php
950
-
951
1109
  use Illuminate\Auth\Authenticatable;
952
1110
  use Illuminate\Auth\User;
953
1111
  use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -956,47 +1114,51 @@ use App\Observers\UserObserver;
956
1114
 
957
1115
  class User extends User implements AuthenticatableContract
958
1116
  {
959
-     use HasFactory, Authenticatable;
960
-    
961
-     protected $fillable = ['name','balance','email'];
962
-     protected $hidden   = ['secretToken','password'];
963
-     protected $guarded  = ['password','apiToken'];
964
-     protected $casts    = ['balance' => 'decimal:2'];
965
-     protected $touches  = ['company'];
966
-     protected $appends  = ['full_name'];
967
-     protected static string $factory = UserFactory::class;
968
-
969
-     protected static function boot()
970
-     {
971
-         parent::boot();
972
-         static::observe(UserObserver::class);
973
-     }
974
-
975
-     public function getFullNameAttribute()
976
-     {
977
-         return $this->attributes['full_name'] ?? null;
978
-     }
979
-
980
-     public function posts()
981
-     {
982
-         return $this->hasMany(Post::class);
983
-     }
1117
+ use HasFactory, Authenticatable;
1118
+
1119
+ protected $fillable = ['name','balance','email'];
1120
+ protected $hidden = ['secretToken','password'];
1121
+ protected $guarded = ['password','apiToken'];
1122
+ protected $casts = ['balance' => 'decimal:2'];
1123
+ protected $touches = ['company'];
1124
+ protected $appends = ['full_name'];
1125
+ protected static string $factory = UserFactory::class;
1126
+
1127
+ protected static function boot()
1128
+ {
1129
+ parent::boot();
1130
+ static::observe(UserObserver::class);
1131
+ }
1132
+
1133
+ public function getFullNameAttribute()
1134
+ {
1135
+ return $this->attributes['full_name'] ?? null;
1136
+ }
1137
+
1138
+ public function posts()
1139
+ {
1140
+ return $this->hasMany(Post::class);
1141
+ }
984
1142
  }
985
1143
  ```
986
1144
 
987
1145
  ---
1146
+
988
1147
  ## Notes & Limitations
989
1148
 
990
1149
  * `@local` is **model-method only**; it does not change the migration. Use `@silent` to suppress a whole model/enum from emission.
991
1150
  * Laravel cannot express **composite pivot keys** directly inside `belongsToMany(...)`. If your schema uses composite pivot keys, consider generating a Pivot model or using query/Compoships patterns on the one-to-many sides.
992
1151
  * The generator keeps your edits safe via merge markers; conflicts will be surfaced with `<<<<<<<` sections for manual resolution.
1152
+ * TS types are generated from the **same DMMF + directives** as PHP. You can always override a single field type with `@type{…}`.
1153
+
993
1154
  ---
994
1155
 
995
1156
  ## 💡 Tips
996
1157
 
997
- - Combine `migration` & `model` in one customize command when table names align.
998
- - Use `noEmit: true` for dry‑runs or CI validation.
999
- - Escape template chars in stub files.
1158
+ * Combine `migration` & `model` & `ts` in one `customize` command when table names align.
1159
+ * Use `noEmit: true` for dry‑runs or CI validation.
1160
+ * Escape template chars in stub files.
1161
+ * Keep your Prisma schema as the single source of truth; everything else is generated.
1000
1162
 
1001
1163
  ---
1002
1164
 
@@ -1008,6 +1170,7 @@ Use the library directly in a script or build tool instead of the CLI.
1008
1170
  import {
1009
1171
  generateLaravelSchema,
1010
1172
  generateLaravelModels,
1173
+ generateTypesFromPrisma,
1011
1174
  sortMigrations,
1012
1175
  } from 'prisma-laravel-migrate';
1013
1176
  import { readFileSync, writeFileSync } from 'fs';
@@ -1020,22 +1183,37 @@ import { getDMMF } from '@prisma/sdk';
1020
1183
  const dmmf = await getDMMF({ datamodel });
1021
1184
 
1022
1185
  /* 2. Run generators entirely in-memory */
1023
- const migrations = generateLaravelSchema({
1186
+ const migrations = await generateLaravelSchema({
1024
1187
  dmmf,
1025
1188
  schemaPath, // ← always pass this
1026
1189
  generator : { config: {} } as any,
1027
1190
  });
1028
1191
 
1029
- const { models, enums } = generateLaravelModels({
1192
+ const { models, enums } = await generateLaravelModels({
1030
1193
  dmmf,
1031
1194
  schemaPath,
1032
1195
  generator : { config: {} } as any,
1033
1196
  });
1034
1197
 
1198
+ const tsResult = await generateTypesFromPrisma({
1199
+ dmmf: dmmf as any,
1200
+ schemaPath,
1201
+ generator: {
1202
+ config: {
1203
+ // types-specific overrides
1204
+ moduleName: 'database/prisma',
1205
+ noEmit: true,
1206
+ },
1207
+ output: { value: 'resources/ts/prisma' },
1208
+ } as any,
1209
+ });
1210
+
1035
1211
  /* 3. Inspect or write output */
1036
1212
  sortMigrations(migrations).forEach(m => {
1037
1213
  writeFileSync(`./out/${m.tableName}.php`, m.statements.join('\n'), 'utf8');
1038
1214
  });
1215
+
1216
+ // tsResult.models / tsResult.enums contain TS model/enum definitions
1039
1217
  })();
1040
1218
  ```
1041
1219
 
@@ -1061,30 +1239,34 @@ generateLaravelSchema({
1061
1239
 
1062
1240
  ### Public exports
1063
1241
 
1064
- | Export | Purpose |
1065
- | -------------------------------- | --------------------------------------------------- |
1066
- | `generateLaravelSchema` | Build migration objects (and optionally write files)|
1067
- | `generateLaravelModels` | Build model + enum definitions |
1068
- | `sortMigrations` | Topologically sort migrations by FK dependencies |
1069
- | `Rule` | Type helper for custom migration shortcuts |
1070
- | _types_ (`column-definition-types`, `laravel-config`) | Full TypeScript typings |
1242
+ | Export | Purpose |
1243
+ | ----------------------------------------------------- | ------------------------------------------------------------- |
1244
+ | `generateLaravelSchema` | Build migration objects (and optionally write files) |
1245
+ | `generateLaravelModels` | Build PHP model + enum definitions |
1246
+ | `generateTypesFromPrisma` | Build TS model + enum definitions, with stub + module support |
1247
+ | `sortMigrations` | Topologically sort migrations by FK dependencies |
1248
+ | `Rule` | Type helper for custom migration shortcuts |
1249
+ | *types* (`column-definition-types`, `laravel-config`) | Full TypeScript typings |
1071
1250
 
1072
1251
  ```ts
1073
1252
  import {
1074
1253
  ColumnDefinition,
1075
1254
  LaravelSharedConfig,
1076
1255
  MigratorConfigOverride,
1256
+ ModelConfigOverride,
1257
+ TypesConfigOverride,
1077
1258
  } from 'prisma-laravel-migrate';
1078
1259
  ```
1079
1260
 
1080
- > **Heads-up:**
1081
- > `generateLaravelSchema` and `generateLaravelModels` **write files by default**
1082
- > (honouring the `outputDir` settings).
1261
+ > **Heads-up:**
1262
+ > `generateLaravelSchema`, `generateLaravelModels`, and `generateTypesFromPrisma`
1263
+ > **write files by default** (honouring their respective `outputDir` settings).
1083
1264
  > If you only want the in-memory objects—e.g. to capture the returned
1084
- > `migrations`, `models`, or `enums` arrays—set
1265
+ > `migrations`, `models`, `enums`, or TS `models`/`enums` arrays—set
1085
1266
  > `noEmit: true` in either
1086
1267
  >
1087
1268
  > * the per-call `generator.config` object:
1269
+ >
1088
1270
  > ```ts
1089
1271
  > generateLaravelSchema({
1090
1272
  > dmmf,
@@ -1093,18 +1275,20 @@ import {
1093
1275
  > });
1094
1276
  > ```
1095
1277
  > * **or** in `prisma/laravel.config.js`:
1278
+ >
1096
1279
  > ```js
1097
1280
  > module.exports = {
1098
1281
  > migrate: { noEmit: true },
1099
1282
  > modeler: { noEmit: true },
1283
+ > types: { noEmit: true },
1100
1284
  > };
1101
1285
  > ```
1286
+ >
1102
1287
  > This prevents any files from being created or overwritten while still
1103
1288
  > returning the fully-populated data structures for custom processing.
1104
1289
 
1105
1290
  ---
1106
1291
 
1107
-
1108
1292
  # Prisma → Laravel Migration Guide
1109
1293
 
1110
1294
  This document explains **how `prisma‑laravel‑migrate` converts your Prisma schema into
@@ -1118,21 +1302,21 @@ clean, idiomatic Laravel migrations.
1118
1302
  The generator first turns every Prisma (or native) scalar into the
1119
1303
  corresponding **Laravel schema builder method**.
1120
1304
 
1121
- | Native type | Laravel method |
1122
- |-------------|----------------|
1123
- | `Text` | `text()` |
1124
- | `VarChar` | `string()` |
1125
- | `Boolean` | `boolean()` |
1126
- | `TinyInt` | `tinyInteger()` |
1305
+ | Native type | Laravel method |
1306
+ | ---------------- | ---------------------- |
1307
+ | `Text` | `text()` |
1308
+ | `VarChar` | `string()` |
1309
+ | `Boolean` | `boolean()` |
1310
+ | `TinyInt` | `tinyInteger()` |
1127
1311
  | `UnsignedBigInt` | `unsignedBigInteger()` |
1128
- | `BigInt` | `bigInteger()` |
1129
- | `Decimal` | `decimal()` |
1130
- | `Double` | `double()` |
1131
- | `DateTime` | `timestamp()` |
1132
- | `Timestamptz` | `timestampsTz()` |
1133
- | `Json` | `json()` |
1134
- | `Uuid` | `uuid()` |
1135
- | `Inet` | `ipAddress()` |
1312
+ | `BigInt` | `bigInteger()` |
1313
+ | `Decimal` | `decimal()` |
1314
+ | `Double` | `double()` |
1315
+ | `DateTime` | `timestamp()` |
1316
+ | `Timestamptz` | `timestampsTz()` |
1317
+ | `Json` | `json()` |
1318
+ | `Uuid` | `uuid()` |
1319
+ | `Inet` | `ipAddress()` |
1136
1320
 
1137
1321
  *(See source `migrationTypes.ts` for the full mapping.)*
1138
1322
 
@@ -1143,17 +1327,17 @@ corresponding **Laravel schema builder method**.
1143
1327
  After basic mapping, each column is checked against a **rule set**
1144
1328
  so that common Laravel helper methods are used instead of verbose definitions.
1145
1329
 
1146
- | Rule | Condition in Prisma | Generated code |
1147
- |------|--------------------|----------------|
1148
- | **Primary ID** | `id BigInt @id @default(autoincrement())` | `$table->id();` |
1149
- | **Timestamps** | `created_at / updated_at` pair (`DateTime` or `Timestamp`) | `$table->timestamps();` |
1150
- | **TimestampsTz** | Same pair but `DateTimeTz` / tz‑aware types | `$table->timestampsTz();` |
1151
- | **Soft‑deletes** | `deleted_at DateTime` | `$table->softDeletes();` |
1152
- | **Soft‑deletesTz** | `deleted_at DateTimeTz` | `$table->softDeletesTz();` |
1153
- | **Remember token** | `remember_token String?` | `$table->rememberToken();` |
1154
- | **`foreignId`** | `<col>_id` + `@relation(...)` | `$table->foreignId('…')->constrained();` |
1155
- | **Morphs / NullableMorphs** | `<base>_id` & `<base>_type` combo | `$table->morphs('base');` / `$table->nullableMorphs('base');` |
1156
- | **UUID / ULID Morphs** | Same but with `Uuid` / `Ulid` id column | `$table->uuidMorphs()` & friends |
1330
+ | Rule | Condition in Prisma | Generated code |
1331
+ | --------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------- |
1332
+ | **Primary ID** | `id BigInt @id @default(autoincrement())` | `$table->id();` |
1333
+ | **Timestamps** | `created_at / updated_at` pair (`DateTime` or `Timestamp`) | `$table->timestamps();` |
1334
+ | **TimestampsTz** | Same pair but `DateTimeTz` / tz‑aware types | `$table->timestampsTz();` |
1335
+ | **Soft‑deletes** | `deleted_at DateTime` | `$table->softDeletes();` |
1336
+ | **Soft‑deletesTz** | `deleted_at DateTimeTz` | `$table->softDeletesTz();` |
1337
+ | **Remember token** | `remember_token String?` | `$table->rememberToken();` |
1338
+ | **`foreignId`** | `<col>_id` + `@relation(...)` | `$table->foreignId('…')->constrained();` |
1339
+ | **Morphs / NullableMorphs** | `<base>_id` & `<base>_type` combo | `$table->morphs('base');` / `$table->nullableMorphs('base');` |
1340
+ | **UUID / ULID Morphs** | Same but with `Uuid` / `Ulid` id column | `$table->uuidMorphs()` & friends |
1157
1341
 
1158
1342
  If a rule fires, both columns involved are marked *ignored* so the fallback
1159
1343
  builder doesn't emit them twice.
@@ -1183,7 +1367,7 @@ Foreign‑key references (`@relation`) then add a separate `$table->foreign()` c
1183
1367
  ```prisma
1184
1368
  model Post {
1185
1369
  id Int @id @default(autoincrement()) // ➜ $table->id()
1186
- title String
1370
+ title String
1187
1371
  body Text
1188
1372
  author_id Int
1189
1373
  author User @relation(fields:[author_id], references:[id])
@@ -1226,7 +1410,9 @@ $table->softDeletes();
1226
1410
  for shortcut rules; non‑standard names fall back to the generic builder.
1227
1411
  * Remember to run migrations **after** generating to ensure FK order is correct
1228
1412
  (the tool topologically sorts tables to avoid dependency loops).
1413
+
1229
1414
  ---
1415
+
1230
1416
  ### Custom `defaultMaps` for `formatDefault`
1231
1417
 
1232
1418
  You can override the built‑in `formatDefault()` logic without forking the package by
@@ -1267,14 +1453,15 @@ module.exports = {
1267
1453
  4. Keys are matched *case‑sensitive* to the Prisma default function
1268
1454
  (`uuid`, `cuid`, `sequence`, etc.).
1269
1455
 
1270
- > **Reference** – full list of Prisma `@@default()` helpers
1271
- > <https://www.prisma.io/docs/orm/reference/prisma-schema-reference#default>
1456
+ > **Reference** – full list of Prisma `@@default()` helpers
1457
+ > [https://www.prisma.io/docs/orm/reference/prisma-schema-reference#default](https://www.prisma.io/docs/orm/reference/prisma-schema-reference#default)
1458
+
1272
1459
  ---
1273
- Happy scaffolding! 🎉
1274
1460
 
1461
+ Happy scaffolding! 🎉
1275
1462
 
1276
1463
  ---
1277
1464
 
1278
1465
  ## 📜 License
1279
1466
 
1280
- MIT — Happy scaffolding! 🎉
1467
+ MIT — Happy scaffolding! 🎉