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