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 +98 -8
- package/dist/bin/strapi2front.js +97 -41
- package/dist/bin/strapi2front.js.map +1 -1
- package/dist/index.js +97 -41
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
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
|
-
|
|
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
|
|
package/dist/bin/strapi2front.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
75
|
+
await fs5.access(configPath);
|
|
76
76
|
const pkgPath = path6.join(cwd, "package.json");
|
|
77
77
|
try {
|
|
78
|
-
const pkgContent = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = [];
|