Skip to content

Commit

Permalink
refactor(language-core): split inline css codegen into separate plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Mar 27, 2024
1 parent ff4b7ea commit fa4a4ba
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 92 deletions.
36 changes: 36 additions & 0 deletions packages/language-core/lib/generators/inlineCss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as CompilerDOM from '@vue/compiler-dom';
import { forEachElementNode } from './template';
import { enableAllFeatures } from './utils';
import type { Code } from '../types';

export function* generate(templateAst: NonNullable<CompilerDOM.RootNode>): Generator<Code> {
for (const node of forEachElementNode(templateAst)) {
for (const prop of node.props) {
if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.name === 'bind'
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.arg.content === 'style'
&& prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
) {
const endCrt = prop.arg.loc.source[prop.arg.loc.source.length - 1]; // " | '
const start = prop.arg.loc.source.indexOf(endCrt) + 1;
const end = prop.arg.loc.source.lastIndexOf(endCrt);
const content = prop.arg.loc.source.substring(start, end);

yield `x { `;
yield [
content,
'template',
prop.arg.loc.start.offset + start,
enableAllFeatures({
format: false,
structure: false,
}),
];
yield ` }\n`;
}
}
}
}
54 changes: 11 additions & 43 deletions packages/language-core/lib/generators/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ const transformContext: CompilerDOM.TransformContext = {
expressionPlugins: ['typescript'],
};

type _CodeAndStack = [codeType: 'ts' | 'tsFormat' | 'inlineCss', ...codeAndStack: CodeAndStack];
export type _CodeAndStack = [
codeType: 'ts' | 'tsFormat',
...codeAndStack: CodeAndStack,
];

export function* generate(
ts: typeof import('typescript'),
Expand Down Expand Up @@ -111,9 +114,6 @@ export function* generate(
const _tsFormat = codegenStack
? (code: Code): _CodeAndStack => ['tsFormat', code, getStack()]
: (code: Code): _CodeAndStack => ['tsFormat', code, ''];
const _inlineCss = codegenStack
? (code: Code): _CodeAndStack => ['inlineCss', code, getStack()]
: (code: Code): _CodeAndStack => ['inlineCss', code, ''];
const nativeTags = new Set(vueCompilerOptions.nativeTags);
const slots = new Map<string, {
name?: string;
Expand Down Expand Up @@ -180,7 +180,7 @@ export function* generate(
return tagOffsetsMap;
}

for (const node of eachElementNode(template.ast)) {
for (const node of forEachElementNode(template.ast)) {
if (node.tag === 'slot') {
// ignore
}
Expand Down Expand Up @@ -818,8 +818,6 @@ export function* generate(
}
}

yield* generateInlineCss(props);

const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data'));
let inScope = false;
let originalConditionsNum = blockConditions.length;
Expand Down Expand Up @@ -1422,36 +1420,6 @@ export function* generate(
}
}

function* generateInlineCss(props: CompilerDOM.ElementNode['props']): Generator<_CodeAndStack> {
for (const prop of props) {
if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.name === 'bind'
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.arg.content === 'style'
&& prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
) {
const endCrt = prop.arg.loc.source[prop.arg.loc.source.length - 1]; // " | '
const start = prop.arg.loc.source.indexOf(endCrt) + 1;
const end = prop.arg.loc.source.lastIndexOf(endCrt);
const content = prop.arg.loc.source.substring(start, end);

yield _inlineCss(`x { `);
yield _inlineCss([
content,
'template',
prop.arg.loc.start.offset + start,
enableAllFeatures({
format: false,
structure: false,
}),
]);
yield _inlineCss(` }\n`);
}
}
}

function* generateDirectives(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> {
for (const prop of node.props) {
if (
Expand Down Expand Up @@ -1986,21 +1954,21 @@ function getPossibleOriginalComponentNames(tagText: string) {
])];
}

export function* eachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator<CompilerDOM.ElementNode> {
export function* forEachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator<CompilerDOM.ElementNode> {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
yield* eachElementNode(child);
yield* forEachElementNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const patchForNode = getVForNode(node);
if (patchForNode) {
yield* eachElementNode(patchForNode);
yield* forEachElementNode(patchForNode);
}
else {
yield node;
for (const child of node.children) {
yield* eachElementNode(child);
yield* forEachElementNode(child);
}
}
}
Expand All @@ -2009,14 +1977,14 @@ export function* eachElementNode(node: CompilerDOM.RootNode | CompilerDOM.Templa
for (let i = 0; i < node.branches.length; i++) {
const branch = node.branches[i];
for (const childNode of branch.children) {
yield* eachElementNode(childNode);
yield* forEachElementNode(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
// v-for
for (const child of node.children) {
yield* eachElementNode(child);
yield* forEachElementNode(child);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/language-core/lib/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks';
import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts';
import useVueSfcStyles from './plugins/vue-sfc-styles';
import useVueSfcTemplate from './plugins/vue-sfc-template';
import useHtmlTemplatePlugin from './plugins/vue-template-html';
import useVueTemplateHtmlPlugin from './plugins/vue-template-html';
import useVueTemplateInlineCssPlugin from './plugins/vue-template-inline-css';
import useVueTsx from './plugins/vue-tsx';
import { pluginVersion, type VueLanguagePlugin } from './types';

Expand All @@ -15,7 +16,8 @@ export function getDefaultVueLanguagePlugins(pluginContext: Parameters<VueLangua
useMdFilePlugin, // .md for VitePress
useHtmlFilePlugin, // .html for PetiteVue
useVueFilePlugin, // .vue and others for Vue
useHtmlTemplatePlugin,
useVueTemplateHtmlPlugin,
useVueTemplateInlineCssPlugin,
useVueSfcStyles,
useVueSfcCustomBlocks,
useVueSfcScriptsFormat,
Expand Down
28 changes: 28 additions & 0 deletions packages/language-core/lib/plugins/vue-template-inline-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { generate as generateInlineCss } from '../generators/inlineCss';
import type { VueLanguagePlugin } from '../types';

const plugin: VueLanguagePlugin = () => {

return {

version: 2,

getEmbeddedCodes(_fileName, sfc) {
if (sfc.template?.ast) {
return [{ id: 'template_inline_css', lang: 'css' }];
}
return [];
},

resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id === 'template_inline_css') {
embeddedFile.parentCodeId = 'template';
if (sfc.template?.ast) {
embeddedFile.content.push(...generateInlineCss(sfc.template.ast));
}
}
},
};
};

export default plugin;
24 changes: 1 addition & 23 deletions packages/language-core/lib/plugins/vue-tsx.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CodeInformation, Mapping, Segment, StackNode, track } from '@volar/language-core';
import { Mapping, StackNode, track } from '@volar/language-core';
import { computed, computedSet } from 'computeds';
import { generate as generateScript } from '../generators/script';
import { generate as generateTemplate } from '../generators/template';
Expand Down Expand Up @@ -34,7 +34,6 @@ const plugin: VueLanguagePlugin = ctx => {

if (sfc.template) {
files.push({ id: 'template_format', lang: 'ts' });
files.push({ id: 'template_style', lang: 'css' });
}

return files;
Expand Down Expand Up @@ -86,19 +85,6 @@ const plugin: VueLanguagePlugin = ctx => {
}
}
}
else if (embeddedFile.id === 'template_style') {

embeddedFile.parentCodeId = 'template';

const template = _tsx.generatedTemplate();
if (template) {
const [content, contentStacks] = ctx.codegenStack
? track([...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 })))
: [[...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 }))];
embeddedFile.content = content as Segment<CodeInformation>[];
embeddedFile.contentStacks = contentStacks;
}
}
},
};

