Skip to content

Commit fa4a4ba

Browse files
committed
refactor(language-core): split inline css codegen into separate plugin
1 parent ff4b7ea commit fa4a4ba

File tree

8 files changed

+89
-92
lines changed

8 files changed

+89
-92
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as CompilerDOM from '@vue/compiler-dom';
2+
import { forEachElementNode } from './template';
3+
import { enableAllFeatures } from './utils';
4+
import type { Code } from '../types';
5+
6+
export function* generate(templateAst: NonNullable<CompilerDOM.RootNode>): Generator<Code> {
7+
for (const node of forEachElementNode(templateAst)) {
8+
for (const prop of node.props) {
9+
if (
10+
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
11+
&& prop.name === 'bind'
12+
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
13+
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
14+
&& prop.arg.content === 'style'
15+
&& prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
16+
) {
17+
const endCrt = prop.arg.loc.source[prop.arg.loc.source.length - 1]; // " | '
18+
const start = prop.arg.loc.source.indexOf(endCrt) + 1;
19+
const end = prop.arg.loc.source.lastIndexOf(endCrt);
20+
const content = prop.arg.loc.source.substring(start, end);
21+
22+
yield `x { `;
23+
yield [
24+
content,
25+
'template',
26+
prop.arg.loc.start.offset + start,
27+
enableAllFeatures({
28+
format: false,
29+
structure: false,
30+
}),
31+
];
32+
yield ` }\n`;
33+
}
34+
}
35+
}
36+
}

packages/language-core/lib/generators/template.ts

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ const transformContext: CompilerDOM.TransformContext = {
6363
expressionPlugins: ['typescript'],
6464
};
6565

66-
type _CodeAndStack = [codeType: 'ts' | 'tsFormat' | 'inlineCss', ...codeAndStack: CodeAndStack];
66+
export type _CodeAndStack = [
67+
codeType: 'ts' | 'tsFormat',
68+
...codeAndStack: CodeAndStack,
69+
];
6770

