reset-framework-cli 1.2.2 → 1.2.4

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.
Files changed (35) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +25 -25
  3. package/package.json +7 -7
  4. package/src/commands/build.js +144 -144
  5. package/src/commands/dev.js +195 -195
  6. package/src/commands/doctor.js +140 -140
  7. package/src/commands/init.js +946 -946
  8. package/src/commands/package.js +68 -68
  9. package/src/index.js +195 -195
  10. package/src/lib/backend.js +123 -123
  11. package/src/lib/context.js +66 -66
  12. package/src/lib/framework.js +57 -57
  13. package/src/lib/logger.js +11 -11
  14. package/src/lib/output.js +283 -283
  15. package/src/lib/process.js +303 -303
  16. package/src/lib/project.js +893 -866
  17. package/src/lib/toolchain.js +62 -62
  18. package/src/lib/ui.js +244 -244
  19. package/templates/basic/README.md +15 -15
  20. package/templates/basic/frontend/README.md +73 -73
  21. package/templates/basic/frontend/eslint.config.js +23 -23
  22. package/templates/basic/frontend/index.html +13 -13
  23. package/templates/basic/frontend/package.json +31 -31
  24. package/templates/basic/frontend/public/icons.svg +24 -24
  25. package/templates/basic/frontend/src/App.css +216 -216
  26. package/templates/basic/frontend/src/App.tsx +77 -77
  27. package/templates/basic/frontend/src/assets/vite.svg +1 -1
  28. package/templates/basic/frontend/src/index.css +111 -111
  29. package/templates/basic/frontend/src/lib/reset.ts +1 -1
  30. package/templates/basic/frontend/src/main.tsx +10 -10
  31. package/templates/basic/frontend/tsconfig.app.json +28 -28
  32. package/templates/basic/frontend/tsconfig.json +7 -7
  33. package/templates/basic/frontend/tsconfig.node.json +26 -26
  34. package/templates/basic/frontend/vite.config.ts +16 -16
  35. package/templates/basic/reset.config.json +58 -58
