strapi2front 0.4.1 → 0.5.0
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 +33 -45
- package/dist/bin/strapi2front.js +208 -231
- package/dist/bin/strapi2front.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +204 -227
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/bin/strapi2front.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import
|
|
3
|
+
import pc3 from 'picocolors';
|
|
4
4
|
import * as p from '@clack/prompts';
|
|
5
5
|
import fs5 from 'fs/promises';
|
|
6
6
|
import path6 from 'path';
|
|
7
7
|
import { spawn, execSync } from 'child_process';
|
|
8
8
|
import fs6 from 'fs';
|
|
9
9
|
import { loadConfig, detectStrapiVersion, fetchSchema, parseSchema } from '@strapi2front/core';
|
|
10
|
-
import { generateByFeature
|
|
10
|
+
import { generateByFeature } from '@strapi2front/generators';
|
|
11
11
|
|
|
12
12
|
var FRAMEWORK_DETECTORS = {
|
|
13
13
|
astro: {
|
|
@@ -174,12 +174,12 @@ function getMajorVersion(version) {
|
|
|
174
174
|
return match ? parseInt(match[1], 10) : null;
|
|
175
175
|
}
|
|
176
176
|
async function runInitPrompts(detection) {
|
|
177
|
-
p.intro(
|
|
177
|
+
p.intro(pc3.cyan("strapi2front setup"));
|
|
178
178
|
p.note(
|
|
179
179
|
[
|
|
180
|
-
`Framework: ${
|
|
181
|
-
`TypeScript: ${detection.typescript.enabled ?
|
|
182
|
-
`Package Manager: ${
|
|
180
|
+
`Framework: ${pc3.green(getFrameworkDisplayName(detection.framework.name))} ${detection.framework.version ? pc3.dim(`v${detection.framework.version}`) : ""}`,
|
|
181
|
+
`TypeScript: ${detection.typescript.enabled ? pc3.green("enabled") : pc3.yellow("disabled")}`,
|
|
182
|
+
`Package Manager: ${pc3.green(detection.packageManager.name)}`
|
|
183
183
|
].join("\n"),
|
|
184
184
|
"Detected Configuration"
|
|
185
185
|
);
|
|
@@ -191,13 +191,13 @@ async function runInitPrompts(detection) {
|
|
|
191
191
|
} else {
|
|
192
192
|
const majorVersion = getMajorVersion(detection.framework.version);
|
|
193
193
|
if (majorVersion !== null && majorVersion < 4) {
|
|
194
|
-
p.log.warn(
|
|
194
|
+
p.log.warn(pc3.yellow(`Astro v${majorVersion} detected. Upgrade to v4+ to enable Actions.`));
|
|
195
195
|
canGenerateActions = false;
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
let outputFormat = "typescript";
|
|
199
199
|
if (!detection.typescript.enabled) {
|
|
200
|
-
p.log.info(
|
|
200
|
+
p.log.info(pc3.dim("TypeScript not detected. Files will be generated as JavaScript with JSDoc annotations."));
|
|
201
201
|
outputFormat = "jsdoc";
|
|
202
202
|
}
|
|
203
203
|
const defaultUrl = "http://localhost:1337";
|
|
@@ -220,9 +220,22 @@ async function runInitPrompts(detection) {
|
|
|
220
220
|
return null;
|
|
221
221
|
}
|
|
222
222
|
const strapiUrl = (strapiUrlInput || "").trim() || defaultUrl;
|
|
223
|
+
p.log.message(
|
|
224
|
+
pc3.dim(
|
|
225
|
+
`
|
|
226
|
+
To generate a token: Strapi Admin > Settings > API Tokens > Create new API Token
|
|
227
|
+
Required permissions:
|
|
228
|
+
Content-type-builder:
|
|
229
|
+
- Components: getComponents, getComponent
|
|
230
|
+
- Content-types: getContentTypes, getContentType
|
|
231
|
+
I18n (if using localization):
|
|
232
|
+
- Locales: listLocales
|
|
233
|
+
`
|
|
234
|
+
)
|
|
235
|
+
);
|
|
223
236
|
const strapiToken = await p.text({
|
|
224
|
-
message: "What is your Strapi
|
|
225
|
-
placeholder: "Press Enter to skip (you can add it later in .env)"
|
|
237
|
+
message: "What is your Strapi sync token?",
|
|
238
|
+
placeholder: "Press Enter to skip (you can add it later in .env as STRAPI_SYNC_TOKEN)"
|
|
226
239
|
});
|
|
227
240
|
if (p.isCancel(strapiToken)) {
|
|
228
241
|
p.cancel("Setup cancelled");
|
|
@@ -230,7 +243,7 @@ async function runInitPrompts(detection) {
|
|
|
230
243
|
}
|
|
231
244
|
const trimmedToken = (strapiToken || "").trim();
|
|
232
245
|
if (trimmedToken === "") {
|
|
233
|
-
p.log.info(
|
|
246
|
+
p.log.info(pc3.dim("Token skipped. Remember to add STRAPI_SYNC_TOKEN to your .env file later."));
|
|
234
247
|
}
|
|
235
248
|
const strapiVersion = await p.select({
|
|
236
249
|
message: "What version of Strapi are you using?",
|
|
@@ -244,7 +257,7 @@ async function runInitPrompts(detection) {
|
|
|
244
257
|
p.cancel("Setup cancelled");
|
|
245
258
|
return null;
|
|
246
259
|
}
|
|
247
|
-
p.log.info(
|
|
260
|
+
p.log.info(pc3.dim(`Using Strapi ${strapiVersion}. This can be changed later in strapi.config.ts`));
|
|
248
261
|
const defaultPrefix = "/api";
|
|
249
262
|
const apiPrefixInput = await p.text({
|
|
250
263
|
message: "What is your Strapi API prefix?",
|
|
@@ -264,7 +277,7 @@ async function runInitPrompts(detection) {
|
|
|
264
277
|
}
|
|
265
278
|
const apiPrefix = (apiPrefixInput || "").trim() || defaultPrefix;
|
|
266
279
|
if (apiPrefix !== defaultPrefix) {
|
|
267
|
-
p.log.info(
|
|
280
|
+
p.log.info(pc3.dim(`Using custom API prefix: ${apiPrefix}`));
|
|
268
281
|
}
|
|
269
282
|
const outputDir = await p.text({
|
|
270
283
|
message: "Where should we generate the Strapi files?",
|
|
@@ -288,10 +301,24 @@ async function runInitPrompts(detection) {
|
|
|
288
301
|
hint: isTypeScript ? "Typed service functions for data fetching" : "Service functions with JSDoc annotations"
|
|
289
302
|
}
|
|
290
303
|
];
|
|
304
|
+
if (isTypeScript) {
|
|
305
|
+
featureOptions.push({
|
|
306
|
+
value: "schemas",
|
|
307
|
+
label: "Schemas",
|
|
308
|
+
hint: "Zod validation schemas for forms (React Hook Form, TanStack Form, etc.)"
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
featureOptions.push({
|
|
312
|
+
value: "upload",
|
|
313
|
+
label: "Upload",
|
|
314
|
+
hint: "File upload helpers (action + public client for browser uploads)"
|
|
315
|
+
});
|
|
291
316
|
if (canGenerateActions && isTypeScript) {
|
|
292
317
|
featureOptions.push({ value: "actions", label: "Astro Actions", hint: "Type-safe actions for client/server" });
|
|
293
318
|
}
|
|
294
|
-
const initialFeatures =
|
|
319
|
+
const initialFeatures = ["types", "services"];
|
|
320
|
+
if (isTypeScript) initialFeatures.push("schemas");
|
|
321
|
+
if (canGenerateActions && isTypeScript) initialFeatures.push("actions");
|
|
295
322
|
const features = await p.multiselect({
|
|
296
323
|
message: "What would you like to generate?",
|
|
297
324
|
options: featureOptions,
|
|
@@ -302,6 +329,19 @@ async function runInitPrompts(detection) {
|
|
|
302
329
|
p.cancel("Setup cancelled");
|
|
303
330
|
return null;
|
|
304
331
|
}
|
|
332
|
+
const selectedFeatures = new Set(features);
|
|
333
|
+
if (selectedFeatures.has("services") && !selectedFeatures.has("types")) {
|
|
334
|
+
selectedFeatures.add("types");
|
|
335
|
+
p.log.info(pc3.dim("Auto-enabled Types (required by Services)"));
|
|
336
|
+
}
|
|
337
|
+
if (selectedFeatures.has("actions") && !selectedFeatures.has("services")) {
|
|
338
|
+
selectedFeatures.add("services");
|
|
339
|
+
p.log.info(pc3.dim("Auto-enabled Services (required by Actions)"));
|
|
340
|
+
if (!selectedFeatures.has("types")) {
|
|
341
|
+
selectedFeatures.add("types");
|
|
342
|
+
p.log.info(pc3.dim("Auto-enabled Types (required by Services)"));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
305
345
|
return {
|
|
306
346
|
strapiUrl,
|
|
307
347
|
strapiToken: trimmedToken,
|
|
@@ -309,28 +349,31 @@ async function runInitPrompts(detection) {
|
|
|
309
349
|
apiPrefix,
|
|
310
350
|
outputFormat,
|
|
311
351
|
outputDir: (outputDir || "").trim() || "src/strapi",
|
|
312
|
-
|
|
313
|
-
generateServices:
|
|
352
|
+
generateTypes: selectedFeatures.has("types"),
|
|
353
|
+
generateServices: selectedFeatures.has("services"),
|
|
354
|
+
generateActions: canGenerateActions && isTypeScript && selectedFeatures.has("actions"),
|
|
355
|
+
generateSchemas: selectedFeatures.has("schemas"),
|
|
356
|
+
generateUpload: selectedFeatures.has("upload")
|
|
314
357
|
};
|
|
315
358
|
}
|
|
316
359
|
var logger = {
|
|
317
360
|
info: (message) => {
|
|
318
|
-
console.log(
|
|
361
|
+
console.log(pc3.blue("i"), message);
|
|
319
362
|
},
|
|
320
363
|
success: (message) => {
|
|
321
|
-
console.log(
|
|
364
|
+
console.log(pc3.green("v"), message);
|
|
322
365
|
},
|
|
323
366
|
warn: (message) => {
|
|
324
|
-
console.log(
|
|
367
|
+
console.log(pc3.yellow("!"), message);
|
|
325
368
|
},
|
|
326
369
|
error: (message) => {
|
|
327
|
-
console.log(
|
|
370
|
+
console.log(pc3.red("x"), message);
|
|
328
371
|
},
|
|
329
372
|
step: (message) => {
|
|
330
|
-
console.log(
|
|
373
|
+
console.log(pc3.cyan(">"), message);
|
|
331
374
|
},
|
|
332
375
|
dim: (message) => {
|
|
333
|
-
console.log(
|
|
376
|
+
console.log(pc3.dim(message));
|
|
334
377
|
},
|
|
335
378
|
newLine: () => {
|
|
336
379
|
console.log("");
|
|
@@ -390,22 +433,35 @@ async function initCommand(_options) {
|
|
|
390
433
|
apiPrefix: answers.apiPrefix,
|
|
391
434
|
outputFormat: answers.outputFormat,
|
|
392
435
|
outputDir: answers.outputDir,
|
|
393
|
-
|
|
436
|
+
generateTypes: answers.generateTypes,
|
|
394
437
|
generateServices: answers.generateServices,
|
|
438
|
+
generateActions: answers.generateActions,
|
|
439
|
+
generateSchemas: answers.generateSchemas,
|
|
440
|
+
generateUpload: answers.generateUpload,
|
|
395
441
|
moduleType
|
|
396
442
|
});
|
|
397
443
|
const configPath = path6.join(cwd, `strapi.config.${configExtension}`);
|
|
398
444
|
await fs5.writeFile(configPath, configContent, "utf-8");
|
|
399
445
|
const envPath = path6.join(cwd, ".env");
|
|
400
|
-
|
|
446
|
+
const envVars = {
|
|
401
447
|
STRAPI_URL: answers.strapiUrl,
|
|
402
|
-
|
|
403
|
-
|
|
448
|
+
STRAPI_SYNC_TOKEN: answers.strapiToken,
|
|
449
|
+
// Token for syncing schema (dev only)
|
|
450
|
+
STRAPI_TOKEN: ""
|
|
451
|
+
// Token for frontend API calls (production)
|
|
452
|
+
};
|
|
453
|
+
if (answers.generateUpload) {
|
|
454
|
+
envVars.PUBLIC_STRAPI_URL = answers.strapiUrl;
|
|
455
|
+
envVars.PUBLIC_STRAPI_UPLOAD_TOKEN = "";
|
|
456
|
+
}
|
|
457
|
+
await appendToEnvFile(envPath, envVars, answers.generateUpload);
|
|
458
|
+
const envExamplePath = path6.join(cwd, ".env.example");
|
|
459
|
+
await createEnvExampleFile(envExamplePath, answers.generateUpload);
|
|
404
460
|
const outputPath = path6.join(cwd, answers.outputDir);
|
|
405
461
|
await fs5.mkdir(outputPath, { recursive: true });
|
|
406
462
|
s.stop("Configuration files created");
|
|
407
463
|
const installDeps = await p.confirm({
|
|
408
|
-
message: "Install required dependencies (strapi2front, strapi
|
|
464
|
+
message: "Install required dependencies (strapi2front, @strapi/client)?",
|
|
409
465
|
initialValue: true
|
|
410
466
|
});
|
|
411
467
|
if (p.isCancel(installDeps)) {
|
|
@@ -414,49 +470,52 @@ async function initCommand(_options) {
|
|
|
414
470
|
}
|
|
415
471
|
if (installDeps) {
|
|
416
472
|
const installStrapi2frontCmd = getInstallDevCommand(packageManager.name, "strapi2front");
|
|
417
|
-
s.start(`Installing strapi2front... (${
|
|
473
|
+
s.start(`Installing strapi2front... (${pc3.dim(installStrapi2frontCmd)})`);
|
|
418
474
|
try {
|
|
419
475
|
await execAsync(installStrapi2frontCmd, cwd);
|
|
420
|
-
s.stop(`${
|
|
476
|
+
s.stop(`${pc3.green("\u2713")} strapi2front installed`);
|
|
421
477
|
} catch {
|
|
422
|
-
s.stop(`${
|
|
478
|
+
s.stop(`${pc3.red("\u2717")} Failed to install strapi2front`);
|
|
423
479
|
logger.warn(`Please install manually: ${installStrapi2frontCmd}`);
|
|
424
480
|
}
|
|
425
|
-
const installSdkCmd = getInstallCommand(packageManager.name, "strapi
|
|
426
|
-
s.start(`Installing strapi
|
|
481
|
+
const installSdkCmd = getInstallCommand(packageManager.name, "@strapi/client");
|
|
482
|
+
s.start(`Installing @strapi/client... (${pc3.dim(installSdkCmd)})`);
|
|
427
483
|
try {
|
|
428
484
|
await execAsync(installSdkCmd, cwd);
|
|
429
|
-
s.stop(`${
|
|
485
|
+
s.stop(`${pc3.green("\u2713")} @strapi/client installed`);
|
|
430
486
|
} catch {
|
|
431
|
-
s.stop(`${
|
|
487
|
+
s.stop(`${pc3.red("\u2717")} Failed to install @strapi/client`);
|
|
432
488
|
logger.warn(`Please install manually: ${installSdkCmd}`);
|
|
433
489
|
}
|
|
434
490
|
} else {
|
|
435
|
-
p.log.info(
|
|
436
|
-
p.log.info(
|
|
437
|
-
p.log.info(
|
|
491
|
+
p.log.info(pc3.dim("Remember to install dependencies manually:"));
|
|
492
|
+
p.log.info(pc3.dim(` ${getInstallDevCommand(packageManager.name, "strapi2front")}`));
|
|
493
|
+
p.log.info(pc3.dim(` ${getInstallCommand(packageManager.name, "@strapi/client")}`));
|
|
438
494
|
}
|
|
439
495
|
const configFileName = `strapi.config.${configExtension}`;
|
|
440
496
|
const fileExt = answers.outputFormat === "jsdoc" ? ".js" : ".ts";
|
|
441
497
|
p.note(
|
|
442
498
|
[
|
|
443
|
-
`${
|
|
444
|
-
`${
|
|
445
|
-
`${
|
|
499
|
+
`${pc3.green("\u2713")} Created ${pc3.cyan(configFileName)}`,
|
|
500
|
+
`${pc3.green("\u2713")} Updated ${pc3.cyan(".env")} with Strapi tokens`,
|
|
501
|
+
`${pc3.green("\u2713")} Created ${pc3.cyan(".env.example")} for reference`,
|
|
502
|
+
`${pc3.green("\u2713")} Created output directory ${pc3.cyan(answers.outputDir)}`,
|
|
446
503
|
"",
|
|
447
|
-
`Output format: ${
|
|
504
|
+
`Output format: ${pc3.cyan(answers.outputFormat === "jsdoc" ? "JavaScript (JSDoc)" : "TypeScript")}`,
|
|
448
505
|
"",
|
|
449
506
|
`Next steps:`,
|
|
450
|
-
` 1. Run ${
|
|
451
|
-
` 2. Import from ${
|
|
452
|
-
|
|
507
|
+
` 1. Run ${pc3.cyan("npx strapi2front sync")} to generate files`,
|
|
508
|
+
` 2. Import from ${pc3.cyan(answers.outputDir + "/collections/*" + fileExt)}`,
|
|
509
|
+
"",
|
|
510
|
+
`Docs: ${pc3.dim("https://strapi2front.dev/docs")}`
|
|
453
511
|
].join("\n"),
|
|
454
512
|
"Setup complete!"
|
|
455
513
|
);
|
|
456
|
-
p.outro(
|
|
514
|
+
p.outro(pc3.green("Happy coding!"));
|
|
457
515
|
} catch (error) {
|
|
458
516
|
s.stop("Failed to create configuration files");
|
|
459
517
|
logger.error(error instanceof Error ? error.message : "Unknown error");
|
|
518
|
+
logger.info(`Docs: ${pc3.dim("https://strapi2front.dev/docs/installation")}`);
|
|
460
519
|
process.exit(1);
|
|
461
520
|
}
|
|
462
521
|
}
|
|
@@ -469,7 +528,8 @@ function generateConfigFile(answers) {
|
|
|
469
528
|
export default defineConfig({
|
|
470
529
|
// Strapi connection
|
|
471
530
|
url: process.env.STRAPI_URL || "${answers.strapiUrl}",
|
|
472
|
-
|
|
531
|
+
// Token for syncing schema (uses STRAPI_SYNC_TOKEN with fallback to STRAPI_TOKEN)
|
|
532
|
+
token: process.env.STRAPI_SYNC_TOKEN || process.env.STRAPI_TOKEN,
|
|
473
533
|
|
|
474
534
|
// API prefix (default: "/api")
|
|
475
535
|
apiPrefix: "${answers.apiPrefix}",
|
|
@@ -480,17 +540,15 @@ export default defineConfig({
|
|
|
480
540
|
// Output configuration
|
|
481
541
|
output: {
|
|
482
542
|
path: "${answers.outputDir}",
|
|
483
|
-
types: "types",
|
|
484
|
-
services: "services",
|
|
485
|
-
actions: "actions/strapi",
|
|
486
|
-
structure: 'by-feature' // or 'by-layer'
|
|
487
543
|
},
|
|
488
544
|
|
|
489
545
|
// Features to generate
|
|
490
546
|
features: {
|
|
491
|
-
types:
|
|
547
|
+
types: ${answers.generateTypes},
|
|
492
548
|
services: ${answers.generateServices},
|
|
493
549
|
actions: ${answers.generateActions},
|
|
550
|
+
schemas: ${answers.generateSchemas}, // Zod schemas for form validation (React Hook Form, TanStack Form, etc.)
|
|
551
|
+
upload: ${answers.generateUpload}, // File upload helpers (action + public client)
|
|
494
552
|
},
|
|
495
553
|
|
|
496
554
|
// Strapi version
|
|
@@ -505,7 +563,8 @@ import { defineConfig } from "strapi2front";
|
|
|
505
563
|
export default defineConfig({
|
|
506
564
|
// Strapi connection
|
|
507
565
|
url: process.env.STRAPI_URL || "${answers.strapiUrl}",
|
|
508
|
-
|
|
566
|
+
// Token for syncing schema (uses STRAPI_SYNC_TOKEN with fallback to STRAPI_TOKEN)
|
|
567
|
+
token: process.env.STRAPI_SYNC_TOKEN || process.env.STRAPI_TOKEN,
|
|
509
568
|
|
|
510
569
|
// API prefix (default: "/api")
|
|
511
570
|
apiPrefix: "${answers.apiPrefix}",
|
|
@@ -519,16 +578,15 @@ export default defineConfig({
|
|
|
519
578
|
// Output configuration
|
|
520
579
|
output: {
|
|
521
580
|
path: "${answers.outputDir}",
|
|
522
|
-
types: "types",
|
|
523
|
-
services: "services",
|
|
524
|
-
structure: 'by-feature' // or 'by-layer'
|
|
525
581
|
},
|
|
526
582
|
|
|
527
583
|
// Features to generate
|
|
528
584
|
features: {
|
|
529
|
-
types:
|
|
585
|
+
types: ${answers.generateTypes},
|
|
530
586
|
services: ${answers.generateServices},
|
|
531
587
|
actions: false, // Actions require TypeScript
|
|
588
|
+
schemas: ${answers.generateSchemas},
|
|
589
|
+
upload: ${answers.generateUpload},
|
|
532
590
|
},
|
|
533
591
|
|
|
534
592
|
// Strapi version
|
|
@@ -542,7 +600,8 @@ const { defineConfig } = require("strapi2front");
|
|
|
542
600
|
module.exports = defineConfig({
|
|
543
601
|
// Strapi connection
|
|
544
602
|
url: process.env.STRAPI_URL || "${answers.strapiUrl}",
|
|
545
|
-
|
|
603
|
+
// Token for syncing schema (uses STRAPI_SYNC_TOKEN with fallback to STRAPI_TOKEN)
|
|
604
|
+
token: process.env.STRAPI_SYNC_TOKEN || process.env.STRAPI_TOKEN,
|
|
546
605
|
|
|
547
606
|
// API prefix (default: "/api")
|
|
548
607
|
apiPrefix: "${answers.apiPrefix}",
|
|
@@ -553,16 +612,15 @@ module.exports = defineConfig({
|
|
|
553
612
|
// Output configuration
|
|
554
613
|
output: {
|
|
555
614
|
path: "${answers.outputDir}",
|
|
556
|
-
types: "types",
|
|
557
|
-
services: "services",
|
|
558
|
-
structure: 'by-feature' // or 'by-layer'
|
|
559
615
|
},
|
|
560
616
|
|
|
561
617
|
// Features to generate
|
|
562
618
|
features: {
|
|
563
|
-
types:
|
|
619
|
+
types: ${answers.generateTypes},
|
|
564
620
|
services: ${answers.generateServices},
|
|
565
621
|
actions: false, // Actions require TypeScript
|
|
622
|
+
schemas: ${answers.generateSchemas},
|
|
623
|
+
upload: ${answers.generateUpload},
|
|
566
624
|
},
|
|
567
625
|
|
|
568
626
|
// Strapi version
|
|
@@ -570,7 +628,7 @@ module.exports = defineConfig({
|
|
|
570
628
|
});
|
|
571
629
|
`;
|
|
572
630
|
}
|
|
573
|
-
async function appendToEnvFile(envPath, variables) {
|
|
631
|
+
async function appendToEnvFile(envPath, variables, includeUploadComment = false) {
|
|
574
632
|
let content = "";
|
|
575
633
|
try {
|
|
576
634
|
content = await fs5.readFile(envPath, "utf-8");
|
|
@@ -583,6 +641,20 @@ async function appendToEnvFile(envPath, variables) {
|
|
|
583
641
|
const newLines = [];
|
|
584
642
|
for (const [key, value] of Object.entries(variables)) {
|
|
585
643
|
if (!existingKeys.has(key)) {
|
|
644
|
+
if (key === "STRAPI_SYNC_TOKEN") {
|
|
645
|
+
newLines.push("");
|
|
646
|
+
newLines.push("# Sync token: Used by strapi2front to sync schema (development only)");
|
|
647
|
+
newLines.push("# Permissions: content-type-builder (getContentTypes, getComponents), i18n (listLocales)");
|
|
648
|
+
newLines.push("# IMPORTANT: Do NOT deploy this token to production");
|
|
649
|
+
} else if (key === "STRAPI_TOKEN") {
|
|
650
|
+
newLines.push("");
|
|
651
|
+
newLines.push("# Frontend token: Used by your app to fetch content (production)");
|
|
652
|
+
newLines.push("# Configure with only the permissions your app needs");
|
|
653
|
+
} else if (key === "PUBLIC_STRAPI_UPLOAD_TOKEN" && includeUploadComment) {
|
|
654
|
+
newLines.push("");
|
|
655
|
+
newLines.push("# Upload token: Create in Strapi Admin > Settings > API Tokens");
|
|
656
|
+
newLines.push("# Set permissions: Upload > upload (only, no delete/update)");
|
|
657
|
+
}
|
|
586
658
|
newLines.push(`${key}=${value}`);
|
|
587
659
|
}
|
|
588
660
|
}
|
|
@@ -592,6 +664,33 @@ async function appendToEnvFile(envPath, variables) {
|
|
|
592
664
|
await fs5.writeFile(envPath, newContent, "utf-8");
|
|
593
665
|
}
|
|
594
666
|
}
|
|
667
|
+
async function createEnvExampleFile(envExamplePath, includeUpload = false) {
|
|
668
|
+
const lines = [
|
|
669
|
+
"# Strapi URL",
|
|
670
|
+
"STRAPI_URL=http://localhost:1337",
|
|
671
|
+
"",
|
|
672
|
+
"# Sync token: Used by strapi2front to sync schema (development only)",
|
|
673
|
+
"# Permissions: content-type-builder (getContentTypes, getComponents), i18n (listLocales)",
|
|
674
|
+
"# IMPORTANT: Do NOT deploy this token to production",
|
|
675
|
+
"STRAPI_SYNC_TOKEN=",
|
|
676
|
+
"",
|
|
677
|
+
"# Frontend token: Used by your app to fetch content (production)",
|
|
678
|
+
"# Configure with only the permissions your app needs",
|
|
679
|
+
"STRAPI_TOKEN="
|
|
680
|
+
];
|
|
681
|
+
if (includeUpload) {
|
|
682
|
+
lines.push(
|
|
683
|
+
"",
|
|
684
|
+
"# Public URL for browser-side uploads",
|
|
685
|
+
"PUBLIC_STRAPI_URL=http://localhost:1337",
|
|
686
|
+
"",
|
|
687
|
+
"# Upload token: Create in Strapi Admin > Settings > API Tokens",
|
|
688
|
+
"# Set permissions: Upload > upload (only, no delete/update)",
|
|
689
|
+
"PUBLIC_STRAPI_UPLOAD_TOKEN="
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
await fs5.writeFile(envExamplePath, lines.join("\n") + "\n", "utf-8");
|
|
693
|
+
}
|
|
595
694
|
var BLOCKS_RENDERER_PACKAGE = "@strapi/blocks-react-renderer";
|
|
596
695
|
function schemaHasBlocks(schema) {
|
|
597
696
|
const fieldsFound = [];
|
|
@@ -643,49 +742,9 @@ function installPackage(packageName, cwd) {
|
|
|
643
742
|
};
|
|
644
743
|
execSync(commands2[pm], { cwd, stdio: "inherit" });
|
|
645
744
|
}
|
|
646
|
-
function getOrphanedFolders(outputPath, currentStructure) {
|
|
647
|
-
const orphanedFolders = [];
|
|
648
|
-
if (currentStructure === "by-feature") {
|
|
649
|
-
const byLayerFolders = ["types", "services", "actions"];
|
|
650
|
-
for (const folder of byLayerFolders) {
|
|
651
|
-
const folderPath = path6.join(outputPath, folder);
|
|
652
|
-
if (fs6.existsSync(folderPath)) {
|
|
653
|
-
orphanedFolders.push(folder);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
if (fs6.existsSync(path6.join(outputPath, "client.ts"))) {
|
|
657
|
-
orphanedFolders.push("client.ts");
|
|
658
|
-
}
|
|
659
|
-
if (fs6.existsSync(path6.join(outputPath, "locales.ts"))) {
|
|
660
|
-
orphanedFolders.push("locales.ts");
|
|
661
|
-
}
|
|
662
|
-
} else {
|
|
663
|
-
const byFeatureFolders = ["collections", "singles", "shared", "components"];
|
|
664
|
-
for (const folder of byFeatureFolders) {
|
|
665
|
-
const folderPath = path6.join(outputPath, folder);
|
|
666
|
-
if (fs6.existsSync(folderPath)) {
|
|
667
|
-
orphanedFolders.push(folder);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
return orphanedFolders;
|
|
672
|
-
}
|
|
673
|
-
function cleanOrphanedFiles(outputPath, orphanedItems) {
|
|
674
|
-
for (const item of orphanedItems) {
|
|
675
|
-
const itemPath = path6.join(outputPath, item);
|
|
676
|
-
if (fs6.existsSync(itemPath)) {
|
|
677
|
-
const stat = fs6.statSync(itemPath);
|
|
678
|
-
if (stat.isDirectory()) {
|
|
679
|
-
fs6.rmSync(itemPath, { recursive: true, force: true });
|
|
680
|
-
} else {
|
|
681
|
-
fs6.unlinkSync(itemPath);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
745
|
async function syncCommand(options) {
|
|
687
746
|
const cwd = process.cwd();
|
|
688
|
-
p.intro(
|
|
747
|
+
p.intro(pc3.cyan("strapi2front sync"));
|
|
689
748
|
const s = p.spinner();
|
|
690
749
|
try {
|
|
691
750
|
s.start("Loading configuration...");
|
|
@@ -699,20 +758,20 @@ async function syncCommand(options) {
|
|
|
699
758
|
if (versionResult.detected !== config.strapiVersion) {
|
|
700
759
|
if (versionResult.detected === "v5" && config.strapiVersion === "v4") {
|
|
701
760
|
p.log.warn(
|
|
702
|
-
|
|
761
|
+
pc3.yellow(`Detected Strapi ${pc3.bold("v5")} but config has ${pc3.bold("v4")}. Using v5.`)
|
|
703
762
|
);
|
|
704
763
|
effectiveVersion = "v5";
|
|
705
764
|
} else if (versionResult.detected === "v4" && config.strapiVersion === "v5") {
|
|
706
765
|
p.log.warn(
|
|
707
|
-
|
|
766
|
+
pc3.yellow(`Detected Strapi ${pc3.bold("v4")} but config has ${pc3.bold("v5")}. Using v4.`)
|
|
708
767
|
);
|
|
709
768
|
effectiveVersion = "v4";
|
|
710
769
|
}
|
|
711
770
|
} else {
|
|
712
|
-
p.log.info(`Strapi ${
|
|
771
|
+
p.log.info(`Strapi ${pc3.green(pc3.bold(config.strapiVersion))}`);
|
|
713
772
|
}
|
|
714
773
|
} else {
|
|
715
|
-
p.log.warn(
|
|
774
|
+
p.log.warn(pc3.yellow(`Could not detect Strapi version. Using ${pc3.bold(config.strapiVersion)}`));
|
|
716
775
|
}
|
|
717
776
|
config = { ...config, strapiVersion: effectiveVersion };
|
|
718
777
|
s.start("Fetching schema from Strapi...");
|
|
@@ -722,9 +781,9 @@ async function syncCommand(options) {
|
|
|
722
781
|
let blocksRendererInstalled = isPackageInstalled(BLOCKS_RENDERER_PACKAGE, cwd);
|
|
723
782
|
const { hasBlocks: hasBlocksFields, fieldsFound: blocksFieldsFound } = schemaHasBlocks(schema);
|
|
724
783
|
if (hasBlocksFields && !blocksRendererInstalled) {
|
|
725
|
-
p.log.info(`Blocks fields detected: ${
|
|
784
|
+
p.log.info(`Blocks fields detected: ${pc3.cyan(blocksFieldsFound.join(", "))}`);
|
|
726
785
|
const installBlocks = await p.confirm({
|
|
727
|
-
message: `Install ${
|
|
786
|
+
message: `Install ${pc3.cyan(BLOCKS_RENDERER_PACKAGE)} for proper type support and rendering?`,
|
|
728
787
|
initialValue: true
|
|
729
788
|
});
|
|
730
789
|
if (p.isCancel(installBlocks)) {
|
|
@@ -742,145 +801,62 @@ async function syncCommand(options) {
|
|
|
742
801
|
logger.warn("You can install it manually later and re-run sync");
|
|
743
802
|
}
|
|
744
803
|
} else {
|
|
745
|
-
p.log.info(
|
|
804
|
+
p.log.info(pc3.dim(`Skipping ${BLOCKS_RENDERER_PACKAGE}. BlocksContent will be typed as unknown[]`));
|
|
746
805
|
}
|
|
747
806
|
}
|
|
748
807
|
const outputPath = path6.join(cwd, config.output.path);
|
|
749
808
|
const generatedFiles = [];
|
|
750
|
-
const generateAll = !options.typesOnly && !options.servicesOnly && !options.actionsOnly;
|
|
751
|
-
const isByFeature = config.output.structure === "by-feature";
|
|
752
|
-
const currentStructure = isByFeature ? "by-feature" : "by-layer";
|
|
753
|
-
if (fs6.existsSync(outputPath)) {
|
|
754
|
-
const orphanedFolders = getOrphanedFolders(outputPath, currentStructure);
|
|
755
|
-
if (orphanedFolders.length > 0) {
|
|
756
|
-
const otherStructure = isByFeature ? "by-layer" : "by-feature";
|
|
757
|
-
p.log.warn(
|
|
758
|
-
pc4.yellow(`Found files from previous ${pc4.bold(otherStructure)} structure:`)
|
|
759
|
-
);
|
|
760
|
-
p.log.message(pc4.dim(` ${orphanedFolders.join(", ")}`));
|
|
761
|
-
let shouldClean = options.clean;
|
|
762
|
-
if (!shouldClean) {
|
|
763
|
-
const cleanResponse = await p.confirm({
|
|
764
|
-
message: `Remove orphaned ${otherStructure} files?`,
|
|
765
|
-
initialValue: true
|
|
766
|
-
});
|
|
767
|
-
if (p.isCancel(cleanResponse)) {
|
|
768
|
-
p.cancel("Sync cancelled");
|
|
769
|
-
process.exit(0);
|
|
770
|
-
}
|
|
771
|
-
shouldClean = cleanResponse;
|
|
772
|
-
}
|
|
773
|
-
if (shouldClean) {
|
|
774
|
-
s.start("Cleaning orphaned files...");
|
|
775
|
-
cleanOrphanedFiles(outputPath, orphanedFolders);
|
|
776
|
-
s.stop(`Removed: ${orphanedFolders.join(", ")}`);
|
|
777
|
-
} else {
|
|
778
|
-
p.log.info(pc4.dim("Keeping orphaned files. You can clean them manually or use --clean flag."));
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
809
|
+
const generateAll = !options.typesOnly && !options.servicesOnly && !options.actionsOnly && !options.schemasOnly && !options.uploadOnly;
|
|
782
810
|
const outputFormat = config.outputFormat || "typescript";
|
|
783
811
|
let moduleType = "commonjs";
|
|
784
812
|
if (outputFormat === "jsdoc") {
|
|
785
813
|
if (config.moduleType) {
|
|
786
814
|
moduleType = config.moduleType;
|
|
787
|
-
p.log.info(`Module type: ${
|
|
815
|
+
p.log.info(`Module type: ${pc3.cyan(moduleType)} (from config)`);
|
|
788
816
|
} else {
|
|
789
817
|
const detected = await detectModuleType(cwd);
|
|
790
818
|
moduleType = detected.type;
|
|
791
|
-
p.log.info(`Module type: ${
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
if (isByFeature) {
|
|
795
|
-
s.start(`Generating files (by-feature, ${outputFormat})...`);
|
|
796
|
-
const files = await generateByFeature(schema, rawSchema.locales, {
|
|
797
|
-
outputDir: outputPath,
|
|
798
|
-
features: {
|
|
799
|
-
types: config.features.types && (generateAll || Boolean(options.typesOnly)),
|
|
800
|
-
services: config.features.services && (generateAll || Boolean(options.servicesOnly)),
|
|
801
|
-
actions: config.features.actions && outputFormat === "typescript" && (generateAll || Boolean(options.actionsOnly))
|
|
802
|
-
},
|
|
803
|
-
blocksRendererInstalled,
|
|
804
|
-
strapiVersion: config.strapiVersion,
|
|
805
|
-
apiPrefix: config.apiPrefix,
|
|
806
|
-
outputFormat,
|
|
807
|
-
moduleType
|
|
808
|
-
});
|
|
809
|
-
generatedFiles.push(...files);
|
|
810
|
-
s.stop(`Generated ${files.length} files`);
|
|
811
|
-
} else {
|
|
812
|
-
if (generateAll || options.typesOnly) {
|
|
813
|
-
if (config.features.types) {
|
|
814
|
-
s.start(`Generating types (${outputFormat})...`);
|
|
815
|
-
const typesPath = path6.join(outputPath, config.output.types);
|
|
816
|
-
const files = await generateTypes(schema, {
|
|
817
|
-
outputDir: typesPath,
|
|
818
|
-
blocksRendererInstalled,
|
|
819
|
-
strapiVersion: config.strapiVersion,
|
|
820
|
-
outputFormat
|
|
821
|
-
});
|
|
822
|
-
generatedFiles.push(...files);
|
|
823
|
-
s.stop(`Generated ${files.length} type files`);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
if (generateAll || options.servicesOnly) {
|
|
827
|
-
if (config.features.services) {
|
|
828
|
-
s.start("Generating client...");
|
|
829
|
-
const clientFiles = await generateClient({ outputDir: outputPath, strapiVersion: config.strapiVersion, apiPrefix: config.apiPrefix });
|
|
830
|
-
generatedFiles.push(...clientFiles);
|
|
831
|
-
s.stop("Generated client");
|
|
832
|
-
s.start("Generating locales...");
|
|
833
|
-
const localesFiles = await generateLocales(rawSchema.locales, { outputDir: outputPath });
|
|
834
|
-
generatedFiles.push(...localesFiles);
|
|
835
|
-
if (rawSchema.locales.length > 0) {
|
|
836
|
-
s.stop(`Generated locales: ${rawSchema.locales.map((l) => l.code).join(", ")}`);
|
|
837
|
-
} else {
|
|
838
|
-
s.stop("Generated locales (i18n not enabled in Strapi)");
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
if (generateAll || options.servicesOnly) {
|
|
843
|
-
if (config.features.services) {
|
|
844
|
-
s.start(`Generating services (${outputFormat})...`);
|
|
845
|
-
const servicesPath = path6.join(outputPath, config.output.services);
|
|
846
|
-
const typesImportPath = path6.relative(servicesPath, path6.join(outputPath, config.output.types)).replace(/\\/g, "/") || ".";
|
|
847
|
-
const files = await generateServices(schema, {
|
|
848
|
-
outputDir: servicesPath,
|
|
849
|
-
typesImportPath: typesImportPath.startsWith(".") ? typesImportPath : "./" + typesImportPath,
|
|
850
|
-
strapiVersion: config.strapiVersion,
|
|
851
|
-
outputFormat
|
|
852
|
-
});
|
|
853
|
-
generatedFiles.push(...files);
|
|
854
|
-
s.stop(`Generated ${files.length} service files`);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
if ((generateAll || options.actionsOnly) && outputFormat === "typescript") {
|
|
858
|
-
if (config.features.actions) {
|
|
859
|
-
s.start("Generating Astro actions...");
|
|
860
|
-
const actionsPath = path6.join(outputPath, config.output.actions);
|
|
861
|
-
const servicesPath = path6.join(outputPath, config.output.services);
|
|
862
|
-
const servicesImportPath = path6.relative(actionsPath, servicesPath).replace(/\\/g, "/") || ".";
|
|
863
|
-
const files = await generateActions(schema, {
|
|
864
|
-
outputDir: actionsPath,
|
|
865
|
-
servicesImportPath: servicesImportPath.startsWith(".") ? servicesImportPath : "./" + servicesImportPath,
|
|
866
|
-
strapiVersion: config.strapiVersion
|
|
867
|
-
});
|
|
868
|
-
generatedFiles.push(...files);
|
|
869
|
-
s.stop(`Generated ${files.length} action files`);
|
|
870
|
-
}
|
|
819
|
+
p.log.info(`Module type: ${pc3.cyan(moduleType)} (${detected.reason})`);
|
|
871
820
|
}
|
|
872
821
|
}
|
|
822
|
+
s.start(`Generating files (${outputFormat})...`);
|
|
823
|
+
const files = await generateByFeature(schema, rawSchema.locales, {
|
|
824
|
+
outputDir: outputPath,
|
|
825
|
+
features: {
|
|
826
|
+
types: config.features.types && (generateAll || Boolean(options.typesOnly)),
|
|
827
|
+
services: config.features.services && (generateAll || Boolean(options.servicesOnly)),
|
|
828
|
+
actions: config.features.actions && outputFormat === "typescript" && (generateAll || Boolean(options.actionsOnly)),
|
|
829
|
+
schemas: config.features.schemas && (generateAll || Boolean(options.schemasOnly)),
|
|
830
|
+
upload: config.features.upload && (generateAll || Boolean(options.uploadOnly))
|
|
831
|
+
},
|
|
832
|
+
schemaOptions: config.schemaOptions,
|
|
833
|
+
blocksRendererInstalled,
|
|
834
|
+
strapiVersion: config.strapiVersion,
|
|
835
|
+
apiPrefix: config.apiPrefix,
|
|
836
|
+
outputFormat,
|
|
837
|
+
moduleType
|
|
838
|
+
});
|
|
839
|
+
generatedFiles.push(...files);
|
|
840
|
+
s.stop(`Generated ${files.length} files`);
|
|
873
841
|
p.note(
|
|
874
842
|
[
|
|
875
|
-
`Generated ${generatedFiles.length} files in ${
|
|
843
|
+
`Generated ${generatedFiles.length} files in ${pc3.cyan(config.output.path)}`,
|
|
876
844
|
"",
|
|
877
845
|
"Files generated:",
|
|
878
|
-
...generatedFiles.slice(0, 10).map((f) => ` ${
|
|
879
|
-
generatedFiles.length > 10 ? ` ${
|
|
846
|
+
...generatedFiles.slice(0, 10).map((f) => ` ${pc3.dim(path6.relative(cwd, f))}`),
|
|
847
|
+
generatedFiles.length > 10 ? ` ${pc3.dim(`... and ${generatedFiles.length - 10} more`)}` : "",
|
|
848
|
+
"",
|
|
849
|
+
`Docs: ${pc3.dim("https://strapi2front.dev/docs")}`
|
|
880
850
|
].filter(Boolean).join("\n"),
|
|
881
851
|
"Sync complete!"
|
|
882
852
|
);
|
|
883
|
-
|
|
853
|
+
const generatedFeatures = [];
|
|
854
|
+
if (config.features.types) generatedFeatures.push("Types");
|
|
855
|
+
if (config.features.services) generatedFeatures.push("Services");
|
|
856
|
+
if (config.features.schemas) generatedFeatures.push("Schemas");
|
|
857
|
+
if (config.features.actions) generatedFeatures.push("Actions");
|
|
858
|
+
if (config.features.upload) generatedFeatures.push("Upload");
|
|
859
|
+
p.outro(pc3.green(`${generatedFeatures.join(", ")} ready to use!`));
|
|
884
860
|
} catch (error) {
|
|
885
861
|
s.stop("Sync failed");
|
|
886
862
|
if (error instanceof Error) {
|
|
@@ -891,6 +867,7 @@ async function syncCommand(options) {
|
|
|
891
867
|
} else {
|
|
892
868
|
logger.error("An unknown error occurred");
|
|
893
869
|
}
|
|
870
|
+
logger.info(`Docs: ${pc3.dim("https://strapi2front.dev/docs")}`);
|
|
894
871
|
process.exit(1);
|
|
895
872
|
}
|
|
896
873
|
}
|
|
@@ -905,14 +882,14 @@ var logo = `
|
|
|
905
882
|
|___/\\__|_| \\__,_| .__/|_|(_) |_| |_| \\___/|_| |_|\\__|
|
|
906
883
|
|_|
|
|
907
884
|
`;
|
|
908
|
-
program.name("strapi2front").description("Generate TypeScript types, services, and framework actions from your Strapi schema").version("0.1.0").addHelpText("beforeAll",
|
|
909
|
-
program.command("init").description("Initialize strapi2front in your project").option("-y, --yes", "Skip prompts and use defaults").option("--url <url>", "Strapi URL").option("--token <token>", "Strapi
|
|
910
|
-
program.command("sync").description("Sync types, services, and
|
|
885
|
+
program.name("strapi2front").description("Generate TypeScript types, services, and framework actions from your Strapi schema").version("0.1.0").addHelpText("beforeAll", pc3.cyan(logo));
|
|
886
|
+
program.command("init").description("Initialize strapi2front in your project").option("-y, --yes", "Skip prompts and use defaults").option("--url <url>", "Strapi URL").option("--token <token>", "Strapi sync token (STRAPI_SYNC_TOKEN)").option("--framework <framework>", "Framework to use (astro)").action(initCommand);
|
|
887
|
+
program.command("sync").description("Sync types, schemas, services, actions, and upload helpers from Strapi schema").option("-f, --force", "Force regeneration of all files").option("--types-only", "Only generate types").option("--services-only", "Only generate services").option("--actions-only", "Only generate actions").option("--schemas-only", "Only generate Zod validation schemas").option("--upload-only", "Only generate upload helpers").action(syncCommand);
|
|
911
888
|
var args = process.argv.slice(2);
|
|
912
889
|
var commands = ["init", "sync", "help", "--help", "-h", "--version", "-V"];
|
|
913
890
|
var hasCommand = args.some((arg) => commands.includes(arg));
|
|
914
891
|
if (args.length === 0 || !hasCommand) {
|
|
915
|
-
console.log(
|
|
892
|
+
console.log(pc3.cyan(logo));
|
|
916
893
|
initCommand();
|
|
917
894
|
} else {
|
|
918
895
|
program.parse();
|