@strapi2front/core 0.1.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/LICENSE +21 -0
- package/dist/index.d.ts +363 -0
- package/dist/index.js +360 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eleven Estudio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration schema for strapi-integrate
|
|
5
|
+
*/
|
|
6
|
+
declare const configSchema: z.ZodObject<{
|
|
7
|
+
url: z.ZodString;
|
|
8
|
+
token: z.ZodOptional<z.ZodString>;
|
|
9
|
+
strapiVersion: z.ZodDefault<z.ZodEnum<["v4", "v5"]>>;
|
|
10
|
+
output: z.ZodDefault<z.ZodObject<{
|
|
11
|
+
path: z.ZodDefault<z.ZodString>;
|
|
12
|
+
types: z.ZodDefault<z.ZodString>;
|
|
13
|
+
services: z.ZodDefault<z.ZodString>;
|
|
14
|
+
actions: z.ZodDefault<z.ZodString>;
|
|
15
|
+
structure: z.ZodDefault<z.ZodEnum<["by-layer", "by-feature"]>>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
path: string;
|
|
18
|
+
types: string;
|
|
19
|
+
services: string;
|
|
20
|
+
actions: string;
|
|
21
|
+
structure: "by-layer" | "by-feature";
|
|
22
|
+
}, {
|
|
23
|
+
path?: string | undefined;
|
|
24
|
+
types?: string | undefined;
|
|
25
|
+
services?: string | undefined;
|
|
26
|
+
actions?: string | undefined;
|
|
27
|
+
structure?: "by-layer" | "by-feature" | undefined;
|
|
28
|
+
}>>;
|
|
29
|
+
features: z.ZodDefault<z.ZodObject<{
|
|
30
|
+
types: z.ZodDefault<z.ZodBoolean>;
|
|
31
|
+
services: z.ZodDefault<z.ZodBoolean>;
|
|
32
|
+
actions: z.ZodDefault<z.ZodBoolean>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
types: boolean;
|
|
35
|
+
services: boolean;
|
|
36
|
+
actions: boolean;
|
|
37
|
+
}, {
|
|
38
|
+
types?: boolean | undefined;
|
|
39
|
+
services?: boolean | undefined;
|
|
40
|
+
actions?: boolean | undefined;
|
|
41
|
+
}>>;
|
|
42
|
+
options: z.ZodDefault<z.ZodObject<{
|
|
43
|
+
includeDrafts: z.ZodDefault<z.ZodBoolean>;
|
|
44
|
+
strictTypes: z.ZodDefault<z.ZodBoolean>;
|
|
45
|
+
}, "strip", z.ZodTypeAny, {
|
|
46
|
+
includeDrafts: boolean;
|
|
47
|
+
strictTypes: boolean;
|
|
48
|
+
}, {
|
|
49
|
+
includeDrafts?: boolean | undefined;
|
|
50
|
+
strictTypes?: boolean | undefined;
|
|
51
|
+
}>>;
|
|
52
|
+
}, "strip", z.ZodTypeAny, {
|
|
53
|
+
url: string;
|
|
54
|
+
strapiVersion: "v4" | "v5";
|
|
55
|
+
options: {
|
|
56
|
+
includeDrafts: boolean;
|
|
57
|
+
strictTypes: boolean;
|
|
58
|
+
};
|
|
59
|
+
output: {
|
|
60
|
+
path: string;
|
|
61
|
+
types: string;
|
|
62
|
+
services: string;
|
|
63
|
+
actions: string;
|
|
64
|
+
structure: "by-layer" | "by-feature";
|
|
65
|
+
};
|
|
66
|
+
features: {
|
|
67
|
+
types: boolean;
|
|
68
|
+
services: boolean;
|
|
69
|
+
actions: boolean;
|
|
70
|
+
};
|
|
71
|
+
token?: string | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
url: string;
|
|
74
|
+
token?: string | undefined;
|
|
75
|
+
strapiVersion?: "v4" | "v5" | undefined;
|
|
76
|
+
options?: {
|
|
77
|
+
includeDrafts?: boolean | undefined;
|
|
78
|
+
strictTypes?: boolean | undefined;
|
|
79
|
+
} | undefined;
|
|
80
|
+
output?: {
|
|
81
|
+
path?: string | undefined;
|
|
82
|
+
types?: string | undefined;
|
|
83
|
+
services?: string | undefined;
|
|
84
|
+
actions?: string | undefined;
|
|
85
|
+
structure?: "by-layer" | "by-feature" | undefined;
|
|
86
|
+
} | undefined;
|
|
87
|
+
features?: {
|
|
88
|
+
types?: boolean | undefined;
|
|
89
|
+
services?: boolean | undefined;
|
|
90
|
+
actions?: boolean | undefined;
|
|
91
|
+
} | undefined;
|
|
92
|
+
}>;
|
|
93
|
+
type StrapiIntegrateConfig = z.infer<typeof configSchema>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Helper function for defining configuration with type safety
|
|
97
|
+
*/
|
|
98
|
+
declare function defineConfig(config: Partial<StrapiIntegrateConfig>): StrapiIntegrateConfig;
|
|
99
|
+
/**
|
|
100
|
+
* Load configuration from strapi.config.ts
|
|
101
|
+
*/
|
|
102
|
+
declare function loadConfig(cwd?: string): Promise<StrapiIntegrateConfig>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Strapi Schema Types
|
|
106
|
+
* These types represent the structure of Strapi content types
|
|
107
|
+
*/
|
|
108
|
+
/**
|
|
109
|
+
* Locale from Strapi i18n
|
|
110
|
+
*/
|
|
111
|
+
interface StrapiLocale {
|
|
112
|
+
id: number;
|
|
113
|
+
documentId: string;
|
|
114
|
+
name: string;
|
|
115
|
+
code: string;
|
|
116
|
+
isDefault: boolean;
|
|
117
|
+
createdAt: string;
|
|
118
|
+
updatedAt: string;
|
|
119
|
+
publishedAt: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Raw schema from Strapi API
|
|
123
|
+
*/
|
|
124
|
+
interface StrapiSchema {
|
|
125
|
+
contentTypes: ContentTypeSchema[];
|
|
126
|
+
components: ComponentSchema[];
|
|
127
|
+
locales: StrapiLocale[];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Content type schema (Strapi v5 structure)
|
|
131
|
+
*/
|
|
132
|
+
interface ContentTypeSchema {
|
|
133
|
+
uid: string;
|
|
134
|
+
apiID: string;
|
|
135
|
+
plugin?: string;
|
|
136
|
+
schema: {
|
|
137
|
+
kind: "collectionType" | "singleType";
|
|
138
|
+
singularName: string;
|
|
139
|
+
pluralName: string;
|
|
140
|
+
displayName: string;
|
|
141
|
+
description?: string;
|
|
142
|
+
draftAndPublish?: boolean;
|
|
143
|
+
visible?: boolean;
|
|
144
|
+
pluginOptions?: Record<string, unknown>;
|
|
145
|
+
attributes: Record<string, Attribute>;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Component schema (Strapi v5 structure)
|
|
150
|
+
*/
|
|
151
|
+
interface ComponentSchema {
|
|
152
|
+
uid: string;
|
|
153
|
+
category: string;
|
|
154
|
+
apiId: string;
|
|
155
|
+
schema: {
|
|
156
|
+
displayName: string;
|
|
157
|
+
description?: string;
|
|
158
|
+
collectionName?: string;
|
|
159
|
+
attributes: Record<string, Attribute>;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Parsed schema (simplified for code generation)
|
|
164
|
+
*/
|
|
165
|
+
interface ParsedSchema {
|
|
166
|
+
collections: CollectionType[];
|
|
167
|
+
singles: SingleType[];
|
|
168
|
+
components: ComponentType[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Collection type
|
|
172
|
+
*/
|
|
173
|
+
interface CollectionType {
|
|
174
|
+
uid: string;
|
|
175
|
+
apiId: string;
|
|
176
|
+
singularName: string;
|
|
177
|
+
pluralName: string;
|
|
178
|
+
displayName: string;
|
|
179
|
+
description?: string;
|
|
180
|
+
draftAndPublish: boolean;
|
|
181
|
+
localized: boolean;
|
|
182
|
+
attributes: Record<string, Attribute>;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Single type
|
|
186
|
+
*/
|
|
187
|
+
interface SingleType {
|
|
188
|
+
uid: string;
|
|
189
|
+
apiId: string;
|
|
190
|
+
singularName: string;
|
|
191
|
+
displayName: string;
|
|
192
|
+
description?: string;
|
|
193
|
+
draftAndPublish: boolean;
|
|
194
|
+
localized: boolean;
|
|
195
|
+
attributes: Record<string, Attribute>;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Component type
|
|
199
|
+
*/
|
|
200
|
+
interface ComponentType {
|
|
201
|
+
uid: string;
|
|
202
|
+
category: string;
|
|
203
|
+
name: string;
|
|
204
|
+
displayName: string;
|
|
205
|
+
description?: string;
|
|
206
|
+
attributes: Record<string, Attribute>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Attribute types
|
|
210
|
+
*/
|
|
211
|
+
type AttributeType = "string" | "text" | "richtext" | "blocks" | "email" | "password" | "uid" | "integer" | "biginteger" | "float" | "decimal" | "boolean" | "date" | "time" | "datetime" | "timestamp" | "json" | "enumeration" | "media" | "relation" | "component" | "dynamiczone";
|
|
212
|
+
/**
|
|
213
|
+
* Base attribute
|
|
214
|
+
*/
|
|
215
|
+
interface BaseAttribute {
|
|
216
|
+
type: AttributeType;
|
|
217
|
+
required?: boolean;
|
|
218
|
+
unique?: boolean;
|
|
219
|
+
private?: boolean;
|
|
220
|
+
configurable?: boolean;
|
|
221
|
+
default?: unknown;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* String attribute
|
|
225
|
+
*/
|
|
226
|
+
interface StringAttribute extends BaseAttribute {
|
|
227
|
+
type: "string" | "text" | "richtext" | "email" | "password" | "uid";
|
|
228
|
+
minLength?: number;
|
|
229
|
+
maxLength?: number;
|
|
230
|
+
regex?: string;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Blocks attribute (Strapi v5 rich text)
|
|
234
|
+
*/
|
|
235
|
+
interface BlocksAttribute extends BaseAttribute {
|
|
236
|
+
type: "blocks";
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Number attribute
|
|
240
|
+
*/
|
|
241
|
+
interface NumberAttribute extends BaseAttribute {
|
|
242
|
+
type: "integer" | "biginteger" | "float" | "decimal";
|
|
243
|
+
min?: number;
|
|
244
|
+
max?: number;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Boolean attribute
|
|
248
|
+
*/
|
|
249
|
+
interface BooleanAttribute extends BaseAttribute {
|
|
250
|
+
type: "boolean";
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Date attribute
|
|
254
|
+
*/
|
|
255
|
+
interface DateAttribute extends BaseAttribute {
|
|
256
|
+
type: "date" | "time" | "datetime" | "timestamp";
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* JSON attribute
|
|
260
|
+
*/
|
|
261
|
+
interface JsonAttribute extends BaseAttribute {
|
|
262
|
+
type: "json";
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Enumeration attribute
|
|
266
|
+
*/
|
|
267
|
+
interface EnumerationAttribute extends BaseAttribute {
|
|
268
|
+
type: "enumeration";
|
|
269
|
+
enum: string[];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Media attribute
|
|
273
|
+
*/
|
|
274
|
+
interface MediaAttribute extends BaseAttribute {
|
|
275
|
+
type: "media";
|
|
276
|
+
multiple?: boolean;
|
|
277
|
+
allowedTypes?: ("images" | "videos" | "files" | "audios")[];
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Relation attribute
|
|
281
|
+
*/
|
|
282
|
+
interface RelationAttribute extends BaseAttribute {
|
|
283
|
+
type: "relation";
|
|
284
|
+
relation: "oneToOne" | "oneToMany" | "manyToOne" | "manyToMany";
|
|
285
|
+
target: string;
|
|
286
|
+
inversedBy?: string;
|
|
287
|
+
mappedBy?: string;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Component attribute
|
|
291
|
+
*/
|
|
292
|
+
interface ComponentAttribute extends BaseAttribute {
|
|
293
|
+
type: "component";
|
|
294
|
+
component: string;
|
|
295
|
+
repeatable?: boolean;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Dynamic zone attribute
|
|
299
|
+
*/
|
|
300
|
+
interface DynamicZoneAttribute extends BaseAttribute {
|
|
301
|
+
type: "dynamiczone";
|
|
302
|
+
components: string[];
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Union of all attribute types
|
|
306
|
+
*/
|
|
307
|
+
type Attribute = StringAttribute | BlocksAttribute | NumberAttribute | BooleanAttribute | DateAttribute | JsonAttribute | EnumerationAttribute | MediaAttribute | RelationAttribute | ComponentAttribute | DynamicZoneAttribute;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Fetch content type schema from Strapi
|
|
311
|
+
*/
|
|
312
|
+
declare function fetchSchema(url: string, token?: string): Promise<StrapiSchema>;
|
|
313
|
+
type StrapiVersion = "v4" | "v5";
|
|
314
|
+
interface VersionDetectionResult {
|
|
315
|
+
detected: StrapiVersion | null;
|
|
316
|
+
confidence: "high" | "medium" | "low";
|
|
317
|
+
reason: string;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Detect Strapi version by analyzing content types unique to each version.
|
|
321
|
+
*
|
|
322
|
+
* V5-exclusive content types:
|
|
323
|
+
* - plugin::content-releases.release
|
|
324
|
+
* - plugin::content-releases.release-action
|
|
325
|
+
* - plugin::review-workflows.workflow
|
|
326
|
+
* - plugin::review-workflows.workflow-stage
|
|
327
|
+
* - admin::session
|
|
328
|
+
*
|
|
329
|
+
* V5-exclusive attributes:
|
|
330
|
+
* - entryDocumentId in plugin::content-releases.release-action
|
|
331
|
+
*/
|
|
332
|
+
declare function detectStrapiVersion(url: string, token?: string): Promise<VersionDetectionResult>;
|
|
333
|
+
/**
|
|
334
|
+
* Test connection to Strapi
|
|
335
|
+
*/
|
|
336
|
+
declare function testConnection(url: string, token?: string): Promise<{
|
|
337
|
+
success: boolean;
|
|
338
|
+
message: string;
|
|
339
|
+
version?: string;
|
|
340
|
+
}>;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Parse raw Strapi schema into a structured format for code generation
|
|
344
|
+
*/
|
|
345
|
+
declare function parseSchema(schema: StrapiSchema): ParsedSchema;
|
|
346
|
+
/**
|
|
347
|
+
* Convert uid to a valid TypeScript identifier
|
|
348
|
+
*/
|
|
349
|
+
declare function uidToIdentifier(uid: string): string;
|
|
350
|
+
/**
|
|
351
|
+
* Convert string to PascalCase
|
|
352
|
+
*/
|
|
353
|
+
declare function toPascalCase(str: string): string;
|
|
354
|
+
/**
|
|
355
|
+
* Convert string to camelCase
|
|
356
|
+
*/
|
|
357
|
+
declare function toCamelCase(str: string): string;
|
|
358
|
+
/**
|
|
359
|
+
* Convert string to kebab-case
|
|
360
|
+
*/
|
|
361
|
+
declare function toKebabCase(str: string): string;
|
|
362
|
+
|
|
363
|
+
export { type Attribute, type AttributeType, type BlocksAttribute, type BooleanAttribute, type CollectionType, type ComponentAttribute, type ComponentSchema, type ComponentType, type ContentTypeSchema, type DateAttribute, type DynamicZoneAttribute, type EnumerationAttribute, type JsonAttribute, type MediaAttribute, type NumberAttribute, type ParsedSchema, type RelationAttribute, type SingleType, type StrapiIntegrateConfig, type StrapiLocale, type StrapiSchema, type StrapiVersion, type StringAttribute, type VersionDetectionResult, configSchema, defineConfig, detectStrapiVersion, fetchSchema, loadConfig, parseSchema, testConnection, toCamelCase, toKebabCase, toPascalCase, uidToIdentifier };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { createJiti } from 'jiti';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import { config } from 'dotenv';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
// src/config/loader.ts
|
|
8
|
+
var configSchema = z.object({
|
|
9
|
+
// Strapi connection
|
|
10
|
+
url: z.string().url("url must be a valid URL"),
|
|
11
|
+
token: z.string().min(1, "token is required").optional(),
|
|
12
|
+
// Strapi version
|
|
13
|
+
strapiVersion: z.enum(["v4", "v5"]).default("v5"),
|
|
14
|
+
// Output paths
|
|
15
|
+
output: z.object({
|
|
16
|
+
path: z.string().default("src/strapi"),
|
|
17
|
+
types: z.string().default("types"),
|
|
18
|
+
services: z.string().default("services"),
|
|
19
|
+
actions: z.string().default("actions/strapi"),
|
|
20
|
+
// Output structure: 'by-layer' (types/, services/, actions/) or 'by-feature' (article/, category/)
|
|
21
|
+
structure: z.enum(["by-layer", "by-feature"]).default("by-feature")
|
|
22
|
+
}).default({}),
|
|
23
|
+
// Features to generate
|
|
24
|
+
features: z.object({
|
|
25
|
+
types: z.boolean().default(true),
|
|
26
|
+
services: z.boolean().default(true),
|
|
27
|
+
actions: z.boolean().default(true)
|
|
28
|
+
}).default({}),
|
|
29
|
+
// Advanced options
|
|
30
|
+
options: z.object({
|
|
31
|
+
// Include draft content types
|
|
32
|
+
includeDrafts: z.boolean().default(false),
|
|
33
|
+
// Generate strict types (no optional fields)
|
|
34
|
+
strictTypes: z.boolean().default(false)
|
|
35
|
+
}).default({})
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// src/config/loader.ts
|
|
39
|
+
var CONFIG_FILES = ["strapi.config.ts", "strapi.config.js", "strapi.config.mjs"];
|
|
40
|
+
function defineConfig(config) {
|
|
41
|
+
return configSchema.parse(config);
|
|
42
|
+
}
|
|
43
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
44
|
+
config({ path: path.join(cwd, ".env") });
|
|
45
|
+
let configPath = null;
|
|
46
|
+
for (const file of CONFIG_FILES) {
|
|
47
|
+
const fullPath = path.join(cwd, file);
|
|
48
|
+
try {
|
|
49
|
+
await fs.access(fullPath);
|
|
50
|
+
configPath = fullPath;
|
|
51
|
+
break;
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!configPath) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Could not find strapi.config.ts in ${cwd}. Run "npx strapi-integrate init" first.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const jiti = createJiti(import.meta.url, {
|
|
61
|
+
interopDefault: true
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
const rawConfig = await jiti.import(configPath);
|
|
65
|
+
const config = rawConfig.default || rawConfig;
|
|
66
|
+
const resolvedConfig = resolveEnvVariables(config);
|
|
67
|
+
return configSchema.parse(resolvedConfig);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
throw new Error(`Failed to load config: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function resolveEnvVariables(config) {
|
|
76
|
+
const resolved = {};
|
|
77
|
+
for (const [key, value] of Object.entries(config)) {
|
|
78
|
+
if (typeof value === "string") {
|
|
79
|
+
if (value.startsWith("process.env.")) {
|
|
80
|
+
const envKey = value.replace("process.env.", "");
|
|
81
|
+
resolved[key] = process.env[envKey] || value;
|
|
82
|
+
} else {
|
|
83
|
+
resolved[key] = value;
|
|
84
|
+
}
|
|
85
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
86
|
+
resolved[key] = resolveEnvVariables(value);
|
|
87
|
+
} else {
|
|
88
|
+
resolved[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!resolved["token"] && process.env["STRAPI_TOKEN"]) {
|
|
92
|
+
resolved["token"] = process.env["STRAPI_TOKEN"];
|
|
93
|
+
}
|
|
94
|
+
return resolved;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/schema/fetcher.ts
|
|
98
|
+
function normalizeUrl(url) {
|
|
99
|
+
return url.replace(/\/+$/, "");
|
|
100
|
+
}
|
|
101
|
+
async function fetchSchema(url, token) {
|
|
102
|
+
const baseUrl = normalizeUrl(url);
|
|
103
|
+
const headers = {
|
|
104
|
+
"Content-Type": "application/json"
|
|
105
|
+
};
|
|
106
|
+
if (token) {
|
|
107
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
108
|
+
}
|
|
109
|
+
const contentTypesResponse = await fetch(
|
|
110
|
+
`${baseUrl}/api/content-type-builder/content-types`,
|
|
111
|
+
{ headers }
|
|
112
|
+
);
|
|
113
|
+
if (!contentTypesResponse.ok) {
|
|
114
|
+
const error = await contentTypesResponse.text();
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Failed to fetch content types: ${contentTypesResponse.status} ${error}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const contentTypesData = await contentTypesResponse.json();
|
|
120
|
+
const componentsResponse = await fetch(
|
|
121
|
+
`${baseUrl}/api/content-type-builder/components`,
|
|
122
|
+
{ headers }
|
|
123
|
+
);
|
|
124
|
+
if (!componentsResponse.ok) {
|
|
125
|
+
const error = await componentsResponse.text();
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Failed to fetch components: ${componentsResponse.status} ${error}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const componentsData = await componentsResponse.json();
|
|
131
|
+
let locales = [];
|
|
132
|
+
try {
|
|
133
|
+
const localesResponse = await fetch(
|
|
134
|
+
`${baseUrl}/api/i18n/locales`,
|
|
135
|
+
{ headers }
|
|
136
|
+
);
|
|
137
|
+
if (localesResponse.ok) {
|
|
138
|
+
locales = await localesResponse.json();
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
const userContentTypes = (contentTypesData.data || []).filter((ct) => {
|
|
143
|
+
return ct.uid.startsWith("api::") && !ct.uid.includes("strapi::") && !ct.uid.includes("admin::");
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
contentTypes: userContentTypes,
|
|
147
|
+
components: componentsData.data || [],
|
|
148
|
+
locales
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function detectStrapiVersion(url, token) {
|
|
152
|
+
const baseUrl = normalizeUrl(url);
|
|
153
|
+
const headers = {
|
|
154
|
+
"Content-Type": "application/json"
|
|
155
|
+
};
|
|
156
|
+
if (token) {
|
|
157
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const response = await fetch(
|
|
161
|
+
`${baseUrl}/api/content-type-builder/content-types`,
|
|
162
|
+
{ headers }
|
|
163
|
+
);
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
return {
|
|
166
|
+
detected: null,
|
|
167
|
+
confidence: "low",
|
|
168
|
+
reason: `Could not fetch schema: ${response.status}`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const data = await response.json();
|
|
172
|
+
const contentTypes = data.data || [];
|
|
173
|
+
if (contentTypes.length === 0) {
|
|
174
|
+
return {
|
|
175
|
+
detected: null,
|
|
176
|
+
confidence: "low",
|
|
177
|
+
reason: "No content types found in schema"
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const v5ExclusiveUids = [
|
|
181
|
+
"plugin::content-releases.release",
|
|
182
|
+
"plugin::content-releases.release-action",
|
|
183
|
+
"plugin::review-workflows.workflow",
|
|
184
|
+
"plugin::review-workflows.workflow-stage",
|
|
185
|
+
"admin::session"
|
|
186
|
+
];
|
|
187
|
+
const contentTypeUids = contentTypes.map((ct) => ct.uid);
|
|
188
|
+
const foundV5ContentType = v5ExclusiveUids.find((uid) => contentTypeUids.includes(uid));
|
|
189
|
+
if (foundV5ContentType) {
|
|
190
|
+
return {
|
|
191
|
+
detected: "v5",
|
|
192
|
+
confidence: "high",
|
|
193
|
+
reason: `Found v5-exclusive content type: ${foundV5ContentType}`
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const releaseAction = contentTypes.find((ct) => ct.uid === "plugin::content-releases.release-action");
|
|
197
|
+
if (releaseAction?.schema?.attributes && "entryDocumentId" in releaseAction.schema.attributes) {
|
|
198
|
+
return {
|
|
199
|
+
detected: "v5",
|
|
200
|
+
confidence: "high",
|
|
201
|
+
reason: "Found entryDocumentId attribute (Strapi v5 feature)"
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const hasV4AdminTypes = contentTypeUids.includes("admin::permission") && contentTypeUids.includes("admin::user") && !contentTypeUids.includes("admin::session");
|
|
205
|
+
if (hasV4AdminTypes) {
|
|
206
|
+
return {
|
|
207
|
+
detected: "v4",
|
|
208
|
+
confidence: "high",
|
|
209
|
+
reason: "No v5-exclusive content types found, confirmed v4 structure"
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
detected: "v4",
|
|
214
|
+
confidence: "medium",
|
|
215
|
+
reason: "Could not find v5-specific features, assuming v4"
|
|
216
|
+
};
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return {
|
|
219
|
+
detected: null,
|
|
220
|
+
confidence: "low",
|
|
221
|
+
reason: error instanceof Error ? error.message : "Detection failed"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function testConnection(url, token) {
|
|
226
|
+
const baseUrl = normalizeUrl(url);
|
|
227
|
+
try {
|
|
228
|
+
const headers = {
|
|
229
|
+
"Content-Type": "application/json"
|
|
230
|
+
};
|
|
231
|
+
if (token) {
|
|
232
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
233
|
+
}
|
|
234
|
+
const response = await fetch(
|
|
235
|
+
`${baseUrl}/api/content-type-builder/content-types`,
|
|
236
|
+
{ headers }
|
|
237
|
+
);
|
|
238
|
+
if (response.ok) {
|
|
239
|
+
return {
|
|
240
|
+
success: true,
|
|
241
|
+
message: "Connected successfully"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (response.status === 401) {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
message: "Invalid or missing API token"
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (response.status === 403) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
message: "API token does not have permission to access content-type-builder"
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
message: `Failed to connect: ${response.status} ${response.statusText}`
|
|
259
|
+
};
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
message: error instanceof Error ? error.message : "Connection failed"
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/schema/parser.ts
|
|
269
|
+
function parseSchema(schema) {
|
|
270
|
+
const collections = [];
|
|
271
|
+
const singles = [];
|
|
272
|
+
for (const ct of schema.contentTypes) {
|
|
273
|
+
const { kind, singularName, pluralName, displayName, description, draftAndPublish, pluginOptions, attributes } = ct.schema;
|
|
274
|
+
const filteredAttributes = filterSystemAttributes(attributes);
|
|
275
|
+
const localized = Boolean(
|
|
276
|
+
pluginOptions?.i18n && typeof pluginOptions.i18n === "object" && pluginOptions.i18n.localized
|
|
277
|
+
);
|
|
278
|
+
if (kind === "collectionType") {
|
|
279
|
+
collections.push({
|
|
280
|
+
uid: ct.uid,
|
|
281
|
+
apiId: ct.apiID,
|
|
282
|
+
singularName,
|
|
283
|
+
pluralName,
|
|
284
|
+
displayName,
|
|
285
|
+
description,
|
|
286
|
+
draftAndPublish: draftAndPublish ?? false,
|
|
287
|
+
localized,
|
|
288
|
+
attributes: filteredAttributes
|
|
289
|
+
});
|
|
290
|
+
} else if (kind === "singleType") {
|
|
291
|
+
singles.push({
|
|
292
|
+
uid: ct.uid,
|
|
293
|
+
apiId: ct.apiID,
|
|
294
|
+
singularName,
|
|
295
|
+
displayName,
|
|
296
|
+
description,
|
|
297
|
+
draftAndPublish: draftAndPublish ?? false,
|
|
298
|
+
localized,
|
|
299
|
+
attributes: filteredAttributes
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const components = schema.components.map((comp) => {
|
|
304
|
+
const filteredAttributes = filterSystemAttributes(comp.schema.attributes);
|
|
305
|
+
return {
|
|
306
|
+
uid: comp.uid,
|
|
307
|
+
category: comp.category,
|
|
308
|
+
name: comp.apiId,
|
|
309
|
+
displayName: comp.schema.displayName,
|
|
310
|
+
description: comp.schema.description,
|
|
311
|
+
attributes: filteredAttributes
|
|
312
|
+
};
|
|
313
|
+
});
|
|
314
|
+
collections.sort((a, b) => a.singularName.localeCompare(b.singularName));
|
|
315
|
+
singles.sort((a, b) => a.singularName.localeCompare(b.singularName));
|
|
316
|
+
components.sort((a, b) => a.name.localeCompare(b.name));
|
|
317
|
+
return {
|
|
318
|
+
collections,
|
|
319
|
+
singles,
|
|
320
|
+
components
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function filterSystemAttributes(attributes) {
|
|
324
|
+
const SYSTEM_ATTRIBUTES = [
|
|
325
|
+
"createdBy",
|
|
326
|
+
"updatedBy",
|
|
327
|
+
"localizations",
|
|
328
|
+
"locale"
|
|
329
|
+
];
|
|
330
|
+
const filtered = {};
|
|
331
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
332
|
+
if (SYSTEM_ATTRIBUTES.includes(key)) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (value.private === true) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
filtered[key] = value;
|
|
339
|
+
}
|
|
340
|
+
return filtered;
|
|
341
|
+
}
|
|
342
|
+
function uidToIdentifier(uid) {
|
|
343
|
+
const parts = uid.split(".");
|
|
344
|
+
const name = parts[parts.length - 1] || uid;
|
|
345
|
+
return toPascalCase(name);
|
|
346
|
+
}
|
|
347
|
+
function toPascalCase(str) {
|
|
348
|
+
return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
349
|
+
}
|
|
350
|
+
function toCamelCase(str) {
|
|
351
|
+
const pascal = toPascalCase(str);
|
|
352
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
353
|
+
}
|
|
354
|
+
function toKebabCase(str) {
|
|
355
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export { configSchema, defineConfig, detectStrapiVersion, fetchSchema, loadConfig, parseSchema, testConnection, toCamelCase, toKebabCase, toPascalCase, uidToIdentifier };
|
|
359
|
+
//# sourceMappingURL=index.js.map
|
|
360
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/schema.ts","../src/config/loader.ts","../src/schema/fetcher.ts","../src/schema/parser.ts"],"names":["loadEnv"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from \"zod\";\n\n/**\n * Configuration schema for strapi-integrate\n */\nexport const configSchema = z.object({\n // Strapi connection\n url: z.string().url(\"url must be a valid URL\"),\n token: z.string().min(1, \"token is required\").optional(),\n\n // Strapi version\n strapiVersion: z.enum([\"v4\", \"v5\"]).default(\"v5\"),\n\n // Output paths\n output: z.object({\n path: z.string().default(\"src/strapi\"),\n types: z.string().default(\"types\"),\n services: z.string().default(\"services\"),\n actions: z.string().default(\"actions/strapi\"),\n // Output structure: 'by-layer' (types/, services/, actions/) or 'by-feature' (article/, category/)\n structure: z.enum([\"by-layer\", \"by-feature\"]).default(\"by-feature\"),\n }).default({}),\n\n // Features to generate\n features: z.object({\n types: z.boolean().default(true),\n services: z.boolean().default(true),\n actions: z.boolean().default(true),\n }).default({}),\n\n // Advanced options\n options: z.object({\n // Include draft content types\n includeDrafts: z.boolean().default(false),\n // Generate strict types (no optional fields)\n strictTypes: z.boolean().default(false),\n }).default({}),\n});\n\nexport type StrapiIntegrateConfig = z.infer<typeof configSchema>;\n","import { createJiti } from 'jiti';\nimport path from 'node:path';\nimport fs from 'node:fs/promises';\nimport { config as loadEnv } from 'dotenv';\nimport { configSchema, type StrapiIntegrateConfig } from './schema.js';\n\nconst CONFIG_FILES = ['strapi.config.ts', 'strapi.config.js', 'strapi.config.mjs'];\n\n/**\n * Helper function for defining configuration with type safety\n */\nexport function defineConfig(config: Partial<StrapiIntegrateConfig>): StrapiIntegrateConfig {\n return configSchema.parse(config);\n}\n\n/**\n * Load configuration from strapi.config.ts\n */\nexport async function loadConfig(cwd: string = process.cwd()): Promise<StrapiIntegrateConfig> {\n // Load environment variables from .env file\n loadEnv({ path: path.join(cwd, '.env') });\n\n // Find config file\n let configPath: string | null = null;\n\n for (const file of CONFIG_FILES) {\n const fullPath = path.join(cwd, file);\n try {\n await fs.access(fullPath);\n configPath = fullPath;\n break;\n } catch {\n // File doesn't exist, continue\n }\n }\n\n if (!configPath) {\n throw new Error(\n `Could not find strapi.config.ts in ${cwd}. Run \"npx strapi-integrate init\" first.`\n );\n }\n\n // Load config using jiti (supports TypeScript)\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n });\n\n try {\n const rawConfig = await jiti.import(configPath);\n const config = (rawConfig as { default?: unknown }).default || rawConfig;\n\n // Resolve environment variables\n const resolvedConfig = resolveEnvVariables(config as Record<string, unknown>);\n\n // Validate with Zod\n return configSchema.parse(resolvedConfig);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to load config: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Resolve environment variables in config\n */\nfunction resolveEnvVariables(config: Record<string, unknown>): Record<string, unknown> {\n const resolved: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === 'string') {\n // Check if it's a process.env reference that wasn't resolved\n if (value.startsWith('process.env.')) {\n const envKey = value.replace('process.env.', '');\n resolved[key] = process.env[envKey] || value;\n } else {\n resolved[key] = value;\n }\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n resolved[key] = resolveEnvVariables(value as Record<string, unknown>);\n } else {\n resolved[key] = value;\n }\n }\n\n // Also check for token from environment\n if (!resolved['token'] && process.env['STRAPI_TOKEN']) {\n resolved['token'] = process.env['STRAPI_TOKEN'];\n }\n\n return resolved;\n}\n","import type { StrapiSchema, ContentTypeSchema, ComponentSchema, StrapiLocale } from \"./types.js\";\n\ninterface ContentTypesApiResponse {\n data: ContentTypeSchema[];\n}\n\ninterface ComponentsApiResponse {\n data: ComponentSchema[];\n}\n\ntype LocalesApiResponse = StrapiLocale[];\n\n/**\n * Normalize URL by removing trailing slashes\n */\nfunction normalizeUrl(url: string): string {\n return url.replace(/\\/+$/, '');\n}\n\n/**\n * Fetch content type schema from Strapi\n */\nexport async function fetchSchema(\n url: string,\n token?: string\n): Promise<StrapiSchema> {\n const baseUrl = normalizeUrl(url);\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (token) {\n headers[\"Authorization\"] = `Bearer ${token}`;\n }\n\n // Fetch content types from content-type-builder API\n const contentTypesResponse = await fetch(\n `${baseUrl}/api/content-type-builder/content-types`,\n { headers }\n );\n\n if (!contentTypesResponse.ok) {\n const error = await contentTypesResponse.text();\n throw new Error(\n `Failed to fetch content types: ${contentTypesResponse.status} ${error}`\n );\n }\n\n const contentTypesData = await contentTypesResponse.json() as ContentTypesApiResponse;\n\n // Fetch components\n const componentsResponse = await fetch(\n `${baseUrl}/api/content-type-builder/components`,\n { headers }\n );\n\n if (!componentsResponse.ok) {\n const error = await componentsResponse.text();\n throw new Error(\n `Failed to fetch components: ${componentsResponse.status} ${error}`\n );\n }\n\n const componentsData = await componentsResponse.json() as ComponentsApiResponse;\n\n // Fetch locales (i18n)\n let locales: StrapiLocale[] = [];\n try {\n const localesResponse = await fetch(\n `${baseUrl}/api/i18n/locales`,\n { headers }\n );\n\n if (localesResponse.ok) {\n locales = await localesResponse.json() as LocalesApiResponse;\n }\n } catch {\n // i18n might not be enabled, continue without locales\n }\n\n // Filter out internal Strapi content types\n const userContentTypes = (contentTypesData.data || []).filter((ct) => {\n return (\n ct.uid.startsWith(\"api::\") &&\n !ct.uid.includes(\"strapi::\") &&\n !ct.uid.includes(\"admin::\")\n );\n });\n\n return {\n contentTypes: userContentTypes,\n components: componentsData.data || [],\n locales,\n };\n}\n\nexport type StrapiVersion = \"v4\" | \"v5\";\n\nexport interface VersionDetectionResult {\n detected: StrapiVersion | null;\n confidence: \"high\" | \"medium\" | \"low\";\n reason: string;\n}\n\n/**\n * Detect Strapi version by analyzing content types unique to each version.\n *\n * V5-exclusive content types:\n * - plugin::content-releases.release\n * - plugin::content-releases.release-action\n * - plugin::review-workflows.workflow\n * - plugin::review-workflows.workflow-stage\n * - admin::session\n *\n * V5-exclusive attributes:\n * - entryDocumentId in plugin::content-releases.release-action\n */\nexport async function detectStrapiVersion(\n url: string,\n token?: string\n): Promise<VersionDetectionResult> {\n const baseUrl = normalizeUrl(url);\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (token) {\n headers[\"Authorization\"] = `Bearer ${token}`;\n }\n\n try {\n const response = await fetch(\n `${baseUrl}/api/content-type-builder/content-types`,\n { headers }\n );\n\n if (!response.ok) {\n return {\n detected: null,\n confidence: \"low\",\n reason: `Could not fetch schema: ${response.status}`,\n };\n }\n\n const data = await response.json() as ContentTypesApiResponse;\n const contentTypes = data.data || [];\n\n if (contentTypes.length === 0) {\n return {\n detected: null,\n confidence: \"low\",\n reason: \"No content types found in schema\",\n };\n }\n\n // V5-exclusive content type UIDs\n const v5ExclusiveUids = [\n \"plugin::content-releases.release\",\n \"plugin::content-releases.release-action\",\n \"plugin::review-workflows.workflow\",\n \"plugin::review-workflows.workflow-stage\",\n \"admin::session\",\n ];\n\n // Check for V5-exclusive content types\n const contentTypeUids = contentTypes.map((ct) => ct.uid);\n const foundV5ContentType = v5ExclusiveUids.find((uid) => contentTypeUids.includes(uid));\n\n if (foundV5ContentType) {\n return {\n detected: \"v5\",\n confidence: \"high\",\n reason: `Found v5-exclusive content type: ${foundV5ContentType}`,\n };\n }\n\n // Check for entryDocumentId attribute (v5 exclusive)\n const releaseAction = contentTypes.find((ct) => ct.uid === \"plugin::content-releases.release-action\");\n if (releaseAction?.schema?.attributes && \"entryDocumentId\" in releaseAction.schema.attributes) {\n return {\n detected: \"v5\",\n confidence: \"high\",\n reason: \"Found entryDocumentId attribute (Strapi v5 feature)\",\n };\n }\n\n // If none of the v5-exclusive features are found, it's v4\n // Check for typical v4 content types to confirm\n const hasV4AdminTypes = contentTypeUids.includes(\"admin::permission\") &&\n contentTypeUids.includes(\"admin::user\") &&\n !contentTypeUids.includes(\"admin::session\");\n\n if (hasV4AdminTypes) {\n return {\n detected: \"v4\",\n confidence: \"high\",\n reason: \"No v5-exclusive content types found, confirmed v4 structure\",\n };\n }\n\n // Fallback - assume v4 since it's the legacy version\n return {\n detected: \"v4\",\n confidence: \"medium\",\n reason: \"Could not find v5-specific features, assuming v4\",\n };\n } catch (error) {\n return {\n detected: null,\n confidence: \"low\",\n reason: error instanceof Error ? error.message : \"Detection failed\",\n };\n }\n}\n\n/**\n * Test connection to Strapi\n */\nexport async function testConnection(\n url: string,\n token?: string\n): Promise<{ success: boolean; message: string; version?: string }> {\n const baseUrl = normalizeUrl(url);\n\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (token) {\n headers[\"Authorization\"] = `Bearer ${token}`;\n }\n\n // Try to fetch content types (requires authentication)\n const response = await fetch(\n `${baseUrl}/api/content-type-builder/content-types`,\n { headers }\n );\n\n if (response.ok) {\n return {\n success: true,\n message: \"Connected successfully\",\n };\n }\n\n if (response.status === 401) {\n return {\n success: false,\n message: \"Invalid or missing API token\",\n };\n }\n\n if (response.status === 403) {\n return {\n success: false,\n message: \"API token does not have permission to access content-type-builder\",\n };\n }\n\n return {\n success: false,\n message: `Failed to connect: ${response.status} ${response.statusText}`,\n };\n } catch (error) {\n return {\n success: false,\n message: error instanceof Error ? error.message : \"Connection failed\",\n };\n }\n}\n","import type {\n StrapiSchema,\n ParsedSchema,\n CollectionType,\n SingleType,\n ComponentType,\n Attribute,\n} from './types.js';\n\n/**\n * Parse raw Strapi schema into a structured format for code generation\n */\nexport function parseSchema(schema: StrapiSchema): ParsedSchema {\n const collections: CollectionType[] = [];\n const singles: SingleType[] = [];\n\n for (const ct of schema.contentTypes) {\n const { kind, singularName, pluralName, displayName, description, draftAndPublish, pluginOptions, attributes } = ct.schema;\n\n // Filter out system attributes\n const filteredAttributes = filterSystemAttributes(attributes);\n\n // Check if i18n is enabled for this content type\n const localized = Boolean(\n pluginOptions?.i18n &&\n typeof pluginOptions.i18n === 'object' &&\n (pluginOptions.i18n as Record<string, unknown>).localized\n );\n\n if (kind === 'collectionType') {\n collections.push({\n uid: ct.uid,\n apiId: ct.apiID,\n singularName,\n pluralName,\n displayName,\n description,\n draftAndPublish: draftAndPublish ?? false,\n localized,\n attributes: filteredAttributes,\n });\n } else if (kind === 'singleType') {\n singles.push({\n uid: ct.uid,\n apiId: ct.apiID,\n singularName,\n displayName,\n description,\n draftAndPublish: draftAndPublish ?? false,\n localized,\n attributes: filteredAttributes,\n });\n }\n }\n\n // Parse components\n const components: ComponentType[] = schema.components.map((comp) => {\n const filteredAttributes = filterSystemAttributes(comp.schema.attributes);\n\n return {\n uid: comp.uid,\n category: comp.category,\n name: comp.apiId,\n displayName: comp.schema.displayName,\n description: comp.schema.description,\n attributes: filteredAttributes,\n };\n });\n\n // Sort alphabetically\n collections.sort((a, b) => a.singularName.localeCompare(b.singularName));\n singles.sort((a, b) => a.singularName.localeCompare(b.singularName));\n components.sort((a, b) => a.name.localeCompare(b.name));\n\n return {\n collections,\n singles,\n components,\n };\n}\n\n/**\n * Filter out system attributes that shouldn't be in the generated types\n */\nfunction filterSystemAttributes(\n attributes: Record<string, Attribute>\n): Record<string, Attribute> {\n const SYSTEM_ATTRIBUTES = [\n 'createdBy',\n 'updatedBy',\n 'localizations',\n 'locale',\n ];\n\n const filtered: Record<string, Attribute> = {};\n\n for (const [key, value] of Object.entries(attributes)) {\n // Skip system attributes\n if (SYSTEM_ATTRIBUTES.includes(key)) {\n continue;\n }\n\n // Skip private attributes\n if (value.private === true) {\n continue;\n }\n\n filtered[key] = value;\n }\n\n return filtered;\n}\n\n/**\n * Get the API endpoint name for a content type\n */\nexport function getApiEndpoint(singularName: string, pluralName: string): string {\n return pluralName || `${singularName}s`;\n}\n\n/**\n * Convert uid to a valid TypeScript identifier\n */\nexport function uidToIdentifier(uid: string): string {\n // api::post.post -> Post\n // api::blog-post.blog-post -> BlogPost\n const parts = uid.split('.');\n const name = parts[parts.length - 1] || uid;\n\n return toPascalCase(name);\n}\n\n/**\n * Convert string to PascalCase\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[-_\\s]+/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Convert string to camelCase\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert string to kebab-case\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@strapi2front/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core utilities and configuration for strapi2front",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"dotenv": "^17.2.3",
|
|
19
|
+
"jiti": "^2.4.0",
|
|
20
|
+
"zod": "^3.22.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsup": "^8.0.0",
|
|
24
|
+
"typescript": "^5.3.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"typescript": ">=5.0.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/eleven-estudio/strapi2front.git",
|
|
36
|
+
"directory": "packages/core"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"dev": "tsup --watch",
|
|
41
|
+
"clean": "rm -rf dist",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
}
|
|
44
|
+
}
|