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/src/sorting.ts
CHANGED
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
import luaparse from "luaparse"
|
|
3
|
-
import promises from "node:fs/promises"
|
|
4
|
-
import * as _ from "lodash"
|
|
5
|
-
import { fengariSortFile } from "./fengari-wrapper";
|
|
6
|
-
|
|
7
|
-
const jsonExtensions = ["json", "dtc"];
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Recursively sorts the keys of an object or elements of an array.
|
|
11
|
-
* This function handles arrays, plain objects, and primitives.
|
|
12
|
-
*
|
|
13
|
-
* @param obj The data structure to sort.
|
|
14
|
-
* @returns A new structure with sorted keys/elements.
|
|
15
|
-
*/
|
|
16
|
-
function lodashSortKeysDeep(obj: any): any {
|
|
17
|
-
if (_.isArray(obj)) {
|
|
18
|
-
// Handle Arrays: Recursively sort each element
|
|
19
|
-
return obj.map(lodashSortKeysDeep);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!_.isPlainObject(obj)) {
|
|
23
|
-
// Handle Primitives and Non-Plain Objects: Return as is
|
|
24
|
-
return obj;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Handle Plain Objects: Get sorted keys and build the new object
|
|
28
|
-
return _.keys(obj)
|
|
29
|
-
.sort() // Sort the keys alphabetically
|
|
30
|
-
.reduce((sortedObj: _.Dictionary<any>, key: string) => {
|
|
31
|
-
// Recursively sort the value for the current key
|
|
32
|
-
sortedObj[key] = lodashSortKeysDeep(obj[key]);
|
|
33
|
-
return sortedObj;
|
|
34
|
-
}, {});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const checkCharCount = (old: string, sorted: string) => {
|
|
39
|
-
return old.replace(/\s/g, "").length === sorted.replace(/\s/g, "").length
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const jsonSort = async (filepath: string) => {
|
|
43
|
-
|
|
44
|
-
const fileString = await promises.readFile(filepath, { encoding: 'utf-8' });
|
|
45
|
-
|
|
46
|
-
const useCRLF = fileString.includes("\r\n");
|
|
47
|
-
const EOL = useCRLF ? "\r\n" : "\n";
|
|
48
|
-
|
|
49
|
-
const obj = JSON.parse(fileString);
|
|
50
|
-
const sorted = lodashSortKeysDeep(obj);
|
|
51
|
-
const stringified = JSON.stringify(sorted);
|
|
52
|
-
if (!checkCharCount(fileString, stringified)) {
|
|
53
|
-
throw new Error(`mismatching input file char count to sorted`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const resultString = stringified.replace(/\r?\n/g, EOL);
|
|
57
|
-
await promises.writeFile(filepath, resultString, 'utf-8');
|
|
58
|
-
console.log(`Sorted JSON: ${filepath}`)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const isLuaSortable = async (filepath: string) => {
|
|
62
|
-
|
|
63
|
-
const filename = path.basename(filepath);
|
|
64
|
-
const extension = path.extname(filename);
|
|
65
|
-
const lua = await promises.readFile(filepath, { encoding: 'utf-8' });
|
|
66
|
-
|
|
67
|
-
if (extension && extension !== "lua") {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const ast = luaparse.parse(lua);
|
|
73
|
-
|
|
74
|
-
if (ast.type !== 'Chunk' || !ast.body) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
for (const statement of ast.body) {
|
|
79
|
-
let isMatch = false;
|
|
80
|
-
|
|
81
|
-
// Check for LOCAL assignment: local Config = {...}
|
|
82
|
-
if (statement.type === 'LocalStatement') {
|
|
83
|
-
isMatch = statement.variables.some(v => v.name === filename);
|
|
84
|
-
if (isMatch) return true;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Check for GLOBAL assignment: Config = {...}
|
|
88
|
-
else if (statement.type === 'AssignmentStatement') {
|
|
89
|
-
isMatch = statement.variables.some(v => v.type === 'Identifier' && v.name === filename);
|
|
90
|
-
if (isMatch) return true;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
console.log(`luaparse failed: ${filename}`);
|
|
96
|
-
}
|
|
97
|
-
return false;
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const sortFile = async (filepath: string) => {
|
|
102
|
-
|
|
103
|
-
const extension = path.extname(filepath);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
if (extension && jsonExtensions.includes(extension)) {
|
|
108
|
-
await jsonSort(filepath);
|
|
109
|
-
}
|
|
110
|
-
else if (await isLuaSortable(filepath)) {
|
|
111
|
-
await fengariSortFile(filepath);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
console.log(`skipping: ${filepath}`);
|
|
115
|
-
}
|
|
116
|
-
} catch (err) {
|
|
117
|
-
console.error(err);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export const sortFiles = async (filePaths: string[]) => {
|
|
124
|
-
for (const filepath of filePaths) {
|
|
125
|
-
await sortFile(filepath);
|
|
126
|
-
}
|
|
127
|
-
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import luaparse from "luaparse"
|
|
3
|
+
import promises from "node:fs/promises"
|
|
4
|
+
import * as _ from "lodash"
|
|
5
|
+
import { fengariSortFile } from "./fengari-wrapper";
|
|
6
|
+
|
|
7
|
+
const jsonExtensions = ["json", "dtc"];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Recursively sorts the keys of an object or elements of an array.
|
|
11
|
+
* This function handles arrays, plain objects, and primitives.
|
|
12
|
+
*
|
|
13
|
+
* @param obj The data structure to sort.
|
|
14
|
+
* @returns A new structure with sorted keys/elements.
|
|
15
|
+
*/
|
|
16
|
+
function lodashSortKeysDeep(obj: any): any {
|
|
17
|
+
if (_.isArray(obj)) {
|
|
18
|
+
// Handle Arrays: Recursively sort each element
|
|
19
|
+
return obj.map(lodashSortKeysDeep);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!_.isPlainObject(obj)) {
|
|
23
|
+
// Handle Primitives and Non-Plain Objects: Return as is
|
|
24
|
+
return obj;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Handle Plain Objects: Get sorted keys and build the new object
|
|
28
|
+
return _.keys(obj)
|
|
29
|
+
.sort() // Sort the keys alphabetically
|
|
30
|
+
.reduce((sortedObj: _.Dictionary<any>, key: string) => {
|
|
31
|
+
// Recursively sort the value for the current key
|
|
32
|
+
sortedObj[key] = lodashSortKeysDeep(obj[key]);
|
|
33
|
+
return sortedObj;
|
|
34
|
+
}, {});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const checkCharCount = (old: string, sorted: string) => {
|
|
39
|
+
return old.replace(/\s/g, "").length === sorted.replace(/\s/g, "").length
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const jsonSort = async (filepath: string) => {
|
|
43
|
+
|
|
44
|
+
const fileString = await promises.readFile(filepath, { encoding: 'utf-8' });
|
|
45
|
+
|
|
46
|
+
const useCRLF = fileString.includes("\r\n");
|
|
47
|
+
const EOL = useCRLF ? "\r\n" : "\n";
|
|
48
|
+
|
|
49
|
+
const obj = JSON.parse(fileString);
|
|
50
|
+
const sorted = lodashSortKeysDeep(obj);
|
|
51
|
+
const stringified = JSON.stringify(sorted);
|
|
52
|
+
if (!checkCharCount(fileString, stringified)) {
|
|
53
|
+
throw new Error(`mismatching input file char count to sorted`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const resultString = stringified.replace(/\r?\n/g, EOL);
|
|
57
|
+
await promises.writeFile(filepath, resultString, 'utf-8');
|
|
58
|
+
console.log(`Sorted JSON: ${filepath}`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const isLuaSortable = async (filepath: string) => {
|
|
62
|
+
|
|
63
|
+
const filename = path.basename(filepath);
|
|
64
|
+
const extension = path.extname(filename);
|
|
65
|
+
const lua = await promises.readFile(filepath, { encoding: 'utf-8' });
|
|
66
|
+
|
|
67
|
+
if (extension && extension !== "lua") {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const ast = luaparse.parse(lua);
|
|
73
|
+
|
|
74
|
+
if (ast.type !== 'Chunk' || !ast.body) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const statement of ast.body) {
|
|
79
|
+
let isMatch = false;
|
|
80
|
+
|
|
81
|
+
// Check for LOCAL assignment: local Config = {...}
|
|
82
|
+
if (statement.type === 'LocalStatement') {
|
|
83
|
+
isMatch = statement.variables.some(v => v.name === filename);
|
|
84
|
+
if (isMatch) return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check for GLOBAL assignment: Config = {...}
|
|
88
|
+
else if (statement.type === 'AssignmentStatement') {
|
|
89
|
+
isMatch = statement.variables.some(v => v.type === 'Identifier' && v.name === filename);
|
|
90
|
+
if (isMatch) return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
console.log(`luaparse failed: ${filename}`);
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const sortFile = async (filepath: string) => {
|
|
102
|
+
|
|
103
|
+
const extension = path.extname(filepath);
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
if (extension && jsonExtensions.includes(extension)) {
|
|
108
|
+
await jsonSort(filepath);
|
|
109
|
+
}
|
|
110
|
+
else if (await isLuaSortable(filepath)) {
|
|
111
|
+
await fengariSortFile(filepath);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.log(`skipping: ${filepath}`);
|
|
115
|
+
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error(err);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const sortFiles = async (filePaths: string[]) => {
|
|
124
|
+
for (const filepath of filePaths) {
|
|
125
|
+
await sortFile(filepath);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
128
|
}
|
package/src/watchers.ts
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import * as _ from "lodash"
|
|
2
|
-
import * as chokidar from 'chokidar';
|
|
3
|
-
import { sortFiles } from "./sorting";
|
|
4
|
-
import { handleArchive, unpackMiz } from "./compression";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { debounce } from "lodash";
|
|
7
|
-
|
|
8
|
-
let locked = false;
|
|
9
|
-
|
|
10
|
-
const unlock = debounce(() => {
|
|
11
|
-
locked = false;
|
|
12
|
-
}, 3000);
|
|
13
|
-
|
|
14
|
-
export const startWatchers = (mizPath: string, outDir: string) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
chokidar.watch(mizPath, { awaitWriteFinish: true, ignoreInitial: true }).on("change", async () => {
|
|
18
|
-
|
|
19
|
-
if (locked) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
console.log("miz changed");
|
|
24
|
-
|
|
25
|
-
locked = true;
|
|
26
|
-
unlock.cancel();
|
|
27
|
-
const files = await unpackMiz(mizPath, outDir)
|
|
28
|
-
const filePaths = files.map(f => path.join(outDir, f.path));
|
|
29
|
-
await sortFiles(filePaths);
|
|
30
|
-
await handleArchive(outDir, mizPath);
|
|
31
|
-
unlock();
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
const outChange = async () => {
|
|
35
|
-
|
|
36
|
-
if (locked) {
|
|
37
|
-
unlock();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log("out changed");
|
|
42
|
-
|
|
43
|
-
locked = true;
|
|
44
|
-
unlock.cancel();
|
|
45
|
-
await handleArchive(outDir, mizPath);
|
|
46
|
-
unlock();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
chokidar.watch(outDir, { ignoreInitial: true })
|
|
50
|
-
.on("add", async () => {
|
|
51
|
-
await outChange();
|
|
52
|
-
}).on("addDir", async () => {
|
|
53
|
-
await outChange();
|
|
54
|
-
}).on("change", async () => {
|
|
55
|
-
await outChange();
|
|
56
|
-
})
|
|
57
|
-
|
|
1
|
+
import * as _ from "lodash"
|
|
2
|
+
import * as chokidar from 'chokidar';
|
|
3
|
+
import { sortFiles } from "./sorting";
|
|
4
|
+
import { handleArchive, unpackMiz } from "./compression";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { debounce } from "lodash";
|
|
7
|
+
|
|
8
|
+
let locked = false;
|
|
9
|
+
|
|
10
|
+
const unlock = debounce(() => {
|
|
11
|
+
locked = false;
|
|
12
|
+
}, 3000);
|
|
13
|
+
|
|
14
|
+
export const startWatchers = (mizPath: string, outDir: string) => {
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
chokidar.watch(mizPath, { awaitWriteFinish: true, ignoreInitial: true }).on("change", async () => {
|
|
18
|
+
|
|
19
|
+
if (locked) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log("miz changed");
|
|
24
|
+
|
|
25
|
+
locked = true;
|
|
26
|
+
unlock.cancel();
|
|
27
|
+
const files = await unpackMiz(mizPath, outDir)
|
|
28
|
+
const filePaths = files.map(f => path.join(outDir, f.path));
|
|
29
|
+
await sortFiles(filePaths);
|
|
30
|
+
await handleArchive(outDir, mizPath);
|
|
31
|
+
unlock();
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const outChange = async () => {
|
|
35
|
+
|
|
36
|
+
if (locked) {
|
|
37
|
+
unlock();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log("out changed");
|
|
42
|
+
|
|
43
|
+
locked = true;
|
|
44
|
+
unlock.cancel();
|
|
45
|
+
await handleArchive(outDir, mizPath);
|
|
46
|
+
unlock();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
chokidar.watch(outDir, { ignoreInitial: true })
|
|
50
|
+
.on("add", async () => {
|
|
51
|
+
await outChange();
|
|
52
|
+
}).on("addDir", async () => {
|
|
53
|
+
await outChange();
|
|
54
|
+
}).on("change", async () => {
|
|
55
|
+
await outChange();
|
|
56
|
+
})
|
|
57
|
+
|
|
58
58
|
}
|
package/tests/sorting.spec.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest';
|
|
2
|
-
import * as fs from 'fs/promises';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { saveConfig } from '../src/config'; // Assume you exported this
|
|
5
|
-
import { jsonSort } from '../src/sorting';
|
|
6
|
-
|
|
7
|
-
describe('Sorting', () => {
|
|
8
|
-
const filepath = "./test.json";
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
vi.clearAllMocks(); // Reset counters before each test
|
|
12
|
-
});
|
|
13
|
-
afterAll(async () => {
|
|
14
|
-
await fs.rm(filepath);
|
|
15
|
-
})
|
|
16
|
-
it('should write sort JSON keys', async () => {
|
|
17
|
-
// 2. Run your function
|
|
18
|
-
|
|
19
|
-
const testData = JSON.stringify({ key: "test", asdf: "test2" });
|
|
20
|
-
await fs.writeFile(filepath, testData, 'utf-8');
|
|
21
|
-
await jsonSort(filepath);
|
|
22
|
-
const sorted = await fs.readFile(filepath, { encoding: 'utf-8' });
|
|
23
|
-
expect(sorted.indexOf("asdf") > sorted.indexOf("key"));
|
|
24
|
-
|
|
25
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { saveConfig } from '../src/config'; // Assume you exported this
|
|
5
|
+
import { jsonSort } from '../src/sorting';
|
|
6
|
+
|
|
7
|
+
describe('Sorting', () => {
|
|
8
|
+
const filepath = "./test.json";
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
vi.clearAllMocks(); // Reset counters before each test
|
|
12
|
+
});
|
|
13
|
+
afterAll(async () => {
|
|
14
|
+
await fs.rm(filepath);
|
|
15
|
+
})
|
|
16
|
+
it('should write sort JSON keys', async () => {
|
|
17
|
+
// 2. Run your function
|
|
18
|
+
|
|
19
|
+
const testData = JSON.stringify({ key: "test", asdf: "test2" });
|
|
20
|
+
await fs.writeFile(filepath, testData, 'utf-8');
|
|
21
|
+
await jsonSort(filepath);
|
|
22
|
+
const sorted = await fs.readFile(filepath, { encoding: 'utf-8' });
|
|
23
|
+
expect(sorted.indexOf("asdf") > sorted.indexOf("key"));
|
|
24
|
+
|
|
25
|
+
});
|
|
26
26
|
});
|