reset-framework-cli 1.1.2 → 1.1.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.
Files changed (34) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +47 -47
  3. package/package.json +4 -4
  4. package/src/commands/build.js +114 -113
  5. package/src/commands/dev.js +148 -144
  6. package/src/commands/doctor.js +89 -89
  7. package/src/commands/init.js +838 -903
  8. package/src/commands/package.js +49 -44
  9. package/src/index.js +213 -213
  10. package/src/lib/context.js +66 -66
  11. package/src/lib/framework.js +150 -28
  12. package/src/lib/logger.js +11 -11
  13. package/src/lib/output.js +214 -106
  14. package/src/lib/process.js +253 -156
  15. package/src/lib/project.js +559 -475
  16. package/src/lib/toolchain.js +62 -62
  17. package/src/lib/ui.js +244 -244
  18. package/templates/basic/README.md +15 -15
  19. package/templates/basic/frontend/README.md +73 -73
  20. package/templates/basic/frontend/eslint.config.js +23 -23
  21. package/templates/basic/frontend/index.html +13 -13
  22. package/templates/basic/frontend/package.json +31 -31
  23. package/templates/basic/frontend/public/icons.svg +24 -24
  24. package/templates/basic/frontend/src/App.css +216 -138
  25. package/templates/basic/frontend/src/App.tsx +77 -76
  26. package/templates/basic/frontend/src/assets/vite.svg +1 -1
  27. package/templates/basic/frontend/src/index.css +111 -111
  28. package/templates/basic/frontend/src/lib/reset.ts +1 -1
  29. package/templates/basic/frontend/src/main.tsx +10 -10
  30. package/templates/basic/frontend/tsconfig.app.json +28 -28
  31. package/templates/basic/frontend/tsconfig.json +7 -7
  32. package/templates/basic/frontend/tsconfig.node.json +26 -26
  33. package/templates/basic/frontend/vite.config.ts +16 -16
  34. package/templates/basic/reset.config.json +58 -58
@@ -1,54 +1,176 @@
1
- import { runCommand } from "./process.js"
1
+ import { existsSync } from "node:fs"
2
+ import { rm } from "node:fs/promises"
3
+ import { readFile } from "node:fs/promises"
4
+ import path from "node:path"
5
+
6
+ import { captureCommandOutput, runCommand } from "./process.js"
2
7
  import { ensureVcpkgToolchain } from "./toolchain.js"
8
+
9
+ function getBuildDirectory(frameworkBuildPaths, mode) {
10
+ return mode === "release"
11
+ ? frameworkBuildPaths.releaseBuildDir
12
+ : frameworkBuildPaths.devBuildDir
13
+ }
14
+
15
+ function getBuildType(mode) {
16
+ return mode === "release" ? "Release" : "Debug"
17
+ }
3
18
 