6871
export function* generate(
6972
ts: typeof import('typescript'),
@@ -111,9 +114,6 @@ export function* generate(
111114
const _tsFormat = codegenStack
112115
? (code: Code): _CodeAndStack => ['tsFormat', code, getStack()]
113116
: (code: Code): _CodeAndStack => ['tsFormat', code, ''];
114-
const _inlineCss = codegenStack
115-
? (code: Code): _CodeAndStack => ['inlineCss', code, getStack()]
116-
: (code: Code): _CodeAndStack => ['inlineCss', code, ''];
117117
const nativeTags = new Set(vueCompilerOptions.nativeTags);
118118
const slots = new Map<string, {
119119
name?: string;
@@ -180,7 +180,7 @@ export function* generate(
180180
return tagOffsetsMap;
181181
}
182182

183-
for (const node of eachElementNode(template.ast)) {
183+
for (const node of forEachElementNode(template.ast)) {
184184
if (node.tag === 'slot') {
185185
// ignore
186186
}
@@ -818,8 +818,6 @@ export function* generate(
818818
}
819819
}
820820

821-
yield* generateInlineCss(props);
822-
823821
const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data'));
824822
let inScope = false;
825823
let originalConditionsNum = blockConditions.length;
@@ -1422,36 +1420,6 @@ export function* generate(
14221420
}
14231421
}
14241422

1425-
function* generateInlineCss(props: CompilerDOM.ElementNode['props']): Generator<_CodeAndStack> {
1426-
for (const prop of props) {
1427-
if (
1428-
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
1429-
&& prop.name === 'bind'
1430-
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
1431-
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
1432-
&& prop.arg.content === 'style'
1433-
&& prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
1434-
) {
1435-
const endCrt = prop.arg.loc.source[prop.arg.loc.source.length - 1]; // " | '
1436-
const start = prop.arg.loc.source.indexOf(endCrt) + 1;
1437-
const end = prop.arg.loc.source.lastIndexOf(endCrt);
1438-
const content = prop.arg.loc.source.substring(start, end);
1439-
1440-
yield _inlineCss(`x { `);
1441-
yield _inlineCss([
1442-
content,
1443-
'template',
1444-
prop.arg.loc.start.offset + start,
1445-
enableAllFeatures({
1446-
format: false,
1447-
structure: false,
1448-
}),
1449-
]);
1450-
yield _inlineCss(` }\n`);
1451-
}
1452-
}
1453-
}
1454-
14551423
function* generateDirectives(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> {
14561424
for (const prop of node.props) {
14571425
if (
@@ -1986,21 +1954,21 @@ function getPossibleOriginalComponentNames(tagText: string) {
19861954
])];
19871955
}
19881956

1989-
export function* eachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator<CompilerDOM.ElementNode> {
1957+
export function* forEachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator<CompilerDOM.ElementNode> {
19901958
if (node.type === CompilerDOM.NodeTypes.ROOT) {
19911959
for (const child of node.children) {
1992-
yield* eachElementNode(child);
1960+
yield* forEachElementNode(child);
19931961
}
19941962
}
19951963
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
19961964
const patchForNode = getVForNode(node);
19971965
if (patchForNode) {
1998-
yield* eachElementNode(patchForNode);
1966+
yield* forEachElementNode(patchForNode);
19991967
}
20001968
else {
20011969
yield node;
20021970
for (const child of node.children) {
2003-
yield* eachElementNode(child);
1971+
yield* forEachElementNode(child);
20041972
}
20051973
}
20061974
}
@@ -2009,14 +1977,14 @@ export function* eachElementNode(node: CompilerDOM.RootNode | CompilerDOM.Templa
20091977
for (let i = 0; i < node.branches.length; i++) {
20101978
const branch = node.branches[i];
20111979
for (const childNode of branch.children) {
2012-
yield* eachElementNode(childNode);
1980+
yield* forEachElementNode(childNode);
20131981
}
20141982
}
20151983
}
20161984
else if (node.type === CompilerDOM.NodeTypes.FOR) {
20171985
// v-for
20181986
for (const child of node.children) {
2019-
yield* eachElementNode(child);
1987+
yield* forEachElementNode(child);
20201988
}
20211989
}
20221990
}

packages/language-core/lib/plugins.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks';
55
import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts';
66
import useVueSfcStyles from './plugins/vue-sfc-styles';
77
import useVueSfcTemplate from './plugins/vue-sfc-template';
8-
import useHtmlTemplatePlugin from './plugins/vue-template-html';
8+
import useVueTemplateHtmlPlugin from './plugins/vue-template-html';
9+
import useVueTemplateInlineCssPlugin from './plugins/vue-template-inline-css';
910
import useVueTsx from './plugins/vue-tsx';
1011
import { pluginVersion, type VueLanguagePlugin } from './types';
1112

@@ -15,7 +16,8 @@ export function getDefaultVueLanguagePlugins(pluginContext: Parameters<VueLangua
1516
useMdFilePlugin, // .md for VitePress
1617
useHtmlFilePlugin, // .html for PetiteVue
1718
useVueFilePlugin, // .vue and others for Vue
18-
useHtmlTemplatePlugin,
19+
useVueTemplateHtmlPlugin,
20+
useVueTemplateInlineCssPlugin,
1921
useVueSfcStyles,
2022
useVueSfcCustomBlocks,
2123
useVueSfcScriptsFormat,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { generate as generateInlineCss } from '../generators/inlineCss';
2+
import type { VueLanguagePlugin } from '../types';
3+
4+
const plugin: VueLanguagePlugin = () => {
5+
6+
return {
7+
8+
version: 2,
9+
10+
getEmbeddedCodes(_fileName, sfc) {
11+
if (sfc.template?.ast) {
12+
return [{ id: 'template_inline_css', lang: 'css' }];
13+
}
14+
return [];
15+
},
16+
17+
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
18+
if (embeddedFile.id === 'template_inline_css') {
19+
embeddedFile.parentCodeId = 'template';
20+
if (sfc.template?.ast) {
21+
embeddedFile.content.push(...generateInlineCss(sfc.template.ast));
22+
}
23+
}
24+
},
25+
};
26+
};
27+
28+
export default plugin;

packages/language-core/lib/plugins/vue-tsx.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CodeInformation, Mapping, Segment, StackNode, track } from '@volar/language-core';
1+
import { Mapping, StackNode, track } from '@volar/language-core';
22
import { computed, computedSet } from 'computeds';
33
import { generate as generateScript } from '../generators/script';
44
import { generate as generateTemplate } from '../generators/template';
@@ -34,7 +34,6 @@ const plugin: VueLanguagePlugin = ctx => {
3434

3535
if (sfc.template) {
3636
files.push({ id: 'template_format', lang: 'ts' });
37-
files.push({ id: 'template_style', lang: 'css' });
3837
}
3938

4039
return files;
@@ -86,19 +85,6 @@ const plugin: VueLanguagePlugin = ctx => {
8685
}
8786
}
8887
}
89-
else if (embeddedFile.id === 'template_style') {
90-
91-
embeddedFile.parentCodeId = 'template';
92-
93-
const template = _tsx.generatedTemplate();
94-
if (template) {
95-
const [content, contentStacks] = ctx.codegenStack
96-
? track([...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 })))
97-
: [[...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 }))];
98-
embeddedFile.content = content as Segment<CodeInformation>[];
99-
embeddedFile.contentStacks = contentStacks;
100-
}
101-
}
10288
},
10389
};
10490

