Skip to content

Commit ad75e70

Browse files
committed
refactor(language-core): extract collectStyleScopedClassReferences
1 parent 55638ff commit ad75e70

File tree

3 files changed

+193
-189
lines changed

3 files changed

+193
-189
lines changed

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

Lines changed: 3 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import * as CompilerDOM from '@vue/compiler-dom';
22
import { camelize, capitalize } from '@vue/shared';
3-
import type * as ts from 'typescript';
4-
import { getNodeText } from '../../parsers/scriptSetupRanges';
53
import type { Code, VueCodeInformation } from '../../types';
64
import { hyphenateTag } from '../../utils/shared';
75
import { createVBindShorthandInlayHintInfo } from '../inlayHints';
8-
import { collectVars, createTsAst, endOfLine, newLine, variableNameRegex, wrapWith } from '../utils';
6+
import { collectVars, createTsAst, endOfLine, newLine, normalizeAttributeValue, variableNameRegex, wrapWith } from '../utils';
97
import { generateCamelized } from '../utils/camelized';
108
import type { TemplateCodegenContext } from './context';
119
import { generateElementChildren } from './elementChildren';
@@ -16,6 +14,7 @@ import type { TemplateCodegenOptions } from './index';
1614
import { generateInterpolation } from './interpolation';
1715
import { generateObjectProperty } from './objectProperty';
1816
import { generatePropertyAccess } from './propertyAccess';
17+
import { collectStyleScopedClassReferences } from './styleScopedClasses';
1918
import { generateTemplateChild } from './templateChild';
2019

