vue-component-meta
Advanced tools
Comparing version 1.8.22 to 1.9.0-alpha.0
@@ -1,7 +0,4 @@ | ||
import * as vue from '@vue/language-core'; | ||
import type * as ts from 'typescript/lib/tsserverlibrary'; | ||
import type { MetaCheckerOptions, ComponentMeta } from './types'; | ||
import type { MetaCheckerOptions } from './types'; | ||
export * from './types'; | ||
export type ComponentMetaChecker = ReturnType<typeof baseCreate>; | ||
export declare function createComponentMetaCheckerByJsonConfig(root: string, json: any, checkerOptions?: MetaCheckerOptions, ts?: typeof import('typescript/lib/tsserverlibrary')): { | ||
export declare function createCheckerByJson(rootPath: string, json: any, checkerOptions?: MetaCheckerOptions): { | ||
updateFile(fileName: string, text: string): void; | ||
@@ -12,8 +9,8 @@ deleteFile(fileName: string): void; | ||
getExportNames: (componentPath: string) => string[]; | ||
getComponentMeta: (componentPath: string, exportName?: string) => ComponentMeta; | ||
getComponentMeta: (componentPath: string, exportName?: string) => import("./types").ComponentMeta; | ||
__internal__: { | ||
tsLs: ts.LanguageService; | ||
tsLs: import("typescript/lib/tsserverlibrary").LanguageService; | ||
}; | ||
}; | ||
export declare function createComponentMetaChecker(tsconfigPath: string, checkerOptions?: MetaCheckerOptions, ts?: typeof import('typescript/lib/tsserverlibrary')): { | ||
export declare function createChecker(tsconfig: string, checkerOptions?: MetaCheckerOptions): { | ||
updateFile(fileName: string, text: string): void; | ||
@@ -24,14 +21,7 @@ deleteFile(fileName: string): void; | ||
getExportNames: (componentPath: string) => string[]; | ||
getComponentMeta: (componentPath: string, exportName?: string) => ComponentMeta; | ||
getComponentMeta: (componentPath: string, exportName?: string) => import("./types").ComponentMeta; | ||
__internal__: { | ||
tsLs: ts.LanguageService; | ||
tsLs: import("typescript/lib/tsserverlibrary").LanguageService; | ||
}; | ||
}; | ||
export declare function baseCreate(_host: vue.TypeScriptLanguageHost, vueCompilerOptions: vue.VueCompilerOptions, checkerOptions: MetaCheckerOptions, globalComponentName: string, ts: typeof import('typescript/lib/tsserverlibrary')): { | ||
getExportNames: (componentPath: string) => string[]; | ||
getComponentMeta: (componentPath: string, exportName?: string) => ComponentMeta; | ||
__internal__: { | ||
tsLs: ts.LanguageService; | ||
}; | ||
}; | ||
//# sourceMappingURL=index.d.ts.map |
668
out/index.js
@@ -17,664 +17,14 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.baseCreate = exports.createComponentMetaChecker = exports.createComponentMetaCheckerByJsonConfig = void 0; | ||
const vue = require("@vue/language-core"); | ||
const path = require("typesafe-path/posix"); | ||
const vue_component_type_helpers_1 = require("vue-component-type-helpers"); | ||
const typescript_1 = require("@volar/typescript"); | ||
exports.createChecker = exports.createCheckerByJson = void 0; | ||
const ts = require("typescript"); | ||
const base_1 = require("./base"); | ||
__exportStar(require("./types"), exports); | ||
const windowsPathReg = /\\/g; | ||
function createComponentMetaCheckerByJsonConfig(root, json, checkerOptions = {}, ts = require('typescript')) { | ||
const rootPath = root.replace(windowsPathReg, '/'); | ||
return createComponentMetaCheckerWorker(() => vue.createParsedCommandLineByJson(ts, ts.sys, root, json), checkerOptions, rootPath, path.join(rootPath, 'jsconfig.json.global.vue'), ts); | ||
function createCheckerByJson(rootPath, json, checkerOptions = {}) { | ||
return (0, base_1.createCheckerByJsonBase)(ts, rootPath, json, checkerOptions); | ||
} | ||
exports.createComponentMetaCheckerByJsonConfig = createComponentMetaCheckerByJsonConfig; | ||
function createComponentMetaChecker(tsconfigPath, checkerOptions = {}, ts = require('typescript')) { | ||
const tsconfig = tsconfigPath.replace(windowsPathReg, '/'); | ||
return createComponentMetaCheckerWorker(() => vue.createParsedCommandLine(ts, ts.sys, tsconfigPath), checkerOptions, path.dirname(tsconfig), tsconfig + '.global.vue', ts); | ||
exports.createCheckerByJson = createCheckerByJson; | ||
function createChecker(tsconfig, checkerOptions = {}) { | ||
return (0, base_1.createCheckerBase)(ts, tsconfig, checkerOptions); | ||
} | ||
exports.createComponentMetaChecker = createComponentMetaChecker; | ||
function createComponentMetaCheckerWorker(loadParsedCommandLine, checkerOptions, rootPath, globalComponentName, ts) { | ||
/** | ||
* Original Host | ||
*/ | ||
let parsedCommandLine = loadParsedCommandLine(); | ||
let fileNames = parsedCommandLine.fileNames.map(path => path.replace(windowsPathReg, '/')); | ||
let projectVersion = 0; | ||
const scriptSnapshots = new Map(); | ||
const _host = { | ||
workspacePath: rootPath, | ||
rootPath: rootPath, | ||
getProjectVersion: () => projectVersion.toString(), | ||
getCompilationSettings: () => parsedCommandLine.options, | ||
getScriptFileNames: () => fileNames, | ||
getProjectReferences: () => parsedCommandLine.projectReferences, | ||
getScriptSnapshot: (fileName) => { | ||
if (!scriptSnapshots.has(fileName)) { | ||
const fileText = ts.sys.readFile(fileName); | ||
if (fileText !== undefined) { | ||
scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(fileText)); | ||
} | ||
} | ||
return scriptSnapshots.get(fileName); | ||
}, | ||
}; | ||
return { | ||
...baseCreate(_host, vue.resolveVueCompilerOptions(parsedCommandLine.vueOptions), checkerOptions, globalComponentName, ts), | ||
updateFile(fileName, text) { | ||
fileName = fileName.replace(windowsPathReg, '/'); | ||
scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(text)); | ||
projectVersion++; | ||
}, | ||
deleteFile(fileName) { | ||
fileName = fileName.replace(windowsPathReg, '/'); | ||
fileNames = fileNames.filter(f => f !== fileName); | ||
projectVersion++; | ||
}, | ||
reload() { | ||
parsedCommandLine = loadParsedCommandLine(); | ||
fileNames = parsedCommandLine.fileNames.map(path => path.replace(windowsPathReg, '/')); | ||
this.clearCache(); | ||
}, | ||
clearCache() { | ||
scriptSnapshots.clear(); | ||
projectVersion++; | ||
}, | ||
}; | ||
} | ||
function baseCreate(_host, vueCompilerOptions, checkerOptions, globalComponentName, ts) { | ||
const globalComponentSnapshot = ts.ScriptSnapshot.fromString('<script setup lang="ts"></script>'); | ||
const metaSnapshots = {}; | ||
const host = new Proxy({ | ||
getScriptFileNames: () => { | ||
const names = _host.getScriptFileNames(); | ||
return [ | ||
...names, | ||
...names.map(getMetaFileName), | ||
globalComponentName, | ||
getMetaFileName(globalComponentName), | ||
]; | ||
}, | ||
getScriptSnapshot: fileName => { | ||
if (isMetaFileName(fileName)) { | ||
if (!metaSnapshots[fileName]) { | ||
metaSnapshots[fileName] = ts.ScriptSnapshot.fromString(getMetaScriptContent(fileName)); | ||
} | ||
return metaSnapshots[fileName]; | ||
} | ||
else if (fileName === globalComponentName) { | ||
return globalComponentSnapshot; | ||
} | ||
else { | ||
return _host.getScriptSnapshot(fileName); | ||
} | ||
}, | ||
}, { | ||
get(target, prop) { | ||
if (prop in target) { | ||
return target[prop]; | ||
} | ||
return _host[prop]; | ||
}, | ||
}); | ||
const vueLanguages = ts ? vue.createLanguages(host.getCompilationSettings(), vueCompilerOptions, ts) : []; | ||
const core = vue.createLanguageContext(host, vueLanguages); | ||
const tsLsHost = (0, typescript_1.createLanguageServiceHost)(core, ts, ts.sys, undefined); | ||
const tsLs = ts.createLanguageService(tsLsHost); | ||
(0, typescript_1.decorateLanguageService)(core.virtualFiles, tsLs, false); | ||
if (checkerOptions.forceUseTs) { | ||
const getScriptKind = tsLsHost.getScriptKind; | ||
tsLsHost.getScriptKind = (fileName) => { | ||
if (fileName.endsWith('.vue.js')) { | ||
return ts.ScriptKind.TS; | ||
} | ||
if (fileName.endsWith('.vue.jsx')) { | ||
return ts.ScriptKind.TSX; | ||
} | ||
return getScriptKind(fileName); | ||
}; | ||
} | ||
let globalPropNames; | ||
return { | ||
getExportNames, | ||
getComponentMeta, | ||
__internal__: { | ||
tsLs, | ||
}, | ||
}; | ||
function isMetaFileName(fileName) { | ||
return fileName.endsWith('.meta.ts'); | ||
} | ||
function getMetaFileName(fileName) { | ||
return (fileName.endsWith('.vue') ? fileName : fileName.substring(0, fileName.lastIndexOf('.'))) + '.meta.ts'; | ||
} | ||
function getMetaScriptContent(fileName) { | ||
let code = ` | ||
import * as Components from '${fileName.substring(0, fileName.length - '.meta.ts'.length)}'; | ||
export default {} as { [K in keyof typeof Components]: ComponentMeta<typeof Components[K]>; }; | ||
interface ComponentMeta<T> { | ||
type: ComponentType<T>; | ||
props: ComponentProps<T>; | ||
emit: ComponentEmit<T>; | ||
slots: ${vueCompilerOptions.target < 3 ? 'Vue2ComponentSlots' : 'ComponentSlots'}<T>; | ||
exposed: ComponentExposed<T>; | ||
}; | ||
${vue_component_type_helpers_1.code} | ||
`.trim(); | ||
return code; | ||
} | ||
function getExportNames(componentPath) { | ||
const program = tsLs.getProgram(); | ||
const typeChecker = program.getTypeChecker(); | ||
return _getExports(program, typeChecker, componentPath).exports.map(e => e.getName()); | ||
} | ||
function getComponentMeta(componentPath, exportName = 'default') { | ||
const program = tsLs.getProgram(); | ||
const typeChecker = program.getTypeChecker(); | ||
const { symbolNode, exports } = _getExports(program, typeChecker, componentPath); | ||
const _export = exports.find((property) => property.getName() === exportName); | ||
if (!_export) { | ||
throw `Could not find export ${exportName}`; | ||
} | ||
const componentType = typeChecker.getTypeOfSymbolAtLocation(_export, symbolNode); | ||
const symbolProperties = componentType.getProperties() ?? []; | ||
let _type; | ||
let _props; | ||
let _events; | ||
let _slots; | ||
let _exposed; | ||
return { | ||
get type() { | ||
return _type ?? (_type = getType()); | ||
}, | ||
get props() { | ||
return _props ?? (_props = getProps()); | ||
}, | ||
get events() { | ||
return _events ?? (_events = getEvents()); | ||
}, | ||
get slots() { | ||
return _slots ?? (_slots = getSlots()); | ||
}, | ||
get exposed() { | ||
return _exposed ?? (_exposed = getExposed()); | ||
}, | ||
}; | ||
function getType() { | ||
const $type = symbolProperties.find(prop => prop.escapedName === 'type'); | ||
if ($type) { | ||
const type = typeChecker.getTypeOfSymbolAtLocation($type, symbolNode); | ||
return Number(typeChecker.typeToString(type)); | ||
} | ||
return 0; | ||
} | ||
function getProps() { | ||
const $props = symbolProperties.find(prop => prop.escapedName === 'props'); | ||
const propEventRegex = /^(on[A-Z])/; | ||
let result = []; | ||
if ($props) { | ||
const type = typeChecker.getTypeOfSymbolAtLocation($props, symbolNode); | ||
const properties = type.getProperties(); | ||
result = properties | ||
.map((prop) => { | ||
const { resolveNestedProperties, } = createSchemaResolvers(typeChecker, symbolNode, checkerOptions, ts, core); | ||
return resolveNestedProperties(prop); | ||
}) | ||
.filter((prop) => !prop.name.match(propEventRegex)); | ||
} | ||
// fill global | ||
if (componentPath !== globalComponentName) { | ||
globalPropNames ??= getComponentMeta(globalComponentName).props.map(prop => prop.name); | ||
for (const prop of result) { | ||
prop.global = globalPropNames.includes(prop.name); | ||
} | ||
} | ||
// fill defaults | ||
const printer = ts.createPrinter(checkerOptions.printer); | ||
const snapshot = host.getScriptSnapshot(componentPath); | ||
const vueSourceFile = core.virtualFiles.getSource(componentPath)?.root; | ||
const vueDefaults = vueSourceFile && exportName === 'default' | ||
? (vueSourceFile instanceof vue.VueFile ? readVueComponentDefaultProps(vueSourceFile, printer, ts, vueCompilerOptions) : {}) | ||
: {}; | ||
const tsDefaults = !vueSourceFile ? readTsComponentDefaultProps(componentPath.substring(componentPath.lastIndexOf('.') + 1), // ts | js | tsx | jsx | ||
snapshot.getText(0, snapshot.getLength()), exportName, printer, ts) : {}; | ||
for (const [propName, defaultExp] of Object.entries({ | ||
...vueDefaults, | ||
...tsDefaults, | ||
})) { | ||
const prop = result.find(p => p.name === propName); | ||
if (prop) { | ||
prop.default = defaultExp.default; | ||
if (defaultExp.required !== undefined) { | ||
prop.required = defaultExp.required; | ||
} | ||
if (prop.default !== undefined) { | ||
prop.required = false; // props with default are always optional | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
function getEvents() { | ||
const $emit = symbolProperties.find(prop => prop.escapedName === 'emit'); | ||
if ($emit) { | ||
const type = typeChecker.getTypeOfSymbolAtLocation($emit, symbolNode); | ||
const calls = type.getCallSignatures(); | ||
return calls.map((call) => { | ||
const { resolveEventSignature, } = createSchemaResolvers(typeChecker, symbolNode, checkerOptions, ts, core); | ||
return resolveEventSignature(call); | ||
}).filter(event => event.name); | ||
} | ||
return []; | ||
} | ||
function getSlots() { | ||
const $slots = symbolProperties.find(prop => prop.escapedName === 'slots'); | ||
if ($slots) { | ||
const type = typeChecker.getTypeOfSymbolAtLocation($slots, symbolNode); | ||
const properties = type.getProperties(); | ||
return properties.map((prop) => { | ||
const { resolveSlotProperties, } = createSchemaResolvers(typeChecker, symbolNode, checkerOptions, ts, core); | ||
return resolveSlotProperties(prop); | ||
}); | ||
} | ||
return []; | ||
} | ||
function getExposed() { | ||
const $exposed = symbolProperties.find(prop => prop.escapedName === 'exposed'); | ||
if ($exposed) { | ||
const type = typeChecker.getTypeOfSymbolAtLocation($exposed, symbolNode); | ||
const properties = type.getProperties().filter(prop => | ||
// only exposed props will not have a valueDeclaration | ||
!prop.valueDeclaration); | ||
return properties.map((prop) => { | ||
const { resolveExposedProperties, } = createSchemaResolvers(typeChecker, symbolNode, checkerOptions, ts, core); | ||
return resolveExposedProperties(prop); | ||
}); | ||
} | ||
return []; | ||
} | ||
} | ||
function _getExports(program, typeChecker, componentPath) { | ||
const sourceFile = program?.getSourceFile(getMetaFileName(componentPath)); | ||
if (!sourceFile) { | ||
throw 'Could not find main source file'; | ||
} | ||
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile); | ||
if (!moduleSymbol) { | ||
throw 'Could not find module symbol'; | ||
} | ||
const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol); | ||
let symbolNode; | ||
for (const symbol of exportedSymbols) { | ||
const [declaration] = symbol.getDeclarations() ?? []; | ||
if (ts.isExportAssignment(declaration)) { | ||
symbolNode = declaration.expression; | ||
} | ||
} | ||
if (!symbolNode) { | ||
throw 'Could not find symbol node'; | ||
} | ||
const exportDefaultType = typeChecker.getTypeAtLocation(symbolNode); | ||
const exports = exportDefaultType.getProperties(); | ||
return { | ||
symbolNode, | ||
exports, | ||
}; | ||
} | ||
} | ||
exports.baseCreate = baseCreate; | ||
function createSchemaResolvers(typeChecker, symbolNode, { rawType, schema: options, noDeclarations }, ts, core) { | ||
const visited = new Set(); | ||
function shouldIgnore(subtype) { | ||
const name = typeChecker.typeToString(subtype); | ||
if (name === 'any') { | ||
return true; | ||
} | ||
if (visited.has(subtype)) { | ||
return true; | ||
} | ||
if (typeof options === 'object') { | ||
for (const item of options.ignore ?? []) { | ||
if (typeof item === 'function') { | ||
const result = item(name, subtype, typeChecker); | ||
if (typeof result === 'boolean') | ||
return result; | ||
} | ||
else if (name === item) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
function reducer(acc, cur) { | ||
acc[cur.name] = cur; | ||
return acc; | ||
} | ||
function resolveNestedProperties(prop) { | ||
const subtype = typeChecker.getTypeOfSymbolAtLocation(prop, symbolNode); | ||
let schema; | ||
let declarations; | ||
return { | ||
name: prop.getEscapedName().toString(), | ||
global: false, | ||
description: ts.displayPartsToString(prop.getDocumentationComment(typeChecker)), | ||
tags: prop.getJsDocTags(typeChecker).map(tag => ({ | ||
name: tag.name, | ||
text: tag.text !== undefined ? ts.displayPartsToString(tag.text) : undefined, | ||
})), | ||
required: !(prop.flags & ts.SymbolFlags.Optional), | ||
type: typeChecker.typeToString(subtype), | ||
rawType: rawType ? subtype : undefined, | ||
get declarations() { | ||
return declarations ??= getDeclarations(prop.declarations ?? []); | ||
}, | ||
get schema() { | ||
return schema ??= resolveSchema(subtype); | ||
}, | ||
}; | ||
} | ||
function resolveSlotProperties(prop) { | ||
const propType = typeChecker.getNonNullableType(typeChecker.getTypeOfSymbolAtLocation(prop, symbolNode)); | ||
const signatures = propType.getCallSignatures(); | ||
const paramType = signatures[0].parameters[0]; | ||
const subtype = typeChecker.getTypeOfSymbolAtLocation(paramType, symbolNode); | ||
let schema; | ||
let declarations; | ||
return { | ||
name: prop.getName(), | ||
type: typeChecker.typeToString(subtype), | ||
rawType: rawType ? subtype : undefined, | ||
description: ts.displayPartsToString(prop.getDocumentationComment(typeChecker)), | ||
get declarations() { | ||
return declarations ??= getDeclarations(prop.declarations ?? []); | ||
}, | ||
get schema() { | ||
return schema ??= resolveSchema(subtype); | ||
}, | ||
}; | ||
} | ||
function resolveExposedProperties(expose) { | ||
const subtype = typeChecker.getTypeOfSymbolAtLocation(expose, symbolNode); | ||
let schema; | ||
let declarations; | ||
return { | ||
name: expose.getName(), | ||
type: typeChecker.typeToString(subtype), | ||
rawType: rawType ? subtype : undefined, | ||
description: ts.displayPartsToString(expose.getDocumentationComment(typeChecker)), | ||
get declarations() { | ||
return declarations ??= getDeclarations(expose.declarations ?? []); | ||
}, | ||
get schema() { | ||
return schema ??= resolveSchema(subtype); | ||
}, | ||
}; | ||
} | ||
function resolveEventSignature(call) { | ||
const subtype = typeChecker.getTypeOfSymbolAtLocation(call.parameters[1], symbolNode); | ||
let schema; | ||
let declarations; | ||
return { | ||
name: typeChecker.getTypeOfSymbolAtLocation(call.parameters[0], symbolNode).value, | ||
type: typeChecker.typeToString(subtype), | ||
rawType: rawType ? subtype : undefined, | ||
signature: typeChecker.signatureToString(call), | ||
get declarations() { | ||
return declarations ??= call.declaration ? getDeclarations([call.declaration]) : []; | ||
}, | ||
get schema() { | ||
return schema ??= typeChecker.getTypeArguments(subtype).map(resolveSchema); | ||
}, | ||
}; | ||
} | ||
function resolveCallbackSchema(signature) { | ||
let schema; | ||
return { | ||
kind: 'event', | ||
type: typeChecker.signatureToString(signature), | ||
get schema() { | ||
return schema ??= signature.parameters.length > 0 | ||
? typeChecker | ||
.getTypeArguments(typeChecker.getTypeOfSymbolAtLocation(signature.parameters[0], symbolNode)) | ||
.map(resolveSchema) | ||
: undefined; | ||
}, | ||
}; | ||
} | ||
function resolveSchema(subtype) { | ||
const type = typeChecker.typeToString(subtype); | ||
if (shouldIgnore(subtype)) { | ||
return type; | ||
} | ||
visited.add(subtype); | ||
if (subtype.isUnion()) { | ||
let schema; | ||
return { | ||
kind: 'enum', | ||
type, | ||
get schema() { | ||
return schema ??= subtype.types.map(resolveSchema); | ||
}, | ||
}; | ||
} | ||
// @ts-ignore - typescript internal, isArrayLikeType exists | ||
else if (typeChecker.isArrayLikeType(subtype)) { | ||
let schema; | ||
return { | ||
kind: 'array', | ||
type, | ||
get schema() { | ||
return schema ??= typeChecker.getTypeArguments(subtype).map(resolveSchema); | ||
}, | ||
}; | ||
} | ||
else if (subtype.getCallSignatures().length === 0 && | ||
(subtype.isClassOrInterface() || subtype.isIntersection() || subtype.objectFlags & ts.ObjectFlags.Anonymous)) { | ||
let schema; | ||
return { | ||
kind: 'object', | ||
type, | ||
get schema() { | ||
return schema ??= subtype.getProperties().map(resolveNestedProperties).reduce(reducer, {}); | ||
}, | ||
}; | ||
} | ||
else if (subtype.getCallSignatures().length === 1) { | ||
return resolveCallbackSchema(subtype.getCallSignatures()[0]); | ||
} | ||
return type; | ||
} | ||
function getDeclarations(declaration) { | ||
if (noDeclarations) { | ||
return []; | ||
} | ||
return declaration.map(getDeclaration).filter(d => !!d); | ||
} | ||
function getDeclaration(declaration) { | ||
const fileName = declaration.getSourceFile().fileName; | ||
const [virtualFile] = core.virtualFiles.getVirtualFile(fileName); | ||
if (virtualFile) { | ||
const maps = core.virtualFiles.getMaps(virtualFile); | ||
for (const [source, [_, map]] of maps) { | ||
const start = map.toSourceOffset(declaration.getStart()); | ||
const end = map.toSourceOffset(declaration.getEnd()); | ||
if (start && end) { | ||
return { | ||
file: source, | ||
range: [start[0], end[0]], | ||
}; | ||
} | ||
; | ||
} | ||
return undefined; | ||
} | ||
return { | ||
file: declaration.getSourceFile().fileName, | ||
range: [declaration.getStart(), declaration.getEnd()], | ||
}; | ||
} | ||
return { | ||
resolveNestedProperties, | ||
resolveSlotProperties, | ||
resolveEventSignature, | ||
resolveExposedProperties, | ||
resolveSchema, | ||
}; | ||
} | ||
function readVueComponentDefaultProps(vueSourceFile, printer, ts, vueCompilerOptions) { | ||
let result = {}; | ||
scriptSetupWorker(); | ||
scriptWorker(); | ||
return result; | ||
function scriptSetupWorker() { | ||
const descriptor = vueSourceFile.sfc; | ||
const scriptSetupRanges = descriptor.scriptSetup ? vue.parseScriptSetupRanges(ts, descriptor.scriptSetup.ast, vueCompilerOptions) : undefined; | ||
if (descriptor.scriptSetup && scriptSetupRanges?.props.withDefaults?.arg) { | ||
const defaultsText = descriptor.scriptSetup.content.substring(scriptSetupRanges.props.withDefaults.arg.start, scriptSetupRanges.props.withDefaults.arg.end); | ||
const ast = ts.createSourceFile('/tmp.' + descriptor.scriptSetup.lang, '(' + defaultsText + ')', ts.ScriptTarget.Latest); | ||
const obj = findObjectLiteralExpression(ast); | ||
if (obj) { | ||
for (const prop of obj.properties) { | ||
if (ts.isPropertyAssignment(prop)) { | ||
const name = prop.name.getText(ast); | ||
const expNode = resolveDefaultOptionExpression(prop.initializer, ts); | ||
const expText = printer?.printNode(ts.EmitHint.Expression, expNode, ast) ?? expNode.getText(ast); | ||
result[name] = { | ||
default: expText, | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
else if (descriptor.scriptSetup && scriptSetupRanges?.props.define?.arg) { | ||
const defaultsText = descriptor.scriptSetup.content.substring(scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end); | ||
const ast = ts.createSourceFile('/tmp.' + descriptor.scriptSetup.lang, '(' + defaultsText + ')', ts.ScriptTarget.Latest); | ||
const obj = findObjectLiteralExpression(ast); | ||
if (obj) { | ||
result = { | ||
...result, | ||
...resolvePropsOption(ast, obj, printer, ts), | ||
}; | ||
} | ||
} | ||
function findObjectLiteralExpression(node) { | ||
if (ts.isObjectLiteralExpression(node)) { | ||
return node; | ||
} | ||
let result; | ||
node.forEachChild(child => { | ||
if (!result) { | ||
result = findObjectLiteralExpression(child); | ||
} | ||
}); | ||
return result; | ||
} | ||
} | ||
function scriptWorker() { | ||
const descriptor = vueSourceFile.sfc; | ||
if (descriptor.script) { | ||
const scriptResult = readTsComponentDefaultProps(descriptor.script.lang, descriptor.script.content, 'default', printer, ts); | ||
for (const [key, value] of Object.entries(scriptResult)) { | ||
result[key] = value; | ||
} | ||
} | ||
} | ||
} | ||
function readTsComponentDefaultProps(lang, tsFileText, exportName, printer, ts) { | ||
const ast = ts.createSourceFile('/tmp.' + lang, tsFileText, ts.ScriptTarget.Latest); | ||
const props = getPropsNode(); | ||
if (props) { | ||
return resolvePropsOption(ast, props, printer, ts); | ||
} | ||
return {}; | ||
function getComponentNode() { | ||
let result; | ||
if (exportName === 'default') { | ||
ast.forEachChild(child => { | ||
if (ts.isExportAssignment(child)) { | ||
result = child.expression; | ||
} | ||
}); | ||
} | ||
else { | ||
ast.forEachChild(child => { | ||
if (ts.isVariableStatement(child) | ||
&& child.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword)) { | ||
for (const dec of child.declarationList.declarations) { | ||
if (dec.name.getText(ast) === exportName) { | ||
result = dec.initializer; | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
return result; | ||
} | ||
function getComponentOptionsNode() { | ||
const component = getComponentNode(); | ||
if (component) { | ||
// export default { ... } | ||
if (ts.isObjectLiteralExpression(component)) { | ||
return component; | ||
} | ||
// export default defineComponent({ ... }) | ||
// export default Vue.extend({ ... }) | ||
else if (ts.isCallExpression(component)) { | ||
if (component.arguments.length) { | ||
const arg = component.arguments[0]; | ||
if (ts.isObjectLiteralExpression(arg)) { | ||
return arg; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function getPropsNode() { | ||
const options = getComponentOptionsNode(); | ||
const props = options?.properties.find(prop => prop.name?.getText(ast) === 'props'); | ||
if (props && ts.isPropertyAssignment(props)) { | ||
if (ts.isObjectLiteralExpression(props.initializer)) { | ||
return props.initializer; | ||
} | ||
} | ||
} | ||
} | ||
function resolvePropsOption(ast, props, printer, ts) { | ||
const result = {}; | ||
for (const prop of props.properties) { | ||
if (ts.isPropertyAssignment(prop)) { | ||
const name = prop.name?.getText(ast); | ||
if (ts.isObjectLiteralExpression(prop.initializer)) { | ||
const defaultProp = prop.initializer.properties.find(p => ts.isPropertyAssignment(p) && p.name.getText(ast) === 'default'); | ||
const requiredProp = prop.initializer.properties.find(p => ts.isPropertyAssignment(p) && p.name.getText(ast) === 'required'); | ||
result[name] = {}; | ||
if (requiredProp) { | ||
const exp = requiredProp.initializer.getText(ast); | ||
result[name].required = exp === 'true'; | ||
} | ||
if (defaultProp) { | ||
const expNode = resolveDefaultOptionExpression(defaultProp.initializer, ts); | ||
const expText = printer?.printNode(ts.EmitHint.Expression, expNode, ast) ?? expNode.getText(ast); | ||
result[name].default = expText; | ||
} | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
function resolveDefaultOptionExpression(_default, ts) { | ||
if (ts.isArrowFunction(_default)) { | ||
if (ts.isBlock(_default.body)) { | ||
return _default; // TODO | ||
} | ||
else if (ts.isParenthesizedExpression(_default.body)) { | ||
return _default.body.expression; | ||
} | ||
else { | ||
return _default.body; | ||
} | ||
} | ||
return _default; | ||
} | ||
exports.createChecker = createChecker; | ||
//# sourceMappingURL=index.js.map |
import type * as ts from 'typescript/lib/tsserverlibrary'; | ||
export type Checker = ReturnType<typeof import('./base')['baseCreate']>; | ||
export interface Declaration { | ||
@@ -3,0 +4,0 @@ file: string; |
{ | ||
"name": "vue-component-meta", | ||
"version": "1.8.22", | ||
"version": "1.9.0-alpha.0", | ||
"main": "out/index.js", | ||
@@ -16,6 +16,6 @@ "license": "MIT", | ||
"dependencies": { | ||
"@volar/typescript": "~1.10.5", | ||
"@vue/language-core": "1.8.22", | ||
"typesafe-path": "^0.2.2", | ||
"vue-component-type-helpers": "1.8.22" | ||
"@volar/typescript": "~1.10.9", | ||
"@vue/language-core": "1.9.0-alpha.0", | ||
"path-browserify": "^1.0.1", | ||
"vue-component-type-helpers": "1.9.0-alpha.0" | ||
}, | ||
@@ -30,3 +30,7 @@ "peerDependencies": { | ||
}, | ||
"gitHead": "1e8d09af0282c42dd816671ffcd5a2321276e3c3" | ||
"devDependencies": { | ||
"@types/node": "latest", | ||
"@types/path-browserify": "latest" | ||
}, | ||
"gitHead": "d6905e4d4d1208f560a544509494f33f431b602a" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
41770
9
859
2
2
1
+ Addedpath-browserify@^1.0.1
+ Added@vue/language-core@1.9.0-alpha.0(transitive)
+ Addedvue-component-type-helpers@1.9.0-alpha.0(transitive)
- Removedtypesafe-path@^0.2.2
- Removed@vue/language-core@1.8.22(transitive)
- Removedtypesafe-path@0.2.2(transitive)
- Removedvue-component-type-helpers@1.8.22(transitive)
Updated@volar/typescript@~1.10.9