/
ElectronFramework.ts
191 lines (165 loc) · 6.93 KB
/
ElectronFramework.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import BluebirdPromise from "bluebird-lst"
import { asArray, executeAppBuilder, log } from "builder-util"
import { CONCURRENCY, copyDir, DO_NOT_USE_HARD_LINKS, statOrNull, unlinkIfExists } from "builder-util/out/fs"
import { emptyDir, readdir, remove, rename } from "fs-extra"
import { Lazy } from "lazy-val"
import * as path from "path"
import { Configuration } from "../configuration"
import { BeforeCopyExtraFilesOptions, Framework, PrepareApplicationStageDirectoryOptions } from "../Framework"
import { Packager, Platform } from "../index"
import { LinuxPackager } from "../linuxPackager"
import MacPackager from "../macPackager"
import { isSafeToUnpackElectronOnRemoteBuildServer } from "../platformPackager"
import { getTemplatePath } from "../util/pathManager"
import { createMacApp } from "./electronMac"
import { computeElectronVersion, getElectronVersionFromInstalled } from "./electronVersion"
export type ElectronPlatformName = "darwin" | "linux" | "win32" | "mas"
export interface ElectronDownloadOptions {
// https://github.com/electron-userland/electron-builder/issues/3077
// must be optional
version?: string
/**
* The [cache location](https://github.com/electron-userland/electron-download#cache-location).
*/
cache?: string | null
/**
* The mirror.
*/
mirror?: string | null
/** @private */
customDir?: string | null
/** @private */
customFilename?: string | null
strictSSL?: boolean
isVerifyChecksum?: boolean
platform?: ElectronPlatformName
arch?: string
}
function createDownloadOpts(opts: Configuration, platform: ElectronPlatformName, arch: string, electronVersion: string): ElectronDownloadOptions {
return {
platform,
arch,
version: electronVersion,
...opts.electronDownload,
}
}
async function beforeCopyExtraFiles(options: BeforeCopyExtraFilesOptions) {
const packager = options.packager
const appOutDir = options.appOutDir
if (packager.platform === Platform.LINUX) {
if (!isSafeToUnpackElectronOnRemoteBuildServer(packager)) {
const linuxPackager = (packager as LinuxPackager)
const executable = path.join(appOutDir, linuxPackager.executableName)
await rename(path.join(appOutDir, "electron"), executable)
}
}
else if (packager.platform === Platform.WINDOWS) {
const executable = path.join(appOutDir, `${packager.appInfo.productFilename}.exe`)
await rename(path.join(appOutDir, "electron.exe"), executable)
}
else {
await createMacApp(packager as MacPackager, appOutDir, options.asarIntegrity, (options.platformName as ElectronPlatformName) === "mas")
const wantedLanguages = asArray(packager.platformSpecificBuildOptions.electronLanguages)
if (wantedLanguages.length === 0) {
return
}
// noinspection SpellCheckingInspection
const langFileExt = ".lproj"
const resourcesDir = packager.getResourcesDir(appOutDir)
await BluebirdPromise.map(readdir(resourcesDir), file => {
if (!file.endsWith(langFileExt)) {
return
}
const language = file.substring(0, file.length - langFileExt.length)
if (!wantedLanguages.includes(language)) {
return remove(path.join(resourcesDir, file))
}
return
}, CONCURRENCY)
}
}
class ElectronFramework implements Framework {
// noinspection JSUnusedGlobalSymbols
readonly macOsDefaultTargets = ["zip", "dmg"]
// noinspection JSUnusedGlobalSymbols
readonly defaultAppIdPrefix = "com.electron."
// noinspection JSUnusedGlobalSymbols
readonly isCopyElevateHelper = true
// noinspection JSUnusedGlobalSymbols
readonly isNpmRebuildRequired = true
constructor(readonly name: string, readonly version: string, readonly distMacOsAppName: string) {
}
getDefaultIcon(platform: Platform) {
if (platform === Platform.LINUX) {
return path.join(getTemplatePath("icons"), "electron-linux")
}
else {
// default icon is embedded into app skeleton
return null
}
}
prepareApplicationStageDirectory(options: PrepareApplicationStageDirectoryOptions) {
return unpack(options, createDownloadOpts(options.packager.config, options.platformName, options.arch, this.version), this.distMacOsAppName)
}
beforeCopyExtraFiles(options: BeforeCopyExtraFilesOptions) {
return beforeCopyExtraFiles(options)
}
}
export async function createElectronFrameworkSupport(configuration: Configuration, packager: Packager): Promise<Framework> {
let version = configuration.electronVersion
if (version == null) {
// for prepacked app asar no dev deps in the app.asar
if (packager.isPrepackedAppAsar) {
version = await getElectronVersionFromInstalled(packager.projectDir)
if (version == null) {
throw new Error(`Cannot compute electron version for prepacked asar`)
}
}
else {
version = await computeElectronVersion(packager.projectDir, new Lazy(() => Promise.resolve(packager.metadata)))
}
configuration.electronVersion = version
}
return new ElectronFramework("electron", version, "Electron.app")
}
async function unpack(prepareOptions: PrepareApplicationStageDirectoryOptions, options: ElectronDownloadOptions, distMacOsAppName: string) {
const packager = prepareOptions.packager
const out = prepareOptions.appOutDir
let dist: string | null | undefined = packager.config.electronDist
if (dist != null) {
const zipFile = `electron-v${options.version}-${prepareOptions.platformName}-${options.arch}.zip`
const resolvedDist = path.resolve(packager.projectDir, dist)
if ((await statOrNull(path.join(resolvedDist, zipFile))) != null) {
options.cache = resolvedDist
dist = null
}
}
let isFullCleanup = false
if (dist == null) {
if (isSafeToUnpackElectronOnRemoteBuildServer(packager)) {
return
}
await executeAppBuilder(["unpack-electron", "--configuration", JSON.stringify([options]), "--output", out, "--distMacOsAppName", distMacOsAppName])
}
else {
isFullCleanup = true
const source = packager.getElectronSrcDir(dist)
const destination = packager.getElectronDestinationDir(out)
log.info({source, destination}, "copying Electron")
await emptyDir(out)
await copyDir(source, destination, {
isUseHardLink: DO_NOT_USE_HARD_LINKS,
})
}
await cleanupAfterUnpack(prepareOptions, distMacOsAppName, isFullCleanup)
}
function cleanupAfterUnpack(prepareOptions: PrepareApplicationStageDirectoryOptions, distMacOsAppName: string, isFullCleanup: boolean) {
const out = prepareOptions.appOutDir
const isMac = prepareOptions.packager.platform === Platform.MAC
const resourcesPath = isMac ? path.join(out, distMacOsAppName, "Contents", "Resources") : path.join(out, "resources")
return Promise.all([
isFullCleanup ? unlinkIfExists(path.join(resourcesPath, "default_app.asar")) : Promise.resolve(),
isFullCleanup ? unlinkIfExists(path.join(out, "version")) : Promise.resolve(),
isMac ? Promise.resolve() : rename(path.join(out, "LICENSE"), path.join(out, "LICENSE.electron.txt")).catch(() => {/* ignore */}),
])
}