UNPKG

@vuedoc/parser

Version:

Generate a JSON documentation for a Vue file

179 lines 5.6 kB
import resolve from 'resolve/sync.js'; import _traverse from '@babel/traverse'; import { readFileSync } from 'node:fs'; import { extname, join } from 'node:path'; import { parse } from '@babel/parser'; import { merge } from '@b613/utils/lib/object.js'; import { Loader } from './Loader.js'; // https://github.com/babel/babel/issues/13855#issuecomment-945123514 const traverse = typeof _traverse === 'function' ? _traverse : _traverse.default; const defaultResolver = { basedir: process.cwd(), extensions: ['.js', '.jsx', '.ts', '.tsx', '.d.ts', '.vue'], }; const BABEL_DEFAULT_PLUGINS = [ 'asyncGenerators', 'bigInt', 'classPrivateMethods', 'classPrivateProperties', 'classProperties', 'decorators-legacy', 'doExpressions', 'dynamicImport', 'exportDefaultFrom', 'exportNamespaceFrom', // 'flow', 'flowComments', 'functionBind', 'functionSent', 'importMeta', 'logicalAssignment', 'nullishCoalescingOperator', 'numericSeparator', 'objectRestSpread', 'optionalCatchBinding', 'optionalChaining', 'partialApplication', ['pipelineOperator', { proposal: 'smart' }], 'placeholders', 'privateIn', 'throwExpressions', 'topLevelAwait', ]; function parseAst(content, plugins = []) { return parse(content, { allowImportExportEverywhere: true, allowAwaitOutsideFunction: true, allowReturnOutsideFunction: true, allowSuperOutsideMethod: true, allowUndeclaredExports: true, createParenthesizedExpressions: false, errorRecovery: false, plugins: [...BABEL_DEFAULT_PLUGINS, ...plugins], sourceType: 'module', strictMode: false, ranges: true, tokens: false, }); } function parseAlias(path, aliasMap) { for (const prefix in aliasMap) { if (path.startsWith(prefix)) { const alias = aliasMap[prefix]; return join(alias, path.slice(prefix.length)); } } return path; } function findComment({ trailingComments = [], leadingComments = trailingComments, ...node }) { const leadingComment = leadingComments?.reverse()[0]; if (leadingComment) { return leadingComment.start && node.end && leadingComment.start > node.end ? null : leadingComment.value; } return null; } function loadFileContent(loaderName, filecontent, { plugins = [], ...options }) { const loaderOptions = { definitions: options.loaders, source: { errors: [], }, }; const LoaderClass = Loader.get(loaderName, loaderOptions); const loader = new LoaderClass(loaderOptions); loader.load({ attrs: { lang: loaderName, }, content: filecontent, }); const file = loader.source; if (file.errors.length) { throw new ParsingError(loader.source.errors[0]); } if (file.script?.content) { if (plugins.length === 0) { plugins = parsePlugins(file.script.attrs.lang, options); } file.script.ast = parseAst(file.script.content, plugins); traverse(file.script.ast, { enter(path) { if (path.node) { if (!path.node.extra) { path.node.extra = {}; } path.node.extra.file = file; path.node.extra.comment = findComment(path.node); } }, }); } else { delete file.script; } return file; } function parsePlugins(lang, options = {}) { const plugins = []; switch (lang) { case 'ts': if (options.path?.endsWith('.d.ts')) { plugins.push(['typescript', { dts: true }]); } else { plugins.push('typescript'); } break; case 'tsx': plugins.push('typescript'); plugins.push('jsx'); break; case 'jsx': plugins.push('jsx'); break; default: plugins.push('typescript'); if (options.jsx) { plugins.push('jsx'); } break; } return plugins; } export class ParsingError extends Error { } export class FS { constructor(options) { this.index = {}; this.options = options; } loadFile(filename, resolver = this.options.resolver || defaultResolver) { const source = resolver.alias ? parseAlias(filename, resolver.alias) : filename; if (source in this.index) { return this.index[source]; } let path; const currentResolver = merge({ ...defaultResolver }, resolver); try { path = resolve(source, currentResolver); } catch { throw new Error(`Cannot find module '${filename}'. Make sure to define options.resolver`); } if (path in this.index) { return this.index[path]; } const lang = extname(path).substring(1); const plugins = parsePlugins(lang, { path, jsx: this.options.jsx }); const filecontent = readFileSync(path, this.options.encoding || 'utf8'); const file = loadFileContent(lang, filecontent, { ...this.options, plugins }); file.filename = path; this.index[source] = file; this.index[path] = file; return file; } loadContent(loaderName, filecontent) { return loadFileContent(loaderName, filecontent, this.options); } } //# sourceMappingURL=FS.js.map