Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
2 changes: 1 addition & 1 deletion extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@
"@volar/typescript": "2.4.26",
"@volar/vscode": "2.4.26",
"@vscode/vsce": "^3.2.1",
"@vue/compiler-sfc": "^3.5.0",
"@vue/compiler-sfc": "^3.5.24",
"@vue/language-core": "3.1.5",
"@vue/language-server": "3.1.5",
"@vue/typescript-plugin": "3.1.5",
Expand Down
1 change: 1 addition & 0 deletions packages/language-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './lib/compilerOptions';
export * from './lib/languagePlugin';
export * from './lib/parsers/scriptSetupRanges';
export * from './lib/plugins';
export * from './lib/template/compile';
export * from './lib/types';
export * from './lib/utils/collectBindings';
export * from './lib/utils/forEachTemplateNode';
Expand Down
32 changes: 31 additions & 1 deletion packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import { endOfLine, identifierRegex, newLine } from '../utils';
import { endBoundary, startBoundary } from '../utils/boundary';
import { generateCamelized } from '../utils/camelized';
import type { TemplateCodegenContext } from './context';
import { generateElementChildren } from './elementChildren';
import { generateElementDirectives } from './elementDirectives';
import { generateElementEvents } from './elementEvents';
import { type FailedPropExpression, generateElementProps } from './elementProps';
import type { TemplateCodegenOptions } from './index';
import { generateInterpolation } from './interpolation';
import { generatePropertyAccess } from './propertyAccess';
import { collectStyleScopedClassReferences } from './styleScopedClasses';
import { generateElementChildren } from './templateChild';
import { generateVSlot } from './vSlot';

const colonReg = /:/g;
Expand Down Expand Up @@ -348,6 +348,36 @@ export function* generateElement(
ctx.currentComponent = currentComponent;
}

export function* generateFragment(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
): Generator<Code> {
const [startTagOffset] = getElementTagOffsets(node, options.template);

// special case for <template v-for="..." :key="..." />
if (node.props.length) {
yield `__VLS_asFunctionalElement(__VLS_intrinsics.template)(`;
const token = yield* startBoundary('template', startTagOffset, codeFeatures.verification);
yield `{${newLine}`;
yield* generateElementProps(
options,
ctx,
node,
node.props,
options.vueCompilerOptions.checkUnknownProps,
);
yield `}`;
yield endBoundary(token, startTagOffset + node.tag.length);
yield `)${endOfLine}`;
}

const { currentComponent } = ctx;
ctx.currentComponent = undefined;
yield* generateElementChildren(options, ctx, node.children);
ctx.currentComponent = currentComponent;
}

