prisma-laravel-migrate 0.0.8 → 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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A generator plugin that translates your **Prisma schema** into Laravel‑ready
4
4
  **Database Migrations**, **Eloquent Models**, and **Enum classes**.
5
- Built in strict TypeScript with fully‑customisable stubs, grouping, and marker‑based updates.
5
+ Built in strict TypeScript with fully‑customisable stubs, grouping, and smart merge updates.
6
6
 
7
7
  ---
8
8
 
@@ -21,24 +21,24 @@ Add both generator blocks to **`schema.prisma`**:
21
21
 
22
22
  ```prisma
23
23
  generator migrate {
24
- provider = "prisma-laravel-migrations"
25
- stubDir = "./prisma/stubs"
26
- output = "database/migrations" // fallback
27
- outputDir = "database/migrations" // takes precedence
28
- startMarker = "// <prisma-laravel:start>"
29
- endMarker = "// <prisma-laravel:end>"
30
- noEmit = false
31
- groups = "./prisma/group-stubs.js"
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
32
  }
33
33
 
34
34
  generator modeler {
35
35
  provider = "prisma-laravel-models"
36
36
  stubDir = "./prisma/stubs"
37
+
37
38
  output = "app/Models"
38
- outputDir = "app/Models" // overrides output
39
+ outputDir = "app/Models" // overrides output
39
40
  outputEnumDir = "app/Enums"
40
- startMarker = "// <prisma-laravel:start>"
41
- endMarker = "// <prisma-laravel:end>"
41
+
42
42
  noEmit = false
43
43
  groups = "./prisma/group-stubs.js"
44
44
  }
@@ -48,22 +48,19 @@ generator modeler {
48
48
 
49
49
  | Key | Notes |
50
50
  | --- | --- |
51
- | `outputDir / output` | Destination folder (`outputDir` takes precedence). |
52
- | `outputEnumDir` | (modeler) directory for PHP enum classes. |
53
- | `stubDir` | Root stubs folder (`migration/`, `model/`, `enum/`). |
54
- | `startMarker/endMarker` | Region markers the generator will update. |
55
- | `groups` | JS module exporting stubgroup mappings. |
56
- | `noEmit` | If `true`, generator parses but **writes no** files. |
57
-
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 (dryrun). |
58
56
 
59
- ### 🔀 `groups` – Stub Grouping
57
+ ---
60
58
 
61
- Use **`groups`** in the generator block to map multiple tables (or enums)
62
- to a shared stub template:
59
+ ## 🔀 `groups` Stub Grouping
63
60
 
64
61
  ```prisma
65
62
  generator migrate {
66
- provider = "prisma-laravel-models"
63
+ provider = "prisma-laravel-migrate"
67
64
  stubDir = "./prisma/stubs"
68
65
  groups = "./prisma/group-stubs.js"
69
66
  }