package/src/lib/output.js CHANGED
@@ -1,283 +1,283 @@
1
- import path from "node:path"
2
- import { cp, mkdir, readdir, rm, writeFile } from "node:fs/promises"
3
- import { existsSync } from "node:fs"
4
-
5
- import { stageAppBackend } from "./backend.js"
6
- import { logger } from "./logger.js"
7
- import { runCommand } from "./process.js"
8
- import {
9
- resolveFrameworkRuntimeBinary,
10
- resolveFrameworkRuntimeOutputDir
11
- } from "./project.js"
12
-
13
- function escapePlistString(value) {
14
- return value
15
- .replaceAll("&", "&")
16
- .replaceAll("<", "&lt;")
17
- .replaceAll(">", "&gt;")
18
- }
19
-
20
- function serializePlistValue(value, indentLevel = 1) {
21
- const indent = " ".repeat(indentLevel)
22
-
23
- if (Array.isArray(value)) {
24
- if (value.length === 0) {
25
- return `${indent}<array/>\n`
26
- }
27
-
28
- return (
29
- `${indent}<array>\n` +
30
- value.map((entry) => serializePlistValue(entry, indentLevel + 1)).join("") +
31
- `${indent}</array>\n`
32
- )
33
- }
34
-
35
- if (typeof value === "object" && value !== null) {
36
- const entries = Object.entries(value)
37
- if (entries.length === 0) {
38
- return `${indent}<dict/>\n`
39
- }
40
-
41
- return (
42
- `${indent}<dict>\n` +
43
- entries
44
- .map(
45
- ([key, entryValue]) =>
46
- `${" ".repeat(indentLevel + 1)}<key>${escapePlistString(key)}</key>\n${serializePlistValue(entryValue, indentLevel + 1)}`
47
- )
48
- .join("") +
49
- `${indent}</dict>\n`
50
- )
51
- }
52
-
53
- if (typeof value === "boolean") {
54
- return `${indent}<${value ? "true" : "false"}/>\n`
55
- }
56
-
57
- if (typeof value === "number") {
58
- return `${indent}<integer>${value}</integer>\n`
59
- }
60
-
61
- return `${indent}<string>${escapePlistString(String(value))}</string>\n`
62
- }
63
-
64
- function buildMacOSInfoPlist(config) {
65
- const urlTypes = Array.isArray(config.protocols?.schemes)
66
- ? config.protocols.schemes.map((schemeConfig) => ({
67
- CFBundleTypeRole: schemeConfig.role ?? "Viewer",
68
- CFBundleURLName:
69
- typeof schemeConfig.name === "string" && schemeConfig.name.trim() !== ""
70
- ? schemeConfig.name
71
- : `${config.appId}.${schemeConfig.scheme}`,
72
- CFBundleURLSchemes: [schemeConfig.scheme]
73
- }))
74
- : []
75
-
76
- const plist = {
77
- CFBundleDevelopmentRegion: "English",
78
- CFBundleDisplayName: config.productName,
79
- CFBundleExecutable: "reset-framework",
80
- CFBundleIdentifier: config.appId,
81
- CFBundleInfoDictionaryVersion: "6.0",
82
- CFBundleName: config.productName,
83
- CFBundlePackageType: "APPL",
84
- CFBundleShortVersionString: config.version,
85
- CFBundleVersion: config.version,
86
- CSResourcesFileMapped: true
87
- }
88
-
89
- if (urlTypes.length > 0) {
90
- plist.CFBundleURLTypes = urlTypes
91
- }
92
-
93
- return (
94
- `<?xml version="1.0" encoding="UTF-8"?>\n` +
95
- `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n` +
96
- `<plist version="1.0">\n` +
97
- serializePlistValue(plist, 0) +
98
- `</plist>\n`
99
- )
100
- }
101
-
102
- function shouldCopyWindowsRuntimeArtifact(entryName) {
103
- const extension = path.extname(entryName).toLowerCase()
104
-
105
- return [
106
- ".bin",
107
- ".dat",
108
- ".dll",
109
- ".exe",
110
- ".json",
111
- ".manifest",
112
- ".pak",
113
- ".pri"
114
- ].includes(extension)
115
- }
116
-
117
- function escapePowerShellLiteral(value) {
118
- return String(value).replaceAll("'", "''")
119
- }
120
-
121
- export async function stageMacOSAppBundle(frameworkPaths, frameworkBuildPaths, config, options = {}) {
122
- const { dryRun = false, outputPaths, configPath, preferSource = false, backendArtifact = null } = options
123
- const runtimeSourceBundle = resolveFrameworkRuntimeOutputDir(frameworkPaths, frameworkBuildPaths, "release", {
124
- mustExist: !dryRun,
125
- preferSource
126
- })
127
-
128
- if (!outputPaths) {
129
- throw new Error("stageMacOSAppBundle requires resolved output paths")
130
- }
131
-
132
- if (!existsSync(outputPaths.frontendEntryFile) && !dryRun) {
133
- throw new Error(
134
- `Missing frontend build output at ${outputPaths.frontendEntryFile}. Run the frontend build first.`
135
- )
136
- }
137
-
138
- logger.info(`Staging app bundle into ${outputPaths.appBundlePath}`)
139
-
140
- if (dryRun) {
141
- logger.info(`- copy ${runtimeSourceBundle} -> ${outputPaths.appBundlePath}`)
142
- logger.info(`- copy ${configPath} -> ${outputPaths.bundledConfigPath}`)
143
- logger.info(`- copy ${outputPaths.frontendDistDir} -> ${outputPaths.bundledFrontendDir}`)
144
- if (backendArtifact) {
145
- logger.info(`- copy ${backendArtifact.bundleEntryPath} -> ${outputPaths.bundledBackendEntryPath}`)
146
- logger.info(`- copy ${backendArtifact.runnerPath} -> ${outputPaths.bundledBackendRunnerPath}`)
147
- logger.info(`- copy ${backendArtifact.supportModulePath} -> ${outputPaths.bundledBackendSupportModulePath}`)
148
- logger.info(`- copy ${backendArtifact.runtimeExecutablePath} -> ${outputPaths.bundledBackendRuntimePath}`)
149
- }
150
- return outputPaths
151
- }
152
-
153
- await mkdir(outputPaths.macosDir, { recursive: true })
154
- await rm(outputPaths.appBundlePath, { recursive: true, force: true })
155
- await cp(runtimeSourceBundle, outputPaths.appBundlePath, { recursive: true })
156
-
157
- await mkdir(outputPaths.resourcesDir, { recursive: true })
158
- await cp(configPath, outputPaths.bundledConfigPath, { force: true })
159
- await rm(outputPaths.bundledFrontendDir, { recursive: true, force: true })
160
- await cp(outputPaths.frontendDistDir, outputPaths.bundledFrontendDir, { recursive: true })
161
- await stageAppBackend(backendArtifact, outputPaths)
162
- await writeFile(
163
- path.join(outputPaths.appBundlePath, "Contents", "Info.plist"),
164
- buildMacOSInfoPlist(config),
165
- "utf8"
166
- )
167
-
168
- return outputPaths
169
- }
170
-
171
- export async function stageWindowsAppBundle(frameworkPaths, frameworkBuildPaths, config, options = {}) {
172
- const { dryRun = false, outputPaths, configPath, preferSource = false, backendArtifact = null } = options
173
- const runtimeSourceBinary = resolveFrameworkRuntimeBinary(frameworkPaths, frameworkBuildPaths, "release", {
174
- mustExist: !dryRun,
175
- preferSource
176
- })
177
- const runtimeSourceDir = resolveFrameworkRuntimeOutputDir(frameworkPaths, frameworkBuildPaths, "release", {
178
- mustExist: !dryRun,
179
- preferSource
180
- })
181
-
182
- if (!outputPaths) {
183
- throw new Error("stageWindowsAppBundle requires resolved output paths")
184
- }
185
-
186
- if (!existsSync(outputPaths.frontendEntryFile) && !dryRun) {
187
- throw new Error(
188
- `Missing frontend build output at ${outputPaths.frontendEntryFile}. Run the frontend build first.`
189
- )
190
- }
191
-
192
- logger.info(`Staging Windows app into ${outputPaths.appBundlePath}`)
193
-
194
- if (dryRun) {
195
- logger.info(`- copy ${runtimeSourceDir} -> ${outputPaths.appBundlePath}`)
196
- logger.info(`- rename ${runtimeSourceBinary} -> ${outputPaths.appExecutablePath}`)
197
- logger.info(`- copy ${configPath} -> ${outputPaths.bundledConfigPath}`)
198
- logger.info(`- copy ${outputPaths.frontendDistDir} -> ${outputPaths.bundledFrontendDir}`)
199
- if (backendArtifact) {
200
- logger.info(`- copy ${backendArtifact.bundleEntryPath} -> ${outputPaths.bundledBackendEntryPath}`)
201
- logger.info(`- copy ${backendArtifact.runnerPath} -> ${outputPaths.bundledBackendRunnerPath}`)
202
- logger.info(`- copy ${backendArtifact.supportModulePath} -> ${outputPaths.bundledBackendSupportModulePath}`)
203
- logger.info(`- copy ${backendArtifact.runtimeExecutablePath} -> ${outputPaths.bundledBackendRuntimePath}`)
204
- }
205
- return outputPaths
206
- }
207
-
208
- await mkdir(outputPaths.windowsDir, { recursive: true })
209
- await rm(outputPaths.appBundlePath, { recursive: true, force: true })
210
- await mkdir(outputPaths.appBundlePath, { recursive: true })
211
- const runtimeEntries = await readdir(runtimeSourceDir, { withFileTypes: true })
212
- const runtimeBinaryName = path.basename(runtimeSourceBinary)
213
-
214
- for (const entry of runtimeEntries) {
215
- if (!entry.isDirectory() && !shouldCopyWindowsRuntimeArtifact(entry.name)) {
216
- continue
217
- }
218
-
219
- const sourcePath = path.join(runtimeSourceDir, entry.name)
220
- const targetPath =
221
- entry.name.toLowerCase() === runtimeBinaryName.toLowerCase()
222
- ? outputPaths.appExecutablePath
223
- : path.join(outputPaths.appBundlePath, entry.name)
224
-
225
- await cp(sourcePath, targetPath, { recursive: true, force: true })
226
- }
227
-
228
- await mkdir(outputPaths.resourcesDir, { recursive: true })
229
- await cp(configPath, outputPaths.bundledConfigPath, { force: true })
230
- await rm(outputPaths.bundledFrontendDir, { recursive: true, force: true })
231
- await cp(outputPaths.frontendDistDir, outputPaths.bundledFrontendDir, { recursive: true })
232
- await stageAppBackend(backendArtifact, outputPaths)
233
-
234
- return outputPaths
235
- }
236
-
237
- export async function createMacOSZipArchive(outputPaths, options = {}) {
238
- const { dryRun = false } = options
239
-
240
- logger.info(`Packaging ${outputPaths.appBundlePath} -> ${outputPaths.zipPath}`)
241
-
242
- if (process.platform !== "darwin") {
243
- throw new Error("Packaging is only implemented for macOS right now")
244
- }
245
-
246
- if (!dryRun) {
247
- await mkdir(outputPaths.packagesDir, { recursive: true })
248
- }
249
-
250
- await runCommand(
251
- "ditto",
252
- ["-c", "-k", "--sequesterRsrc", "--keepParent", outputPaths.appBundlePath, outputPaths.zipPath],
253
- { dryRun }
254
- )
255
- }
256
-
257
- export async function createWindowsZipArchive(outputPaths, options = {}) {
258
- const { dryRun = false } = options
259
-
260
- logger.info(`Packaging ${outputPaths.appBundlePath} -> ${outputPaths.zipPath}`)
261
-
262
- if (process.platform !== "win32") {
263
- throw new Error("Packaging is only implemented for Windows and macOS right now")
264
- }
265
-
266
- if (!dryRun) {
267
- await mkdir(outputPaths.packagesDir, { recursive: true })
268
- await rm(outputPaths.zipPath, { force: true })
269
- }
270
-
271
- const literalSourcePath = escapePowerShellLiteral(outputPaths.appBundlePath)
272
- const literalDestinationPath = escapePowerShellLiteral(outputPaths.zipPath)
273
- const archiveCommand = [
274
- "$ErrorActionPreference = 'Stop'",
275
- `Compress-Archive -LiteralPath '${literalSourcePath}' -DestinationPath '${literalDestinationPath}' -CompressionLevel Optimal`
276
- ].join("; ")
277
-
278
- await runCommand(
279
- "powershell.exe",
280
- ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", archiveCommand],
281
- { dryRun }
282
- )
283
- }
1
+ import path from "node:path"
2
+ import { cp, mkdir, readdir, rm, writeFile } from "node:fs/promises"
3
+ import { existsSync } from "node:fs"
4
+
5
+ import { stageAppBackend } from "./backend.js"
6
+ import { logger } from "./logger.js"
7
+ import { runCommand } from "./process.js"
8
+ import {
9
+ resolveFrameworkRuntimeBinary,
10
+ resolveFrameworkRuntimeOutputDir
11
+ } from "./project.js"
12
+
13
+ function escapePlistString(value) {
14
+ return value
15
+ .replaceAll("&", "&amp;")
16
+ .replaceAll("<", "&lt;")
17
+ .replaceAll(">", "&gt;")
18
+ }
19
+
20
+ function serializePlistValue(value, indentLevel = 1) {
21
+ const indent = " ".repeat(indentLevel)
22
+
23
+ if (Array.isArray(value)) {
24
+ if (value.length === 0) {
25
+ return `${indent}<array/>\n`
26
+ }
27
+
28
+ return (
29
+ `${indent}<array>\n` +
30
+ value.map((entry) => serializePlistValue(entry, indentLevel + 1)).join("") +
31
+ `${indent}</array>\n`
32
+ )
33
+ }
34
+
35
+ if (typeof value === "object" && value !== null) {
36
+ const entries = Object.entries(value)
37
+ if (entries.length === 0) {
38
+ return `${indent}<dict/>\n`
39
+ }
40
+
41
+ return (
42
+ `${indent}<dict>\n` +
43
+ entries
44
+ .map(
45
+ ([key, entryValue]) =>
46
+ `${" ".repeat(indentLevel + 1)}<key>${escapePlistString(key)}</key>\n${serializePlistValue(entryValue, indentLevel + 1)}`
47
+ )
48
+ .join("") +
49
+ `${indent}</dict>\n`
50
+ )
51
+ }
52
+
53
+ if (typeof value === "boolean") {
54
+ return `${indent}<${value ? "true" : "false"}/>\n`
55
+ }
56
+
57
+ if (typeof value === "number") {
58
+ return `${indent}<integer>${value}</integer>\n`
59
+ }
60
+
61
+ return `${indent}<string>${escapePlistString(String(value))}</string>\n`
62
+ }
63
+
64
+ function buildMacOSInfoPlist(config) {
65
+ const urlTypes = Array.isArray(config.protocols?.schemes)
66
+ ? config.protocols.schemes.map((schemeConfig) => ({
67
+ CFBundleTypeRole: schemeConfig.role ?? "Viewer",
68
+ CFBundleURLName:
69
+ typeof schemeConfig.name === "string" && schemeConfig.name.trim() !== ""
70
+ ? schemeConfig.name
71
+ : `${config.appId}.${schemeConfig.scheme}`,
72
+ CFBundleURLSchemes: [schemeConfig.scheme]
73
+ }))
74
+ : []
75
+
76
+ const plist = {
77
+ CFBundleDevelopmentRegion: "English",
78
+ CFBundleDisplayName: config.productName,
79
+ CFBundleExecutable: "reset-framework",
80
+ CFBundleIdentifier: config.appId,
81
+ CFBundleInfoDictionaryVersion: "6.0",
82
+ CFBundleName: config.productName,
83
+ CFBundlePackageType: "APPL",
84
+ CFBundleShortVersionString: config.version,
85
+ CFBundleVersion: config.version,
86
+ CSResourcesFileMapped: true
87
+ }
88
+
89
+ if (urlTypes.length > 0) {
90
+ plist.CFBundleURLTypes = urlTypes
91
+ }
92
+
93
+ return (
94
+ `<?xml version="1.0" encoding="UTF-8"?>\n` +
95
+ `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n` +
96
+ `<plist version="1.0">\n` +
97
+ serializePlistValue(plist, 0) +
98
+ `</plist>\n`
99
+ )
100
+ }
101
+
102
+ function shouldCopyWindowsRuntimeArtifact(entryName) {
103
+ const extension = path.extname(entryName).toLowerCase()
104
+
105
+ return [
106
+ ".bin",
107
+ ".dat",
108
+ ".dll",
109
+ ".exe",
110
+ ".json",
111
+ ".manifest",
112
+ ".pak",
113
+ ".pri"
114
+ ].includes(extension)
115
+ }
116
+
117
+ function escapePowerShellLiteral(value) {
118
+ return String(value).replaceAll("'", "''")
119
+ }
120
+
121
+ export async function stageMacOSAppBundle(frameworkPaths, frameworkBuildPaths, config, options = {}) {
122
+ const { dryRun = false, outputPaths, configPath, preferSource = false, backendArtifact = null } = options
123
+ const runtimeSourceBundle = resolveFrameworkRuntimeOutputDir(frameworkPaths, frameworkBuildPaths, "release", {
124
+ mustExist: !dryRun,
125
+ preferSource
126
+ })
127
+
128
+ if (!outputPaths) {
129
+ throw new Error("stageMacOSAppBundle requires resolved output paths")
130
+ }
131
+
132
+ if (!existsSync(outputPaths.frontendEntryFile) && !dryRun) {
133
+ throw new Error(
134
+ `Missing frontend build output at ${outputPaths.frontendEntryFile}. Run the frontend build first.`
135
+ )
136
+ }
137
+
138
+ logger.info(`Staging app bundle into ${outputPaths.appBundlePath}`)
139
+
140
+ if (dryRun) {
141
+ logger.info(`- copy ${runtimeSourceBundle} -> ${outputPaths.appBundlePath}`)
142
+ logger.info(`- copy ${configPath} -> ${outputPaths.bundledConfigPath}`)
143
+ logger.info(`- copy ${outputPaths.frontendDistDir} -> ${outputPaths.bundledFrontendDir}`)
144
+ if (backendArtifact) {
145
+ logger.info(`- copy ${backendArtifact.bundleEntryPath} -> ${outputPaths.bundledBackendEntryPath}`)
146
+ logger.info(`- copy ${backendArtifact.runnerPath} -> ${outputPaths.bundledBackendRunnerPath}`)
147
+ logger.info(`- copy ${backendArtifact.supportModulePath} -> ${outputPaths.bundledBackendSupportModulePath}`)
148
+ logger.info(`- copy ${backendArtifact.runtimeExecutablePath} -> ${outputPaths.bundledBackendRuntimePath}`)
149
+ }
150
+ return outputPaths
151
+ }
152
+
153
+ await mkdir(outputPaths.macosDir, { recursive: true })
154
+ await rm(outputPaths.appBundlePath, { recursive: true, force: true })
155
+ await cp(runtimeSourceBundle, outputPaths.appBundlePath, { recursive: true })
156
+
157
+ await mkdir(outputPaths.resourcesDir, { recursive: true })
158
+ await cp(configPath, outputPaths.bundledConfigPath, { force: true })
159
+ await rm(outputPaths.bundledFrontendDir, { recursive: true, force: true })
160
+ await cp(outputPaths.frontendDistDir, outputPaths.bundledFrontendDir, { recursive: true })
161
+ await stageAppBackend(backendArtifact, outputPaths)
162
+ await writeFile(
163
+ path.join(outputPaths.appBundlePath, "Contents", "Info.plist"),
164
+ buildMacOSInfoPlist(config),
165
+ "utf8"
166
+ )
167
+
168
+ return outputPaths
169
+ }
170
+
171
+ export async function stageWindowsAppBundle(frameworkPaths, frameworkBuildPaths, config, options = {}) {
172
+ const { dryRun = false, outputPaths, configPath, preferSource = false, backendArtifact = null } = options
173
+ const runtimeSourceBinary = resolveFrameworkRuntimeBinary(frameworkPaths, frameworkBuildPaths, "release", {
174
+ mustExist: !dryRun,
175
+ preferSource
176
+ })
177
+ const runtimeSourceDir = resolveFrameworkRuntimeOutputDir(frameworkPaths, frameworkBuildPaths, "release", {
178
+ mustExist: !dryRun,
179
+ preferSource
180
+ })
181
+
182
+ if (!outputPaths) {
183
+ throw new Error("stageWindowsAppBundle requires resolved output paths")
184
+ }
185
+
186
+ if (!existsSync(outputPaths.frontendEntryFile) && !dryRun) {
187
+ throw new Error(
188
+ `Missing frontend build output at ${outputPaths.frontendEntryFile}. Run the frontend build first.`
189
+ )
190
+ }
191
+
192
+ logger.info(`Staging Windows app into ${outputPaths.appBundlePath}`)
193
+
194
+ if (dryRun) {
195
+ logger.info(`- copy ${runtimeSourceDir} -> ${outputPaths.appBundlePath}`)
196
+ logger.info(`- rename ${runtimeSourceBinary} -> ${outputPaths.appExecutablePath}`)
197
+ logger.info(`- copy ${configPath} -> ${outputPaths.bundledConfigPath}`)
198
+ logger.info(`- copy ${outputPaths.frontendDistDir} -> ${outputPaths.bundledFrontendDir}`)
199
+ if (backendArtifact) {
200
+ logger.info(`- copy ${backendArtifact.bundleEntryPath} -> ${outputPaths.bundledBackendEntryPath}`)
201
+ logger.info(`- copy ${backendArtifact.runnerPath} -> ${outputPaths.bundledBackendRunnerPath}`)
202
+ logger.info(`- copy ${backendArtifact.supportModulePath} -> ${outputPaths.bundledBackendSupportModulePath}`)
203
+ logger.info(`- copy ${backendArtifact.runtimeExecutablePath} -> ${outputPaths.bundledBackendRuntimePath}`)
204
+ }
205
+ return outputPaths
206
+ }
207
+
208
+ await mkdir(outputPaths.windowsDir, { recursive: true })
209
+ await rm(outputPaths.appBundlePath, { recursive: true, force: true })
210
+ await mkdir(outputPaths.appBundlePath, { recursive: true })
211
+ const runtimeEntries = await readdir(runtimeSourceDir, { withFileTypes: true })
212
+ const runtimeBinaryName = path.basename(runtimeSourceBinary)
213
+
214
+ for (const entry of runtimeEntries) {
215
+ if (!entry.isDirectory() && !shouldCopyWindowsRuntimeArtifact(entry.name)) {
216
+ continue
217
+ }
218
+
219
+ const sourcePath = path.join(runtimeSourceDir, entry.name)
220
+ const targetPath =
221
+ entry.name.toLowerCase() === runtimeBinaryName.toLowerCase()
222
+ ? outputPaths.appExecutablePath
223
+ : path.join(outputPaths.appBundlePath, entry.name)
224
+
225
+ await cp(sourcePath, targetPath, { recursive: true, force: true })
226
+ }
227
+
228
+ await mkdir(outputPaths.resourcesDir, { recursive: true })
229
+ await cp(configPath, outputPaths.bundledConfigPath, { force: true })
230
+ await rm(outputPaths.bundledFrontendDir, { recursive: true, force: true })
231
+ await cp(outputPaths.frontendDistDir, outputPaths.bundledFrontendDir, { recursive: true })
232
+ await stageAppBackend(backendArtifact, outputPaths)
233
+
234
+ return outputPaths
235
+ }
236
+
237
+ export async function createMacOSZipArchive(outputPaths, options = {}) {
238
+ const { dryRun = false } = options
239
+
240
+ logger.info(`Packaging ${outputPaths.appBundlePath} -> ${outputPaths.zipPath}`)
241
+
242
+ if (process.platform !== "darwin") {
243
+ throw new Error("Packaging is only implemented for macOS right now")
244
+ }
245
+
246
+ if (!dryRun) {
247
+ await mkdir(outputPaths.packagesDir, { recursive: true })
248
+ }
249
+
250
+ await runCommand(
251
+ "ditto",
252
+ ["-c", "-k", "--sequesterRsrc", "--keepParent", outputPaths.appBundlePath, outputPaths.zipPath],
253
+ { dryRun }
254
+ )
255
+ }
256
+
257
+ export async function createWindowsZipArchive(outputPaths, options = {}) {
258
+ const { dryRun = false } = options
259
+
260
+ logger.info(`Packaging ${outputPaths.appBundlePath} -> ${outputPaths.zipPath}`)
261
+
262
+ if (process.platform !== "win32") {
263
+ throw new Error("Packaging is only implemented for Windows and macOS right now")
264
+ }
265
+
266
+ if (!dryRun) {
267
+ await mkdir(outputPaths.packagesDir, { recursive: true })
268
+ await rm(outputPaths.zipPath, { force: true })
269
+ }
270
+
271
+ const literalSourcePath = escapePowerShellLiteral(outputPaths.appBundlePath)
272
+ const literalDestinationPath = escapePowerShellLiteral(outputPaths.zipPath)
273
+ const archiveCommand = [
274
+ "$ErrorActionPreference = 'Stop'",
275
+ `Compress-Archive -LiteralPath '${literalSourcePath}' -DestinationPath '${literalDestinationPath}' -CompressionLevel Optimal`
276
+ ].join("; ")
277
+
278
+ await runCommand(
279
+ "powershell.exe",
280
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", archiveCommand],
281
+ { dryRun }
282
+ )
283
+ }