4
- function getBuildDirectory(frameworkBuildPaths, mode) {
5
- return mode === "release"
6
- ? frameworkBuildPaths.releaseBuildDir
7
- : frameworkBuildPaths.devBuildDir
19
+ function getVsWherePath() {
20
+ const candidates = [
21
+ path.join(process.env["ProgramFiles(x86)"] ?? "", "Microsoft Visual Studio", "Installer", "vswhere.exe"),
22
+ path.join(process.env.ProgramFiles ?? "", "Microsoft Visual Studio", "Installer", "vswhere.exe")
23
+ ]
24
+
25
+ return candidates.find((candidate) => candidate && existsSync(candidate)) ?? null
8
26
  }
9
27
 
10
- function getBuildType(mode) {
11
- return mode === "release" ? "Release" : "Debug"
28
+ async function findVisualStudioGenerator() {
29
+ const vswherePath = getVsWherePath()
30
+ if (!vswherePath) {
31
+ return null
32
+ }
33
+
34
+ try {
35
+ const version = await captureCommandOutput(vswherePath, [
36
+ "-latest",
37
+ "-products",
38
+ "*",
39
+ "-requires",
40
+ "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
41
+ "-property",
42
+ "catalog_productLineVersion"
43
+ ])
44
+
45
+ const normalized = String(version).trim()
46
+ if (normalized === "2022") {
47
+ return "Visual Studio 17 2022"
48
+ }
49
+
50
+ if (normalized === "2019") {
51
+ return "Visual Studio 16 2019"
52
+ }
53
+ } catch {
54
+ // Fall through to the default Windows generator if detection fails.
55
+ }
56
+
57
+ return "Visual Studio 17 2022"
58
+ }
59
+
60
+ async function resolveCmakeGenerator() {
61
+ const envGenerator =
62
+ typeof process.env.CMAKE_GENERATOR === "string" && process.env.CMAKE_GENERATOR.trim() !== ""
63
+ ? process.env.CMAKE_GENERATOR.trim()
64
+ : null
65
+
66
+ if (envGenerator) {
67
+ return {
68
+ name: envGenerator,
69
+ configureArgs: process.platform === "win32" &&
70
+ !process.env.CMAKE_GENERATOR_PLATFORM &&
71
+ envGenerator.startsWith("Visual Studio ")
72
+ ? ["-G", envGenerator, "-A", "x64"]
73
+ : ["-G", envGenerator],
74
+ buildArgs: envGenerator.startsWith("Visual Studio ")
75
+ ? ["--config"]
76
+ : [],
77
+ multiConfig: envGenerator.startsWith("Visual Studio ")
78
+ }
79
+ }
80
+
81
+ if (process.platform === "win32") {
82
+ const generator = await findVisualStudioGenerator()
83
+ if (!generator) {
84
+ throw new Error(
85
+ "Could not find a supported Visual Studio C++ installation. Install Visual Studio Build Tools or set CMAKE_GENERATOR manually."
86
+ )
87
+ }
88
+
89
+ return {
90
+ name: generator,
91
+ configureArgs: ["-G", generator, "-A", "x64"],
92
+ buildArgs: ["--config"],
93
+ multiConfig: true
94
+ }
95
+ }
96
+
97
+ return {
98
+ name: "Ninja",
99
+ configureArgs: ["-G", "Ninja"],
100
+ buildArgs: [],
101
+ multiConfig: false
102
+ }
103
+ }
104
+
105
+ async function ensureMatchingCmakeSource(buildDir, frameworkRoot) {
106
+ const cachePath = path.join(buildDir, "CMakeCache.txt")
107
+ if (!existsSync(cachePath)) {
108
+ return
109
+ }
110
+
111
+ let cache
112
+ try {
113
+ cache = await readFile(cachePath, "utf8")
114
+ } catch {
115
+ return
116
+ }
117
+
118
+ const match = cache.match(/^CMAKE_HOME_DIRECTORY:INTERNAL=(.+)$/m)
119
+ if (!match) {
120
+ return
121
+ }
122
+
123
+ const cachedRoot = path.resolve(match[1].trim())
124
+ const currentRoot = path.resolve(frameworkRoot)
125
+
126
+ if (cachedRoot !== currentRoot) {
127
+ await rm(buildDir, { recursive: true, force: true })
128
+ }
12
129
  }
13
130
 
14
131
  export async function configureFrameworkBuild(frameworkPaths, frameworkBuildPaths, mode, options = {}) {
15
132
  const { dryRun = false, cwd } = options
16
133
  const toolchainFile = await ensureVcpkgToolchain({ dryRun })
17
134
  const buildDir = getBuildDirectory(frameworkBuildPaths, mode)
135
+ const generator = await resolveCmakeGenerator()
136
+ if (!dryRun) {
137
+ await ensureMatchingCmakeSource(buildDir, frameworkPaths.frameworkRoot)
138
+ }
139
+ const configureArgs = [
140
+ "-S",
141
+ frameworkPaths.frameworkRoot,
142
+ "-B",
143
+ buildDir,
144
+ ...generator.configureArgs,
145
+ `-DCMAKE_BUILD_TYPE=${getBuildType(mode)}`,
146
+ "-DCMAKE_CXX_STANDARD=20",
147
+ "-DCMAKE_CXX_STANDARD_REQUIRED=ON",
148
+ "-DCMAKE_CXX_EXTENSIONS=OFF",
149
+ "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
150
+ `-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}`
151
+ ]
18
152
 
19
- await runCommand(
20
- "cmake",
21
- [
22
- "-S",
23
- frameworkPaths.frameworkRoot,
24
- "-B",
25
- buildDir,
26
- "-G",
27
- "Ninja",
28
- `-DCMAKE_BUILD_TYPE=${getBuildType(mode)}`,
29
- "-DCMAKE_CXX_STANDARD=20",
30
- "-DCMAKE_CXX_STANDARD_REQUIRED=ON",
31
- "-DCMAKE_CXX_EXTENSIONS=OFF",
32
- "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
33
- `-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}`
34
- ],
35
- {
153
+ try {
154
+ await runCommand("cmake", configureArgs, {
36
155
  cwd,
37
156
  dryRun
38
- }
39
- )
157
+ })
158
+ } catch (error) {
159
+ throw error
160
+ }
40
161
  }
41
-
162
+
42
163
  export async function buildFrameworkRuntime(frameworkPaths, frameworkBuildPaths, mode, options = {}) {
43
164
  const { dryRun = false, cwd } = options
44
165
  const buildDir = getBuildDirectory(frameworkBuildPaths, mode)
166
+ const generator = await resolveCmakeGenerator()
45
167
 
46
168
  await configureFrameworkBuild(frameworkPaths, frameworkBuildPaths, mode, {
47
169
  cwd,
48
170
  dryRun
49
171
  })
50
172
 
51
- await runCommand("cmake", ["--build", buildDir], {
173
+ await runCommand("cmake", ["--build", buildDir, ...generator.buildArgs, ...(generator.multiConfig ? [getBuildType(mode)] : [])], {
52
174
  cwd,
53
175
  dryRun
54
176
  })
package/src/lib/logger.js CHANGED
@@ -1,11 +1,11 @@
1
- export const logger = {
2
- info(message) {
3
- console.log(message)
4
- },
5
- warn(message) {
6
- console.warn(message)
7
- },
8
- error(message) {
9
- console.error(message)
10
- }
11
- }
1
+ export const logger = {
2
+ info(message) {
3
+ console.log(message)
4
+ },
5
+ warn(message) {
6
+ console.warn(message)
7
+ },
8
+ error(message) {
9
+ console.error(message)
10
+ }
11
+ }
package/src/lib/output.js CHANGED
@@ -1,110 +1,178 @@
1
1
  import path from "node:path"
2
- import { cp, mkdir, rm, writeFile } from "node:fs/promises"
2
+ import { cp, mkdir, readdir, rm, writeFile } from "node:fs/promises"
3
3
  import { existsSync } from "node:fs"
4
4
 
5
5
  import { logger } from "./logger.js"
6
6
  import { runCommand } from "./process.js"
7
-
8
- function escapePlistString(value) {
9
- return value
10
- .replaceAll("&", "&")
11
- .replaceAll("<", "&lt;")
12
- .replaceAll(">", "&gt;")
7
+ import {
8
+ resolveFrameworkRuntimeBinary,
9
+ resolveFrameworkRuntimeOutputDir
10
+ } from "./project.js"
11
+
12
+ function escapePlistString(value) {
13
+ return value
14
+ .replaceAll("&", "&amp;")
15
+ .replaceAll("<", "&lt;")
16
+ .replaceAll(">", "&gt;")
17
+ }
18
+
19
+ function serializePlistValue(value, indentLevel = 1) {
20
+ const indent = " ".repeat(indentLevel)
21
+
22
+ if (Array.isArray(value)) {
23
+ if (value.length === 0) {
24
+ return `${indent}<array/>\n`
25
+ }
26
+
27
+ return (
28
+ `${indent}<array>\n` +
29
+ value.map((entry) => serializePlistValue(entry, indentLevel + 1)).join("") +
30
+ `${indent}</array>\n`
31
+ )
32
+ }
33
+
34
+ if (typeof value === "object" && value !== null) {
35
+ const entries = Object.entries(value)
36
+ if (entries.length === 0) {
37
+ return `${indent}<dict/>\n`
38
+ }
39
+
40
+ return (
41
+ `${indent}<dict>\n` +
42
+ entries
43
+ .map(
44
+ ([key, entryValue]) =>
45
+ `${" ".repeat(indentLevel + 1)}<key>${escapePlistString(key)}</key>\n${serializePlistValue(entryValue, indentLevel + 1)}`
46
+ )
47
+ .join("") +
48
+ `${indent}</dict>\n`
49
+ )
50
+ }
51
+
52
+ if (typeof value === "boolean") {
53
+ return `${indent}<${value ? "true" : "false"}/>\n`
54
+ }
55
+
56
+ if (typeof value === "number") {
57
+ return `${indent}<integer>${value}</integer>\n`
58
+ }
59
+
60
+ return `${indent}<string>${escapePlistString(String(value))}</string>\n`
61
+ }
62
+
63
+ function buildMacOSInfoPlist(config) {
64
+ const urlTypes = Array.isArray(config.protocols?.schemes)
65
+ ? config.protocols.schemes.map((schemeConfig) => ({
66
+ CFBundleTypeRole: schemeConfig.role ?? "Viewer",
67
+ CFBundleURLName:
68
+ typeof schemeConfig.name === "string" && schemeConfig.name.trim() !== ""
69
+ ? schemeConfig.name
70
+ : `${config.appId}.${schemeConfig.scheme}`,
71
+ CFBundleURLSchemes: [schemeConfig.scheme]
72
+ }))
73
+ : []
74
+
75
+ const plist = {
76
+ CFBundleDevelopmentRegion: "English",
77
+ CFBundleDisplayName: config.productName,
78
+ CFBundleExecutable: "reset-framework",
79
+ CFBundleIdentifier: config.appId,
80
+ CFBundleInfoDictionaryVersion: "6.0",
81
+ CFBundleName: config.productName,
82
+ CFBundlePackageType: "APPL",
83
+ CFBundleShortVersionString: config.version,
84
+ CFBundleVersion: config.version,
85
+ CSResourcesFileMapped: true
86
+ }
87
+
88
+ if (urlTypes.length > 0) {
89
+ plist.CFBundleURLTypes = urlTypes
90
+ }
91
+
92
+ return (
93
+ `<?xml version="1.0" encoding="UTF-8"?>\n` +
94
+ `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n` +
95
+ `<plist version="1.0">\n` +
96
+ serializePlistValue(plist, 0) +
97
+ `</plist>\n`
98
+ )
13
99
  }
14
100
 
15
- function serializePlistValue(value, indentLevel = 1) {
16
- const indent = " ".repeat(indentLevel)
17
-
18
- if (Array.isArray(value)) {
19
- if (value.length === 0) {
20
- return `${indent}<array/>\n`
21
- }
22
-
23
- return (
24
- `${indent}<array>\n` +
25
- value.map((entry) => serializePlistValue(entry, indentLevel + 1)).join("") +
26
- `${indent}</array>\n`
27
- )
28
- }
29
-
30
- if (typeof value === "object" && value !== null) {
31
- const entries = Object.entries(value)
32
- if (entries.length === 0) {
33
- return `${indent}<dict/>\n`
34
- }
35
-
36
- return (
37
- `${indent}<dict>\n` +
38
- entries
39
- .map(
40
- ([key, entryValue]) =>
41
- `${" ".repeat(indentLevel + 1)}<key>${escapePlistString(key)}</key>\n${serializePlistValue(entryValue, indentLevel + 1)}`
42
- )
43
- .join("") +
44
- `${indent}</dict>\n`
45
- )
46
- }
101
+ function shouldCopyWindowsRuntimeArtifact(entryName) {
102
+ const extension = path.extname(entryName).toLowerCase()
47
103
 
48
- if (typeof value === "boolean") {
49
- return `${indent}<${value ? "true" : "false"}/>\n`
50
- }
51
-
52
- if (typeof value === "number") {
53
- return `${indent}<integer>${value}</integer>\n`
54
- }
55
-
56
- return `${indent}<string>${escapePlistString(String(value))}</string>\n`
104
+ return [
105
+ ".bin",
106
+ ".dat",
107
+ ".dll",
108
+ ".exe",
109
+ ".json",
110
+ ".manifest",
111
+ ".pak",
112
+ ".pri"
113
+ ].includes(extension)
57
114
  }
58
115
 
59
- function buildMacOSInfoPlist(config) {
60
- const urlTypes = Array.isArray(config.protocols?.schemes)
61
- ? config.protocols.schemes.map((schemeConfig) => ({
62
- CFBundleTypeRole: schemeConfig.role ?? "Viewer",
63
- CFBundleURLName:
64
- typeof schemeConfig.name === "string" && schemeConfig.name.trim() !== ""
65
- ? schemeConfig.name
66
- : `${config.appId}.${schemeConfig.scheme}`,
67
- CFBundleURLSchemes: [schemeConfig.scheme]
68
- }))
69
- : []
70
-
71
- const plist = {
72
- CFBundleDevelopmentRegion: "English",
73
- CFBundleDisplayName: config.productName,
74
- CFBundleExecutable: "reset-framework",
75
- CFBundleIdentifier: config.appId,
76
- CFBundleInfoDictionaryVersion: "6.0",
77
- CFBundleName: config.productName,
78
- CFBundlePackageType: "APPL",
79
- CFBundleShortVersionString: config.version,
80
- CFBundleVersion: config.version,
81
- CSResourcesFileMapped: true
82
- }
83
-
84
- if (urlTypes.length > 0) {
85
- plist.CFBundleURLTypes = urlTypes
86
- }
87
-
88
- return (
89
- `<?xml version="1.0" encoding="UTF-8"?>\n` +
90
- `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n` +
91
- `<plist version="1.0">\n` +
92
- serializePlistValue(plist, 0) +
93
- `</plist>\n`
94
- )
116
+ function escapePowerShellLiteral(value) {
117
+ return String(value).replaceAll("'", "''")
95
118
  }
96
-
119
+
97
120
  export async function stageMacOSAppBundle(frameworkBuildPaths, appPaths, config, options = {}) {
121
+ const { dryRun = false, outputPaths, configPath } = options
122
+
123
+ if (!outputPaths) {
124
+ throw new Error("stageMacOSAppBundle requires resolved output paths")
125
+ }
126
+
127
+ if (!existsSync(frameworkBuildPaths.releaseAppTemplate) && !dryRun) {
128
+ throw new Error(
129
+ `Missing runtime app bundle at ${frameworkBuildPaths.releaseAppTemplate}. Run reset-framework-cli build first.`
130
+ )
131
+ }
132
+
133
+ if (!existsSync(outputPaths.frontendEntryFile) && !dryRun) {
134
+ throw new Error(
135
+ `Missing frontend build output at ${outputPaths.frontendEntryFile}. Run the frontend build first.`
136
+ )
137
+ }
138
+
139
+ logger.info(`Staging app bundle into ${outputPaths.appBundlePath}`)
140
+
141
+ if (dryRun) {
142
+ logger.info(`- copy ${frameworkBuildPaths.releaseAppTemplate} -> ${outputPaths.appBundlePath}`)
143
+ logger.info(`- copy ${configPath} -> ${outputPaths.bundledConfigPath}`)
144
+ logger.info(`- copy ${outputPaths.frontendDistDir} -> ${outputPaths.bundledFrontendDir}`)
145
+ return outputPaths
146
+ }
147
+
148
+ await mkdir(outputPaths.macosDir, { recursive: true })
149
+ await rm(outputPaths.appBundlePath, { recursive: true, force: true })
150
+ await cp(frameworkBuildPaths.releaseAppTemplate, outputPaths.appBundlePath, { recursive: true })
151
+
152
+ await mkdir(outputPaths.resourcesDir, { recursive: true })
153
+ await cp(configPath, outputPaths.bundledConfigPath, { force: true })
154
+ await rm(outputPaths.bundledFrontendDir, { recursive: true, force: true })
155
+ await cp(outputPaths.frontendDistDir, outputPaths.bundledFrontendDir, { recursive: true })
156
+ await writeFile(
157
+ path.join(outputPaths.appBundlePath, "Contents", "Info.plist"),
158
+ buildMacOSInfoPlist(config),
159
+ "utf8"
160
+ )
161
+
162
+ return outputPaths
163
+ }
164
+
165
+ export async function stageWindowsAppBundle(frameworkBuildPaths, appPaths, config, options = {}) {
98
166
  const { dryRun = false, outputPaths, configPath } = options
167
+ const runtimeSourceBinary = resolveFrameworkRuntimeBinary(frameworkBuildPaths, "release", {
168
+ mustExist: !dryRun
169
+ })
170
+ const runtimeSourceDir = resolveFrameworkRuntimeOutputDir(frameworkBuildPaths, "release", {
171
+ mustExist: !dryRun
172
+ })
99
173
 
100
174
  if (!outputPaths) {
101
- throw new Error("stageMacOSAppBundle requires resolved output paths")
102
- }
103
-
104
- if (!existsSync(frameworkBuildPaths.releaseAppTemplate) && !dryRun) {
105
- throw new Error(
106
- `Missing runtime app bundle at ${frameworkBuildPaths.releaseAppTemplate}. Run reset-framework-cli build first.`
107
- )
175
+ throw new Error("stageWindowsAppBundle requires resolved output paths")
108
176
  }
109
177
 
110
178
  if (!existsSync(outputPaths.frontendEntryFile) && !dryRun) {
@@ -113,48 +181,88 @@ export async function stageMacOSAppBundle(frameworkBuildPaths, appPaths, config,
113
181
  )
114
182
  }
115
183
 
116
- logger.info(`Staging app bundle into ${outputPaths.appBundlePath}`)
184
+ logger.info(`Staging Windows app into ${outputPaths.appBundlePath}`)
117
185
 
118
186
  if (dryRun) {
119
- logger.info(`- copy ${frameworkBuildPaths.releaseAppTemplate} -> ${outputPaths.appBundlePath}`)
187
+ logger.info(`- copy ${runtimeSourceDir} -> ${outputPaths.appBundlePath}`)
188
+ logger.info(`- rename ${runtimeSourceBinary} -> ${outputPaths.appExecutablePath}`)
120
189
  logger.info(`- copy ${configPath} -> ${outputPaths.bundledConfigPath}`)
121
190
  logger.info(`- copy ${outputPaths.frontendDistDir} -> ${outputPaths.bundledFrontendDir}`)
122
191
  return outputPaths
123
192
  }
124
193
 
125
- await mkdir(outputPaths.macosDir, { recursive: true })
194
+ await mkdir(outputPaths.windowsDir, { recursive: true })
126
195
  await rm(outputPaths.appBundlePath, { recursive: true, force: true })
127
- await cp(frameworkBuildPaths.releaseAppTemplate, outputPaths.appBundlePath, { recursive: true })
196
+ await mkdir(outputPaths.appBundlePath, { recursive: true })
197
+ const runtimeEntries = await readdir(runtimeSourceDir, { withFileTypes: true })
198
+ const runtimeBinaryName = path.basename(runtimeSourceBinary)
199
+
200
+ for (const entry of runtimeEntries) {
201
+ if (!entry.isDirectory() && !shouldCopyWindowsRuntimeArtifact(entry.name)) {
202
+ continue
203
+ }
204
+
205
+ const sourcePath = path.join(runtimeSourceDir, entry.name)
206
+ const targetPath =
207
+ entry.name.toLowerCase() === runtimeBinaryName.toLowerCase()
208
+ ? outputPaths.appExecutablePath
209
+ : path.join(outputPaths.appBundlePath, entry.name)
210
+
211
+ await cp(sourcePath, targetPath, { recursive: true, force: true })
212
+ }
128
213
 
129
214
  await mkdir(outputPaths.resourcesDir, { recursive: true })
130
215
  await cp(configPath, outputPaths.bundledConfigPath, { force: true })
131
216
  await rm(outputPaths.bundledFrontendDir, { recursive: true, force: true })
132
217
  await cp(outputPaths.frontendDistDir, outputPaths.bundledFrontendDir, { recursive: true })
133
- await writeFile(
134
- path.join(outputPaths.appBundlePath, "Contents", "Info.plist"),
135
- buildMacOSInfoPlist(config),
136
- "utf8"
137
- )
138
218
 
139
219
  return outputPaths
140
220
  }
141
-
221
+
142
222
  export async function createMacOSZipArchive(outputPaths, options = {}) {
143
223
  const { dryRun = false } = options
224
+
225
+ logger.info(`Packaging ${outputPaths.appBundlePath} -> ${outputPaths.zipPath}`)
226
+
227
+ if (process.platform !== "darwin") {
228
+ throw new Error("Packaging is only implemented for macOS right now")
229
+ }
230
+
231
+ if (!dryRun) {
232
+ await mkdir(outputPaths.packagesDir, { recursive: true })
233
+ }
234
+
235
+ await runCommand(
236
+ "ditto",
237
+ ["-c", "-k", "--sequesterRsrc", "--keepParent", outputPaths.appBundlePath, outputPaths.zipPath],
238
+ { dryRun }
239
+ )
240
+ }
241
+
242
+ export async function createWindowsZipArchive(outputPaths, options = {}) {
243
+ const { dryRun = false } = options
144
244
 
145
245
  logger.info(`Packaging ${outputPaths.appBundlePath} -> ${outputPaths.zipPath}`)
146
246
 
147
- if (process.platform !== "darwin") {
148
- throw new Error("Packaging is only implemented for macOS right now")
247
+ if (process.platform !== "win32") {
248
+ throw new Error("Packaging is only implemented for Windows and macOS right now")
149
249
  }
150
250
 
151
251
  if (!dryRun) {
152
252
  await mkdir(outputPaths.packagesDir, { recursive: true })
253
+ await rm(outputPaths.zipPath, { force: true })
153
254
  }
154
255
 
256
+ const literalSourcePath = escapePowerShellLiteral(outputPaths.appBundlePath)
257
+ const literalDestinationPath = escapePowerShellLiteral(outputPaths.zipPath)
258
+ const archiveCommand = [
259
+ "$ErrorActionPreference = 'Stop'",
260
+ `Compress-Archive -LiteralPath '${literalSourcePath}' -DestinationPath '${literalDestinationPath}' -CompressionLevel Optimal`
261
+ ].join("; ")
262
+
155
263
  await runCommand(
156
- "ditto",
157
- ["-c", "-k", "--sequesterRsrc", "--keepParent", outputPaths.appBundlePath, outputPaths.zipPath],
264
+ "powershell.exe",
265
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", archiveCommand],
158
266
  { dryRun }
159
267
  )
160
268
  }