@@ -72,20 +69,14 @@ generator migrate {
72
69
  `prisma/group-stubs.js`
73
70
 
74
71
  ```js
75
- /**
76
- * Each object links one stub file (relative to stubDir/<type>/)
77
- * to an array of tables (or enums) that should use it.
78
- */
79
72
  module.exports = [
80
73
  {
81
- // Auth domain
82
- stubFile: "auth.stub", // stubs/migration/auth.stub
83
- tables: ["users", "accounts", "password_resets"]
74
+ stubFile: "auth.stub", // stubs/migration/auth.stub
75
+ tables: ["users","accounts","password_resets"]
84
76
  },
85
77
  {
86
- // Billing domain
87
- stubFile: "billing.stub", // stubs/migration/billing.stub
88
- tables: ["invoices", "transactions"]
78
+ stubFile: "billing.stub", // stubs/migration/billing.stub
79
+ tables: ["invoices","transactions"]
89
80
  }
90
81
  ];
91
82
  ```
@@ -96,12 +87,11 @@ module.exports = [
96
87
  2. Matching group stub (`stubFile`)
97
88
  3. `stubs/<type>/index.stub` (default)
98
89
 
99
-
100
90
  ---
101
91
 
102
92
  ## 📁 Stub Folder Layout
103
93
 
104
- ```
94
+ ```text
105
95
  prisma/stubs/
106
96
  ├── migration/index.stub
107
97
  ├── model/index.stub
@@ -109,17 +99,17 @@ prisma/stubs/
109
99
  └── enum/index.stub
110
100
  ```
111
101
 
112
- Create a table‑specific override with
102
+ Add table‑specific overrides at
113
103
  `stubs/<type>/<table>.stub` (e.g. `stubs/model/users.stub`).
114
104
 
115
105
  ---
116
106
 
117
107
  ## 🔧 CLI Commands
118
108
 
119
- | Command | What it does |
109
+ | Command | Purpose |
120
110
  | --- | --- |
121
111
  | `init` | Inject generator blocks & scaffold stub folders |
122
- | `customize` | Create pertable stub overrides |
112
+ | `customize` | Create per-table stub overrides |
123
113
  | `gen` | Run `prisma generate` then Laravel generators |
124
114
 
125
115
  ### init
@@ -130,80 +120,69 @@ npx prisma-laravel-cli init --schema=prisma/schema.prisma
130
120
 
131
121
  ### customize
132
122
 
133
- Generate override stubs.
134
-
135
123
  ```bash
136
- npx prisma-laravel-cli customize \
137
- -t <types> \
138
- -n <names> \
139
- [--force] \
140
- [--config <path>]
124
+ npx prisma-laravel-cli customize -t migration,model -n users,accounts --force
141
125
  ```
142
126
 
143
127
  | Flag | Description |
144
128
  | --- | --- |
145
- | `-t, --type` | **Required.** Comma‑separated list of stub types.<br>Valid values: `migration`, `model`, `enum`. You may combine `migration,model`; `enum` must be used alone. |
146
- | `-n, --names` | **Required.** Comma‑separated table or enum names (e.g. `users,accounts`). |
129
+ | `-t, --type` | **Required.** Stub types (`migration`, `model`, `enum`). `enum` may not mix. |
130
+ | `-n, --names` | **Required.** Table or enum names (`users,accounts`). |
147
131
  | `--force` | Overwrite existing stub files. |
148
- | `--config` | Path to an alternate CLI config file (custom stubDir/groups). |
149
-
150
- **Behaviour**
151
-
152
- 1. Checks `<stubDir>/<type>/<name>.stub`.
153
- 2. If missing, copies from `index.stub` → that path and logs:
154
- `➡️ Created stubs/<type>/<name>.stub from index.stub`
155
- 3. If present and `--force` **not** set:
156
- `⏭️ Skipped existing stubs/<type>/<name>.stub`
157
- 4. With `--force`, overwrites and logs creation.
158
-
159
- **Example**
160
-
161
- ```bash
162
- # migration + model overrides for two tables
163
- npx prisma-laravel-cli customize -t migration,model -n users,accounts
164
- ```
132
+ | `--config` | Alternate CLI config path. |
165
133
 
166
134
  ### gen
167
135
 
168
136
  ```bash
169
- # run prisma generate then Laravel generators
170
137
  npx prisma-laravel-cli gen --config=prisma/laravel.config.js
171
-
172
138
  # skip prisma generate step
173
139
  npx prisma-laravel-cli gen --config=prisma/laravel.config.js --skipGenerate
174
140
  ```
175
141
 
176
- `prisma/laravel.config.js` example:
142
+ `prisma/laravel.config.js`
177
143
 
178
144
  ```js
179
145
  module.exports = {
180
146
  migrator: {
181
147
  outputDir: "database/migrations",
182
- stubDir: "prisma/stubs",
183
- groups: "./prisma/group-stubs.js",
148
+ stubDir: "prisma/stubs",
149
+ groups: "./prisma/group-stubs.js"
184
150
  },
185
151
  modeler: {
186
- outputDir: "app/Models",
152
+ outputDir: "app/Models",
187
153
  outputEnumDir: "app/Enums",
188
- stubDir: "prisma/stubs",
189
- groups: "./prisma/group-stubs.js",
190
- },
154
+ stubDir: "prisma/stubs",
155
+ groups: "./prisma/group-stubs.js"
156
+ }
191
157
  };
192
158
  ```
193
159
 
194
160
  ---
195
161
 
196
- ## Stub Customization Notes
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
197
179
 
198
- Stubs are **JS template literals**. Escape \\` and \\${ } if you want them literally.
180
+ Stubs are **JavaScript template literals**. Escape \` and \${ } if you want them literally.
199
181
 
200
- > **Full custom model stubs**
201
- > If you plan to hand-craft **all** the internal sections yourself
202
- > (fillable, hidden, casts, etc.), remove the `${content}` placeholder
203
- > but **keep** the `// <prisma-laravel:start>` and
204
- > `// <prisma-laravel:end>` markers so the generator still knows where
205
- > to inject future updates.
206
- > Remove the markers only if you never want the file touched again
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.
207
186
 
208
187
  ---
209
188
 
@@ -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, columns: generated } = printer.printMigration(mig);
78
+ const { fullContent: content } = printer.printMigration(mig);
79
79
  // 4) Write with markers as before
