UNPKG

@vuedoc/parser

Version:

Generate a JSON documentation for a Vue file

405 lines (356 loc) 11.1 kB
import { PropType } from '@b613/utils/typings'; import { ParseResult } from '@babel/parser'; import { ImportResolver } from './ImportResolver'; import { File as FileSystemFile } from './FileSystem'; import { Loader } from './Loader'; import { Entry } from './Entry'; import * as Babel from '@babel/types'; export namespace Parser { type FilenameOptions = { /** * The filename to parse */ filename: string; }; type FilecontentOptions = Partial<FilenameOptions> & { /** * The SFC file content to parse */ filecontent: string; }; type Options = (FilenameOptions | FilecontentOptions) & { /** * The file encoding * @default 'utf8' */ encoding?: string; /** * The component features to parse and extract * @default ['name', 'description', 'slots', 'props', 'data', 'computed', 'events', 'methods'] */ features?: Feature[]; /** * List of ignored visibilities * @default ['protected', 'private'] */ ignoredVisibilities?: Entry.Visibility[]; /** * Additional composition tokens for advanced components. * This option is useful when you're using third library which expose * composition functions. */ composition?: Partial<ParsingComposition>; /** * Set to `true` to enable JSX parsing * @default false */ jsx?: boolean; /** * Custom import resolver */ resolver?: ImportResolver; /** * User loaders definitions */ loaders?: Loader.Definition[]; /** * User parsing plugins */ plugins?: ParsingPlugin[]; }; /** * @example Example 1: * Given the folowing SFC file: * * ```html * <script setup> * const { hello, greeting } = useStorage('my-store', { hello: 'hi', greeting: 'Hello' }); * </script> * ``` * * The composition parser could be: * * ```js * { * fname: 'useStorage', * valueIndex: 1, * ignoreVariableId: false, * } * ``` * @example Example 2: * Given the folowing SFC file: * * ```html * <script setup> * const state = useStorage('my-store', { hello: 'hi', greeting: 'Hello' }); * </script> * ``` * * The composition parser could be: * * ```js * { * fname: 'useStorage', * valueIndex: 1, * ignoreVariableId: true, * } * ``` */ type CompositionDeclaration = { /** * The composition function name. * Example 'useAttrs` */ fname: string; /** * Define which index of the argument list will be parsed as a value. * @example * For the given codes: * - `const name = ref('Chaka')` => we have `valueIndex = 0` * \_0_/ * - `const flag = useStorage('my-flag', false)` => we have `valueIndex = 1` * \__0__/ \_1_/ */ valueIndex?: number; /** * Define which index of the type parameter list will be parsed as a type. * @example * For the given codes: * - `const name = ref<CustomString>('Chaka')` => we have `valueIndex = 0` * \___ 0 ____/ */ typeParameterIndex?: number; /** * Wheter or not the value from `.valueIndex` can be undefined. * This is the case for `const { name } = defineProps<{ name: string }>()` */ valueCanBeUndefined?: boolean; /** * Use this to define the identifier suffix for the composition value. * @example * For the given code: * - `const name = ref('Chaka')` => we have `identifierSuffixes = ['value']`. * This will help Vuedoc to handle `name.value` as a direct node for the * value `Chaka`. */ identifierSuffixes?: string[]; /** * Use this to force the type returned by the composition function. * By default Vuedoc Parser will try to automatically extract the returning * type */ returningType?: string; /** * Use this to force the value returned by the composition function * By default Vuedoc Parser will try to automatically extract the returning * value */ returningValue?: string; /** * Whether or not the identifier of the variable should be ignored. * For example, given `const attrs = useAttrs()`, `attrs` will be * emited as a component data if `options.ignoreVariableId = true`. * @default false */ ignoreVariableIdentifier?: boolean; /** * Use this function to parse the CallExpression node and return the entry * value */ parseEntryValue?(node: Babel.CallExpression, context: Context): Value<any> | undefined; /** * Use this function to parse the CallExpression node and return nodes * to parse as entries */ parseEntryNode?(node: Babel.CallExpression, context: Context): void; }; type CompositionFeature = Exclude<Feature, 'name' | 'description' | 'keywords' | 'slots'>; type CompiledComposition = CompositionDeclaration & { feature: CompositionFeature; }; type ParsingComposition = Record<CompositionFeature, CompositionDeclaration[]>; type ParsingPlugin<T> = (parser: Interface) => PluginDefinition<T> | undefined; type PluginDefinition<T> = T & Pick<Options, 'composition' | 'resolver'> & { /** * List of resource files to preload before parsing */ preload?: string[]; }; type EmitEntryFeatureOptions = { feature: Feature; name: string; ref: Value<any>; node: Babel.Node; nodeType?: Babel.Node; nodeComment?: Babel.Node; }; interface Context { readonly scope: Scope; emitEntryFeature(meta: EmitEntryFeatureOptions): void; isNamespace(ref: NS | ScopeEntry): boolean; createNamespace(): NS; parseElements(elements: (Babel.Expression | Babel.SpreadElement)[]): Babel.Expression[]; parseElements(elements: (Babel.ObjectMethod | Babel.ObjectProperty | Babel.SpreadElement)[]): Array<Babel.ObjectMethod | Babel.ObjectProperty>; getValue<T = any>(node: Babel.Node | null): Value<T>; getTypingValue<T = any>(node: Babel.Node): Value<T> | null; getIdentifier(node: Babel.Identifier): ScopeEntry | NS | null; getReturnType(node: Babel.Node, defaultType?: Type): Type | Type[]; getScopeValue(key: string): ScopeEntry<any, any, any> | null; setValue(key: string, ref: Value<any>, node: Babel.Node): void; setScopeEntry(ref: ScopeEntry): void; } type File = { template?: Source; script?: Required<Script>; errors: string[]; }; type Type = string; type Feature = 'name' | 'description' | 'keywords' | 'slots' | 'props' | 'data' | 'computed' | 'events' | 'methods'; declare const SUPPORTED_FEATURES: Feature[]; type Source<Attrs extends object = object> = { content: string; attrs: Attrs & { lang?: string; [additionalAttribute: string]: string | undefined; }; }; type Script = Source<{ setup?: boolean }> & { ast?: AST.Result; }; type Scope<TypeValue = string, NodeValue = Babel.Node, NoteType = NodeValue> = Record< string, ScopeEntry<TypeValue, NodeValue, NoteType> | NS >; interface NS { $ns: symbol; readonly scope: Record<string, ScopeEntry>; } declare class Value<TValue = any> { type: Parser.Type | Parser.Type[]; value: TValue; raw: string; rawObject?: Record<string, any>; rawNode?: Record<string, Babel.Node> | Babel.Node[]; member: boolean; expression?: boolean; function?: boolean; $kind?: string; readonly kind: string; constructor(type?: Parser.Type | Parser.Type[], value?: any, raw?: string); static isNativeType(type): boolean; static parseNativeType(type): string; } type ScopeEntry<TypeValue = string, NodeValue = Babel.Node, NoteType = NodeValue> = { value: Value<TypeValue>; key: string; source?: string; function?: boolean; computed?: boolean; tsValue?: AST.TSValue; composition?: CompiledComposition; node: { value: NodeValue; type: NoteType; comment: NoteType; }; }; namespace AST { type File = Babel.File; type Result = ParseResult<File>; type Program = Babel.Program; type Statement = Babel.Statement; interface VariableDeclarator extends Babel.VariableDeclarator { id: LVal; } interface VariableDeclaration extends Babel.VariableDeclaration { declarations: VariableDeclarator[]; } type LVal = Babel.LVal & { name: string; }; type Node = (Babel.Expression | Babel.Node) & { extra: { file: FileSystemFile; $tsValue?: TSValue; $declarator?: VariableDeclarator; $composition?: CompositionDeclaration; }; typeParameters?: Babel.TSTypeParameterInstantiation | null; typeAnnotation?: Babel.TSTypeAnnotation | Babel.TSType; }; type TSValue = { node: Babel.Node | Record<string, Babel.Node> | Babel.Node[]; type: string | string[] | Record<string, string>; kind?: string; computed?: boolean; compositionType?: string; }; } class EntryEvent<T extends Entry.Type = Entry.Type> extends Event { /** * Parsed entry ready for emission */ readonly entry: T; constructor(entry: T); } type MessageEventType = 'warning' | 'error'; class MessageEvent<T extends MessageEventType> extends Event { readonly message: string; constructor(type: T, message: string); } class EndEvent extends Event { constructor(); } class FatalErrorEvent extends Event { readonly error: Error; constructor(err: Error); } interface EventListener<T extends EntryEvent | MessageEvent> { (evt: T): void; } class Private extends EventTarget { static SUPPORTED_FEATURES: Feature[]; /** * Enabled features */ readonly features: Feature[]; /** * Resolved options */ readonly options: Options; constructor(options: Options); addEventListener<T extends Entry.Type, K extends Entry.Kind = PropType<T, 'kind'>>( type: K, callback: EventListener<EntryEvent<T>>, options?: boolean | AddEventListenerOptions ): void; addEventListener<T extends MessageEventType>( type: T, callback: EventListener<MessageEvent<T>>, options?: boolean | AddEventListenerOptions ): void; addEventListener( type: 'end', callback: EventListener<EndEvent>, options?: boolean | AddEventListenerOptions ): void; addEventListener( type: 'fatal', callback: EventListener<FatalErrorEvent>, options?: boolean | AddEventListenerOptions ): void; /** * Start the parsing process */ walk(): void; } class Interface extends Private { readonly composition: Composition; readonly scope: Vuedoc.Parser.Scope; readonly fs: FS; emitEntry(entry: Vuedoc.Entry.Type): void; emitWarning(message: string): void; emitError(message: string): void; execAsync(fn: AsyncOperation): void; } }