@allior/wmake-cli 0.0.5 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"AAwCA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0I5E"}
1
+ {"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"AAIA,OAAO,EAAwB,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGnF,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8DAA8D;AAC9D,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,iBA6IjE"}
package/dist/widget.js CHANGED
@@ -1,145 +1,138 @@
1
- /**
2
- * Сборка виджета: html.txt, js.txt, css.txt, fields.txt, data.txt и архив .zip.
3
- * Все пути относительно текущей папки (откуда вызывается скрипт).
4
- */
5
1
  import fs from "node:fs";
6
2
  import path from "node:path";
7
- import { spawnSync } from "node:child_process";
8
- import { runGenerateFields, runGenerateFieldsFromModule, } from "./generate-fields.js";
9
- /** Текущая папка (каталог проекта виджета). */
10
- function getProjectDir() {
11
- return path.resolve(process.cwd());
12
- }
13
- function run(cmd, args, cwd, env) {
14
- const r = spawnSync(cmd, args, {
15
- cwd,
16
- stdio: "inherit",
17
- shell: true,
18
- env: { ...process.env, ...env },
3
+ import { build } from "vite";
4
+ import { getRawWmakeGlobals, getWmakeDeps, getWmakeDepsWithVersions } from "./vite-config.js";
5
+ import { generateMergedFields } from "./generate-fields.js";
6
+ import admZip from "adm-zip";
7
+ /** Builds project for StreamElements widget using CDN mode */
8
+ export async function buildWidget(options = {}) {
9
+ const { full = false, seDir } = options;
10
+ const projectDir = process.cwd();
11
+ await build({
12
+ configFile: path.resolve(projectDir, "vite.widget.config.ts"),
13
+ build: {
14
+ emptyOutDir: true,
15
+ minify: true,
16
+ }
19
17
  });
20
- return r.status === 0;
21
- }
22
- function getPackageManager() {
23
- const userAgent = process.env.npm_config_user_agent || "";
24
- if (userAgent.includes("bun"))
25
- return "bun";
26
- if (userAgent.includes("pnpm"))
27
- return "pnpm";
28
- if (userAgent.includes("yarn"))
29
- return "yarn";
30
- return "npm";
31
- }
32
- export async function buildWidget(options) {
33
- const projectDir = getProjectDir();
34
18
  const distDir = path.join(projectDir, "dist");
35
- const buildDir = path.resolve(projectDir, process.env.BUILD_DIR ?? "build");
36
- const archiveName = process.env.ARCHIVE_NAME ?? "widget";
37
- console.log("Generating fields...");
38
- const basePath = path.join(projectDir, "fields.base.json");
39
- const fieldsTsPath = path.join(projectDir, "fields.ts");
40
- if (fs.existsSync(basePath)) {
41
- const mod = (await import("@allior/wmake-streamelements-events"));
42
- runGenerateFields({
43
- fieldsDir: projectDir,
44
- testMessages: mod.testMessages,
45
- testAlerts: mod.testAlerts,
46
- });
47
- }
48
- else if (fs.existsSync(fieldsTsPath)) {
49
- runGenerateFieldsFromModule(fieldsTsPath);
50
- }
51
- else {
52
- console.log("No fields.base.json or fields.ts found, skipping fields generation");
53
- }
54
- console.log("Building...");
55
- const buildEnv = options.full ? { BUILD_FULL: "1" } : {};
56
- if (!run(getPackageManager(), ["run", "build"], projectDir, buildEnv)) {
57
- throw new Error("Build failed.");
19
+ const buildDir = path.join(projectDir, "build");
20
+ if (!fs.existsSync(buildDir))
21
+ fs.mkdirSync(buildDir, { recursive: true });
22
+ let html = fs.readFileSync(path.join(distDir, "index.html"), "utf-8");
23
+ let css = fs.readFileSync(path.join(distDir, "assets/style.css"), "utf-8");
24
+ let js = fs.readFileSync(path.join(distDir, "assets/index.js"), "utf-8");
25
+ if (!full) {
26
+ const rawGlobals = getRawWmakeGlobals(projectDir);
27
+ const requiredGlobals = JSON.stringify(Array.from(new Set(Object.values(rawGlobals))));
28
+ const wmakeDepsWithVersions = getWmakeDepsWithVersions(projectDir);
29
+ const wmakeDeps = getWmakeDeps(projectDir);
30
+ const globalsInOrder = Array.from(new Set(Object.values(rawGlobals)));
31
+ const loaderJs = `(function() {
32
+ var req = ${requiredGlobals};
33
+ var wmakeDeps = ${JSON.stringify(wmakeDeps)};
34
+ var wmakeFallbacks = ${JSON.stringify(wmakeDepsWithVersions)};
35
+
36
+ for (var i = 0; i < req.length; i++) {
37
+ if (!window.hasOwnProperty(req[i])) window[req[i]] = undefined;
38
+ }
39
+
40
+ function check() {
41
+ var missing = [];
42
+ for (var i = 0; i < req.length; i++) {
43
+ if (!window[req[i]]) { missing.push(req[i]); }
58
44
  }
59
- if (!fs.existsSync(distDir)) {
60
- throw new Error("dist not found. Run build first.");
61
- }
62
- fs.mkdirSync(buildDir, { recursive: true });
63
- const html = fs.readFileSync(path.join(distDir, "index.html"), "utf-8");
64
- fs.writeFileSync(path.join(buildDir, "html.txt"), html);
65
- const assetsDir = path.join(distDir, "assets");
66
- const findFiles = (dir, ext) => {
67
- const found = [];
68
- const walk = (d) => {
69
- if (!fs.existsSync(d))
70
- return;
71
- for (const e of fs.readdirSync(d)) {
72
- const full = path.join(d, e);
73
- const stat = fs.statSync(full);
74
- if (stat.isDirectory())
75
- walk(full);
76
- else if (e.endsWith(ext))
77
- found.push(full);
78
- }
79
- };
80
- walk(dir);
81
- return found;
82
- };
83
- const jsFiles = findFiles(assetsDir, ".js");
84
- const cssFiles = findFiles(assetsDir, ".css");
85
- if (jsFiles.length === 0 || cssFiles.length === 0) {
86
- throw new Error("Expected .js and .css in dist/assets");
87
- }
88
- fs.writeFileSync(path.join(buildDir, "js.txt"), fs.readFileSync(jsFiles[0], "utf-8"));
89
- fs.writeFileSync(path.join(buildDir, "css.txt"), fs.readFileSync(cssFiles[0], "utf-8"));
90
- const fieldsJsonPath = path.join(projectDir, "fields.json");
91
- if (fs.existsSync(fieldsJsonPath)) {
92
- fs.copyFileSync(fieldsJsonPath, path.join(buildDir, "fields.txt"));
45
+ if (missing.length === 0) {
46
+ console.log("[WMAKE] All context dependencies initialized");
47
+ var run = function() {
48
+ ${globalsInOrder.map(g => `var ${g} = window.${g};`).join(" ")}
49
+ try {
50
+ ${js.trim()}
51
+ } catch (e) {
52
+ console.error("[WMAKE] Runtime error:", e);
53
+ }
54
+ };
55
+ run();
56
+ } else {
57
+ setTimeout(check, 100);
93
58
  }
94
- const pkgPath = path.join(projectDir, "package.json");
95
- const wmakeVersion = fs.existsSync(pkgPath) &&
96
- (() => {
97
- try {
98
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
99
- return typeof pkg.version === "string" ? pkg.version : undefined;
100
- }
101
- catch {
102
- return undefined;
103
- }
104
- })();
105
- const dataPath = path.join(projectDir, "data.json");
106
- const data = fs.existsSync(dataPath)
107
- ? JSON.parse(fs.readFileSync(dataPath, "utf-8"))
108
- : {};
109
- if (wmakeVersion) {
110
- data.wmakeVersion = wmakeVersion;
59
+ }
60
+
61
+ function injectScripts(fieldData) {
62
+ var CDN_BASE = "https://cdn.jsdelivr.net/npm";
63
+ var head = document.getElementsByTagName("head")[0];
64
+
65
+ var queue = [];
66
+ for (var i = 0; i < wmakeDeps.length; i++) {
67
+ queue.push({ pkg: wmakeDeps[i], entry: "root" });
68
+ queue.push({ pkg: wmakeDeps[i], entry: "react" });
111
69
  }
112
- fs.writeFileSync(path.join(buildDir, "data.txt"), JSON.stringify(data, null, 2));
113
- console.log("Widget files:", path.join(buildDir, "html.txt"), "js.txt, css.txt, fields.txt, data.txt");
114
- const zipPath = path.join(distDir, `${archiveName}.zip`);
115
- const zip = spawnSync("zip", ["-r", zipPath, "."], {
116
- cwd: buildDir,
117
- stdio: "pipe",
118
- });
119
- if (zip.status === 0) {
120
- console.log("Archive:", zipPath);
70
+
71
+ function loadNext(qIdx) {
72
+ if (qIdx >= queue.length) {
73
+ check();
121
74
  return;
75
+ }
76
+
77
+ var item = queue[qIdx];
78
+ var pkg = item.pkg;
79
+ var name = pkg.replace("@allior/wmake-", "");
80
+ var pascalName = name.split("-").map(function(w) { return w.charAt(0).toUpperCase() + w.slice(1); }).join("");
81
+ var fieldKey = "wmake" + pascalName + "Version";
82
+ var version = (fieldData && fieldData[fieldKey]) || wmakeFallbacks[pkg] || "latest";
83
+
84
+ var src = CDN_BASE + "/" + pkg + "@" + version + "/dist/" + item.entry + "/index.iife.js";
85
+ var script = document.createElement("script");
86
+ script.src = src;
87
+ script.onload = function() { loadNext(qIdx + 1); };
88
+ script.onerror = function() { loadNext(qIdx + 1); };
89
+ head.appendChild(script);
122
90
  }
123
- try {
124
- const AdmZip = (await import("adm-zip")).default;
125
- const archive = new AdmZip();
126
- for (const f of [
127
- "html.txt",
128
- "js.txt",
129
- "css.txt",
130
- "fields.txt",
131
- "data.txt",
132
- ]) {
133
- const fp = path.join(buildDir, f);
134
- if (fs.existsSync(fp))
135
- archive.addLocalFile(fp, "", f);
136
- }
137
- archive.writeZip(zipPath);
138
- console.log("Archive (adm-zip):", zipPath);
91
+
92
+ loadNext(0);
93
+ }
94
+
95
+ window.addEventListener("onWidgetLoad", function(obj) {
96
+ var detail = obj.detail;
97
+ window.__WMAKE_INIT_DATA__ = detail;
98
+ injectScripts(detail && detail.fieldData);
99
+ });
100
+ })();`;
101
+ js = loaderJs;
139
102
  }
140
- catch (e) {
141
- console.error("zip failed and adm-zip fallback error:", e);
142
- console.error("Install 'zip' (e.g. apk add zip) or ensure adm-zip is installed.");
143
- throw e;
103
+ fs.writeFileSync(path.join(buildDir, "html.txt"), html, "utf-8");
104
+ fs.writeFileSync(path.join(buildDir, "css.txt"), css, "utf-8");
105
+ fs.writeFileSync(path.join(buildDir, "js.txt"), js, "utf-8");
106
+ const fields = generateMergedFields(projectDir, seDir, {
107
+ skipWmakeVersions: options.skipWmakeVersions,
108
+ skipTestAlerts: options.skipTestAlerts,
109
+ skipTestMessages: options.skipTestMessages,
110
+ });
111
+ fs.writeFileSync(path.join(buildDir, "fields.txt"), JSON.stringify(fields, null, 2), "utf-8");
112
+ const dataPath = seDir
113
+ ? path.join(projectDir, seDir, "data.json")
114
+ : path.join(projectDir, "data.json");
115
+ let dataFilename = "";
116
+ if (fs.existsSync(dataPath)) {
117
+ dataFilename = "data.txt";
118
+ fs.writeFileSync(path.join(buildDir, dataFilename), fs.readFileSync(dataPath, "utf-8"), "utf-8");
144
119
  }
120
+ const iniContent = `[HTML]
121
+ path = "html.txt"
122
+
123
+ [CSS]
124
+ path = "css.txt"
125
+
126
+ [JS]
127
+ path = "js.txt"
128
+
129
+ [FIELDS]
130
+ path = "fields.txt"
131
+ ${dataFilename ? `\n[DATA]\npath = "${dataFilename}"` : ""}
132
+ `;
133
+ fs.writeFileSync(path.join(buildDir, "widget.ini"), iniContent, "utf-8");
134
+ const zip = new admZip();
135
+ zip.addLocalFolder(buildDir);
136
+ const zipPath = path.join(distDir, "widget.zip");
137
+ zip.writeZip(zipPath);
145
138
  }
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@allior/wmake-cli",
3
- "version": "0.0.5",
4
- "description": "Streamiby/wmake CLI: build widgets, base64 encode assets, generate fields",
3
+ "version": "0.0.7",
4
+ "description": "WMake CLI: build widgets, base64 encode assets, generate fields",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./vite": "./dist/vite-config.js"
10
+ },
7
11
  "bin": {
8
12
  "wmake-cli": "dist/bin.js",
9
13
  "@allior/wmake-cli": "dist/bin.js"
package/src/bin.ts CHANGED
@@ -1,82 +1,44 @@
1
- #!/usr/bin/env node
2
- import path from "node:path";
3
- import fs from "node:fs";
1
+ #!/usr/bin/env bun
4
2
  import { Command } from "commander";
5
- import { processPath } from "./base64.js";
6
3
  import { buildWidget } from "./widget.js";
7
4
  import { runGenerateFields } from "./generate-fields.js";
8
- import { runExtractTestData } from "./extract-test-data.js";
5
+ import { readFileSync } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import path from "node:path";
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const pkg = JSON.parse(readFileSync(path.join(__dirname, "../package.json"), "utf8"));
9
11
 
10
12
  const program = new Command();
11
13
 
12
- program.name("wmake").description("CLI for streamiby / wmake").version("1.0.0");
14
+ program
15
+ .name("wmake")
16
+ .description("Build tool for @allior/wmake components")
17
+ .version(pkg.version);
13
18
 
14
19
  program
15
20
  .command("widget")
16
- .description("Build chat-demo into widget files and zip in examples/chat-demo/dist")
17
- .option("--full", "Full bundle (no CDN external), like the old build")
18
- .action(async (opts: { full?: boolean }) => {
19
- try {
20
- await buildWidget({ full: opts.full });
21
- } catch (e) {
22
- console.error((e as Error).message);
23
- process.exit(1);
24
- }
21
+ .description("Build a StreamElements widget")
22
+ .option("-f, --full", "Build a full bundle without CDN deps")
23
+ .option("-s, --se-dir <dir>", "Directory containing StreamElements files (fields.json, data.json)")
24
+ .option("--no-wmake-versions", "Do not include wmake versions in fields")
25
+ .option("--no-test-alerts", "Do not include test alerts in fields")
26
+ .option("--no-test-messages", "Do not include test messages in fields")
27
+ .action(async (options) => {
28
+ await buildWidget({
29
+ full: options.full,
30
+ seDir: options.seDir,
31
+ skipWmakeVersions: !options.wmakeVersions,
32
+ skipTestAlerts: !options.testAlerts,
33
+ skipTestMessages: !options.testMessages
34
+ });
25
35
  });
26
36
 
27
37
  program
28
38
  .command("generate-fields")
29
- .description("Generate fields.json from fields.base.json and test data objects (WMAKE_FIELDS_DIR)")
30
- .action(async () => {
31
- try {
32
- const fieldsDir = process.env.WMAKE_FIELDS_DIR;
33
- if (!fieldsDir?.trim()) {
34
- throw new Error("WMAKE_FIELDS_DIR must be set.");
35
- }
36
- const mod = (await import("@allior/wmake-streamelements-events")) as unknown as {
37
- testMessages: Record<string, unknown>;
38
- testAlerts: Record<string, unknown>;
39
- };
40
- runGenerateFields({
41
- fieldsDir: path.resolve(fieldsDir),
42
- testMessages: mod.testMessages,
43
- testAlerts: mod.testAlerts,
44
- });
45
- } catch (e) {
46
- console.error((e as Error).message);
47
- process.exit(1);
48
- }
49
- });
50
-
51
- program
52
- .command("extract-test-data")
53
- .description("No-op: test data lives in streamelements/src/assets as TS objects")
54
- .action(async () => {
55
- try {
56
- await runExtractTestData();
57
- } catch (e) {
58
- console.error((e as Error).message);
59
- process.exit(1);
60
- }
61
- });
62
-
63
- program
64
- .command("base64")
65
- .description("Convert images, videos, SVG to base64 for browsers")
66
- .argument("<path>", "File or directory path")
67
- .option("-o, --output <path>", "Output file or directory")
68
- .option("-f, --full", "Output with additional info")
69
- .action(async (inputPath: string, options: { output?: string; full?: boolean }) => {
70
- if (!fs.existsSync(inputPath)) {
71
- console.error("Error: Path does not exist");
72
- process.exit(1);
73
- }
74
- try {
75
- await processPath(inputPath, options.output ?? null, options.full ?? false);
76
- } catch (e) {
77
- console.error("Error:", (e as Error).message);
78
- process.exit(1);
79
- }
39
+ .description("Generate fields.json from fields.base.json (manual source update)")
40
+ .action(() => {
41
+ runGenerateFields(process.cwd());
80
42
  });
81
43
 
82
44
  program.parse();
@@ -1,117 +1,104 @@
1
- /**
2
- * Генерирует fields.json из fields.base.json и ключей из объектов testMessages / testAlerts.
3
- * Путь к каталогу с fields.base.json задаётся WMAKE_FIELDS_DIR.
4
- * Данные сообщений и алертов передаются объектами (из скриптов streamelements).
5
- */
6
-
7
- import fs from "node:fs";
8
- import path from "node:path";
9
- import { spawnSync } from "node:child_process";
10
-
11
- const TEST_GROUP = "Tests / Тесты";
12
-
13
- type FieldDef = Record<string, unknown>;
14
- type FieldsMap = Record<string, FieldDef>;
15
-
16
- function pascalCase(s: string): string {
17
- return s.charAt(0).toUpperCase() + s.slice(1);
18
- }
19
-
20
- /** camelCase → sentence case (e.g. veryShort → "Very short", selfSub → "Self sub") */
21
- function keyToTitle(key: string): string {
22
- const words = key
23
- .replace(/([A-Z])/g, " $1")
24
- .toLowerCase()
25
- .trim();
26
- return words.charAt(0).toUpperCase() + words.slice(1);
27
- }
28
-
29
- function messageLabel(key: string): string {
30
- return `${keyToTitle(key)} message`;
31
- }
32
-
33
- function alertLabel(key: string): string {
34
- return `${keyToTitle(key)} alert`;
35
- }
36
-
37
- export type MessagesRecord = Record<string, unknown>;
38
- export type AlertsRecord = Record<string, unknown>;
39
-
40
- export function runGenerateFields(options: {
41
- fieldsDir: string;
42
- testMessages: MessagesRecord;
43
- testAlerts: AlertsRecord;
44
- }): void {
45
- const { fieldsDir, testMessages: messages, testAlerts: alerts } = options;
46
-
47
- const basePath = path.join(fieldsDir, "fields.base.json");
48
- const outPath = path.join(fieldsDir, "fields.json");
49
-
50
- const base = JSON.parse(fs.readFileSync(basePath, "utf-8")) as FieldsMap;
51
-
52
- const messageKeys = Object.keys(messages);
53
- const alertKeys = Object.keys(alerts);
54
-
55
- const testMessageFields: FieldsMap = {};
56
- for (const key of messageKeys) {
57
- const fieldId = "testMessage" + pascalCase(key);
58
- testMessageFields[fieldId] = {
59
- type: "button",
60
- label: messageLabel(key),
61
- group: TEST_GROUP,
62
- };
63
- }
64
-
65
- const testAlertFields: FieldsMap = {};
66
- for (const key of alertKeys) {
67
- const fieldId = "testAlert" + pascalCase(key);
68
- testAlertFields[fieldId] = {
69
- type: "button",
70
- label: alertLabel(key),
71
- group: TEST_GROUP,
72
- };
73
- }
74
-
75
- const insertAfterKey = "blueFlowerAnimationDuration";
76
- const result: FieldsMap = {};
77
- let inserted = false;
78
- for (const k of Object.keys(base)) {
79
- if (k.startsWith("_")) continue;
80
- result[k] = base[k] as FieldDef;
81
- if (k === insertAfterKey) {
82
- Object.assign(result, testMessageFields, testAlertFields);
83
- inserted = true;
84
- }
85
- }
86
- if (!inserted) {
87
- Object.assign(result, testMessageFields, testAlertFields);
88
- }
89
-
90
- fs.writeFileSync(outPath, JSON.stringify(result, null, 2), "utf-8");
91
- console.log("Wrote", outPath);
92
- console.log("Test message buttons:", messageKeys.length);
93
- console.log("Test alert buttons:", alertKeys.length);
94
- }
95
-
96
- /**
97
- * Генерирует fields.json из TS/JS модуля, экспортирующего default AdvancedField[].
98
- * Использует tsx для загрузки .ts файлов.
99
- */
100
- export function runGenerateFieldsFromModule(fieldsModulePath: string): void {
101
- const absPath = path.resolve(fieldsModulePath);
102
- const outPath = path.join(path.dirname(absPath), "fields.json");
103
- const runnerPath = path.join(
104
- __dirname,
105
- "..",
106
- "src",
107
- "generate-fields-from-module-runner.ts",
108
- );
109
- const r = spawnSync("npx", ["tsx", runnerPath, absPath, outPath], {
110
- stdio: "inherit",
111
- shell: true,
112
- cwd: path.dirname(absPath),
113
- });
114
- if (r.status !== 0) {
115
- throw new Error(`Failed to generate fields from ${fieldsModulePath}`);
116
- }
117
- }
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { getWmakeDepsWithVersions } from "./vite-config.js";
4
+
5
+ export interface GenerateFieldsOptions {
6
+ skipWmakeVersions?: boolean;
7
+ skipTestAlerts?: boolean;
8
+ skipTestMessages?: boolean;
9
+ }
10
+
11
+ /** Merges base fields with dynamic WMake version fields and testing tools. */
12
+ export function generateMergedFields(
13
+ cwd: string = process.cwd(),
14
+ seDir?: string,
15
+ options: GenerateFieldsOptions = {}
16
+ ): any {
17
+ const baseFieldsPath = seDir
18
+ ? path.join(cwd, seDir, "fields.json")
19
+ : path.join(cwd, "fields.json");
20
+
21
+ const legacyPath = path.join(cwd, "fields.base.json");
22
+
23
+ let finalBaseFieldsPath = baseFieldsPath;
24
+ if (!fs.existsSync(baseFieldsPath) && fs.existsSync(legacyPath)) {
25
+ finalBaseFieldsPath = legacyPath;
26
+ }
27
+
28
+ if (!fs.existsSync(finalBaseFieldsPath)) {
29
+ console.error(`Fields file not found at ${finalBaseFieldsPath}`);
30
+ return {};
31
+ }
32
+
33
+ const baseFields = JSON.parse(fs.readFileSync(finalBaseFieldsPath, "utf-8"));
34
+ const finalFields: any = { ...baseFields };
35
+ const mergedFields: any = {};
36
+
37
+ // 1. Prepare WMake versions
38
+ const wmakeFields: any = {};
39
+ if (!options.skipWmakeVersions) {
40
+ const wmakeVersions = getWmakeDepsWithVersions(cwd);
41
+ Object.keys(wmakeVersions).forEach(pkg => {
42
+ const name = pkg.replace("@allior/wmake-", "");
43
+ const pascalName = pascalCase(name);
44
+ const fieldKey = `wmake${pascalName}Version`;
45
+ wmakeFields[fieldKey] = {
46
+ type: "text",
47
+ label: `${pascalName} version`,
48
+ value: wmakeVersions[pkg],
49
+ group: "WMake"
50
+ };
51
+ });
52
+ }
53
+
54
+ // 2. Prepare Testing fields
55
+ const testFields: any = {};
56
+ if (!options.skipTestAlerts) {
57
+ testFields["testAlert"] = {
58
+ type: "button",
59
+ label: "Test Alert",
60
+ value: "testAlert",
61
+ group: "Testing"
62
+ };
63
+ }
64
+ if (!options.skipTestMessages) {
65
+ testFields["testMessage"] = {
66
+ type: "button",
67
+ label: "Test Message",
68
+ value: "testMessage",
69
+ group: "Testing"
70
+ };
71
+ }
72
+
73
+ const dynamicFields = { ...wmakeFields, ...testFields };
74
+ const dynamicKeys = Object.keys(dynamicFields).sort();
75
+ const sortedDynamic: any = {};
76
+ dynamicKeys.forEach(k => sortedDynamic[k] = dynamicFields[k]);
77
+
78
+ let dynamicInserted = false;
79
+ for (const key in finalFields) {
80
+ if (key.startsWith("testMessage") && !dynamicInserted) {
81
+ Object.assign(mergedFields, sortedDynamic);
82
+ dynamicInserted = true;
83
+ }
84
+ mergedFields[key] = finalFields[key];
85
+ }
86
+
87
+ if (!dynamicInserted) {
88
+ Object.assign(mergedFields, sortedDynamic);
89
+ }
90
+
91
+ return mergedFields;
92
+ }
93
+
94
+ /** Standalone command entry point */
95
+ export function runGenerateFields(cwd: string = process.cwd()) {
96
+ const fields = generateMergedFields(cwd);
97
+ const fieldsPath = path.join(cwd, "fields.json");
98
+ fs.writeFileSync(fieldsPath, JSON.stringify(fields, null, 2), "utf-8");
99
+ console.log(`Wrote ${fieldsPath}`);
100
+ }
101
+
102
+ function pascalCase(str: string): string {
103
+ return str.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join("");
104
+ }