/* eslint-disable no-empty */ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable max-len */ import * as React from "react"; import { ReactFramework, StoryContext } from "@storybook/react"; import { ClientApi } from "@storybook/client-api"; import { convertReactTemplate, convertPath, makeArgs, convertTemplate } from "../utils"; import { DEFAULT_CSS_TEMPLATE } from "../templates/default"; import { getEntries, isFunction } from "@daybrush/utils"; // production 모드거나, 실패시 true declare const SKIP_TEST: boolean; // test 여부 declare const EXEC_TEST: boolean; export interface StoryParameter { app: any; text?: string; path?: string; argsTypes?: Record; args?: Record; texts?: Record; play?: (context: StoryContext) => Promise | void; } function makeProxyObject( defaultValue: Record = {}, setter?: (obj: Record, prop: string, value: any) => any, ): Record { const proxy = new Proxy({} as Record, { set(obj, prop, value) { if (!setter?.(obj, prop as string, value)) { obj[prop as string] = value; } return true; }, }); getEntries(defaultValue).forEach(([key, value]) => { proxy[key] = value; }); return proxy; } export function add(storyTitle: string, parameter: StoryParameter) { const { text, argsTypes, args, path, play, app, } = parameter; const previews: any[] = [ { tab: "CSS", template: DEFAULT_CSS_TEMPLATE, copy: true, language: "css", }, ]; // test라면 play가 없는 스토리는 전부 빈공백으로 표시 if (EXEC_TEST && !play) { return function Empty() { return null; }; } // test가 아니라면 code preview 표시 if (!EXEC_TEST && path) { const filePath = path.replace("./stories/", ""); const directory = filePath.replace(/\/([^/]+)$/g, "/"); const fileName = filePath.replace(/\S+\/(\S+)\.\S+$/g, "$1"); const reactCode = require(`!!raw-loader!@/stories/${filePath}`).default; previews.unshift({ tab: "React", template: convertReactTemplate(convertPath(reactCode)), copy: true, // codesandbox: DEFAULT_REACT_CODESANDBOX(["react-moveable"]), language: "tsx", }); // HTML try { const htmlCode = require(`!!raw-loader!@/stories/${directory}script/${fileName}/App.html`).default; previews.push({ tab: "Script", description: "HTML", template: htmlCode, copy: true, language: "html", }); } catch (e) { } // Script try { const scriptCode = require(`!!raw-loader!@/stories/${directory}script/${fileName}/App.js`).default; previews.push({ tab: "Script", description: "JavaScript", template: convertTemplate(scriptCode, /"\$preview_([^"]+)"/g), copy: true, language: "tsx", }); } catch (e) { } // Vue3 try { const vueCode = require(`!!raw-loader!@/stories/${directory}vue3/${fileName}/App.vue`).default; previews.push({ tab: "Vue3", template: convertTemplate(vueCode, /"\$preview_([^"]+)"/g), copy: true, language: "html", }); } catch (e) { } // Vue2 try { const vueCode = require(`!!raw-loader!@/stories/${directory}vue2/${fileName}/App.vue`).default; previews.push({ tab: "Vue2", template: convertTemplate(vueCode, /"\$preview_([^"]+)"/g), copy: true, language: "html", }); } catch (e) { } // Svelte try { const svelteCode = require(`!!raw-loader!@/stories/${directory}svelte/${fileName}/App.svelte`).default; previews.push({ tab: "Svelte", template: convertTemplate(svelteCode, /"\$preview_([^"]+)"/g), copy: true, language: "html", }); } catch (e) { } // Angular html try { const angularCode = require(`!!raw-loader!@/stories/${directory}angular/${fileName}/App.component.html`).default; previews.push({ tab: "Angular", template: angularCode, description: "App.comoponent.html", copy: true, language: "tsx", }); } catch (e) { } // Angular try { const angularCode = require(`!!raw-loader!@/stories/${directory}angular/${fileName}/App.ts`).default; previews.push({ tab: "Angular", template: convertTemplate(angularCode, /"\$preview_([^"]+)"/g), description: "App.comoponent.ts", copy: true, language: "tsx", }); } catch (e) { } // Lit try { const litCode = require(`!!raw-loader!@/stories/${directory}lit/${fileName}/App.ts`).default; previews.push({ tab: "Lit", template: convertTemplate(litCode, /"\$preview_([^"]+)"/g), copy: true, language: "tsx", }); } catch (e) { } } else if (text) { previews.unshift({ tab: "React", template: convertReactTemplate(convertPath(text)), // codesandbox: DEFAULT_REACT_CODESANDBOX(["react-moveable"]), copy: true, language: "tsx", }); } const func = function (this: any, props: any) { const Component = app; return ; }; func.storyName = storyTitle; func.argTypes = argsTypes || {}; func.args = { ...makeArgs(argsTypes || {}), args, }; func.parameters = { preview: previews, }; // dev에서만 표시 if (play) { const clientApi = (window as any).__STORYBOOK_CLIENT_API__ as ClientApi; const facade = clientApi.facade; const csfExports = facade.csfExports; const allStories = facade.stories; if (!SKIP_TEST) { // dev mode or test mode func.play = play; type StoryFunction = ((...args: any[]) => any) & { storyName: string; play: any; args: any; argTypes: any; parameters: any; isSkip?: boolean; }; if (!("__PROXY__" in csfExports)) { facade.csfExports = makeProxyObject({ __PROXY__: () => { }, ...csfExports, }, (obj, prop, value) => { obj[prop] = makeProxyObject(value, (child, childProp, childValue: StoryFunction) => { if (!isFunction(childValue) || !childValue.play || childValue.isSkip) { return; } if (childValue.storyName.match(/Test$/g)) { return; } const copied = childValue.bind({}) as StoryFunction; copied.storyName = `${childValue.storyName} (original)`; copied.argTypes = childValue.argTypes; copied.args = childValue.args; copied.parameters = childValue.parameters; // copied.play = childValue.play; copied.isSkip = true; child[`${childProp}Original`] = copied; // childValue.play = null; childValue.isSkip = true; childValue.storyName = `${childValue.storyName} (test)`; }); return true; }); facade.stories = makeProxyObject(allStories, (obj, prop, value) => { if (value.name?.includes(" (test)")) { const id = `${prop}-original`; obj[`${prop}-original`] = { ...value, id, name: value.name.replace(" (original)", " (test)"), }; } }); } } else if (!EXEC_TEST) { // production mode if (!("__PROXY__" in csfExports)) { facade.csfExports = makeProxyObject({ __PROXY__: () => { }, ...csfExports, }); facade.stories = makeProxyObject(allStories, (obj, prop, value) => { if (value.name?.match(/Test$/g)) { return true; } }); } } } return func; }