@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.
@@ -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
+ }
@@ -1,156 +1,152 @@
1
- import type { PluginOption } from "vite";
2
- import fs from "node:fs";
3
1
  import path from "node:path";
2
+ import fs from "node:fs";
3
+ import type { AliasOptions, PluginOption } from "vite";
4
4
 
5
- /** Базовые зависимости, которые всегда выносим в CDN (IIFE) */
6
- const BASE_GLOBALS: Record<string, string> = {
5
+ export const BASE_GLOBALS: Record<string, string> = {
7
6
  react: "React",
8
7
  "react-dom": "ReactDOM",
9
8
  "react-dom/client": "ReactDOM",
10
9
  "react/jsx-runtime": "React",
11
10
  };
12
11
 
13
- /**
14
- * Преобразует @allior/wmake-something в WmakeSomething
15
- * @allior/wmake-something/react -> WmakeSomethingReact
16
- */
17
- function toGlobalName(pkg: string): string {
18
- if (pkg.endsWith("/react")) {
19
- return toGlobalName(pkg.replace("/react", "")) + "React";
20
- }
21
- const name = pkg
22
- .replace("@allior/", "")
23
- .replace(/\//g, "-")
24
- .split("-")
25
- .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
26
- .join("");
27
- return name;
28
- }
29
-
30
- /** Получает список @allior/wmake-* пакетов из депенденси текущего проекта */
31
- function getWmakeDeps(cwd: string): string[] {
32
- const pkgPath = path.join(cwd, "package.json");
33
- if (!fs.existsSync(pkgPath)) return [];
34
- try {
35
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
36
- const deps = { ...pkg.dependencies };
37
- return Object.keys(deps).filter((d) => d.startsWith("@allior/wmake-") && d !== "@allior/wmake-cli");
38
- } catch {
39
- return [];
40
- }
41
- }
42
-
43
- /** Генерирует карту глобалов для Rollup. Используем чистые имена. */
44
- export function getWmakeGlobals(cwd: string = process.cwd()) {
12
+ /** Translates package name to global window variable name */
13
+ export function getWmakeGlobals(cwd: string = process.cwd()): Record<string, string> {
45
14
  const deps = getWmakeDeps(cwd);
46
15
  const res: Record<string, string> = { ...BASE_GLOBALS };
47
16
  for (const pkg of deps) {
48
- res[pkg] = toGlobalName(pkg);
49
- res[`${pkg}/react`] = toGlobalName(pkg) + "React";
17
+ const name = pkg.replace("@allior/wmake-", "");
18
+ const pascalName = name
19
+ .split("-")
20
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
21
+ .join("");
22
+ res[pkg] = `Wmake${pascalName}`;
23
+ res[`${pkg}/react`] = `Wmake${pascalName}React`;
50
24
  }
51
25
  return res;
52
26
  }
53
27
 
54
- /** Вспомогательная функция для получения ЧИСТЫХ имен глобалов (без window.) для проверок */
55
- export function getRawWmakeGlobals(cwd: string = process.cwd()) {
28
+ /** Translates package name to global window variable name (without React/ReactDOM) */
29
+ export function getRawWmakeGlobals(cwd: string = process.cwd()): Record<string, string> {
56
30
  const deps = getWmakeDeps(cwd);
57
- const res: Record<string, string> = { ...BASE_GLOBALS };
31
+ const res: Record<string, string> = {};
58
32
  for (const pkg of deps) {
59
- res[pkg] = toGlobalName(pkg);
60
- res[`${pkg}/react`] = toGlobalName(pkg) + "React";
33
+ const name = pkg.replace("@allior/wmake-", "");
34
+ const pascalName = name
35
+ .split("-")
36
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
37
+ .join("");
38
+ res[pkg] = `Wmake${pascalName}`;
39
+ res[`${pkg}/react`] = `Wmake${pascalName}React`;
61
40
  }
62
41
  return res;
63
42
  }
64
43
 
65
- /** Получает Set внешних зависимостей для CDN режима */
66
- export function getExternalForCdn(cwd: string = process.cwd()): Set<string> {
67
- const deps = getWmakeDeps(cwd);
68
- const res = new Set(Object.keys(BASE_GLOBALS));
69
- for (const pkg of deps) {
70
- res.add(pkg);
71
- res.add(`${pkg}/react`);
44
+ /** Generates Vite aliases for workspace development */
45
+ export function getWmakeAliases(root: string): AliasOptions {
46
+ const dirs = fs.readdirSync(root, { withFileTypes: true });
47
+ const aliases: any = {};
48
+ for (const dir of dirs) {
49
+ if (dir.isDirectory()) {
50
+ const pkgPath = path.join(root, dir.name, "package.json");
51
+ if (fs.existsSync(pkgPath)) {
52
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
53
+ if (pkg.name && pkg.name.startsWith("@allior/wmake-")) {
54
+ aliases[pkg.name] = path.resolve(root, dir.name, "dist/root/index.js");
55
+ aliases[`${pkg.name}/react`] = path.resolve(root, dir.name, "dist/react/index.js");
56
+ }
57
+ }
58
+ }
59
+ }
60
+ return aliases;
61
+ }
62
+
63
+ /** Detects @allior/wmake-* dependencies in package.json and sorts them by hierarchy */
64
+ export function getWmakeDeps(cwd: string = process.cwd()): string[] {
65
+ const pkgPath = path.join(cwd, "package.json");
66
+ if (!fs.existsSync(pkgPath)) return [];
67
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
68
+ const deps = Object.keys(pkg.dependencies || {}).filter((d) => d.startsWith("@allior/wmake-"));
69
+
70
+ return sortWmakeDeps(deps);
71
+ }
72
+
73
+ /** Detects dependencies and their current versions from package.json */
74
+ export function getWmakeDepsWithVersions(cwd: string = process.cwd()): Record<string, string> {
75
+ const pkgPath = path.join(cwd, "package.json");
76
+ if (!fs.existsSync(pkgPath)) return {};
77
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
78
+ const res: Record<string, string> = {};
79
+ const deps = pkg.dependencies || {};
80
+ for (const d in deps) {
81
+ if (d.startsWith("@allior/wmake-")) {
82
+ res[d] = deps[d].replace(/[\^~]/, "");
83
+ }
72
84
  }
73
85
  return res;
74
86
  }
75
87
 
76
- /** Реактивная настройка алиасов для разработки (локальные пути) */
77
- export function getWmakeAliases(root: string, cwd: string = process.cwd()) {
78
- const deps = getWmakeDeps(cwd);
79
- const aliases: Record<string, string> = {};
80
- for (const pkg of deps) {
81
- const name = pkg.replace("@allior/wmake-", "");
82
- aliases[pkg] = path.join(root, `${name}/dist/root/index.js`);
83
- aliases[`${pkg}/react`] = path.join(root, `${name}/dist/react/index.js`);
88
+ /** Checks if a dependency should be external in CDN mode */
89
+ export function isExternalForCdn(id: string, cwd: string = process.cwd()): boolean {
90
+ const normId = id.replace(/\\/g, "/");
91
+ if (Object.keys(BASE_GLOBALS).some(base => id === base || id.startsWith(base + "/"))) return true;
92
+ if (id.startsWith("@allior/wmake-") && id !== "@allior/wmake-cli") return true;
93
+
94
+ const WMAKE_LIBS = ["utils", "streamelements", "streamelements-events", "emotes"];
95
+ for (const lib of WMAKE_LIBS) {
96
+ if (normId.includes(`/${lib}/dist/`)) return true;
84
97
  }
85
- return aliases;
98
+
99
+ if (normId.includes("/@allior/wmake-") || normId.includes("/wmake-")) {
100
+ if (!normId.includes("/wmake-cli/")) return true;
101
+ }
102
+ return false;
86
103
  }
87
104
 
88
- /** Оптимизация Vite (чтобы он не пересобирал их сто раз) */
89
- export function getWmakeOptimizeDeps(cwd: string = process.cwd()) {
105
+ /** Legacy helper for external dependencies */
106
+ export function getExternalForCdn(cwd: string = process.cwd()): string[] {
90
107
  const deps = getWmakeDeps(cwd);
91
- const res = [];
108
+ const res = [...Object.keys(BASE_GLOBALS)];
92
109
  for (const pkg of deps) {
93
- res.push(pkg, `${pkg}/react`);
110
+ res.push(pkg);
111
+ res.push(`${pkg}/react`);
94
112
  }
95
113
  return res;
96
114
  }
97
115
 
98
- /** Плагин для вставки тегов <script> из CDN в HTML.txt */
99
- export function wmakeScriptCdnPlugin(options: {
100
- useCdn: boolean;
101
- projectDir?: string;
102
- }): PluginOption {
103
- const { useCdn, projectDir = process.cwd() } = options;
104
-
105
- return {
106
- name: "wmake-script-cdn",
107
- transformIndexHtml: {
108
- order: "post",
109
- handler(html) {
110
- if (!useCdn) return html;
111
-
112
- // По требованию пользователя используем React 18.2.0 по умолчанию для совместимости
113
- const reactVer = "18.2.0";
114
-
115
- const CDN_BASE = "https://cdn.jsdelivr.net/npm";
116
-
117
- // Скрипты реакта - ВСЕГДА В НАЧАЛО
118
- const reactTags = [
119
- `<script src="${CDN_BASE}/react@${reactVer}/umd/react.production.min.js"></script>`,
120
- `<script src="${CDN_BASE}/react-dom@${reactVer}/umd/react-dom.production.min.js"></script>`
121
- ].join("\n ");
122
-
123
- // Шим для JSX Runtime, чтобы не падал на React.createElement
124
- const shim = `
125
- <script>
126
- (function() {
127
- var r = function() { return window.React; };
128
- window.ReactJSXRuntime = {
129
- get jsx() { return r().createElement; },
130
- get jsxs() { return r().createElement; },
131
- get Fragment() { return r().Fragment; }
132
- };
133
- })();
134
- </script>`;
135
-
136
- // Остальные библиотеки @allior/wmake-*
137
- const deps = getWmakeDeps(projectDir);
138
- const otherTags = deps.map(pkg => {
139
- return [
140
- `<script src="${CDN_BASE}/${pkg}@latest/dist/root/index.iife.js"></script>`,
141
- `<script src="${CDN_BASE}/${pkg}@latest/dist/react/index.iife.js"></script>`
142
- ].join("\n ");
143
- }).join("\n ");
144
-
145
- // Вставляем В НАЧАЛО <head>, чтобы зависимости были первыми
146
- let res = html.replace(/(<head[^>]*>)/i, `$1\n ${reactTags}${shim ? "\n " + shim : ""}\n ${otherTags}`);
116
+ /** Provides list of dependencies for pre-optimization */
117
+ export function getWmakeOptimizeDeps(cwd: string = process.cwd()): string[] {
118
+ const deps = getWmakeDeps(cwd);
119
+ return [...deps, ...deps.map((d) => `${d}/react`)];
120
+ }
147
121
 
148
- // Для работы в Streamelements нам НЕ НУЖЕН тег основного скрипта в HTML,
149
- // так как код живет во вкладке JS. Удаляем его вместе с атрибутами type="module" и crossorigin.
150
- res = res.replace(/<script(?=[^>]*src=["']\/assets\/index\.js["'])[\s\S]*?><\/script>/gi, '');
122
+ /** Vite plugin to inject global window shims for widget development */
123
+ export function wmakeScriptCdnPlugin(options: { useCdn?: boolean } = {}): PluginOption {
124
+ const { useCdn = true } = options;
125
+ if (!useCdn) return { name: "wmake-script-cdn-plugin" };
151
126
 
152
- return res;
153
- },
127
+ return {
128
+ name: "wmake-script-cdn-plugin",
129
+ transformIndexHtml(html: string) {
130
+ return html.replace(
131
+ "</head>",
132
+ `<script src="https://cdn.jsdelivr.net/bundle/npm/react@18.2.0/umd/react.production.min.js"></script>
133
+ <script src="https://cdn.jsdelivr.net/bundle/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
134
+ </head>`
135
+ );
154
136
  },
155
137
  };
156
138
  }
139
+
140
+ /** Sorts dependencies in correct loading order based on internals */
141
+ export function sortWmakeDeps(deps: string[]): string[] {
142
+ const order = ["utils", "streamelements-events", "emotes", "streamelements"];
143
+ return [...deps].sort((a, b) => {
144
+ const aName = a.replace("@allior/wmake-", "");
145
+ const bName = b.replace("@allior/wmake-", "");
146
+ let aIdx = order.indexOf(aName);
147
+ let bIdx = order.indexOf(bName);
148
+ if (aIdx === -1) aIdx = 999;
149
+ if (bIdx === -1) bIdx = 999;
150
+ return aIdx - bIdx;
151
+ });
152
+ }