strapi2front 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  <div align="center">
2
- <a href="https://strapi2front.dev">
2
+ <!-- <a href="https://strapi2front.dev">
3
3
  <picture>
4
4
  <source media="(prefers-color-scheme: dark)" srcset="https://strapi2front.dev/logo-dark.svg">
5
5
  <img alt="strapi2front logo" src="https://strapi2front.dev/logo-light.svg" height="128">
6
6
  </picture>
7
- </a>
7
+ </a> -->
8
8
  <h1>strapi2front</h1>
9
9
 
10
10
  <a href="https://elevenestudio.com"><img alt="Made by Eleven Estudio" src="https://img.shields.io/badge/MADE%20BY%20Eleven%20Estudio-000000.svg?style=for-the-badge&labelColor=000"></a>
@@ -36,6 +36,7 @@ npx strapi2front sync
36
36
  ⊹ **Type Generation** - Auto-generate TypeScript interfaces from your Strapi schema\
37
37
  ⊹ **Service Generation** - Create typed service functions for all content types\
38
38
  ⊹ **Astro Actions** - Generate type-safe Astro Actions for client/server data fetching\
39
+ ⊹ **JSDoc Support** - Generate JavaScript files with JSDoc annotations (no TypeScript required)\
39
40
  ⊹ **Smart Detection** - Automatically detects framework, TypeScript, and package manager\
40
41
  ⊹ **Strapi v4 & v5** - Full support for both Strapi versions\
41
42
  ⊹ **By-Feature Structure** - Organize generated code by feature (screaming architecture)
@@ -65,16 +66,105 @@ src/strapi/
65
66
 
66
67
  - Node.js 18+
67
68
  - Strapi v4 or v5
68
- - Astro 4+ (more frameworks coming soon)
69
+ - Astro 4+ (for Astro Actions, more frameworks coming soon)
69
70
 
70
- ## Learn more
71
+ ---
72
+
73
+ ## Quick Setup
74
+
75
+ ### 1. Create a Strapi API Token
76
+
77
+ You need an API token with **read-only access** to the Content-Type Builder:
78
+
79
+ 1. Go to **Strapi Admin** → **Settings** → **API Tokens**
80
+ 2. Create a new **Custom** token
81
+ 3. Enable these permissions:
82
+ - **Content-type-builder**: `getComponents`, `getComponent`, `getContentTypes`, `getContentType`
83
+ - **I18n** (optional): `listLocales`
84
+ 4. Add the token to your `.env`:
85
+
86
+ ```env
87
+ STRAPI_TOKEN=your-token-here
88
+ ```
89
+
90
+ > This token only reads schema structure, not your content data.
91
+
92
+ ### 2. Initialize
93
+
94
+ ```bash
95
+ npx strapi2front@latest init
96
+ ```
97
+
98
+ ### 3. Sync Types
99
+
100
+ ```bash
101
+ npx strapi2front sync
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Configuration
107
+
108
+ After init, a `strapi.config.ts` is created:
109
+
110
+ ```typescript
111
+ import { defineConfig } from "strapi2front";
112
+
113
+ export default defineConfig({
114
+ url: "http://localhost:1337",
115
+ strapiVersion: "v5",
116
+ apiPrefix: "/api",
117
+ output: {
118
+ path: "src/strapi",
119
+ },
120
+ features: {
121
+ types: true,
122
+ services: true,
123
+ actions: true,
124
+ },
125
+ });
126
+ ```
127
+
128
+ ### Key Options
129
+
130
+ | Option | Description |
131
+ |--------|-------------|
132
+ | `url` | Your Strapi instance URL |
133
+ | `strapiVersion` | `"v4"` or `"v5"` |
134
+ | `apiPrefix` | API prefix (default: `/api`) |
135
+ | `outputFormat` | `"typescript"` or `"jsdoc"` |
136
+ | `moduleType` | `"esm"` or `"commonjs"` (auto-detected) |
137
+ | `output.path` | Where to generate files |
138
+ | `output.structure` | `"by-feature"` or `"by-layer"` |
139
+
140
+ ### Environment Variables
141
+
142
+ ```env
143
+ STRAPI_URL=http://localhost:1337
144
+ STRAPI_TOKEN=your-api-token
145
+ STRAPI_API_PREFIX=/api
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Framework Support
151
+
152
+ | Framework | Types | Services | Actions |
153
+ |-----------|-------|----------|---------|
154
+ | Astro 4+ | ✅ | ✅ | ✅ |
155
+ | Next.js | ✅ | ✅ | 🔜 Soon |
156
+ | Nuxt | ✅ | ✅ | 🔜 Soon |
157
+ | Other | ✅ | ✅ | ❌ |
158
+
159
+ ---
160
+
161
+ ## Full Documentation
71
162
 
