Last active
August 26, 2020 09:00
-
-
Save eyn/b9dd0956b2fa6f783b9898b6ffaeb192 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-undef */ | |
/* eslint-disable @typescript-eslint/no-var-requires */ | |
const { writeFileSync } = require("fs"); | |
const { compile } = require("json-schema-to-typescript"); | |
const { mapValues, upperFirst, camelCase } = require("lodash"); | |
const prettier = require("prettier"); | |
const StoryblokClient = require("storyblok-js-client"); | |
const interfaceName = (name) => upperFirst(camelCase(name)); | |
if (!process.env.STORYBLOK_TOKEN || !process.env.STORYBLOK_SPACE_ID) { | |
console.log( | |
`You must set STORYBLOK_TOKEN and STORYBLOK_SPACE_ID in Environment. To get a token go to: https://app.storyblok.com/#!/me/account` | |
); | |
process.exit(); | |
} | |
const Storyblok = new StoryblokClient({ | |
// eslint-disable-next-line no-undef | |
oauthToken: process.env.STORYBLOK_TOKEN, | |
}); | |
const spaceId = process.env.STORYBLOK_SPACE_ID; | |
async function main() { | |
const { | |
data: { component_groups: componentGroups }, | |
} = await Storyblok.get(`spaces/${spaceId}/component_groups/`, {}); | |
const { | |
data: { components }, | |
} = await Storyblok.get(`spaces/${spaceId}/components/`); | |
const { | |
data: { datasources }, | |
} = await Storyblok.get(`spaces/${spaceId}/datasources/`, {}); | |
function getRequired(item) { | |
return Object.entries(item) | |
.filter(([_key, value]) => value.required) | |
.map(([key]) => key); | |
} | |
function convertItem(item, _name) { | |
switch (item.type) { | |
case "text": | |
case "textarea": | |
case "markdown": | |
return { type: "string" }; | |
case "boolean": | |
return { type: "boolean" }; | |
case "asset": | |
return { | |
type: "string", | |
tsType: "Asset", | |
}; | |
case "image": | |
return { | |
type: "object", | |
properties: { | |
filename: { type: "string" }, | |
}, | |
required: ["filename"], | |
additionalProperties: false, | |
}; | |
case "datetime": | |
return { | |
type: "string", | |
}; | |
case "option": | |
if (!item.source) { | |
return { | |
type: "string", | |
enum: item.options.map((x) => x.value), | |
}; | |
} else { | |
if (item.source === "internal_stories") { | |
return { | |
type: "string", | |
tsType: `ContentResult<${interfaceName(item.filter_content_type) || "ContentTypes"}>`, | |
}; | |
} else if (item.source === "internal") { | |
return { | |
type: "string", | |
tsType: `${interfaceName(item.datasource_slug)}DataSource`, | |
}; | |
} | |
throw new Error("UNKNOWN SOURCE" + item.source); | |
} | |
case "options": | |
if (!item.source) { | |
return { | |
type: "array", | |
items: { type: "string", enum: item.options.map((x) => x.value) }, | |
}; | |
} else { | |
if (item.source === "internal_stories") { | |
return { | |
type: "array", | |
items: { | |
type: "string", | |
tsType: `ContentResult<${ | |
interfaceName(item.filter_content_type) || "ContentTypes" | |
}>`, | |
}, | |
}; | |
} else if (item.source === "internal") { | |
return { | |
type: "array", | |
items: { type: "string" }, | |
}; | |
} | |
throw new Error("UNKNOWN SOURCE" + item.source); | |
} | |
case "bloks": | |
if (item.restrict_components) { | |
if (item.component_whitelist && !item.restrict_type) { | |
return { | |
type: "array", | |
items: { | |
anyOf: item.component_whitelist.map((component) => ({ | |
type: "object", | |
tsType: interfaceName(component), | |
})), | |
}, | |
}; | |
} else if (item.restrict_type === "groups") { | |
return { | |
type: "array", | |
items: { | |
anyOf: item.component_group_whitelist.map((componentGroup) => ({ | |
type: "object", | |
tsType: `${interfaceName( | |
componentGroups.find((x) => x.uuid === componentGroup).name | |
)}ComponentGroup`, | |
})), | |
}, | |
}; | |
} else { | |
return { type: "array", items: { type: "object" } }; | |
} | |
} else { | |
return { type: "array", items: { type: "object", tsType: "NestableComponents" } }; | |
} | |
default: | |
throw new Error("UNKNOWN TYPE" + item.type); | |
} | |
} | |
function generateSchema(schema) { | |
return [getRequired(schema), mapValues(schema, convertItem)]; | |
} | |
const definitions = [ | |
`export interface ContentType { | |
name: string; | |
created_at: string; | |
published_at: string; | |
alternates: []; | |
id: number; | |
}`, | |
`export interface Asset { | |
alt: string | null; | |
name: string; | |
focus: null; | |
title: string | null; | |
filename: string; | |
}`, | |
`export interface ContentResult<T> { | |
alternates: []; | |
content: T; | |
created_at: string; | |
default_full_slug: string | null; | |
first_published_at: string; | |
full_slug: string; | |
group_id: string; | |
id: number; | |
is_startpage: boolean; | |
lang: string; | |
meta_data: null; | |
name: string; | |
parent_id: number; | |
path: null; | |
position: number; | |
published_at: string; | |
release_id: null; | |
slug: string; | |
sort_by_date: null; | |
tag_list: string[]; | |
translated_slugs: string[]; | |
uuid: string; | |
}`, | |
]; | |
for (const component of components) { | |
const [required, properties] = generateSchema(component.schema); | |
const jsonSchema = { | |
title: component.name, | |
type: "object", | |
properties, | |
additionalProperties: false, | |
required, | |
}; | |
if (component.is_nestable) { | |
jsonSchema.properties.component = { | |
type: "string", | |
enum: [component.name], | |
}; | |
jsonSchema.required.push("component"); | |
} | |
jsonSchema.properties._uid = { | |
type: "string", | |
}; | |
jsonSchema.required.push("_uid"); | |
const result = await compile(jsonSchema, jsonSchema.title, { | |
declareExternallyReferenced: true, | |
bannerComment: "", | |
}); | |
definitions.push(`${result};\n`); | |
} | |
const nestableComponents = components | |
.filter((x) => x.is_nestable) | |
.map((x) => interfaceName(x.name)); | |
definitions.push(`export type NestableComponents = ${nestableComponents.join(" | ")};`); | |
const contentTypes = components.filter((x) => x.is_root).map((x) => interfaceName(x.name)); | |
definitions.push(`export type ContentTypes = ${contentTypes.join(" | ")};`); | |
for (const datasource of datasources) { | |
const entries = await Storyblok.get(`spaces/${spaceId}/datasource_entries/`, { | |
datasource_id: datasource.id, | |
}); | |
definitions.push( | |
`export enum ${interfaceName( | |
datasource.name | |
)}DataSource { ${entries.data.datasource_entries | |
.map((x) => `${x.value} = "${x.value}"`) | |
.join(",")}};\n` | |
); | |
} | |
for (const group of componentGroups) { | |
const groupComponents = components | |
.filter((x) => x.component_group_uuid === group.uuid) | |
.map((x) => interfaceName(x.name)); | |
definitions.push( | |
`export type ${interfaceName(group.name)}ComponentGroup = ${groupComponents.join(" | ")}` | |
); | |
} | |
writeFileSync( | |
"./schema/index.ts", | |
prettier.format(definitions.join("\n\n"), { | |
parser: "typescript", | |
}) | |
); | |
} | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface ContentType { | |
name: string; | |
created_at: string; | |
published_at: string; | |
alternates: []; | |
id: number; | |
} | |
export interface Asset { | |
alt: string | null; | |
name: string; | |
focus: null; | |
title: string | null; | |
filename: string; | |
} | |
export interface ContentResult<T> { | |
alternates: []; | |
content: T; | |
created_at: string; | |
default_full_slug: string | null; | |
first_published_at: string; | |
full_slug: string; | |
group_id: string; | |
id: number; | |
is_startpage: boolean; | |
lang: string; | |
meta_data: null; | |
name: string; | |
parent_id: number; | |
path: null; | |
position: number; | |
published_at: string; | |
release_id: null; | |
slug: string; | |
sort_by_date: null; | |
tag_list: string[]; | |
translated_slugs: string[]; | |
uuid: string; | |
} | |
export interface Author { | |
name?: string; | |
picture?: Asset; | |
role?: string; | |
_uid: string; | |
} | |
export interface BlogRoll { | |
variant: "two_rows_featured_post" | "hero_post" | "single_row"; | |
category?: ContentResult<Category>; | |
tags?: ContentResult<Tag>[]; | |
component: "blog_roll"; | |
_uid: string; | |
} | |
export interface Bullets { | |
bullets: string; | |
icon?: IconsDataSource; | |
iconColor?: ColorsDataSource; | |
component: "bullets"; | |
_uid: string; | |
} | |
export interface Category { | |
_uid: string; | |
} | |
export interface Faq { | |
answer?: string; | |
_uid: string; | |
} | |
export interface FaqsList { | |
show_titles?: boolean; | |
title?: string; | |
subtitle?: string; | |
category_uuid?: ContentResult<Category>; | |
tag_uuids?: ContentResult<Tag>[]; | |
component: "faqs_list"; | |
_uid: string; | |
} | |
export interface HeaderAndText { | |
header: string; | |
text: string; | |
component: "header_and_text"; | |
_uid: string; | |
} | |
export interface ImportedHtml { | |
content?: string; | |
component: "imported_html"; | |
_uid: string; | |
} | |
export interface Page { | |
body?: PageItemsComponentGroup[]; | |
_uid: string; | |
} | |
export interface PageHeaderLarge { | |
title: string; | |
subtitle: string; | |
background?: Asset; | |
backgroundColor?: ColorsDataSource; | |
titleColor?: ColorsDataSource; | |
component: "page_header_large"; | |
_uid: string; | |
} | |
export interface PageHeaderSmall { | |
title: string; | |
subtitle?: string; | |
backgroundColor?: ColorsDataSource; | |
component: "page_header_small"; | |
_uid: string; | |
} | |
export interface Partner { | |
logo: Asset; | |
_uid: string; | |
} | |
export interface PartnersList { | |
title?: string; | |
link?: boolean; | |
partners?: ContentResult<Partner>[]; | |
component: "partners_list"; | |
_uid: string; | |
} | |
export interface Post { | |
title?: string; | |
excerpt?: string; | |
cover_image?: Asset; | |
date?: string; | |
author?: ContentResult<Author>; | |
category?: ContentResult<Category>; | |
tags?: ContentResult<Tag>[]; | |
content?: NestableComponents[]; | |
_uid: string; | |
} | |
export interface ProcessCardGrid { | |
preTitle?: string; | |
title?: string; | |
items?: ProcessCardItem[]; | |
component: "process_card_grid"; | |
_uid: string; | |
} | |
export interface ProcessCardItem { | |
title?: string; | |
description?: string; | |
component: "process_card_item"; | |
_uid: string; | |
} | |
export interface ProfileCard { | |
component: "profile_card"; | |
_uid: string; | |
} | |
export interface ProfilesPreview { | |
component: "profiles_preview"; | |
_uid: string; | |
} | |
export interface Quote { | |
title?: string; | |
quote?: string; | |
who?: string; | |
component: "quote"; | |
_uid: string; | |
} | |
export interface SideBySideImageAndContent { | |
image?: Asset; | |
content: (Bullets | HeaderAndText)[]; | |
image_position?: "left" | "right" | ""; | |
new_image?: { | |
filename: string; | |
}; | |
component: "side_by_side_image_and_content"; | |
_uid: string; | |
} | |
export interface Tab { | |
label?: string; | |
content: NestableComponents[]; | |
component: "tab"; | |
_uid: string; | |
} | |
export interface Tabs { | |
items?: Tab[]; | |
style?: "default" | "fullWidth" | "centered"; | |
component: "tabs"; | |
_uid: string; | |
} | |
export interface Tag { | |
_uid: string; | |
} | |
export interface UspGrid { | |
items?: UspItem[]; | |
component: "usp_grid"; | |
_uid: string; | |
} | |
export interface UspItem { | |
title?: string; | |
icon?: IconsDataSource; | |
description?: string; | |
component: "usp_item"; | |
_uid: string; | |
} | |
export type NestableComponents = | |
| BlogRoll | |
| Bullets | |
| FaqsList | |
| HeaderAndText | |
| ImportedHtml | |
| PageHeaderLarge | |
| PageHeaderSmall | |
| PartnersList | |
| ProcessCardGrid | |
| ProcessCardItem | |
| ProfileCard | |
| ProfilesPreview | |
| Quote | |
| SideBySideImageAndContent | |
| Tab | |
| Tabs | |
| UspGrid | |
| UspItem; | |
export type ContentTypes = | |
| Author | |
| Category | |
| Faq | |
| Page | |
| Partner | |
| Post | |
| Tag; | |
export enum IconsDataSource { | |
LockOpen = "LockOpen", | |
PersonOutline = "PersonOutline", | |
Star = "Star", | |
PersonAddOutlined = "PersonAddOutlined", | |
ScheduleOutlined = "ScheduleOutlined", | |
CallOutlined = "CallOutlined", | |
} | |
export enum ColorsDataSource { | |
default = "default", | |
charcoal = "charcoal", | |
honey = "honey", | |
kale = "kale", | |
mint = "mint", | |
salmon = "salmon", | |
spinach = "spinach", | |
} | |
export type BlogItemsComponentGroup = ImportedHtml; | |
export type PageItemsComponentGroup = | |
| BlogRoll | |
| FaqsList | |
| PageHeaderLarge | |
| PageHeaderSmall | |
| PartnersList | |
| ProcessCardGrid | |
| ProcessCardItem | |
| ProfileCard | |
| ProfilesPreview | |
| Quote | |
| SideBySideImageAndContent | |
| Tab | |
| Tabs | |
| UspGrid; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment