Skip to content

Commit 4125a48

Browse files
committed
feat: support for component schemas
1 parent b88c296 commit 4125a48

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sensinum/astro-strapi-loader",
3-
"version": "1.0.7",
3+
"version": "1.0.8",
44
"description": "Astro loader for Strapi CMS",
55
"keywords": [
66
"astro",

src/types/strapi.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ export interface StrapiContentType {
3434
};
3535
}
3636

37+
export interface StrapiComponent {
38+
uid: string;
39+
category: string;
40+
apiId: string;
41+
schema: {
42+
displayName: string;
43+
description: string;
44+
icon: string;
45+
collectionName: string;
46+
attributes: Array<StrapiAttribute>;
47+
}
48+
}
49+
3750
export interface StrapiAttribute {
3851
type: string;
3952
required?: boolean;

src/utils/__tests__/schema.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe("StrapiSchemaGenerator", () => {
3535
let generator: StrapiSchemaGenerator;
3636

3737
beforeEach(() => {
38-
generator = new StrapiSchemaGenerator([mockContentType]);
38+
generator = new StrapiSchemaGenerator([mockContentType], []);
3939
});
4040

4141
describe("generateSchema", () => {

src/utils/schema.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { z } from "zod";
2-
import type { StrapiContentType, StrapiAttribute } from "../types/strapi";
2+
import type { StrapiComponent, StrapiContentType, StrapiAttribute } from "../types/strapi";
33

44
export class StrapiSchemaGenerator {
55
private contentTypes: Array<StrapiContentType> = [];
6+
private components: Array<StrapiComponent> = [];
67
private strict: boolean = false;
78

8-
constructor(contentTypes: Array<StrapiContentType>, strict: boolean = false) {
9+
constructor(contentTypes: Array<StrapiContentType>, components: Array<StrapiComponent>, strict: boolean = false) {
910
this.contentTypes = contentTypes;
11+
this.components = components;
1012
this.strict = strict;
1113
}
1214

@@ -123,8 +125,16 @@ export class StrapiSchemaGenerator {
123125
case "component":
124126
if (!attribute.component)
125127
throw new Error("Component type requires component name");
126-
// TODO: Implement component schema generation
127-
return z.any();
128+
const component = this.components.find(
129+
(component) => component.uid === attribute.component,
130+
);
131+
if (!component)
132+
throw new Error(`Component ${attribute.component} not found`);
133+
134+
if (attribute.repeatable) {
135+
return z.array(this.generateComponentSchema(component.schema));
136+
}
137+
return this.generateComponentSchema(component.schema);
128138

129139
case "dynamiczone":
130140
// TODO: Implement dynamiczone schema generation
@@ -172,6 +182,16 @@ export class StrapiSchemaGenerator {
172182
return z.object(shape);
173183
}
174184

185+
private generateComponentSchema(componentSchema: StrapiComponent["schema"]): z.ZodObject<any> {
186+
const ref = this;
187+
const shape: Record<string, z.ZodTypeAny> = Object.entries(componentSchema.attributes).reduce((acc, [key, attribute]) => {
188+
const schema = ref.generateAttributeSchema(attribute);
189+
return { ...acc, [key]: schema };
190+
}, {});
191+
192+
return z.object(shape);
193+
}
194+
175195
public generateSchema(contentTypeName: string): z.ZodObject<any> {
176196
const contentType = this.contentTypes.find(
177197
(contentType) => contentType.apiID === contentTypeName,

src/utils/strapi.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z, defineCollection, CollectionConfig } from "astro:content";
22

3-
import type { StrapiContentType, StrapiResponse } from "../types/strapi";
3+
import type { StrapiComponent, StrapiContentType, StrapiResponse } from "../types/strapi";
44
import { StrapiSchemaGenerator } from "./schema";
55
import { strapiLoader } from "./loader";
66

@@ -55,6 +55,18 @@ export async function fetchContentTypes(
5555
return contentTypes.data.filter((contentType) => !contentType.plugin);
5656
}
5757

58+
export async function fetchComponents(
59+
options: Omit<StrapiRequestOptions, "path">,
60+
): Promise<Array<StrapiComponent>> {
61+
const components = await strapiRequest<
62+
StrapiResponse<Array<StrapiComponent>>
63+
>({
64+
...options,
65+
path: "content-type-builder/components",
66+
});
67+
return components.data;
68+
}
69+
5870
export async function fetchContent(
5971
options: Omit<StrapiRequestOptions, "path"> & { contentType: string },
6072
): Promise<any> {
@@ -73,7 +85,8 @@ export async function generateStrapiSchema(
7385
const { url, token, strict } = options;
7486

7587
const contentTypes = await fetchContentTypes({ url, token });
76-
const schemaGenerator = new StrapiSchemaGenerator(contentTypes, strict);
88+
const components = await fetchComponents({ url, token });
89+
const schemaGenerator = new StrapiSchemaGenerator(contentTypes, components, strict);
7790
return schemaGenerator.generateAllSchemas();
7891
}
7992

0 commit comments

Comments
 (0)