72
- Visit [strapi2front.dev](https://strapi2front.dev) to learn more about the project.\
73
- ⊹ Visit [strapi2front.dev/docs](https://strapi2front.dev/docs) to view the full documentation.
163
+ For complete documentation including troubleshooting, advanced configuration, and examples, see the [main README](https://github.com/eleven-estudio/strapi2front#readme).
74
164
 
75
165
  ## Contributing
76
166
 
77
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
167
+ We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.
78
168
 
79
169
  ## Security
80
170
 
@@ -84,7 +174,7 @@ Please report any issues to [hello@elevenestudio.com](mailto:hello@elevenestudio
84
174
 
85
175
  ## License
86
176
 
87
- MIT - see [LICENSE](LICENSE) for details.
177
+ MIT - see [LICENSE](../../LICENSE) for details.
88
178
 
89
179
  ## Disclaimer
90
180
 
@@ -2,7 +2,7 @@
2
2
  import { Command } from 'commander';
3
3
  import pc4 from 'picocolors';
4
4
  import * as p from '@clack/prompts';
5
- import fs4 from 'fs/promises';
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';
@@ -26,7 +26,7 @@ var FRAMEWORK_DETECTORS = {
26
26
  async function detectFramework(cwd = process.cwd()) {
27
27
  const pkgPath = path6.join(cwd, "package.json");
28
28
  try {
29
- const pkgContent = await fs4.readFile(pkgPath, "utf-8");
29
+ const pkgContent = await fs5.readFile(pkgPath, "utf-8");
30
30
  const pkg = JSON.parse(pkgContent);
31
31
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
32
32
  for (const [pkgName, detector] of Object.entries(FRAMEWORK_DETECTORS)) {
@@ -34,7 +34,7 @@ async function detectFramework(cwd = process.cwd()) {
34
34
  for (const configFile of detector.configFiles) {
35
35
  const configPath = path6.join(cwd, configFile);
36
36
  try {
37
- await fs4.access(configPath);
37
+ await fs5.access(configPath);
38
38
  return {
39
39
  name: detector.name,
40
40
  version: deps[pkgName],
@@ -72,10 +72,10 @@ async function detectTypeScript(cwd = process.cwd()) {
72
72
  for (const configFile of TS_CONFIG_FILES) {
73
73
  const configPath = path6.join(cwd, configFile);
74
74
  try {
75
- await fs4.access(configPath);
75
+ await fs5.access(configPath);
76
76
  const pkgPath = path6.join(cwd, "package.json");
77
77
  try {
78
- const pkgContent = await fs4.readFile(pkgPath, "utf-8");
78
+ const pkgContent = await fs5.readFile(pkgPath, "utf-8");
79
79
  const pkg = JSON.parse(pkgContent);
80
80
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
81
81
  return {
@@ -109,7 +109,7 @@ async function detectPackageManager(cwd = process.cwd()) {
109
109
  for (const [lockFile, pm] of Object.entries(LOCK_FILES)) {
110
110
  const lockPath = path6.join(cwd, lockFile);
111
111
  try {
112
- await fs4.access(lockPath);
112
+ await fs5.access(lockPath);
113
113
  return {
114
114
  name: pm,
115
115
  lockFile
@@ -140,6 +140,34 @@ function getInstallDevCommand(pm, pkg) {
140
140
  };
141
141
  return commands2[pm];
142
142
  }
143
+ async function detectModuleType(cwd = process.cwd()) {
144
+ try {
145
+ const packageJsonPath = path6.join(cwd, "package.json");
146
+ const content = await fs5.readFile(packageJsonPath, "utf-8");
147
+ const packageJson = JSON.parse(content);
148
+ if (packageJson.type === "module") {
149
+ return { type: "esm", reason: 'package.json has "type": "module"' };
150
+ }
151
+ if (packageJson.type === "commonjs") {
152
+ return { type: "commonjs", reason: 'package.json has "type": "commonjs"' };
153
+ }
154
+ } catch {
155
+ }
156
+ const esmIndicators = [
157
+ "next.config.mjs",
158
+ "nuxt.config.mjs",
159
+ "vite.config.mjs",
160
+ "astro.config.mjs"
161
+ ];
162
+ for (const file of esmIndicators) {
163
+ try {
164
+ await fs5.access(path6.join(cwd, file));
165
+ return { type: "esm", reason: `Found ${file} (ESM config file)` };
166
+ } catch {
167
+ }
168
+ }
169
+ return { type: "commonjs", reason: "Default (no ESM indicators found)" };
170
+ }
143
171
  function getMajorVersion(version) {
144
172
  if (!version) return null;
145
173
  const match = version.replace(/^[\^~]/, "").match(/^(\d+)/);
@@ -192,6 +220,19 @@ async function runInitPrompts(detection) {
192
220
  return null;
193
221
  }
194
222
  const strapiUrl = (strapiUrlInput || "").trim() || defaultUrl;
223
+ p.log.message(
224
+ pc4.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
+ );
195
236
  const strapiToken = await p.text({
196
237
  message: "What is your Strapi API token?",
197
238
  placeholder: "Press Enter to skip (you can add it later in .env)"
@@ -350,6 +391,11 @@ async function initCommand(_options) {
350
391
  }
351
392
  s.start("Creating configuration files...");
352
393
  try {
394
+ let moduleType = "commonjs";
395
+ if (answers.outputFormat === "jsdoc") {
396
+ const detected = await detectModuleType(cwd);
397
+ moduleType = detected.type;
398
+ }
353
399
  const configExtension = answers.outputFormat === "jsdoc" ? "js" : "ts";
354
400
  const configContent = generateConfigFile({
355
401
  strapiUrl: answers.strapiUrl,
@@ -358,17 +404,18 @@ async function initCommand(_options) {
358
404
  outputFormat: answers.outputFormat,
359
405
  outputDir: answers.outputDir,
360
406
  generateActions: answers.generateActions,
361
- generateServices: answers.generateServices
407
+ generateServices: answers.generateServices,
408
+ moduleType
362
409
  });
363
410
  const configPath = path6.join(cwd, `strapi.config.${configExtension}`);
364
- await fs4.writeFile(configPath, configContent, "utf-8");
411
+ await fs5.writeFile(configPath, configContent, "utf-8");
365
412
  const envPath = path6.join(cwd, ".env");
366
413
  await appendToEnvFile(envPath, {
367
414
  STRAPI_URL: answers.strapiUrl,
368
415
  STRAPI_TOKEN: answers.strapiToken
369
416
  });
370
417
  const outputPath = path6.join(cwd, answers.outputDir);
371
- await fs4.mkdir(outputPath, { recursive: true });
418
+ await fs5.mkdir(outputPath, { recursive: true });
372
419
  s.stop("Configuration files created");
373
420
  const installDeps = await p.confirm({
374
421
  message: "Install required dependencies (strapi2front, strapi-sdk-js)?",
@@ -428,6 +475,7 @@ async function initCommand(_options) {
428
475
  }
429
476
  function generateConfigFile(answers) {
430
477
  const isTypeScript = answers.outputFormat === "typescript";
478
+ const useESM = answers.moduleType === "esm";
431
479
  if (isTypeScript) {
432
480
  return `import { defineConfig } from "strapi2front";
433
481
 
@@ -461,6 +509,44 @@ export default defineConfig({
461
509
  // Strapi version
462
510
  strapiVersion: "${answers.strapiVersion}",
463
511
  });
512
+ `;
513
+ }
514
+ if (useESM) {
515
+ return `// @ts-check
516
+ import { defineConfig } from "strapi2front";
517
+
518
+ export default defineConfig({
519
+ // Strapi connection
520
+ url: process.env.STRAPI_URL || "${answers.strapiUrl}",
521
+ token: process.env.STRAPI_TOKEN,
522
+
523
+ // API prefix (default: "/api")
524
+ apiPrefix: "${answers.apiPrefix}",
525
+
526
+ // Output format: "typescript" (.ts) or "jsdoc" (.js with JSDoc)
527
+ outputFormat: "jsdoc",
528
+
529
+ // Module type: auto-detected as ESM
530
+ moduleType: "esm",
531
+
532
+ // Output configuration
533
+ output: {
534
+ path: "${answers.outputDir}",
535
+ types: "types",
536
+ services: "services",
537
+ structure: 'by-feature' // or 'by-layer'
538
+ },
539
+
540
+ // Features to generate
541
+ features: {
542
+ types: true,
543
+ services: ${answers.generateServices},
544
+ actions: false, // Actions require TypeScript
545
+ },
546
+
547
+ // Strapi version
548
+ strapiVersion: "${answers.strapiVersion}",
549
+ });
464
550
  `;
465
551
  }
466
552
  return `// @ts-check
@@ -500,7 +586,7 @@ module.exports = defineConfig({
500
586
  async function appendToEnvFile(envPath, variables) {
501
587
  let content = "";
502
588
  try {
503
- content = await fs4.readFile(envPath, "utf-8");
589
+ content = await fs5.readFile(envPath, "utf-8");
504
590
  } catch {
505
591
  }
506
592
  const lines = content.split("\n");
@@ -516,39 +602,9 @@ async function appendToEnvFile(envPath, variables) {
516
602
  if (newLines.length > 0) {
517
603
  const separator = content.endsWith("\n") || content === "" ? "" : "\n";
518
604
  const newContent = content + separator + newLines.join("\n") + "\n";
519
- await fs4.writeFile(envPath, newContent, "utf-8");
520
- }
521
- }
522
- async function detectModuleType(cwd = process.cwd()) {
523
- try {
524
- const packageJsonPath = path6.join(cwd, "package.json");
525
- const content = await fs4.readFile(packageJsonPath, "utf-8");
526
- const packageJson = JSON.parse(content);
527
- if (packageJson.type === "module") {
528
- return { type: "esm", reason: 'package.json has "type": "module"' };
529
- }
530
- if (packageJson.type === "commonjs") {
531
- return { type: "commonjs", reason: 'package.json has "type": "commonjs"' };
532
- }
533
- } catch {
534
- }
535
- const esmIndicators = [
536
- "next.config.mjs",
537
- "nuxt.config.mjs",
538
- "vite.config.mjs",
539
- "astro.config.mjs"
540
- ];
541
- for (const file of esmIndicators) {
542
- try {
543
- await fs4.access(path6.join(cwd, file));
544
- return { type: "esm", reason: `Found ${file} (ESM config file)` };
545
- } catch {
546
- }
605
+ await fs5.writeFile(envPath, newContent, "utf-8");
547
606
  }
548
- return { type: "commonjs", reason: "Default (no ESM indicators found)" };
549
607
  }
550
-
551
- // src/commands/sync.ts
552
608
  var BLOCKS_RENDERER_PACKAGE = "@strapi/blocks-react-renderer";
553
609
  function schemaHasBlocks(schema) {
554
610
  const fieldsFound = [];