Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular feature #15

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# @takuma-ru/auto-story-generator

> [!NOTE]
> 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.

# auto-angular-story-generator

![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
```bash
npm i @takuma-ru/auto-story-generator
npm i auto-angular-story-generator
```

### 2. Add config
Expand Down Expand Up @@ -37,6 +41,55 @@ 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` [Webpack custom config](https://storybook.js.org/docs/builders/webpack#working-with-webpack-plugins)

```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;
```
> [!NOTE]
> In Angular, for first time story creation, a run tme error occurs, can ignore it.



## Supported Frameworks
> ✅: Supported
> 🚧: Work in progress
Expand All @@ -48,5 +101,5 @@ export default config;
| React | ✅ |
| Vue | 🚧 |
| Lit | ✅ |
| Angular | |
| Angular | 🚧 |
| Svelte | 📝 |
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"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\"",
"d:react": "pnpm -F \"react\"",
"d:next": "pnpm -F \"next\"",
"d:lit": "pnpm -F \"lit\""
},
"author": "takuma-ru <[email protected]> (https://github.com/takuma-ru/)",
"author": "gadhikari(https://github.com/GeetaKrishna65/auto-angular-story-generator)",
"license": "ISC",
"packageManager": "[email protected]",
"engines": {
Expand Down
22 changes: 20 additions & 2 deletions packages/auto-story-generator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { consola } from "consola";

Check warning on line 1 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console

Check warning on line 1 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console
import { loadFile } from "magicast";
import { minimatch } from "minimatch";
import { Project } from "ts-morph";
Expand All @@ -6,9 +6,10 @@

import { genLitStoryFile } from "~/src/presets/lit/genLitStoryFile";
import { genReactStoryFile } from "~/src/presets/react/genReactStoryFile";
import { genAngularStoryFile } from "~/src/presets/angular/genAngularStoryFile";

Check failure on line 9 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

`~/src/presets/angular/genAngularStoryFile` import should occur before import of `~/src/presets/lit/genLitStoryFile`

export type AsgOptions = {
preset: "lit" | "react" | "vue" | "custom";
preset: "lit" | "react" | "vue" | "custom" | "angular";
/**
* @default undefined
*
Expand Down Expand Up @@ -41,7 +42,10 @@
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();

Check failure on line 47 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Unnecessary escape character: \/

const fileType = fileName?.split(".").slice(1).join(".");
const componentName =
fileName?.replace(`.${fileType}`, "") === "index"
Expand All @@ -50,7 +54,7 @@
const relativeSourceFilePath = file.replace(projectRootDir, "");

if (!componentName || !fileName) {
return consola.error("Could not find component name");

Check warning on line 57 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console
}

// consola.info(`${componentName} component has been changed`);
Expand All @@ -59,7 +63,7 @@
const project = new Project();
const sourceFile = project.createSourceFile(fileName || "", mod.$code);

consola.start(`${componentName} Story file is being generated ....`);

Check warning on line 66 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console

switch (options.preset) {
case "lit": {
Expand Down Expand Up @@ -90,18 +94,32 @@
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.");

Check warning on line 112 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console
break;
}

case "custom": {
consola.error("Not yet supported.");

Check warning on line 117 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console
break;
}

default: {
consola.error(

Check warning on line 122 in packages/auto-story-generator/src/index.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console
`Preset ${options.preset} is not supported. Please use one of the following: lit, react, vue, custom`,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { consola } from "consola";

Check warning on line 1 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console

Check warning on line 1 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Logs are output on the console

Check failure on line 1 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

'consola' is defined but never used
import { pascalCase } from "scule";

Check failure on line 2 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

There should be at least one empty line between import groups
import { getAngularPropTypes } from "~/src/presets/angular/getAngularPropTypes";

Check failure on line 3 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

There should be no empty line within import group

Check failure on line 3 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

'getAngularPropTypes' is defined but never used

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');

Check failure on line 22 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Strings must use doublequote
// if (!propTypes) return consola.error("Could not find argTypes");

// Angular doesn't need file with extension
let file = fileName.split('.');

Check failure on line 26 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

'file' is never reassigned. Use 'const' instead

Check failure on line 26 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Strings must use doublequote
file.pop();

const initialCode = `
import type { Meta, StoryObj } from "@storybook/angular";

import { ${pascalComponentName} } from "./${file.join('.')}";

Check failure on line 32 in packages/auto-story-generator/src/presets/angular/genAngularStoryFile.ts

View workflow job for this annotation

GitHub Actions / eslint-check

Strings must use doublequote

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,
},
},
});
};
Original file line number Diff line number Diff line change
@@ -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,
// };
// };
Loading