dcs-git-utils 1.0.0 → 1.0.9
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/.github/workflows/pr.yml +33 -33
- package/.github/workflows/release.yml +71 -64
- package/.vscode/launch.json +22 -22
- package/LICENSE +21 -21
- package/README.md +24 -15
- package/dist/compression.js +119 -109
- package/dist/compression.js.map +1 -1
- package/dist/config.js +149 -139
- package/dist/config.js.map +1 -1
- package/dist/fengari-wrapper.js +88 -78
- package/dist/fengari-wrapper.js.map +1 -1
- package/dist/files/file-picker.js +75 -76
- package/dist/files/file-picker.js.map +1 -1
- package/dist/files/miz-selector.js +23 -23
- package/dist/files/miz-selector.js.map +1 -1
- package/dist/lua +72 -72
- package/dist/main.js +51 -48
- package/dist/main.js.map +1 -1
- package/dist/sorting.js +153 -143
- package/dist/sorting.js.map +1 -1
- package/dist/watchers.js +92 -82
- package/dist/watchers.js.map +1 -1
- package/lua/sorter_lib.lua +72 -72
- package/package.json +61 -62
- package/src/compression.ts +87 -87
- package/src/config.ts +119 -119
- package/src/fengari-wrapper.ts +67 -67
- package/src/files/file-picker.ts +63 -63
- package/src/files/miz-selector.ts +9 -9
- package/src/main.ts +53 -49
- package/src/sorting.ts +127 -127
- package/src/watchers.ts +57 -57
- package/tests/sorting.spec.ts +25 -25
- package/tsconfig.json +100 -100
- package/vitest.config.ts +9 -9
package/lua/sorter_lib.lua
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
-- sorter_lib.lua
|
|
2
|
-
|
|
3
|
-
local global_var_name = ...
|
|
4
|
-
local target_table = _G[global_var_name]
|
|
5
|
-
|
|
6
|
-
if type(target_table) ~= "table" then
|
|
7
|
-
return nil, "Table '" .. tostring(global_var_name) .. "' not found."
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
-- CONFIGURATION
|
|
11
|
-
-- true = "key = value" (Clean, standard Lua)
|
|
12
|
-
-- false = "['key'] = value" (Verbose, preserves quotes)
|
|
13
|
-
local SIMPLIFY_KEYS = true
|
|
14
|
-
|
|
15
|
-
-- Keywords that MUST have brackets if used as keys
|
|
16
|
-
local keywords = {
|
|
17
|
-
["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true, ["elseif"]=true, ["end"]=true,
|
|
18
|
-
["false"]=true, ["for"]=true, ["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
|
|
19
|
-
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true, ["repeat"]=true, ["return"]=true,
|
|
20
|
-
["then"]=true, ["true"]=true, ["until"]=true, ["while"]=true
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
local function serialize_sorted(tbl, indent_level)
|
|
24
|
-
indent_level = indent_level or 1
|
|
25
|
-
local indent_str = string.rep(" ", indent_level)
|
|
26
|
-
local result = "{\n"
|
|
27
|
-
|
|
28
|
-
local keys = {}
|
|
29
|
-
for k in pairs(tbl) do table.insert(keys, k) end
|
|
30
|
-
|
|
31
|
-
table.sort(keys, function(a, b)
|
|
32
|
-
local ta, tb = type(a), type(b)
|
|
33
|
-
if ta ~= tb then return ta == "number" end
|
|
34
|
-
if ta == "number" then return a < b end
|
|
35
|
-
return tostring(a) < tostring(b)
|
|
36
|
-
end)
|
|
37
|
-
|
|
38
|
-
for _, k in ipairs(keys) do
|
|
39
|
-
local v = tbl[k]
|
|
40
|
-
local key_str
|
|
41
|
-
|
|
42
|
-
if type(k) == "number" then
|
|
43
|
-
key_str = "[" .. tostring(k) .. "]"
|
|
44
|
-
|
|
45
|
-
elseif type(k) == "string" then
|
|
46
|
-
-- CHECK: Do we want to simplify "['key']" to "key"?
|
|
47
|
-
if SIMPLIFY_KEYS and k:match("^[%a_][%w_]*$") and not keywords[k] then
|
|
48
|
-
key_str = k -- Output: airports =
|
|
49
|
-
else
|
|
50
|
-
key_str = '["' .. k .. '"]' -- Output: ["airports"] =
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
else
|
|
54
|
-
key_str = "[" .. tostring(k) .. "]"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
local val_str
|
|
58
|
-
if type(v) == "table" then
|
|
59
|
-
val_str = serialize_sorted(v, indent_level + 1)
|
|
60
|
-
elseif type(v) == "string" then
|
|
61
|
-
val_str = string.format("%q", v)
|
|
62
|
-
else
|
|
63
|
-
val_str = tostring(v)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
result = result .. indent_str .. key_str .. " = " .. val_str .. ",\n"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
result = result .. string.rep(" ", indent_level - 1) .. "}"
|
|
70
|
-
return result
|
|
71
|
-
end
|
|
72
|
-
|
|
1
|
+
-- sorter_lib.lua
|
|
2
|
+
|
|
3
|
+
local global_var_name = ...
|
|
4
|
+
local target_table = _G[global_var_name]
|
|
5
|
+
|
|
6
|
+
if type(target_table) ~= "table" then
|
|
7
|
+
return nil, "Table '" .. tostring(global_var_name) .. "' not found."
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
-- CONFIGURATION
|
|
11
|
+
-- true = "key = value" (Clean, standard Lua)
|
|
12
|
+
-- false = "['key'] = value" (Verbose, preserves quotes)
|
|
13
|
+
local SIMPLIFY_KEYS = true
|
|
14
|
+
|
|
15
|
+
-- Keywords that MUST have brackets if used as keys
|
|
16
|
+
local keywords = {
|
|
17
|
+
["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true, ["elseif"]=true, ["end"]=true,
|
|
18
|
+
["false"]=true, ["for"]=true, ["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
|
|
19
|
+
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true, ["repeat"]=true, ["return"]=true,
|
|
20
|
+
["then"]=true, ["true"]=true, ["until"]=true, ["while"]=true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
local function serialize_sorted(tbl, indent_level)
|
|
24
|
+
indent_level = indent_level or 1
|
|
25
|
+
local indent_str = string.rep(" ", indent_level)
|
|
26
|
+
local result = "{\n"
|
|
27
|
+
|
|
28
|
+
local keys = {}
|
|
29
|
+
for k in pairs(tbl) do table.insert(keys, k) end
|
|
30
|
+
|
|
31
|
+
table.sort(keys, function(a, b)
|
|
32
|
+
local ta, tb = type(a), type(b)
|
|
33
|
+
if ta ~= tb then return ta == "number" end
|
|
34
|
+
if ta == "number" then return a < b end
|
|
35
|
+
return tostring(a) < tostring(b)
|
|
36
|
+
end)
|
|
37
|
+
|
|
38
|
+
for _, k in ipairs(keys) do
|
|
39
|
+
local v = tbl[k]
|
|
40
|
+
local key_str
|
|
41
|
+
|
|
42
|
+
if type(k) == "number" then
|
|
43
|
+
key_str = "[" .. tostring(k) .. "]"
|
|
44
|
+
|
|
45
|
+
elseif type(k) == "string" then
|
|
46
|
+
-- CHECK: Do we want to simplify "['key']" to "key"?
|
|
47
|
+
if SIMPLIFY_KEYS and k:match("^[%a_][%w_]*$") and not keywords[k] then
|
|
48
|
+
key_str = k -- Output: airports =
|
|
49
|
+
else
|
|
50
|
+
key_str = '["' .. k .. '"]' -- Output: ["airports"] =
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
else
|
|
54
|
+
key_str = "[" .. tostring(k) .. "]"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
local val_str
|
|
58
|
+
if type(v) == "table" then
|
|
59
|
+
val_str = serialize_sorted(v, indent_level + 1)
|
|
60
|
+
elseif type(v) == "string" then
|
|
61
|
+
val_str = string.format("%q", v)
|
|
62
|
+
else
|
|
63
|
+
val_str = tostring(v)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
result = result .. indent_str .. key_str .. " = " .. val_str .. ",\n"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
result = result .. string.rep(" ", indent_level - 1) .. "}"
|
|
70
|
+
return result
|
|
71
|
+
end
|
|
72
|
+
|
|
73
73
|
return global_var_name .. " = " .. serialize_sorted(target_table)
|
package/package.json
CHANGED
|
@@ -1,62 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dcs-git-utils",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "dist/main.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"dcs-git-utils": "dist/main.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"prestart": "npm run compile && npm run copy-assets",
|
|
11
|
-
"clean": "shx rm -rf dist && shx rm -rf bin",
|
|
12
|
-
"compile": "tsc",
|
|
13
|
-
"build": "npm run clean && npm run compile && npm run copy-assets",
|
|
14
|
-
"copy-assets": "shx cp lua/sorter_lib.lua dist/lua",
|
|
15
|
-
"start": "node dist/main.js",
|
|
16
|
-
"package": "pkg .",
|
|
17
|
-
"test": "vitest",
|
|
18
|
-
"test:run": "vitest run",
|
|
19
|
-
"test:ci": "vitest"
|
|
20
|
-
},
|
|
21
|
-
"author": "",
|
|
22
|
-
"license": "ISC",
|
|
23
|
-
"pkg": {
|
|
24
|
-
"scripts": [
|
|
25
|
-
"dist/**/*.js",
|
|
26
|
-
"!dist/**/*.test.js",
|
|
27
|
-
"!dist/**/*.spec.js",
|
|
28
|
-
"!dist/tests/**/*",
|
|
29
|
-
"dist/**/*.js"
|
|
30
|
-
],
|
|
31
|
-
"assets": [
|
|
32
|
-
"./lua/sorter_lib.lua"
|
|
33
|
-
],
|
|
34
|
-
"targets": [
|
|
35
|
-
"node18-win-x64",
|
|
36
|
-
"node18-linux-x64"
|
|
37
|
-
],
|
|
38
|
-
"outputPath": "bin"
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@types/archiver": "^6.0.3",
|
|
42
|
-
"@types/
|
|
43
|
-
"@types/
|
|
44
|
-
"@types/
|
|
45
|
-
"@types/
|
|
46
|
-
"@types/
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"fengari": "^0.1.
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dcs-git-utils",
|
|
3
|
+
"version": "1.0.9",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/main.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dcs-git-utils": "dist/main.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"prestart": "npm run compile && npm run copy-assets",
|
|
11
|
+
"clean": "shx rm -rf dist && shx rm -rf bin",
|
|
12
|
+
"compile": "tsc",
|
|
13
|
+
"build": "npm run clean && npm run compile && npm run copy-assets",
|
|
14
|
+
"copy-assets": "shx cp lua/sorter_lib.lua dist/lua",
|
|
15
|
+
"start": "node dist/main.js",
|
|
16
|
+
"package": "pkg .",
|
|
17
|
+
"test": "vitest",
|
|
18
|
+
"test:run": "vitest run",
|
|
19
|
+
"test:ci": "vitest"
|
|
20
|
+
},
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"pkg": {
|
|
24
|
+
"scripts": [
|
|
25
|
+
"dist/**/*.js",
|
|
26
|
+
"!dist/**/*.test.js",
|
|
27
|
+
"!dist/**/*.spec.js",
|
|
28
|
+
"!dist/tests/**/*",
|
|
29
|
+
"dist/**/*.js"
|
|
30
|
+
],
|
|
31
|
+
"assets": [
|
|
32
|
+
"./lua/sorter_lib.lua"
|
|
33
|
+
],
|
|
34
|
+
"targets": [
|
|
35
|
+
"node18-win-x64",
|
|
36
|
+
"node18-linux-x64"
|
|
37
|
+
],
|
|
38
|
+
"outputPath": "bin"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/archiver": "^6.0.3",
|
|
42
|
+
"@types/decompress": "^4.2.7",
|
|
43
|
+
"@types/inquirer": "^8.2.12",
|
|
44
|
+
"@types/lodash": "^4.17.20",
|
|
45
|
+
"@types/luaparse": "^0.2.13",
|
|
46
|
+
"@types/node": "^24.0.10",
|
|
47
|
+
"pkg": "^5.8.1",
|
|
48
|
+
"shx": "^0.4.0",
|
|
49
|
+
"vitest": "^4.0.14"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"archiver": "^7.0.1",
|
|
53
|
+
"chokidar": "^3.6.0",
|
|
54
|
+
"decompress": "^4.2.1",
|
|
55
|
+
"fengari": "^0.1.4",
|
|
56
|
+
"fengari-interop": "^0.1.3",
|
|
57
|
+
"inquirer": "^8.2.7",
|
|
58
|
+
"lodash": "^4.17.21",
|
|
59
|
+
"luaparse": "^0.3.1"
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/compression.ts
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
|
|
2
|
-
import fs, { promises } from "node:fs";
|
|
3
|
-
import archiver from "archiver";
|
|
4
|
-
import decompress from "decompress";
|
|
5
|
-
|
|
6
|
-
export async function checkStable(
|
|
7
|
-
filePath: string,
|
|
8
|
-
stableChecks = 10, // how many *consecutive* stable reads required
|
|
9
|
-
interval = 250 // ms between checks
|
|
10
|
-
): Promise<boolean> {
|
|
11
|
-
|
|
12
|
-
let lastSize: number | null = null;
|
|
13
|
-
let stableCount = 0;
|
|
14
|
-
|
|
15
|
-
while (stableCount < stableChecks) {
|
|
16
|
-
let stat;
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
stat = await promises.stat(filePath);
|
|
20
|
-
} catch {
|
|
21
|
-
// file might not exist yet – reset
|
|
22
|
-
lastSize = null;
|
|
23
|
-
stableCount = 0;
|
|
24
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const size = stat.size;
|
|
29
|
-
|
|
30
|
-
if (lastSize !== null && size === lastSize) {
|
|
31
|
-
stableCount++;
|
|
32
|
-
} else {
|
|
33
|
-
stableCount = 0; // reset if changed
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
lastSize = size;
|
|
37
|
-
|
|
38
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return true; // stable!
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const handleArchive = (dirPath: string, mizpath: string) => {
|
|
45
|
-
return new Promise<void>(async (resolve, reject) => {
|
|
46
|
-
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
47
|
-
|
|
48
|
-
const output = fs.createWriteStream(mizpath);
|
|
49
|
-
|
|
50
|
-
// Handle events
|
|
51
|
-
output.on('close', function () {
|
|
52
|
-
console.log(`Archive created successfully at ${mizpath}, total bytes: ${archive.pointer()}`);
|
|
53
|
-
resolve();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
archive.on('error', function (err) {
|
|
57
|
-
throw err;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Pipe archive data to the file
|
|
61
|
-
archive.pipe(output);
|
|
62
|
-
|
|
63
|
-
// Append directory
|
|
64
|
-
archive.directory(dirPath, false); // false = no root folder in zip
|
|
65
|
-
|
|
66
|
-
// Finalize the archive
|
|
67
|
-
await archive.finalize();
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export const unpackMiz = async (path: string, outPath: string) => {
|
|
72
|
-
try {
|
|
73
|
-
|
|
74
|
-
console.log('decompressing');
|
|
75
|
-
|
|
76
|
-
const result = await decompress(path, outPath);
|
|
77
|
-
|
|
78
|
-
if (!result.length) {
|
|
79
|
-
throw new Error('Decompression yielded no results, something is wrong with the .miz');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return result;
|
|
83
|
-
|
|
84
|
-
} catch (err) {
|
|
85
|
-
console.error(err);
|
|
86
|
-
throw err;
|
|
87
|
-
}
|
|
1
|
+
|
|
2
|
+
import fs, { promises } from "node:fs";
|
|
3
|
+
import archiver from "archiver";
|
|
4
|
+
import decompress from "decompress";
|
|
5
|
+
|
|
6
|
+
export async function checkStable(
|
|
7
|
+
filePath: string,
|
|
8
|
+
stableChecks = 10, // how many *consecutive* stable reads required
|
|
9
|
+
interval = 250 // ms between checks
|
|
10
|
+
): Promise<boolean> {
|
|
11
|
+
|
|
12
|
+
let lastSize: number | null = null;
|
|
13
|
+
let stableCount = 0;
|
|
14
|
+
|
|
15
|
+
while (stableCount < stableChecks) {
|
|
16
|
+
let stat;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
stat = await promises.stat(filePath);
|
|
20
|
+
} catch {
|
|
21
|
+
// file might not exist yet – reset
|
|
22
|
+
lastSize = null;
|
|
23
|
+
stableCount = 0;
|
|
24
|
+
await new Promise((res) => setTimeout(res, interval));
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const size = stat.size;
|
|
29
|
+
|
|
30
|
+
if (lastSize !== null && size === lastSize) {
|
|
31
|
+
stableCount++;
|
|
32
|
+
} else {
|
|
33
|
+
stableCount = 0; // reset if changed
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
lastSize = size;
|
|
37
|
+
|
|
38
|
+
await new Promise((res) => setTimeout(res, interval));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return true; // stable!
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const handleArchive = (dirPath: string, mizpath: string) => {
|
|
45
|
+
return new Promise<void>(async (resolve, reject) => {
|
|
46
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
47
|
+
|
|
48
|
+
const output = fs.createWriteStream(mizpath);
|
|
49
|
+
|
|
50
|
+
// Handle events
|
|
51
|
+
output.on('close', function () {
|
|
52
|
+
console.log(`Archive created successfully at ${mizpath}, total bytes: ${archive.pointer()}`);
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
archive.on('error', function (err) {
|
|
57
|
+
throw err;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Pipe archive data to the file
|
|
61
|
+
archive.pipe(output);
|
|
62
|
+
|
|
63
|
+
// Append directory
|
|
64
|
+
archive.directory(dirPath, false); // false = no root folder in zip
|
|
65
|
+
|
|
66
|
+
// Finalize the archive
|
|
67
|
+
await archive.finalize();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const unpackMiz = async (path: string, outPath: string) => {
|
|
72
|
+
try {
|
|
73
|
+
|
|
74
|
+
console.log('decompressing');
|
|
75
|
+
|
|
76
|
+
const result = await decompress(path, outPath);
|
|
77
|
+
|
|
78
|
+
if (!result.length) {
|
|
79
|
+
throw new Error('Decompression yielded no results, something is wrong with the .miz');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(err);
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
88
|
}
|