@allior/wmake-cli 0.0.6 → 0.0.7

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/src/widget.ts CHANGED
@@ -1,216 +1,155 @@
1
- /**
2
- * Сборка виджета: html.txt, js.txt, css.txt, fields.txt, data.txt и архив .zip.
3
- * Все пути относительно текущей папки (откуда вызывается скрипт).
4
- */
5
- import fs from "node:fs";
6
- import path from "node:path";
7
- import { spawnSync } from "node:child_process";
8
- import {
9
- runGenerateFields,
10
- runGenerateFieldsFromModule,
11
- } from "./generate-fields.js";
12
-
13
- /** Текущая папка (каталог проекта виджета). */
14
- function getProjectDir(): string {
15
- return path.resolve(process.cwd());
16
- }
17
-
18
- function run(
19
- cmd: string,
20
- args: string[],
21
- cwd: string,
22
- env?: NodeJS.ProcessEnv,
23
- ): boolean {
24
- const r = spawnSync(cmd, args, {
25
- cwd,
26
- stdio: "inherit",
27
- shell: true,
28
- env: { ...process.env, ...env },
29
- });
30
- return r.status === 0;
31
- }
32
-
33
- function getPackageManager(): string {
34
- const userAgent = process.env.npm_config_user_agent || "";
35
- if (userAgent.includes("bun")) return "bun";
36
- if (userAgent.includes("pnpm")) return "pnpm";
37
- if (userAgent.includes("yarn")) return "yarn";
38
- return "npm";
39
- }
40
-
41
- export async function buildWidget(options: { full?: boolean }): Promise<void> {
42
- const projectDir = getProjectDir();
43
- const distDir = path.join(projectDir, "dist");
44
- const buildDir = path.resolve(projectDir, process.env.BUILD_DIR ?? "build");
45
- const archiveName = process.env.ARCHIVE_NAME ?? "widget";
46
-
47
- console.log("Generating fields...");
48
- const basePath = path.join(projectDir, "fields.base.json");
49
- const fieldsTsPath = path.join(projectDir, "fields.ts");
50
- if (fs.existsSync(basePath)) {
51
- const mod =
52
- (await import("@allior/wmake-streamelements-events")) as unknown as {
53
- testMessages: Record<string, unknown>;
54
- testAlerts: Record<string, unknown>;
55
- };
56
- runGenerateFields({
57
- fieldsDir: projectDir,
58
- testMessages: mod.testMessages,
59
- testAlerts: mod.testAlerts,
60
- });
61
- } else if (fs.existsSync(fieldsTsPath)) {
62
- runGenerateFieldsFromModule(fieldsTsPath);
63
- } else {
64
- console.log(
65
- "No fields.base.json or fields.ts found, skipping fields generation",
66
- );
67
- }
68
- console.log("Building...");
69
- const buildEnv = options.full ? { BUILD_FULL: "1" } : {};
70
- if (!run(getPackageManager(), ["run", "build"], projectDir, buildEnv)) {
71
- throw new Error("Build failed.");
72
- }
73
-
74
- if (!fs.existsSync(distDir)) {
75
- throw new Error("dist not found. Run build first.");
76
- }
77
-
78
- fs.mkdirSync(buildDir, { recursive: true });
79
-
80
- const html = fs.readFileSync(path.join(distDir, "index.html"), "utf-8");
81
- fs.writeFileSync(path.join(buildDir, "html.txt"), html);
82
-
83
- const assetsDir = path.join(distDir, "assets");
84
- const findFiles = (dir: string, ext: string): string[] => {
85
- const found: string[] = [];
86
- const walk = (d: string) => {
87
- if (!fs.existsSync(d)) return;
88
- for (const e of fs.readdirSync(d)) {
89
- const full = path.join(d, e);
90
- const stat = fs.statSync(full);
91
- if (stat.isDirectory()) walk(full);
92
- else if (e.endsWith(ext)) found.push(full);
93
- }
94
- };
95
- walk(dir);
96
- return found;
97
- };
98
- const jsFiles = findFiles(assetsDir, ".js");
99
- const cssFiles = findFiles(assetsDir, ".css");
100
-
101
- if (jsFiles.length === 0 || cssFiles.length === 0) {
102
- throw new Error("Expected .js and .css in dist/assets");
103
- }
104
-
105
- let finalJs = fs.readFileSync(jsFiles[0], "utf-8");
106
-
107
- // Если мы НЕ в FULL режиме, оборачиваем в проверку готовности глобалов
108
- if (!options.full) {
109
- const { getRawWmakeGlobals } = await import("./vite-config.js");
110
- const rawGlobals = getRawWmakeGlobals(projectDir);
111
- const requiredGlobals = JSON.stringify(Array.from(new Set(Object.values(rawGlobals))));
112
-
113
- finalJs = `(function() {
114
- var req = ${requiredGlobals};
115
- function check() {
116
- var missing = [];
117
- for (var i = 0; i < req.length; i++) {
118
- if (!window[req[i]]) { missing.push(req[i]); }
119
- }
120
- if (missing.length === 0) {
121
- console.log("[WMAKE] All dependencies confirmed: " + req.join(", "));
122
- var run = function() {
123
- // Принудительно гарантируем наличие переменных в глобальной области
124
- ${Array.from(new Set(Object.values(rawGlobals))).map(g => `if (!window.${g}) window.${g} = undefined; var ${g} = window.${g};`).join(" ")}
125
- try {
126
- ${finalJs}
127
- } catch (e) {
128
- console.error("[WMAKE] Runtime error during widget execution:", e);
129
- }
130
- };
131
- run();
132
- } else {
133
- if (window.__WMAKE_LOG_MISSING !== missing.join(",")) {
134
- console.warn("[WMAKE] Waiting for dependencies: " + missing.join(", "));
135
- window.__WMAKE_LOG_MISSING = missing.join(",");
136
- }
137
- setTimeout(check, 100);
138
- }
139
- }
140
- console.log("[WMAKE] Initializing dependency check...");
141
- check();
142
- })();`;
143
- }
144
-
145
- fs.writeFileSync(path.join(buildDir, "js.txt"), finalJs);
146
- fs.writeFileSync(
147
- path.join(buildDir, "css.txt"),
148
- fs.readFileSync(cssFiles[0], "utf-8"),
149
- );
150
-
151
- const fieldsJsonPath = path.join(projectDir, "fields.json");
152
- if (fs.existsSync(fieldsJsonPath)) {
153
- fs.copyFileSync(fieldsJsonPath, path.join(buildDir, "fields.txt"));
154
- }
155
-
156
- const pkgPath = path.join(projectDir, "package.json");
157
- const wmakeVersion =
158
- fs.existsSync(pkgPath) &&
159
- (() => {
160
- try {
161
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
162
- return typeof pkg.version === "string" ? pkg.version : undefined;
163
- } catch {
164
- return undefined;
165
- }
166
- })();
167
- const dataPath = path.join(projectDir, "data.json");
168
- const data = fs.existsSync(dataPath)
169
- ? JSON.parse(fs.readFileSync(dataPath, "utf-8"))
170
- : {};
171
- if (wmakeVersion) {
172
- data.wmakeVersion = wmakeVersion;
173
- }
174
- fs.writeFileSync(
175
- path.join(buildDir, "data.txt"),
176
- JSON.stringify(data, null, 2),
177
- );
178
-
179
- console.log(
180
- "Widget files:",
181
- path.join(buildDir, "html.txt"),
182
- "js.txt, css.txt, fields.txt, data.txt",
183
- );
184
-
185
- const zipPath = path.join(distDir, `${archiveName}.zip`);
186
- const zip = spawnSync("zip", ["-r", zipPath, "."], {
187
- cwd: buildDir,
188
- stdio: "pipe",
189
- });
190
- if (zip.status === 0) {
191
- console.log("Archive:", zipPath);
192
- return;
193
- }
194
- try {
195
- const AdmZip = (await import("adm-zip")).default;
196
- const archive = new AdmZip();
197
- for (const f of [
198
- "html.txt",
199
- "js.txt",
200
- "css.txt",
201
- "fields.txt",
202
- "data.txt",
203
- ]) {
204
- const fp = path.join(buildDir, f);
205
- if (fs.existsSync(fp)) archive.addLocalFile(fp, "", f);
206
- }
207
- archive.writeZip(zipPath);
208
- console.log("Archive (adm-zip):", zipPath);
209
- } catch (e) {
210
- console.error("zip failed and adm-zip fallback error:", e);
211
- console.error(
212
- "Install 'zip' (e.g. apk add zip) or ensure adm-zip is installed.",
213
- );
214
- throw e;
215
- }
216
- }
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { build } from "vite";
4
+ import { getRawWmakeGlobals, getWmakeDeps, getWmakeDepsWithVersions } from "./vite-config.js";
5
+ import { generateMergedFields, GenerateFieldsOptions } from "./generate-fields.js";
6
+ import admZip from "adm-zip";
7
+
8
+ export interface BuildWidgetOptions extends GenerateFieldsOptions {
9
+ full?: boolean;
10
+ seDir?: string;
11
+ }
12
+
13
+ /** Builds project for StreamElements widget using CDN mode */
14
+ export async function buildWidget(options: BuildWidgetOptions = {}) {
15
+ const { full = false, seDir } = options;
16
+ const projectDir = process.cwd();
17
+
18
+ await build({
19
+ configFile: path.resolve(projectDir, "vite.widget.config.ts"),
20
+ build: {
21
+ emptyOutDir: true,
22
+ minify: true,
23
+ }
24
+ });
25
+
26
+ const distDir = path.join(projectDir, "dist");
27
+ const buildDir = path.join(projectDir, "build");
28
+ if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir, { recursive: true });
29
+
30
+ let html = fs.readFileSync(path.join(distDir, "index.html"), "utf-8");
31
+ let css = fs.readFileSync(path.join(distDir, "assets/style.css"), "utf-8");
32
+ let js = fs.readFileSync(path.join(distDir, "assets/index.js"), "utf-8");
33
+
34
+ if (!full) {
35
+ const rawGlobals = getRawWmakeGlobals(projectDir);
36
+ const requiredGlobals = JSON.stringify(Array.from(new Set(Object.values(rawGlobals))));
37
+ const wmakeDepsWithVersions = getWmakeDepsWithVersions(projectDir);
38
+ const wmakeDeps = getWmakeDeps(projectDir);
39
+ const globalsInOrder = Array.from(new Set(Object.values(rawGlobals)));
40
+
41
+ const loaderJs = `(function() {
42
+ var req = ${requiredGlobals};
43
+ var wmakeDeps = ${JSON.stringify(wmakeDeps)};
44
+ var wmakeFallbacks = ${JSON.stringify(wmakeDepsWithVersions)};
45
+
46
+ for (var i = 0; i < req.length; i++) {
47
+ if (!window.hasOwnProperty(req[i])) window[req[i]] = undefined;
48
+ }
49
+
50
+ function check() {
51
+ var missing = [];
52
+ for (var i = 0; i < req.length; i++) {
53
+ if (!window[req[i]]) { missing.push(req[i]); }
54
+ }
55
+ if (missing.length === 0) {
56
+ console.log("[WMAKE] All context dependencies initialized");
57
+ var run = function() {
58
+ ${globalsInOrder.map(g => `var ${g} = window.${g};`).join(" ")}
59
+ try {
60
+ ${js.trim()}
61
+ } catch (e) {
62
+ console.error("[WMAKE] Runtime error:", e);
63
+ }
64
+ };
65
+ run();
66
+ } else {
67
+ setTimeout(check, 100);
68
+ }
69
+ }
70
+
71
+ function injectScripts(fieldData) {
72
+ var CDN_BASE = "https://cdn.jsdelivr.net/npm";
73
+ var head = document.getElementsByTagName("head")[0];
74
+
75
+ var queue = [];
76
+ for (var i = 0; i < wmakeDeps.length; i++) {
77
+ queue.push({ pkg: wmakeDeps[i], entry: "root" });
78
+ queue.push({ pkg: wmakeDeps[i], entry: "react" });
79
+ }
80
+
81
+ function loadNext(qIdx) {
82
+ if (qIdx >= queue.length) {
83
+ check();
84
+ return;
85
+ }
86
+
87
+ var item = queue[qIdx];
88
+ var pkg = item.pkg;
89
+ var name = pkg.replace("@allior/wmake-", "");
90
+ var pascalName = name.split("-").map(function(w) { return w.charAt(0).toUpperCase() + w.slice(1); }).join("");
91
+ var fieldKey = "wmake" + pascalName + "Version";
92
+ var version = (fieldData && fieldData[fieldKey]) || wmakeFallbacks[pkg] || "latest";
93
+
94
+ var src = CDN_BASE + "/" + pkg + "@" + version + "/dist/" + item.entry + "/index.iife.js";
95
+ var script = document.createElement("script");
96
+ script.src = src;
97
+ script.onload = function() { loadNext(qIdx + 1); };
98
+ script.onerror = function() { loadNext(qIdx + 1); };
99
+ head.appendChild(script);
100
+ }
101
+
102
+ loadNext(0);
103
+ }
104
+
105
+ window.addEventListener("onWidgetLoad", function(obj) {
106
+ var detail = obj.detail;
107
+ window.__WMAKE_INIT_DATA__ = detail;
108
+ injectScripts(detail && detail.fieldData);
109
+ });
110
+ })();`;
111
+
112
+ js = loaderJs;
113
+ }
114
+
115
+ fs.writeFileSync(path.join(buildDir, "html.txt"), html, "utf-8");
116
+ fs.writeFileSync(path.join(buildDir, "css.txt"), css, "utf-8");
117
+ fs.writeFileSync(path.join(buildDir, "js.txt"), js, "utf-8");
118
+
119
+ const fields = generateMergedFields(projectDir, seDir, {
120
+ skipWmakeVersions: options.skipWmakeVersions,
121
+ skipTestAlerts: options.skipTestAlerts,
122
+ skipTestMessages: options.skipTestMessages,
123
+ });
124
+ fs.writeFileSync(path.join(buildDir, "fields.txt"), JSON.stringify(fields, null, 2), "utf-8");
125
+
126
+ const dataPath = seDir
127
+ ? path.join(projectDir, seDir, "data.json")
128
+ : path.join(projectDir, "data.json");
129
+
130
+ let dataFilename = "";
131
+ if (fs.existsSync(dataPath)) {
132
+ dataFilename = "data.txt";
133
+ fs.writeFileSync(path.join(buildDir, dataFilename), fs.readFileSync(dataPath, "utf-8"), "utf-8");
134
+ }
135
+
136
+ const iniContent = `[HTML]
137
+ path = "html.txt"
138
+
139
+ [CSS]
140
+ path = "css.txt"
141
+
142
+ [JS]
143
+ path = "js.txt"
144
+
145
+ [FIELDS]
146
+ path = "fields.txt"
147
+ ${dataFilename ? `\n[DATA]\npath = "${dataFilename}"` : ""}
148
+ `;
149
+ fs.writeFileSync(path.join(buildDir, "widget.ini"), iniContent, "utf-8");
150
+
151
+ const zip = new admZip();
152
+ zip.addLocalFolder(buildDir);
153
+ const zipPath = path.join(distDir, "widget.zip");
154
+ zip.writeZip(zipPath);
155
+ }