style: typing

This commit is contained in:
Acid Chicken (硫酸鶏) 2023-03-22 00:48:11 +09:00
parent 1d0ca7eecf
commit bf5fff879f
No known key found for this signature in database
GPG key ID: 3E87B98A3F6BAB99

View file

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