api-core-lib 12.0.73 → 12.0.75
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.cjs +194 -71
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -74,6 +74,110 @@ var import_chalk = __toESM(require("chalk"), 1);
|
|
|
74
74
|
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
75
75
|
var import_swagger_parser = __toESM(require("@apidevtools/swagger-parser"), 1);
|
|
76
76
|
var import_openapi_types = __toESM(require_dist(), 1);
|
|
77
|
+
|
|
78
|
+
// src/generator/core/_propToZod.ts
|
|
79
|
+
function _propToZod(prop) {
|
|
80
|
+
let zodChain;
|
|
81
|
+
const getRuleMessage = (rule, defaultKey) => {
|
|
82
|
+
const customMessages = prop.errorMessages;
|
|
83
|
+
const message = customMessages?.[rule] || defaultKey;
|
|
84
|
+
return `{ "message": "${message}" }`;
|
|
85
|
+
};
|
|
86
|
+
switch (prop.type) {
|
|
87
|
+
case "string": {
|
|
88
|
+
if (prop.enum && prop.enum.length > 0) {
|
|
89
|
+
const messages = {
|
|
90
|
+
required_error: prop.errorMessages?.required || "validation.required",
|
|
91
|
+
invalid_type_error: prop.errorMessages?.invalid_type || "validation.string.enum"
|
|
92
|
+
};
|
|
93
|
+
zodChain = `z.enum(${JSON.stringify(prop.enum)}, ${JSON.stringify(messages)})`;
|
|
94
|
+
} else {
|
|
95
|
+
zodChain = "z.string()";
|
|
96
|
+
if (prop.isRequired) {
|
|
97
|
+
zodChain += `.min(1, ${getRuleMessage("minLength", "validation.string.nonempty")})`;
|
|
98
|
+
}
|
|
99
|
+
if (prop.maxLength !== void 0) {
|
|
100
|
+
zodChain += `.max(${prop.maxLength}, ${getRuleMessage("maxLength", "validation.string.max")})`;
|
|
101
|
+
}
|
|
102
|
+
if (prop.pattern) {
|
|
103
|
+
zodChain += `.regex(/${prop.pattern}/, ${getRuleMessage("pattern", "validation.string.regex")})`;
|
|
104
|
+
}
|
|
105
|
+
if (prop.format) {
|
|
106
|
+
const msg = getRuleMessage("format", `validation.string.${prop.format}`);
|
|
107
|
+
if (prop.format === "email") zodChain += `.email(${msg})`;
|
|
108
|
+
if (prop.format === "url") zodChain += `.url(${msg})`;
|
|
109
|
+
if (prop.format === "uuid") zodChain += `.uuid(${msg})`;
|
|
110
|
+
if (prop.format === "datetime") zodChain += `.datetime(${msg})`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "integer": {
|
|
116
|
+
zodChain = `z.number().int(${getRuleMessage("integer", "validation.number.integer")})`;
|
|
117
|
+
if (prop.minimum !== void 0) {
|
|
118
|
+
zodChain += `.min(${prop.minimum}, ${getRuleMessage("minimum", "validation.number.min")})`;
|
|
119
|
+
}
|
|
120
|
+
if (prop.maximum !== void 0) {
|
|
121
|
+
zodChain += `.max(${prop.maximum}, ${getRuleMessage("maximum", "validation.number.max")})`;
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case "number": {
|
|
126
|
+
zodChain = "z.number()";
|
|
127
|
+
if (prop.minimum !== void 0) {
|
|
128
|
+
zodChain += `.min(${prop.minimum}, ${getRuleMessage("minimum", "validation.number.min")})`;
|
|
129
|
+
}
|
|
130
|
+
if (prop.maximum !== void 0) {
|
|
131
|
+
zodChain += `.max(${prop.maximum}, ${getRuleMessage("maximum", "validation.number.max")})`;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "boolean": {
|
|
136
|
+
zodChain = "z.boolean()";
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "array": {
|
|
140
|
+
const itemSchema = prop.items ? _propToZod(prop.items) : "z.any()";
|
|
141
|
+
zodChain = `z.array(${itemSchema})`;
|
|
142
|
+
if (prop.minItems !== void 0) {
|
|
143
|
+
zodChain += `.min(${prop.minItems}, ${getRuleMessage("minItems", "validation.array.min")})`;
|
|
144
|
+
}
|
|
145
|
+
if (prop.maxItems !== void 0) {
|
|
146
|
+
zodChain += `.max(${prop.maxItems}, ${getRuleMessage("maxItems", "validation.array.max")})`;
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "object": {
|
|
151
|
+
if (prop.properties && prop.properties.length > 0) {
|
|
152
|
+
const shape = prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
153
|
+
zodChain = `z.object({
|
|
154
|
+
${shape}
|
|
155
|
+
})`;
|
|
156
|
+
} else {
|
|
157
|
+
zodChain = "z.record(z.unknown())";
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
default:
|
|
162
|
+
zodChain = "z.any()";
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
if (prop.description) {
|
|
166
|
+
zodChain += `.describe(${JSON.stringify(prop.description)})`;
|
|
167
|
+
}
|
|
168
|
+
if (!prop.isRequired) {
|
|
169
|
+
zodChain += ".optional()";
|
|
170
|
+
}
|
|
171
|
+
if (prop.isNullable) {
|
|
172
|
+
zodChain += ".nullable()";
|
|
173
|
+
}
|
|
174
|
+
if (prop.defaultValue !== void 0) {
|
|
175
|
+
zodChain += `.default(${JSON.stringify(prop.defaultValue)})`;
|
|
176
|
+
}
|
|
177
|
+
return zodChain;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/generator/index.ts
|
|
77
181
|
var DEBUG_MODE = process.env.DEBUG === "true";
|
|
78
182
|
var debugLog = (title, data) => DEBUG_MODE && console.log(import_chalk.default.yellow(`
|
|
79
183
|
[DEBUG: ${title}]`), import_util.default.inspect(data, { depth: 5, colors: true }));
|
|
@@ -276,25 +380,109 @@ ${prop.example ? ` * @example ${JSON.stringify(prop.example)}
|
|
|
276
380
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "types.ts"), typesContent);
|
|
277
381
|
console.log(import_chalk.default.gray(` \u2713 types.ts`));
|
|
278
382
|
indexContent.push(`export * from './types';`);
|
|
279
|
-
|
|
280
|
-
import { z } from 'zod';
|
|
281
|
-
|
|
282
|
-
|
|
383
|
+
const validationFileHeader = `// This file is auto-generated.
|
|
384
|
+
import { z, ZodIssueCode, ZodError } from 'zod';
|
|
385
|
+
${enumsToImport.length > 0 ? `import { ${enumsToImport.join(", ")} } from './enums';
|
|
386
|
+
` : ""}
|
|
387
|
+
// =============================================================================
|
|
388
|
+
// I. \u0625\u0639\u062F\u0627\u062F \u062E\u0631\u064A\u0637\u0629 \u0623\u062E\u0637\u0627\u0621 \u0639\u0627\u0644\u0645\u064A\u0629 (Global Error Map) \u0644\u062F\u0639\u0645 \u0627\u0644\u062A\u0631\u062C\u0645\u0629
|
|
389
|
+
// =============================================================================
|
|
390
|
+
//
|
|
391
|
+
// \u0645\u0644\u0627\u062D\u0638\u0629 \u0647\u0627\u0645\u0629: \u0641\u064A \u062A\u0637\u0628\u064A\u0642 \u062D\u0642\u064A\u0642\u064A\u060C \u064A\u062C\u0628 \u062A\u0639\u0631\u064A\u0641 \u0647\u0630\u0647 \u0627\u0644\u062E\u0631\u064A\u0637\u0629 \u0645\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0641\u0642\u0637
|
|
392
|
+
// \u0641\u064A \u0646\u0642\u0637\u0629 \u0627\u0644\u062F\u062E\u0648\u0644 \u0644\u062A\u0637\u0628\u064A\u0642\u0643 (entry-point). \u062A\u0645 \u0648\u0636\u0639\u0647\u0627 \u0647\u0646\u0627 \u0644\u0644\u062A\u0648\u0636\u064A\u062D.
|
|
393
|
+
//
|
|
394
|
+
// \u0627\u0644\u0647\u062F\u0641: \u0631\u0628\u0637 \u0645\u0641\u0627\u062A\u064A\u062D \u0627\u0644\u062A\u0631\u062C\u0645\u0629 (\u0645\u062B\u0644 "validation.required") \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0648\u0644\u064A\u062F\u0647\u0627
|
|
395
|
+
// \u0641\u064A \u0627\u0644\u0645\u062E\u0637\u0637\u0627\u062A \u0623\u062F\u0646\u0627\u0647 \u0628\u0631\u0633\u0627\u0626\u0644 \u062D\u0642\u064A\u0642\u064A\u0629 \u064A\u0645\u0643\u0646 \u0639\u0631\u0636\u0647\u0627 \u0644\u0644\u0645\u0633\u062A\u062E\u062F\u0645.
|
|
396
|
+
|
|
397
|
+
// 1. \u0645\u062B\u0627\u0644 \u0628\u0633\u064A\u0637 \u0644\u062F\u0627\u0644\u0629 \u062A\u0631\u062C\u0645\u0629 (\u0641\u064A \u062A\u0637\u0628\u064A\u0642\u0643 \u0633\u062A\u0633\u062A\u062E\u062F\u0645 \u0645\u0643\u062A\u0628\u0629 \u0645\u062B\u0644 i18next)
|
|
398
|
+
const t = (key: string, args?: any): string => {
|
|
399
|
+
// \u0647\u0646\u0627 \u062A\u0636\u0639 \u0645\u0646\u0637\u0642 \u0627\u0644\u062A\u0631\u062C\u0645\u0629 \u0627\u0644\u062D\u0642\u064A\u0642\u064A \u0627\u0644\u062E\u0627\u0635 \u0628\u0643.
|
|
400
|
+
// \u0647\u0630\u0627 \u0645\u062C\u0631\u062F \u0645\u062B\u0627\u0644 \u0628\u0633\u064A\u0637.
|
|
401
|
+
if (key === 'validation.string.nonempty') return '\u0647\u0630\u0627 \u0627\u0644\u062D\u0642\u0644 \u0645\u0637\u0644\u0648\u0628 \u0648\u0644\u0627 \u064A\u0645\u0643\u0646 \u0623\u0646 \u064A\u0643\u0648\u0646 \u0641\u0627\u0631\u063A\u064B\u0627.';
|
|
402
|
+
if (key === 'validation.string.email') return '\u0627\u0644\u0631\u062C\u0627\u0621 \u0625\u062F\u062E\u0627\u0644 \u0628\u0631\u064A\u062F \u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A \u0635\u0627\u0644\u062D.';
|
|
403
|
+
if (key === 'validation.required') return '\u0647\u0630\u0627 \u0627\u0644\u062D\u0642\u0644 \u0625\u0644\u0632\u0627\u0645\u064A.';
|
|
404
|
+
// \u0631\u0633\u0627\u0644\u0629 \u0627\u062D\u062A\u064A\u0627\u0637\u064A\u0629
|
|
405
|
+
return key.split('.').pop()?.replace(/_/g, ' ') || key;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// 2. \u062A\u0639\u0631\u064A\u0641 \u062E\u0631\u064A\u0637\u0629 \u0627\u0644\u0623\u062E\u0637\u0627\u0621 \u0627\u0644\u0645\u062E\u0635\u0635\u0629
|
|
409
|
+
const customErrorMap: z.ZodErrorMap = (issue, ctx) => {
|
|
410
|
+
if (issue.message) {
|
|
411
|
+
// \u0625\u0630\u0627 \u0643\u0627\u0646\u062A \u0627\u0644\u0631\u0633\u0627\u0644\u0629 \u0647\u064A \u0645\u0641\u062A\u0627\u062D \u062A\u0631\u062C\u0645\u0629\u060C \u0642\u0645 \u0628\u062A\u0631\u062C\u0645\u062A\u0647\u0627
|
|
412
|
+
if (issue.message.startsWith('validation.')) {
|
|
413
|
+
return { message: t(issue.message) };
|
|
414
|
+
}
|
|
415
|
+
// \u0648\u0625\u0644\u0627\u060C \u0641\u0647\u064A \u0631\u0633\u0627\u0644\u0629 \u0645\u062E\u0635\u0635\u0629 \u0645\u0628\u0627\u0634\u0631\u0629
|
|
416
|
+
return { message: issue.message };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// \u062A\u0631\u062C\u0645\u0629 \u0627\u0644\u0623\u062E\u0637\u0627\u0621 \u0627\u0644\u0627\u0641\u062A\u0631\u0627\u0636\u064A\u0629 \u0627\u0644\u0639\u0627\u0645\u0629
|
|
420
|
+
if (issue.code === ZodIssueCode.invalid_type) {
|
|
421
|
+
return { message: t('validation.required') };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return { message: ctx.defaultError };
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// 3. \u0642\u0645 \u0628\u062A\u0639\u064A\u064A\u0646 \u0627\u0644\u062E\u0631\u064A\u0637\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629 \u0639\u0627\u0644\u0645\u064A\u064B\u0627 (\u064A\u062C\u0628 \u0639\u0645\u0644 \u0630\u0644\u0643 \u0645\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0641\u064A \u0627\u0644\u062A\u0637\u0628\u064A\u0642)
|
|
428
|
+
z.setErrorMap(customErrorMap);
|
|
283
429
|
|
|
430
|
+
// =============================================================================
|
|
431
|
+
// II. \u0645\u062E\u0637\u0637\u0627\u062A \u0627\u0644\u062A\u062D\u0642\u0642 \u0627\u0644\u0645\u0648\u0644\u062F\u0629 \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627 (Generated Validation Schemas)
|
|
432
|
+
// =============================================================================
|
|
284
433
|
`;
|
|
434
|
+
let validationContent = validationFileHeader;
|
|
285
435
|
for (const typeName of schemasToImport) {
|
|
286
436
|
const parsedSchema = allSchemas.get(typeName);
|
|
287
437
|
if (parsedSchema) {
|
|
288
438
|
let zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
439
|
+
validationContent += `
|
|
440
|
+
/**
|
|
441
|
+
* Schema for validating ${typeName} objects.
|
|
442
|
+
* @description ${parsedSchema.description || ""}
|
|
443
|
+
*/
|
|
444
|
+
`;
|
|
289
445
|
validationContent += `export const ${typeName}Schema = z.object({
|
|
290
446
|
${zodShape}
|
|
291
447
|
});
|
|
292
|
-
|
|
448
|
+
`;
|
|
449
|
+
validationContent += `export type ${typeName}Schema = z.infer<typeof ${typeName}Schema>;
|
|
293
450
|
`;
|
|
294
451
|
}
|
|
295
452
|
}
|
|
453
|
+
if (schemasToImport.length > 0) {
|
|
454
|
+
const firstSchemaName = schemasToImport[0];
|
|
455
|
+
validationContent += `
|
|
456
|
+
// =============================================================================
|
|
457
|
+
// III. \u0645\u062B\u0627\u0644 \u0639\u0644\u0649 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0627\u0644\u0645\u062E\u0637\u0637 \u0641\u064A \u0627\u0644\u0643\u0648\u062F
|
|
458
|
+
// =============================================================================
|
|
459
|
+
/*
|
|
460
|
+
function
|
|
461
|
+
|
|
462
|
+
validate${firstSchemaName}(data: unknown) {
|
|
463
|
+
try {
|
|
464
|
+
const validatedData = ${firstSchemaName}Schema.parse(data);
|
|
465
|
+
console.log("\u2705 Validation successful:", validatedData);
|
|
466
|
+
return { success: true, data: validatedData };
|
|
467
|
+
} catch (error) {
|
|
468
|
+
if (error instanceof ZodError) {
|
|
469
|
+
console.error("\u274C Validation failed:");
|
|
470
|
+
// .format() is great for getting a structured error object
|
|
471
|
+
console.log(JSON.stringify(error.format(), null, 2));
|
|
472
|
+
return { success: false, errors: error.format() };
|
|
473
|
+
}
|
|
474
|
+
throw error; // Rethrow other unexpected errors
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Example usage with invalid data:
|
|
479
|
+
const invalidData = { /* ... \u0628\u064A\u0627\u0646\u0627\u062A \u063A\u064A\u0631 \u0643\u0627\u0645\u0644\u0629 \u0623\u0648 \u062E\u0627\u0637\u0626\u0629 ... */ };
|
|
480
|
+
// validate${firstSchemaName}(invalidData);
|
|
481
|
+
*/
|
|
482
|
+
`;
|
|
483
|
+
}
|
|
296
484
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "validation.ts"), validationContent);
|
|
297
|
-
console.log(import_chalk.default.gray(` \u2713 validation.ts`));
|
|
485
|
+
console.log(import_chalk.default.gray(` \u2713 validation.ts (with integrated usage example)`));
|
|
298
486
|
indexContent.push(`export * from './validation';`);
|
|
299
487
|
let mocksContent = `// This file is auto-generated.
|
|
300
488
|
import type { ${schemasToImport.join(", ")} } from './types';
|
|
@@ -321,71 +509,6 @@ import type { ${schemasToImport.join(", ")} } from './types';
|
|
|
321
509
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "index.ts"), indexContent.join("\n"));
|
|
322
510
|
console.log(import_chalk.default.gray(` \u2713 index.ts`));
|
|
323
511
|
}
|
|
324
|
-
function _propToZod(prop) {
|
|
325
|
-
let chain;
|
|
326
|
-
const requiredErrorMessage = { required_error: `${prop.name} is required.` };
|
|
327
|
-
switch (prop.type) {
|
|
328
|
-
case "string":
|
|
329
|
-
chain = `z.string({ ...requiredErrorMessage, invalid_type_error: "Expected a string for ${prop.name}" })`;
|
|
330
|
-
if (prop.format === "email") chain += `.email({ message: "Invalid email address for ${prop.name}" })`;
|
|
331
|
-
if (prop.format === "uuid") chain += `.uuid({ message: "Invalid UUID for ${prop.name}" })`;
|
|
332
|
-
if (prop.format === "url") chain += `.url({ message: "Invalid URL for ${prop.name}" })`;
|
|
333
|
-
if (prop.format === "datetime") chain += `.datetime({ message: "Invalid datetime format for ${prop.name}" })`;
|
|
334
|
-
if (prop.minLength !== void 0) chain += `.min(${prop.minLength}, { message: "${prop.name} must be at least ${prop.minLength} characters long" })`;
|
|
335
|
-
if (prop.maxLength !== void 0) chain += `.max(${prop.maxLength}, { message: "${prop.name} must be at most ${prop.maxLength} characters long" })`;
|
|
336
|
-
if (prop.pattern) chain += `.regex(/${prop.pattern}/, { message: "Invalid format for ${prop.name}" })`;
|
|
337
|
-
if (prop.enum) {
|
|
338
|
-
if (prop.enum.length > 0) {
|
|
339
|
-
chain = `z.enum(${JSON.stringify(prop.enum)})`;
|
|
340
|
-
} else {
|
|
341
|
-
chain = `z.string().refine(() => false, { message: "Enum for ${prop.name} is empty" })`;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
break;
|
|
345
|
-
case "integer":
|
|
346
|
-
chain = `z.number({ ...requiredErrorMessage, invalid_type_error: "Expected a number for ${prop.name}" }).int({ message: "${prop.name} must be an integer" })`;
|
|
347
|
-
if (prop.minimum !== void 0) chain += `.min(${prop.minimum}, { message: "${prop.name} must be at least ${prop.minimum}" })`;
|
|
348
|
-
if (prop.maximum !== void 0) chain += `.max(${prop.maximum}, { message: "${prop.name} must be at most ${prop.maximum}" })`;
|
|
349
|
-
break;
|
|
350
|
-
case "number":
|
|
351
|
-
chain = `z.number({ ...requiredErrorMessage, invalid_type_error: "Expected a number for ${prop.name}" })`;
|
|
352
|
-
if (prop.minimum !== void 0) chain += `.min(${prop.minimum}, { message: "${prop.name} must be at least ${prop.minimum}" })`;
|
|
353
|
-
if (prop.maximum !== void 0) chain += `.max(${prop.maximum}, { message: "${prop.name} must be at most ${prop.maximum}" })`;
|
|
354
|
-
break;
|
|
355
|
-
case "boolean":
|
|
356
|
-
chain = `z.boolean({ ...requiredErrorMessage, invalid_type_error: "Expected a boolean for ${prop.name}" })`;
|
|
357
|
-
break;
|
|
358
|
-
case "array":
|
|
359
|
-
const itemSchema = prop.items ? _propToZod(prop.items) : "z.any()";
|
|
360
|
-
chain = `z.array(${itemSchema})`;
|
|
361
|
-
if (prop.minItems !== void 0) chain += `.min(${prop.minItems}, { message: "${prop.name} must contain at least ${prop.minItems} item(s)" })`;
|
|
362
|
-
if (prop.maxItems !== void 0) chain += `.max(${prop.maxItems}, { message: "${prop.name} must contain at most ${prop.maxItems} item(s)" })`;
|
|
363
|
-
break;
|
|
364
|
-
case "object":
|
|
365
|
-
if (prop.properties && prop.properties.length > 0) {
|
|
366
|
-
const shape = prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
367
|
-
chain = `z.object({
|
|
368
|
-
${shape}
|
|
369
|
-
})`;
|
|
370
|
-
} else {
|
|
371
|
-
chain = "z.record(z.unknown())";
|
|
372
|
-
}
|
|
373
|
-
break;
|
|
374
|
-
default:
|
|
375
|
-
chain = "z.any()";
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
if (prop.description) {
|
|
379
|
-
chain += `.describe(${JSON.stringify(prop.description)})`;
|
|
380
|
-
}
|
|
381
|
-
if (!prop.isRequired) {
|
|
382
|
-
chain += ".optional()";
|
|
383
|
-
}
|
|
384
|
-
if (prop.isNullable) {
|
|
385
|
-
chain += ".nullable()";
|
|
386
|
-
}
|
|
387
|
-
return chain;
|
|
388
|
-
}
|
|
389
512
|
function _propToMock(prop) {
|
|
390
513
|
if (prop.example) return prop.example;
|
|
391
514
|
if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
|