@vtj/coder
Version:
VTJ 是一款基于 Vue3 + Typescript 的低代码页面可视化设计器。内置低代码引擎、渲染器和代码生成器,面向前端开发者,开箱即用。 无缝嵌入本地开发工程,不改变前端开发流程和编码习惯。
763 lines (761 loc) • 21.1 kB
JavaScript
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
};