@omnifyjp/ts 8.0.5 → 8.0.7
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/dist/cli.js
CHANGED
|
@@ -241,6 +241,7 @@ program
|
|
|
241
241
|
let phpSkippedSibling = 0;
|
|
242
242
|
let phpShadowedOutOfSync = 0;
|
|
243
243
|
let phpShadowedAtRisk = 0;
|
|
244
|
+
let phpNotExtendingBase = 0;
|
|
244
245
|
const skippedSiblingExamples = [];
|
|
245
246
|
for (const file of phpFiles) {
|
|
246
247
|
const filePath = resolve(configDir, file.path);
|
|
@@ -261,7 +262,10 @@ program
|
|
|
261
262
|
// assignment with no error anywhere.
|
|
262
263
|
const shadowResults = evaluateShadowingConflicts(existing, file.conflicts ?? []);
|
|
263
264
|
if (shadowResults.length > 0) {
|
|
264
|
-
if (shadowResults.some((r) => r.drift === '
|
|
265
|
+
if (shadowResults.some((r) => r.drift === 'not-extending-base')) {
|
|
266
|
+
phpNotExtendingBase++;
|
|
267
|
+
}
|
|
268
|
+
else if (shadowResults.some((r) => r.drift === 'out-of-sync')) {
|
|
265
269
|
phpShadowedOutOfSync++;
|
|
266
270
|
}
|
|
267
271
|
else {
|
|
@@ -319,7 +323,7 @@ program
|
|
|
319
323
|
console.log(` ${phpCreated} files created (user-editable)`);
|
|
320
324
|
if (phpSkipped > 0)
|
|
321
325
|
console.log(` ${phpSkipped} files skipped (already exist)`);
|
|
322
|
-
if (phpShadowedOutOfSync > 0 || phpShadowedAtRisk > 0) {
|
|
326
|
+
if (phpShadowedOutOfSync > 0 || phpShadowedAtRisk > 0 || phpNotExtendingBase > 0) {
|
|
323
327
|
const summaryParts = [];
|
|
324
328
|
if (phpShadowedOutOfSync > 0) {
|
|
325
329
|
summaryParts.push(`${phpShadowedOutOfSync} editable file(s) already out of sync with the base`);
|
|
@@ -327,6 +331,9 @@ program
|
|
|
327
331
|
if (phpShadowedAtRisk > 0) {
|
|
328
332
|
summaryParts.push(`${phpShadowedAtRisk} shadow a generated property and will drift on the next schema change`);
|
|
329
333
|
}
|
|
334
|
+
if (phpNotExtendingBase > 0) {
|
|
335
|
+
summaryParts.push(`${phpNotExtendingBase} do not extend their generated base model`);
|
|
336
|
+
}
|
|
330
337
|
console.warn(` ${summaryParts.join(', ')} (see warnings above).`);
|
|
331
338
|
}
|
|
332
339
|
if (phpSkippedSibling > 0) {
|
|
@@ -69,19 +69,16 @@ trait HasFiles
|
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
// One UPDATE, so the attach is atomic without a caller transaction.
|
|
73
|
+
// increment() with extra columns, not DB::raw('sort_order + '.$startOrder):
|
|
74
|
+
// raw SQL is typed literal-string, and the offset is bound here.
|
|
75
|
+
File::whereIn('id', $fileIds)->increment('sort_order', $startOrder, [
|
|
73
76
|
'fileable_type' => $this->getMorphClass(),
|
|
74
77
|
'fileable_id' => $this->getKey(),
|
|
75
78
|
'collection' => $collection,
|
|
76
79
|
'status' => FileStatusEnum::Permanent,
|
|
77
80
|
'expires_at' => null,
|
|
78
81
|
]);
|
|
79
|
-
|
|
80
|
-
// increment(), not DB::raw('sort_order + '.$startOrder): raw SQL is
|
|
81
|
-
// typed literal-string, and a column offset needs no SQL of our own.
|
|
82
|
-
if ($startOrder !== 0) {
|
|
83
|
-
File::whereIn('id', $fileIds)->increment('sort_order', $startOrder);
|
|
84
|
-
}
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
/**
|
|
@@ -15,9 +15,9 @@ export declare function shadowingPropertyConflicts(baseClass: string, baseArrayV
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function parseRedeclaredStringArray(source: string, prop: string): string[] | null;
|
|
17
17
|
export declare function formatShadowingDrift(baseValues: readonly string[], childValues: string[] | null): string;
|
|
18
|
-
export type ShadowingDrift = 'out-of-sync' | 'extra-only' | 'in-sync' | 'unknown';
|
|
18
|
+
export type ShadowingDrift = 'out-of-sync' | 'extra-only' | 'in-sync' | 'unknown' | 'not-extending-base';
|
|
19
19
|
export type ShadowingConflictResult = {
|
|
20
|
-
property: ShadowingEloquentProperty;
|
|
20
|
+
property: ShadowingEloquentProperty | 'extends';
|
|
21
21
|
message: string;
|
|
22
22
|
drift: ShadowingDrift;
|
|
23
23
|
};
|
|
@@ -109,6 +109,7 @@ export function shadowingPropertyConflicts(baseClass, baseArrayValues) {
|
|
|
109
109
|
hint: `redeclares $${prop}, which REPLACES ${baseClass}::$${prop} rather than ` +
|
|
110
110
|
`merging with it (PHP does not merge redeclared properties). ` +
|
|
111
111
|
`${shadowingConsequence(prop)} ${shadowingFixAdvice(prop, baseClass)}`,
|
|
112
|
+
baseClass,
|
|
112
113
|
...(baseValues ? { baseValues } : {}),
|
|
113
114
|
};
|
|
114
115
|
});
|
|
@@ -117,6 +118,7 @@ export function shadowingPropertyConflicts(baseClass, baseArrayValues) {
|
|
|
117
118
|
form: 'method',
|
|
118
119
|
pattern: CASTS_METHOD_PATTERN,
|
|
119
120
|
hint: shadowingCastsMethodHint(baseClass),
|
|
121
|
+
baseClass,
|
|
120
122
|
...(castsKeys ? { baseValues: castsKeys } : {}),
|
|
121
123
|
};
|
|
122
124
|
return [...propertyConflicts, methodCastsConflict];
|
|
@@ -197,9 +199,53 @@ function childValuesForConflict(existingRaw, conflict, baseValues) {
|
|
|
197
199
|
}
|
|
198
200
|
return parseRedeclaredStringArray(existingRaw, conflict.property);
|
|
199
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Issue #312: a redeclared property only REPLACES the base's when the class
|
|
204
|
+
* extends that base. Returns the class name and its `extends` target when the
|
|
205
|
+
* class does not reach `baseClass`, or null when it does (or the class
|
|
206
|
+
* declaration cannot be read, so the shadowing checks stay as they were).
|
|
207
|
+
*/
|
|
208
|
+
function notExtendingBase(stripped, baseClass) {
|
|
209
|
+
const decl = stripped.match(/\bclass\s+(\w+)(?:\s+extends\s+([\\\w]+))?/);
|
|
210
|
+
if (!decl) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const parent = decl[2] ?? null;
|
|
214
|
+
if (parent !== null) {
|
|
215
|
+
const name = parent.split('\\').pop();
|
|
216
|
+
if (name === baseClass) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const aliased = new RegExp(String.raw `\buse\s+[\\\w]*\\` + baseClass + String.raw `\s+as\s+` + name + String.raw `\s*;`);
|
|
220
|
+
if (aliased.test(stripped)) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return { className: decl[1], parent };
|
|
225
|
+
}
|
|
226
|
+
function notExtendingBaseHint(className, parent, baseClass) {
|
|
227
|
+
return (`${className} ${parent ? `extends ${parent}, not ${baseClass}` : `does not extend ${baseClass}`}, ` +
|
|
228
|
+
`so nothing omnify generates on ${baseClass} (relations, casts, $fillable, $hidden) ` +
|
|
229
|
+
`applies to this model. Its own $fillable / $casts replace nothing and are not checked. ` +
|
|
230
|
+
`Fix: extend ${baseClass}. If a parent class in between already extends it, or this ` +
|
|
231
|
+
`model is intentionally detached, add \`/** @omnify-base intentional */\` to silence this warning.`);
|
|
232
|
+
}
|
|
200
233
|
export function evaluateShadowingConflicts(existingRaw, conflicts) {
|
|
201
234
|
const stripped = stripPhpComments(existingRaw);
|
|
202
235
|
const results = [];
|
|
236
|
+
const baseClass = conflicts.find((c) => c.baseClass)?.baseClass;
|
|
237
|
+
const detached = baseClass ? notExtendingBase(stripped, baseClass) : null;
|
|
238
|
+
if (baseClass && detached) {
|
|
239
|
+
if (existingRaw.includes('@omnify-base intentional')) {
|
|
240
|
+
return results;
|
|
241
|
+
}
|
|
242
|
+
results.push({
|
|
243
|
+
property: 'extends',
|
|
244
|
+
message: notExtendingBaseHint(detached.className, detached.parent, baseClass),
|
|
245
|
+
drift: 'not-extending-base',
|
|
246
|
+
});
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
203
249
|
const skipsPropertyCasts = childDeclaresCastsMethod(stripped);
|
|
204
250
|
for (const conflict of conflicts) {
|
|
205
251
|
if (conflict.property === 'casts' && conflict.form !== 'method' && skipsPropertyCasts) {
|
package/dist/php/types.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ export type ShadowingPropertyConflict = {
|
|
|
58
58
|
readonly baseValues?: readonly string[];
|
|
59
59
|
/** Property redeclaration vs method redeclaration (issue #245: casts()). */
|
|
60
60
|
readonly form?: 'property' | 'method';
|
|
61
|
+
/** Generated base the editable class should extend (issue #312). */
|
|
62
|
+
readonly baseClass?: string;
|
|
61
63
|
};
|
|
62
64
|
/** Category of base file for modular path resolution. */
|
|
63
65
|
export type BaseCategory = 'Models' | 'Controllers' | 'Requests' | 'Resources' | 'Services' | 'Enums' | 'Traits' | 'Locales' | 'Policies';
|