@jaeymo/toybox 1.0.0 → 1.0.1

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/README.md CHANGED
@@ -1 +1,33 @@
1
- # toybox
1
+ # Toybox
2
+ Toybox scans a Roblox asset hierarchy and produces a Rojo mapping so all of your assets are statically represented
3
+
4
+ ### Usage
5
+ Run the CLI to generate a Rojo mapping from Roblox `.rbxlx` file:
6
+ ```console
7
+ node dist/cli.js generate -i ./toybox-test.rbxlx -o ./default.project.json -f Assets
8
+ ```
9
+ - `-i` / --input – Path to the `.rbxlx` file.
10
+ - `-o` / --output – Path to your `.project.json` file.
11
+ - `-f` / --folder – Name of the folder in ReplicatedStorage to map (default: Assets).
12
+
13
+ ### Example
14
+ If your ReplicatedStorage contains:
15
+ ```JSON
16
+ ReplicatedStorage
17
+ └── Assets
18
+ ├── Models
19
+ └── SomeModel
20
+ ```
21
+
22
+ Running the command would produce:
23
+ ```JSON
24
+ "Asset": {
25
+ "$className": "Folder",
26
+ "Models": {
27
+ "$className": "Folder",
28
+ "SomeModel": {
29
+ "$className": "Model",
30
+ }
31
+ },
32
+ }
33
+ ```
@@ -1,34 +1,34 @@
1
- {
2
- "name": "sometest",
3
- "tree": {
4
- "$className": "DataModel",
5
- "ReplicatedStorage": {
6
- "FilesHere": {
7
- "$className": "Folder"
8
- },
9
- "Asset": {
10
- "$className": "Folder",
11
- "Models": {
12
- "$className": "Folder",
13
- "AnotherModel": {
14
- "$className": "Model",
15
- "SomePartName": {
16
- "$className": "Part"
17
- },
18
- "PrettyPrincess": {
19
- "$className": "Model"
20
- }
21
- }
22
- },
23
- "SomeModelName": {
24
- "$className": "Model"
25
- }
26
- }
27
- },
28
- "ServerScriptService": {
29
- "files": {
30
- "$className": "Folder"
31
- }
32
- }
33
- }
1
+ {
2
+ "name": "sometest",
3
+ "tree": {
4
+ "$className": "DataModel",
5
+ "ReplicatedStorage": {
6
+ "FilesHere": {
7
+ "$className": "Folder"
8
+ },
9
+ "Asset": {
10
+ "$className": "Folder",
11
+ "Models": {
12
+ "$className": "Folder",
13
+ "AnotherModel": {
14
+ "$className": "Model",
15
+ "SomePartName": {
16
+ "$className": "Part"
17
+ },
18
+ "PrettyPrincess": {
19
+ "$className": "Model"
20
+ }
21
+ }
22
+ },
23
+ "SomeModelName": {
24
+ "$className": "Model"
25
+ }
26
+ }
27
+ },
28
+ "ServerScriptService": {
29
+ "files": {
30
+ "$className": "Folder"
31
+ }
32
+ }
33
+ }
34
34
  }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,17 @@
1
+ import { Command } from "commander";
2
+ import { generateMapping } from "./generator.js";
3
+ const program = new Command();
4
+ program
5
+ .name("toybox")
6
+ .description("Generate rojo mappings from a Roblox directory")
7
+ .version("1.0.0");
8
+ program
9
+ .command("generate")
10
+ .requiredOption("-i, --input <path>", "Input roblox directory")
11
+ .option("-f, --folder <name>", "Folder inside Roblox to map", "Asset")
12
+ .requiredOption("-o, --output <path>", "Path to .project.json")
13
+ .action((options) => {
14
+ generateMapping(options.input, options.output, options.folder);
15
+ });
16
+ program.parse(process.argv);
17
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,gDAAgD,CAAC;KAC7D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,OAAO;KACF,OAAO,CAAC,UAAU,CAAC;KACnB,cAAc,CAAC,oBAAoB,EAAE,wBAAwB,CAAC;KAC9D,MAAM,CAAC,qBAAqB,EAAE,6BAA6B,EAAE,OAAO,CAAC;KACrE,cAAc,CAAC,qBAAqB,EAAE,uBAAuB,CAAC;KAC9D,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;IAChB,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEP,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function buildTreeFromRbxlx(filePath: string, rootName: string): Record<string, any>;
2
+ export declare function generateMapping(input: string, rojoPath: string, folderName: string): void;
3
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AA0EA,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAgBrB;AAED,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,QAwBrB"}
@@ -0,0 +1,97 @@
1
+ import fs from "fs";
2
+ import { XMLParser } from "fast-xml-parser";
3
+ function getItemName(item) {
4
+ if (!item.Properties || !item.Properties.string)
5
+ return null;
6
+ const strings = Array.isArray(item.Properties.string)
7
+ ? item.Properties.string
8
+ : [item.Properties.string];
9
+ for (const str of strings) {
10
+ if (str.name === "Name") {
11
+ if (typeof str === "string")
12
+ return str;
13
+ if ("#text" in str)
14
+ return str["#text"];
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+ function findFolderByName(item, name) {
20
+ const itemName = getItemName(item);
21
+ if (itemName === name)
22
+ return item;
23
+ const children = item.Item
24
+ ? Array.isArray(item.Item)
25
+ ? item.Item
26
+ : [item.Item]
27
+ : [];
28
+ for (const child of children) {
29
+ const found = findFolderByName(child, name);
30
+ if (found)
31
+ return found;
32
+ }
33
+ return null;
34
+ }
35
+ function traverse(item) {
36
+ const name = getItemName(item) || "Unnamed";
37
+ const node = {
38
+ name,
39
+ className: item.class,
40
+ children: {},
41
+ };
42
+ const children = item.Item
43
+ ? Array.isArray(item.Item)
44
+ ? item.Item
45
+ : [item.Item]
46
+ : [];
47
+ for (const child of children) {
48
+ const childNode = traverse(child);
49
+ node.children[childNode.name] = childNode;
50
+ }
51
+ return node;
52
+ }
53
+ function toRojo(node) {
54
+ const result = { $className: node.className };
55
+ if (node.children && Object.keys(node.children).length > 0) {
56
+ for (const [name, child] of Object.entries(node.children)) {
57
+ result[name] = toRojo(child);
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ export function buildTreeFromRbxlx(filePath, rootName) {
63
+ const xmlData = fs.readFileSync(filePath, "utf-8");
64
+ const parser = new XMLParser({
65
+ ignoreAttributes: false,
66
+ attributeNamePrefix: "",
67
+ });
68
+ const jsonObj = parser.parse(xmlData);
69
+ const items = Array.isArray(jsonObj.roblox.Item)
70
+ ? jsonObj.roblox.Item
71
+ : [jsonObj.roblox.Item];
72
+ const rootItem = findFolderByName({ Item: items }, rootName);
73
+ if (!rootItem)
74
+ throw new Error(`Folder '${rootName}' not found in .rbxlx`);
75
+ const rootNode = traverse(rootItem);
76
+ return { [rootNode.name]: toRojo(rootNode) };
77
+ }
78
+ export function generateMapping(input, rojoPath, folderName) {
79
+ if (!fs.existsSync(input)) {
80
+ console.error("Input file does not exist");
81
+ process.exit(1);
82
+ }
83
+ if (!fs.existsSync(rojoPath)) {
84
+ console.error("Rojo project file does not exist");
85
+ process.exit(1);
86
+ }
87
+ const project = JSON.parse(fs.readFileSync(rojoPath, "utf-8"));
88
+ if (!project.tree || !project.tree.ReplicatedStorage) {
89
+ console.error("Invalid Rojo project structure (is there a ReplicatedStorage?)");
90
+ process.exit(1);
91
+ }
92
+ const mapping = buildTreeFromRbxlx(input, folderName);
93
+ project.tree.ReplicatedStorage[folderName] = mapping[folderName];
94
+ fs.writeFileSync(rojoPath, JSON.stringify(project, null, 4));
95
+ console.log(`Mapping generated for '${folderName}'`);
96
+ }
97
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,39 +1,42 @@
1
- {
2
- "name": "@jaeymo/toybox",
3
- "version": "1.0.0",
4
- "publishConfig": {
5
- "access": "public"
6
- },
7
- "description": "Toybox scans a Roblox hierarchy and produces a Rojo mapping so all of your assets are statically represented",
8
- "homepage": "https://github.com/jaeymo/toybox#readme",
9
- "bugs": {
10
- "url": "https://github.com/jaeymo/toybox/issues"
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/jaeymo/toybox.git"
15
- },
16
- "license": "MIT",
17
- "author": "Jaeymo",
18
- "type": "module",
19
- "main": "index.js",
20
- "scripts": {
21
- "build": "tsc",
22
- "dev": "ts-node src/cli.ts",
23
- "clean": "rimraf dist"
24
- },
25
- "dependencies": {
26
- "commander": "^14.0.3",
27
- "fast-xml-parser": "^5.3.6",
28
- "ts-node": "^10.9.2",
29
- "typescript": "^5.9.3"
30
- },
31
- "devDependencies": {
32
- "@eslint/js": "^10.0.1",
33
- "@types/node": "^25.2.3",
34
- "eslint": "^10.0.0",
35
- "globals": "^17.3.0",
36
- "rimraf": "^6.1.3",
37
- "typescript-eslint": "^8.56.0"
38
- }
39
- }
1
+ {
2
+ "name": "@jaeymo/toybox",
3
+ "version": "1.0.1",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Toybox scans a Roblox hierarchy and produces a Rojo mapping so all of your assets are statically represented",
8
+ "homepage": "https://github.com/jaeymo/toybox#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/jaeymo/toybox/issues"
11
+ },
12
+ "bin": {
13
+ "toybox": "dist/cli.js"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/jaeymo/toybox.git"
18
+ },
19
+ "license": "MIT",
20
+ "author": "Jaeymo",
21
+ "type": "module",
22
+ "main": "index.js",
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "ts-node src/cli.ts",
26
+ "clean": "rimraf dist"
27
+ },
28
+ "dependencies": {
29
+ "commander": "^14.0.3",
30
+ "fast-xml-parser": "^5.3.6",
31
+ "ts-node": "^10.9.2",
32
+ "typescript": "^5.9.3"
33
+ },
34
+ "devDependencies": {
35
+ "@eslint/js": "^10.0.1",
36
+ "@types/node": "^25.2.3",
37
+ "eslint": "^10.0.0",
38
+ "globals": "^17.3.0",
39
+ "rimraf": "^6.1.3",
40
+ "typescript-eslint": "^8.56.0"
41
+ }
42
+ }