import { existsSync, readFileSync } from 'node:fs'; import { writeFile } from 'node:fs/promises'; 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 { format } from 'prettier'; interface SatisfiesExpression extends estree.BaseExpression { type: 'SatisfiesExpression'; expression: estree.Expression; reference: estree.Identifier; } const generator = { ...GENERATOR, SatisfiesExpression(node: SatisfiesExpression, state: State) { this[node.expression.type](node.expression, state); state.write(' satisfies '); this[node.reference.type](node.reference, state); }, } 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; } function toStories(component: string): string { const msw = `${component.slice(0, -'.vue'.length)}.msw`; const implStories = `${component.slice(0, -'.vue'.length)}.stories.impl`; const hasMsw = existsSync(`${msw}.ts`); const hasImplStories = existsSync(`${implStories}.ts`); const base = basename(component); const dir = dirname(component); const literal = ( ) as unknown as estree.Literal; const identifier = ( ) as unknown as estree.Identifier; const parameters = ( } value={} kind="init" />, ...hasMsw ? [ } value={} kind="init" shorthand />, ] : [], ]} /> ); const program = ( } specifiers={[ } imported={} />, ...hasImplStories ? [] : [ } imported={} />, ], ]} />, ...hasMsw ? [ } specifiers={[ } />, ]} />, ] : [], ...hasImplStories ? [] : [ } specifiers={[ , ]} />, ], } init={ } value={literal} kind="init" />, } value={identifier} kind="init" />, ]} /> } reference={`} />} /> } />, ]} />, ...hasImplStories ? [ ] : [ } init={ } value={ } params={[ , } value={} kind="init" shorthand />, ]} />, ]} body={ } value={ , ]} /> } kind="init" />, } value={ } property={} /> } arguments={[ , ]} /> } kind="init" />, } value={`} />} kind="init" />, ]} /> } />, ]} /> } /> } method kind="init" />, } value={parameters} kind="init" />, ]} /> } reference={`} />} /> } />, ]} /> } />, ], } />, ]} /> ) as unknown as estree.Program; return format( generate(program, { generator }) + (hasImplStories ? readFileSync(`${implStories}.ts`, 'utf-8') : ''), { parser: 'babel-ts', singleQuote: true, useTabs: true, } ); } 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)); }) ));