quickjs-zig 1.0.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/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "quickjs"]
2
+ path = quickjs
3
+ url = https://github.com/bellard/quickjs
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # QuickJS-Zig ⚡
2
+
3
+ A high-performance build system for **QuickJS**, powered by the **Zig** compiler. This project allows you to compile JavaScript applications into standalone native binaries for Windows, Linux, and macOS with zero system dependencies.
4
+
5
+ ## Features
6
+
7
+ * **Zero Config Cross-Compilation**: Build for Windows, Linux, and macOS from any host (Intel or Apple Silicon).
8
+ * **Zig-Powered**: Uses **Zig 0.15.2** as a C compiler for modern, safe, and highly optimized binaries.
9
+ * **Custom C Modules**: Easily inject and register your own C modules into the QuickJS engine.
10
+ * **Native Windows Support**: Includes a custom `exec` implementation for Windows, bypassing typical QuickJS POSIX limitations.
11
+ * **Clean Source Management**: Automatically patches and restores QuickJS source files to keep the core library pristine.
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ Add `quickjs-zig` as a development dependency in your project:
18
+
19
+ ```bash
20
+ npm install --save-dev quickjs-zig
21
+
22
+ ```
23
+
24
+ > **Note**: During installation, the `postinstall` script will automatically download the appropriate Zig 0.15.2 binary for your platform and initialize the QuickJS submodules.
25
+
26
+ ### Development / Local setup
27
+
28
+ If you want to contribute or test the latest version from source:
29
+
30
+ ```bash
31
+ git clone https://github.com/eid-app/quickjs-zig.git
32
+ cd quickjs-zig
33
+ npm install
34
+ npm link
35
+
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Configuration
41
+
42
+ Configure your entry point and custom C modules in your project's `package.json`:
43
+
44
+ ```json
45
+ {
46
+ "name": "my-app",
47
+ "quickJs": {
48
+ "input": "app/index.mjs",
49
+ "modules": {
50
+ "my_module": "src/my_module.c"
51
+ }
52
+ }
53
+ }
54
+
55
+ ```
56
+
57
+ * **`input`**: The entry point of your JavaScript application (defaults to `app/index.mjs`).
58
+ * **`modules`**: A key-value map of custom C modules (Module Name -> C Source Path).
59
+
60
+ ---
61
+
62
+ ## Example Usage
63
+
64
+ ### 1. Create a C Module (`src/my_module.c`)
65
+
66
+ This module will be automatically compiled and linked into your final binary.
67
+
68
+ ```c
69
+ #include "quickjs-libc.h"
70
+
71
+ // A simple native function
72
+ static JSValue js_hello(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
73
+ return JS_NewString(ctx, "Hello from the native side!");
74
+ }
75
+
76
+ // Module initialization
77
+ JSModuleDef *js_init_module_my_module(JSContext *ctx, const char *module_name) {
78
+ JSModuleDef *m = JS_NewCModule(ctx, module_name, NULL);
79
+ if (!m) return NULL;
80
+ JS_AddModuleExport(ctx, m, "hello");
81
+ JS_SetModuleExport(ctx, m, "hello", JS_NewCFunction(ctx, js_hello, "hello", 0));
82
+ return m;
83
+ }
84
+
85
+ ```
86
+
87
+ ### 2. Create your JavaScript (`app/index.mjs`)
88
+
89
+ Import your custom module just like any other ES module.
90
+
91
+ ```javascript
92
+ import { hello } from 'my_module';
93
+ import * as os from 'os';
94
+
95
+ // Use the native function
96
+ const message = hello();
97
+ console.log(message);
98
+
99
+ // Standard QuickJS modules are also fully supported
100
+ console.log(`Platform: ${os.platform}`);
101
+
102
+ ```
103
+
104
+ ### 3. Build your binaries
105
+
106
+ Run the build command to generate tools and standalone binaries for all targets:
107
+
108
+ ```bash
109
+ npx quickjs-zig build
110
+
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Supported Targets
116
+
117
+ The build system generates binaries for the following platforms in the `dist/` folder:
118
+
119
+ | Platform | Architecture | Target ID | Binary Name |
120
+ | --- | --- | --- | --- |
121
+ | **Windows** | x64 / x86 | `x86_64-windows-gnu` / `x86-windows-gnu` | `app_win64.exe` / `app_win32.exe` |
122
+ | **Linux** | x64 / x86 / ARM64 | `x86_64-linux-gnu` / `x86-linux-gnu` / `aarch64-linux-gnu` | `app_linux64` / `app_linux32` / `app_linux_arm64` |
123
+ | **macOS** | Apple / Intel | `aarch64-macos` / `x86_64-macos` | `app_mac_arm` / `app_mac_intel` |
124
+
125
+ ---
126
+
127
+ ## Technical Details
128
+
129
+ ### Why Zig?
130
+
131
+ Zig is not just a language; it's a powerful C/C++ toolchain. It allows `quickjs-zig` to:
132
+
133
+ * Cross-compile to Windows (MinGW) from macOS/Linux without installing complex toolchains.
134
+ * Provide a consistent `libc` environment across different platforms.
135
+ * Produce small, fast, and statically linked binaries.
136
+
137
+ ### Windows Patching
138
+
139
+ QuickJS is designed for POSIX systems. This tool automatically patches `quickjs-libc.c` during the build process to provide a working `os.exec` on Windows using a native `_spawnvp` implementation.
140
+
141
+ ---
142
+
143
+ ## License
144
+
145
+ MIT - Created by **eid-app**.
146
+
147
+ ---
package/build.mjs ADDED
@@ -0,0 +1,175 @@
1
+ import { execSync } from 'child_process';
2
+ import { writeFileSync, mkdirSync, unlinkSync, existsSync, readdirSync, cpSync, readFileSync, renameSync } from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ // --- PATH CONFIGURATION ---
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const USER_CWD = process.cwd();
10
+
11
+ // Load the user project's package.json
12
+ const USER_PKG_PATH = path.join(USER_CWD, 'package.json');
13
+ if (!existsSync(USER_PKG_PATH)) {
14
+ console.error("❌ Error: package.json not found in current directory.");
15
+ process.exit(1);
16
+ }
17
+
18
+ const userPackageJson = JSON.parse(readFileSync(USER_PKG_PATH, 'utf8'));
19
+ const customModules = userPackageJson.quickJs?.modules || {};
20
+ const APP_NAME = userPackageJson.name || 'app';
21
+
22
+ // Input file from package.json or default to app/index.mjs
23
+ const INPUT_FILE_RELATIVE = userPackageJson.quickJs?.input || 'app/index.mjs';
24
+ const INPUT_FILE_ABS = path.resolve(USER_CWD, INPUT_FILE_RELATIVE);
25
+
26
+ if (!existsSync(INPUT_FILE_ABS)) {
27
+ console.error(`❌ Error: Input file not found at ${INPUT_FILE_ABS}`);
28
+ process.exit(1);
29
+ }
30
+
31
+ const QUICKJS_DIR = path.resolve(__dirname, 'quickjs');
32
+ const ZIG_PATH = path.resolve(__dirname, 'bin', 'zig', os.platform() === 'win32' ? 'zig.exe' : 'zig');
33
+ const VERSION = '2024-01-13';
34
+ const BIN_DIR = path.join(USER_CWD, 'bin');
35
+ const DIST_DIR = path.join(USER_CWD, 'dist');
36
+
37
+ const LIBC_PATH = path.join(QUICKJS_DIR, 'quickjs-libc.c');
38
+ const LIBC_BAK = path.join(QUICKJS_DIR, 'quickjs-libc.c.bak');
39
+ const QJS_C_PATH = path.join(QUICKJS_DIR, 'qjs.c');
40
+ const QJS_C_BAK = path.join(QUICKJS_DIR, 'qjs.c.bak');
41
+ const QJSC_C_PATH = path.join(QUICKJS_DIR, 'qjsc.c');
42
+ const QJSC_C_BAK = path.join(QUICKJS_DIR, 'qjsc.c.bak');
43
+ const EXEC_SRC_PATH = path.resolve(__dirname, 'src', 'exec.c');
44
+
45
+ if (!existsSync(BIN_DIR)) mkdirSync(BIN_DIR, { recursive: true });
46
+ if (!existsSync(DIST_DIR)) mkdirSync(DIST_DIR, { recursive: true });
47
+
48
+ // Detect the host architecture to find the correct qjsc binary
49
+ const platform = os.platform();
50
+ const arch = os.arch();
51
+ let hostQjscName = '';
52
+
53
+ if (platform === 'darwin') hostQjscName = (arch === 'arm64') ? 'qjsc_mac_arm' : 'qjsc_mac_intel';
54
+ else if (platform === 'linux') hostQjscName = (arch === 'arm64' || arch === 'aarch64') ? 'qjsc_linux_arm' : 'qjsc_linux64';
55
+ else if (platform === 'win32') hostQjscName = 'qjsc_win64.exe';
56
+
57
+ const hostQjscPath = path.join(BIN_DIR, hostQjscName);
58
+
59
+ // ==========================================================
60
+ // STAGE 0: PATCHING
61
+ // ==========================================================
62
+ console.log("=== STAGE 0: PATCHING QUICKJS SOURCES ===");
63
+
64
+ if (existsSync(LIBC_PATH)) {
65
+ if (!existsSync(LIBC_BAK)) cpSync(LIBC_PATH, LIBC_BAK);
66
+ let content = readFileSync(LIBC_PATH, 'utf8');
67
+
68
+ if (!content.includes('#include <process.h>')) {
69
+ content = '#if defined(_WIN32)\n#include <process.h>\n#endif\n' + content;
70
+ }
71
+
72
+ for (const name of Object.keys(customModules)) {
73
+ const decl = `JSModuleDef *js_init_module_${name}(JSContext *ctx, const char *module_name);`;
74
+ if (!content.includes(decl)) content = content.replace('#include "quickjs-libc.h"', `#include "quickjs-libc.h"\n\n${decl}`);
75
+ }
76
+
77
+ if (existsSync(EXEC_SRC_PATH)) {
78
+ const execImpl = readFileSync(EXEC_SRC_PATH, 'utf8');
79
+ if (!content.includes('js_os_exec_win32')) {
80
+ content = content.replace('static JSClassDef js_worker_class = {', execImpl + '\nstatic JSClassDef js_worker_class = {');
81
+ }
82
+ const oldEntry = ' JS_CFUNC_DEF("exec", 1, js_os_exec ),';
83
+ const newEntry = `#if defined(_WIN32)\n JS_CFUNC_DEF("exec", 1, js_os_exec_win32 ),\n#else\n JS_CFUNC_DEF("exec", 1, js_os_exec ),\n#endif`;
84
+ if (content.includes(oldEntry)) content = content.replace(oldEntry, newEntry);
85
+ }
86
+ writeFileSync(LIBC_PATH, content);
87
+ console.log("✅ quickjs-libc.c patched.");
88
+ }
89
+
90
+ [ [QJS_C_PATH, QJS_C_BAK], [QJSC_C_PATH, QJSC_C_BAK] ].forEach(([p, b]) => {
91
+ if (!existsSync(p)) return;
92
+ if (!existsSync(b)) cpSync(p, b);
93
+ let content = readFileSync(p, 'utf8');
94
+ const isQjsc = p.includes('qjsc.c');
95
+
96
+ for (const name of Object.keys(customModules)) {
97
+ if (isQjsc) {
98
+ const entry = `namelist_add(&cmodule_list, "${name}", "${name}", 0);`;
99
+ if (!content.includes(entry)) content = content.replace('namelist_add(&cmodule_list, "os", "os", 0);', `namelist_add(&cmodule_list, "os", "os", 0);\n ${entry}`);
100
+ } else {
101
+ const decl = `JSModuleDef *js_init_module_${name}(JSContext *ctx, const char *module_name);`;
102
+ const call = `js_init_module_${name}(ctx, "${name}");`;
103
+ if (!content.includes(decl)) content = content.replace('#include "quickjs-libc.h"', `#include "quickjs-libc.h"\n\n${decl}`);
104
+ if (!content.includes(call)) content = content.replace('js_init_module_os(ctx, "os");', `js_init_module_os(ctx, "os");\n ${call}`);
105
+ }
106
+ }
107
+ writeFileSync(p, content);
108
+ console.log(`✅ ${path.basename(p)} patched.`);
109
+ });
110
+
111
+ const baseSources = ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'quickjs-libc.c', 'dtoa.c']
112
+ .map(f => path.join(QUICKJS_DIR, f))
113
+ .concat(Object.values(customModules).map(f => path.resolve(USER_CWD, f)))
114
+ .join(' ');
115
+
116
+ const targets = [
117
+ { id: 'x86_64-windows-gnu', qjs: 'qjs_win64.exe', qjsc: 'qjsc_win64.exe', app: `${APP_NAME}_win64.exe`, libs: '-lm', cflags: '-D_GNU_SOURCE' },
118
+ { id: 'x86-windows-gnu', qjs: 'qjs_win32.exe', qjsc: 'qjsc_win32.exe', app: `${APP_NAME}_win32.exe`, libs: '-lm', cflags: '-D_GNU_SOURCE' },
119
+ { id: 'x86_64-linux-gnu', qjs: 'qjs_linux64', qjsc: 'qjsc_linux64', app: `${APP_NAME}_linux64`, libs: '-lm -lpthread -ldl', cflags: '-D_GNU_SOURCE -DCONFIG_PTHREAD' },
120
+ { id: 'x86-linux-gnu', qjs: 'qjs_linux32', qjsc: 'qjsc_linux32', app: `${APP_NAME}_linux32`, libs: '-lm -lpthread -ldl', cflags: '-D_GNU_SOURCE -DCONFIG_PTHREAD' },
121
+ { id: 'aarch64-linux-gnu', qjs: 'qjs_linux_arm64', qjsc: 'qjsc_linux_arm64', app: `${APP_NAME}_linux_arm64`, libs: '-lm -lpthread -ldl', cflags: '-D_GNU_SOURCE -DCONFIG_PTHREAD' },
122
+ { id: 'aarch64-macos', qjs: 'qjs_mac_arm', qjsc: 'qjsc_mac_arm', app: `${APP_NAME}_mac_arm`, libs: '-lm -lpthread -ldl', cflags: '-D_GNU_SOURCE -DCONFIG_PTHREAD' },
123
+ { id: 'x86_64-macos', qjs: 'qjs_mac_intel', qjsc: 'qjsc_mac_intel', app: `${APP_NAME}_mac_intel`, libs: '-lm -lpthread -ldl', cflags: '-D_GNU_SOURCE -DCONFIG_PTHREAD' }
124
+ ];
125
+
126
+ // ==========================================================
127
+ // STAGE 1 & 2: TOOLS AND APPLICATION COMPILATION
128
+ // ==========================================================
129
+ const stubPath = path.join(QUICKJS_DIR, 'repl_stub.c');
130
+ writeFileSync(stubPath, `const unsigned char qjsc_repl[] = {0}; const unsigned int qjsc_repl_size = 0;`);
131
+
132
+ targets.forEach(t => {
133
+ console.log(`\n--- Compiling for: ${t.id} ---`);
134
+ const cmdBase = `${ZIG_PATH} cc -target ${t.id} -I${QUICKJS_DIR} -O2 ${t.cflags} -Wno-ignored-attributes -DCONFIG_VERSION=\\"${VERSION}\\" ${t.libs} -s`;
135
+
136
+ try {
137
+ execSync(`${cmdBase} -o "${path.join(BIN_DIR, t.qjs)}" ${baseSources} ${stubPath} "${path.join(QUICKJS_DIR, 'qjs.c')}"`);
138
+ execSync(`${cmdBase} -o "${path.join(BIN_DIR, t.qjsc)}" ${baseSources} "${path.join(QUICKJS_DIR, 'qjsc.c')}"`);
139
+ console.log(`✅ Build tools generated.`);
140
+
141
+ const tempC = path.join(BIN_DIR, `${t.id}_app.c`);
142
+ const relativeInput = path.relative(USER_CWD, INPUT_FILE_ABS);
143
+
144
+ execSync(`"${hostQjscPath}" -e -o "${tempC}" "${relativeInput}"`, { cwd: USER_CWD });
145
+
146
+ execSync(`${cmdBase} -o "${path.join(DIST_DIR, t.app)}" "${tempC}" ${baseSources} -I${QUICKJS_DIR}`);
147
+ console.log(`✅ Binary built: ${t.app}`);
148
+ } catch (e) {
149
+ console.error(`❌ Compilation failed for ${t.id}`);
150
+ console.error(e.stderr?.toString() || e.message);
151
+ }
152
+ });
153
+
154
+ // ==========================================================
155
+ // STAGE 3: CLEANUP
156
+ // ==========================================================
157
+ console.log("\n=== STAGE 3: CLEANUP ===");
158
+
159
+ // Remove all generated .c files in the bin directory
160
+ const binFiles = readdirSync(BIN_DIR);
161
+ binFiles.forEach(file => {
162
+ if (file.endsWith('_app.c')) {
163
+ unlinkSync(path.join(BIN_DIR, file));
164
+ console.log(`🗑️ Removed temporary source: ${file}`);
165
+ }
166
+ });
167
+
168
+ [ [LIBC_BAK, LIBC_PATH], [QJS_C_BAK, QJS_C_PATH], [QJSC_C_BAK, QJSC_C_PATH] ].forEach(([b, s]) => {
169
+ if (existsSync(b)) {
170
+ renameSync(b, s);
171
+ console.log(`🔄 Restored original: ${path.basename(s)}`);
172
+ }
173
+ });
174
+ if (existsSync(stubPath)) unlinkSync(stubPath);
175
+ console.log("🚀 Build process complete.");
package/index.mjs ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * quickjs-zig CLI Dispatcher
5
+ * This script is the entry point when running 'npx quickjs-zig'
6
+ */
7
+
8
+ import { spawn } from 'child_process';
9
+ import { fileURLToPath } from 'url';
10
+ import path from 'path';
11
+
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+ const args = process.argv.slice(2);
14
+ const command = args[0];
15
+
16
+ async function run() {
17
+ switch (command) {
18
+ case 'build':
19
+ console.log("🛠️ quickjs-zig: Starting build process...");
20
+
21
+ // On appelle build.mjs en lui passant le reste des arguments
22
+ const buildScript = path.join(__dirname, 'build.mjs');
23
+ const child = spawn('node', [buildScript, ...args.slice(1)], {
24
+ stdio: 'inherit',
25
+ shell: true
26
+ });
27
+
28
+ child.on('exit', (code) => {
29
+ if (code === 0) {
30
+ console.log("✅ Build completed successfully.");
31
+ }
32
+ process.exit(code);
33
+ });
34
+ break;
35
+
36
+ case 'help':
37
+ case undefined:
38
+ console.log(`
39
+ quickjs-zig - Native Binary Compiler for QuickJS
40
+
41
+ Usage:
42
+ npx quickjs-zig <command> [options]
43
+
44
+ Commands:
45
+ build Compiles your project into native binaries based on package.json
46
+ help Shows this help message
47
+
48
+ Example:
49
+ npx quickjs-zig build
50
+ `);
51
+ break;
52
+
53
+ default:
54
+ console.log(`❌ Unknown command: "${command}"`);
55
+ console.log("Type 'npx quickjs-zig help' for usage.");
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ run();
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "quickjs-zig",
3
+ "version": "1.0.0",
4
+ "description": "A generic build engine to compile QuickJS scripts into standalone native binaries using Zig for multi-platform cross-compilation.",
5
+ "type": "module",
6
+ "main": "index.mjs",
7
+ "bin": {
8
+ "quickjs-zig": "index.mjs"
9
+ },
10
+ "scripts": {
11
+ "postinstall": "node postinstall.mjs"
12
+ },
13
+ "engines": {
14
+ "node": ">=18.0.0"
15
+ },
16
+ "keywords": [
17
+ "quickjs",
18
+ "zig",
19
+ "compiler",
20
+ "native-binaries",
21
+ "cross-compilation",
22
+ "c-modules"
23
+ ],
24
+ "author": "eid-app",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/eid-app/quickjs-zig.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/eid-app/quickjs-zig/issues"
32
+ },
33
+ "homepage": "https://github.com/eid-app/quickjs-zig#readme"
34
+ }
@@ -0,0 +1,116 @@
1
+ import { execSync } from 'child_process';
2
+ import { mkdirSync, existsSync, writeFileSync, chmodSync, unlinkSync } from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import https from 'https';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ // --- CONFIGURATION ---
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const ZIG_VERSION = '0.15.2';
11
+ const BIN_DIR = path.resolve(__dirname, 'bin', 'zig');
12
+
13
+ // Mapping for Zig download URLs
14
+ const platformMap = {
15
+ win32: 'windows',
16
+ darwin: 'macos',
17
+ linux: 'linux'
18
+ };
19
+
20
+ const archMap = {
21
+ x64: 'x86_64',
22
+ arm64: 'aarch64'
23
+ };
24
+
25
+ const platform = platformMap[os.platform()];
26
+ const arch = archMap[os.arch()];
27
+
28
+ if (!platform || !arch) {
29
+ console.error(`❌ Unsupported platform/arch: ${os.platform()} ${os.arch()}`);
30
+ process.exit(1);
31
+ }
32
+
33
+ // Fixed: Correct Zig URL order is arch-platform (e.g., x86_64-macos)
34
+ const zigTarget = `${arch}-${platform}`;
35
+ const zigFileName = `zig-${zigTarget}-${ZIG_VERSION}.${platform === 'windows' ? 'zip' : 'tar.xz'}`;
36
+ const zigUrl = `https://ziglang.org/download/${ZIG_VERSION}/${zigFileName}`;
37
+
38
+ const ZIG_BIN_PATH = path.join(BIN_DIR, platform === 'windows' ? 'zig.exe' : 'zig');
39
+
40
+ // --- DOWNLOAD HELPER ---
41
+ const downloadFile = (url, dest) => {
42
+ return new Promise((resolve, reject) => {
43
+ https.get(url, (res) => {
44
+ if (res.statusCode === 302 || res.statusCode === 301) {
45
+ return downloadFile(res.headers.location, dest).then(resolve).catch(reject);
46
+ }
47
+ if (res.statusCode !== 200) {
48
+ reject(new Error(`Failed to download: ${res.statusCode}`));
49
+ return;
50
+ }
51
+ const chunks = [];
52
+ res.on('data', chunk => chunks.push(chunk));
53
+ res.on('end', () => {
54
+ writeFileSync(dest, Buffer.concat(chunks));
55
+ resolve();
56
+ });
57
+ }).on('error', reject);
58
+ });
59
+ };
60
+
61
+ // --- MAIN EXECUTION ---
62
+ async function install() {
63
+ console.log("=== POSTINSTALL: INITIALIZING PROJECT DEPENDENCIES ===");
64
+
65
+ // 1. Initialize Git Submodules (QuickJS source)
66
+ try {
67
+ console.log("📂 Initializing git submodules...");
68
+ execSync('git submodule update --init --recursive', { stdio: 'inherit' });
69
+ console.log("✅ Submodules initialized.");
70
+ } catch (err) {
71
+ console.warn("⚠️ Git submodule update failed. Ensure you are in a git repository or have QuickJS sources.");
72
+ }
73
+
74
+ // 2. Install Zig Compiler
75
+ if (existsSync(ZIG_BIN_PATH)) {
76
+ console.log(`✅ Zig ${ZIG_VERSION} already installed.`);
77
+ return;
78
+ }
79
+
80
+ if (!existsSync(BIN_DIR)) mkdirSync(BIN_DIR, { recursive: true });
81
+
82
+ console.log(`📥 Downloading Zig ${ZIG_VERSION} for ${zigTarget}...`);
83
+ const archivePath = path.join(BIN_DIR, zigFileName);
84
+
85
+ try {
86
+ await downloadFile(zigUrl, archivePath);
87
+ console.log(`📦 Extracting ${zigFileName}...`);
88
+
89
+ if (platform === 'windows') {
90
+ // Extraction using PowerShell for Windows
91
+ execSync(`powershell -command "Expand-Archive -Path '${archivePath}' -DestinationPath '${BIN_DIR}' -Force"`);
92
+ // Zig extracts into a subfolder named 'zig-windows-x86_64-0.15.2', let's flatten it
93
+ const subDir = path.join(BIN_DIR, `zig-${zigTarget}-${ZIG_VERSION}`);
94
+ if (existsSync(subDir)) {
95
+ execSync(`powershell -command "Move-Item -Path '${subDir}\\*' -Destination '${BIN_DIR}' -Force"`);
96
+ }
97
+ } else {
98
+ // Extraction for Unix with strip-components to avoid nested folder
99
+ execSync(`tar -xJf "${archivePath}" -C "${BIN_DIR}" --strip-components=1`);
100
+ }
101
+
102
+ // Cleanup
103
+ if (existsSync(archivePath)) unlinkSync(archivePath);
104
+
105
+ if (platform !== 'windows') {
106
+ chmodSync(ZIG_BIN_PATH, 0o755);
107
+ }
108
+
109
+ console.log(`🚀 Zig ${ZIG_VERSION} installed successfully at ${ZIG_BIN_PATH}`);
110
+ } catch (err) {
111
+ console.error(`❌ Installation failed: ${err.message}`);
112
+ process.exit(1);
113
+ }
114
+ }
115
+
116
+ install();
package/src/exec.c ADDED
@@ -0,0 +1,183 @@
1
+ #if defined(_WIN32)
2
+ #include <fcntl.h>
3
+ #include <io.h>
4
+
5
+ /* Helper to build the environment block for Windows */
6
+ static char **build_envp(JSContext *ctx, JSValueConst obj)
7
+ {
8
+ uint32_t len, i;
9
+ JSPropertyEnum *tab;
10
+ char **envp, *pair;
11
+ const char *key, *str;
12
+ JSValue val;
13
+ size_t key_len, str_len;
14
+
15
+ if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj,
16
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0)
17
+ return NULL;
18
+
19
+ envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1));
20
+ if (!envp)
21
+ goto fail;
22
+
23
+ for(i = 0; i < len; i++) {
24
+ val = JS_GetProperty(ctx, obj, tab[i].atom);
25
+ if (JS_IsException(val))
26
+ goto fail;
27
+ str = JS_ToCString(ctx, val);
28
+ JS_FreeValue(ctx, val); // OK: 2 arguments
29
+ if (!str)
30
+ goto fail;
31
+
32
+ key = JS_AtomToCString(ctx, tab[i].atom);
33
+ if (!key) {
34
+ JS_FreeCString(ctx, str);
35
+ goto fail;
36
+ }
37
+
38
+ key_len = strlen(key);
39
+ str_len = strlen(str);
40
+ pair = js_malloc(ctx, key_len + str_len + 2);
41
+ if (!pair) {
42
+ JS_FreeCString(ctx, key);
43
+ JS_FreeCString(ctx, str);
44
+ goto fail;
45
+ }
46
+
47
+ memcpy(pair, key, key_len);
48
+ pair[key_len] = '=';
49
+ memcpy(pair + key_len + 1, str, str_len);
50
+ pair[key_len + 1 + str_len] = '\0';
51
+ envp[i] = pair;
52
+
53
+ JS_FreeCString(ctx, key);
54
+ JS_FreeCString(ctx, str);
55
+ }
56
+ envp[len] = NULL;
57
+
58
+ done:
59
+ JS_FreePropertyEnum(ctx, tab, len);
60
+ return envp;
61
+ fail:
62
+ if (envp) {
63
+ for(i = 0; i < len; i++) {
64
+ if (envp[i]) js_free(ctx, envp[i]);
65
+ }
66
+ js_free(ctx, envp);
67
+ envp = NULL;
68
+ }
69
+ goto done;
70
+ }
71
+
72
+ /* Main exec implementation for Windows */
73
+ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
74
+ int argc, JSValueConst *argv)
75
+ {
76
+ JSValueConst options, args = argv[0];
77
+ JSValue val, ret_val = JS_UNDEFINED;
78
+ const char **exec_argv = NULL;
79
+ char **envp = NULL;
80
+ const char *file = NULL;
81
+ uint32_t exec_argc = 0, i;
82
+ int ret, mode;
83
+ BOOL block_flag = TRUE, use_path = TRUE;
84
+
85
+ int custom_stdout_fd = -1;
86
+ int old_stdout = -1;
87
+
88
+ // 1. Arguments length
89
+ val = JS_GetPropertyStr(ctx, args, "length");
90
+ if (JS_IsException(val)) return JS_EXCEPTION;
91
+ ret = JS_ToUint32(ctx, &exec_argc, val);
92
+ JS_FreeValue(ctx, val); // OK: 2 arguments
93
+ if (ret < 0) return JS_EXCEPTION;
94
+
95
+ exec_argv = js_mallocz(ctx, sizeof(char *) * (exec_argc + 1));
96
+ if (!exec_argv) return JS_EXCEPTION;
97
+
98
+ for(i = 0; i < exec_argc; i++) {
99
+ val = JS_GetPropertyUint32(ctx, args, i);
100
+ if (JS_IsException(val)) goto exception;
101
+ exec_argv[i] = JS_ToCString(ctx, val);
102
+ JS_FreeValue(ctx, val); // OK: 2 arguments
103
+ if (!exec_argv[i]) goto exception;
104
+ }
105
+ exec_argv[exec_argc] = NULL;
106
+
107
+ // 2. Options & stdout fileno
108
+ if (argc >= 2) {
109
+ options = argv[1];
110
+ get_bool_option(ctx, &block_flag, options, "block");
111
+ get_bool_option(ctx, &use_path, options, "usePath");
112
+
113
+ val = JS_GetPropertyStr(ctx, options, "stdout");
114
+ if (!JS_IsUndefined(val) && !JS_IsNull(val)) {
115
+ JS_ToInt32(ctx, &custom_stdout_fd, val);
116
+ }
117
+ JS_FreeValue(ctx, val); // Était trop peu d'arguments ici !
118
+
119
+ val = JS_GetPropertyStr(ctx, options, "file");
120
+ if (!JS_IsUndefined(val) && !JS_IsNull(val)) {
121
+ file = JS_ToCString(ctx, val);
122
+ }
123
+ JS_FreeValue(ctx, val); // Était trop peu d'arguments ici !
124
+
125
+ val = JS_GetPropertyStr(ctx, options, "env");
126
+ if (!JS_IsUndefined(val) && !JS_IsNull(val)) {
127
+ envp = build_envp(ctx, val);
128
+ }
129
+ JS_FreeValue(ctx, val); // OK: 2 arguments
130
+ }
131
+
132
+ if (envp == NULL) envp = (char **)_environ;
133
+ const char *spawn_file = file ? file : exec_argv[0];
134
+
135
+ // 3. Redirection logic
136
+ if (custom_stdout_fd != -1) {
137
+ _flushall();
138
+ old_stdout = _dup(_fileno(stdout));
139
+ if (_dup2(custom_stdout_fd, _fileno(stdout)) == -1) {
140
+ _close(old_stdout);
141
+ old_stdout = -1;
142
+ }
143
+ }
144
+
145
+ mode = block_flag ? _P_WAIT : _P_NOWAIT;
146
+ _flushall();
147
+
148
+ if (use_path)
149
+ ret = _spawnvpe(mode, spawn_file, (const char * const *)exec_argv, (const char * const *)envp);
150
+ else
151
+ ret = _spawnve(mode, spawn_file, (const char * const *)exec_argv, (const char * const *)envp);
152
+
153
+ if (old_stdout != -1) {
154
+ _flushall();
155
+ _dup2(old_stdout, _fileno(stdout));
156
+ _close(old_stdout);
157
+ }
158
+
159
+ if (ret == -1) {
160
+ JS_ThrowTypeError(ctx, "exec error (spawn failed)");
161
+ goto exception;
162
+ }
163
+
164
+ ret_val = JS_NewInt32(ctx, ret);
165
+
166
+ done:
167
+ if (file) JS_FreeCString(ctx, file);
168
+ if (exec_argv) {
169
+ for(i = 0; i < exec_argc; i++) if (exec_argv[i]) JS_FreeCString(ctx, exec_argv[i]);
170
+ js_free(ctx, exec_argv);
171
+ }
172
+ if (envp && envp != (char **)_environ) {
173
+ char **p = envp;
174
+ while (*p != NULL) { js_free(ctx, *p); p++; }
175
+ js_free(ctx, envp);
176
+ }
177
+ return ret_val;
178
+
179
+ exception:
180
+ ret_val = JS_EXCEPTION;
181
+ goto done;
182
+ }
183
+ #endif