icuewidget-cli 0.4.47 → 0.4.48
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/package.json +1 -1
- package/src-node/cli.js +5 -1
- package/src-node/constants.js +1 -1
- package/src-node/prompts.js +21 -1
- package/src-node/validation.js +21 -4
package/package.json
CHANGED
package/src-node/cli.js
CHANGED
|
@@ -19,6 +19,8 @@ import { notifyAboutUpdate, runUpdateCheckWorker } from "./update-check.js";
|
|
|
19
19
|
import { ValidationError, validateWidget } from "./validation.js";
|
|
20
20
|
import { createPackage } from "./zip.js";
|
|
21
21
|
|
|
22
|
+
const COMMANDS = ["init", "validate", "update", "package"];
|
|
23
|
+
|
|
22
24
|
export async function main(argv = process.argv) {
|
|
23
25
|
try {
|
|
24
26
|
const args = argv.slice(2);
|
|
@@ -30,7 +32,9 @@ export async function main(argv = process.argv) {
|
|
|
30
32
|
printHelp();
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
|
-
|
|
35
|
+
// `update <widget> --version <semver>` uses --version as a subcommand option, so only
|
|
36
|
+
// treat it as the global flag when no subcommand leads the argument list.
|
|
37
|
+
if (!COMMANDS.includes(args[0]) && (has(args, "--version") || has(args, "-v"))) {
|
|
34
38
|
console.log(CLI_VERSION);
|
|
35
39
|
return;
|
|
36
40
|
}
|
package/src-node/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const APP_NAME = "icuewidget";
|
|
2
2
|
export const NPM_PACKAGE_NAME = "icuewidget-cli";
|
|
3
|
-
export const CLI_VERSION = "0.4.
|
|
3
|
+
export const CLI_VERSION = "0.4.48";
|
|
4
4
|
export const DEFAULT_APP_VERSION = "5.45";
|
|
5
5
|
export const DEFAULT_FRAMEWORK_VERSION = "1.0.0";
|
|
6
6
|
export const DEFAULT_PLATFORM = "windows";
|
package/src-node/prompts.js
CHANGED
|
@@ -10,10 +10,30 @@ function reader() {
|
|
|
10
10
|
|
|
11
11
|
export async function prompt(label, defaultValue = "") {
|
|
12
12
|
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
13
|
-
const answer = (await
|
|
13
|
+
const answer = (await ask(`${label}${suffix}: `, label)).trim();
|
|
14
14
|
return answer || defaultValue;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// readline never settles a pending question when stdin ends (closed pipe, CI, /dev/null), so
|
|
18
|
+
// Node would exit 0 with the command half-run. Reject instead so the failure is visible.
|
|
19
|
+
function ask(question, label) {
|
|
20
|
+
const rl = reader();
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const onClose = () => reject(new Error(`Input ended while waiting for "${label}". Pass the value as a flag or run interactively.`));
|
|
23
|
+
rl.once("close", onClose);
|
|
24
|
+
rl.question(question).then(
|
|
25
|
+
(answer) => {
|
|
26
|
+
rl.off("close", onClose);
|
|
27
|
+
resolve(answer);
|
|
28
|
+
},
|
|
29
|
+
(error) => {
|
|
30
|
+
rl.off("close", onClose);
|
|
31
|
+
reject(error);
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
17
37
|
export async function promptValidated(label, defaultValue, validator, errorMessage) {
|
|
18
38
|
while (true) {
|
|
19
39
|
const answer = await prompt(label, defaultValue);
|
package/src-node/validation.js
CHANGED
|
@@ -23,7 +23,7 @@ export async function validateWidget(widgetDir) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const manifest = await validateManifest(root);
|
|
26
|
+
const manifest = await validateManifest(root, warnings);
|
|
27
27
|
if (await pathExists(path.join(root, "translation.json"))) {
|
|
28
28
|
await readJson(path.join(root, "translation.json"));
|
|
29
29
|
} else {
|
|
@@ -34,7 +34,7 @@ export async function validateWidget(widgetDir) {
|
|
|
34
34
|
return { widgetDir: root, manifest, warnings };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
async function validateManifest(root) {
|
|
37
|
+
async function validateManifest(root, warnings) {
|
|
38
38
|
const manifest = await readJson(path.join(root, "manifest.json"));
|
|
39
39
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
40
40
|
throw new ValidationError("manifest.json must contain a JSON object");
|
|
@@ -78,6 +78,18 @@ async function validateManifest(root) {
|
|
|
78
78
|
if (manifest.interactive !== undefined && typeof manifest.interactive !== "boolean") {
|
|
79
79
|
throw new ValidationError("manifest.interactive must be a boolean when provided");
|
|
80
80
|
}
|
|
81
|
+
if (manifest.interactive === true) {
|
|
82
|
+
const nonDashboardTypes = manifest.supported_devices
|
|
83
|
+
.map((d) => d.type)
|
|
84
|
+
.filter((t) => t !== "dashboard_lcd");
|
|
85
|
+
if (nonDashboardTypes.length > 0) {
|
|
86
|
+
warnings.push(
|
|
87
|
+
`manifest.interactive is true, but supported_devices includes ${nonDashboardTypes.join(", ")}. ` +
|
|
88
|
+
`iCUE only recognizes interactive widgets on dashboard_lcd (Xeneon Edge). ` +
|
|
89
|
+
`The widget will not appear for ${nonDashboardTypes.join(" or ")} devices.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
81
93
|
if (manifest.required_plugins !== undefined) validateStringArray(manifest.required_plugins, "manifest.required_plugins");
|
|
82
94
|
if (manifest.modules !== undefined) {
|
|
83
95
|
validateStringArray(manifest.modules, "manifest.modules");
|
|
@@ -91,19 +103,23 @@ async function validateIndex(root, warnings) {
|
|
|
91
103
|
const html = await fs.readFile(path.join(root, "index.html"), "utf8");
|
|
92
104
|
if (!/<title>[^<]+<\/title>/i.test(html)) warnings.push("index.html does not define a <title>.");
|
|
93
105
|
if (!/rel=["'][^"']*icon/i.test(html)) warnings.push("index.html does not reference a favicon/icon.");
|
|
94
|
-
if (!html.includes("icueEvents")) warnings.push("index.html or scripts do not reference icueEvents.");
|
|
95
106
|
|
|
96
107
|
validateHeadWellFormed(html);
|
|
97
108
|
validatePropertyTypes(html);
|
|
98
109
|
validatePropertyGroups(html);
|
|
99
110
|
|
|
111
|
+
let referencesIcueEvents = html.includes("icueEvents");
|
|
100
112
|
const assetRefs = [...extractAttributes(html, "href"), ...extractAttributes(html, "src")];
|
|
101
113
|
for (const ref of assetRefs) {
|
|
102
114
|
if (isRemote(ref) || ref.startsWith("data:") || ref.startsWith("#")) continue;
|
|
103
115
|
await validateRelativeFile(root, stripQuery(ref), `index.html asset reference ${ref}`);
|
|
104
116
|
if (ref.endsWith(".css")) await validateCss(path.join(root, stripQuery(ref)));
|
|
105
|
-
if (ref.endsWith(".js"))
|
|
117
|
+
if (ref.endsWith(".js")) {
|
|
118
|
+
const code = await validateJs(path.join(root, stripQuery(ref)));
|
|
119
|
+
if (code.includes("icueEvents")) referencesIcueEvents = true;
|
|
120
|
+
}
|
|
106
121
|
}
|
|
122
|
+
if (!referencesIcueEvents) warnings.push("index.html or scripts do not reference icueEvents.");
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
// iCUE imports a widget by parsing <head> with a strict XML parser (Qt QDomDocument).
|
|
@@ -215,6 +231,7 @@ async function validateJs(filePath) {
|
|
|
215
231
|
} catch (error) {
|
|
216
232
|
throw new ValidationError(`${path.basename(filePath)} has invalid JavaScript: ${error.message}`);
|
|
217
233
|
}
|
|
234
|
+
return code;
|
|
218
235
|
}
|
|
219
236
|
|
|
220
237
|
async function validateRelativeFile(root, relativePath, label) {
|