Expand Down Expand Up @@ -168,7 +154,6 @@ function createTsx(

const tsCodes: Code[] = [];
const tsFormatCodes: Code[] = [];
const inlineCssCodes: Code[] = [];
const tsCodegenStacks: string[] = [];
const tsFormatCodegenStacks: string[] = [];
const inlineCssCodegenStacks: string[] = [];
Expand All @@ -195,19 +180,13 @@ function createTsx(
else if (type === 'tsFormat') {
tsFormatCodes.push(code);
}
else if (type === 'inlineCss') {
inlineCssCodes.push(code);
}
if (ctx.codegenStack) {
if (type === 'ts') {
tsCodegenStacks.push(stack);
}
else if (type === 'tsFormat') {
tsFormatCodegenStacks.push(stack);
}
else if (type === 'inlineCss') {
inlineCssCodegenStacks.push(stack);
}
}
current = codegen.next();
}
Expand All @@ -218,7 +197,6 @@ function createTsx(
codeStacks: tsCodegenStacks,
formatCodes: tsFormatCodes,
formatCodeStacks: tsFormatCodegenStacks,
cssCodes: inlineCssCodes,
cssCodeStacks: inlineCssCodegenStacks,
};
});
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/lib/ideFeatures/nameCasing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ function getTemplateTagsAndAttrs(sourceFile: VirtualCode): Tags {
const ast = sourceFile.sfc.template?.ast;
const tags: Tags = new Map();
if (ast) {
for (const node of vue.eachElementNode(ast)) {
for (const node of vue.forEachElementNode(ast)) {

if (!tags.has(node.tag)) {
tags.set(node.tag, { offsets: [], attrs: new Map() });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { LanguageServicePlugin, LanguageServicePluginInstance } from '@volar/language-service';
import { VueGeneratedCode, eachElementNode, type CompilerDOM } from '@vue/language-core';
import { VueGeneratedCode, forEachElementNode, type CompilerDOM } from '@vue/language-core';
import type * as vscode from 'vscode-languageserver-protocol';

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

for (const node of eachElementNode(template.ast)) {
for (const node of forEachElementNode(template.ast)) {
if (startOffset > templateStartOffset + node.loc.end.offset || endOffset < templateStartOffset + node.loc.start.offset) {
return;
}
Expand Down
27 changes: 6 additions & 21 deletions packages/typescript-plugin/lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ export function getComponentSpans(
...validComponentNames,
...validComponentNames.map(vue.hyphenateTag),
]);
template.ast?.children.forEach(function visit(node) {
if (node.loc.end.offset <= spanTemplateRange.start || node.loc.start.offset >= (spanTemplateRange.start + spanTemplateRange.length)) {
return;
}
if (node.type === 1 satisfies vue.CompilerDOM.NodeTypes.ELEMENT) {
if (template.ast) {
for (const node of vue.forEachElementNode(template.ast)) {
if (node.loc.end.offset <= spanTemplateRange.start || node.loc.start.offset >= (spanTemplateRange.start + spanTemplateRange.length)) {
continue;
}
if (components.has(node.tag)) {
let start = node.loc.start.offset;
if (template.lang === 'html') {
Expand All @@ -144,22 +144,7 @@ export function getComponentSpans(
});
}
}
for (const child of node.children) {
visit(child);
}
}
else if (node.type === 9 satisfies vue.CompilerDOM.NodeTypes.IF) {
for (const branch of node.branches) {
for (const child of branch.children) {
visit(child);
}
}
}
else if (node.type === 11 satisfies vue.CompilerDOM.NodeTypes.FOR) {
for (const child of node.children) {
visit(child);
}
}
});
}
return result;
}

0 comments on commit fa4a4ba

Please sign in to comment.