diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index 60f7d26d7b..49304602d8 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -32,14 +32,18 @@ const generator = { state.write(' satisfies ', node as unknown as estree.Expression); this[node.reference.type](node.reference, state); }, -} +}; -type SplitCamel<T extends string, YC extends string = '', YN extends readonly string[] = []> = T extends `${infer XH}${infer XR}` +type SplitCamel< + T extends string, + YC extends string = '', + YN extends readonly string[] = [] +> = T extends `${infer XH}${infer XR}` ? XR extends '' ? [...YN, Uncapitalize<`${YC}${XH}`>] : XH extends Uppercase<XH> - ? SplitCamel<XR, Lowercase<XH>, [...YN, YC]> - : SplitCamel<XR, `${YC}${XH}`, YN> + ? SplitCamel<XR, Lowercase<XH>, [...YN, YC]> + : SplitCamel<XR, `${YC}${XH}`, YN> : YN; // @ts-ignore @@ -47,25 +51,36 @@ type SplitKebab<T extends string> = T extends `${infer XH}-${infer XR}` ? [XH, ...SplitKebab<XR>] : [T]; -type ToKebab<T extends readonly string[]> = T extends readonly [infer XO extends string] +type ToKebab<T extends readonly string[]> = T extends readonly [ + infer XO extends string +] ? XO - : T extends readonly [infer XH extends string, ...infer XR extends readonly string[]] - ? `${XH}${XR extends readonly string[] ? `-${ToKebab<XR>}` : ''}` - : ''; + : T extends readonly [ + infer XH extends string, + ...infer XR extends readonly string[] + ] + ? `${XH}${XR extends readonly string[] ? `-${ToKebab<XR>}` : ''}` + : ''; // @ts-ignore -type ToPascal<T extends readonly string[]> = T extends readonly [infer XH extends string, ...infer XR extends readonly string[]] +type ToPascal<T extends readonly string[]> = T extends readonly [ + infer XH extends string, + ...infer XR extends readonly string[] +] ? `${Capitalize<XH>}${ToPascal<XR>}` : ''; -function h<T extends estree.Node>(component: T['type'], props: Omit<T, 'type'>): T { +function h<T extends estree.Node>( + component: T['type'], + props: Omit<T, 'type'> +): T { const type = component.replace(/(?:^|-)([a-z])/g, (_, c) => c.toUpperCase()); return Object.assign(props, { type }) as T; } declare global { namespace JSX { - type Element = never; + type Element = estree.Node; type ElementClass = never; type ElementAttributesProperty = never; type ElementChildrenAttribute = never; @@ -73,7 +88,10 @@ declare global { type IntrinsicClassAttributes<T> = never; type IntrinsicElements = { [T in keyof typeof generator as ToKebab<SplitCamel<Uncapitalize<T>>>]: { - [K in keyof Omit<Parameters<typeof generator[T]>[0], 'type'>]?: Parameters<typeof generator[T]>[0][K]; + [K in keyof Omit< + Parameters<(typeof generator)[T]>[0], + 'type' + >]?: Parameters<(typeof generator)[T]>[0][K]; }; }; } @@ -88,217 +106,391 @@ function toStories(component: string): string { const dir = dirname(component); const literal = ( <literal value={component.slice('src/'.length, -'.vue'.length)} /> - ) as unknown as estree.Literal; + ) as estree.Literal; const identifier = ( - <identifier name={base.slice(0, -'.vue'.length).replace(/[-.]|^(?=\d)/g, '_').replace(/(?<=^[^A-Z_]*$)/, '_')} /> - ) as unknown as estree.Identifier; + <identifier + name={base + .slice(0, -'.vue'.length) + .replace(/[-.]|^(?=\d)/g, '_') + .replace(/(?<=^[^A-Z_]*$)/, '_')} + /> + ) as estree.Identifier; const parameters = ( <object-expression properties={[ - <property - key={<identifier name='layout' />} - value={<literal value={`${dir}/`.startsWith('src/pages/') ? 'fullscreen' : 'centered'} />} - kind={'init' as const} - />, - ...hasMsw + ( + <property + key={(<identifier name='layout' />) as estree.Identifier} + value={ + ( + <literal + value={ + `${dir}/`.startsWith('src/pages/') + ? 'fullscreen' + : 'centered' + } + /> + ) as estree.Literal + } + kind={'init' as const} + /> + ) as estree.Property, + ...(hasMsw ? [ - <property - key={<identifier name='msw' />} - value={<identifier name='msw' />} - kind={'init' as const} - shorthand - />, - ] - : [], + ( + <property + key={(<identifier name='msw' />) as estree.Identifier} + value={(<identifier name='msw' />) as estree.Identifier} + kind={'init' as const} + shorthand + /> + ) as estree.Property, + ] + : []), ]} /> - ); + ) as estree.ObjectExpression; const program = ( <program body={[ - <import-declaration - source={<literal value='@storybook/vue3' />} - specifiers={[ - <import-specifier - local={<identifier name='Meta' />} - imported={<identifier name='Meta' />} - />, - ...hasImplStories - ? [] - : [ - <import-specifier - local={<identifier name='StoryObj' />} - imported={<identifier name='StoryObj' />} - />, - ], - ]} - />, - ...hasMsw + ( + <import-declaration + source={(<literal value='@storybook/vue3' />) as estree.Literal} + specifiers={[ + ( + <import-specifier + local={(<identifier name='Meta' />) as estree.Identifier} + imported={(<identifier name='Meta' />) as estree.Identifier} + /> + ) as estree.ImportSpecifier, + ...(hasImplStories + ? [] + : [ + ( + <import-specifier + local={ + (<identifier name='StoryObj' />) as estree.Identifier + } + imported={ + (<identifier name='StoryObj' />) as estree.Identifier + } + /> + ) as estree.ImportSpecifier, + ]), + ]} + /> + ) as estree.ImportDeclaration, + ...(hasMsw ? [ - <import-declaration - source={<literal value={`./${basename(msw)}`} />} - specifiers={[ - <import-namespace-specifier - local={<identifier name='msw' />} - />, - ]} - />, - ] - : [], - ...hasImplStories + ( + <import-declaration + source={ + (<literal value={`./${basename(msw)}`} />) as estree.Literal + } + specifiers={[ + ( + <import-namespace-specifier + local={(<identifier name='msw' />) as estree.Identifier} + /> + ) as estree.ImportNamespaceSpecifier, + ]} + /> + ) as estree.ImportDeclaration, + ] + : []), + ...(hasImplStories ? [] : [ - <import-declaration - source={<literal value={`./${base}`} />} - specifiers={[ - <import-default-specifier - local={identifier} - />, - ]} - />, - ], - <variable-declaration - kind={'const' as const} - declarations={[ - <variable-declarator - id={<identifier name='meta' />} - init={ - <satisfies-expression - expression={ - <object-expression - properties={[ - <property - key={<identifier name='title' />} - value={literal} - kind={'init' as const} - />, - <property - key={<identifier name='component' />} - value={identifier} - kind={'init' as const} - />, - ]} - /> - } - reference={<identifier name={`Meta<typeof ${identifier.name}>`} />} + ( + <import-declaration + source={(<literal value={`./${base}`} />) as estree.Literal} + specifiers={[ + ( + <import-default-specifier local={identifier} /> + ) as estree.ImportDefaultSpecifier, + ]} /> - } - />, - ]} - />, - ...hasImplStories - ? [ - ] - : [ - <export-named-declaration - declaration={ - <variable-declaration - kind={'const' as const} - declarations={[ - <variable-declarator - id={<identifier name='Default' />} - init={ - <satisfies-expression - expression={ - <object-expression - properties={[ + ) as estree.ImportDeclaration, + ]), + ( + <variable-declaration + kind={'const' as const} + declarations={[ + ( + <variable-declarator + id={(<identifier name='meta' />) as estree.Identifier} + init={ + ( + <satisfies-expression + expression={ + ( + <object-expression + properties={[ + ( <property - key={<identifier name='render' />} - value={ - <function-expression - params={[ - <identifier name='args' />, - <object-pattern - properties={[ - <property - key={<identifier name='argTypes' />} - value={<identifier name='argTypes' />} - kind={'init' as const} - shorthand - />, - ]} - />, - ]} - body={ - <block-statement - body={[ - <return-statement - argument={ - <object-expression - properties={[ - <property - key={<identifier name='components' />} - value={ - <object-expression - properties={[ - <property - key={identifier} - value={identifier} - kind={'init' as const} - shorthand - />, - ]} - /> - } - kind={'init' as const} - />, - <property - key={<identifier name='props' />} - value={ - <call-expression - callee={ - <member-expression - object={<identifier name='Object' />} - property={<identifier name='keys' />} - /> - } - arguments={[ - <identifier name='argTypes' />, - ]} - /> - } - kind={'init' as const} - />, - <property - key={<identifier name='template' />} - value={<literal value={`<${identifier.name} v-bind="$props" />`} />} - kind={'init' as const} - />, - ]} - /> - } - />, - ]} - /> - } - /> + key={ + ( + <identifier name='title' /> + ) as estree.Identifier } - method + value={literal} kind={'init' as const} - />, + /> + ) as estree.Property, + ( <property - key={<identifier name='parameters' />} - value={parameters} + key={ + ( + <identifier name='component' /> + ) as estree.Identifier + } + value={identifier} kind={'init' as const} - />, - ]} - /> - } - reference={<identifier name={`StoryObj<typeof ${identifier.name}>`} />} - /> + /> + ) as estree.Property, + ]} + /> + ) as estree.ObjectExpression } - />, - ]} - /> - } - />, - ], - <export-default-declaration - declaration={<identifier name='meta' />} - />, + reference={ + ( + <identifier + name={`Meta<typeof ${identifier.name}>`} + /> + ) as estree.Identifier + } + /> + ) as estree.Expression + } + /> + ) as estree.VariableDeclarator, + ]} + /> + ) as estree.VariableDeclaration, + ...(hasImplStories + ? [] + : [ + ( + <export-named-declaration + declaration={ + ( + <variable-declaration + kind={'const' as const} + declarations={[ + ( + <variable-declarator + id={ + ( + <identifier name='Default' /> + ) as estree.Identifier + } + init={ + ( + <satisfies-expression + expression={ + ( + <object-expression + properties={[ + ( + <property + key={ + ( + <identifier name='render' /> + ) as estree.Identifier + } + value={ + ( + <function-expression + params={[ + ( + <identifier name='args' /> + ) as estree.Identifier, + ( + <object-pattern + properties={[ + ( + <property + key={ + ( + <identifier name='argTypes' /> + ) as estree.Identifier + } + value={ + ( + <identifier name='argTypes' /> + ) as estree.Identifier + } + kind={ + 'init' as const + } + shorthand + /> + ) as estree.AssignmentProperty, + ]} + /> + ) as estree.ObjectPattern, + ]} + body={ + ( + <block-statement + body={[ + ( + <return-statement + argument={ + ( + <object-expression + properties={[ + ( + <property + key={ + ( + <identifier name='components' /> + ) as estree.Identifier + } + value={ + ( + <object-expression + properties={[ + ( + <property + key={ + identifier + } + value={ + identifier + } + kind={ + 'init' as const + } + shorthand + /> + ) as estree.Property, + ]} + /> + ) as estree.ObjectExpression + } + kind={ + 'init' as const + } + /> + ) as estree.Property, + ( + <property + key={ + ( + <identifier name='props' /> + ) as estree.Identifier + } + value={ + ( + <call-expression + callee={ + ( + <member-expression + object={ + ( + <identifier name='Object' /> + ) as estree.Identifier + } + property={ + ( + <identifier name='keys' /> + ) as estree.Identifier + } + /> + ) as estree.MemberExpression + } + arguments={[ + ( + <identifier name='argTypes' /> + ) as estree.Identifier, + ]} + /> + ) as estree.CallExpression + } + kind={ + 'init' as const + } + /> + ) as estree.Property, + ( + <property + key={ + ( + <identifier name='template' /> + ) as estree.Identifier + } + value={ + ( + <literal + value={`<${identifier.name} v-bind="$props" />`} + /> + ) as estree.Literal + } + kind={ + 'init' as const + } + /> + ) as estree.Property, + ]} + /> + ) as estree.ObjectExpression + } + /> + ) as estree.ReturnStatement, + ]} + /> + ) as estree.BlockStatement + } + /> + ) as estree.FunctionExpression + } + method + kind={'init' as const} + /> + ) as estree.Property, + ( + <property + key={ + ( + <identifier name='parameters' /> + ) as estree.Identifier + } + value={parameters} + kind={'init' as const} + /> + ) as estree.Property, + ]} + /> + ) as estree.ObjectExpression + } + reference={ + ( + <identifier + name={`StoryObj<typeof ${identifier.name}>`} + /> + ) as estree.Identifier + } + /> + ) as estree.Expression + } + /> + ) as estree.VariableDeclarator, + ]} + /> + ) as estree.VariableDeclaration + } + /> + ) as estree.ExportNamedDeclaration, + ]), + ( + <export-default-declaration + declaration={(<identifier name='meta' />) as estree.Identifier} + /> + ) as estree.ExportDefaultDeclaration, ]} /> - ) as unknown as estree.Program; + ) as estree.Program; return format( '/* eslint-disable @typescript-eslint/explicit-function-return-type */\n' + '/* eslint-disable import/no-default-export */\n' + @@ -312,9 +504,12 @@ function toStories(component: string): string { ); } -promisify(glob)('src/{components,pages,ui,widgets}/**/*.vue').then((components) => Promise.all( - components.map((component) => { - const stories = component.replace(/\.vue$/, '.stories.ts'); - return writeFile(stories, toStories(component)); - }) -)); +promisify(glob)('src/{components,pages,ui,widgets}/**/*.vue').then( + (components) => + Promise.all( + components.map((component) => { + const stories = component.replace(/\.vue$/, '.stories.ts'); + return writeFile(stories, toStories(component)); + }) + ) +);