mechanica 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bin.js +37 -0
- package/bin/build.js +63 -0
- package/bin/compile.js +481 -0
- package/bin/push.js +80 -0
- package/dist/editor.js +10 -11
- package/dist/editor.js.map +1 -1
- package/dist/main.js +12 -4
- package/dist/main.js.map +1 -1
- package/dist/plugin.d.ts +0 -1
- package/dist/plugin.js.map +1 -1
- package/dist/schemaAliases-DFqu3A9-.js +156 -0
- package/dist/schemaAliases-DFqu3A9-.js.map +1 -0
- package/dist/style.css +42 -42
- package/dist/updater.js +5 -0
- package/dist/updater.js.map +1 -1
- package/package.json +3 -3
- package/dist/schemaAliases-CX3SQvLF.js +0 -54
- package/dist/schemaAliases-CX3SQvLF.js.map +0 -1
package/bin/bin.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import buildClient from './build.js'
|
|
3
|
+
import push from './push.js'
|
|
4
|
+
import compile from './compile.js'
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import { join } from 'path'
|
|
7
|
+
|
|
8
|
+
const keys = {}
|
|
9
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
10
|
+
const key = process.argv[i].slice(2)
|
|
11
|
+
if (key.includes("=")) {
|
|
12
|
+
const [ newKey, value ] = key.split("=")
|
|
13
|
+
keys[newKey] = value
|
|
14
|
+
} else {
|
|
15
|
+
keys[key] = process.argv[i+1]?.startsWith("--") === false? process.argv[i+1]: true
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (process.argv[2] === "build") {
|
|
20
|
+
buildClient()
|
|
21
|
+
} else if (process.argv[2] === "push") {
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(join(process.cwd(), ".env"))) {
|
|
24
|
+
const env = fs.readFileSync(join(process.cwd(), ".env"), "utf-8")
|
|
25
|
+
const data = Object.fromEntries(env.split("\n").map(item => item.split("=").map(str => str.trim())))
|
|
26
|
+
Object.assign(process.env, data)
|
|
27
|
+
}
|
|
28
|
+
} catch(e) {
|
|
29
|
+
console.warn("Failed to parse .env file. Use system env values")
|
|
30
|
+
}
|
|
31
|
+
push(keys.key ?? process.env.BUILDER_API_KEY, keys.host ?? process.env.BUILDER_HOST ?? "https://builder.jt3.ru", keys.folder)
|
|
32
|
+
} else if (process.argv[2] === "export") {
|
|
33
|
+
compile()
|
|
34
|
+
} else {
|
|
35
|
+
console.info(`Use "mechanica build" to build client files`)
|
|
36
|
+
console.info(`Use "mechanica push --apiKey=<key> --host=<host> --dir=<dir> to push your builded client files`)
|
|
37
|
+
}
|
package/bin/build.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { build, loadConfigFromFile } from 'vite'
|
|
2
|
+
|
|
3
|
+
export default async () => {
|
|
4
|
+
|
|
5
|
+
const defaultConfig = await loadConfigFromFile("vite.config.ts")
|
|
6
|
+
const mechanicaPlugin = defaultConfig.config.plugins.find(item => item.name === 'mechanica')
|
|
7
|
+
if (!mechanicaPlugin) {
|
|
8
|
+
console.error("Add mechanica plugin to your vite config file")
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const userOptions = mechanicaPlugin.userOptions ?? {}
|
|
13
|
+
|
|
14
|
+
console.info("Build client files...")
|
|
15
|
+
await build({
|
|
16
|
+
configFile: "vite.config.ts",
|
|
17
|
+
build: {
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
output: {
|
|
20
|
+
manualChunks: id => {
|
|
21
|
+
const [ path, query ] = id.split("?")
|
|
22
|
+
if (path.endsWith("/updater.ts")) return "updater"
|
|
23
|
+
// if (query === 'dev' || path.endsWith("DevApp.vue")) return 'dev'
|
|
24
|
+
// if (path.endsWith('/entry.ts') || path.endsWith('index.html')) return 'index'
|
|
25
|
+
return 'app'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
console.info("")
|
|
33
|
+
console.info("Build client SSR files...")
|
|
34
|
+
|
|
35
|
+
await build({
|
|
36
|
+
configFile: "vite.config.ts",
|
|
37
|
+
ssr: {
|
|
38
|
+
// noExternal: ['vue', 'vue-router', 'pinia']
|
|
39
|
+
// external: [ "vue", "pinia", "@vueuse/core", "compact-json-schema" ],
|
|
40
|
+
noExternal: true,
|
|
41
|
+
optimizeDeps: {
|
|
42
|
+
include: ['*']
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
// resolve: {
|
|
46
|
+
// alias: {
|
|
47
|
+
// // Используем ESM-версию Vue, оптимизированную для SSR
|
|
48
|
+
// 'vue/server-renderer': '../../node_modules/vue/server-renderer/index.js',
|
|
49
|
+
// 'vue': '../../node_modules/vue/dist/vue.runtime.global.js',
|
|
50
|
+
// }
|
|
51
|
+
// },
|
|
52
|
+
build: {
|
|
53
|
+
emptyOutDir: false,
|
|
54
|
+
ssr: true,
|
|
55
|
+
rollupOptions: {
|
|
56
|
+
input: {
|
|
57
|
+
ssr: `${userOptions.entrySsr ?? './src/main.ts'}?ssr`
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
package/bin/compile.js
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { resolve, parse, join, relative } from "node:path";
|
|
4
|
+
const defaultImageSrc = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiBmaWxsPSIjRjJGMkYyIiByeD0iOCIvPjxwYXRoIGZpbGw9IiM2RDZENkQiIGQ9Ik00MS42IDE5LjZIMjIuNEE0LjQgNC40IDAgMCAwIDE4IDI0djE2YTQuNCA0LjQgMCAwIDAgNC40IDQuNGgxOS4yQTQuNCA0LjQgMCAwIDAgNDYgNDBWMjRhNC40IDQuNCAwIDAgMC00LjQtNC40TTIyLjQgMjJoMTkuMmEyIDIgMCAwIDEgMiAydjEzLjEwNGwtNC4zNTItNC4zNTJhMS4xMiAxLjEyIDAgMCAwLS44OTYtLjM1MiAxLjI4IDEuMjggMCAwIDAtLjg4LjQzMmwtMi4wNjQgMi40OC03LjM2LTcuMzZhMS4xMiAxLjEyIDAgMCAwLS44NDgtLjM1MiAxLjI4IDEuMjggMCAwIDAtLjg4LjQzMmwtNS45MiA3LjA1NlYyNGEyIDIgMCAwIDEgMi0ybS0yIDE4di0xLjE2OGw2Ljg4LTguMjU2IDYuNTkyIDYuNTkyLTQuMDMyIDQuOEgyMi40YTIgMiAwIDAgMS0yLTEuOTY4bTIxLjIgMmgtOC42NGw1LjUyLTYuNjI0IDUuMDQgNS4wNEExLjk3IDEuOTcgMCAwIDEgNDEuNiA0MiIvPjwvc3ZnPg==";
|
|
5
|
+
const getDefaultValue = (schema) => {
|
|
6
|
+
if (schema.default) return schema.default;
|
|
7
|
+
if (schema.nullable) return null;
|
|
8
|
+
if (schema.type === "object" && schema.format === "image") {
|
|
9
|
+
return { src: defaultImageSrc, previewSrc: defaultImageSrc };
|
|
10
|
+
}
|
|
11
|
+
if (schema.type === "number" || schema.type === "integer") return 0;
|
|
12
|
+
if (schema.type === "object") {
|
|
13
|
+
if (!schema.properties) return {};
|
|
14
|
+
return Object.fromEntries(Object.entries(schema.properties).map(([key, value]) => {
|
|
15
|
+
var _a;
|
|
16
|
+
if (!((_a = schema.required) == null ? void 0 : _a.includes(key))) {
|
|
17
|
+
return [key, void 0];
|
|
18
|
+
}
|
|
19
|
+
return [key, getDefaultValue(value)];
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
if (schema.type === "array" && schema.format === "richText") {
|
|
23
|
+
return [{ text: "" }];
|
|
24
|
+
}
|
|
25
|
+
if (schema.type === "array") return [];
|
|
26
|
+
if (schema.type === "boolean") return false;
|
|
27
|
+
return "";
|
|
28
|
+
};
|
|
29
|
+
const passDefaultValue = (state, schema) => {
|
|
30
|
+
if (!schema) return state;
|
|
31
|
+
if (schema.type === "object" && state) {
|
|
32
|
+
for (let key in schema.properties) {
|
|
33
|
+
if (!(key in state) && !schema.required.includes(key)) continue;
|
|
34
|
+
state[key] = passDefaultValue(state[key], schema.properties[key]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return state ?? getDefaultValue(schema);
|
|
38
|
+
};
|
|
39
|
+
const walkTree = (blocks, callback) => {
|
|
40
|
+
for (let block of blocks) {
|
|
41
|
+
callback(block);
|
|
42
|
+
if (!block.children) continue;
|
|
43
|
+
if (Array.isArray(block.children)) {
|
|
44
|
+
walkTree(block.children, callback);
|
|
45
|
+
} else {
|
|
46
|
+
for (let value of Object.values(block.children)) {
|
|
47
|
+
walkTree(value, callback);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const walkSchema = (obj, schema, callback) => {
|
|
53
|
+
if (schema.type === "object" && schema.properties) {
|
|
54
|
+
if (!obj) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
for (let [key, childSchema] of Object.entries(schema.properties)) {
|
|
58
|
+
const isRequired = schema.required.includes(key);
|
|
59
|
+
callback(obj[key], childSchema, key, obj, isRequired);
|
|
60
|
+
if (childSchema.type === "array" || childSchema.type === "object") {
|
|
61
|
+
walkSchema(obj[key], childSchema, callback);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (schema.type === "array" && schema.items) {
|
|
66
|
+
for (let value of obj) {
|
|
67
|
+
callback(value, schema.items);
|
|
68
|
+
if (schema.items.type === "array" || schema.items.type === "object") {
|
|
69
|
+
walkSchema(value, schema.items, callback);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const updaterScript = 'window.__MECHANICA_STATE__ = null;\nwindow.addEventListener("message", (e) => {\n const { type, payload } = e.data;\n const { content, data } = payload;\n if (type === "update:state") {\n const state = window.__MECHANICA_STATE__;\n if (!state) {\n console.warn("Mechanica state not found. Updater is not available");\n }\n if (data) {\n for (let [key, value] of Object.entries(data)) {\n if (!state.data[key]) {\n state.data[key] = {};\n } else {\n for (let _key in state.data[key]) {\n if (_key in value) continue;\n delete state.data[key][_key];\n }\n }\n Object.assign(state.data[key], value);\n }\n }\n state.content.value = content;\n }\n});\n//# sourceMappingURL=updater.js.map\n';
|
|
75
|
+
const getValueByPath = (data, key) => {
|
|
76
|
+
let resp = data;
|
|
77
|
+
for (let prop of key.split(".")) {
|
|
78
|
+
resp = resp[prop];
|
|
79
|
+
if (!resp) return resp;
|
|
80
|
+
}
|
|
81
|
+
return resp;
|
|
82
|
+
};
|
|
83
|
+
const passDataToHTML = (html, data) => {
|
|
84
|
+
let resp = "";
|
|
85
|
+
let lastIndex = 0;
|
|
86
|
+
for (let place of html.matchAll(/{{.+}}/g)) {
|
|
87
|
+
resp += html.slice(lastIndex, place.index) + (getValueByPath(data, place[0].slice(2, -2)) ?? "");
|
|
88
|
+
lastIndex = place.index + place[0].length;
|
|
89
|
+
}
|
|
90
|
+
resp += html.slice(lastIndex);
|
|
91
|
+
return resp;
|
|
92
|
+
};
|
|
93
|
+
const generatePage = async (options) => {
|
|
94
|
+
walkTree(options.state.content, (block) => {
|
|
95
|
+
const schema = options.blocksMap.get(block.blockId);
|
|
96
|
+
if (!schema) return;
|
|
97
|
+
passDefaultValue(block.data, schema.props);
|
|
98
|
+
});
|
|
99
|
+
const data = { ...options.projectData, ...options.state.data };
|
|
100
|
+
const finalData = Object.fromEntries(options.dataEntries.map((entry) => [entry.id, passDefaultValue(data[entry.id] ?? {}, entry.props)]));
|
|
101
|
+
const state = {
|
|
102
|
+
content: options.state.content,
|
|
103
|
+
data: finalData,
|
|
104
|
+
baseUrl: options.baseUrl,
|
|
105
|
+
query: void 0
|
|
106
|
+
};
|
|
107
|
+
if (options.queryResolver) {
|
|
108
|
+
const getQuery2 = async (key) => {
|
|
109
|
+
const resp = await options.queryResolver(key);
|
|
110
|
+
state.query = state.query ?? {};
|
|
111
|
+
state.query[key] = resp;
|
|
112
|
+
return resp;
|
|
113
|
+
};
|
|
114
|
+
globalThis.getQuery = getQuery2;
|
|
115
|
+
}
|
|
116
|
+
globalThis.location = { pathname: options.path };
|
|
117
|
+
const html = await options.render(state, options.path ?? "");
|
|
118
|
+
let index = options.index;
|
|
119
|
+
index = passDataToHTML(index, state.data);
|
|
120
|
+
const match = index.match(/(<div.*? id="app".*?>).*?<\/div>/);
|
|
121
|
+
if (match !== null) {
|
|
122
|
+
index = index.slice(0, match.index + match[1].length) + html + index.slice(match.index + match[0].length - "</div>".length);
|
|
123
|
+
}
|
|
124
|
+
let contentCode = `
|
|
125
|
+
<script>
|
|
126
|
+
window.state=${JSON.stringify(state)}
|
|
127
|
+
<\/script>
|
|
128
|
+
`.trim();
|
|
129
|
+
if (options.isDev) {
|
|
130
|
+
contentCode += `
|
|
131
|
+
<script >
|
|
132
|
+
${updaterScript}
|
|
133
|
+
<\/script>
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
136
|
+
if (options.assetsUrl) {
|
|
137
|
+
index = index.replace(/\/assets\//g, options.assetsUrl);
|
|
138
|
+
}
|
|
139
|
+
index = index.replace("</body>", `${contentCode}
|
|
140
|
+
</body>`);
|
|
141
|
+
return index;
|
|
142
|
+
};
|
|
143
|
+
async function* generateProject(options) {
|
|
144
|
+
for (let page of options.pages) {
|
|
145
|
+
page.content = page.content ?? [];
|
|
146
|
+
walkTree(page.content, (block) => {
|
|
147
|
+
const schema = options.blocksMap.get(block.blockId);
|
|
148
|
+
walkSchema(block.data, schema.props, (val, schema2, key, parent) => {
|
|
149
|
+
if (!val) return;
|
|
150
|
+
if (schema2.format === "image") {
|
|
151
|
+
if (val.src) {
|
|
152
|
+
val.src = options.onFile(val.src);
|
|
153
|
+
}
|
|
154
|
+
if (val.previewSrc) {
|
|
155
|
+
val.previewSrc = options.onFile(val.previewSrc);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (schema2.format === "richText" && schema2.type === "array") {
|
|
159
|
+
for (let row of val) {
|
|
160
|
+
if (row.image) {
|
|
161
|
+
if (row.image.src) {
|
|
162
|
+
row.image.src = options.onFile(row.image.src);
|
|
163
|
+
}
|
|
164
|
+
if (row.image.previewSrc) {
|
|
165
|
+
row.image.previewSrc = options.onFile(row.image.previewSrc);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
const assetsUrl = options.assetsUrl ?? (options.baseUrl && options.baseUrl !== "/" ? options.baseUrl + "/assets/" : void 0);
|
|
173
|
+
const html = await generatePage({ ...options, assetsUrl, state: page, path: page.path });
|
|
174
|
+
yield [html, page.path];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const getBlockName = (path) => {
|
|
178
|
+
if (!path) return void 0;
|
|
179
|
+
const str = path.split("/").at(-1);
|
|
180
|
+
return (str == null ? void 0 : str.split(".", 1)[0].replace(/([A-Z])/g, " $1").trimStart()) ?? "Unnamed block";
|
|
181
|
+
};
|
|
182
|
+
const aliases$1 = {};
|
|
183
|
+
const registerAlias$1 = (key, item) => {
|
|
184
|
+
aliases$1[key] = item;
|
|
185
|
+
};
|
|
186
|
+
const baseTypes = ["string", "boolean", "number", "integer", "bigint", "object", "array"];
|
|
187
|
+
const isBaseType = (key) => {
|
|
188
|
+
return baseTypes.some((item) => item === key || item + "?" === key || item + "??" === key);
|
|
189
|
+
};
|
|
190
|
+
const trimType = (type) => {
|
|
191
|
+
if (type.endsWith("??"))
|
|
192
|
+
return type.slice(0, -2);
|
|
193
|
+
if (type.endsWith("?"))
|
|
194
|
+
return type.slice(0, -1);
|
|
195
|
+
return type;
|
|
196
|
+
};
|
|
197
|
+
const parseObjectFields = (props, unfoldSchema2) => {
|
|
198
|
+
const required = [];
|
|
199
|
+
const properties = {};
|
|
200
|
+
for (let [key, value] of Object.entries(props)) {
|
|
201
|
+
if (Array.isArray(value)) {
|
|
202
|
+
required.push(key);
|
|
203
|
+
} else if (typeof value === "object") {
|
|
204
|
+
if (typeof value.type !== "string" || !value.type.endsWith("?")) {
|
|
205
|
+
required.push(key);
|
|
206
|
+
}
|
|
207
|
+
} else if (typeof value === "string") {
|
|
208
|
+
if (!value.endsWith("?")) {
|
|
209
|
+
required.push(key);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
properties[key] = unfoldSchema2(value);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
properties,
|
|
216
|
+
required
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
const unfoldArray$1 = (schema2) => {
|
|
220
|
+
const isEnum = schema2.every((item) => typeof item === "string") && schema2.some((item) => !isBaseType(item));
|
|
221
|
+
if (isEnum) {
|
|
222
|
+
if (schema2.length === 1) {
|
|
223
|
+
return { type: "string", const: schema2[0] };
|
|
224
|
+
} else {
|
|
225
|
+
return { type: "string", enum: schema2 };
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
return {
|
|
229
|
+
oneOf: schema2.map((item) => unfoldSchema(item))
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const unfoldSchema = (schema2) => {
|
|
234
|
+
if (typeof schema2 === "string") {
|
|
235
|
+
const trimmedType = trimType(schema2);
|
|
236
|
+
if (trimmedType in aliases$1) {
|
|
237
|
+
if (schema2.endsWith("??")) {
|
|
238
|
+
return { ...unfoldSchema(aliases$1[trimmedType]), nullable: true };
|
|
239
|
+
}
|
|
240
|
+
return unfoldSchema(aliases$1[trimmedType]);
|
|
241
|
+
}
|
|
242
|
+
if (schema2.endsWith("??")) {
|
|
243
|
+
return { type: trimmedType, nullable: true };
|
|
244
|
+
}
|
|
245
|
+
return { type: trimmedType };
|
|
246
|
+
}
|
|
247
|
+
if (Array.isArray(schema2)) {
|
|
248
|
+
return unfoldArray$1(schema2);
|
|
249
|
+
}
|
|
250
|
+
if (typeof schema2 !== "object" || schema2 === null) {
|
|
251
|
+
throw new Error(`Schema type ${schema2} is not object`);
|
|
252
|
+
}
|
|
253
|
+
if (typeof schema2.type === "string") {
|
|
254
|
+
const { items, properties, type, ...otherProps } = schema2;
|
|
255
|
+
const trimmedType = trimType(type);
|
|
256
|
+
if (trimmedType === "array" && items) {
|
|
257
|
+
return {
|
|
258
|
+
type: "array",
|
|
259
|
+
nullable: type.endsWith("??") ? true : void 0,
|
|
260
|
+
...otherProps,
|
|
261
|
+
items: unfoldSchema(items)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (trimmedType === "object" && properties) {
|
|
265
|
+
return {
|
|
266
|
+
type: "object",
|
|
267
|
+
nullable: type.endsWith("??") ? true : void 0,
|
|
268
|
+
...otherProps,
|
|
269
|
+
...parseObjectFields(properties, unfoldSchema)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
...unfoldSchema(type),
|
|
274
|
+
...otherProps
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
type: "object",
|
|
279
|
+
...parseObjectFields(schema2, unfoldSchema)
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
const aliases = {};
|
|
283
|
+
const registerAlias = (key, item) => {
|
|
284
|
+
aliases[key] = item;
|
|
285
|
+
};
|
|
286
|
+
const registerAliases = (registerCallback) => {
|
|
287
|
+
const registerAlias$12 = registerCallback ?? registerAlias;
|
|
288
|
+
registerAlias$12("image", { type: "object", format: "image", properties: { src: "string", previewSrc: "string?" } });
|
|
289
|
+
registerAlias$12("file", { type: "object", format: "file", properties: { src: "string" } });
|
|
290
|
+
registerAlias$12("color", { type: "string", format: "color" });
|
|
291
|
+
registerAlias$12("smartLink", { type: "object", format: "smartLink", properties: { url: "string", title: "string", external: "boolean" } });
|
|
292
|
+
registerAlias$12("richText", {
|
|
293
|
+
type: "array",
|
|
294
|
+
format: "richText",
|
|
295
|
+
items: { type: "object", properties: { text: "string", type: "string?", styles: "object?" } }
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
const queryCache = /* @__PURE__ */ new Map();
|
|
299
|
+
const getQuery = async (key) => {
|
|
300
|
+
if (queryCache.has(key)) {
|
|
301
|
+
return queryCache.get(key);
|
|
302
|
+
}
|
|
303
|
+
const type = key.slice(0, key.indexOf("."));
|
|
304
|
+
const value = JSON.parse(key.slice(key.indexOf(".") + 1));
|
|
305
|
+
if (type === "getPages") {
|
|
306
|
+
let pages = await getAvailablePages(value);
|
|
307
|
+
if (value.folderName) {
|
|
308
|
+
pages = pages.filter((item) => item.folderPath === value.folderName);
|
|
309
|
+
}
|
|
310
|
+
for (let page of pages) {
|
|
311
|
+
delete page.order;
|
|
312
|
+
delete page.folderPath;
|
|
313
|
+
delete page.orderAfter;
|
|
314
|
+
}
|
|
315
|
+
queryCache.set(key, pages);
|
|
316
|
+
return pages;
|
|
317
|
+
}
|
|
318
|
+
return {};
|
|
319
|
+
};
|
|
320
|
+
const getAvailablePages = async (options) => {
|
|
321
|
+
if (!fs.existsSync(resolve(`.mech/pages`))) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
const pages = await fs.promises.readdir(resolve(`.mech/pages`), { recursive: true });
|
|
325
|
+
const arr = [];
|
|
326
|
+
for (let _path of pages) {
|
|
327
|
+
const { name, dir } = parse(_path);
|
|
328
|
+
if (!_path.endsWith(".json")) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const pageData = JSON.parse(await fs.promises.readFile(join(`.mech/pages`, _path), "utf-8"));
|
|
332
|
+
const path = `/${dir}/${name === "index" ? "" : name}`.replace("//", "/").replace(/\/$/, "") || "/";
|
|
333
|
+
const data = {};
|
|
334
|
+
for (let item of options.data ?? []) {
|
|
335
|
+
data[item.id] = pageData.data[item.id] ?? {};
|
|
336
|
+
}
|
|
337
|
+
arr.push({
|
|
338
|
+
path,
|
|
339
|
+
name: pageData.name ?? name,
|
|
340
|
+
folderPath: dir === "" ? null : dir,
|
|
341
|
+
order: pageData.order ?? 0,
|
|
342
|
+
orderAfter: pageData.orderAfter ?? null,
|
|
343
|
+
...data
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
arr.sort((a, b) => {
|
|
347
|
+
if (a.folderPath === b.folderPath) {
|
|
348
|
+
if (a.orderAfter === b.path) return 1;
|
|
349
|
+
if (b.orderAfter === a.path) return -1;
|
|
350
|
+
if (a.order === b.order) {
|
|
351
|
+
return (a.orderAfter ?? a.path).localeCompare(b.orderAfter ?? b.path);
|
|
352
|
+
} else {
|
|
353
|
+
return a.order - b.order;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (!a.folderPath) return -1;
|
|
357
|
+
if (!b.folderPath) return 1;
|
|
358
|
+
return a.folderPath.localeCompare(b.folderPath);
|
|
359
|
+
});
|
|
360
|
+
return arr;
|
|
361
|
+
};
|
|
362
|
+
const compile = async () => {
|
|
363
|
+
console.info("Build client files...");
|
|
364
|
+
registerAliases(registerAlias$1);
|
|
365
|
+
process.env.DISABLE_HTML_GENERATE = "true";
|
|
366
|
+
let baseUrl = "/";
|
|
367
|
+
let assetsUrl = void 0;
|
|
368
|
+
let uploadsUrl = "/mech-assets";
|
|
369
|
+
for (let argv of process.argv) {
|
|
370
|
+
if (argv.startsWith("--base")) {
|
|
371
|
+
baseUrl = argv.split("=")[1];
|
|
372
|
+
if (!baseUrl) {
|
|
373
|
+
return console.error(`Use --base=/path`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (argv.startsWith("--assets-dir")) {
|
|
377
|
+
assetsUrl = argv.split("=")[1];
|
|
378
|
+
if (!assetsUrl) {
|
|
379
|
+
return console.error(`Use --assets-dir=/path`);
|
|
380
|
+
}
|
|
381
|
+
if (!assetsUrl.endsWith("/")) {
|
|
382
|
+
assetsUrl = assetsUrl + "/";
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (argv.startsWith("--uploads-dir")) {
|
|
386
|
+
uploadsUrl = argv.split("=")[1].replace(/\/+$/, "");
|
|
387
|
+
if (!uploadsUrl) {
|
|
388
|
+
return console.error(`Use --uploads-dir=/path`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
let assetsPath = (assetsUrl == null ? void 0 : assetsUrl.replace(/^\.?\/+/, "").replace(/\/+$/, "")) ?? "assets";
|
|
393
|
+
fs.rmSync(`${process.cwd()}/export`, { recursive: true, force: true });
|
|
394
|
+
fs.mkdirSync(`${process.cwd()}/export`, { recursive: true });
|
|
395
|
+
fs.cpSync(`${process.cwd()}/dist/assets`, `${process.cwd()}/export/${assetsPath}`, { recursive: true });
|
|
396
|
+
const index = fs.readFileSync(`${process.cwd()}/dist/index.html`, "utf-8");
|
|
397
|
+
const pagesPath = "/.mech/pages";
|
|
398
|
+
const window = { addEventListener() {
|
|
399
|
+
}, removeEventListener() {
|
|
400
|
+
}, state: { content: [], data: {} } };
|
|
401
|
+
globalThis.window = window;
|
|
402
|
+
globalThis.document = { location: { pathname: "/", query: "" }, createElement() {
|
|
403
|
+
} };
|
|
404
|
+
globalThis.document.location.pathname = `/`;
|
|
405
|
+
const module = await import(pathToFileURL(`${process.cwd()}/dist/ssr.js`).href);
|
|
406
|
+
const blocksMap = new Map(module.blocksList.map((file) => {
|
|
407
|
+
const blockData = {
|
|
408
|
+
...file.blockSchema,
|
|
409
|
+
id: file.blockSchema.id ?? file.__name,
|
|
410
|
+
name: file.blockSchema.name ?? getBlockName(file.__name) ?? "Unnamed block",
|
|
411
|
+
props: file.blockSchema.props ? unfoldSchema(file.blockSchema.props) : {}
|
|
412
|
+
};
|
|
413
|
+
return [blockData.id, blockData];
|
|
414
|
+
}));
|
|
415
|
+
const render = async (state, pathName) => {
|
|
416
|
+
globalThis.document.location.pathname = pathName;
|
|
417
|
+
window.state = state;
|
|
418
|
+
module.updateState(state);
|
|
419
|
+
const html = await module.render(state);
|
|
420
|
+
return html;
|
|
421
|
+
};
|
|
422
|
+
fs.rmSync(`${process.cwd()}/export/mech-assets`, { recursive: true, force: true });
|
|
423
|
+
let hasUploadFolder = false;
|
|
424
|
+
const copiedFiles = /* @__PURE__ */ new Set();
|
|
425
|
+
const onFile = (imagePath) => {
|
|
426
|
+
if (!hasUploadFolder) {
|
|
427
|
+
fs.mkdirSync(`${process.cwd()}/export${uploadsUrl}`, { recursive: true });
|
|
428
|
+
hasUploadFolder = true;
|
|
429
|
+
}
|
|
430
|
+
const path = imagePath.replace("@mechanica", ".mech");
|
|
431
|
+
const newPath = uploadsUrl + imagePath.replace("/@mechanica", "").replace("/assets", "");
|
|
432
|
+
if (!copiedFiles.has(newPath)) {
|
|
433
|
+
copiedFiles.add(newPath);
|
|
434
|
+
fs.copyFileSync(`${process.cwd()}${path}`, `${process.cwd()}/export/${newPath}`);
|
|
435
|
+
}
|
|
436
|
+
return newPath;
|
|
437
|
+
};
|
|
438
|
+
const pages = [];
|
|
439
|
+
for await (let page of fs.opendirSync(`${process.cwd()}${pagesPath}`, { recursive: true })) {
|
|
440
|
+
if (page.isDirectory()) continue;
|
|
441
|
+
let relativePath = relative(`${process.cwd()}${pagesPath}`, page.parentPath);
|
|
442
|
+
try {
|
|
443
|
+
if (relativePath) {
|
|
444
|
+
relativePath = "/" + relativePath.replace("\\\\g", "/");
|
|
445
|
+
}
|
|
446
|
+
const pageName = page.name.slice(0, -".json".length);
|
|
447
|
+
const path = `${relativePath}/${pageName === "index" ? "" : pageName}`;
|
|
448
|
+
const { data, content, title } = JSON.parse(fs.readFileSync(`${page.parentPath}/${page.name}`, "utf-8"));
|
|
449
|
+
pages.push({ data, content, title, path });
|
|
450
|
+
} catch (e) {
|
|
451
|
+
console.warn(`Unable to read ${page.parentPath}/${page.name} directory`, e);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const iterator = generateProject({
|
|
455
|
+
index,
|
|
456
|
+
pages,
|
|
457
|
+
dataEntries: module.dataEntries,
|
|
458
|
+
projectData: {},
|
|
459
|
+
blocksMap,
|
|
460
|
+
baseUrl,
|
|
461
|
+
assetsUrl,
|
|
462
|
+
queryResolver: getQuery,
|
|
463
|
+
render,
|
|
464
|
+
onFile
|
|
465
|
+
});
|
|
466
|
+
for await (let [html, path] of iterator) {
|
|
467
|
+
if (path != "/") {
|
|
468
|
+
const dir = join(process.cwd(), `/export/${path}/index.html`);
|
|
469
|
+
fs.mkdirSync(`${process.cwd()}/export/${path}`, { recursive: true });
|
|
470
|
+
fs.writeFileSync(dir, html);
|
|
471
|
+
console.info(`Create page ${dir}`);
|
|
472
|
+
} else {
|
|
473
|
+
const dir = join(process.cwd(), "/export/index.html");
|
|
474
|
+
fs.writeFileSync(dir, html);
|
|
475
|
+
console.info(`Create page ${dir}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
export {
|
|
480
|
+
compile as default
|
|
481
|
+
};
|
package/bin/push.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { request as httpsRequest } from 'https'
|
|
2
|
+
import { request as httpRequest } from 'http'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
|
|
6
|
+
export default async (apiKey, host, folder) => {
|
|
7
|
+
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
console.warn("[ERROR] apiKey is not defined. Use mechanica push --key=<key> for push client files")
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const buffers = []
|
|
14
|
+
let writedSize = 0
|
|
15
|
+
|
|
16
|
+
const getFolderStructure = async (dirPath) => {
|
|
17
|
+
const files = []
|
|
18
|
+
const filePaths = await fs.promises.readdir(dirPath)
|
|
19
|
+
for (let fileName of filePaths) {
|
|
20
|
+
const path = join(dirPath, fileName)
|
|
21
|
+
const stat = fs.statSync(path)
|
|
22
|
+
if (stat.isDirectory()) {
|
|
23
|
+
const children = await getFolderStructure(path)
|
|
24
|
+
files.push({ type: "folder", name: fileName, children, mtime: stat.mtimeMs })
|
|
25
|
+
} else {
|
|
26
|
+
const file = await fs.promises.readFile(path)
|
|
27
|
+
files.push({ type: "file", name: fileName, size: stat.size, mtime: stat.mtimeMs, offset: writedSize, length: file.byteLength })
|
|
28
|
+
buffers.push(file)
|
|
29
|
+
writedSize += file.byteLength
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return files
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const structure = await getFolderStructure(join(process.cwd(), folder ?? 'dist'))
|
|
36
|
+
|
|
37
|
+
const jsonBuffer = Buffer.from(JSON.stringify(structure), 'utf-8')
|
|
38
|
+
buffers.unshift(jsonBuffer)
|
|
39
|
+
|
|
40
|
+
const controls = new Uint32Array(2)
|
|
41
|
+
controls[0] = jsonBuffer.length
|
|
42
|
+
controls[1] = writedSize
|
|
43
|
+
buffers.unshift(Buffer.from(controls.buffer))
|
|
44
|
+
|
|
45
|
+
const outputBuffer = Buffer.concat(buffers)
|
|
46
|
+
|
|
47
|
+
const url = `${host}/api/projects/push`
|
|
48
|
+
const request = url.startsWith("https")? httpsRequest: httpRequest
|
|
49
|
+
|
|
50
|
+
const resp = await new Promise((res, rej) => {
|
|
51
|
+
const onResponse = async (response) => {
|
|
52
|
+
const buffers = [];
|
|
53
|
+
for await (const chunk of response) {
|
|
54
|
+
buffers.push(chunk);
|
|
55
|
+
}
|
|
56
|
+
const data = Buffer.concat(buffers)
|
|
57
|
+
if (response.statusCode !== 200) {
|
|
58
|
+
rej(new Error(data.toString()))
|
|
59
|
+
}
|
|
60
|
+
res(data.toString())
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const clientRequest = request(url, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
'authorization': `Bearer ${apiKey}`,
|
|
67
|
+
'content-type': "application/octet-stream",
|
|
68
|
+
"content-length": outputBuffer.byteLength
|
|
69
|
+
}
|
|
70
|
+
}, onResponse)
|
|
71
|
+
|
|
72
|
+
clientRequest.on("error", (err) => {
|
|
73
|
+
rej(err)
|
|
74
|
+
})
|
|
75
|
+
clientRequest.end(outputBuffer)
|
|
76
|
+
|
|
77
|
+
console.info(`Push to site ${host}...`)
|
|
78
|
+
})
|
|
79
|
+
console.info(`Project successfull pushed. ${resp}`)
|
|
80
|
+
}
|