@stacksjs/buddy 0.70.87 → 0.70.88
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/package.json +2 -2
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -165
- package/dist/commands/about.d.ts +0 -2
- package/dist/commands/about.js +0 -35
- package/dist/commands/add.d.ts +0 -2
- package/dist/commands/add.js +0 -33
- package/dist/commands/auth.d.ts +0 -2
- package/dist/commands/auth.js +0 -68
- package/dist/commands/build.d.ts +0 -2
- package/dist/commands/build.js +0 -153
- package/dist/commands/cd.d.ts +0 -20
- package/dist/commands/cd.js +0 -39
- package/dist/commands/changelog.d.ts +0 -2
- package/dist/commands/changelog.js +0 -32
- package/dist/commands/clean.d.ts +0 -2
- package/dist/commands/clean.js +0 -37
- package/dist/commands/cloud.d.ts +0 -2
- package/dist/commands/cloud.js +0 -503
- package/dist/commands/commit.d.ts +0 -2
- package/dist/commands/commit.js +0 -16
- package/dist/commands/completion.d.ts +0 -2
- package/dist/commands/completion.js +0 -143
- package/dist/commands/config-migrate.d.ts +0 -26
- package/dist/commands/config-migrate.js +0 -94
- package/dist/commands/configure.d.ts +0 -2
- package/dist/commands/configure.js +0 -48
- package/dist/commands/create.d.ts +0 -2
- package/dist/commands/create.js +0 -141
- package/dist/commands/deploy.d.ts +0 -107
- package/dist/commands/deploy.js +0 -1439
- package/dist/commands/dev.d.ts +0 -10
- package/dist/commands/dev.js +0 -907
- package/dist/commands/dns.d.ts +0 -2
- package/dist/commands/dns.js +0 -102
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.js +0 -285
- package/dist/commands/domains.d.ts +0 -2
- package/dist/commands/domains.js +0 -159
- package/dist/commands/email.d.ts +0 -2
- package/dist/commands/email.js +0 -524
- package/dist/commands/env.d.ts +0 -2
- package/dist/commands/env.js +0 -246
- package/dist/commands/extension.d.ts +0 -8
- package/dist/commands/extension.js +0 -78
- package/dist/commands/features.d.ts +0 -180
- package/dist/commands/features.js +0 -422
- package/dist/commands/fresh.d.ts +0 -2
- package/dist/commands/fresh.js +0 -39
- package/dist/commands/generate.d.ts +0 -2
- package/dist/commands/generate.js +0 -104
- package/dist/commands/http.d.ts +0 -2
- package/dist/commands/http.js +0 -33
- package/dist/commands/index.d.ts +0 -53
- package/dist/commands/index.js +0 -53
- package/dist/commands/install.d.ts +0 -2
- package/dist/commands/install.js +0 -22
- package/dist/commands/key.d.ts +0 -2
- package/dist/commands/key.js +0 -23
- package/dist/commands/lint.d.ts +0 -2
- package/dist/commands/lint.js +0 -46
- package/dist/commands/list.d.ts +0 -2
- package/dist/commands/list.js +0 -108
- package/dist/commands/mail.d.ts +0 -2
- package/dist/commands/mail.js +0 -693
- package/dist/commands/maintenance.d.ts +0 -2
- package/dist/commands/maintenance.js +0 -141
- package/dist/commands/make.d.ts +0 -2
- package/dist/commands/make.js +0 -375
- package/dist/commands/migrate-project.d.ts +0 -2
- package/dist/commands/migrate-project.js +0 -49
- package/dist/commands/migrate.d.ts +0 -4
- package/dist/commands/migrate.js +0 -455
- package/dist/commands/outdated.d.ts +0 -2
- package/dist/commands/outdated.js +0 -19
- package/dist/commands/package.d.ts +0 -2
- package/dist/commands/package.js +0 -17
- package/dist/commands/phone.d.ts +0 -2
- package/dist/commands/phone.js +0 -188
- package/dist/commands/ports.d.ts +0 -2
- package/dist/commands/ports.js +0 -118
- package/dist/commands/prepublish.d.ts +0 -2
- package/dist/commands/prepublish.js +0 -17
- package/dist/commands/projects.d.ts +0 -2
- package/dist/commands/projects.js +0 -29
- package/dist/commands/publish.d.ts +0 -2
- package/dist/commands/publish.js +0 -172
- package/dist/commands/queue.d.ts +0 -2
- package/dist/commands/queue.js +0 -248
- package/dist/commands/release.d.ts +0 -2
- package/dist/commands/release.js +0 -30
- package/dist/commands/route.d.ts +0 -2
- package/dist/commands/route.js +0 -21
- package/dist/commands/saas.d.ts +0 -2
- package/dist/commands/saas.js +0 -25
- package/dist/commands/schedule.d.ts +0 -2
- package/dist/commands/schedule.js +0 -61
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/search.js +0 -84
- package/dist/commands/seed.d.ts +0 -2
- package/dist/commands/seed.js +0 -71
- package/dist/commands/serve.d.ts +0 -25
- package/dist/commands/serve.js +0 -182
- package/dist/commands/setup.d.ts +0 -12
- package/dist/commands/setup.js +0 -215
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/share.js +0 -214
- package/dist/commands/sms.d.ts +0 -2
- package/dist/commands/sms.js +0 -328
- package/dist/commands/stacks.d.ts +0 -2
- package/dist/commands/stacks.js +0 -69
- package/dist/commands/telemetry.d.ts +0 -2
- package/dist/commands/telemetry.js +0 -74
- package/dist/commands/test.d.ts +0 -2
- package/dist/commands/test.js +0 -130
- package/dist/commands/tinker.d.ts +0 -2
- package/dist/commands/tinker.js +0 -37
- package/dist/commands/types.d.ts +0 -2
- package/dist/commands/types.js +0 -18
- package/dist/commands/upgrade.d.ts +0 -2
- package/dist/commands/upgrade.js +0 -98
- package/dist/commands/version.d.ts +0 -2
- package/dist/commands/version.js +0 -16
- package/dist/config.d.ts +0 -43
- package/dist/config.js +0 -223
- package/dist/custom-cli.d.ts +0 -1
- package/dist/custom-cli.js +0 -23
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/lazy-commands.d.ts +0 -61
- package/dist/lazy-commands.js +0 -188
- package/dist/migrators/index.d.ts +0 -9
- package/dist/migrators/index.js +0 -62
- package/dist/migrators/laravel/index.d.ts +0 -2
- package/dist/migrators/laravel/index.js +0 -148
- package/dist/migrators/laravel/migrations.d.ts +0 -52
- package/dist/migrators/laravel/migrations.js +0 -232
- package/dist/migrators/laravel/models.d.ts +0 -34
- package/dist/migrators/laravel/models.js +0 -134
- package/dist/migrators/rails/index.d.ts +0 -2
- package/dist/migrators/rails/index.js +0 -11
- package/dist/migrators/types.d.ts +0 -44
- package/dist/migrators/types.js +0 -0
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
export function parseLaravelMigration(source) {
|
|
2
|
-
const createMatch = source.match(/Schema::create\(\s*['"]([a-z0-9_]+)['"]\s*,\s*function\s*\([^)]*\)\s*\{([\s\S]*?)\}\s*\)\s*;/i);
|
|
3
|
-
if (!createMatch?.[1] || createMatch[2] === void 0)
|
|
4
|
-
return null;
|
|
5
|
-
const table = createMatch[1], body = createMatch[2], skipped = [], columns = [], indexes = [], lines = body.split(`
|
|
6
|
-
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("//") && !line.startsWith("*") && !line.startsWith("/*"));
|
|
7
|
-
for (const line of lines) {
|
|
8
|
-
const trimmed = line.replace(/;\s*$/, "");
|
|
9
|
-
if (!trimmed.startsWith("$table->")) {
|
|
10
|
-
skipped.push(line);
|
|
11
|
-
continue;
|
|
12
|
-
}
|
|
13
|
-
const parsed = parseColumnLine(trimmed);
|
|
14
|
-
if (!parsed) {
|
|
15
|
-
skipped.push(line);
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (parsed.kind === "columns") {
|
|
19
|
-
columns.push(...parsed.columns);
|
|
20
|
-
if (parsed.indexes)
|
|
21
|
-
indexes.push(...parsed.indexes);
|
|
22
|
-
} else if (parsed.kind === "index")
|
|
23
|
-
indexes.push(parsed.index);
|
|
24
|
-
else if (parsed.kind === "skip")
|
|
25
|
-
skipped.push(line);
|
|
26
|
-
}
|
|
27
|
-
const sql = emitCreateTable(table, columns, indexes);
|
|
28
|
-
return { table, sql, skipped };
|
|
29
|
-
}
|
|
30
|
-
function parseColumnLine(line) {
|
|
31
|
-
const methodMatch = line.match(/^\$table->([a-zA-Z_]+)\s*\(([^)]*)\)(.*)$/);
|
|
32
|
-
if (!methodMatch?.[1] || methodMatch[2] === void 0 || methodMatch[3] === void 0)
|
|
33
|
-
return null;
|
|
34
|
-
const method = methodMatch[1], args = methodMatch[2].trim(), rest = methodMatch[3];
|
|
35
|
-
if (method === "timestamps")
|
|
36
|
-
return {
|
|
37
|
-
kind: "columns",
|
|
38
|
-
columns: [
|
|
39
|
-
{ name: "created_at", type: "DATETIME", nullable: !0, unique: !1, primaryKey: !1, autoIncrement: !1, defaultValue: "CURRENT_TIMESTAMP", index: !1 },
|
|
40
|
-
{ name: "updated_at", type: "DATETIME", nullable: !0, unique: !1, primaryKey: !1, autoIncrement: !1, defaultValue: null, index: !1 }
|
|
41
|
-
]
|
|
42
|
-
};
|
|
43
|
-
if (method === "softDeletes")
|
|
44
|
-
return {
|
|
45
|
-
kind: "columns",
|
|
46
|
-
columns: [
|
|
47
|
-
{ name: "deleted_at", type: "DATETIME", nullable: !0, unique: !1, primaryKey: !1, autoIncrement: !1, defaultValue: null, index: !1 }
|
|
48
|
-
]
|
|
49
|
-
};
|
|
50
|
-
if (method === "rememberToken")
|
|
51
|
-
return {
|
|
52
|
-
kind: "columns",
|
|
53
|
-
columns: [
|
|
54
|
-
{ name: "remember_token", type: "TEXT", nullable: !0, unique: !1, primaryKey: !1, autoIncrement: !1, defaultValue: null, index: !1 }
|
|
55
|
-
]
|
|
56
|
-
};
|
|
57
|
-
if (method === "id" || method === "increments" || method === "bigIncrements")
|
|
58
|
-
return {
|
|
59
|
-
kind: "columns",
|
|
60
|
-
columns: [
|
|
61
|
-
{ name: parseStringArg(args) || "id", type: "INTEGER", nullable: !1, unique: !1, primaryKey: !0, autoIncrement: !0, defaultValue: null, index: !1 }
|
|
62
|
-
]
|
|
63
|
-
};
|
|
64
|
-
if (method === "index" || method === "unique") {
|
|
65
|
-
const cols = parseIndexArg(args);
|
|
66
|
-
if (cols.length === 0)
|
|
67
|
-
return { kind: "skip" };
|
|
68
|
-
return {
|
|
69
|
-
kind: "index",
|
|
70
|
-
index: { name: "", columns: cols, unique: method === "unique" }
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
const columnName = parseStringArg(args);
|
|
74
|
-
if (!columnName)
|
|
75
|
-
return null;
|
|
76
|
-
const sqlType = mapType(method);
|
|
77
|
-
if (!sqlType)
|
|
78
|
-
return { kind: "skip" };
|
|
79
|
-
const column = {
|
|
80
|
-
name: columnName,
|
|
81
|
-
type: sqlType,
|
|
82
|
-
nullable: !1,
|
|
83
|
-
unique: !1,
|
|
84
|
-
primaryKey: !1,
|
|
85
|
-
autoIncrement: !1,
|
|
86
|
-
defaultValue: null,
|
|
87
|
-
index: !1
|
|
88
|
-
};
|
|
89
|
-
applyModifiers(rest, column);
|
|
90
|
-
const indexes = [];
|
|
91
|
-
if (column.unique)
|
|
92
|
-
indexes.push({ name: "", columns: [columnName], unique: !0 });
|
|
93
|
-
if (column.index)
|
|
94
|
-
indexes.push({ name: "", columns: [columnName], unique: !1 });
|
|
95
|
-
return { kind: "columns", columns: [column], indexes };
|
|
96
|
-
}
|
|
97
|
-
function applyModifiers(rest, column) {
|
|
98
|
-
const modRegex = /->([a-zA-Z_]+)\s*\(([^)]*)\)/g;
|
|
99
|
-
let match;
|
|
100
|
-
while ((match = modRegex.exec(rest)) !== null) {
|
|
101
|
-
const mod = match[1], args = match[2]?.trim() ?? "";
|
|
102
|
-
if (!mod)
|
|
103
|
-
continue;
|
|
104
|
-
switch (mod) {
|
|
105
|
-
case "nullable":
|
|
106
|
-
column.nullable = !0;
|
|
107
|
-
break;
|
|
108
|
-
case "unique":
|
|
109
|
-
column.unique = !0;
|
|
110
|
-
break;
|
|
111
|
-
case "index":
|
|
112
|
-
column.index = !0;
|
|
113
|
-
break;
|
|
114
|
-
case "default":
|
|
115
|
-
column.defaultValue = parseDefaultArg(args);
|
|
116
|
-
break;
|
|
117
|
-
case "primary":
|
|
118
|
-
column.primaryKey = !0;
|
|
119
|
-
break;
|
|
120
|
-
case "autoIncrement":
|
|
121
|
-
column.autoIncrement = !0;
|
|
122
|
-
break;
|
|
123
|
-
case "unsigned":
|
|
124
|
-
case "comment":
|
|
125
|
-
case "after":
|
|
126
|
-
case "change":
|
|
127
|
-
break;
|
|
128
|
-
default:
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
function parseStringArg(args) {
|
|
134
|
-
return args.match(/^['"]([^'"]+)['"]/)?.[1] ?? null;
|
|
135
|
-
}
|
|
136
|
-
function parseIndexArg(args) {
|
|
137
|
-
const single = parseStringArg(args);
|
|
138
|
-
if (single)
|
|
139
|
-
return [single];
|
|
140
|
-
const arr = args.match(/\[([^\]]*)\]/);
|
|
141
|
-
if (!arr?.[1])
|
|
142
|
-
return [];
|
|
143
|
-
return arr[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
|
|
144
|
-
}
|
|
145
|
-
function parseDefaultArg(args) {
|
|
146
|
-
const stringMatch = args.match(/^['"]([^'"]*)['"]/);
|
|
147
|
-
if (stringMatch?.[1] !== void 0)
|
|
148
|
-
return `'${stringMatch[1].replace(/'/g, "''")}'`;
|
|
149
|
-
const trimmed = args.trim();
|
|
150
|
-
if (trimmed === "true")
|
|
151
|
-
return "1";
|
|
152
|
-
if (trimmed === "false")
|
|
153
|
-
return "0";
|
|
154
|
-
if (trimmed === "null")
|
|
155
|
-
return "NULL";
|
|
156
|
-
if (/^-?\d+(\.\d+)?$/.test(trimmed))
|
|
157
|
-
return trimmed;
|
|
158
|
-
return trimmed;
|
|
159
|
-
}
|
|
160
|
-
const TYPE_MAP = {
|
|
161
|
-
string: "TEXT",
|
|
162
|
-
text: "TEXT",
|
|
163
|
-
mediumText: "TEXT",
|
|
164
|
-
longText: "TEXT",
|
|
165
|
-
char: "TEXT",
|
|
166
|
-
integer: "INTEGER",
|
|
167
|
-
tinyInteger: "INTEGER",
|
|
168
|
-
smallInteger: "INTEGER",
|
|
169
|
-
mediumInteger: "INTEGER",
|
|
170
|
-
bigInteger: "INTEGER",
|
|
171
|
-
unsignedBigInteger: "INTEGER",
|
|
172
|
-
unsignedInteger: "INTEGER",
|
|
173
|
-
foreignId: "INTEGER",
|
|
174
|
-
boolean: "INTEGER",
|
|
175
|
-
date: "DATE",
|
|
176
|
-
dateTime: "DATETIME",
|
|
177
|
-
dateTimeTz: "DATETIME",
|
|
178
|
-
timestamp: "DATETIME",
|
|
179
|
-
timestampTz: "DATETIME",
|
|
180
|
-
time: "TIME",
|
|
181
|
-
timeTz: "TIME",
|
|
182
|
-
year: "INTEGER",
|
|
183
|
-
json: "TEXT",
|
|
184
|
-
jsonb: "TEXT",
|
|
185
|
-
uuid: "TEXT",
|
|
186
|
-
ulid: "TEXT",
|
|
187
|
-
ipAddress: "TEXT",
|
|
188
|
-
macAddress: "TEXT",
|
|
189
|
-
float: "REAL",
|
|
190
|
-
double: "REAL",
|
|
191
|
-
decimal: "REAL",
|
|
192
|
-
binary: "BLOB",
|
|
193
|
-
enum: "TEXT",
|
|
194
|
-
set: "TEXT"
|
|
195
|
-
};
|
|
196
|
-
function mapType(laravelMethod) {
|
|
197
|
-
return TYPE_MAP[laravelMethod] ?? null;
|
|
198
|
-
}
|
|
199
|
-
function emitCreateTable(table, columns, indexes) {
|
|
200
|
-
const lines = [];
|
|
201
|
-
lines.push(`CREATE TABLE IF NOT EXISTS ${table} (`);
|
|
202
|
-
const colDefs = columns.map((c) => {
|
|
203
|
-
const parts = [` ${c.name} ${c.type}`];
|
|
204
|
-
if (c.primaryKey)
|
|
205
|
-
parts.push("PRIMARY KEY");
|
|
206
|
-
if (c.autoIncrement)
|
|
207
|
-
parts.push("AUTOINCREMENT");
|
|
208
|
-
if (!c.nullable && !c.primaryKey)
|
|
209
|
-
parts.push("NOT NULL");
|
|
210
|
-
if (c.defaultValue !== null)
|
|
211
|
-
parts.push(`DEFAULT ${c.defaultValue}`);
|
|
212
|
-
return parts.join(" ");
|
|
213
|
-
});
|
|
214
|
-
lines.push(colDefs.join(`,
|
|
215
|
-
`));
|
|
216
|
-
lines.push(");");
|
|
217
|
-
for (const idx of indexes) {
|
|
218
|
-
const cols = idx.columns.join(", "), indexName = idx.name || `${table}_${idx.columns.join("_")}_${idx.unique ? "unique" : "index"}`, verb = idx.unique ? "CREATE UNIQUE INDEX" : "CREATE INDEX";
|
|
219
|
-
lines.push(`${verb} IF NOT EXISTS ${indexName} ON ${table}(${cols});`);
|
|
220
|
-
}
|
|
221
|
-
return `${lines.join(`
|
|
222
|
-
`)}
|
|
223
|
-
`;
|
|
224
|
-
}
|
|
225
|
-
export function laravelFilenameToStacks(filename, sequence, table) {
|
|
226
|
-
const padded = String(sequence).padStart(10, "0"), base = filename.toLowerCase();
|
|
227
|
-
if (base.includes("create_") && base.includes("_table"))
|
|
228
|
-
return `${padded}-create-${table}-table.sql`;
|
|
229
|
-
if (base.includes("add_") || base.includes("alter_"))
|
|
230
|
-
return `${padded}-alter-${table}-migration.sql`;
|
|
231
|
-
return `${padded}-create-${table}-table.sql`;
|
|
232
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export declare function parseLaravelModel(source: string): ParsedModel | null;
|
|
2
|
-
/**
|
|
3
|
-
* Laravel Eloquent → Stacks `defineModel({})` translator.
|
|
4
|
-
*
|
|
5
|
-
* Eloquent models are PHP classes that lean on convention plus a few
|
|
6
|
-
* `protected $foo = ...` properties to describe schema and behaviour.
|
|
7
|
-
* We pull the bits we need by regex:
|
|
8
|
-
*
|
|
9
|
-
* - class name → model `name`
|
|
10
|
-
* - protected $table → `table`
|
|
11
|
-
* - protected $fillable → which attributes are `fillable: true`
|
|
12
|
-
* - protected $casts → which attributes get a typed
|
|
13
|
-
* validation rule
|
|
14
|
-
* - hasMany/hasOne/belongsTo → relationship hints in the report
|
|
15
|
-
*
|
|
16
|
-
* Anything else (scopes, accessors, mutators, observers) gets dropped
|
|
17
|
-
* with a note in the migration report. The user can re-add them by
|
|
18
|
-
* hand from the Stacks docs.
|
|
19
|
-
*/
|
|
20
|
-
export declare interface ParsedModel {
|
|
21
|
-
className: string
|
|
22
|
-
table: string
|
|
23
|
-
fillable: string[]
|
|
24
|
-
hidden: string[]
|
|
25
|
-
casts: Record<string, string>
|
|
26
|
-
relationships: ParsedRelationship[]
|
|
27
|
-
tsSource: string
|
|
28
|
-
notes: string[]
|
|
29
|
-
}
|
|
30
|
-
export declare interface ParsedRelationship {
|
|
31
|
-
name: string
|
|
32
|
-
kind: 'belongsTo' | 'hasMany' | 'hasOne' | 'belongsToMany'
|
|
33
|
-
target: string
|
|
34
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
const RELATION_KINDS = ["belongsTo", "hasMany", "hasOne", "belongsToMany"];
|
|
2
|
-
export function parseLaravelModel(source) {
|
|
3
|
-
const classMatch = source.match(/\bclass\s+([A-Z][A-Za-z0-9_]*)\b/);
|
|
4
|
-
if (!classMatch?.[1])
|
|
5
|
-
return null;
|
|
6
|
-
const className = classMatch[1], table = extractStringProperty(source, "table") ?? snakeCasePlural(className), fillable = extractArrayProperty(source, "fillable"), hidden = extractArrayProperty(source, "hidden"), casts = extractKeyedArrayProperty(source, "casts"), relationships = extractRelationships(source), notes = [];
|
|
7
|
-
if (/protected\s+\$appends\s*=/.test(source))
|
|
8
|
-
notes.push("$appends accessors require a custom Stacks computed-attribute setup");
|
|
9
|
-
if (/protected\s+\$dates\s*=/.test(source))
|
|
10
|
-
notes.push("$dates is implicit in Stacks \u2014 datetime columns are already typed via validation rules");
|
|
11
|
-
if (/\buse\b[^;]*\bSoftDeletes\b/.test(source))
|
|
12
|
-
notes.push("SoftDeletes trait \u2192 enable `useDeletedAt` (or matching trait) in the Stacks model traits block");
|
|
13
|
-
if (/\buse\b[^;]*\bHasFactory\b/.test(source))
|
|
14
|
-
notes.push("HasFactory \u2192 Stacks generates factories automatically when `useSeeder` is enabled");
|
|
15
|
-
const tsSource = emitStacksModel({ className, table, fillable, hidden, casts, relationships });
|
|
16
|
-
return { className, table, fillable, hidden, casts, relationships, tsSource, notes };
|
|
17
|
-
}
|
|
18
|
-
function extractStringProperty(source, name) {
|
|
19
|
-
const re = new RegExp(`protected\\s+\\$${name}\\s*=\\s*['"]([^'"]+)['"]`);
|
|
20
|
-
return source.match(re)?.[1] ?? null;
|
|
21
|
-
}
|
|
22
|
-
function extractArrayProperty(source, name) {
|
|
23
|
-
const re = new RegExp(`protected\\s+\\$${name}\\s*=\\s*\\[([^\\]]*)\\]`, "s"), m = source.match(re);
|
|
24
|
-
if (!m?.[1])
|
|
25
|
-
return [];
|
|
26
|
-
return m[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
|
|
27
|
-
}
|
|
28
|
-
function extractKeyedArrayProperty(source, name) {
|
|
29
|
-
const re = new RegExp(`protected\\s+\\$${name}\\s*=\\s*\\[([^\\]]*)\\]`, "s"), m = source.match(re);
|
|
30
|
-
if (!m?.[1])
|
|
31
|
-
return {};
|
|
32
|
-
const out = {}, pairs = m[1].split(",");
|
|
33
|
-
for (const pair of pairs) {
|
|
34
|
-
const kv = pair.match(/['"]([^'"]+)['"]\s*=>\s*['"]([^'"]+)['"]/);
|
|
35
|
-
if (kv?.[1] && kv[2] !== void 0)
|
|
36
|
-
out[kv[1]] = kv[2];
|
|
37
|
-
}
|
|
38
|
-
return out;
|
|
39
|
-
}
|
|
40
|
-
function extractRelationships(source) {
|
|
41
|
-
const found = [], methodRegex = /public\s+function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*(?::\s*[^\{]+)?\{[\s\S]*?return\s+\$this\s*->\s*(belongsTo|hasMany|hasOne|belongsToMany)\s*\(\s*([A-Z][A-Za-z0-9_]*)::class/g;
|
|
42
|
-
let m;
|
|
43
|
-
while ((m = methodRegex.exec(source)) !== null) {
|
|
44
|
-
const name = m[1], kind = m[2], target = m[3];
|
|
45
|
-
if (!name || !kind || !target)
|
|
46
|
-
continue;
|
|
47
|
-
found.push({ name, kind, target });
|
|
48
|
-
}
|
|
49
|
-
return found;
|
|
50
|
-
}
|
|
51
|
-
function emitStacksModel(args) {
|
|
52
|
-
const attributeNames = unique([...args.fillable, ...Object.keys(args.casts)]), attrBlock = attributeNames.length === 0 ? " attributes: {}," : ` attributes: {
|
|
53
|
-
${attributeNames.map((n) => emitAttribute(n, args)).join(`
|
|
54
|
-
`)}
|
|
55
|
-
},`, relationsBlock = args.relationships.length === 0 ? "" : `
|
|
56
|
-
// Relationships translated from Eloquent \u2014 refine targets / foreign keys to match your schema.
|
|
57
|
-
relations: {
|
|
58
|
-
${args.relationships.map((r) => ` ${r.name}: { type: '${r.kind}', target: '${r.target}' },`).join(`
|
|
59
|
-
`)}
|
|
60
|
-
},
|
|
61
|
-
`;
|
|
62
|
-
return `import { defineModel } from '@stacksjs/orm'
|
|
63
|
-
import { schema } from '@stacksjs/validation'
|
|
64
|
-
|
|
65
|
-
export default defineModel({
|
|
66
|
-
name: '${args.className}',
|
|
67
|
-
table: '${args.table}',
|
|
68
|
-
primaryKey: 'id',
|
|
69
|
-
autoIncrement: true,
|
|
70
|
-
|
|
71
|
-
traits: {
|
|
72
|
-
useTimestamps: true,
|
|
73
|
-
},
|
|
74
|
-
${relationsBlock}
|
|
75
|
-
${attrBlock}
|
|
76
|
-
} as const)
|
|
77
|
-
`;
|
|
78
|
-
}
|
|
79
|
-
function emitAttribute(name, args) {
|
|
80
|
-
const rule = ruleForCast(args.casts[name]) ?? "schema.string()", fillable = args.fillable.includes(name), hidden = args.hidden.includes(name), notes = [];
|
|
81
|
-
if (hidden)
|
|
82
|
-
notes.push("hidden from API responses in source \u2014 handle via your Stacks resource/transformer");
|
|
83
|
-
const noteLine = notes.length > 0 ? ` // ${notes.join("; ")}
|
|
84
|
-
` : "";
|
|
85
|
-
return ` ${name}: {
|
|
86
|
-
fillable: ${fillable},
|
|
87
|
-
${noteLine} validation: {
|
|
88
|
-
rule: ${rule},
|
|
89
|
-
},
|
|
90
|
-
},`;
|
|
91
|
-
}
|
|
92
|
-
function ruleForCast(cast) {
|
|
93
|
-
if (!cast)
|
|
94
|
-
return null;
|
|
95
|
-
switch (cast.split(":")[0]) {
|
|
96
|
-
case "integer":
|
|
97
|
-
case "int":
|
|
98
|
-
return "schema.number().int()";
|
|
99
|
-
case "float":
|
|
100
|
-
case "double":
|
|
101
|
-
case "real":
|
|
102
|
-
return "schema.number()";
|
|
103
|
-
case "boolean":
|
|
104
|
-
case "bool":
|
|
105
|
-
return "schema.boolean()";
|
|
106
|
-
case "string":
|
|
107
|
-
return "schema.string()";
|
|
108
|
-
case "array":
|
|
109
|
-
case "collection":
|
|
110
|
-
case "json":
|
|
111
|
-
case "object":
|
|
112
|
-
return "schema.unknown()";
|
|
113
|
-
case "date":
|
|
114
|
-
case "datetime":
|
|
115
|
-
case "immutable_date":
|
|
116
|
-
case "immutable_datetime":
|
|
117
|
-
return "schema.string()";
|
|
118
|
-
case "decimal":
|
|
119
|
-
return "schema.number()";
|
|
120
|
-
default:
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
function snakeCasePlural(className) {
|
|
125
|
-
const snake = className.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
126
|
-
if (snake.endsWith("y") && !/[aeiou]y$/.test(snake))
|
|
127
|
-
return `${snake.slice(0, -1)}ies`;
|
|
128
|
-
if (snake.endsWith("s") || snake.endsWith("x") || snake.endsWith("z") || snake.endsWith("ch") || snake.endsWith("sh"))
|
|
129
|
-
return `${snake}es`;
|
|
130
|
-
return `${snake}s`;
|
|
131
|
-
}
|
|
132
|
-
function unique(arr) {
|
|
133
|
-
return [...new Set(arr)];
|
|
134
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export const railsDriver = {
|
|
2
|
-
name: "rails",
|
|
3
|
-
async migrate(_req) {
|
|
4
|
-
return [{
|
|
5
|
-
source: ".",
|
|
6
|
-
target: "",
|
|
7
|
-
status: "skipped",
|
|
8
|
-
note: "Rails driver is not yet implemented (stacksjs/stacks#1241 \u2014 Laravel driver shipped first). Track follow-up at github.com/stacksjs/stacks/issues/1241."
|
|
9
|
-
}];
|
|
10
|
-
}
|
|
11
|
-
};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export declare interface MigrateProjectRequest {
|
|
2
|
-
source: string
|
|
3
|
-
target: string
|
|
4
|
-
from: SourceFramework
|
|
5
|
-
dryRun?: boolean
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* A single output produced (or attempted) by a driver. Drivers append
|
|
9
|
-
* one `ReportEntry` per source file they touch so the report reads
|
|
10
|
-
* like a checklist of what was translated.
|
|
11
|
-
*/
|
|
12
|
-
export declare interface ReportEntry {
|
|
13
|
-
source: string
|
|
14
|
-
target: string
|
|
15
|
-
status: 'translated' | 'copied' | 'skipped' | 'failed'
|
|
16
|
-
note?: string
|
|
17
|
-
}
|
|
18
|
-
export declare interface MigrationReport {
|
|
19
|
-
source: string
|
|
20
|
-
target: string
|
|
21
|
-
from: SourceFramework
|
|
22
|
-
startedAt: string
|
|
23
|
-
finishedAt: string
|
|
24
|
-
entries: ReportEntry[]
|
|
25
|
-
}
|
|
26
|
-
export declare interface Driver {
|
|
27
|
-
readonly name: SourceFramework
|
|
28
|
-
migrate: (req: MigrateProjectRequest) => Promise<ReportEntry[]>
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Shared types for the `./buddy migrate:project` migrators (Laravel,
|
|
32
|
-
* Rails, …). Each driver consumes a source-project root and emits
|
|
33
|
-
* Stacks-shaped files under a target root, returning a structured
|
|
34
|
-
* report so the CLI can show the user what landed and what got
|
|
35
|
-
* skipped.
|
|
36
|
-
*
|
|
37
|
-
* The migration is intentionally best-effort. The drivers prioritise
|
|
38
|
-
* the high-value 80% (schema → migrations, models, env) and surface
|
|
39
|
-
* unrecognised constructs as `skipped` entries rather than throwing —
|
|
40
|
-
* a partial port the user can clean up beats a full failure mid-run.
|
|
41
|
-
*
|
|
42
|
-
* stacksjs/stacks#1241.
|
|
43
|
-
*/
|
|
44
|
-
export type SourceFramework = 'laravel' | 'rails';
|
package/dist/migrators/types.js
DELETED
|
File without changes
|