feat: parser style

This commit is contained in:
“chenhuachun”
2025-04-02 16:22:56 +08:00
parent f9b9125d32
commit 4e22bc8aa2
7 changed files with 317 additions and 33 deletions

View File

@@ -1,22 +1,43 @@
import { type BlockSchema, type NodeSchema, BlockModel } from '@vtj/core';
import {
type BlockSchema,
type NodeSchema,
type Dependencie,
BlockModel
} from '@vtj/core';
import { tsFormatter } from '@vtj/coder';
import { parseSFC, isJSCode } from '../shared';
import { parseTemplate } from './template';
import { parseScripts } from './scripts';
import { parseScripts, type ImportStatement } from './scripts';
import { parseStyle } from './style';
import { patchCode } from './utils';
export interface ParseVueOptions {
id: string;
name: string;
source: string;
dependencies?: Dependencie[];
}
export async function parseVue(options: ParseVueOptions) {
const { id, name, source } = options;
const { id, name, source, dependencies = [] } = options;
const sfc = parseSFC(source);
const { state, watch, lifeCycles, computed, methods, props, emits, inject } =
parseScripts(sfc.script);
const { nodes, slots, context } = parseTemplate(id, name, sfc.template);
const { styles, css } = parseStyle(sfc.styles.join('\n'));
const {
state,
watch,
lifeCycles,
computed,
methods,
props,
emits,
inject,
handlers,
imports
} = parseScripts(sfc.script);
const { nodes, slots, context } = parseTemplate(id, name, sfc.template, {
handlers,
styles
});
const dsl: BlockSchema = {
id,
name,
@@ -29,16 +50,19 @@ export async function parseVue(options: ParseVueOptions) {
methods,
slots,
emits,
nodes
nodes,
css
};
const computedKeys = Object.keys(computed || {});
const { libs } = parseDeps(imports, dependencies);
await walkDsl(dsl, async (node: NodeSchema) => {
await walkNode(node, async (content: any) => {
if (isJSCode(content)) {
const options = {
context,
computed: [],
libs: {}
computed: computedKeys,
libs
};
const code = await tsFormatter(content.value);
content.value = patchCode(code, node.id as string, options);
@@ -88,3 +112,28 @@ async function walkNode(node: NodeSchema, callback: (n: any) => Promise<void>) {
};
await walking(node);
}
function parseDeps(
imports: ImportStatement[] = [],
dependencies: Dependencie[] = []
) {
const libs: Record<string, string> = {};
const depsMap = dependencies.reduce(
(prev, current) => {
prev[current.package] = current.library;
return prev;
},
{} as Record<string, string>
);
for (const { from, imports: names } of imports) {
names.forEach((name) => {
const library = depsMap[from];
if (library) {
libs[name] = library;
}
});
}
return {
libs
};
}

View File

@@ -0,0 +1,33 @@
import postcss from 'postcss';
export type CSSRules = Record<string, Record<string, string>>;
export interface ParseStyleResult {
styles: CSSRules;
css: string;
}
export function parseStyle(content: string) {
const styles: CSSRules = {};
const css: string[] = [];
const root = postcss.parse(content);
const classRegex = /^.[\w]+_[\w]{5,}/;
for (const rule of root.nodes) {
if (rule.type === 'rule') {
const style: Record<string, string> = {};
if (classRegex.test(rule.selector)) {
rule.nodes.forEach((decl) => {
if (decl.type === 'decl') {
style[decl.prop] = decl.value;
}
});
styles[rule.selector] = style;
} else {
css.push(rule.toString());
}
}
}
return {
styles,
css: css.join('\n')
};
}

View File

@@ -4,7 +4,8 @@ import {
type NodeEvents,
type JSExpression,
type NodeDirective,
type BlockSlot
type BlockSlot,
type JSFunction
} from '@vtj/core';
import { compileTemplate } from '@vue/compiler-sfc';
import {
@@ -20,13 +21,29 @@ import {
import { uid } from '@vtj/base';
import { isJSExpression, isNodeSchema } from '../shared';
import { getJSExpression, getJSFunction } from './utils';
import type { CSSRules } from './style';
let __slots: BlockSlot[] = [];
let __context: Record<string, Set<string>> = {};
let __handlers: Record<string, JSFunction> = {};
let __styles: CSSRules = {};
export function parseTemplate(id: string, name: string, content: string = '') {
export interface ParseTemplateOptions {
handlers?: Record<string, JSFunction>;
styles?: CSSRules;
}
export function parseTemplate(
id: string,
name: string,
content: string = '',
options?: ParseTemplateOptions
) {
__slots = [];
__context = {};
__handlers = options?.handlers || {};
__styles = options?.styles || {};
const result = compileTemplate({
id,
filename: name,
@@ -64,7 +81,21 @@ function getProps(nodes: Array<AttributeNode | DirectiveNode>) {
for (const item of nodes) {
// 普通属性
if (item.type === NodeTypes.ATTRIBUTE) {
props[item.name] = item.value?.content || '';
if (item.name === 'class') {
const classValue = item.value?.content || '';
const classRegex = /[\w]+_[\w]{5,}/;
const selector = classValue.match(classRegex)?.[0] || '';
const classes = classValue.split(' ').filter((n) => n !== selector);
const style = __styles[`.${selector}`];
if (style) {
props.style = style;
}
if (classes.length) {
props.class = classes.join(' ');
}
} else {
props[item.name] = item.value?.content || '';
}
}
// 动态绑定的属性
@@ -81,7 +112,10 @@ function getProps(nodes: Array<AttributeNode | DirectiveNode>) {
return props;
}
function getEvents(nodes: Array<AttributeNode | DirectiveNode>) {
function getEvents(
nodes: Array<AttributeNode | DirectiveNode>,
handlers: Record<string, JSFunction> = {}
) {
const events: NodeEvents = {};
for (const item of nodes) {
// 动态绑定的属性
@@ -97,11 +131,23 @@ function getEvents(nodes: Array<AttributeNode | DirectiveNode>) {
},
{} as Record<string, boolean>
);
events[item.arg.content] = {
name: item.arg.content,
handler: getJSFunction(`(${item.exp?.loc.source})`),
modifiers
};
const code = item.exp?.loc.source || '';
const regex = new RegExp(`${item.arg.content}_\[\\w\]\{5\,\}`);
const name = code.match(regex)?.[0] || '';
const handler = handlers[name];
if (name && handler) {
events[item.arg.content] = {
name: item.arg.content,
handler,
modifiers
};
} else {
events[item.arg.content] = {
name: item.arg.content,
handler: getJSFunction(`(${code})`),
modifiers
};
}
}
}
}
@@ -232,7 +278,7 @@ function createNodeSchema(
const dsl: NodeSchema = {
name: node.tag,
props: getProps(node.props),
events: getEvents(node.props),
events: getEvents(node.props, __handlers),
directives: getDirectives(scope || node)
};
dsl.id = getNodeId(dsl);