2120
const colonReg = /:/g;
@@ -420,7 +419,7 @@ function* generateVScope(
420419

421420
yield* generateElementDirectives(options, ctx, node);
422421
const [refName, offset] = yield* generateReferencesForElements(options, ctx, node); // <el ref="foo" />
423-
yield* generateReferencesForScopedCssClasses(options, ctx, node);
422+
collectStyleScopedClassReferences(options, ctx, node);
424423

425424
if (inScope) {
426425
yield `}${newLine}`;
@@ -618,193 +617,10 @@ function* generateReferencesForElements(
618617
return [];
619618
}
620619

621-
function* generateReferencesForScopedCssClasses(
622-
options: TemplateCodegenOptions,
623-
ctx: TemplateCodegenContext,
624-
node: CompilerDOM.ElementNode
625-
): Generator<Code> {
626-
for (const prop of node.props) {
627-
if (
628-
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
629-
&& prop.name === 'class'
630-
&& prop.value
631-
) {
632-
if (options.template.lang === 'pug') {
633-
const getClassOffset = Reflect.get(prop.value.loc.start, 'getClassOffset') as (offset: number) => number;
634-
const content = prop.value.loc.source.slice(1, -1);
635-
636-
let startOffset = 1;
637-
for (const className of content.split(' ')) {
638-
if (className) {
639-
ctx.scopedClasses.push({
640-
source: 'template',
641-
className,
642-
offset: getClassOffset(startOffset),
643-
});
644-
}
645-
startOffset += className.length + 1;
646-
}
647-
}
648-
else {
649-
let isWrapped = false;
650-
const [content, startOffset] = normalizeAttributeValue(prop.value);
651-
if (content) {
652-
const classes = collectClasses(content, startOffset + (isWrapped ? 1 : 0));
653-
ctx.scopedClasses.push(...classes);
654-
}
655-
else {
656-
ctx.emptyClassOffsets.push(startOffset);
657-
}
658-
}
659-
}
660-
else if (
661-
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
662-
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
663-
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
664-
&& prop.arg.content === 'class'
665-
) {
666-
const content = '`${' + prop.exp.content + '}`';
667-
const startOffset = prop.exp.loc.start.offset - 3;
668-
669-
const { ts } = options;
670-
const ast = ts.createSourceFile('', content, 99 satisfies ts.ScriptTarget.Latest);
671-
const literals: ts.StringLiteralLike[] = [];
672-
673-
ts.forEachChild(ast, node => {
674-
if (
675-
!ts.isExpressionStatement(node) ||
676-
!isTemplateExpression(node.expression)
677-
) {
678-
return;
679-
}
680-
681-
const expression = node.expression.templateSpans[0].expression;
682-
683-
if (ts.isStringLiteralLike(expression)) {
684-
literals.push(expression);
685-
}
686-
687-
if (ts.isArrayLiteralExpression(expression)) {
688-
walkArrayLiteral(expression);
689-
}
690-
691-
if (ts.isObjectLiteralExpression(expression)) {
692-
walkObjectLiteral(expression);
693-
}
694-
});
695-
696-
for (const literal of literals) {
697-
if (literal.text) {
698-
const classes = collectClasses(
699-
literal.text,
700-
literal.end - literal.text.length - 1 + startOffset
701-
);
702-
ctx.scopedClasses.push(...classes);
703-
}
704-
else {
705-
ctx.emptyClassOffsets.push(literal.end - 1 + startOffset);
706-
}
707-
}
708-
709-
function walkArrayLiteral(node: ts.ArrayLiteralExpression) {
710-
const { elements } = node;
711-
for (const element of elements) {
712-
if (ts.isStringLiteralLike(element)) {
713-
literals.push(element);
714-
}
715-
else if (ts.isObjectLiteralExpression(element)) {
716-
walkObjectLiteral(element);
717-
}
718-
}
719-
}
720-
721-
function walkObjectLiteral(node: ts.ObjectLiteralExpression) {
722-
const { properties } = node;
723-
for (const property of properties) {
724-
if (ts.isPropertyAssignment(property)) {
725-
const { name } = property;
726-
if (ts.isIdentifier(name)) {
727-
walkIdentifier(name);
728-
}
729-
else if (ts.isStringLiteral(name)) {
730-
literals.push(name);
731-
}
732-
else if (ts.isComputedPropertyName(name)) {
733-
const { expression } = name;
734-
if (ts.isStringLiteralLike(expression)) {
735-
literals.push(expression);
736-
}
737-
}
738-
}
739-
else if (ts.isShorthandPropertyAssignment(property)) {
740-
walkIdentifier(property.name);
741-
}
742-
}
743-
}
744-
745-
function walkIdentifier(node: ts.Identifier) {
746-
const text = getNodeText(ts, node, ast);
747-
ctx.scopedClasses.push({
748-
source: 'template',
749-
className: text,
750-
offset: node.end - text.length + startOffset
751-
});
752-
}
753-
}
754-
}
755-
}
756-
757620
function camelizeComponentName(newName: string) {
758621
return camelize('-' + newName);
759622
}
760623

761624
function getTagRenameApply(oldName: string) {
762625
return oldName === hyphenateTag(oldName) ? hyphenateTag : undefined;
763626
}
764-
765-
function normalizeAttributeValue(node: CompilerDOM.TextNode): [string, number] {
766-
let offset = node.loc.start.offset;
767-
let content = node.loc.source;
768-
if (
769-
(content.startsWith(`'`) && content.endsWith(`'`))
770-
|| (content.startsWith(`"`) && content.endsWith(`"`))
771-
) {
772-
offset++;
773-
content = content.slice(1, -1);
774-
}
775-
return [content, offset];
776-
}
777-
778-
function collectClasses(content: string, startOffset = 0) {
779-
const classes: {
780-
source: string;
781-
className: string;
782-
offset: number;
783-
}[] = [];
784-
785-
let currentClassName = '';
786-
let offset = 0;
787-
for (const char of (content + ' ')) {
788-
if (char.trim() === '') {
789-
if (currentClassName !== '') {
790-
classes.push({
791-
source: 'template',
792-
className: currentClassName,
793-
offset: offset + startOffset
794-
});
795-
offset += currentClassName.length;
796-
currentClassName = '';
797-
}
798-
offset += char.length;
799-
}
800-
else {
801-
currentClassName += char;
802-
}
803-
}
804-
return classes;
805-
}
806-
807-
// isTemplateExpression is missing in tsc
808-
function isTemplateExpression(node: ts.Node): node is ts.TemplateExpression {
809-
return node.kind === 228 satisfies ts.SyntaxKind.TemplateExpression;
810-
}

0 commit comments

Comments
 (0)