function* generateFailedPropExps(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
Expand Down
99 changes: 0 additions & 99 deletions packages/language-core/lib/codegen/template/elementChildren.ts

This file was deleted.

11 changes: 7 additions & 4 deletions packages/language-core/lib/codegen/template/elementProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function* generateElementProps(
}

const shouldSpread = propName === 'style' || propName === 'class';
const shouldCamelize = isComponent && getShouldCamelize(options, prop, propName);
const shouldCamelize = getShouldCamelize(options, node, prop, propName);
const features = getPropsCodeFeatures(strictPropsCheck);

if (shouldSpread) {
Expand Down Expand Up @@ -167,7 +167,7 @@ export function* generateElementProps(
}

const shouldSpread = prop.name === 'style' || prop.name === 'class';
const shouldCamelize = isComponent && getShouldCamelize(options, prop, prop.name);
const shouldCamelize = getShouldCamelize(options, node, prop, prop.name);
const features = getPropsCodeFeatures(strictPropsCheck);

if (shouldSpread) {
Expand Down Expand Up @@ -285,13 +285,16 @@ function* generateAttrValue(

function getShouldCamelize(
options: TemplateCodegenOptions,
node: CompilerDOM.ElementNode,
prop: CompilerDOM.AttributeNode | CompilerDOM.DirectiveNode,
propName: string,
) {
return (
node.tagType === CompilerDOM.ElementTypes.COMPONENT
|| node.tagType === CompilerDOM.ElementTypes.SLOT
) && (
prop.type !== CompilerDOM.NodeTypes.DIRECTIVE
|| !prop.arg
|| (prop.arg.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic)
|| prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic
)
&& hyphenateAttr(propName) === propName
&& !options.vueCompilerOptions.htmlAttributes.some(pattern => isMatch(propName, pattern));
Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/template/slotOutlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { createVBindShorthandInlayHintInfo } from '../inlayHints';
import { endOfLine, newLine } from '../utils';
import { endBoundary, startBoundary } from '../utils/boundary';
import type { TemplateCodegenContext } from './context';
import { generateElementChildren } from './elementChildren';
import { generateElementProps, generatePropExp } from './elementProps';
import type { TemplateCodegenOptions } from './index';
import { generateInterpolation } from './interpolation';
import { generatePropertyAccess } from './propertyAccess';
import { generateElementChildren } from './templateChild';

export function* generateSlotOutlet(
options: TemplateCodegenOptions,
Expand Down
119 changes: 24 additions & 95 deletions packages/language-core/lib/codegen/template/templateChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,88 +4,67 @@ import { hyphenateTag } from '../../utils/shared';
import { codeFeatures } from '../codeFeatures';
import { endOfLine } from '../utils';
import type { TemplateCodegenContext } from './context';
import { generateComponent, generateElement } from './element';
import { generateElementChildren } from './elementChildren';
import { generateComponent, generateElement, generateFragment } from './element';
import type { TemplateCodegenOptions } from './index';
import { generateInterpolation } from './interpolation';
import { generateSlotOutlet } from './slotOutlet';
import { generateVFor } from './vFor';
import { generateVIf } from './vIf';
import { generateVSlot } from './vSlot';

// @ts-ignore
const transformContext: CompilerDOM.TransformContext = {
onError: () => {},
helperString: str => str.toString(),
replaceNode: () => {},
cacheHandlers: false,
prefixIdentifiers: false,
scopes: {
vFor: 0,
vOnce: 0,
vPre: 0,
vSlot: 0,
},
expressionPlugins: ['typescript'],
};
export function* generateElementChildren(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
children: CompilerDOM.TemplateChildNode[],
enterNode = true,
isVForChild: boolean = false,
): Generator<Code> {
const endScope = ctx.startScope();
for (const child of children) {
yield* generateTemplateChild(options, ctx, child, enterNode, isVForChild);
}
yield* endScope();
}

export function* generateTemplateChild(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode | CompilerDOM.SimpleExpressionNode,
enterNode: boolean = true,
isVForChild: boolean = false,
): Generator<Code> {
if (enterNode && !ctx.enter(node)) {
return;
}

const cur = node as CompilerDOM.ElementNode | CompilerDOM.IfNode | CompilerDOM.ForNode;
if (cur.codegenNode?.type === CompilerDOM.NodeTypes.JS_CACHE_EXPRESSION) {
cur.codegenNode = cur.codegenNode.value as any;
}

if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const item of collectSingleRootNodes(options, node.children)) {
ctx.singleRootNodes.add(item);
}
yield* generateElementChildren(options, ctx, node.children);
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const vForNode = getVForNode(node);
if (vForNode) {
yield* generateVFor(options, ctx, vForNode);
}
else if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
yield* generateSlotOutlet(options, ctx, node);
}
else {
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as
| CompilerDOM.DirectiveNode
| undefined;
if (
node.tagType === CompilerDOM.ElementTypes.TEMPLATE
&& ctx.currentComponent
&& slotDir
) {
const slotDir = node.props.find(CompilerDOM.isVSlot);
if (node.tagType === CompilerDOM.ElementTypes.TEMPLATE && ctx.currentComponent && slotDir) {
yield* generateVSlot(options, ctx, node, slotDir);
}
else if (
node.tagType === CompilerDOM.ElementTypes.ELEMENT
|| node.tagType === CompilerDOM.ElementTypes.TEMPLATE
) {
yield* generateElement(options, ctx, node);
else if (node.tagType === CompilerDOM.ElementTypes.TEMPLATE && isVForChild) {
yield* generateFragment(options, ctx, node);
}
else {
else if (node.tagType === CompilerDOM.ElementTypes.COMPONENT) {
const { currentComponent } = ctx;
yield* generateComponent(options, ctx, node);
ctx.currentComponent = currentComponent;
}
else {
yield* generateElement(options, ctx, node);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.TEXT_CALL) {
// {{ var }}
yield* generateTemplateChild(options, ctx, node.content, false);
}
else if (node.type === CompilerDOM.NodeTypes.COMPOUND_EXPRESSION) {
// {{ ... }} {{ ... }}
for (const child of node.children) {
Expand Down Expand Up @@ -117,9 +96,6 @@ export function* generateTemplateChild(
// v-for
yield* generateVFor(options, ctx, node);
}
else if (node.type === CompilerDOM.NodeTypes.TEXT) {
// not needed progress
}

if (enterNode) {
yield* ctx.exit();
Expand Down Expand Up @@ -159,53 +135,6 @@ function* collectSingleRootNodes(
}
}

// TODO: track https://github.com/vuejs/vue-next/issues/3498
export function getVForNode(node: CompilerDOM.ElementNode) {
const forDirective = node.props.find(
(prop): prop is CompilerDOM.DirectiveNode =>
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.name === 'for',
);
if (forDirective) {
let forNode: CompilerDOM.ForNode | undefined;
CompilerDOM.processFor(node, forDirective, transformContext, _forNode => {
forNode = { ..._forNode };
return undefined;
});
if (forNode) {
forNode.children = [{
...node,
props: node.props.filter(prop => prop !== forDirective),
}];
return forNode;
}
}
}

export function getVIfNode(node: CompilerDOM.ElementNode) {
const ifDirective = node.props.find(
(prop): prop is CompilerDOM.DirectiveNode =>
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.name === 'if',
);
if (ifDirective) {
let ifNode: CompilerDOM.IfNode | undefined;
CompilerDOM.processIf(node, ifDirective, transformContext, _ifNode => {
ifNode = { ..._ifNode };
return undefined;
});
if (ifNode) {
for (const branch of ifNode.branches) {
branch.children = [{
...node,
props: node.props.filter(prop => prop !== ifDirective),
}];
}
return ifNode;
}
}
}

export function parseInterpolationNode(node: CompilerDOM.InterpolationNode, template: string) {
let start = node.content.loc.start.offset;
let end = node.content.loc.end.offset;
Expand Down
Loading
Loading