@vtj/coder
Version:
VTJ 是一款基于 Vue3 + Typescript 的低代码页面可视化设计器。内置低代码引擎、渲染器和代码生成器,面向前端开发者,开箱即用。 无缝嵌入本地开发工程,不改变前端开发流程和编码习惯。
717 lines (715 loc) • 19.9 kB
JavaScript
import { mitt as K, dedupArray as w, toArray as ee, kebabCase as I, 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.11.15
* @license <a href="https://vtj.pro/license.html">MIT License</a>
*/
const Le = "0.11.15";
/**!
* Copyright (c) 2025, VTJ.PRO All rights reserved.
* @name @vtj/core
* @author CHC chenhuachun1549@dingtalk.com
* @version 0.11.15
* @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 R = {
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 M(t, e) {
return e ? t : await B(t, {
parser: "vue",
...R,
plugins: [se, D, H, V]
});
}
async function ie(t, e) {
return e ? t : await B(t, {
parser: "babel-ts",
...R,
plugins: [D, H]
});
}
async function W(t, e) {
return e ? t : B(t, {
parser: "scss",
...R,
plugins: [V]
});
}
function S(t) {
return t && t.type === "JSExpression";
}
function N(t) {
return typeof t == "object" && t && t.type === "JSFunction";
}
function $(t) {
return S(t) || N(t);
}
function q(t) {
return t.replace(new RegExp("this.", "g"), "");
}
function J(t) {
return t.replace(/this\.context\??\./g, "");
}
function f(t, e = !0, n = !0, s = []) {
let r = $(t) ? t.value : e ? JSON.stringify(t) : t;
return r = k(r, s), n ? q(J(r)) : J(r);
}
function k(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();
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}": ${f(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 ue {
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 c = this.dependencies.find(
(a) => a.library === i
)?.package;
c && (this.imports[c] || (this.imports[c] = /* @__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 c of i) {
const a = this.collectImport(c);
if (a) {
const p = a.path.replace(/\$/g, "\\$");
s = s.replace(new RegExp(p, "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)
$(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 w(n);
}
collectContext(e, n) {
const s = new Set(n?.id ? this.context[n.id] : []), r = (e.directives || []).find((a) => a.name === "vFor");
let i = new Set(Array.from(s));
if (r) {
const { item: a = "item", index: p = "index" } = r.iterator || {};
i = /* @__PURE__ */ new Set([a, p, ...Array.from(i)]);
}
const c = e.slot;
if (c) {
const a = typeof c == "string" ? [] : c.params || [], p = a.length ? a : [`scope_${n?.id}`];
i = /* @__PURE__ */ new Set([...p, ...Array.from(i)]);
}
this.context[e.id] = i;
}
collectStyle(e) {
e.id && e.props?.style && Object.keys(e.props.style).length && !$(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 le(t = {}) {
return Object.entries(t).map(([e, n]) => {
const s = f(n, !1);
return `${e}:${s}`;
});
}
function pe(t = []) {
return t.map((e) => `${e.name}: {
from: '${e.from || e.name}',
default: ${f(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}: {}` : ($(n.default) && !n.default.value && (n.default.value = "undefined"), `${n.name}: {
type:${e(n.type)},
required: ${f(!!n.required, !0, !1)},
default: ${f(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(f(s, !1, !1));
return r = k(r, e), r.startsWith("async") ? `async ${n}${r.replace(/^async/, "")}` : `${n}${r}`;
});
}
function he(t = [], e = []) {
const n = t.reduce(
(i, c) => (c.id && N(c.source) && (i[`watcher_${c.id}`] = c.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",
"vShow",
"vModel",
"vFor",
"vBind",
"vHtml"
];
function z(t, e, n = [], s = {}, r) {
const i = [];
let c = {}, a = [];
const p = [];
let o = [];
return $e(t).forEach((u) => {
const m = [];
for (const d of u.children) {
let { id: b, name: g, invisible: F, from: y } = d;
if (F)
continue;
const C = ve(g, e, y);
C && a.push(C), X(y) && o.push({ id: y.id, name: g });
const { props: P, events: O, handlers: x } = we(
d,
b,
d.props,
d.events,
s,
n
), T = Ce(
d.directives,
n,
p
).join(" "), j = d.children ? Oe(
d.children,
n,
e,
s,
d
) : "";
Object.assign(c, x);
let E = "";
typeof j == "string" ? E = j : (E = (j?.nodes || []).join(`
`), Object.assign(c, j?.methods || {}), a = a.concat(j?.components || []), o = o.concat(j?.importBlocks || []));
const _ = ["@dcloudio/uni-h5", "@dcloudio/uni-ui"].includes(
y
) ? I(g) : Y(y) || Z(y) ? "component" : g;
m.push(
ye.includes(_) ? `<${_} ${T} ${P} ${O} />` : `<${_} ${T} ${P} ${O}>${E ? `
` + E.trim() : ""}</${_}>`
);
}
const h = xe(u.slot, m.join(`
`), r?.id);
i.push(h);
}), {
nodes: i,
methods: c,
directives: ge(p),
components: w(a),
importBlocks: w(o, "id")
};
}
function ge(t) {
return w(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 X(n) || s ? t : null;
}
function X(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" ? $(e) ? `:style="${f({
...e,
value: k(e.value, n)
})}"` : "" : t === "__class" && $(e) ? `:class="${f({
...e,
value: k(e.value, n)
})}"` : typeof e == "string" ? `${t}="${e}"` : $(e) ? `:${t}="${f({
...e,
value: k(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, $(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, c]) => be(i, c, 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(([a, p]) => {
const o = p.handler.value.startsWith("this."), l = o ? q(p.handler.value) : `${v(a)}_${t}${i}`;
return o || (s[l] = r.length ? {
type: "JSFunction",
value: `{
return (${p.handler.value}).apply(this, args);
}`
} : p.handler), Se(a, p, l, r, o);
}),
handlers: s
};
}
function we(t, e, n = {}, s = {}, r = {}, i) {
const { binders: c, handlers: a } = ke(e, s, r);
return {
props: je(t, n, i).join(" "),
handlers: a,
binders: c,
events: c.join(" ")
};
}
function Ce(t = [], e = [], n = []) {
const s = [], { vIf: r, vShow: i, vModels: c, vFor: a, vBind: p, vHtml: o, customDirectives: l } = Pe(t);
if (r && s.push(`v-if="${f(r.value, !0, !0, e)}"`), i && s.push(
`v-show="${f(i.value, !0, !0, e)}"`
), p && s.push(
`v-bind="${f(p.value, !0, !0, e)}"`
), c.forEach((u) => {
const m = G(u.modifiers, !0), h = u.arg ? S(u.arg) ? `:[${f(u.arg, !0, !0, e)}]` : `:${u.arg}` : "";
s.push(
`v-model${h}${m}="${f(u.value, !0, !0, e)}"`
);
}), a) {
const { item: u, index: m } = { item: "item", index: "index", ...a.iterator };
s.push(
`v-for="(${u}, ${m}) in ${f(a.value, !0, !0, e)}"`
);
}
return o && s.push(
`v-html="${f(o.value, !0, !0, e)}"`
), l && l.length && l.forEach((u) => {
if (!u.name) return;
let m = "", h = "";
S(u.name) ? (h = f(u.name, !0, !0, e), n.push(h)) : h = u.name;
const d = h?.startsWith("v") ? I(h) : I("v-" + h);
if (m += d, u.arg && (S(u.arg) ? m += `:[${f(u.name, !0, !0, e)}]` : m += `:${u.arg}`), u.modifiers) {
const b = Object.keys(u.modifiers);
b.length && (m += b.map((g) => "." + g));
}
u.value ? s.push(
`${m}="${f(u.value, !0, !0, e)}"`
) : s.push(m);
}), s;
}
function Pe(t = []) {
const e = t.filter(
(o) => L.includes(o.name)
), n = t.filter(
(o) => !L.includes(o.name)
), s = e.find(
(o) => v(o.name) === "vIf"
), r = e.find(
(o) => v(o.name) === "vFor"
), i = e.find(
(o) => v(o.name) === "vShow"
), c = e.find(
(o) => v(o.name) === "vBind"
), a = e.find(
(o) => v(o.name) === "vHtml"
), p = e.filter(
(o) => v(o.name) === "vModel"
);
return {
vIf: s,
vFor: r,
vShow: i,
vModels: p,
vBind: c,
vHtml: a,
customDirectives: n
};
}
function Oe(t, e, n, s, r) {
return typeof t == "string" ? t : S(t) ? `{{ ${f(t, !1, !0, e)} }}` : Array.isArray(t) ? z(t, n, e, s, r) : "";
}
function xe(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 Ee(t, e = [], n = [], s = {}, r = "web") {
const i = [
"@dcloudio/uni-h5",
"uni-h5",
"@dcloudio/uni-ui",
"uni-ui"
], c = {
vue: ["defineComponent", "reactive"]
}, a = [];
for (const o of e) {
const l = t.get(o.split(":")[0]);
if (l && l.package) {
const u = c[l.package] ?? (c[l.package] = []), m = l.parent || (l.alias || "").split(".")[0] || l.name;
u.push(m), r === "uniapp" && i.includes(l.package) && a.push(m);
}
}
for (const [o, l] of Object.entries(s))
(c[o] ?? (c[o] = [])).push(...Array.from(l)), r === "uniapp" && i.includes(o) && a.push(...Array.from(l));
return {
imports: Object.entries(c).filter(([o, l]) => r === "uniapp" ? !i.includes(o) && !!l.length : !!l.length).map(([o, l]) => `import { ${w(l).join(
","
)}} from '${o}';`).concat(n),
uniComponents: a
};
}
function _e(t = {}) {
const e = [];
for (const [n, s] of Object.entries(t))
e.push(`
${n} {
${ce(s)}
}
`);
return e.join(`
`);
}
function Ae(t = {}) {
const e = [];
return Object.entries(t).forEach(([n, s]) => {
e.push(
`const ${n} = provider.defineUrlSchemaComponent('${s.url}');`
);
}), e;
}
function Ne(t = {}) {
const e = [];
return Object.entries(t).forEach(([n, s]) => {
e.push(
`const ${n} = provider.definePluginComponent(${JSON.stringify(s)});`
);
}), e;
}
function Fe(t, e, n = "web") {
const { dsl: s } = t, r = Object.keys(s.computed || {}), i = A(s.lifeCycles, r), c = A(s.computed, r), a = he(s.watch, r), p = de(s.dataSources), { methods: o, nodes: l, components: u, importBlocks: m, directives: h } = z(
s.nodes || [],
e,
r,
t.context
), d = [...c, ...a.computed], b = A(
{
...o,
...s.methods || {}
},
r
), g = m.map((x) => `import ${x.name} from './${x.id}.vue';`);
let { imports: F, uniComponents: y } = Ee(
e,
u,
g,
t.imports,
n
);
const C = Object.keys({
...t.urlSchemas,
...t.blockPlugins
}), P = Ae(t.urlSchemas), O = Ne(t.blockPlugins);
return {
id: s.id,
version: s.__VERSION__,
name: s.name,
state: le(s.state).join(","),
inject: pe(s.inject).join(","),
props: fe(s.props).join(","),
emits: me(s.emits).join(","),
watch: a.watches.join(","),
lifeCycles: i.join(","),
computed: d.join(","),
methods: [...p, ...b].join(","),
imports: `
` + F.join(`
`),
components: ae(u, y).join(","),
directives: h.join(","),
returns: t.members.join(","),
template: l.join(`
`),
css: s.css || "",
style: _e(t.style),
urlSchemas: P.join(`
`),
blockPlugins: O.join(`
`),
asyncComponents: C.join(","),
uniComponents: y,
renderer: n === "uniapp" ? "@vtj/uni-app" : "@vtj/renderer"
};
}
const Ie = `
// @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<Record<string, any>>({ <%= 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>
`, Re = U(Ie), Te = U(Be);
async function Ue(t, e = /* @__PURE__ */ new Map(), n = [], s = "web", r) {
const i = new ue(ne(t), n), c = Fe(i, e, s), a = Re(c), p = Te({
template: c.template,
css: await W(c.css, r),
script: await ie(a, r),
style: await W(c.style, r)
});
return await M(p, r).catch((o) => (o.content = p, Promise.reject(o)));
}
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 M(e);
}
export {
Le as VTJ_CODER_VERSION,
De as createEmptyPage,
W as cssFormatter,
Ue as generator,
ie as tsFormatter,
M as vueFormatter
};