polybundle 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.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # PolyBundle
2
+
3
+ Simple (heavily clanker-made) Lua script bundler for Polytoria.
4
+
5
+ ## Installation
6
+
7
+ install from this repo:
8
+
9
+ ```
10
+ npm install
11
+ npm link
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ Initialize a project (creates init_scripts.json and a dev sample):
17
+
18
+ ```
19
+ polybundle init
20
+ ```
21
+
22
+ Bundle using the current folder's init_scripts.json:
23
+
24
+ ```
25
+ polybundle
26
+ ```
27
+
28
+ Specify an output file:
29
+
30
+ ```
31
+ polybundle --out dist/bundle.lua
32
+ ```
33
+
34
+ ## init_scripts.json format
35
+
36
+ An array of entry script paths (order matters):
37
+
38
+ ```json
39
+ [
40
+ "./dev/init1.lua",
41
+ "./dev/init2.lua"
42
+ ]
43
+ ```
44
+
45
+ ## Example Output:
46
+ ```lua
47
+ local __module_env = {}
48
+
49
+ -- polybundle: module scripts/lib/library1.module.lua
50
+ __module_env["scripts/lib/library1.module.lua"] = (function()
51
+ local returnedLib = {}
52
+
53
+ returnedLib.Add = function(a, b)
54
+ return a + b
55
+ end
56
+
57
+ return returnedLib
58
+ end)()
59
+
60
+ -- polybundle: begin scripts\init1.lua
61
+ coroutine.wrap(function()
62
+ local lib = __module_env["scripts/lib/library1.module.lua"]
63
+
64
+ local addResult = lib.Add(2, 50)
65
+
66
+ print(addResult)
67
+ print("added successfully in init1")
68
+ end)()
69
+ -- polybundle: end scripts\init1.lua
70
+
71
+ -- polybundle: begin scripts\init2.lua
72
+ coroutine.wrap(function()
73
+ local addLib = __module_env["scripts/lib/library1.module.lua"]
74
+
75
+ local addResult = addLib.Add(9, 10)
76
+
77
+ local res = "add result is "
78
+
79
+ local final = res .. tostring(addResult)
80
+
81
+ print(final)
82
+ end)()
83
+ -- polybundle: end scripts\init2.lua
84
+ ```
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { run } = require("../src/cli");
4
+
5
+ run(process.argv.slice(2));
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "polybundle",
3
+ "version": "0.1.0",
4
+ "description": "Lua script bundler for Polytoria.",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "polybundle": "./bin/polybundle.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "src/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "type": "commonjs",
16
+ "engines": {
17
+ "node": ">=18"
18
+ }
19
+ }
package/src/bundler.js ADDED
@@ -0,0 +1,154 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const requireRegex = /require\s*\(\s*(['"])(.*?)\1\s*\)/g;
5
+
6
+ function normalizeLuaSource(source) {
7
+ return source.endsWith("\n") ? source : source + "\n";
8
+ }
9
+
10
+ function isModuleFile(filePath) {
11
+ return filePath.endsWith(".module.lua");
12
+ }
13
+
14
+ function toLuaKey(filePath, rootDir) {
15
+ const relative = path.relative(rootDir, filePath);
16
+ return relative.split(path.sep).join("/");
17
+ }
18
+
19
+ function resolveModulePath(request, fromDir, rootDir) {
20
+ const hasExtension = request.endsWith(".lua") || request.endsWith(".module.lua");
21
+ const withExtension = hasExtension ? request : `${request}.module.lua`;
22
+
23
+ let candidate;
24
+ if (path.isAbsolute(withExtension)) {
25
+ candidate = withExtension;
26
+ } else if (withExtension.startsWith("./") || withExtension.startsWith("../")) {
27
+ candidate = path.resolve(fromDir, withExtension);
28
+ } else if (withExtension.startsWith("/")) {
29
+ candidate = path.resolve(rootDir, withExtension.slice(1));
30
+ } else {
31
+ candidate = path.resolve(rootDir, withExtension);
32
+ }
33
+
34
+ if (fs.existsSync(candidate)) {
35
+ return candidate;
36
+ }
37
+
38
+ if (!hasExtension) {
39
+ const fallback = candidate.replace(/\.module\.lua$/, ".lua");
40
+ if (fs.existsSync(fallback)) {
41
+ return fallback;
42
+ }
43
+ }
44
+
45
+ throw new Error(`Module not found for require("${request}")`);
46
+ }
47
+
48
+ function collectModulesFromSource(source, filePath, rootDir, moduleList, moduleSet, stack) {
49
+ return source.replace(requireRegex, (match, quote, request) => {
50
+ const modulePath = resolveModulePath(request, path.dirname(filePath), rootDir);
51
+ if (stack.includes(modulePath)) {
52
+ const cycle = [...stack, modulePath]
53
+ .map((entry) => path.relative(rootDir, entry))
54
+ .join(" -> ");
55
+ throw new Error(`Circular require detected: ${cycle}`);
56
+ }
57
+
58
+ if (!moduleSet.has(modulePath)) {
59
+ moduleSet.add(modulePath);
60
+ moduleList.push(modulePath);
61
+ const moduleSource = fs.readFileSync(modulePath, "utf8");
62
+ collectModulesFromSource(moduleSource, modulePath, rootDir, moduleList, moduleSet, stack.concat(modulePath));
63
+ }
64
+
65
+ return match;
66
+ });
67
+ }
68
+
69
+ function replaceRequiresWithModuleRefs(source, filePath, rootDir, moduleKeyMap) {
70
+ return source.replace(requireRegex, (match, quote, request) => {
71
+ const modulePath = resolveModulePath(request, path.dirname(filePath), rootDir);
72
+ const moduleKey = moduleKeyMap.get(modulePath);
73
+ if (!moduleKey) {
74
+ throw new Error(`Missing module entry for require(\"${request}\")`);
75
+ }
76
+ return `__module_env[\"${moduleKey}\"]`;
77
+ });
78
+ }
79
+
80
+ function bundleModuleSource(source, filePath, rootDir, moduleKeyMap, cache, stack) {
81
+ if (cache.has(filePath)) {
82
+ return cache.get(filePath);
83
+ }
84
+
85
+ if (stack.includes(filePath)) {
86
+ const cycle = [...stack, filePath].map((entry) => path.relative(rootDir, entry)).join(" -> ");
87
+ throw new Error(`Circular require detected: ${cycle}`);
88
+ }
89
+
90
+ const nextStack = stack.concat(filePath);
91
+ const replaced = replaceRequiresWithModuleRefs(source, filePath, rootDir, moduleKeyMap);
92
+ cache.set(filePath, replaced);
93
+ return replaced;
94
+ }
95
+
96
+ function bundleEntries(entries, rootDir) {
97
+ const moduleList = [];
98
+ const moduleSet = new Set();
99
+
100
+ for (const entry of entries) {
101
+ const entryPath = path.resolve(rootDir, entry);
102
+ if (isModuleFile(entryPath)) {
103
+ if (!moduleSet.has(entryPath)) {
104
+ moduleSet.add(entryPath);
105
+ moduleList.push(entryPath);
106
+ const moduleSource = fs.readFileSync(entryPath, "utf8");
107
+ collectModulesFromSource(moduleSource, entryPath, rootDir, moduleList, moduleSet, [entryPath]);
108
+ }
109
+ continue;
110
+ }
111
+
112
+ const source = fs.readFileSync(entryPath, "utf8");
113
+ collectModulesFromSource(source, entryPath, rootDir, moduleList, moduleSet, [entryPath]);
114
+ }
115
+
116
+ const moduleKeyMap = new Map();
117
+ for (const modulePath of moduleList) {
118
+ moduleKeyMap.set(modulePath, toLuaKey(modulePath, rootDir));
119
+ }
120
+
121
+ const moduleCache = new Map();
122
+ const chunks = [];
123
+
124
+ chunks.push("local __module_env = {}\n");
125
+
126
+ for (const modulePath of moduleList) {
127
+ const moduleSource = fs.readFileSync(modulePath, "utf8");
128
+ const bundledModule = bundleModuleSource(moduleSource, modulePath, rootDir, moduleKeyMap, moduleCache, []);
129
+ const moduleKey = moduleKeyMap.get(modulePath);
130
+ const header = `-- polybundle: module ${moduleKey}\n`;
131
+ const assignment = `__module_env[\"${moduleKey}\"] = (function()\n${normalizeLuaSource(bundledModule)}end)()\n`;
132
+ chunks.push(header + assignment);
133
+ }
134
+
135
+ for (const entry of entries) {
136
+ const entryPath = path.resolve(rootDir, entry);
137
+ if (isModuleFile(entryPath)) {
138
+ continue;
139
+ }
140
+
141
+ const entrySource = fs.readFileSync(entryPath, "utf8");
142
+ const replaced = replaceRequiresWithModuleRefs(entrySource, entryPath, rootDir, moduleKeyMap);
143
+ const wrapped = `coroutine.wrap(function()\n${normalizeLuaSource(replaced)}end)()`;
144
+ const header = `-- polybundle: begin ${path.relative(rootDir, entryPath)}\n`;
145
+ const footer = `-- polybundle: end ${path.relative(rootDir, entryPath)}\n`;
146
+ chunks.push(header + normalizeLuaSource(wrapped) + footer);
147
+ }
148
+
149
+ return chunks.join("\n");
150
+ }
151
+
152
+ module.exports = {
153
+ bundleEntries
154
+ };
package/src/cli.js ADDED
@@ -0,0 +1,138 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { bundleEntries } = require("./bundler");
4
+
5
+ function printHelp() {
6
+ console.log("Usage: polybundle [init] [--out <file>]");
7
+ }
8
+
9
+ function initScriptTree(scriptTreePath) {
10
+ const example = [
11
+ "./dev/init1.lua",
12
+ "./dev/init2.lua"
13
+ ];
14
+
15
+ if (!fs.existsSync(scriptTreePath)) {
16
+ fs.writeFileSync(scriptTreePath, JSON.stringify(example, null, 2) + "\n", "utf8");
17
+ console.log("Created init_scripts.json with example entries.");
18
+ } else {
19
+ console.log("init_scripts.json already exists.");
20
+ }
21
+
22
+ const devDir = path.resolve(path.dirname(scriptTreePath), "dev");
23
+ const libDir = path.join(devDir, "lib");
24
+
25
+ fs.mkdirSync(libDir, { recursive: true });
26
+
27
+ const init1Path = path.join(devDir, "init1.lua");
28
+ const init2Path = path.join(devDir, "init2.lua");
29
+ const libraryPath = path.join(libDir, "library1.module.lua");
30
+
31
+ const init1Content = "local lib = require(\"./lib/library1.module.lua\")\n\n" +
32
+ "local addResult = lib.Add(2, 50)\n\n" +
33
+ "print(addResult)\n" +
34
+ "print(\"added successfully in init1\")\n";
35
+
36
+ const init2Content = "local addLib = require(\"./lib/library1.module.lua\")\n\n" +
37
+ "local addResult = addLib.Add(9, 10)\n\n" +
38
+ "local res = \"add result is \"\n\n" +
39
+ "local final = res .. tostring(addResult)\n\n" +
40
+ "print(final)\n";
41
+
42
+ const libraryContent = "local returnedLib = {}\n\n" +
43
+ "returnedLib.Add = function(a, b)\n" +
44
+ " return a + b\n" +
45
+ "end\n\n" +
46
+ "return returnedLib\n";
47
+
48
+ if (!fs.existsSync(init1Path)) {
49
+ fs.writeFileSync(init1Path, init1Content, "utf8");
50
+ }
51
+
52
+ if (!fs.existsSync(init2Path)) {
53
+ fs.writeFileSync(init2Path, init2Content, "utf8");
54
+ }
55
+
56
+ if (!fs.existsSync(libraryPath)) {
57
+ fs.writeFileSync(libraryPath, libraryContent, "utf8");
58
+ }
59
+ }
60
+
61
+ function parseArgs(args) {
62
+ const result = {
63
+ command: null,
64
+ outFile: null,
65
+ help: false
66
+ };
67
+
68
+ for (let i = 0; i < args.length; i += 1) {
69
+ const arg = args[i];
70
+ if (arg === "--help" || arg === "-h") {
71
+ result.help = true;
72
+ } else if (arg === "--out") {
73
+ result.outFile = args[i + 1] || null;
74
+ i += 1;
75
+ } else if (!result.command) {
76
+ result.command = arg;
77
+ }
78
+ }
79
+
80
+ return result;
81
+ }
82
+
83
+ function run(args) {
84
+ const options = parseArgs(args);
85
+
86
+ if (options.help) {
87
+ printHelp();
88
+ return;
89
+ }
90
+
91
+ const cwd = process.cwd();
92
+ const scriptTreePath = path.resolve(cwd, "init_scripts.json");
93
+
94
+ if (options.command === "init") {
95
+ initScriptTree(scriptTreePath);
96
+ return;
97
+ }
98
+
99
+ if (!fs.existsSync(scriptTreePath)) {
100
+ console.log("init_scripts.json not found. Run `polybundle init` first.");
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+
105
+ let entries;
106
+ try {
107
+ const raw = fs.readFileSync(scriptTreePath, "utf8");
108
+ entries = JSON.parse(raw);
109
+ } catch (error) {
110
+ console.error("Failed to read init_scripts.json:", error.message);
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+
115
+ if (!Array.isArray(entries) || !entries.every((value) => typeof value === "string")) {
116
+ console.error("init_scripts.json must be an array of string paths.");
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+
121
+ const outFile = options.outFile
122
+ ? path.resolve(cwd, options.outFile)
123
+ : path.resolve(cwd, "dist", "bundle.lua");
124
+
125
+ try {
126
+ const bundled = bundleEntries(entries, cwd);
127
+ fs.mkdirSync(path.dirname(outFile), { recursive: true });
128
+ fs.writeFileSync(outFile, bundled, "utf8");
129
+ console.log(`Bundled ${entries.length} entries into ${outFile}`);
130
+ } catch (error) {
131
+ console.error("Bundling failed:", error.message);
132
+ process.exitCode = 1;
133
+ }
134
+ }
135
+
136
+ module.exports = {
137
+ run
138
+ };