Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
463 changes: 162 additions & 301 deletions packages/component-meta/lib/base.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/component-meta/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) =>
expect(bar).toMatchInlineSnapshot(`
{
"declarations": [],
"default": ""BAR"",
"default": "'BAR'",
"description": "",
"getTypeObject": [Function],
"global": false,
Expand Down Expand Up @@ -1459,7 +1459,7 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) =>
{
"declarations": [],
"default": "{
foo: "bar",
foo: 'bar',
}",
"description": "Default function Object",
"getTypeObject": [Function],
Expand Down
1 change: 1 addition & 0 deletions packages/language-core/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './lib/codegen/template';
export * from './lib/compilerOptions';
export * from './lib/languagePlugin';
export * from './lib/parsers/scriptRanges';
export * from './lib/parsers/scriptSetupRanges';
export * from './lib/plugins';
export * from './lib/types';
Expand Down
8 changes: 4 additions & 4 deletions packages/language-core/lib/codegen/script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function* generateWorker(

// <script>
let selfType: string | undefined;
const { exportDefault } = scriptRanges;
const exportDefault = scriptRanges.exports.default;
if (exportDefault) {
yield* generateScriptWithExportDefault(
ctx,
Expand Down Expand Up @@ -154,7 +154,7 @@ function* generateWorker(
}
// only <script>
else if (script && scriptRanges) {
const { exportDefault } = scriptRanges;
const exportDefault = scriptRanges.exports.default;
if (exportDefault) {
yield* generateScriptWithExportDefault(
ctx,
Expand Down Expand Up @@ -182,12 +182,12 @@ function* generateScriptWithExportDefault(
ctx: ScriptCodegenContext,
script: NonNullable<Sfc['script']>,
scriptRanges: ScriptRanges,
exportDefault: NonNullable<ScriptRanges['exportDefault']>,
exportDefault: NonNullable<ScriptRanges['exports'][string]>,
vueCompilerOptions: VueCompilerOptions,
varName: string,
templateGenerator?: Generator<Code>,
): Generator<Code> {
const { componentOptions } = scriptRanges;
const componentOptions = scriptRanges.exports.default?.options;
const { expression, isObjectLiteral } = componentOptions ?? exportDefault;

let wrapLeft: string | undefined;
Expand Down
8 changes: 4 additions & 4 deletions packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ function* generateTemplateComponents(
if (ctx.generatedTypes.has(names.SetupExposed)) {
types.push(names.SetupExposed);
}
if (script && scriptRanges?.componentOptions?.components) {
const { components } = scriptRanges.componentOptions;
if (script && scriptRanges?.exports.default?.options?.components) {
const { components } = scriptRanges.exports.default.options;
yield `const __VLS_componentsOption = `;
yield* generateSfcBlockSection(
script,
Expand Down Expand Up @@ -124,8 +124,8 @@ function* generateTemplateDirectives(
if (ctx.generatedTypes.has(names.SetupExposed)) {
types.push(names.SetupExposed);
}
if (script && scriptRanges?.componentOptions?.directives) {
const { directives } = scriptRanges.componentOptions;
if (script && scriptRanges?.exports.default?.options?.directives) {
const { directives } = scriptRanges.exports.default.options;
yield `const __VLS_directivesOption = `;
yield* generateSfcBlockSection(
script,
Expand Down
198 changes: 122 additions & 76 deletions packages/language-core/lib/parsers/scriptRanges.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type * as ts from 'typescript';
import { names } from '../..';
import type { TextRange, VueCompilerOptions } from '../types';
import { getNodeText, getStartEnd } from '../utils/shared';
import { getClosestMultiLineCommentRange, parseBindingRanges } from './utils';
Expand All @@ -7,108 +8,153 @@ export interface ScriptRanges extends ReturnType<typeof parseScriptRanges> {}

export function parseScriptRanges(
ts: typeof import('typescript'),
ast: ts.SourceFile,
sourceFile: ts.SourceFile,
vueCompilerOptions: VueCompilerOptions,
) {
let exportDefault:
| TextRange & {
const _exports: Record<
'default' | string,
TextRange & {
expression: TextRange;
isObjectLiteral: boolean;
}
| undefined;
let componentOptions:
| {
isObjectLiteral: boolean;
expression: TextRange;
args: TextRange;
argsNode: ts.ObjectLiteralExpression;
components: TextRange | undefined;
componentsNode: ts.ObjectLiteralExpression | undefined;
directives: TextRange | undefined;
name: TextRange | undefined;
inheritAttrs: string | undefined;
}
| undefined;
options?: {
isObjectLiteral: boolean;
expression: TextRange;
args: TextRange<ts.ObjectLiteralExpression>;
components: TextRange<ts.ObjectLiteralExpression> | undefined;
directives: TextRange | undefined;
name: TextRange | undefined;
inheritAttrs: string | undefined;
};
} | undefined
> = {};

const { bindings, components } = parseBindingRanges(ts, ast, vueCompilerOptions.extensions);
const { bindings, components } = parseBindingRanges(ts, sourceFile, vueCompilerOptions.extensions);

ts.forEachChild(ast, raw => {
if (ts.isExportAssignment(raw)) {
exportDefault = {
..._getStartEnd(raw),
expression: _getStartEnd(raw.expression),
isObjectLiteral: ts.isObjectLiteralExpression(raw.expression),
ts.forEachChild(sourceFile, child => {
// export default ...
if (ts.isExportAssignment(child)) {
_exports.default = {
..._getStartEnd(child),
expression: _getStartEnd(child.expression),
isObjectLiteral: ts.isObjectLiteralExpression(child.expression),
options: getOptions(child.expression),
};
const comment = getClosestMultiLineCommentRange(ts, raw, [], ast);
const comment = getClosestMultiLineCommentRange(ts, child, [], sourceFile);
if (comment) {
exportDefault.start = comment.start;
_exports.default.start = comment.start;
}

let node: ts.AsExpression | ts.ExportAssignment | ts.ParenthesizedExpression = raw;
while (isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882
node = node.expression;
}

let obj: ts.ObjectLiteralExpression | undefined;
if (ts.isObjectLiteralExpression(node.expression)) {
obj = node.expression;
}
else if (ts.isCallExpression(node.expression) && node.expression.arguments.length) {
const arg0 = node.expression.arguments[0]!;
if (ts.isObjectLiteralExpression(arg0)) {
obj = arg0;
}
}
if (obj) {
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
let directivesOptionNode: ts.ObjectLiteralExpression | undefined;
let nameOptionNode: ts.Expression | undefined;
let inheritAttrsOption: string | undefined;
ts.forEachChild(obj, node => {
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
const name = _getNodeText(node.name);
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
componentsOptionNode = node.initializer;
}
else if (name === 'directives' && ts.isObjectLiteralExpression(node.initializer)) {
directivesOptionNode = node.initializer;
}
else if (name === 'name') {
nameOptionNode = node.initializer;
}
else if (name === 'inheritAttrs') {
inheritAttrsOption = _getNodeText(node.initializer);
// const __VLS_export = ...
const expressionText = sourceFile.text.slice(_exports.default.expression.start, _exports.default.expression.end);
if (expressionText.includes(names._export)) {
let exportExp: ts.Expression | undefined;
ts.forEachChild(sourceFile, child2 => {
if (ts.isVariableStatement(child2)) {
for (const decl of child2.declarationList.declarations) {
if (!ts.isIdentifier(decl.name)) {
continue;
}
if (getNodeText(ts, decl.name, sourceFile) === names._export && decl.initializer) {
exportExp = decl.initializer;
}
}
}
});
componentOptions = {
isObjectLiteral: ts.isObjectLiteralExpression(node.expression),
expression: _getStartEnd(node.expression),
args: _getStartEnd(obj),
argsNode: obj,
components: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
componentsNode: componentsOptionNode,
directives: directivesOptionNode ? _getStartEnd(directivesOptionNode) : undefined,
name: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
inheritAttrs: inheritAttrsOption,
if (exportExp) {
_exports.default.expression = _getStartEnd(exportExp);
_exports.default.isObjectLiteral = ts.isObjectLiteralExpression(exportExp);
_exports.default.options = getOptions(exportExp);
}
}
}

// export const Foo = ...
if (ts.isVariableStatement(child) && child.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
for (const decl of child.declarationList.declarations) {
if (!ts.isIdentifier(decl.name)) {
continue;
}
const exportVar = getNodeText(ts, decl.name, sourceFile);
let node = decl.initializer;
if (!node) {
continue;
}
_exports[exportVar] = {
..._getStartEnd(decl),
expression: _getStartEnd(node),
isObjectLiteral: ts.isObjectLiteralExpression(node),
options: getOptions(node),
};
}
}
});

return {
exportDefault,
componentOptions,
exports: _exports,
bindings,
components,
};

function _getStartEnd(node: ts.Node) {
return getStartEnd(ts, node, ast);
function getOptions(exp: ts.Node) {
let obj: ts.ObjectLiteralExpression | undefined;

while (isAsExpression(exp) || ts.isParenthesizedExpression(exp)) { // fix https://github.com/vuejs/language-tools/issues/1882
exp = exp.expression;
}

if (ts.isObjectLiteralExpression(exp)) {
obj = exp;
}
else if (ts.isCallExpression(exp) && exp.arguments.length) {
const arg0 = exp.arguments[0]!;
if (ts.isObjectLiteralExpression(arg0)) {
obj = arg0;
}
}

if (obj) {
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
let directivesOptionNode: ts.ObjectLiteralExpression | undefined;
let nameOptionNode: ts.Expression | undefined;
let inheritAttrsOption: string | undefined;
ts.forEachChild(obj, node => {
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
const name = _getNodeText(node.name);
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
componentsOptionNode = node.initializer;
}
else if (name === 'directives' && ts.isObjectLiteralExpression(node.initializer)) {
directivesOptionNode = node.initializer;
}
else if (name === 'name') {
nameOptionNode = node.initializer;
}
else if (name === 'inheritAttrs') {
inheritAttrsOption = _getNodeText(node.initializer);
}
}
});
return {
isObjectLiteral: ts.isObjectLiteralExpression(exp),
expression: _getStartEnd(exp),
args: _getStartEnd(obj),
argsNode: obj,
components: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
componentsNode: componentsOptionNode,
directives: directivesOptionNode ? _getStartEnd(directivesOptionNode) : undefined,
name: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
nameNode: nameOptionNode,
inheritAttrs: inheritAttrsOption,
};
}
}

function _getStartEnd<T extends ts.Node>(node: T): TextRange<T> {
return getStartEnd(ts, node, sourceFile);
}

function _getNodeText(node: ts.Node) {
return getNodeText(ts, node, ast);
return getNodeText(ts, node, sourceFile);
}

// isAsExpression is missing in tsc
Expand Down
Loading