@amxts/menu-core 0.1.0

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.
@@ -0,0 +1,97 @@
1
+ // Rewrites the colour codes of a menu file - Pawn's \y, \r, \w, \d and \R -
2
+ // as the tags a menu file writes colours with: !y, !r, !w, !d and !R.
3
+ //
4
+ // bun scripts/menu-colors.ts <file>... rewrites each file in place
5
+ // bun scripts/menu-colors.ts --dry-run <file>... says what it would change
6
+ //
7
+ // The file's format is its extension: .ini, .yaml or .yml, .json or .jsonc.
8
+ // In INI and in a plain or single-quoted YAML value a code is a backslash and
9
+ // the letter; in JSON and in a double-quoted YAML value the backslash is
10
+ // escaped, `\\y`, and a single `\r` there is a line break and stays. Comments
11
+ // are rewritten too: they describe the file. Line ends and a byte order mark
12
+ // stay as they are.
13
+ import { readFileSync, writeFileSync } from "node:fs";
14
+ import { extname } from "node:path";
15
+ import process from "node:process";
16
+
17
+ const LETTERS = "yrwdR";
18
+
19
+ /** A file's text with its colour codes made tags, and how many there were. */
20
+ export function convertMenuColors(text: string, format: "ini" | "yaml" | "json"): { text: string; count: number } {
21
+ let count = 0;
22
+ const tag = (_: string, letter: string) => {
23
+ count++;
24
+ return `!${letter}`;
25
+ };
26
+
27
+ if (format === "ini") return { text: text.replace(/\\([yrwdR])/g, tag), count };
28
+ if (format === "json") return { text: text.replace(/(?<!\\)\\\\([yrwdR])/g, tag), count };
29
+
30
+ const lines = text.split("\n").map((line) => {
31
+ let out = "";
32
+ let quoted = false;
33
+ for (let i = 0; i < line.length; i++) {
34
+ const c = line[i];
35
+ const next = line[i + 1] ?? "";
36
+
37
+ if (quoted && c === "\\") {
38
+ // An escape: `\\y` is a code; anything else - `\"`, `\n`, `\r` - stays.
39
+ if (next === "\\" && LETTERS.includes(line[i + 2] ?? "")) {
40
+ out += tag("", line[i + 2]);
41
+ i += 2;
42
+ } else {
43
+ out += c + next;
44
+ i++;
45
+ }
46
+ continue;
47
+ }
48
+
49
+ if (!quoted && c === "\\" && LETTERS.includes(next) && next.length > 0) {
50
+ out += tag("", next);
51
+ i++;
52
+ continue;
53
+ }
54
+
55
+ // A double quote opens a value where a value starts: after `key: `, `- `, `[`, `{`, `,` or the indent.
56
+ if (c === "\"" && (quoted || /(?:^|[:\-[{,])\s*$/.test(line.slice(0, i)))) quoted = !quoted;
57
+ out += c;
58
+ }
59
+ return out;
60
+ });
61
+ return { text: lines.join("\n"), count };
62
+ }
63
+
64
+ function formatOf(file: string): "ini" | "yaml" | "json" | null {
65
+ const extension = extname(file).toLowerCase();
66
+ if (extension === ".ini") return "ini";
67
+ if (extension === ".yaml" || extension === ".yml") return "yaml";
68
+ if (extension === ".json" || extension === ".jsonc") return "json";
69
+ return null;
70
+ }
71
+
72
+ if (import.meta.main) {
73
+ const args = process.argv.slice(2);
74
+ const dryRun = args.includes("--dry-run");
75
+ const files = args.filter(arg => arg !== "--dry-run");
76
+ if (files.length === 0) {
77
+ console.error("usage: bun scripts/menu-colors.ts [--dry-run] <menu file>...");
78
+ process.exit(1);
79
+ }
80
+
81
+ let failed = false;
82
+ for (const file of files) {
83
+ const format = formatOf(file);
84
+ if (!format) {
85
+ console.error(`${file}: not a menu file - .ini, .yaml, .yml, .json or .jsonc`);
86
+ failed = true;
87
+ continue;
88
+ }
89
+
90
+ const converted = convertMenuColors(readFileSync(file, "utf8"), format);
91
+ if (converted.count > 0 && !dryRun) writeFileSync(file, converted.text);
92
+ const verb = dryRun ? "would be" : "were";
93
+ const plural = converted.count === 1 ? "" : "s";
94
+ console.log(converted.count > 0 ? `${file}: ${converted.count} colour code${plural} ${verb} made tags` : `${file}: no colour codes - nothing to change`);
95
+ }
96
+ process.exit(failed ? 1 : 0);
97
+ }