@@ -168,7 +154,6 @@ function createTsx(
168154

169155
const tsCodes: Code[] = [];
170156
const tsFormatCodes: Code[] = [];
171-
const inlineCssCodes: Code[] = [];
172157
const tsCodegenStacks: string[] = [];
173158
const tsFormatCodegenStacks: string[] = [];
174159
const inlineCssCodegenStacks: string[] = [];
@@ -195,19 +180,13 @@ function createTsx(
195180
else if (type === 'tsFormat') {
196181
tsFormatCodes.push(code);
197182
}
198-
else if (type === 'inlineCss') {
199-
inlineCssCodes.push(code);
200-
}
201183
if (ctx.codegenStack) {
202184
if (type === 'ts') {
203185
tsCodegenStacks.push(stack);
204186
}
205187
else if (type === 'tsFormat') {
206188
tsFormatCodegenStacks.push(stack);
207189
}
208-
else if (type === 'inlineCss') {
209-
inlineCssCodegenStacks.push(stack);
210-
}
211190
}
212191
current = codegen.next();
213192
}
@@ -218,7 +197,6 @@ function createTsx(
218197
codeStacks: tsCodegenStacks,
219198
formatCodes: tsFormatCodes,
220199
formatCodeStacks: tsFormatCodegenStacks,
221-
cssCodes: inlineCssCodes,
222200
cssCodeStacks: inlineCssCodegenStacks,
223201
};
224202
});

packages/language-service/lib/ideFeatures/nameCasing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ function getTemplateTagsAndAttrs(sourceFile: VirtualCode): Tags {
231231
const ast = sourceFile.sfc.template?.ast;
232232
const tags: Tags = new Map();
233233
if (ast) {
234-
for (const node of vue.eachElementNode(ast)) {
234+
for (const node of vue.forEachElementNode(ast)) {
235235

236236
if (!tags.has(node.tag)) {
237237
tags.set(node.tag, { offsets: [], attrs: new Map() });

packages/language-service/lib/plugins/vue-toggle-v-bind-codeaction.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { LanguageServicePlugin, LanguageServicePluginInstance } from '@volar/language-service';
2-
import { VueGeneratedCode, eachElementNode, type CompilerDOM } from '@vue/language-core';
2+
import { VueGeneratedCode, forEachElementNode, type CompilerDOM } from '@vue/language-core';
33
import type * as vscode from 'vscode-languageserver-protocol';
44

55
export function create(ts: typeof import('typescript')): LanguageServicePlugin {
@@ -26,7 +26,7 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin {
2626
const templateStartOffset = template.startTagEnd;
2727
const result: vscode.CodeAction[] = [];
2828

29-
for (const node of eachElementNode(template.ast)) {
29+
for (const node of forEachElementNode(template.ast)) {
3030
if (startOffset > templateStartOffset + node.loc.end.offset || endOffset < templateStartOffset + node.loc.start.offset) {
3131
return;
3232
}

packages/typescript-plugin/lib/common.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ export function getComponentSpans(
123123
...validComponentNames,
124124
...validComponentNames.map(vue.hyphenateTag),
125125
]);
126-
template.ast?.children.forEach(function visit(node) {
127-
if (node.loc.end.offset <= spanTemplateRange.start || node.loc.start.offset >= (spanTemplateRange.start + spanTemplateRange.length)) {
128-
return;
129-
}
130-
if (node.type === 1 satisfies vue.CompilerDOM.NodeTypes.ELEMENT) {
126+
if (template.ast) {
127+
for (const node of vue.forEachElementNode(template.ast)) {
128+
if (node.loc.end.offset <= spanTemplateRange.start || node.loc.start.offset >= (spanTemplateRange.start + spanTemplateRange.length)) {
129+
continue;
130+
}
131131
if (components.has(node.tag)) {
132132
let start = node.loc.start.offset;
133133
if (template.lang === 'html') {
@@ -144,22 +144,7 @@ export function getComponentSpans(
144144
});
145145
}
146146
}
147-
for (const child of node.children) {
148-
visit(child);
149-
}
150147
}
151-
else if (node.type === 9 satisfies vue.CompilerDOM.NodeTypes.IF) {
152-
for (const branch of node.branches) {
153-
for (const child of branch.children) {
154-
visit(child);
155-
}
156-
}
157-
}
158-
else if (node.type === 11 satisfies vue.CompilerDOM.NodeTypes.FOR) {
159-
for (const child of node.children) {
160-
visit(child);
161-
}
162-
}
163-
});
148+
}
164149
return result;
165150
}

0 commit comments

Comments
 (0)