From 15123e48eee644a4a6450f6765c622e824e32f7d Mon Sep 17 00:00:00 2001 From: Adhikari Date: Sun, 4 Feb 2024 07:43:00 +0530 Subject: [PATCH 1/5] Angular support for aut-story-generator plugin --- README.md | 49 ++++++- packages/auto-story-generator/src/index.ts | 22 +++- .../presets/angular/genAngularStoryFile.ts | 124 ++++++++++++++++++ .../presets/angular/getAngularPropTypes.ts | 120 +++++++++++++++++ 4 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts create mode 100644 packages/auto-story-generator/src/presets/angular/getAngularPropTypes.ts diff --git a/README.md b/README.md index 1051792..0c34730 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![asg-thumbnail](https://github.com/takuma-ru/auto-story-generator/assets/49429291/dca65c2c-3384-45c0-a761-e85276cb2393) ## Description -Automatic real-time story file generation from React, Vue, and Lit component files +Automatic real-time story file generation from React, Vue, Lit and Angular component files ## Getting Started ### 1. Install the package @@ -37,6 +37,51 @@ const config: StorybookConfig = { export default config; ``` +> [!WARNING] +> Don't run this plugin(Angular part) on your apps right away. Test it on a sample Application or create a new Angular app. + +> [!NOTE] +> Angular feature is a WIP. Only a basic story can be created at this point of time. Modify the created stories as required. Will try to improve story creation. + +For `Angular` [a link] (https://storybook.js.org/docs/builders/webpack#extending-storybooks-webpack-config) + +```ts +import type { StorybookConfig } from "@storybook/angular"; + +import autoStoryGenerator from "@takuma-ru/auto-story-generator"; + +const customConfig = { + webpackFinal: async (config) => { + let plugin = autoStoryGenerator.webpack({ + preset: "angular", + imports: ["**/src/**/*.component.ts"], + }); + config.plugins.push(plugin); + return config; + } +} + +const primeConfig: StorybookConfig = { + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: [ + "@storybook/addon-links", + "@storybook/addon-essentials", + "@storybook/addon-interactions", + ], + framework: { + name: "@storybook/angular", + options: {}, + }, + // spread the object here instead of mergeConfig(avaialable for vite) + ...customConfig, + docs: { + autodocs: "tag", + }, +}; + +export default primeConfig; +``` + ## Supported Frameworks > ✅: Supported > 🚧: Work in progress @@ -48,5 +93,5 @@ export default config; | React | ✅ | | Vue | 🚧 | | Lit | ✅ | -| Angular | ❌ | +| Angular | 🚧 | | Svelte | 📝 | \ No newline at end of file diff --git a/packages/auto-story-generator/src/index.ts b/packages/auto-story-generator/src/index.ts index d22d972..e8fcd92 100644 --- a/packages/auto-story-generator/src/index.ts +++ b/packages/auto-story-generator/src/index.ts @@ -6,9 +6,10 @@ import { createUnplugin } from "unplugin"; import { genLitStoryFile } from "~/src/presets/lit/genLitStoryFile"; import { genReactStoryFile } from "~/src/presets/react/genReactStoryFile"; +import { genAngularStoryFile } from "~/src/presets/angular/genAngularStoryFile"; export type AsgOptions = { - preset: "lit" | "react" | "vue" | "custom"; + preset: "lit" | "react" | "vue" | "custom" | "angular"; /** * @default undefined * @@ -41,7 +42,10 @@ const unplugin = createUnplugin((options: AsgOptions) => { if (!isMatches.includes(true)) return; const projectRootDir = process.cwd(); - const fileName = file.split("/").pop(); + + // split for either forward or backward slash + const fileName = file.split(/[\\\/]/).pop(); + const fileType = fileName?.split(".").slice(1).join("."); const componentName = fileName?.replace(`.${fileType}`, "") === "index" @@ -90,6 +94,20 @@ const unplugin = createUnplugin((options: AsgOptions) => { break; } + case "angular": { + await genAngularStoryFile({ + componentName, + fileName: fileName, + path: file, + type: `.${fileType}`, + relativeSourceFilePath, + sourceFile, + prettierConfigPath: options.prettierConfigPath, + }); + + break; + } + case "vue": { consola.error("Not yet supported."); break; diff --git a/packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts b/packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts new file mode 100644 index 0000000..ad61d9b --- /dev/null +++ b/packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts @@ -0,0 +1,124 @@ +import { consola } from "consola"; +import { pascalCase } from "scule"; +import { getAngularPropTypes } from "~/src/presets/angular/getAngularPropTypes"; + +import { GenStoryFileOptions } from "~/src/types/GenStoryFileType"; +import { genStoryFile } from "~/src/utils/genStoryFile"; + +export const genAngularStoryFile = async ({ + componentName, + fileName, + path, + type, + relativeSourceFilePath, + sourceFile, + prettierConfigPath, +}: GenStoryFileOptions["fileOptions"]) => { + // const { propTypes } = + // getAngularPropTypes({ + // sourceFile, + // componentName, + // }); + const pascalComponentName = pascalCase(componentName + 'Component'); + // if (!propTypes) return consola.error("Could not find argTypes"); + + // Angular doesn't need file with extension + let file = fileName.split('.'); + file.pop(); + + const initialCode = ` +import type { Meta, StoryObj } from "@storybook/angular"; + +import { ${pascalComponentName} } from "./${file.join('.')}"; + +const meta: Meta<${pascalComponentName}> = { + title: "components/${pascalComponentName}", + component: ${pascalComponentName}, + tags: ["autodocs"], + render: (args: ${pascalComponentName}) => ({ + props: { + ...args + } + }) +}; + +export default meta; +type Story = StoryObj<${pascalComponentName}>; + +export const Primary: Story = {}; +`; + + const componentCode = `${pascalComponentName}`; + + // const args: GenStoryFileOptions["generateOptions"]["meta"]["args"] = {}; + + // propTypes.forEach((prop) => { + // if (prop.isOptional) { + // return (args[prop.name] = "undefined"); + // } + + // let value: string | boolean | undefined = + // prop.value.length > 0 ? prop.value[0] : "undefined"; + + // if (prop.type.includes("boolean")) { + // value = true; + // } + + // args[prop.name] = value; + // }); + + // const argTypes: GenStoryFileOptions["generateOptions"]["meta"]["argTypes"] = + // {}; + + // propTypes.forEach((prop) => { + // if (prop.type[0] === "boolean") { + // return (argTypes[prop.name] = { + // control: "boolean", + // }); + // } + + // if (prop.type[0] === "object") { + // return (argTypes[prop.name] = { + // control: "object", + // }); + // } + + // if (prop.value.length > 1) { + // return (argTypes[prop.name] = { + // control: "select", + // options: prop.value, + // }); + // } else { + // if (prop.type[0] === "string") { + // return (argTypes[prop.name] = { + // control: "text", + // }); + // } + + // if (prop.type[0] === "number") { + // return (argTypes[prop.name] = { + // control: "number", + // }); + // } + // } + // }); + + genStoryFile({ + fileOptions: { + componentName, + fileName, + path, + type, + relativeSourceFilePath, + sourceFile, + prettierConfigPath, + }, + generateOptions: { + fileType: ".stories.ts", + initialCode, + meta: { + // component: componentCode, + }, + }, + }); +}; diff --git a/packages/auto-story-generator/src/presets/angular/getAngularPropTypes.ts b/packages/auto-story-generator/src/presets/angular/getAngularPropTypes.ts new file mode 100644 index 0000000..be785aa --- /dev/null +++ b/packages/auto-story-generator/src/presets/angular/getAngularPropTypes.ts @@ -0,0 +1,120 @@ +import { pascalCase } from "scule"; +import { TypeFlags, ts, Symbol, SourceFile } from "ts-morph"; + +import { + GenReactPropTypesOptions, + GenReactPropTypesReturn, +} from "~/src/types/GenPropTypeType"; + +const getTypeFlagsName = (flags: TypeFlags) => { + // Get all the keys of TypeFlags + const keys = Object.keys(TypeFlags) as (keyof typeof TypeFlags)[]; + + // Filter the keys where the flag is set + const setFlags = keys.find((key) => flags === TypeFlags[key]); + + return setFlags || "err"; +}; + +// Didn't understnd the Props yet, will add this after proper understanding is reached. +export const getAngularPropTypes = ({sourceFile, componentName}: { sourceFile: SourceFile; componentName: string; })=> { + const pascalComponentName = pascalCase(componentName); +} + +// export const getAngularPropTypes = ({ +// sourceFile, +// componentName, +// }: GenReactPropTypesOptions): { +// propsPattern?: "component-props" | "props" | "inline"; +// propTypes: GenReactPropTypesReturn; +// } => { +// let propsPattern: "component-props" | "props" | "inline" = "component-props"; + + +// const props = +// propsType?.getType() || +// propsInterface?.getType() || +// propsOnlyType?.getType() || +// propsOnlyInterface?.getType() || +// propsInline?.getParameters()[0].getType(); + +// if (!props) { +// return { +// propTypes: undefined, +// }; +// } + +// // eslint-disable-next-line @typescript-eslint/ban-types +// let propsProperties: Symbol[] = []; +// const isPropsIntersection = props.isIntersection(); +// if (isPropsIntersection) { +// propsProperties = []; + +// const intersectionTypes = props.getIntersectionTypes(); + +// intersectionTypes.forEach((intersectionType) => { +// const intersectionTypeText = intersectionType.getText(); + +// if (intersectionTypeText.includes("HTMLAttributes")) { +// return; +// } + +// return propsProperties.push(...intersectionType.getProperties()); +// }); +// } else { +// propsProperties = props.getProperties(); +// } + +// if (propsOnlyType || propsOnlyInterface) { +// propsPattern = "props"; +// } + +// if (propsInline) { +// propsPattern = "inline"; +// } + +// const propTypes = propsProperties.map((prop) => { +// const propName = prop.getName(); +// const propType = prop.getValueDeclaration()?.getType(); + +// if (!propType) { +// return { +// name: propName, +// type: ["err"], +// isOptional: prop.isOptional(), +// value: [], +// }; +// } + +// if (propType.isUnion() && !propType.isBoolean()) { +// const unionTypes = propType.getUnionTypes(); + +// const type = Array.from( +// new Set( +// unionTypes.map((union) => +// getTypeFlagsName(union.getFlags().valueOf()), +// ), +// ), +// ); + +// return { +// name: propName, +// type, +// isOptional: prop.isOptional(), +// value: unionTypes.map((union) => union.getText().replace(/"/g, "")), +// }; +// } + +// return { +// name: propName, +// type: [prop.getValueDeclaration()!.getType().getText()], +// isOptional: prop.isOptional(), +// value: [], +// }; +// }); + +// return { +// propsPattern, +// propTypes, +// }; +// }; From 971872b921cfa64cfc7ba79927ef51261acc7704 Mon Sep 17 00:00:00 2001 From: Adhikari Date: Sun, 4 Feb 2024 10:52:45 +0530 Subject: [PATCH 2/5] modified README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c34730..c1ed0bc 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ export default config; > [!NOTE] > Angular feature is a WIP. Only a basic story can be created at this point of time. Modify the created stories as required. Will try to improve story creation. -For `Angular` [a link] (https://storybook.js.org/docs/builders/webpack#extending-storybooks-webpack-config) +For `Angular` [Webpack custom config](https://storybook.js.org/docs/builders/webpack#working-with-webpack-plugins) ```ts import type { StorybookConfig } from "@storybook/angular"; @@ -81,6 +81,10 @@ const primeConfig: StorybookConfig = { export default primeConfig; ``` +> [!NOTE] +> In Angular, for first time story creation, a run tme error occurs, can ignore it. + + ## Supported Frameworks > ✅: Supported From 76d3efa7642b65c93d95842f8e0fa0dc9a4b8613 Mon Sep 17 00:00:00 2001 From: Adhikari Date: Sun, 4 Feb 2024 11:13:54 +0530 Subject: [PATCH 3/5] modified README.md & package.json --- README.md | 4 ++++ package.json | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1ed0bc..3dc62da 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ + +> [!NOTE] +> This is a fork of @takuma-ru/auto-story-generator(which doesn't support Angular). This is meant to extend @takuma-ru/auto-story-generator and support Angular. + # @takuma-ru/auto-story-generator ![asg-thumbnail](https://github.com/takuma-ru/auto-story-generator/assets/49429291/dca65c2c-3384-45c0-a761-e85276cb2393) diff --git a/package.json b/package.json index 29fc946..473e5b9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "@takuma-ru/auto-story-generator", + "name": "auto-angular-story-generator", "version": "0.0.0", - "description": "", + "description": "Extends @takuma-ru/auto-story-generator and supports creation of stories for Angular Componenets too", "scripts": { "asg": "pnpm -F \"@takuma-ru/auto-story-generator\"", "doc": "pnpm -F \"docs\"", @@ -9,7 +9,7 @@ "d:next": "pnpm -F \"next\"", "d:lit": "pnpm -F \"lit\"" }, - "author": "takuma-ru (https://github.com/takuma-ru/)", + "author": "gadhikari(https://github.com/GeetaKrishna65/auto-angular-story-generator)", "license": "ISC", "packageManager": "pnpm@8.14.1", "engines": { From b4a49b750127278e30c5e176d50c10e00cdadb81 Mon Sep 17 00:00:00 2001 From: Adhikari Date: Sun, 4 Feb 2024 11:21:13 +0530 Subject: [PATCH 4/5] modified README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3dc62da..a455a46 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ > [!NOTE] -> This is a fork of @takuma-ru/auto-story-generator(which doesn't support Angular). This is meant to extend @takuma-ru/auto-story-generator and support Angular. +> This is a fork of [@takuma-ru/auto-story-generator](https://github.com/takuma-ru/auto-story-generator/assets/49429291/dca65c2c-3384-45c0-a761-e85276cb2393)(which doesn't support Angular). This is meant to extend @takuma-ru/auto-story-generator and support Angular. -# @takuma-ru/auto-story-generator +# auto-angular-story-generator ![asg-thumbnail](https://github.com/takuma-ru/auto-story-generator/assets/49429291/dca65c2c-3384-45c0-a761-e85276cb2393) @@ -12,7 +12,7 @@ Automatic real-time story file generation from React, Vue, Lit and Angular compo ## Getting Started ### 1. Install the package ```bash -npm i @takuma-ru/auto-story-generator +npm i auto-angular-story-generator ``` ### 2. Add config From dca288c27d322c85c237a9e4d0c6ada33a3ad2ca Mon Sep 17 00:00:00 2001 From: Adhikari Date: Tue, 3 Dec 2024 16:48:34 +0530 Subject: [PATCH 5/5] path for throw error --- packages/auto-story-generator/src/utils/throwError.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auto-story-generator/src/utils/throwError.ts b/packages/auto-story-generator/src/utils/throwError.ts index 6d03793..21735eb 100644 --- a/packages/auto-story-generator/src/utils/throwError.ts +++ b/packages/auto-story-generator/src/utils/throwError.ts @@ -1,6 +1,6 @@ import { consola } from "consola"; -import { errorDefinition } from "../constants/error"; +import { errorDefinition } from "~/src/constants/error"; type ThrowErrType = { [K in keyof typeof errorDefinition]: (typeof errorDefinition)[K]["isCustomDetail"] extends true