strapi2front 0.2.1 → 0.2.2

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import pc4 from 'picocolors';
4
- import * as p3 from '@clack/prompts';
4
+ import * as p from '@clack/prompts';
5
5
  import fs4 from 'fs/promises';
6
6
  import path5 from 'path';
7
7
  import { spawn, execSync } from 'child_process';
@@ -141,8 +141,8 @@ function getInstallDevCommand(pm, pkg) {
141
141
  return commands2[pm];
142
142
  }
143
143
  async function runInitPrompts(detection) {
144
- p3.intro(pc4.cyan("strapi2front setup"));
145
- p3.note(
144
+ p.intro(pc4.cyan("strapi2front setup"));
145
+ p.note(
146
146
  [
147
147
  `Framework: ${pc4.green(getFrameworkDisplayName(detection.framework.name))} ${detection.framework.version ? pc4.dim(`v${detection.framework.version}`) : ""}`,
148
148
  `TypeScript: ${detection.typescript.enabled ? pc4.green("enabled") : pc4.yellow("disabled")}`,
@@ -151,15 +151,15 @@ async function runInitPrompts(detection) {
151
151
  "Detected Configuration"
152
152
  );
153
153
  if (detection.framework.name === "unknown") {
154
- p3.cancel("Could not detect a supported framework. Currently only Astro is supported.");
154
+ p.cancel("Could not detect a supported framework. Currently only Astro is supported.");
155
155
  return null;
156
156
  }
157
157
  if (detection.framework.name !== "astro") {
158
- p3.cancel(`${detection.framework.name} is not yet supported. Currently only Astro is supported.`);
158
+ p.cancel(`${detection.framework.name} is not yet supported. Currently only Astro is supported.`);
159
159
  return null;
160
160
  }
161
161
  const defaultUrl = "http://localhost:1337";
162
- const strapiUrlInput = await p3.text({
162
+ const strapiUrlInput = await p.text({
163
163
  message: "What is your Strapi URL?",
164
164
  placeholder: `${defaultUrl} (press Enter for default)`,
165
165
  validate: (value) => {
@@ -173,24 +173,24 @@ async function runInitPrompts(detection) {
173
173
  }
174
174
  }
175
175
  });
176
- if (p3.isCancel(strapiUrlInput)) {
177
- p3.cancel("Setup cancelled");
176
+ if (p.isCancel(strapiUrlInput)) {
177
+ p.cancel("Setup cancelled");
178
178
  return null;
179
179
  }
180
180
  const strapiUrl = (strapiUrlInput || "").trim() || defaultUrl;
181
- const strapiToken = await p3.text({
181
+ const strapiToken = await p.text({
182
182
  message: "What is your Strapi API token?",
183
183
  placeholder: "Press Enter to skip (you can add it later in .env)"
184
184
  });
185
- if (p3.isCancel(strapiToken)) {
186
- p3.cancel("Setup cancelled");
185
+ if (p.isCancel(strapiToken)) {
186
+ p.cancel("Setup cancelled");
187
187
  return null;
188
188
  }
189
189
  const trimmedToken = (strapiToken || "").trim();
190
190
  if (trimmedToken === "") {
191
- p3.log.info(pc4.dim("Token skipped. Remember to add STRAPI_TOKEN to your .env file later."));
191
+ p.log.info(pc4.dim("Token skipped. Remember to add STRAPI_TOKEN to your .env file later."));
192
192
  }
193
- const strapiVersion = await p3.select({
193
+ const strapiVersion = await p.select({
194
194
  message: "What version of Strapi are you using?",
195
195
  options: [
196
196
  { value: "v5", label: "Strapi v5", hint: "Recommended - Latest version" },
@@ -198,21 +198,42 @@ async function runInitPrompts(detection) {
198
198
  ],
199
199
  initialValue: "v5"
200
200
  });
201
- if (p3.isCancel(strapiVersion)) {
202
- p3.cancel("Setup cancelled");
201
+ if (p.isCancel(strapiVersion)) {
202
+ p.cancel("Setup cancelled");
203
203
  return null;
204
204
  }
205
- p3.log.info(pc4.dim(`Using Strapi ${strapiVersion}. This can be changed later in strapi.config.ts`));
206
- const outputDir = await p3.text({
205
+ p.log.info(pc4.dim(`Using Strapi ${strapiVersion}. This can be changed later in strapi.config.ts`));
206
+ const defaultPrefix = "/api";
207
+ const apiPrefixInput = await p.text({
208
+ message: "What is your Strapi API prefix?",
209
+ placeholder: `${defaultPrefix} (press Enter for default)`,
210
+ validate: (value) => {
211
+ const trimmed = (value || "").trim();
212
+ if (trimmed === "") return void 0;
213
+ if (!trimmed.startsWith("/")) {
214
+ return "API prefix must start with /";
215
+ }
216
+ return void 0;
217
+ }
218
+ });
219
+ if (p.isCancel(apiPrefixInput)) {
220
+ p.cancel("Setup cancelled");
221
+ return null;
222
+ }
223
+ const apiPrefix = (apiPrefixInput || "").trim() || defaultPrefix;
224
+ if (apiPrefix !== defaultPrefix) {
225
+ p.log.info(pc4.dim(`Using custom API prefix: ${apiPrefix}`));
226
+ }
227
+ const outputDir = await p.text({
207
228
  message: "Where should we generate the Strapi files?",
208
229
  placeholder: "src/strapi",
209
230
  defaultValue: "src/strapi"
210
231
  });
211
- if (p3.isCancel(outputDir)) {
212
- p3.cancel("Setup cancelled");
232
+ if (p.isCancel(outputDir)) {
233
+ p.cancel("Setup cancelled");
213
234
  return null;
214
235
  }
215
- const features = await p3.multiselect({
236
+ const features = await p.multiselect({
216
237
  message: "What would you like to generate?",
217
238
  options: [
218
239
  { value: "types", label: "Types", hint: "TypeScript interfaces for your content types" },
@@ -222,14 +243,15 @@ async function runInitPrompts(detection) {
222
243
  initialValues: ["types", "services", "actions"],
223
244
  required: true
224
245
  });
225
- if (p3.isCancel(features)) {
226
- p3.cancel("Setup cancelled");
246
+ if (p.isCancel(features)) {
247
+ p.cancel("Setup cancelled");
227
248
  return null;
228
249
  }
229
250
  return {
230
251
  strapiUrl,
231
252
  strapiToken: trimmedToken,
232
253
  strapiVersion,
254
+ apiPrefix,
233
255
  outputDir: (outputDir || "").trim() || "src/strapi",
234
256
  generateActions: features.includes("actions"),
235
257
  generateServices: features.includes("services")
@@ -282,7 +304,7 @@ function execAsync(command, cwd) {
282
304
  }
283
305
  async function initCommand(_options) {
284
306
  const cwd = process.cwd();
285
- const s = p3.spinner();
307
+ const s = p.spinner();
286
308
  s.start("Detecting project configuration...");
287
309
  const [framework, typescript, packageManager] = await Promise.all([
288
310
  detectFramework(cwd),
@@ -303,6 +325,7 @@ async function initCommand(_options) {
303
325
  const configContent = generateConfigFile({
304
326
  strapiUrl: answers.strapiUrl,
305
327
  strapiVersion: answers.strapiVersion,
328
+ apiPrefix: answers.apiPrefix,
306
329
  outputDir: answers.outputDir,
307
330
  generateActions: answers.generateActions,
308
331
  generateServices: answers.generateServices
@@ -317,12 +340,12 @@ async function initCommand(_options) {
317
340
  const outputPath = path5.join(cwd, answers.outputDir);
318
341
  await fs4.mkdir(outputPath, { recursive: true });
319
342
  s.stop("Configuration files created");
320
- const installDeps = await p3.confirm({
343
+ const installDeps = await p.confirm({
321
344
  message: "Install required dependencies (strapi2front, strapi-sdk-js)?",
322
345
  initialValue: true
323
346
  });
324
- if (p3.isCancel(installDeps)) {
325
- p3.cancel("Setup cancelled");
347
+ if (p.isCancel(installDeps)) {
348
+ p.cancel("Setup cancelled");
326
349
  process.exit(0);
327
350
  }
328
351
  if (installDeps) {
@@ -345,11 +368,11 @@ async function initCommand(_options) {
345
368
  logger.warn(`Please install manually: ${installSdkCmd}`);
346
369
  }
347
370
  } else {
348
- p3.log.info(pc4.dim("Remember to install dependencies manually:"));
349
- p3.log.info(pc4.dim(` ${getInstallDevCommand(packageManager.name, "strapi2front")}`));
350
- p3.log.info(pc4.dim(` ${getInstallCommand(packageManager.name, "strapi-sdk-js")}`));
371
+ p.log.info(pc4.dim("Remember to install dependencies manually:"));
372
+ p.log.info(pc4.dim(` ${getInstallDevCommand(packageManager.name, "strapi2front")}`));
373
+ p.log.info(pc4.dim(` ${getInstallCommand(packageManager.name, "strapi-sdk-js")}`));
351
374
  }
352
- p3.note(
375
+ p.note(
353
376
  [
354
377
  `${pc4.green("v")} Created ${pc4.cyan("strapi.config.ts")}`,
355
378
  `${pc4.green("v")} Updated ${pc4.cyan(".env")} with Strapi credentials`,
@@ -362,7 +385,7 @@ async function initCommand(_options) {
362
385
  ].join("\n"),
363
386
  "Setup complete!"
364
387
  );
365
- p3.outro(pc4.green("Happy coding!"));
388
+ p.outro(pc4.green("Happy coding!"));
366
389
  } catch (error) {
367
390
  s.stop("Failed to create configuration files");
368
391
  logger.error(error instanceof Error ? error.message : "Unknown error");
@@ -370,16 +393,17 @@ async function initCommand(_options) {
370
393
  }
371
394
  }
372
395
  function generateConfigFile(answers) {
396
+ const apiPrefixLine = answers.apiPrefix !== "/api" ? `
397
+ // API prefix (customized from default "/api")
398
+ apiPrefix: "${answers.apiPrefix}",
399
+ ` : "";
373
400
  return `import { defineConfig } from "strapi2front";
374
401
 
375
402
  export default defineConfig({
376
403
  // Strapi connection
377
404
  url: process.env.STRAPI_URL || "${answers.strapiUrl}",
378
405
  token: process.env.STRAPI_TOKEN,
379
-
380
- // API prefix (default: "/api", change if you customized it in Strapi)
381
- // apiPrefix: "/api",
382
-
406
+ ${apiPrefixLine}
383
407
  // Output configuration
384
408
  output: {
385
409
  path: "${answers.outputDir}",
@@ -516,35 +540,12 @@ function cleanOrphanedFiles(outputPath, orphanedItems) {
516
540
  }
517
541
  async function syncCommand(options) {
518
542
  const cwd = process.cwd();
519
- p3.intro(pc4.cyan("strapi2front sync"));
520
- const s = p3.spinner();
543
+ p.intro(pc4.cyan("strapi2front sync"));
544
+ const s = p.spinner();
521
545
  try {
522
546
  s.start("Loading configuration...");
523
547
  let config = await loadConfig(cwd);
524
548
  s.stop("Configuration loaded");
525
- const currentPrefix = config.apiPrefix || "/api";
526
- const apiPrefixInput = await p3.text({
527
- message: "What is your Strapi API prefix?",
528
- placeholder: `${currentPrefix} (press Enter to use configured value)`,
529
- initialValue: currentPrefix,
530
- validate: (value) => {
531
- const trimmed = (value || "").trim();
532
- if (trimmed === "") return void 0;
533
- if (!trimmed.startsWith("/")) {
534
- return "API prefix must start with /";
535
- }
536
- return void 0;
537
- }
538
- });
539
- if (p3.isCancel(apiPrefixInput)) {
540
- p3.cancel("Sync cancelled");
541
- process.exit(0);
542
- }
543
- const apiPrefix = (apiPrefixInput || "").trim() || currentPrefix;
544
- if (apiPrefix !== currentPrefix) {
545
- p3.log.info(pc4.dim(`Using API prefix: ${apiPrefix}`));
546
- config = { ...config, apiPrefix };
547
- }
548
549
  s.start("Detecting Strapi version...");
549
550
  const versionResult = await detectStrapiVersion(config.url, config.token, config.apiPrefix);
550
551
  s.stop("Version detection complete");
@@ -552,21 +553,21 @@ async function syncCommand(options) {
552
553
  if (versionResult.detected) {
553
554
  if (versionResult.detected !== config.strapiVersion) {
554
555
  if (versionResult.detected === "v5" && config.strapiVersion === "v4") {
555
- p3.log.warn(
556
+ p.log.warn(
556
557
  pc4.yellow(`Detected Strapi ${pc4.bold("v5")} but config has ${pc4.bold("v4")}. Using v5.`)
557
558
  );
558
559
  effectiveVersion = "v5";
559
560
  } else if (versionResult.detected === "v4" && config.strapiVersion === "v5") {
560
- p3.log.warn(
561
+ p.log.warn(
561
562
  pc4.yellow(`Detected Strapi ${pc4.bold("v4")} but config has ${pc4.bold("v5")}. Using v4.`)
562
563
  );
563
564
  effectiveVersion = "v4";
564
565
  }
565
566
  } else {
566
- p3.log.info(`Strapi ${pc4.green(pc4.bold(config.strapiVersion))}`);
567
+ p.log.info(`Strapi ${pc4.green(pc4.bold(config.strapiVersion))}`);
567
568
  }
568
569
  } else {
569
- p3.log.warn(pc4.yellow(`Could not detect Strapi version. Using ${pc4.bold(config.strapiVersion)}`));
570
+ p.log.warn(pc4.yellow(`Could not detect Strapi version. Using ${pc4.bold(config.strapiVersion)}`));
570
571
  }
571
572
  config = { ...config, strapiVersion: effectiveVersion };
572
573
  s.start("Fetching schema from Strapi...");
@@ -576,13 +577,13 @@ async function syncCommand(options) {
576
577
  let blocksRendererInstalled = isPackageInstalled(BLOCKS_RENDERER_PACKAGE, cwd);
577
578
  const { hasBlocks: hasBlocksFields, fieldsFound: blocksFieldsFound } = schemaHasBlocks(schema);
578
579
  if (hasBlocksFields && !blocksRendererInstalled) {
579
- p3.log.info(`Blocks fields detected: ${pc4.cyan(blocksFieldsFound.join(", "))}`);
580
- const installBlocks = await p3.confirm({
580
+ p.log.info(`Blocks fields detected: ${pc4.cyan(blocksFieldsFound.join(", "))}`);
581
+ const installBlocks = await p.confirm({
581
582
  message: `Install ${pc4.cyan(BLOCKS_RENDERER_PACKAGE)} for proper type support and rendering?`,
582
583
  initialValue: true
583
584
  });
584
- if (p3.isCancel(installBlocks)) {
585
- p3.cancel("Sync cancelled");
585
+ if (p.isCancel(installBlocks)) {
586
+ p.cancel("Sync cancelled");
586
587
  process.exit(0);
587
588
  }
588
589
  if (installBlocks) {
@@ -596,7 +597,7 @@ async function syncCommand(options) {
596
597
  logger.warn("You can install it manually later and re-run sync");
597
598
  }
598
599
  } else {
599
- p3.log.info(pc4.dim(`Skipping ${BLOCKS_RENDERER_PACKAGE}. BlocksContent will be typed as unknown[]`));
600
+ p.log.info(pc4.dim(`Skipping ${BLOCKS_RENDERER_PACKAGE}. BlocksContent will be typed as unknown[]`));
600
601
  }
601
602
  }
602
603
  const outputPath = path5.join(cwd, config.output.path);
@@ -608,18 +609,18 @@ async function syncCommand(options) {
608
609
  const orphanedFolders = getOrphanedFolders(outputPath, currentStructure);
609
610
  if (orphanedFolders.length > 0) {
610
611
  const otherStructure = isByFeature ? "by-layer" : "by-feature";
611
- p3.log.warn(
612
+ p.log.warn(
612
613
  pc4.yellow(`Found files from previous ${pc4.bold(otherStructure)} structure:`)
613
614
  );
614
- p3.log.message(pc4.dim(` ${orphanedFolders.join(", ")}`));
615
+ p.log.message(pc4.dim(` ${orphanedFolders.join(", ")}`));
615
616
  let shouldClean = options.clean;
616
617
  if (!shouldClean) {
617
- const cleanResponse = await p3.confirm({
618
+ const cleanResponse = await p.confirm({
618
619
  message: `Remove orphaned ${otherStructure} files?`,
619
620
  initialValue: true
620
621
  });
621
- if (p3.isCancel(cleanResponse)) {
622
- p3.cancel("Sync cancelled");
622
+ if (p.isCancel(cleanResponse)) {
623
+ p.cancel("Sync cancelled");
623
624
  process.exit(0);
624
625
  }
625
626
  shouldClean = cleanResponse;
@@ -629,7 +630,7 @@ async function syncCommand(options) {
629
630
  cleanOrphanedFiles(outputPath, orphanedFolders);
630
631
  s.stop(`Removed: ${orphanedFolders.join(", ")}`);
631
632
  } else {
632
- p3.log.info(pc4.dim("Keeping orphaned files. You can clean them manually or use --clean flag."));
633
+ p.log.info(pc4.dim("Keeping orphaned files. You can clean them manually or use --clean flag."));
633
634
  }
634
635
  }
635
636
  }
@@ -708,7 +709,7 @@ async function syncCommand(options) {
708
709
  }
709
710
  }
710
711
  }
711
- p3.note(
712
+ p.note(
712
713
  [
713
714
  `Generated ${generatedFiles.length} files in ${pc4.cyan(config.output.path)}`,
714
715
  "",
@@ -718,7 +719,7 @@ async function syncCommand(options) {
718
719
  ].filter(Boolean).join("\n"),
719
720
  "Sync complete!"
720
721
  );
721
- p3.outro(pc4.green("Types and services are ready to use!"));
722
+ p.outro(pc4.green("Types and services are ready to use!"));
722
723
  } catch (error) {
723
724
  s.stop("Sync failed");
724
725
  if (error instanceof Error) {