Skip to content

Commit

Permalink
fix: avoid declaring component as variable
Browse files Browse the repository at this point in the history
close #2030
  • Loading branch information
johnsoncodehk committed Oct 23, 2022
1 parent 1c23fe5 commit 440d6fc
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 42 deletions.
95 changes: 64 additions & 31 deletions vue-language-tools/vue-language-core/src/generators/template.ts
Expand Up @@ -153,6 +153,8 @@ export function generate(

const data: Record<string, string> = {};

codeGen.push(`let __VLS_templateComponents!: {\n`);

for (const tagName in tagNames) {

if (isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, tagName))
Expand All @@ -162,19 +164,36 @@ export function generate(
if (isNamespacedTag)
continue;

const names = new Set([
// order is important: https://github.com/johnsoncodehk/volar/issues/2010
capitalize(camelize(tagName)),
camelize(tagName),
tagName,
]);
const varName = capitalize(camelize(tagName.replace(/:/g, '-')));

codeGen.push(`${varName}: import('./__VLS_types.js').GetComponents<typeof __VLS_components, ${[...names].map(name => `'${name}'`).join(', ')}>;\n`);

data[tagName] = varName;
}

codeGen.push(`};\n`);

for (const tagName in tagNames) {

const varName = data[tagName];
if (!varName)
continue;

const tagOffsets = tagNames[tagName];
const tagRanges: [number, number][] = tagOffsets.map(offset => [offset, offset + tagName.length]);
const var_componentVar = capitalize(camelize(tagName.replace(/:/g, '-')));

const names = new Set([
// order is important: https://github.com/johnsoncodehk/volar/issues/2010
capitalize(camelize(tagName)),
camelize(tagName),
tagName,
]);

codeGen.push(`let ${var_componentVar}!: import('./__VLS_types.js').GetComponents<typeof __VLS_components, ${[...names].map(name => `'${name}'`).join(', ')}>;\n`);

for (const name of names) {
for (const tagRange of tagRanges) {
codeGen.push('__VLS_components');
Expand All @@ -194,7 +213,22 @@ export function generate(
}
codeGen.push('\n');

data[tagName] = var_componentVar;
codeGen.push(`[`);
for (const tagRange of tagRanges) {
codeGen.push([
varName,
'template',
tagRange,
{
completion: {
additional: true,
autoImportOnly: true,
},
},
]);
codeGen.push(',');
}
codeGen.push(`];\n`);
}

return data;
Expand Down Expand Up @@ -422,7 +456,6 @@ export function generate(

const _isIntrinsicElement = isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, node.tag);
const _isNamespacedTag = node.tag.indexOf('.') >= 0;
const tagText = componentVars[node.tag] ?? node.tag;

if (vueCompilerOptions.jsxTemplates) {

Expand All @@ -438,8 +471,11 @@ export function generate(
};

codeGen.push(`<`);
if (componentVars[node.tag]) {
codeGen.push(`__VLS_templateComponents.`);
}
codeGen.push([
tagText,
componentVars[node.tag] ?? node.tag,
'template',
[startTagOffset, startTagOffset + node.tag.length],
tagCapabilities,
Expand All @@ -453,8 +489,11 @@ export function generate(
}
else {
codeGen.push(`></`);
if (componentVars[node.tag]) {
codeGen.push(`__VLS_templateComponents.`);
}
codeGen.push([
tagText,
componentVars[node.tag] ?? node.tag,
'template',
[endTagOffset, endTagOffset + node.tag.length],
tagCapabilities,
Expand Down Expand Up @@ -512,10 +551,10 @@ export function generate(
}
else if (_isNamespacedTag) {

codeGen.push(`let ${var_props}!: import('./__VLS_types.js').ComponentProps<typeof ${tagText}>;\n`);
codeGen.push(`let ${var_props}!: import('./__VLS_types.js').ComponentProps<typeof ${node.tag}>;\n`);

codeGen.push([
tagText,
node.tag,
'template',
[startTagOffset, startTagOffset + node.tag.length],
capabilitiesSet.all,
Expand All @@ -524,7 +563,7 @@ export function generate(

if (endTagOffset !== undefined) {
codeGen.push([
tagText,
node.tag,
'template',
[endTagOffset, endTagOffset + node.tag.length],
capabilitiesSet.all,
Expand All @@ -535,32 +574,26 @@ export function generate(
else {

codeGen.push(`let ${var_props}!: import('./__VLS_types.js').ComponentProps<typeof `);
if (componentVars[node.tag]) {
codeGen.push(`__VLS_templateComponents.`);
}
codeGen.push([
tagText,
componentVars[node.tag] ?? node.tag,
'template',
[startTagOffset, startTagOffset + node.tag.length],
{
...capabilitiesSet.tagHover,
completion: {
additional: true,
autoImportOnly: true,
},
},
capabilitiesSet.tagHover,
]);
codeGen.push(`>;\n`);

if (endTagOffset !== undefined) {
if (componentVars[node.tag]) {
codeGen.push(`__VLS_templateComponents.`);
}
codeGen.push([
tagText,
componentVars[node.tag] ?? node.tag,
'template',
[endTagOffset, endTagOffset + node.tag.length],
{
...capabilitiesSet.tagHover,
completion: {
additional: true,
autoImportOnly: true,
},
},
capabilitiesSet.tagHover,
]);
codeGen.push(`;\n`);
}
Expand Down Expand Up @@ -675,9 +708,9 @@ export function generate(
codeGen.push(`JSX.IntrinsicElements['${node.tag}'];\n`);
}
else {
codeGen.push(`import('./__VLS_types.js').InstanceProps<typeof ${varComponentInstance}, ${componentVar ? 'typeof ' + componentVar : '{}'}>;\n`);;
codeGen.push(`import('./__VLS_types.js').InstanceProps<typeof ${varComponentInstance}, ${componentVar ? 'typeof __VLS_templateComponents.' + componentVar : '{}'}>;\n`);;
}
codeGen.push(`const __VLS_${elementIndex++}: import('./__VLS_types.js').EventObject<typeof ${varComponentInstance}, '${prop.arg.loc.source}', ${componentVar ? 'typeof ' + componentVar : '{}'}, `);
codeGen.push(`const __VLS_${elementIndex++}: import('./__VLS_types.js').EventObject<typeof ${varComponentInstance}, '${prop.arg.loc.source}', ${componentVar ? 'typeof __VLS_templateComponents.' + componentVar : '{}'}, `);

codeGen.push(`${varInstanceProps}[`);
writeCodeWithQuotes(
Expand Down Expand Up @@ -1237,10 +1270,10 @@ export function generate(
const varSlots = `__VLS_${elementIndex++}`;

if (componentVar && parentEl) {
codeGen.push(`const ${varComponentInstanceA} = new ${componentVar}({ `);
codeGen.push(`const ${varComponentInstanceA} = new __VLS_templateComponents.${componentVar}({ `);
writeProps(parentEl, 'class', 'slots');
codeGen.push(`});\n`);
codeGen.push(`const ${varComponentInstanceB} = ${componentVar}({ `);
codeGen.push(`const ${varComponentInstanceB} = __VLS_templateComponents.${componentVar}({ `);
writeProps(parentEl, 'class', 'slots');
codeGen.push(`});\n`);
writeInterpolationVarsExtraCompletion();
Expand Down
Expand Up @@ -112,7 +112,7 @@ export type ComponentProps<T> =
(
T extends (...args: any) => any ? (T extends (...args: any) => { props: infer Props } ? Props : {})
: T extends new (...args: any) => any ? (T extends new (...args: any) => { $props: infer Props } ? Props : {})
: T; // IntrinsicElement
: T // IntrinsicElement
);
export type InstanceProps<I, C> = I extends { $props: infer Props } ? Props & Record<string, unknown> : C & Record<string, unknown>;
export type EventObject<I, K1 extends string, C, E1> = {
Expand Down
30 changes: 20 additions & 10 deletions vue-language-tools/vue-language-service/src/languageService.ts
Expand Up @@ -58,7 +58,25 @@ export function getLanguageServicePlugins(
},
complete: {
..._tsPlugin.complete,
async on(document, position, context) {
const result = await _tsPlugin.complete!.on!(document, position, context);
if (result) {
const map = apis.context.documents.sourceMapFromEmbeddedDocumentUri(document.uri);
const doc = map ? apis.context.documents.get(map.sourceDocument.uri) : undefined;
if (map && doc?.file instanceof vue.VueSourceFile) {
if (map.toSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.autoImportOnly)) {
result.items.forEach(item => {
item.data.__isComponentAutoImport = true;
});
}
}
}
return result;
},
async resolve(item) {
if (item.textEdit?.newText === 'IconCone') {
console.log(item.textEdit);
}
item = await _tsPlugin.complete!.resolve!(item);

if (
Expand All @@ -76,22 +94,14 @@ export function getLanguageServicePlugins(
}

const data: Data = item.data;
if (data && item.additionalTextEdits?.length && item.textEdit) {
if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit) {
const map = apis.context.documents.sourceMapFromEmbeddedDocumentUri(data.uri);
const doc = map ? apis.context.documents.get(map.sourceDocument.uri) : undefined;
if (map && doc?.file instanceof vue.VueSourceFile) {
let isComponentAutoImport = false;
for (const [_, mapping] of map.toSourceOffsets(data.offset)) {
if (typeof mapping.data.completion === 'object' && mapping.data.completion.autoImportOnly) {
isComponentAutoImport = true;
break;
}
}

const sfc = doc.file.sfc;
const componentName = item.textEdit.newText;
const textDoc = doc.getDocument();
if (isComponentAutoImport && sfc.scriptAst && sfc.script) {
if (sfc.scriptAst && sfc.script) {
const ts = context.typescript.module;
const _scriptRanges = vue.scriptRanges.parseScriptRanges(ts, sfc.scriptAst, !!sfc.scriptSetup, true);
const exportDefault = _scriptRanges.exportDefault;
Expand Down

0 comments on commit 440d6fc

Please sign in to comment.