UNPKG

@vtj/coder

Version:

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

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