nanoshell 1.2.1 → 1.3.5
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 +2 -0
- package/cli/bundler.zig +54 -3
- package/cli/dev_server.zig +176 -5
- package/cli/installer.zig +4 -4
- package/package.json +1 -1
- package/zig-out/bin/example_app.exe +0 -0
- package/zig-out/bin/nanoshell.exe +0 -0
package/README.md
CHANGED
package/cli/bundler.zig
CHANGED
|
@@ -21,9 +21,60 @@ pub const SingleBinaryBundler = struct {
|
|
|
21
21
|
std.log.info("Starting ZeroUI Native App Bundler for '{s}'...", .{self.config.app_name});
|
|
22
22
|
std.log.info("Packaging Assets -> {s}", .{self.config.entry_point});
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const io = std.Io.Threaded.global_single_threaded.io();
|
|
25
|
+
_ = std.Io.Dir.cwd().createDir(io, self.config.output_dir, .default_dir) catch {};
|
|
26
26
|
|
|
27
|
-
std.
|
|
27
|
+
var dist_dir = std.Io.Dir.cwd().openDir(io, self.config.output_dir, .{}) catch |err| {
|
|
28
|
+
std.log.err("Failed to open output dir: {s}", .{@errorName(err)});
|
|
29
|
+
return err;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
_ = dist_dir.createDir(io, "app", .default_dir) catch {};
|
|
33
|
+
_ = dist_dir.createDir(io, "resources", .default_dir) catch {};
|
|
34
|
+
|
|
35
|
+
const cwd = std.Io.Dir.cwd();
|
|
36
|
+
|
|
37
|
+
// Copy Main Executable bin/<app_name>.exe -> dist/<app_name>.exe
|
|
38
|
+
const src_exe_path = try std.fmt.allocPrint(self.allocator, "bin/{s}.exe", .{self.config.app_name});
|
|
39
|
+
defer self.allocator.free(src_exe_path);
|
|
40
|
+
const dst_exe_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}.exe", .{ self.config.output_dir, self.config.app_name });
|
|
41
|
+
defer self.allocator.free(dst_exe_path);
|
|
42
|
+
|
|
43
|
+
_ = cwd.copyFile(src_exe_path, dist_dir, try std.fmt.allocPrint(self.allocator, "{s}.exe", .{self.config.app_name}), io, .{}) catch |err| {
|
|
44
|
+
std.log.warn("Executable copy warning: {s}", .{@errorName(err)});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Copy DLLs
|
|
48
|
+
const dlls = [_][]const u8{ "AppCore.dll", "Ultralight.dll", "UltralightCore.dll", "WebCore.dll", "vcruntime140.dll", "msvcp140.dll", "vcruntime140_1.dll" };
|
|
49
|
+
for (dlls) |dll| {
|
|
50
|
+
const src_dll = try std.fmt.allocPrint(self.allocator, "bin/{s}", .{dll});
|
|
51
|
+
defer self.allocator.free(src_dll);
|
|
52
|
+
_ = cwd.copyFile(src_dll, dist_dir, dll, io, .{}) catch {};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Copy Resource Files
|
|
56
|
+
const res_files = [_][]const u8{ "icudt67l.dat", "cacert.pem" };
|
|
57
|
+
for (res_files) |file| {
|
|
58
|
+
const src_res = try std.fmt.allocPrint(self.allocator, "bin/{s}", .{file});
|
|
59
|
+
defer self.allocator.free(src_res);
|
|
60
|
+
_ = cwd.copyFile(src_res, dist_dir, file, io, .{}) catch {};
|
|
61
|
+
_ = cwd.copyFile(src_res, dist_dir, try std.fmt.allocPrint(self.allocator, "resources/{s}", .{file}), io, .{}) catch {};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Copy app/* files to dist/app/*
|
|
65
|
+
var app_dir_opt = cwd.openDir(io, "app", .{ .iterate = true }) catch null;
|
|
66
|
+
if (app_dir_opt) |*app_dir| {
|
|
67
|
+
var dist_app_dir = dist_dir.openDir(io, "app", .{}) catch null;
|
|
68
|
+
if (dist_app_dir) |*dst_app| {
|
|
69
|
+
var iter = app_dir.iterate();
|
|
70
|
+
while (iter.next(io) catch null) |entry| {
|
|
71
|
+
if (entry.kind == .file) {
|
|
72
|
+
_ = app_dir.copyFile(entry.name, dst_app.*, entry.name, io, .{}) catch {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
std.log.info("Built Custom Named Executable -> '{s}' (Self-contained release package)", .{dst_exe_path});
|
|
28
79
|
}
|
|
29
80
|
};
|
package/cli/dev_server.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const installer = @import("installer.zig");
|
|
2
3
|
|
|
3
4
|
pub const DevServer = struct {
|
|
4
5
|
allocator: std.mem.Allocator,
|
|
@@ -8,14 +9,184 @@ pub const DevServer = struct {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
pub fn runDevMode(self: *DevServer, app_dir_name: []const u8) !void {
|
|
12
|
+
_ = app_dir_name;
|
|
11
13
|
std.log.info("=================================================================", .{});
|
|
12
|
-
std.log.info(" ⚡ NanoShell
|
|
13
|
-
std.log.info(" Watching app/ directory for
|
|
14
|
+
std.log.info(" ⚡ NanoShell Dev Server Started", .{});
|
|
15
|
+
std.log.info(" Watching app/ directory & nanoshell.json for live changes...", .{});
|
|
14
16
|
std.log.info("=================================================================", .{});
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
defer self.allocator
|
|
18
|
+
var config = installer.loadConfig(self.allocator) catch installer.AppConfig{};
|
|
19
|
+
defer config.deinit(self.allocator);
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
// Find executable inside bin/ or zig-out/bin/
|
|
22
|
+
const io = std.Io.Threaded.global_single_threaded.io();
|
|
23
|
+
const cwd = std.Io.Dir.cwd();
|
|
24
|
+
|
|
25
|
+
var found_exe: ?[]const u8 = null;
|
|
26
|
+
|
|
27
|
+
const candidate_paths = [_][]const u8{
|
|
28
|
+
"zig-out/bin/example_app.exe",
|
|
29
|
+
"bin/example_app.exe",
|
|
30
|
+
"zig-out/bin/nanoshell.exe",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (candidate_paths) |p| {
|
|
34
|
+
if (cwd.openFile(io, p, .{})) |f| {
|
|
35
|
+
f.close(io);
|
|
36
|
+
found_exe = p;
|
|
37
|
+
break;
|
|
38
|
+
} else |_| {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (found_exe == null) {
|
|
42
|
+
// Search bin/ for any .exe
|
|
43
|
+
var bin_dir = cwd.openDir(io, "bin", .{ .iterate = true }) catch null;
|
|
44
|
+
if (bin_dir) |*d| {
|
|
45
|
+
defer d.close(io);
|
|
46
|
+
var iter = d.iterate();
|
|
47
|
+
while (iter.next(io) catch null) |entry| {
|
|
48
|
+
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".exe")) {
|
|
49
|
+
found_exe = try std.fmt.allocPrint(self.allocator, "bin/{s}", .{entry.name});
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var win_exe_buf: [256]u8 = undefined;
|
|
57
|
+
const exe_to_run = found_exe orelse "bin\\app.exe";
|
|
58
|
+
var pos: usize = 0;
|
|
59
|
+
for (exe_to_run) |ch| {
|
|
60
|
+
if (ch == '/') {
|
|
61
|
+
win_exe_buf[pos] = '\\';
|
|
62
|
+
} else {
|
|
63
|
+
win_exe_buf[pos] = ch;
|
|
64
|
+
}
|
|
65
|
+
pos += 1;
|
|
66
|
+
}
|
|
67
|
+
win_exe_buf[pos] = 0;
|
|
68
|
+
const win_path = win_exe_buf[0..pos];
|
|
69
|
+
|
|
70
|
+
var dev_cmd_buf: [300]u8 = undefined;
|
|
71
|
+
const dev_cmd = std.fmt.bufPrintZ(&dev_cmd_buf, "{s} --dev", .{win_path}) catch win_path;
|
|
72
|
+
|
|
73
|
+
std.log.info("Launching NanoShell runtime in DEV mode: {s}", .{dev_cmd});
|
|
74
|
+
|
|
75
|
+
// Launch app process once - native engine handles smooth in-window hot reload in dev mode
|
|
76
|
+
var child = try self.spawnChildProcess(dev_cmd);
|
|
77
|
+
defer _ = child.kill();
|
|
78
|
+
|
|
79
|
+
std.log.info("App running! In-window hot reload active for app/index.html, styles.css, app.js.", .{});
|
|
80
|
+
|
|
81
|
+
const WinApi = struct {
|
|
82
|
+
pub extern "kernel32" fn Sleep(dwMilliseconds: u32) callconv(.winapi) void;
|
|
83
|
+
pub extern "kernel32" fn GetExitCodeProcess(hProcess: std.os.windows.HANDLE, lpExitCode: *u32) callconv(.winapi) i32;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
var exit_code: u32 = 259; // STILL_ACTIVE
|
|
87
|
+
while (exit_code == 259) {
|
|
88
|
+
WinApi.Sleep(500);
|
|
89
|
+
_ = WinApi.GetExitCodeProcess(child.hProcess, &exit_code);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fn spawnChildProcess(self: *DevServer, exe_path: []const u8) !ChildProcess {
|
|
94
|
+
_ = self;
|
|
95
|
+
var cmd_utf16: [1024]u16 = undefined;
|
|
96
|
+
const len = try std.unicode.utf8ToUtf16Le(&cmd_utf16, exe_path);
|
|
97
|
+
cmd_utf16[len] = 0;
|
|
98
|
+
|
|
99
|
+
const STARTUPINFOW = extern struct {
|
|
100
|
+
cb: u32 = 104,
|
|
101
|
+
lpReserved: ?[*:0]u16 = null,
|
|
102
|
+
lpDesktop: ?[*:0]u16 = null,
|
|
103
|
+
lpTitle: ?[*:0]u16 = null,
|
|
104
|
+
dwX: u32 = 0,
|
|
105
|
+
dwY: u32 = 0,
|
|
106
|
+
dwXSize: u32 = 0,
|
|
107
|
+
dwYSize: u32 = 0,
|
|
108
|
+
dwXCountChars: u32 = 0,
|
|
109
|
+
dwYCountChars: u32 = 0,
|
|
110
|
+
dwFillAttribute: u32 = 0,
|
|
111
|
+
dwFlags: u32 = 0,
|
|
112
|
+
wShowWindow: u16 = 0,
|
|
113
|
+
cbReserved2: u16 = 0,
|
|
114
|
+
lpReserved2: ?*u8 = null,
|
|
115
|
+
hStdInput: ?std.os.windows.HANDLE = null,
|
|
116
|
+
hStdOutput: ?std.os.windows.HANDLE = null,
|
|
117
|
+
hStdError: ?std.os.windows.HANDLE = null,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const PROCESS_INFORMATION = extern struct {
|
|
121
|
+
hProcess: std.os.windows.HANDLE,
|
|
122
|
+
hThread: std.os.windows.HANDLE,
|
|
123
|
+
dwProcessId: u32,
|
|
124
|
+
dwThreadId: u32,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const WinApi = struct {
|
|
128
|
+
pub extern "kernel32" fn CreateProcessW(
|
|
129
|
+
lpApplicationName: ?[*:0]const u16,
|
|
130
|
+
lpCommandLine: ?[*:0]u16,
|
|
131
|
+
lpProcessAttributes: ?*anyopaque,
|
|
132
|
+
lpThreadAttributes: ?*anyopaque,
|
|
133
|
+
bInheritHandles: i32,
|
|
134
|
+
dwCreationFlags: u32,
|
|
135
|
+
lpEnvironment: ?*anyopaque,
|
|
136
|
+
lpCurrentDirectory: ?[*:0]const u16,
|
|
137
|
+
lpStartupInfo: *const STARTUPINFOW,
|
|
138
|
+
lpProcessInformation: *PROCESS_INFORMATION,
|
|
139
|
+
) callconv(.winapi) i32;
|
|
140
|
+
|
|
141
|
+
pub extern "kernel32" fn TerminateProcess(hProcess: std.os.windows.HANDLE, uExitCode: u32) callconv(.winapi) i32;
|
|
142
|
+
pub extern "kernel32" fn CloseHandle(hObject: std.os.windows.HANDLE) callconv(.winapi) i32;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
var si = STARTUPINFOW{};
|
|
146
|
+
var pi: PROCESS_INFORMATION = undefined;
|
|
147
|
+
|
|
148
|
+
const res = WinApi.CreateProcessW(null, @ptrCast(&cmd_utf16), null, null, 0, 0, null, null, &si, &pi);
|
|
149
|
+
if (res == 0) return error.ProcessCreationFailed;
|
|
150
|
+
|
|
151
|
+
return ChildProcess{
|
|
152
|
+
.hProcess = pi.hProcess,
|
|
153
|
+
.hThread = pi.hThread,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn getAppModTime(self: *DevServer) i128 {
|
|
158
|
+
_ = self;
|
|
159
|
+
const io = std.Io.Threaded.global_single_threaded.io();
|
|
160
|
+
const cwd = std.Io.Dir.cwd();
|
|
161
|
+
var max_mtime: i128 = 0;
|
|
162
|
+
|
|
163
|
+
var app_dir = cwd.openDir(io, "app", .{ .iterate = true }) catch return 0;
|
|
164
|
+
defer app_dir.close(io);
|
|
165
|
+
|
|
166
|
+
var iter = app_dir.iterate();
|
|
167
|
+
while (iter.next(io) catch null) |entry| {
|
|
168
|
+
if (entry.kind == .file) {
|
|
169
|
+
if (app_dir.statFile(io, entry.name, .{})) |st| {
|
|
170
|
+
const ns = st.mtime.toNanoseconds();
|
|
171
|
+
if (ns > max_mtime) max_mtime = ns;
|
|
172
|
+
} else |_| {}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return max_mtime;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
pub const ChildProcess = struct {
|
|
180
|
+
hProcess: std.os.windows.HANDLE,
|
|
181
|
+
hThread: std.os.windows.HANDLE,
|
|
182
|
+
|
|
183
|
+
pub fn kill(self: *ChildProcess) void {
|
|
184
|
+
const WinApi = struct {
|
|
185
|
+
pub extern "kernel32" fn TerminateProcess(hProcess: std.os.windows.HANDLE, uExitCode: u32) callconv(.winapi) i32;
|
|
186
|
+
pub extern "kernel32" fn CloseHandle(hObject: std.os.windows.HANDLE) callconv(.winapi) i32;
|
|
187
|
+
};
|
|
188
|
+
_ = WinApi.TerminateProcess(self.hProcess, 1);
|
|
189
|
+
_ = WinApi.CloseHandle(self.hProcess);
|
|
190
|
+
_ = WinApi.CloseHandle(self.hThread);
|
|
20
191
|
}
|
|
21
192
|
};
|
package/cli/installer.zig
CHANGED
|
@@ -193,9 +193,9 @@ pub fn generateIss(allocator: std.mem.Allocator, config: AppConfig) ![]const u8
|
|
|
193
193
|
\\Source: "bin\Ultralight.dll"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
194
194
|
\\Source: "bin\UltralightCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
195
195
|
\\Source: "bin\WebCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
196
|
-
\\Source: "bin\vcruntime140.dll"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
197
|
-
\\Source: "bin\msvcp140.dll"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
198
|
-
\\Source: "bin\vcruntime140_1.dll"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
196
|
+
\\Source: "bin\vcruntime140.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
197
|
+
\\Source: "bin\msvcp140.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
198
|
+
\\Source: "bin\vcruntime140_1.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
199
199
|
\\
|
|
200
200
|
\\; ICU Unicode data and TLS certs
|
|
201
201
|
\\Source: "bin\icudt67l.dat"; DestDir: "{{app}}"; Flags: ignoreversion
|
|
@@ -349,4 +349,4 @@ pub fn run(allocator: std.mem.Allocator) !void {
|
|
|
349
349
|
std.log.info(" 2. Open setup.iss or run: ISCC setup.iss", .{});
|
|
350
350
|
std.log.info(" 3. Output binary will be saved in: dist\\{s}-Setup.exe", .{config.name});
|
|
351
351
|
}
|
|
352
|
-
}
|
|
352
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|