Skip to content

Commit 3af4903

Browse files
refactor(component-meta): redundant logic between deduplication and language-core (#5875)
1 parent eab878d commit 3af4903

File tree

12 files changed

+346
-453
lines changed

12 files changed

+346
-453
lines changed

packages/component-meta/lib/base.ts

Lines changed: 162 additions & 301 deletions
Large diffs are not rendered by default.

packages/component-meta/tests/index.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) =>
661661
expect(bar).toMatchInlineSnapshot(`
662662
{
663663
"declarations": [],
664-
"default": ""BAR"",
664+
"default": "'BAR'",
665665
"description": "",
666666
"getTypeObject": [Function],
667667
"global": false,
@@ -1459,7 +1459,7 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) =>
14591459
{
14601460
"declarations": [],
14611461
"default": "{
1462-
foo: "bar",
1462+
foo: 'bar',
14631463
}",
14641464
"description": "Default function Object",
14651465
"getTypeObject": [Function],

packages/language-core/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './lib/codegen/template';
22
export * from './lib/compilerOptions';
33
export * from './lib/languagePlugin';
4+
export * from './lib/parsers/scriptRanges';
45
export * from './lib/parsers/scriptSetupRanges';
56
export * from './lib/plugins';
67
export * from './lib/types';

packages/language-core/lib/codegen/script/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function* generateWorker(
7171

7272
// <script>
7373
let selfType: string | undefined;
74-
const { exportDefault } = scriptRanges;
74+
const exportDefault = scriptRanges.exports.default;
7575
if (exportDefault) {
7676
yield* generateScriptWithExportDefault(
7777
ctx,
@@ -154,7 +154,7 @@ function* generateWorker(
154154
}
155155
// only <script>
156156
else if (script && scriptRanges) {
157-
const { exportDefault } = scriptRanges;
157+
const exportDefault = scriptRanges.exports.default;
158158
if (exportDefault) {
159159
yield* generateScriptWithExportDefault(
160160
ctx,
@@ -182,12 +182,12 @@ function* generateScriptWithExportDefault(
182182
ctx: ScriptCodegenContext,
183183
script: NonNullable<Sfc['script']>,
184184
scriptRanges: ScriptRanges,
185-
exportDefault: NonNullable<ScriptRanges['exportDefault']>,
185+
exportDefault: NonNullable<ScriptRanges['exports'][string]>,
186186
vueCompilerOptions: VueCompilerOptions,
187187
varName: string,
188188
templateGenerator?: Generator<Code>,
189189
): Generator<Code> {
190-
const { componentOptions } = scriptRanges;
190+
const componentOptions = scriptRanges.exports.default?.options;
191191
const { expression, isObjectLiteral } = componentOptions ?? exportDefault;
192192

193193
let wrapLeft: string | undefined;

packages/language-core/lib/codegen/script/template.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ function* generateTemplateComponents(
8888
if (ctx.generatedTypes.has(names.SetupExposed)) {
8989
types.push(names.SetupExposed);
9090
}
91-
if (script && scriptRanges?.componentOptions?.components) {
92-
const { components } = scriptRanges.componentOptions;
91+
if (script && scriptRanges?.exports.default?.options?.components) {
92+
const { components } = scriptRanges.exports.default.options;
9393
yield `const __VLS_componentsOption = `;
9494
yield* generateSfcBlockSection(
9595
script,
@@ -124,8 +124,8 @@ function* generateTemplateDirectives(
124124
if (ctx.generatedTypes.has(names.SetupExposed)) {
125125
types.push(names.SetupExposed);
126126
}
127-
if (script && scriptRanges?.componentOptions?.directives) {
128-
const { directives } = scriptRanges.componentOptions;
127+
if (script && scriptRanges?.exports.default?.options?.directives) {
128+
const { directives } = scriptRanges.exports.default.options;
129129
yield `const __VLS_directivesOption = `;
130130
yield* generateSfcBlockSection(
131131
script,

packages/language-core/lib/parsers/scriptRanges.ts

Lines changed: 122 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type * as ts from 'typescript';
2+
import { names } from '../..';
23
import type { TextRange, VueCompilerOptions } from '../types';
34
import { getNodeText, getStartEnd } from '../utils/shared';
45
import { getClosestMultiLineCommentRange, parseBindingRanges } from './utils';
@@ -7,108 +8,153 @@ export interface ScriptRanges extends ReturnType<typeof parseScriptRanges> {}
78

89
export function parseScriptRanges(
910
ts: typeof import('typescript'),
10-
ast: ts.SourceFile,
11+
sourceFile: ts.SourceFile,
1112
vueCompilerOptions: VueCompilerOptions,
1213
) {
13-
let exportDefault:
14-
| TextRange & {
14+
const _exports: Record<
15+
'default' | string,
16+
TextRange & {
1517
expression: TextRange;
1618
isObjectLiteral: boolean;
17-
}
18-
| undefined;
19-
let componentOptions:
20-
| {
21-
isObjectLiteral: boolean;
22-
expression: TextRange;
23-
args: TextRange;
24-
argsNode: ts.ObjectLiteralExpression;
25-
components: TextRange | undefined;
26-
componentsNode: ts.ObjectLiteralExpression | undefined;
27-
directives: TextRange | undefined;
28-
name: TextRange | undefined;
29-
inheritAttrs: string | undefined;
30-
}
31-
| undefined;
19+
options?: {
20+
isObjectLiteral: boolean;
21+
expression: TextRange;
22+
args: TextRange<ts.ObjectLiteralExpression>;
23+
components: TextRange<ts.ObjectLiteralExpression> | undefined;
24+
directives: TextRange | undefined;
25+
name: TextRange | undefined;
26+
inheritAttrs: string | undefined;
27+
};
28+
} | undefined
29+
> = {};
3230

33-
const { bindings, components } = parseBindingRanges(ts, ast, vueCompilerOptions.extensions);
31+
const { bindings, components } = parseBindingRanges(ts, sourceFile, vueCompilerOptions.extensions);
3432

35-
ts.forEachChild(ast, raw => {
36-
if (ts.isExportAssignment(raw)) {
37-
exportDefault = {
38-
..._getStartEnd(raw),
39-
expression: _getStartEnd(raw.expression),
40-
isObjectLiteral: ts.isObjectLiteralExpression(raw.expression),
33+
ts.forEachChild(sourceFile, child => {
34+
// export default ...
35+
if (ts.isExportAssignment(child)) {
36+
_exports.default = {
37+
..._getStartEnd(child),
38+
expression: _getStartEnd(child.expression),
39+
isObjectLiteral: ts.isObjectLiteralExpression(child.expression),
40+
options: getOptions(child.expression),
4141
};
42-
const comment = getClosestMultiLineCommentRange(ts, raw, [], ast);
42+
const comment = getClosestMultiLineCommentRange(ts, child, [], sourceFile);
4343
if (comment) {
44-
exportDefault.start = comment.start;
44+
_exports.default.start = comment.start;
4545
}
4646

47-
let node: ts.AsExpression | ts.ExportAssignment | ts.ParenthesizedExpression = raw;
48-
while (isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882
49-
node = node.expression;
50-
}
51-
52-
let obj: ts.ObjectLiteralExpression | undefined;
53-
if (ts.isObjectLiteralExpression(node.expression)) {
54-
obj = node.expression;
55-
}
56-
else if (ts.isCallExpression(node.expression) && node.expression.arguments.length) {
57-
const arg0 = node.expression.arguments[0]!;
58-
if (ts.isObjectLiteralExpression(arg0)) {
59-
obj = arg0;
60-
}
61-
}
62-
if (obj) {
63-
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
64-
let directivesOptionNode: ts.ObjectLiteralExpression | undefined;
65-
let nameOptionNode: ts.Expression | undefined;
66-
let inheritAttrsOption: string | undefined;
67-
ts.forEachChild(obj, node => {
68-
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
69-
const name = _getNodeText(node.name);
70-
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
71-
componentsOptionNode = node.initializer;
72-
}
73-
else if (name === 'directives' && ts.isObjectLiteralExpression(node.initializer)) {
74-
directivesOptionNode = node.initializer;
75-
}
76-
else if (name === 'name') {
77-
nameOptionNode = node.initializer;
78-
}
79-
else if (name === 'inheritAttrs') {
80-
inheritAttrsOption = _getNodeText(node.initializer);
47+
// const __VLS_export = ...
48+
const expressionText = sourceFile.text.slice(_exports.default.expression.start, _exports.default.expression.end);
49+
if (expressionText.includes(names._export)) {
50+
let exportExp: ts.Expression | undefined;
51+
ts.forEachChild(sourceFile, child2 => {
52+
if (ts.isVariableStatement(child2)) {
53+
for (const decl of child2.declarationList.declarations) {
54+
if (!ts.isIdentifier(decl.name)) {
55+
continue;
56+
}
57+
if (getNodeText(ts, decl.name, sourceFile) === names._export && decl.initializer) {
58+
exportExp = decl.initializer;
59+
}
8160
}
8261
}
8362
});
84-
componentOptions = {
85-
isObjectLiteral: ts.isObjectLiteralExpression(node.expression),
86-
expression: _getStartEnd(node.expression),
87-
args: _getStartEnd(obj),
88-
argsNode: obj,
89-
components: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
90-
componentsNode: componentsOptionNode,
91-
directives: directivesOptionNode ? _getStartEnd(directivesOptionNode) : undefined,
92-
name: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
93-
inheritAttrs: inheritAttrsOption,
63+
if (exportExp) {
64+
_exports.default.expression = _getStartEnd(exportExp);
65+
_exports.default.isObjectLiteral = ts.isObjectLiteralExpression(exportExp);
66+
_exports.default.options = getOptions(exportExp);
67+
}
68+
}
69+
}
70+
71+
// export const Foo = ...
72+
if (ts.isVariableStatement(child) && child.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
73+
for (const decl of child.declarationList.declarations) {
74+
if (!ts.isIdentifier(decl.name)) {
75+
continue;
76+
}
77+
const exportVar = getNodeText(ts, decl.name, sourceFile);
78+
let node = decl.initializer;
79+
if (!node) {
80+
continue;
81+
}
82+
_exports[exportVar] = {
83+
..._getStartEnd(decl),
84+
expression: _getStartEnd(node),
85+
isObjectLiteral: ts.isObjectLiteralExpression(node),
86+
options: getOptions(node),
9487
};
9588
}
9689
}
9790
});
9891

9992
return {
100-
exportDefault,
101-
componentOptions,
93+
exports: _exports,
10294
bindings,
10395
components,
10496
};
10597

106-
function _getStartEnd(node: ts.Node) {
107-
return getStartEnd(ts, node, ast);
98+
function getOptions(exp: ts.Node) {
99+
let obj: ts.ObjectLiteralExpression | undefined;
100+
101+
while (isAsExpression(exp) || ts.isParenthesizedExpression(exp)) { // fix https://github.com/vuejs/language-tools/issues/1882
102+
exp = exp.expression;
103+
}
104+
105+
if (ts.isObjectLiteralExpression(exp)) {
106+
obj = exp;
107+
}
108+
else if (ts.isCallExpression(exp) && exp.arguments.length) {
109+
const arg0 = exp.arguments[0]!;
110+
if (ts.isObjectLiteralExpression(arg0)) {
111+
obj = arg0;
112+
}
113+
}
114+
115+
if (obj) {
116+
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
117+
let directivesOptionNode: ts.ObjectLiteralExpression | undefined;
118+
let nameOptionNode: ts.Expression | undefined;
119+
let inheritAttrsOption: string | undefined;
120+
ts.forEachChild(obj, node => {
121+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
122+
const name = _getNodeText(node.name);
123+
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
124+
componentsOptionNode = node.initializer;
125+
}
126+
else if (name === 'directives' && ts.isObjectLiteralExpression(node.initializer)) {
127+
directivesOptionNode = node.initializer;
128+
}
129+
else if (name === 'name') {
130+
nameOptionNode = node.initializer;
131+
}
132+
else if (name === 'inheritAttrs') {
133+
inheritAttrsOption = _getNodeText(node.initializer);
134+
}
135+
}
136+
});
137+
return {
138+
isObjectLiteral: ts.isObjectLiteralExpression(exp),
139+
expression: _getStartEnd(exp),
140+
args: _getStartEnd(obj),
141+
argsNode: obj,
142+
components: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
143+
componentsNode: componentsOptionNode,
144+
directives: directivesOptionNode ? _getStartEnd(directivesOptionNode) : undefined,
145+
name: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
146+
nameNode: nameOptionNode,
147+
inheritAttrs: inheritAttrsOption,
148+
};
149+
}
150+
}
151+
152+
function _getStartEnd<T extends ts.Node>(node: T): TextRange<T> {
153+
return getStartEnd(ts, node, sourceFile);
108154
}
109155

110156
function _getNodeText(node: ts.Node) {
111-
return getNodeText(ts, node, ast);
157+
return getNodeText(ts, node, sourceFile);
112158
}
113159

114160
// isAsExpression is missing in tsc

0 commit comments

Comments
 (0)