UNPKG

@vuedoc/parser

Version:

Generate a JSON documentation for a Vue file

286 lines (240 loc) 7.89 kB
// import { Feature, Parser, PluginInterface, Syntax, Type } from '@vuedoc/parser'; import { Parser, PluginInterface } from '@vuedoc/parser'; import { Feature, Syntax, Type } from '../main.js'; import { generateUndefineValue } from '../entity/Value.js'; import * as Babel from '@babel/types'; type StoreEntry = Record<string, Parser.NS>; type Store = Record<'default' | string, StoreEntry>; const fnameKey = { mapState: 'state', mapGetters: 'getters', mapActions: 'actions', }; export function createVuexPlugin(storeFilenames: string[] = []): PluginInterface { return () => { const store: Store = { default: {}, }; const useStoreComposition: Parser.CompositionDeclaration = { fname: 'useStore', identifierSuffixes: ['state', 'getters'], typeParameterIndex: 0, returningValue: '', parseEntryValue(node, context) { if (node.arguments.length) { const mainStore = createStore(node.arguments[0], context); Object.assign(store, mainStore); } else if ('typeParameters' in node && node.typeParameters.params.length) { return context.getTypingValue(node.typeParameters.params[0]); } return generateUndefineValue.next().value; }, parseEntryNode(node, context) { this.parseEntryValue(node, context); }, }; return { resolver: { alias: { vuex: 'vuex/types/index.d.ts', }, }, preload: storeFilenames, composition: { data: [ useStoreComposition, { ...useStoreComposition, fname: 'createStore', }, ], computed: [ { fname: 'mapState', parseEntryNode(node, context) { parseNodeEntry.call(this, node, context, store); }, }, { fname: 'mapGetters', parseEntryNode(node, context) { parseNodeEntry.call(this, node, context, store); }, }, ], methods: [ { fname: 'mapActions', parseEntryNode(node, context) { parseNodeEntry.call(this, node, context, store); }, }, ], }, }; }; } function createStore(node: Babel.Node, context: Parser.Context) { const store: Store = { default: {}, }; switch (node.type) { case Syntax.Identifier: { const identifier = context.getIdentifier(node); if (identifier && 'node' in identifier) { if (identifier.node.value.type === Syntax.ObjectExpression && identifier.value.rawObject) { for (const key in identifier.value.rawObject) { const ref = identifier.value.rawObject[key]; const refNode = identifier.value.rawNode[key]; context.setScopeEntry({ key, value: ref, node: { value: refNode, type: refNode, comment: refNode, }, }); } } const identifierStore = createStore(identifier.node.value, context); Object.assign(store, identifierStore); } break; } case Syntax.ObjectExpression: { const properties = context.parseElements(node.properties); for (const property of properties) { if ('name' in property.key && property.type === Syntax.ObjectProperty) { switch (property.key.name) { case 'state': case 'getters': case 'actions': parseStoreEntry(property.key.name, property, context, store.default); break; case 'modules': if (property.value.type === Syntax.ObjectExpression) { const modules = context.parseElements(property.value.properties); for (const module of modules) { if ('name' in module.key && module.type === Syntax.ObjectProperty) { store[module.key.name] = createStore(module.value, context).default; } } } break; } } } } } return store; } function parseStoreEntry(type: string, property: Babel.ObjectProperty, context: Parser.Context, storeEntry: StoreEntry) { let objectExpressionNode: Babel.Node = property.value; if (property.value.type === Syntax.Identifier) { const identifier = context.getIdentifier(property.value); if (identifier) { if ('$ns' in identifier) { storeEntry[type] = identifier; return; } context.setScopeEntry(identifier); objectExpressionNode = identifier.node.value; } } if (objectExpressionNode.type === Syntax.ObjectExpression) { const ns = context.createNamespace(); const items = context.parseElements(objectExpressionNode.properties); storeEntry[type] = ns; for (const item of items) { if ('name' in item.key) { const key = item.key.name; const nodeValue = item.type === Syntax.ObjectProperty ? item.value : item; const value = context.getValue(nodeValue); ns.scope[key] = { key, value, node: { value: nodeValue, type: nodeValue, comment: item, }, }; } } } } function parseFeatureEntry( fname: string, feature: Parser.Feature, entryName: string, node: Babel.Node, context: Parser.Context, store: StoreEntry ) { let entryRef: Parser.Value; let entryNode: Babel.Node; if (fname in fnameKey) { const key = fnameKey[fname]; if (key in store) { const ns = store[key]; if (entryName in ns.scope) { const ref = ns.scope[entryName]; entryRef = ref.value; entryNode = ref.node.value; if ('params' in entryNode) { const [param] = entryNode.params.splice(0, 1); if (key === 'getters') { const state = context.getScopeValue('state'); if (state && param.type === Syntax.Identifier) { context.scope[param.name] = state; } } } if (feature === Feature.computed && 'params' in entryNode) { entryRef.type = context.getReturnType(entryNode); } } } } if (!entryRef) { entryRef = context.getValue(node); entryRef.type = Type.unknown; entryNode = node; } context.emitEntryFeature({ feature, name: entryName, ref: entryRef, node: entryNode, }); } function parseNodeEntry(node: Babel.CallExpression, context: Parser.Context, store: Store) { if (node.arguments.length) { const firstArgNode = node.arguments[0]; const firstArgRef = context.getValue(firstArgNode); const namespaceName = typeof firstArgRef.value === 'string' ? firstArgRef.value : 'default'; const storeEntry = store[namespaceName] || {}; for (const argument of node.arguments) { const elements = argument.type === Syntax.ObjectExpression ? context.parseElements(argument.properties) : argument.type === Syntax.ArrayExpression ? context.parseElements(argument.elements) : []; for (const element of elements) { const entryFeature = this.fname === 'mapActions' ? Feature.methods : Feature.computed; switch (element.type) { case Syntax.ObjectProperty: case Syntax.ObjectMethod: { const name = context.getValue<string>(element.key); parseFeatureEntry(this.fname, entryFeature, name.value, element, context, storeEntry); break; } case Syntax.StringLiteral: parseFeatureEntry(this.fname, entryFeature, element.value, element, context, storeEntry); break; } } } } }