build(#10336): make .storybook as an individual TypeScript project

This commit is contained in:
Acid Chicken (硫酸鶏) 2023-03-21 13:01:40 +09:00
parent bf527cd6e7
commit cc3fbefc83
No known key found for this signature in database
GPG key ID: 3E87B98A3F6BAB99
4 changed files with 95 additions and 24 deletions

View file

@ -1,8 +1,9 @@
# (cd path/to/frontend; pnpm tsc --jsx react --jsxFactory h .storybook/generate.tsx && node .storybook/generate.js) # (cd path/to/frontend; pnpm tsc -p .storybook)
# (cd path/to/frontend; node .storybook/generate.js)
/generate.js /generate.js
# (cd path/to/frontend; pnpm tsc .storybook/preload-locale.ts && node .storybook/preload-locale.js) # (cd path/to/frontend; node .storybook/preload-locale.js)
/preload-locale.js /preload-locale.js
/locale.ts /locale.ts
# (cd path/to/frontend; pnpm tsc .storybook/preload-theme.ts && node .storybook/preload-theme.js) # (cd path/to/frontend; node .storybook/preload-theme.js)
/preload-theme.js /preload-theme.js
/themes.ts /themes.ts

View file

@ -4,7 +4,7 @@ import { basename, dirname } from 'node:path/posix';
import { promisify } from 'node:util'; import { promisify } from 'node:util';
import { GENERATOR, type State, generate } from 'astring'; import { GENERATOR, type State, generate } from 'astring';
import type * as estree from 'estree'; import type * as estree from 'estree';
import * as glob from 'glob'; import glob from 'glob';
import { format } from 'prettier'; import { format } from 'prettier';
interface SatisfiesExpression extends estree.BaseExpression { interface SatisfiesExpression extends estree.BaseExpression {
@ -16,23 +16,69 @@ interface SatisfiesExpression extends estree.BaseExpression {
const generator = { const generator = {
...GENERATOR, ...GENERATOR,
SatisfiesExpression(node: SatisfiesExpression, state: State) { SatisfiesExpression(node: SatisfiesExpression, state: State) {
if (node.expression.type === 'ArrowFunctionExpression') { switch (node.expression.type) {
state.write('('); case 'ArrowFunctionExpression': {
this[node.expression.type](node.expression, state); state.write('(');
state.write(')'); this[node.expression.type](node.expression, state);
} else { state.write(')');
this[node.expression.type](node.expression, state); break;
}
default: {
// @ts-ignore
this[node.expression.type](node.expression, state);
break;
}
} }
state.write(' satisfies '); state.write(' satisfies ');
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}`
? XR extends ''
? [...YN, Uncapitalize<`${YC}${XH}`>]
: XH extends Uppercase<XH>
? SplitCamel<XR, Lowercase<XH>, [...YN, YC]>
: SplitCamel<XR, `${YC}${XH}`, YN>
: YN;
// @ts-ignore
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]
? XO
: 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[]]
? `${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 {
namespace JSX {
type Element = never;
type ElementClass = never;
type ElementAttributesProperty = never;
type ElementChildrenAttribute = never;
type IntrinsicAttributes = never;
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];
};
};
}
}
function toStories(component: string): string { function toStories(component: string): string {
const msw = `${component.slice(0, -'.vue'.length)}.msw`; const msw = `${component.slice(0, -'.vue'.length)}.msw`;
const implStories = `${component.slice(0, -'.vue'.length)}.stories.impl`; const implStories = `${component.slice(0, -'.vue'.length)}.stories.impl`;
@ -52,14 +98,14 @@ function toStories(component: string): string {
<property <property
key={<identifier name="layout" />} key={<identifier name="layout" />}
value={<literal value={`${dir}/`.startsWith('src/pages/') ? 'fullscreen' : 'centered'} />} value={<literal value={`${dir}/`.startsWith('src/pages/') ? 'fullscreen' : 'centered'} />}
kind="init" kind={"init" as const}
/>, />,
...hasMsw ...hasMsw
? [ ? [
<property <property
key={<identifier name="msw" />} key={<identifier name="msw" />}
value={<identifier name="msw" />} value={<identifier name="msw" />}
kind="init" kind={"init" as const}
shorthand shorthand
/>, />,
] ]
@ -107,13 +153,12 @@ function toStories(component: string): string {
specifiers={[ specifiers={[
<import-default-specifier <import-default-specifier
local={identifier} local={identifier}
imported={identifier}
/>, />,
]} ]}
/>, />,
], ],
<variable-declaration <variable-declaration
kind="const" kind={"const" as const}
declarations={[ declarations={[
<variable-declarator <variable-declarator
id={<identifier name="meta" />} id={<identifier name="meta" />}
@ -125,12 +170,12 @@ function toStories(component: string): string {
<property <property
key={<identifier name="title" />} key={<identifier name="title" />}
value={literal} value={literal}
kind="init" kind={"init" as const}
/>, />,
<property <property
key={<identifier name="component" />} key={<identifier name="component" />}
value={identifier} value={identifier}
kind="init" kind={"init" as const}
/>, />,
]} ]}
/> />
@ -148,7 +193,7 @@ function toStories(component: string): string {
<export-named-declaration <export-named-declaration
declaration={ declaration={
<variable-declaration <variable-declaration
kind="const" kind={"const" as const}
declarations={[ declarations={[
<variable-declarator <variable-declarator
id={<identifier name="Default" />} id={<identifier name="Default" />}
@ -169,7 +214,7 @@ function toStories(component: string): string {
<property <property
key={<identifier name="argTypes" />} key={<identifier name="argTypes" />}
value={<identifier name="argTypes" />} value={<identifier name="argTypes" />}
kind="init" kind={"init" as const}
shorthand shorthand
/>, />,
]} ]}
@ -190,13 +235,13 @@ function toStories(component: string): string {
<property <property
key={identifier} key={identifier}
value={identifier} value={identifier}
kind="init" kind={"init" as const}
shorthand shorthand
/>, />,
]} ]}
/> />
} }
kind="init" kind={"init" as const}
/>, />,
<property <property
key={<identifier name="props" />} key={<identifier name="props" />}
@ -213,12 +258,12 @@ function toStories(component: string): string {
]} ]}
/> />
} }
kind="init" kind={"init" as const}
/>, />,
<property <property
key={<identifier name="template" />} key={<identifier name="template" />}
value={<literal value={`<${identifier.name} v-bind="$props" />`} />} value={<literal value={`<${identifier.name} v-bind="$props" />`} />}
kind="init" kind={"init" as const}
/>, />,
]} ]}
/> />
@ -230,12 +275,12 @@ function toStories(component: string): string {
/> />
} }
method method
kind="init" kind={"init" as const}
/>, />,
<property <property
key={<identifier name="parameters" />} key={<identifier name="parameters" />}
value={parameters} value={parameters}
kind="init" kind={"init" as const}
/>, />,
]} ]}
/> />

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"strict": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"checkJs": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"jsxFactory": "h"
},
"files": ["./generate.tsx", "./preload-locale.ts", "./preload-theme.ts"]
}

View file

@ -43,5 +43,8 @@
".eslintrc.js", ".eslintrc.js",
"./**/*.ts", "./**/*.ts",
"./**/*.vue" "./**/*.vue"
],
"exclude": [
".storybook/**/*",
] ]
} }