diff --git a/packages/frontend/.storybook/.gitignore b/packages/frontend/.storybook/.gitignore index 0de26507c6..649b36b848 100644 --- a/packages/frontend/.storybook/.gitignore +++ b/packages/frontend/.storybook/.gitignore @@ -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 -# (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 /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 /themes.ts diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index 0a4ec39108..8ce2af772c 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -4,7 +4,7 @@ import { basename, dirname } from 'node:path/posix'; import { promisify } from 'node:util'; import { GENERATOR, type State, generate } from 'astring'; import type * as estree from 'estree'; -import * as glob from 'glob'; +import glob from 'glob'; import { format } from 'prettier'; interface SatisfiesExpression extends estree.BaseExpression { @@ -16,23 +16,69 @@ interface SatisfiesExpression extends estree.BaseExpression { const generator = { ...GENERATOR, SatisfiesExpression(node: SatisfiesExpression, state: State) { - if (node.expression.type === 'ArrowFunctionExpression') { - state.write('('); - this[node.expression.type](node.expression, state); - state.write(')'); - } else { - this[node.expression.type](node.expression, state); + switch (node.expression.type) { + case 'ArrowFunctionExpression': { + state.write('('); + this[node.expression.type](node.expression, state); + state.write(')'); + break; + } + default: { + // @ts-ignore + this[node.expression.type](node.expression, state); + break; + } } state.write(' satisfies '); this[node.reference.type](node.reference, state); }, } +type SplitCamel = T extends `${infer XH}${infer XR}` + ? XR extends '' + ? [...YN, Uncapitalize<`${YC}${XH}`>] + : XH extends Uppercase + ? SplitCamel, [...YN, YC]> + : SplitCamel + : YN; + +// @ts-ignore +type SplitKebab = T extends `${infer XH}-${infer XR}` + ? [XH, ...SplitKebab] + : [T]; + +type ToKebab = 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}` : ''}` + : ''; + +// @ts-ignore +type ToPascal = T extends readonly [infer XH extends string, ...infer XR extends readonly string[]] + ? `${Capitalize}${ToPascal}` + : ''; + function h(component: T['type'], props: Omit): 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 ElementClass = never; + type ElementAttributesProperty = never; + type ElementChildrenAttribute = never; + type IntrinsicAttributes = never; + type IntrinsicClassAttributes = never; + type IntrinsicElements = { + [T in keyof typeof generator as ToKebab>>]: { + [K in keyof Omit[0], 'type'>]?: Parameters[0][K]; + }; + }; + } +} + function toStories(component: string): string { const msw = `${component.slice(0, -'.vue'.length)}.msw`; const implStories = `${component.slice(0, -'.vue'.length)}.stories.impl`; @@ -52,14 +98,14 @@ function toStories(component: string): string { } value={} - kind="init" + kind={"init" as const} />, ...hasMsw ? [ } value={} - kind="init" + kind={"init" as const} shorthand />, ] @@ -107,13 +153,12 @@ function toStories(component: string): string { specifiers={[ , ]} />, ], } @@ -125,12 +170,12 @@ function toStories(component: string): string { } value={literal} - kind="init" + kind={"init" as const} />, } value={identifier} - kind="init" + kind={"init" as const} />, ]} /> @@ -148,7 +193,7 @@ function toStories(component: string): string { } @@ -169,7 +214,7 @@ function toStories(component: string): string { } value={} - kind="init" + kind={"init" as const} shorthand />, ]} @@ -190,13 +235,13 @@ function toStories(component: string): string { , ]} /> } - kind="init" + kind={"init" as const} />, } @@ -213,12 +258,12 @@ function toStories(component: string): string { ]} /> } - kind="init" + kind={"init" as const} />, } value={`} />} - kind="init" + kind={"init" as const} />, ]} /> @@ -230,12 +275,12 @@ function toStories(component: string): string { /> } method - kind="init" + kind={"init" as const} />, } value={parameters} - kind="init" + kind={"init" as const} />, ]} /> diff --git a/packages/frontend/.storybook/tsconfig.json b/packages/frontend/.storybook/tsconfig.json new file mode 100644 index 0000000000..01aa9db6eb --- /dev/null +++ b/packages/frontend/.storybook/tsconfig.json @@ -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"] +} diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json index 54e5219b56..4d582daa3c 100644 --- a/packages/frontend/tsconfig.json +++ b/packages/frontend/tsconfig.json @@ -43,5 +43,8 @@ ".eslintrc.js", "./**/*.ts", "./**/*.vue" + ], + "exclude": [ + ".storybook/**/*", ] }