routesync 1.0.15 → 1.0.17
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 +14 -0
- package/dist/cli.js +49 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -249,6 +249,20 @@ const api = defineApi({
|
|
|
249
249
|
}, config)
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
> **Note on Laravel Auto-generation:** When using `routesync sync` or `routesync scan` with the `--zod` flag, RouteSync uses PHP Reflection to automatically generate Zod schemas based on your backend validation rules.
|
|
253
|
+
>
|
|
254
|
+
> **Important:** To ensure your schemas are detected automatically, you **must use Laravel `FormRequest` classes**. Inline `$request->validate([...])` calls inside Controller methods cannot be reliably extracted.
|
|
255
|
+
>
|
|
256
|
+
> ```php
|
|
257
|
+
> // ✅ DO THIS: RouteSync will generate Zod schemas automatically
|
|
258
|
+
> public function store(StoreProductRequest $request)
|
|
259
|
+
>
|
|
260
|
+
> // ❌ AVOID THIS: Validation rules will be ignored
|
|
261
|
+
> public function store(Request $request) {
|
|
262
|
+
> $request->validate([...]);
|
|
263
|
+
> }
|
|
264
|
+
> ```
|
|
265
|
+
|
|
252
266
|
---
|
|
253
267
|
|
|
254
268
|
### Auto-generate TanStack hooks from defineApi
|
package/dist/cli.js
CHANGED
|
@@ -8406,6 +8406,31 @@ foreach ($routes as $route) {
|
|
|
8406
8406
|
}
|
|
8407
8407
|
}
|
|
8408
8408
|
}
|
|
8409
|
+
|
|
8410
|
+
// Fallback: Try to parse $request->validate([...]) from source code
|
|
8411
|
+
if (empty($schema)) {
|
|
8412
|
+
$fileName = $reflector->getFileName();
|
|
8413
|
+
$startLine = $reflector->getStartLine();
|
|
8414
|
+
$endLine = $reflector->getEndLine();
|
|
8415
|
+
|
|
8416
|
+
if ($fileName && $startLine !== false && $endLine !== false) {
|
|
8417
|
+
$lines = file($fileName);
|
|
8418
|
+
// startLine is 1-indexed
|
|
8419
|
+
$methodSource = implode("", array_slice($lines, $startLine - 1, $endLine - $startLine + 1));
|
|
8420
|
+
|
|
8421
|
+
// Look for $request->validate([ ... ])
|
|
8422
|
+
if (preg_match('/\\\\$request->validate\\\\s*\\\\(\\\\s*\\\\[(.*?)\\\\]\\\\s*\\\\)/s', $methodSource, $matches)) {
|
|
8423
|
+
$rulesString = $matches[1];
|
|
8424
|
+
// Match 'field' => 'rules'
|
|
8425
|
+
preg_match_all('~[\\'"]([a-zA-Z0-9_.*]+)[\\'"]\\\\s*=>\\\\s*[\\'"](.*?)[\\'"]~', $rulesString, $ruleMatches);
|
|
8426
|
+
if (!empty($ruleMatches[1])) {
|
|
8427
|
+
foreach ($ruleMatches[1] as $index => $field) {
|
|
8428
|
+
$schema[$field] = $ruleMatches[2][$index];
|
|
8429
|
+
}
|
|
8430
|
+
}
|
|
8431
|
+
}
|
|
8432
|
+
}
|
|
8433
|
+
}
|
|
8409
8434
|
} catch (\\Exception $e) {}
|
|
8410
8435
|
}
|
|
8411
8436
|
}
|
|
@@ -8751,6 +8776,11 @@ var TypeGenerator = class {
|
|
|
8751
8776
|
const isOptional = col.nullable ? "?" : "";
|
|
8752
8777
|
lines.push(` ${col.name}${isOptional}: ${tsType}`);
|
|
8753
8778
|
}
|
|
8779
|
+
if (model.appends && model.appends.length > 0) {
|
|
8780
|
+
for (const append of model.appends) {
|
|
8781
|
+
lines.push(` ${append}?: unknown`);
|
|
8782
|
+
}
|
|
8783
|
+
}
|
|
8754
8784
|
lines.push(`}`);
|
|
8755
8785
|
lines.push(``);
|
|
8756
8786
|
}
|
|
@@ -8770,17 +8800,26 @@ var TypeGenerator = class {
|
|
|
8770
8800
|
}
|
|
8771
8801
|
}
|
|
8772
8802
|
if (hasSchemas) {
|
|
8803
|
+
const generatedSchemas = /* @__PURE__ */ new Map();
|
|
8773
8804
|
for (const route of manifest.routes) {
|
|
8774
|
-
if (route.schema && Object.keys(route.schema).length > 0) {
|
|
8775
|
-
|
|
8776
|
-
|
|
8805
|
+
if (route.schema && route.schema.rules && Object.keys(route.schema.rules).length > 0) {
|
|
8806
|
+
let actionName = toMethodName(route);
|
|
8807
|
+
let schemaName = actionName + "Schema";
|
|
8808
|
+
if (generatedSchemas.has(schemaName)) {
|
|
8809
|
+
const count = generatedSchemas.get(schemaName) + 1;
|
|
8810
|
+
generatedSchemas.set(schemaName, count);
|
|
8811
|
+
actionName = actionName + String(count);
|
|
8812
|
+
schemaName = actionName + "Schema";
|
|
8813
|
+
} else {
|
|
8814
|
+
generatedSchemas.set(schemaName, 1);
|
|
8815
|
+
}
|
|
8777
8816
|
lines.push(`export const ${schemaName} = z.object({`);
|
|
8778
|
-
for (const [field, rules] of Object.entries(route.schema)) {
|
|
8817
|
+
for (const [field, rules] of Object.entries(route.schema.rules)) {
|
|
8779
8818
|
const ruleStr = Array.isArray(rules) ? rules.join("|") : String(rules);
|
|
8780
8819
|
let zodRule = "z.string()";
|
|
8781
|
-
if (ruleStr.includes("numeric") || ruleStr.includes("integer")) {
|
|
8820
|
+
if (ruleStr.includes("numeric") || ruleStr.includes("integer") || ruleStr.includes("int")) {
|
|
8782
8821
|
zodRule = "z.number()";
|
|
8783
|
-
} else if (ruleStr.includes("boolean")) {
|
|
8822
|
+
} else if (ruleStr.includes("boolean") || ruleStr.includes("bool")) {
|
|
8784
8823
|
zodRule = "z.boolean()";
|
|
8785
8824
|
} else if (ruleStr.includes("array")) {
|
|
8786
8825
|
zodRule = "z.array(z.unknown())";
|
|
@@ -8797,7 +8836,8 @@ var TypeGenerator = class {
|
|
|
8797
8836
|
if (ruleStr.includes("nullable")) {
|
|
8798
8837
|
zodRule += ".nullable()";
|
|
8799
8838
|
}
|
|
8800
|
-
|
|
8839
|
+
const safeField = field.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/) ? field : `"${field}"`;
|
|
8840
|
+
lines.push(` ${safeField}: ${zodRule},`);
|
|
8801
8841
|
}
|
|
8802
8842
|
lines.push(`})`);
|
|
8803
8843
|
lines.push(`export type ${toTypeName(actionName + "Payload")} = z.infer<typeof ${schemaName}>`);
|
|
@@ -9100,7 +9140,7 @@ var ModelGenerator = class {
|
|
|
9100
9140
|
};
|
|
9101
9141
|
|
|
9102
9142
|
// packages/cli/src/commands/generate.ts
|
|
9103
|
-
var generateCommand = new Command("generate").description("Generate typed SDK, types, and hooks from route manifest").option("-m, --manifest <path>", "Path to route manifest", "routesync.manifest.json").option("-o, --output <path>", "Output directory", "src/api").option("--no-hooks", "Skip generating React hooks").option("--next-actions", "Generate Next.js Server Actions").option("--msw", "Generate MSW Mock Handlers").option("--echo", "Generate Laravel Echo Hooks").action(async (options) => {
|
|
9143
|
+
var generateCommand = new Command("generate").description("Generate typed SDK, types, and hooks from route manifest").option("-m, --manifest <path>", "Path to route manifest", "routesync.manifest.json").option("-o, --output <path>", "Output directory", "src/api").option("--no-hooks", "Skip generating React hooks").option("--next-actions", "Generate Next.js Server Actions").option("--msw", "Generate MSW Mock Handlers").option("--echo", "Generate Laravel Echo Hooks").option("--zod", "Generate Zod schemas for validation").action(async (options) => {
|
|
9104
9144
|
const spinner = ora("Generating SDK...").start();
|
|
9105
9145
|
try {
|
|
9106
9146
|
if (!import_fs_extra11.default.existsSync(options.manifest)) {
|
|
@@ -9113,7 +9153,7 @@ var generateCommand = new Command("generate").description("Generate typed SDK, t
|
|
|
9113
9153
|
spinner.text = "Generating types...";
|
|
9114
9154
|
await TypeGenerator.generate(manifest, options.output);
|
|
9115
9155
|
spinner.text = "Generating SDK...";
|
|
9116
|
-
await SDKGenerator.generate(manifest, options.output);
|
|
9156
|
+
await SDKGenerator.generate(manifest, options.output, options);
|
|
9117
9157
|
if (options.hooks !== false) {
|
|
9118
9158
|
spinner.text = "Generating hooks...";
|
|
9119
9159
|
await HookGenerator.generate(manifest, options.output);
|