UNPKG

@vtj/coder

Version:

VTJ 是一款基于 Vue3 + Typescript 的低代码页面可视化设计器。内置低代码引擎、渲染器和代码生成器,面向前端开发者,开箱即用。 无缝嵌入本地开发工程,不改变前端开发流程和编码习惯。

742 lines (740 loc) 20.3 kB
import { mitt as K, dedupArray as C, toArray as ee, kebabCase as F, camelCase as v, isPlainObject as te, template as U, cloneDeep as ne } from "@vtj/base"; import { format as B } from "prettier/standalone"; import * as se from "prettier/plugins/html"; import * as D from "prettier/plugins/babel"; import * as V from "prettier/plugins/postcss"; import * as H from "prettier/plugins/estree"; /**! * Copyright (c) 2025, VTJ.PRO All rights reserved. * @name @vtj/coder * @author CHC chenhuachun1549@dingtalk.com * @version 0.12.44 * @license <a href="https://vtj.pro/license.html">MIT License</a> */ const Le = "0.12.44"; /**! * Copyright (c) 2025, VTJ.PRO All rights reserved. * @name @vtj/core * @author CHC chenhuachun1549@dingtalk.com * @version 0.12.44 * @license <a href="https://vtj.pro/license.html">MIT License</a> */ const re = [ "slot", "template", "component", "img", "div", "p", "h1", "h2", "h3", "span", "a" ]; K(); const T = { arrowParens: "always", bracketSpacing: !0, bracketSameLine: !0, endOfLine: "lf", htmlWhitespaceSensitivity: "css", insertPragma: !1, jsxBracketSameLine: !0, jsxSingleQuote: !0, printWidth: 80, proseWrap: "preserve", quoteProps: "as-needed", requirePragma: !1, semi: !0, singleQuote: !0, tabWidth: 2, trailingComma: "none", useTabs: !1, vueIndentScriptAndStyle: !0 }; async function q(t, e) { return e ? t : await B(t, { parser: "vue", ...T, plugins: [se, D, H, V] }); } async function ie(t, e) { if (e) return t; try { return (await B(t, { parser: "babel-ts", ...T, plugins: [D, H] })).replace(/;\n$/gi, ""); } catch (n) { return console.warn(n), t; } } async function W(t, e) { return e ? t : B(t, { parser: "scss", ...T, plugins: [V] }); } function k(t) { return t && t.type === "JSExpression"; } function N(t) { return typeof t == "object" && t && t.type === "JSFunction"; } function b(t) { return k(t) || N(t); } function M(t) { return t.replace(new RegExp("this.", "g"), ""); } function J(t) { return t.replace(/this\.context\??\./g, ""); } function h(t, e = !0, n = !0, s = []) { let r = b(t) ? t.value.trim().replace(/\"/g, "'").replace(/;$/, "") : e ? JSON.stringify(t) : t; return r = w(r, s), n ? M(J(r)) : J(r); } function w(t, e = []) { let n = t; for (const s of e) n = n.replace( new RegExp(`this.${s}.value`, "g"), `this.${s}` ); return n; } function Q(t) { let e = t.trim().replace(/;$/, ""); if (e = /^\((\(|async|function)/.test(e) ? e.substring(1, e.length - 1) : e, e.startsWith("{")) return e; if (e.startsWith("async function")) e = e.replace(/^async function/, "async"); else if (e.startsWith("function")) e = e.replace(/^function/, ""); else { const r = /^(async\s)?\([\w]*\)\s+\=\>\s([\w\W]+)/, i = e.match(r); i && i[2] && (i[2].startsWith("{") || (e = e.replace(i[2], `{ return ${i[2]} }`))), e = e.replace("=>", ""); } return e; } function oe(t = {}) { return Object.entries(t).map(([e, n]) => `"${e}": ${h(n)}`); } function G(t = {}, e = !1) { const n = Object.keys(t); return e ? n.map((s) => "." + s) : n; } function ce(t) { let e = ""; for (var n in t) if (t.hasOwnProperty(n)) { var s = t[n]; e += n + ": " + s + ";"; } return e; } function ae(t = [], e = []) { return t.filter((n) => !e.includes(n)); } class le { constructor(e, n) { this.dsl = e, this.dependencies = n, this.libraryRegex = this.collectLibrary(), this.walk(e), this.walkNodes(e), this.members = this.getLibraryMember(); } /** * { 'element-plus': ['ElButton', 'ElInput' ...] } */ imports = {}; context = {}; style = {}; members = []; urlSchemas = {}; blockPlugins = {}; libraryRegex = []; collectLibrary() { return this.dependencies.filter((e) => !!e.library).map((e) => new RegExp(`(this.\\$libs.${e.library}.([\\w]+))`, "g")); } /** * 收集 import 信息 * @param regexMatchItem ex: this.$libs.ElementPlus.ElButton * @returns ex: { name: 'ElButton', path: 'this.$libs.ElementPlus.', library: 'ElementPlus' } */ collectImport(e) { const n = e.split("."); if (n.length === 4) { const s = n.pop(), r = n.join(".") + ".", i = n.pop(); if (s && i) { const o = this.dependencies.find( (c) => c.library === i )?.package; o && (this.imports[o] || (this.imports[o] = /* @__PURE__ */ new Set())).add(s); } return { name: s, path: r, library: i }; } return null; } // 代码中包含依赖库的引用,需要从代码中移除 replaceLibraryPath(e) { const { libraryRegex: n } = this; let s = e.value; for (const r of n) { const i = e.value?.match(r) || []; for (const o of i) { const c = this.collectImport(o); if (c) { const f = c.path.replace(/\$/g, "\\$"); s = s.replace(new RegExp(f, "g"), ""); } } } return s; } walk(e) { const n = (s) => { if (!s || typeof s != "object") return; if (Array.isArray(s)) { for (let i of s) n(i); return; } const r = Object.values(s); for (const i of r) b(i) ? i.value = this.replaceLibraryPath(i) : n(i); }; n(e); } getLibraryMember(e = []) { let n = [...e]; const s = { ...this.imports }; delete s["uni-h5"], delete s["@dcloudio/uni-h5"], delete s["uni-ui"], delete s["@dcloudio/uni-ui"]; for (const r of Object.values(s)) n = n.concat(Array.from(r)); return C(n); } collectContext(e, n) { const s = new Set(n?.id ? this.context[n.id] : []), r = (e.directives || []).find((c) => c.name === "vFor"); let i = new Set(Array.from(s)); if (r) { const { item: c = "item", index: f = "index" } = r.iterator || {}; i = /* @__PURE__ */ new Set([c, f, ...Array.from(i)]); } const o = e.slot; if (o) { const c = typeof o == "string" ? [] : o.params || [], f = c.length ? c : [`scope_${n?.id}`]; i = /* @__PURE__ */ new Set([...f, ...Array.from(i)]); } this.context[e.id] = i; } collectStyle(e) { e.id && e.props?.style && Object.keys(e.props.style).length && !b(e.props.style) && (this.style[`.${e.name}_${e.id}`] = e.props.style); } collectUrlSchema(e) { typeof e.from == "object" && e.from.type === "UrlSchema" && (this.urlSchemas[e.name] = e.from); } collectBlockPlugin(e) { typeof e.from == "object" && e.from.type === "Plugin" && (this.blockPlugins[e.name] = e.from); } walkNodes(e) { const n = (s, r) => { this.collectContext(s, r), this.collectStyle(s), this.collectUrlSchema(s), this.collectBlockPlugin(s), Array.isArray(s.children) && s.children.forEach((i) => n(i, s)); }; Array.isArray(e.nodes) && e.nodes.forEach((s) => n(s)); } } function ue(t = {}) { return Object.entries(t).map(([e, n]) => { const s = h(n, !1); return `${e}:${s}`; }); } function pe(t = []) { return t.map((e) => `${e.name}: { from: '${e.from || e.name}', default: ${h(e.default, !0, !1)} }`); } function fe(t = []) { const e = (n) => n ? `[${ee(n).map((i) => i.replace(/\'|\"/gi, "")).join(",")}]` : void 0; return t.map((n) => typeof n == "string" ? `${n}: {}` : (b(n.default) && !n.default.value && (n.default.value = "undefined"), `${n.name}: { type:${e(n.type)}, required: ${h(!!n.required, !0, !1)}, default: ${h(n.default, !0, !1)} }`)); } function me(t = []) { return t.map((e) => `'${typeof e == "string" ? e : e.name}'`); } function A(t = {}, e = []) { return Object.entries(t).map(([n, s]) => { let r = Q(h(s, !1, !1)); return r = w(r, e), r.startsWith("async") ? `async ${n}${r.replace(/^async/, "")}` : `${n}${r}`; }); } function he(t = [], e = []) { const n = t.reduce( (i, o) => (o.id && N(o.source) && (i[`watcher_${o.id}`] = o.source), i), {} ), s = A(n, e), r = t.map((i) => `watcher_${i.id}: { deep: ${i.deep}, immediate:${i.immediate}, handler${Q(i.handler.value)} }`); return { computed: s, watches: r }; } function de(t = {}) { return Object.values(t).map((e) => { if (e.type === "mock") { const n = N(e.mockTemplate) && e.mockTemplate.value || "(params) => ({})"; return `async ${e.name}(...args:any[]) { const mock = this.provider.createMock(${n}) return await mock.apply(this, args); }`; } else { const n = N(e.transform) && e.transform.value || "(res) => res"; return `async ${e.name}(...args:any[]) { return await this.provider.apis['${e.ref}'].apply(this, args).then(${n}); }`; } }); } const ye = [ "img", "input", "br", "hr", "area", "base", "col", "embed", "link", "meta", "param", "source", "track", "wbr" ], L = [ "vIf", "vElseIf", "vElse", "vShow", "vModel", "vFor", "vBind", "vHtml" ]; function X(t, e, n = [], s = {}, r) { const i = []; let o = {}, c = []; const f = []; let l = []; return $e(t).forEach((a) => { const y = []; for (const m of a.children) { let { id: d, name: $, invisible: j, from: g } = m; if (j) continue; const E = ve($, e, g); E && c.push(E), z(g) && l.push({ id: g.id, name: $ }); const { props: P, events: O, handlers: x } = we( m, d, m.props, m.events, s, n ), R = Ce( m.directives, n, f ).join(" "), S = m.children ? Pe( m.children, n, e, s, m ) : ""; Object.assign(o, x); let _ = ""; typeof S == "string" ? _ = S : (_ = (S?.nodes || []).join(` `), Object.assign(o, S?.methods || {}), c = c.concat(S?.components || []), l = l.concat(S?.importBlocks || [])); const I = ["@dcloudio/uni-h5", "@dcloudio/uni-ui"].includes( g || e.get($)?.package ) ? F($) : Y(g) || Z(g) ? "component" : $; y.push( ye.includes(I) ? `<${I} ${R} ${P} ${O} />` : `<${I} ${R} ${P} ${O}>${_ ? ` ` + _.trim() : ""}</${I}>` ); } const p = Oe(a.slot, y.join(` `), r?.id); i.push(p); }), { nodes: i, methods: o, directives: ge(f), components: C(c), importBlocks: C(l, "id") }; } function ge(t) { return C(t).map((e) => `${e.startsWith("v") ? e.substring(1) : e}:${e}`); } function $e(t = []) { const e = /* @__PURE__ */ new Map(); for (const n of t) { const s = typeof n.slot == "string" ? n.slot : n.slot?.name, r = e.get(s); r ? r.children.push(n) : e.set(s, { slot: n.slot, children: [n] }); } return e; } function ve(t, e, n) { if (re.includes(t)) return null; const s = e.get(t); if (s && s.alias) { const r = s.parent ? `${s.parent}.${s.alias}` : s.alias; return `${t}: ${r}`; } return z(n) || s ? t : null; } function z(t) { return !!t && typeof t == "object" && t.type === "Schema"; } function Y(t) { return typeof t == "object" && t.type === "UrlSchema"; } function Z(t) { return typeof t == "object" && t.type === "Plugin"; } function be(t, e, n = []) { return t === "style" ? b(e) ? `:style="${h({ ...e, value: w(e.value, n) })}"` : "" : t === "__class" && b(e) ? `:class="${h({ ...e, value: w(e.value, n) })}"` : typeof e == "string" ? `${t}="${e}"` : b(e) ? `:${t}="${h({ ...e, value: w(e.value, n) })}"` : te(e) ? `:${t}='{${oe( e ).join(", ")}}'` : `:${t}='${JSON.stringify(e)}'`; } function je(t, e = {}, n = []) { if (!!Object.keys(e.style || {}).length) { const i = `${t.name}_${t.id}`; e.class ? typeof e.class == "string" ? e.class = [e.class, i].join(" ") : (e.__class = e.class, e.class = i) : e.class = i, b(e.style) || delete e.style; } const r = t.from; return (Y(r) || Z(r)) && (e.is = { type: "JSExpression", value: t.name }), Object.entries(e).map(([i, o]) => be(i, o, n)); } function Se(t, e, n, s, r) { const i = G(e.modifiers, !0); return r ? `@${t}${i.join("")}="${n}"` : s && s.length > 0 ? `@${t}${i.join("")}="(...args:any[]) => ${n}"` : `@${t}${i.join("")}="${n}"`; } function ke(t, e = {}, n = {}) { const s = {}, r = Array.from(n[t] || /* @__PURE__ */ new Set([])), i = r.length ? `({${r.join(", ")}}, args)` : ""; return { binders: Object.entries(e).map(([c, f]) => { const l = f.handler.value.startsWith("this."), u = l ? M(f.handler.value) : `${v(c)}_${t}${i}`; return l || (s[u] = r.length ? { type: "JSFunction", value: `{ return (${f.handler.value}).apply(this, args); }` } : f.handler), Se(c, f, u, r, l); }), handlers: s }; } function we(t, e, n = {}, s = {}, r = {}, i) { const { binders: o, handlers: c } = ke(e, s, r); return { props: je(t, n, i).join(" "), handlers: c, binders: o, events: o.join(" ") }; } function Ce(t = [], e = [], n = []) { const s = [], { vIf: r, vElse: i, vElseIf: o, vShow: c, vModels: f, vFor: l, vBind: u, vHtml: a, customDirectives: y } = Ee(t); if (r && s.push(`v-if="${h(r.value, !0, !0, e)}"`), o && s.push( `v-else-if="${h(o.value, !0, !0, e)}"` ), i && s.push("v-else"), c && s.push( `v-show="${h(c.value, !0, !0, e)}"` ), u && s.push( `v-bind="${h(u.value, !0, !0, e)}"` ), f.forEach((p) => { const m = G(p.modifiers, !0), d = p.arg ? k(p.arg) ? `:[${h(p.arg, !0, !0, e)}]` : `:${p.arg}` : ""; s.push( `v-model${d}${m}="${h(p.value, !0, !0, e)}"` ); }), l) { const { item: p, index: m } = { item: "item", index: "index", ...l.iterator }; s.push( `v-for="(${p}, ${m}) in ${h(l.value, !0, !0, e)}"` ); } return a && s.push( `v-html="${h(a.value, !0, !0, e)}"` ), y && y.length && y.forEach((p) => { if (!p.name) return; let m = "", d = ""; k(p.name) ? (d = h(p.name, !0, !0, e), n.push(d)) : d = p.name; const $ = d?.startsWith("v") ? F(d) : F("v-" + d); if (m += $, p.arg && (k(p.arg) ? m += `:[${h(p.name, !0, !0, e)}]` : m += `:${p.arg}`), p.modifiers) { const j = Object.keys(p.modifiers); j.length && (m += j.map((g) => "." + g)); } p.value ? s.push( `${m}="${h(p.value, !0, !0, e)}"` ) : s.push(m); }), s; } function Ee(t = []) { const e = t.filter( (a) => L.includes(a.name) ), n = t.filter( (a) => !L.includes(a.name) ), s = e.find( (a) => v(a.name) === "vIf" ), r = e.find( (a) => v(a.name) === "vElseIf" ), i = e.find( (a) => v(a.name) === "vElse" ), o = e.find( (a) => v(a.name) === "vFor" ), c = e.find( (a) => v(a.name) === "vShow" ), f = e.find( (a) => v(a.name) === "vBind" ), l = e.find( (a) => v(a.name) === "vHtml" ), u = e.filter( (a) => v(a.name) === "vModel" ); return { vIf: s, vElseIf: r, vElse: i, vFor: o, vShow: c, vModels: u, vBind: f, vHtml: l, customDirectives: n }; } function Pe(t, e, n, s, r) { return typeof t == "string" ? t : k(t) ? `{{ ${h(t, !1, !0, e)} }}` : Array.isArray(t) ? X(t, n, e, s, r) : ""; } function Oe(t, e, n) { if (!t) return e; const s = typeof t == "string" ? { name: t, params: [] } : { params: [], ...t }; return `<template ${`#${s.name}="${s.params?.length > 0 ? `{${s.params?.join(",")}}` : `scope_${n}`}"`}> ${e} </template>`; } function xe(t, e = [], n = [], s = {}, r = "web") { const i = [ "@dcloudio/uni-h5", "uni-h5", "@dcloudio/uni-ui", "uni-ui" ], o = { vue: ["defineComponent", "reactive"] }, c = []; for (const l of e) { const u = t.get(l.split(":")[0]); if (u && u.package) { const a = o[u.package] ?? (o[u.package] = []), y = u.parent || (u.alias || "").split(".")[0] || u.name; a.push(y), r === "uniapp" && i.includes(u.package) && c.push(y); } } for (const [l, u] of Object.entries(s)) (o[l] ?? (o[l] = [])).push(...Array.from(u)), r === "uniapp" && i.includes(l) && c.push(...Array.from(u)); return { imports: Object.entries(o).filter(([l, u]) => r === "uniapp" ? !i.includes(l) && !!u.length : !!u.length).map(([l, u]) => `import { ${C(u).join( "," )}} from '${l}';`).concat(n), uniComponents: c }; } function _e(t = {}) { const e = []; for (const [n, s] of Object.entries(t)) e.push(` ${n} { ${ce(s)} } `); return e.join(` `); } function Ie(t = {}) { const e = []; return Object.entries(t).forEach(([n, s]) => { e.push( `const ${n} = provider.defineUrlSchemaComponent('${s.url}');` ); }), e; } function Ae(t = {}) { const e = []; return Object.entries(t).forEach(([n, s]) => { e.push( `const ${n} = provider.definePluginComponent(${JSON.stringify(s)});` ); }), e; } function Ne(t, e, n = "web") { const { dsl: s } = t, r = Object.keys(s.computed || {}), i = A(s.lifeCycles, r), o = A(s.computed, r), c = he(s.watch, r), f = de(s.dataSources), { methods: l, nodes: u, components: a, importBlocks: y, directives: p } = X( s.nodes || [], e, r, t.context ), m = [...o, ...c.computed], d = A( { ...l, ...s.methods || {} }, r ), $ = y.map((x) => `import ${x.name} from './${x.id}.vue';`); let { imports: j, uniComponents: g } = xe( e, a, $, t.imports, n ); const E = Object.keys({ ...t.urlSchemas, ...t.blockPlugins }), P = Ie(t.urlSchemas), O = Ae(t.blockPlugins); return { id: s.id, version: s.__VERSION__, name: s.name, state: ue(s.state).join(","), inject: pe(s.inject).join(","), props: fe(s.props).join(","), emits: me(s.emits).join(","), watch: c.watches.join(","), lifeCycles: i.join(","), computed: m.join(","), methods: [...f, ...d].join(","), imports: ` ` + j.join(` `), components: ae(a, g).join(","), directives: p.join(","), returns: t.members.join(","), template: u.join(` `), css: s.css || "", style: _e(t.style), urlSchemas: P.join(` `), blockPlugins: O.join(` `), asyncComponents: E.join(","), uniComponents: g, renderer: n === "uniapp" ? "@vtj/uni-app" : "@vtj/renderer" }; } const Fe = ` // @ts-nocheck <%= imports %> import { useProvider } from '<%= renderer %>'; export default defineComponent({ name: '<%= name %>', <% if(inject) { %> inject: { <%= inject %>}, <% } %> <% if(components) { %> components: { <%= components %> }, <% } %> <% if(directives) { %> directives: { <%= directives %> }, <% } %> <% if(props) { %> props: { <%= props %> }, <% } %> <% if(emits) {%> emits: [<%= emits %>], <% } %> setup(props) { const provider = useProvider({ id: '<%= id %>', version: '<%= version %>' }); const state = reactive({ <%= state %> }); <%= urlSchemas %> <%= blockPlugins %> return { state, props, provider <% if(asyncComponents) { %>, <%= asyncComponents %> <% }%> <% if(returns) { %>, <%= returns %> <% } %> }; }, <% if(computed) { %> computed: { <%= computed %> }, <% } %> <% if(methods) { %> methods: { <%= methods %> }, <% } %> <% if(watch) { %> watch: { <%= watch %> }, <% } %> <%= lifeCycles %> }); `.replace(/(\n|\r|\t)/g, ""), Be = ` <template> <%= template %> </template> <script lang="ts"> <%= script %> <\/script> <style lang="scss" scoped> <%= css %> <%= style %> </style> `, Te = U(Fe), Re = U(Be); async function Ue(t, e = /* @__PURE__ */ new Map(), n = [], s = "web", r) { const i = new le(ne(t), n), o = Ne(i, e, s), c = Te(o), f = Re({ template: o.template, css: await W(o.css, r), script: await ie(c, r), style: await W(o.style, r) }); return await q(f, r).catch((l) => (l.content = f, Promise.reject(l))); } async function De(t) { const e = ` <template> <div> <h3>源码模式页面</h3> <div>文件路径:/.vtj/vue/${t.id}.vue</div> </div> </template> <script lang="ts" setup> <\/script> <style scoped lang="scss"> </style> `; return await q(e); } export { Le as VTJ_CODER_VERSION, De as createEmptyPage, W as cssFormatter, Ue as generator, ie as tsFormatter, q as vueFormatter };