shopify-accelerate
Version:
Shopify Theme development with full Typescript Support
340 lines (316 loc) • 11 kB
text/typescript
import path from "path";
import { ShopifySection, ShopifySettingsInput } from "../../@types/shopify";
import { config } from "../../shopify-accelerate";
import { capitalize } from "../utils/capitalize";
import { writeCompareFile } from "../utils/fs";
import { toPascalCase } from "../utils/to-pascal-case";
export const generateSectionsTypes = () => {
const { folders, sources } = config;
const sections = sources.sectionSchemas;
const sectionTypesPath = path.join(folders.types, "sections.ts");
const imports = getImports(sections);
let sectionUnionType = "export type Sections =";
let typeContent = "";
for (const key in sections) {
const schema = sections[key] as ShopifySection;
typeContent += `${sectionToTypes(schema, key)}\n`;
sectionUnionType += `\n | ${capitalize(key)}Section`;
}
if (!typeContent) return;
const finalContent = `${imports + typeContent + sectionUnionType};\n`;
writeCompareFile(sectionTypesPath, finalContent);
};
export const getImports = (sections: { [T: string]: ShopifySection }) => {
const localTypes = [];
let themeBlocks = false;
const analyseSetting = (setting) => {
if (setting.type === "richtext") {
if (localTypes.includes("_BlockTag")) return;
localTypes.push("_BlockTag");
}
if (setting.type === "article") {
if (localTypes.includes("_Article_liquid")) return;
localTypes.push("_Article_liquid");
}
if (setting.type === "blog") {
if (localTypes.includes("_Blog_liquid")) return;
localTypes.push("_Blog_liquid");
}
if (setting.type === "collection" && !setting.id.includes("__handle_only")) {
if (localTypes.includes("_Collection_liquid")) return;
localTypes.push("_Collection_liquid");
}
if (setting.type === "collection_list" && !setting.id.includes("__handle_only")) {
if (localTypes.includes("_Collection_liquid")) return;
localTypes.push("_Collection_liquid");
}
if (setting.type === "color") {
if (localTypes.includes("_Color_liquid")) return;
localTypes.push("_Color_liquid");
}
if (setting.type === "image_picker") {
if (localTypes.includes("_Image_liquid")) return;
localTypes.push("_Image_liquid");
}
if (setting.type === "font_picker") {
if (localTypes.includes("_Font_liquid")) return;
localTypes.push("_Font_liquid");
}
if (setting.type === "font_picker") {
if (localTypes.includes("_Font_options")) return;
localTypes.push("_Font_options");
}
if (setting.type === "link_list") {
if (localTypes.includes("_Linklist_liquid")) return;
localTypes.push("_Linklist_liquid");
}
if (setting.type === "page") {
if (localTypes.includes("_Page_liquid")) return;
localTypes.push("_Page_liquid");
}
if (setting.type === "product" && !setting.id.includes("__handle_only")) {
if (localTypes.includes("_Product_liquid")) return;
localTypes.push("_Product_liquid");
}
if (setting.type === "product_list" && !setting.id.includes("__handle_only")) {
if (localTypes.includes("_Product_liquid")) return;
localTypes.push("_Product_liquid");
}
if (setting.type === "video") {
if (localTypes.includes("_Video_liquid")) return;
localTypes.push("_Video_liquid");
}
};
for (const key in sections) {
const schema = sections[key];
schema.settings?.forEach(analyseSetting, localTypes);
if (schema.blocks?.some((block) => block.type === "@theme")) {
themeBlocks = true;
}
schema.blocks?.forEach((block) => {
block?.settings?.forEach(analyseSetting, localTypes);
});
}
const returnArr = [];
if (localTypes.length) {
returnArr.push(`import { ${localTypes.join(", ")} } from "./shopify";`);
}
if (themeBlocks) {
returnArr.push(`import { ThemeBlocks } from "./blocks";`);
}
returnArr.push(``);
return returnArr.join("\n");
};
export const sectionToTypes = (section, key) => {
const filename = section.folder;
const arr = [];
const settings: ShopifySettingsInput[] = section.settings
?.filter((s) => s.type !== "header" && s.type !== "paragraph")
.sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0));
const hasNonThemeBlocks = section.blocks?.filter((b) => b.type !== "@app" && b.type !== "@theme")
?.length;
const hasThemeBlocks = section.blocks?.some((block) => block.type === "@theme");
arr.push(`export type ${capitalize(key)}Section = {`);
if (hasNonThemeBlocks && !hasThemeBlocks) {
arr.push(` blocks: ${capitalize(key)}Blocks[];`);
}
if (!hasNonThemeBlocks && hasThemeBlocks) {
arr.push(` blocks: ThemeBlocks[];`);
}
if (hasNonThemeBlocks && hasThemeBlocks) {
arr.push(` blocks: (${capitalize(key)}Blocks | ThemeBlocks)[];`);
}
if (!hasNonThemeBlocks && !hasThemeBlocks) {
arr.push(` blocks${config.headless ? "?" : ""}: never[];`);
}
arr.push(` id${config.headless ? "?" : ""}: string;`);
arr.push(` disabled?: boolean;`);
arr.push(` settings${!settings?.length ? "?" : ""}: {`);
if (settings?.length) {
arr.push(
settings
.map(
(setting) =>
` /** Input type: ${setting.type} */\n ` +
`${/[^\w_]/gi.test(setting.id) ? `"${setting.id}"` : `${setting.id}`}${getSettingsType(
setting
)};`
)
.sort((a, b) => {
const aX = a.split("\n")[1];
const bX = b.split("\n")[1];
if (aX.includes("?") && !bX.includes("?")) {
return 1;
} else if (!aX.includes("?") && bX.includes("?")) {
return -1;
} else if (aX > bX) {
return 1;
} else if (aX < bX) {
return -1;
} else {
return 0;
}
})
.join("\n")
);
}
arr.push(` };`);
arr.push(` type: "${filename}";`);
arr.push(`};`);
if (section.blocks?.length) {
section.blocks?.forEach((block) => {
const blockSettings: ShopifySettingsInput[] = block?.settings
?.filter((s) => s.type !== "header" && s.type !== "paragraph")
.sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0));
arr.push("");
arr.push(
`export type ${capitalize(key)}Blocks${toPascalCase(block.type.replace("@", ""))} = {`
);
arr.push(` id${config.headless ? "?" : ""}: string;`);
if (blockSettings?.length) {
arr.push(` settings: {`);
arr.push(
blockSettings
.map(
(setting) =>
` /** Input type: ${setting.type} */\n ` +
`${
/[^\w_]/gi.test(setting.id) ? `"${setting.id}"` : `${setting.id}`
}${getSettingsType(setting)};`
)
.sort((a, b) => {
const aX = a.split("\n")[1];
const bX = b.split("\n")[1];
if (aX.includes("?") && !bX.includes("?")) {
return 1;
} else if (!aX.includes("?") && bX.includes("?")) {
return -1;
} else if (aX > bX) {
return 1;
} else if (aX < bX) {
return -1;
} else {
return 0;
}
})
.join("\n")
);
arr.push(` };`);
}
arr.push(` type: "${block.type}";`);
arr.push(`};`);
});
}
if (section.blocks?.length && section.blocks?.length === 1) {
arr.push("");
arr.push(
`export type ${capitalize(key)}Blocks = ${capitalize(key)}Blocks${toPascalCase(
section.blocks[0].type.replace("@", "")
)};`
);
}
if (section.blocks?.length && section.blocks?.length > 1) {
arr.push("");
arr.push(`export type ${capitalize(key)}Blocks =`);
section.blocks?.forEach((block, i) => {
if (section.blocks?.length - 1 === i) {
arr.push(` | ${capitalize(key)}Blocks${toPascalCase(block.type.replace("@", ""))};`);
} else {
arr.push(` | ${capitalize(key)}Blocks${toPascalCase(block.type.replace("@", ""))}`);
}
});
}
arr.push("");
return arr.join("\n");
};
export const getSettingsType = (setting: ShopifySettingsInput) => {
switch (setting.type) {
case "article":
return "?: _Article_liquid | string";
case "checkbox":
return ": boolean";
case "number":
return "?: number";
case "radio":
return `: ${setting.options.map(({ value }) => `"${value}"`).join(" | ")}`;
case "range":
return ": number";
case "select":
return `: ${setting.options.map(({ value }) => `"${value}"`).join(" | ")}`;
case "text":
return "?: string";
case "textarea":
return "?: string";
case "blog":
return "?: _Blog_liquid | string";
case "collection": {
if (setting.id.includes("__handle_only")) {
return "?: string";
}
return "?: _Collection_liquid";
}
case "collection_list": {
if (setting.id.includes("__handle_only")) {
return "?: string[]";
}
return "?: _Collection_liquid[]";
}
case "color":
return config.headless ? "?: string" : "?: _Color_liquid | string";
case "color_background":
return "?: string";
case "font_picker":
return config.headless ? ": string" : ": _Font_liquid | _Font_options";
case "html":
return "?: string";
case "image_picker":
return config.headless
? "?: { src?: string | null, alt?: string | null }"
: "?: _Image_liquid | string";
case "link_list":
return "?: _Linklist_liquid";
case "liquid":
return "?: string";
case "page":
return "?: _Page_liquid | string";
case "product": {
if (setting.id.includes("__handle_only") || config.headless) {
return "?: string";
}
return "?: _Product_liquid";
}
case "product_list": {
if (setting.id.includes("__handle_only") || config.headless) {
return "?: string[]";
}
return "?: _Product_liquid[]";
}
case "color_scheme_group":
return `?: {\n [T:string]: {${setting.definition
.map((option) => {
if ("id" in option) {
return `\n ${option.id}: string;`;
}
return "";
})
.join("")}\n }\n }`;
case "richtext":
return "?: `<${_BlockTag}${string}</${_BlockTag}>`";
case "inline_richtext":
return "?: string";
case "url":
return "?: string";
case "video":
return config.headless
? "?: { src?: string | null, mimeType?: string | null, alt?: string | null }"
: "?: _Video_liquid";
case "video_url":
return "?: `${string}youtube${string}` | `${string}vimeo${string}`";
case "font":
return "?: string";
case "color_scheme":
return "?: string";
case "text_alignment":
return `: "left" | "center" | "right"`;
}
};