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,14 +32,18 @@ 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>
? SplitCamel<XR, Lowercase<XH>, [...YN, YC]> ? SplitCamel<XR, Lowercase<XH>, [...YN, YC]>
: SplitCamel<XR, `${YC}${XH}`, YN> : SplitCamel<XR, `${YC}${XH}`, YN>
: YN; : YN;
// @ts-ignore // @ts-ignore
@ -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 [
? `${XH}${XR extends readonly string[] ? `-${ToKebab<XR>}` : ''}` infer XH extends string,
: ''; ...infer XR extends readonly string[]
]
? `${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 (
key={<identifier name='layout' />} <property
value={<literal value={`${dir}/`.startsWith('src/pages/') ? 'fullscreen' : 'centered'} />} key={(<identifier name='layout' />) as estree.Identifier}
kind={'init' as const} value={
/>, (
...hasMsw <literal
value={
`${dir}/`.startsWith('src/pages/')
? 'fullscreen'
: 'centered'
}
/>
) as estree.Literal
}
kind={'init' as const}
/>
) as estree.Property,
...(hasMsw
? [ ? [
<property (
key={<identifier name='msw' />} <property
value={<identifier name='msw' />} key={(<identifier name='msw' />) as estree.Identifier}
kind={'init' as const} value={(<identifier name='msw' />) as estree.Identifier}
shorthand kind={'init' as const}
/>, shorthand
] />
: [], ) as estree.Property,
]
: []),
]} ]}
/> />
); ) as estree.ObjectExpression;
const program = ( const program = (
<program <program
body={[ body={[
<import-declaration (
source={<literal value='@storybook/vue3' />} <import-declaration
specifiers={[ source={(<literal value='@storybook/vue3' />) as estree.Literal}
<import-specifier specifiers={[
local={<identifier name='Meta' />} (
imported={<identifier name='Meta' />} <import-specifier
/>, local={(<identifier name='Meta' />) as estree.Identifier}
...hasImplStories imported={(<identifier name='Meta' />) as estree.Identifier}
? [] />
: [ ) as estree.ImportSpecifier,
<import-specifier ...(hasImplStories
local={<identifier name='StoryObj' />} ? []
imported={<identifier name='StoryObj' />} : [
/>, (
], <import-specifier
]} local={
/>, (<identifier name='StoryObj' />) as estree.Identifier
...hasMsw }
imported={
(<identifier name='StoryObj' />) as estree.Identifier
}
/>
) as estree.ImportSpecifier,
]),
]}
/>
) as estree.ImportDeclaration,
...(hasMsw
? [ ? [
<import-declaration (
source={<literal value={`./${basename(msw)}`} />} <import-declaration
specifiers={[ source={
<import-namespace-specifier (<literal value={`./${basename(msw)}`} />) as estree.Literal
local={<identifier name='msw' />} }
/>, specifiers={[
]} (
/>, <import-namespace-specifier
] local={(<identifier name='msw' />) as estree.Identifier}
: [], />
...hasImplStories ) as estree.ImportNamespaceSpecifier,
]}
/>
) as estree.ImportDeclaration,
]
: []),
...(hasImplStories
? [] ? []
: [ : [
<import-declaration (
source={<literal value={`./${base}`} />} <import-declaration
specifiers={[ source={(<literal value={`./${base}`} />) as estree.Literal}
<import-default-specifier specifiers={[
local={identifier} (
/>, <import-default-specifier local={identifier} />
]} ) as estree.ImportDefaultSpecifier,
/>, ]}
],
<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}>`} />}
/> />
} ) as estree.ImportDeclaration,
/>, ]),
]} (
/>, <variable-declaration
...hasImplStories kind={'const' as const}
? [ declarations={[
] (
: [ <variable-declarator
<export-named-declaration id={(<identifier name='meta' />) as estree.Identifier}
declaration={ init={
<variable-declaration (
kind={'const' as const} <satisfies-expression
declarations={[ expression={
<variable-declarator (
id={<identifier name='Default' />} <object-expression
init={ properties={[
<satisfies-expression (
expression={
<object-expression
properties={[
<property <property
key={<identifier name='render' />} key={
value={ (
<function-expression <identifier name='title' />
params={[ ) as estree.Identifier
<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}
/>,
]}
/>
}
/>,
]}
/>
}
/>
} }
method value={literal}
kind={'init' as const} kind={'init' as const}
/>, />
) as estree.Property,
(
<property <property
key={<identifier name='parameters' />} key={
value={parameters} (
<identifier name='component' />
) as estree.Identifier
}
value={identifier}
kind={'init' as const} kind={'init' as const}
/>, />
]} ) as estree.Property,
/> ]}
} />
reference={<identifier name={`StoryObj<typeof ${identifier.name}>`} />} ) as estree.ObjectExpression
/>
} }
/>, reference={
]} (
/> <identifier
} name={`Meta<typeof ${identifier.name}>`}
/>, />
], ) as estree.Identifier
<export-default-declaration }
declaration={<identifier name='meta' />} />
/>, ) 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( 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.map((component) => { (components) =>
const stories = component.replace(/\.vue$/, '.stories.ts'); Promise.all(
return writeFile(stories, toStories(component)); components.map((component) => {
}) const stories = component.replace(/\.vue$/, '.stories.ts');
)); return writeFile(stories, toStories(component));
})
)
);