80
- writeWithMarkers(filePath, content, generated, cfg.startMarker, cfg.endMarker, cfg.overwriteExisting ?? false);
80
+ writeWithMerge(filePath, content, cfg.overwriteExisting ?? false);
81
81
  });
82
82
  return migrations;
83
83
  }
@@ -1,9 +1,10 @@
1
1
  import { existsSync, mkdirSync } from "fs";
2
2
  import path from "path";
3
- import { buildModelContent, writeWithMarkers } from "../utils.js";
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
@@ -63,7 +64,7 @@ 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
- writeWithMarkers(enumFile, enumPhp, enumDef.values.map(v => ` case ${v} = '${v}';`).join('\n'), cfg.startMarker, cfg.endMarker, cfg.overwriteExisting ?? false);
67
+ writeWithMerge(enumFile, enumPhp, cfg.overwriteExisting ?? false);
67
68
  }
68
69
  // 5) Write model files
69
70
  for (const model of models) {
@@ -72,7 +73,7 @@ export async function generateLaravelModels(options) {
72
73
  const content = buildModelContent(model);
73
74
  const modelPhp = printer.printModel(model, enums, content);
74
75
  const modelFile = path.join(modelsDir, `${model.className}.php`);
75
- writeWithMarkers(modelFile, modelPhp, content, cfg.startMarker, cfg.endMarker, cfg.overwriteExisting ?? false);
76
+ writeWithMerge(modelFile, modelPhp, cfg.overwriteExisting ?? false);
76
77
  }
77
78
  return { models, enums };
78
79
  }
@@ -1,6 +1,6 @@
1
1
  import { NativeToMigrationTypeMap } from "./migrator/column-maps.js";
2
2
  import { MigrationTypes } from "./migrator/migrationTypes.js";
3
- import { existsSync, readFileSync, writeFileSync } from 'fs';
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
@@ -143,38 +143,6 @@ export function buildModelContent(model) {
143
143
  */
144
144
  export function formatStub(stub) {
145
145
  return stub;
146
- // escape backslashes first
147
- // .replace(/\\/g, '\\\\')
148
- // then escape any backticks
149
- // .replace(/`/g, '\\`');
150
- }
151
- /**
152
- * Safely write or update a file by replacing the region between
153
- * startMarker and endMarker if both exist, otherwise overwrite the whole file.
154
- *
155
- * @param filePath Path to the target file
156
- * @param fullContent The full text to write if markers are missing
157
- * @param generated The text to inject between the markers
158
- * @param startMarker Literal string marking the region start
159
- * @param endMarker Literal string marking the region end
160
- * @param overwrite If false and file exists, do nothing
161
- */
162
- export function writeWithMarkers(filePath, fullContent, generated, startMarker, endMarker, overwrite) {
163
- // If the file exists but we're *not* overwriting, skip entirely
164
- if (existsSync(filePath) && !overwrite)
165
- return;
166
- if (existsSync(filePath)) {
167
- const existing = readFileSync(filePath, 'utf-8');
168
- // If both markers are present, do an in‐place replace
169
- if (existing.includes(startMarker) && existing.includes(endMarker)) {
170
- const escaped = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
171
- const re = new RegExp(`${escaped(startMarker)}[\\s\\S]*?${escaped(endMarker)}`, 'm');
172
- const updated = existing.replace(re, `${startMarker}\n${generated}\n${endMarker}`);
173
- return writeFileSync(filePath, updated, 'utf-8');
174
- }
175
- }
176
- // Otherwise write the full content (which itself can include the markers)
177
- writeFileSync(filePath, fullContent, 'utf-8');
178
146
  }
179
147
  export function resolveStub(cfg, type, tableName) {
180
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.8",
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",