@stacksjs/stx 0.2.120 → 0.2.121

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.
@@ -62,10 +62,16 @@ export declare interface NativeBuildConfig {
62
62
  }
63
63
  }
64
64
  systemTray?: boolean
65
+ menubarOnly?: boolean
66
+ infoPlist?: Record<string, string | number | boolean>
67
+ minimumSystemVersion?: string
68
+ category?: string
69
+ copyright?: string
65
70
  hotReload?: boolean
66
71
  devTools?: boolean
67
72
  craftPath?: string
68
73
  env?: Record<string, string>
74
+ context?: Record<string, unknown>
69
75
  verbose?: boolean
70
76
  }
71
77
  export declare interface NativeBuildResult {
@@ -4,6 +4,7 @@ import { spawn, execSync } from "node:child_process";
4
4
  import { tmpdir, homedir, platform as osPlatform } from "node:os";
5
5
  import { injectCraftBridge } from "./craft-bridge.js";
6
6
  import { processCraftComponents, CRAFT_COMPONENT_STYLES } from "./craft-components.js";
7
+ import { renderTemplate } from "./render.js";
7
8
  export async function buildNative(config) {
8
9
  const startTime = Date.now();
9
10
  try {
@@ -17,7 +18,10 @@ export async function buildNative(config) {
17
18
  const target = config.target || detectPlatform(), format = config.format || getDefaultFormat(target), outputDir = config.output || join(process.cwd(), "dist", "native");
18
19
  if (!existsSync(outputDir))
19
20
  mkdirSync(outputDir, { recursive: !0 });
20
- let html = await Bun.file(config.input).text();
21
+ let html = await renderTemplate(config.input, {
22
+ context: config.context,
23
+ options: { isDev: !1 }
24
+ });
21
25
  html = injectCraftBridge(html, { debug: config.verbose });
22
26
  html = processCraftComponents(html);
23
27
  if (!html.includes("craft-component-styles")) {
@@ -87,64 +91,51 @@ async function buildMacOS(html, appName, appVersion, outputDir, format, config,
87
91
  sidebarConfigPath = join(resourcesDir, "sidebar-config.json");
88
92
  await Bun.write(sidebarConfigPath, JSON.stringify(config.sidebar.config));
89
93
  }
90
- const sidebarFlags = config.sidebar?.enabled ? `--native-sidebar --sidebar-width ${config.sidebar?.width || 240} --sidebar-config "$(cat "$RESOURCES/sidebar-config.json")"` : "", launcherScript = `#!/bin/bash
94
+ const sidebarFlags = config.sidebar?.enabled ? `--native-sidebar --sidebar-width ${config.sidebar?.width || 240} --sidebar-config "$(cat "$RESOURCES/sidebar-config.json")"` : "", craftBinaryDest = join(macOSDir, "craft");
95
+ copyFileSync(craftPath, craftBinaryDest);
96
+ chmodSync(craftBinaryDest, 493);
97
+ const menubarOnly = config.menubarOnly ?? config.systemTray, launcherScript = `#!/bin/bash
98
+ set -euo pipefail
91
99
  DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd )"
92
100
  RESOURCES="$DIR/../Resources"
93
- "${craftPath}" --html "$(cat "$RESOURCES/index.html")" \\
94
- --title "${config.window?.title || appName}" \\
95
- --width ${config.window?.width || 1200} \\
96
- --height ${config.window?.height || 800} \\
97
- ${config.systemTray ? "--system-tray" : ""} \\
98
- ${config.devTools ? "--dev-tools" : ""} \\
99
- ${config.window?.frameless ? "--frameless" : ""} \\
100
- ${config.window?.alwaysOnTop ? "--always-on-top" : ""} \\
101
- ${sidebarFlags}
101
+ exec "$DIR/craft" --html-file "$RESOURCES/index.html" \\
102
+ ${[
103
+ `--title "${config.window?.title || appName}"`,
104
+ `--width ${config.window?.width || 1200}`,
105
+ `--height ${config.window?.height || 800}`,
106
+ menubarOnly ? "--menubar-only" : config.systemTray ? "--system-tray" : "",
107
+ config.devTools ? "" : "--no-devtools",
108
+ config.window?.frameless ? "--frameless" : "",
109
+ config.window?.transparent ? "--transparent" : "",
110
+ config.window?.alwaysOnTop ? "--always-on-top" : "",
111
+ config.window?.resizable === !1 ? "--no-resize" : "",
112
+ sidebarFlags
113
+ ].filter(Boolean).join(" \\\n ")}
102
114
  `, launcherPath = join(macOSDir, appName);
103
115
  await Bun.write(launcherPath, launcherScript);
104
116
  chmodSync(launcherPath, 493);
105
- const craftBinaryDest = join(macOSDir, "craft");
106
- try {
107
- copyFileSync(craftPath, craftBinaryDest);
108
- chmodSync(craftBinaryDest, 493);
109
- } catch {
110
- const craftWrapperScript = `#!/bin/bash
111
- exec "${craftPath}" "$@"`;
112
- await Bun.write(craftBinaryDest, craftWrapperScript);
113
- chmodSync(craftBinaryDest, 493);
114
- }
115
- const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
116
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
117
- <plist version="1.0">
118
- <dict>
119
- <key>CFBundleExecutable</key>
120
- <string>${appName}</string>
121
- <key>CFBundleIdentifier</key>
122
- <string>${bundleId}</string>
123
- <key>CFBundleName</key>
124
- <string>${appName}</string>
125
- <key>CFBundleDisplayName</key>
126
- <string>${appName}</string>
127
- <key>CFBundleShortVersionString</key>
128
- <string>${appVersion}</string>
129
- <key>CFBundleVersion</key>
130
- <string>${appVersion}</string>
131
- <key>CFBundlePackageType</key>
132
- <string>APPL</string>
133
- <key>CFBundleSignature</key>
134
- <string>????</string>
135
- <key>LSMinimumSystemVersion</key>
136
- <string>10.15</string>
137
- <key>NSHighResolutionCapable</key>
138
- <true/>
139
- <key>NSSupportsAutomaticGraphicsSwitching</key>
140
- <true/>
141
- <key>LSUIElement</key>
142
- <${config.systemTray ? "true" : "false"}/>
143
- </dict>
144
- </plist>`;
145
- await Bun.write(join(contentsDir, "Info.plist"), infoPlist);
146
- if (config.icon && await Bun.file(config.icon).exists())
117
+ const hasIcon = Boolean(config.icon && await Bun.file(config.icon).exists());
118
+ if (hasIcon)
147
119
  copyFileSync(config.icon, join(resourcesDir, "AppIcon.icns"));
120
+ const plistEntries = {
121
+ CFBundleExecutable: appName,
122
+ CFBundleIdentifier: bundleId,
123
+ CFBundleName: appName,
124
+ CFBundleDisplayName: appName,
125
+ CFBundleShortVersionString: appVersion,
126
+ CFBundleVersion: appVersion,
127
+ CFBundlePackageType: "APPL",
128
+ CFBundleSignature: "????",
129
+ LSMinimumSystemVersion: config.minimumSystemVersion || "13.0",
130
+ NSHighResolutionCapable: !0,
131
+ NSSupportsAutomaticGraphicsSwitching: !0,
132
+ LSUIElement: config.menubarOnly ?? Boolean(config.systemTray),
133
+ ...hasIcon ? { CFBundleIconFile: "AppIcon" } : {},
134
+ ...config.category ? { LSApplicationCategoryType: config.category } : {},
135
+ ...config.copyright ? { NSHumanReadableCopyright: config.copyright } : {},
136
+ ...config.infoPlist || {}
137
+ };
138
+ await Bun.write(join(contentsDir, "Info.plist"), renderInfoPlist(plistEntries));
148
139
  if (format === "dmg") {
149
140
  const dmgPath = join(outputDir, `${appName}-${appVersion}.dmg`), dmgResult = await createDMG(appBundlePath, dmgPath, appName);
150
141
  if (!dmgResult.success)
@@ -153,19 +144,38 @@ exec "${craftPath}" "$@"`;
153
144
  }
154
145
  return { success: !0, target: "macos", format: "app", outputPath: appBundlePath };
155
146
  }
147
+ function escapeXml(value) {
148
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
149
+ }
150
+ function renderInfoPlist(entries) {
151
+ return `<?xml version="1.0" encoding="UTF-8"?>
152
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
153
+ <plist version="1.0">
154
+ <dict>
155
+ ${Object.entries(entries).map(([key, value]) => {
156
+ const rendered = typeof value === "boolean" ? `<${value}/>` : typeof value === "number" ? `<integer>${value}</integer>` : `<string>${escapeXml(String(value))}</string>`;
157
+ return ` <key>${escapeXml(key)}</key>
158
+ ${rendered}`;
159
+ }).join(`
160
+ `)}
161
+ </dict>
162
+ </plist>
163
+ `;
164
+ }
156
165
  async function buildWindows(html, appName, appVersion, outputDir, format, config, craftPath) {
157
166
  const appDir = join(outputDir, appName);
158
167
  mkdirSync(appDir, { recursive: !0 });
159
168
  const htmlPath = join(appDir, "index.html");
160
169
  await Bun.write(htmlPath, html);
170
+ copyFileSync(craftPath, join(appDir, "craft.exe"));
161
171
  const launcherBat = `@echo off
162
172
  set DIR=%~dp0
163
- "${craftPath}" --html-file "%DIR%index.html" ^
173
+ "%DIR%craft.exe" --html-file "%DIR%index.html" ^
164
174
  --title "${config.window?.title || appName}" ^
165
175
  --width ${config.window?.width || 1200} ^
166
176
  --height ${config.window?.height || 800} ^
167
177
  ${config.systemTray ? "--system-tray" : ""} ^
168
- ${config.devTools ? "--dev-tools" : ""}
178
+ ${config.devTools ? "" : "--no-devtools"}
169
179
  `;
170
180
  await Bun.write(join(appDir, `${appName}.bat`), launcherBat);
171
181
  if (format === "zip") {
@@ -181,14 +191,18 @@ async function buildLinux(html, appName, appVersion, outputDir, format, config,
181
191
  mkdirSync(appDir, { recursive: !0 });
182
192
  const htmlPath = join(appDir, "index.html");
183
193
  await Bun.write(htmlPath, html);
194
+ const craftDest = join(appDir, "craft");
195
+ copyFileSync(craftPath, craftDest);
196
+ chmodSync(craftDest, 493);
184
197
  const launcherScript = `#!/bin/bash
198
+ set -euo pipefail
185
199
  DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd )"
186
- "${craftPath}" --html-file "$DIR/index.html" \\
200
+ exec "$DIR/craft" --html-file "$DIR/index.html" \\
187
201
  --title "${config.window?.title || appName}" \\
188
202
  --width ${config.window?.width || 1200} \\
189
203
  --height ${config.window?.height || 800} \\
190
204
  ${config.systemTray ? "--system-tray" : ""} \\
191
- ${config.devTools ? "--dev-tools" : ""}
205
+ ${config.devTools ? "" : "--no-devtools"}
192
206
  `, launcherPath = join(appDir, appName.toLowerCase());
193
207
  await Bun.write(launcherPath, launcherScript);
194
208
  chmodSync(launcherPath, 493);
@@ -231,10 +245,13 @@ async function buildAndroid(_html, appName, _appVersion, _outputDir, _config, _c
231
245
  };
232
246
  }
233
247
  async function findCraftBinary() {
248
+ if (process.env.CRAFT_BIN && await Bun.file(process.env.CRAFT_BIN).exists())
249
+ return process.env.CRAFT_BIN;
234
250
  const possiblePaths = [
235
- join(process.cwd(), "../../craft/packages/zig/zig-out/bin/craft-minimal"),
236
- join(process.cwd(), "../craft/packages/zig/zig-out/bin/craft-minimal"),
237
- join(homedir(), "Code/craft/packages/zig/zig-out/bin/craft-minimal"),
251
+ join(process.cwd(), "../../craft/packages/zig/zig-out/bin/craft"),
252
+ join(process.cwd(), "../craft/packages/zig/zig-out/bin/craft"),
253
+ join(homedir(), "Code/Tools/craft/packages/zig/zig-out/bin/craft"),
254
+ join(homedir(), "Code/craft/packages/zig/zig-out/bin/craft"),
238
255
  "/usr/local/bin/craft",
239
256
  "/usr/bin/craft",
240
257
  join(homedir(), ".local/bin/craft"),