@lakutata/builder 3.0.0-beta.0
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/LICENSE +23 -0
- package/README.md +27 -0
- package/dist/esm/index.js +13 -0
- package/dist/esm/lib/Archive.js +21 -0
- package/dist/esm/lib/Bundle.js +162 -0
- package/dist/esm/lib/Executable.js +221 -0
- package/dist/esm/lib/Icons.js +372 -0
- package/dist/esm/lib/Installer.js +158 -0
- package/dist/esm/lib/Native.js +533 -0
- package/dist/esm/lib/NativeRuntime.js +177 -0
- package/dist/esm/lib/Platform.js +9 -0
- package/dist/esm/lib/Processes.js +42 -0
- package/dist/esm/lib/Scripts.js +161 -0
- package/dist/esm/lib/Tools.js +109 -0
- package/dist/esm/lib/electron/Electron.js +366 -0
- package/dist/esm/lib/linux/Linux.js +361 -0
- package/dist/esm/lib/mac/Mac.js +270 -0
- package/dist/esm/lib/win/Windows.js +525 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/lib/Archive.d.ts +14 -0
- package/dist/types/lib/Bundle.d.ts +96 -0
- package/dist/types/lib/Executable.d.ts +50 -0
- package/dist/types/lib/Icons.d.ts +70 -0
- package/dist/types/lib/Installer.d.ts +242 -0
- package/dist/types/lib/Native.d.ts +196 -0
- package/dist/types/lib/NativeRuntime.d.ts +75 -0
- package/dist/types/lib/Platform.d.ts +4 -0
- package/dist/types/lib/Processes.d.ts +21 -0
- package/dist/types/lib/Scripts.d.ts +77 -0
- package/dist/types/lib/Tools.d.ts +32 -0
- package/dist/types/lib/electron/Electron.d.ts +13 -0
- package/dist/types/lib/linux/Linux.d.ts +29 -0
- package/dist/types/lib/mac/Mac.d.ts +19 -0
- package/dist/types/lib/win/Windows.d.ts +20 -0
- package/package.json +51 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { chmod, mkdir, mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { bundleProject } from '../Bundle.js';
|
|
7
|
+
import { applicationIcon, icnsFile, icoFile, writeLinuxIcons } from '../Icons.js';
|
|
8
|
+
import { scriptAction } from '../linux/Linux.js';
|
|
9
|
+
import { embeddedShell, encodedShell, INSTALLER_HOOKS, resolveScripts } from '../Scripts.js';
|
|
10
|
+
/**
|
|
11
|
+
* The installers of the Electron applications by default, the ones of electron-builder (its targets) otherwise
|
|
12
|
+
*/
|
|
13
|
+
const ELECTRON_TARGETS = { linux: ['deb'], mac: ['dmg'], win: ['nsis'] };
|
|
14
|
+
/**
|
|
15
|
+
* The moments the installers of electron-builder run the scripts of the application at: the packages of fpm (deb, rpm)
|
|
16
|
+
* all of them; the pkg package of macOS the ones of the installation (an application of macOS is removed by its
|
|
17
|
+
* user); the installer of NSIS but the postremove one (its uninstaller ends with the removal of the files)
|
|
18
|
+
* @param platform
|
|
19
|
+
*/
|
|
20
|
+
function electronHooks(platform) {
|
|
21
|
+
return (target) => {
|
|
22
|
+
if (platform === 'linux')
|
|
23
|
+
return target === 'deb' || target === 'rpm' ? INSTALLER_HOOKS : [];
|
|
24
|
+
if (platform === 'mac')
|
|
25
|
+
return target === 'pkg' ? ['preinstall', 'postinstall'] : [];
|
|
26
|
+
return target === 'nsis' ? ['preinstall', 'postinstall', 'preremove'] : [];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A configuration of electron-builder over another one: its objects merged, its other values replacing
|
|
31
|
+
* @param base
|
|
32
|
+
* @param over
|
|
33
|
+
*/
|
|
34
|
+
function merge(base, over) {
|
|
35
|
+
const merged = { ...base };
|
|
36
|
+
for (const [key, value] of Object.entries(over)) {
|
|
37
|
+
const plain = (item) => !!item && typeof item === 'object' && !Array.isArray(item);
|
|
38
|
+
merged[key] = plain(value) && plain(merged[key]) ? merge(merged[key], value) : value;
|
|
39
|
+
}
|
|
40
|
+
return merged;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The maintainer of the packages of Linux, which fpm needs with its email ("Name <email>")
|
|
44
|
+
* @param maintainer
|
|
45
|
+
* @param name
|
|
46
|
+
* @param warnings
|
|
47
|
+
*/
|
|
48
|
+
function packageMaintainer(maintainer, name, warnings) {
|
|
49
|
+
if (maintainer && /<[^>]+@[^>]+>/.test(maintainer))
|
|
50
|
+
return maintainer;
|
|
51
|
+
warnings.push(`The packages of Linux need the email of their maintainer ("Name <email>": the author of the package.json, or lakutata.installer.maintainer): ${name}@localhost is given`);
|
|
52
|
+
return `${maintainer || name} <${name}@localhost>`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The scripts of the packages of fpm (deb, rpm) running the scripts of the application: the ones run before the
|
|
56
|
+
* installation and the removal (given to fpm), the ones run after them (templates of electron-builder: its own ones, then
|
|
57
|
+
* the ones of the application, encoded)
|
|
58
|
+
* @param target
|
|
59
|
+
* @param scripts
|
|
60
|
+
* @param prefix the directory of the application (/opt/<product>)
|
|
61
|
+
* @param templates the directory of the templates of electron-builder
|
|
62
|
+
* @param directory where they are written
|
|
63
|
+
*/
|
|
64
|
+
async function fpmScripts(target, scripts, prefix, templates, directory) {
|
|
65
|
+
const options = {};
|
|
66
|
+
const fpm = [];
|
|
67
|
+
await mkdir(directory, { recursive: true });
|
|
68
|
+
const part = (hook, script) => [scriptAction(target, hook), 'if [ -n "$lakutata_action" ]; then', ...encodedShell(script, prefix, '"$lakutata_action"'), 'fi'];
|
|
69
|
+
for (const hook of INSTALLER_HOOKS) {
|
|
70
|
+
const script = scripts.hooks[hook];
|
|
71
|
+
if (!script)
|
|
72
|
+
continue;
|
|
73
|
+
const file = path.join(directory, `${target}-${hook}`);
|
|
74
|
+
if (hook === 'preinstall' || hook === 'preremove') {
|
|
75
|
+
await writeFile(file, ['#!/bin/sh', 'set -e', ...part(hook, script), ''].join('\n'), { mode: 0o755 });
|
|
76
|
+
fpm.push(hook === 'preinstall' ? '--before-install' : '--before-remove', file);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
//The template of electron-builder (the link of the program, the sandbox of Chromium…), then the script
|
|
80
|
+
const template = readFileSync(path.join(templates, hook === 'postinstall' ? 'after-install.tpl' : 'after-remove.tpl'), 'utf8');
|
|
81
|
+
await writeFile(file, [template.trimEnd(), '', '# The script of the application', ...part(hook, script), ''].join('\n'), { mode: 0o755 });
|
|
82
|
+
options[hook === 'postinstall' ? 'afterInstall' : 'afterRemove'] = file;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (fpm.length)
|
|
86
|
+
options.fpm = fpm;
|
|
87
|
+
return options;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The scripts of the pkg package of macOS (pkgbuild): what the installation does (the application installed replaced by
|
|
91
|
+
* an upgrade), told by the preinstall script to the postinstall one, and the ones of the application; each run once
|
|
92
|
+
* @param scripts
|
|
93
|
+
* @param prefix the application (/Applications/<product>.app)
|
|
94
|
+
* @param directory where they are written
|
|
95
|
+
*/
|
|
96
|
+
async function pkgScripts(scripts, prefix, directory) {
|
|
97
|
+
await mkdir(directory, { recursive: true });
|
|
98
|
+
const action = '"$INSTALLER_TEMP/lakutata-action"';
|
|
99
|
+
const quoted = `'${prefix.replace(/'/g, `'\\''`)}'`;
|
|
100
|
+
//electron-builder gives them as the scripts of the package and as the ones of its bundle: run once
|
|
101
|
+
const once = (name) => [`if [ -n "$INSTALLER_TEMP" ]; then [ -e "$INSTALLER_TEMP/lakutata-${name}" ] && exit 0; touch "$INSTALLER_TEMP/lakutata-${name}"; fi`];
|
|
102
|
+
await writeFile(path.join(directory, 'preinstall'), [
|
|
103
|
+
'#!/bin/sh',
|
|
104
|
+
'set -e',
|
|
105
|
+
...once('preinstall'),
|
|
106
|
+
`if [ -e ${quoted} ]; then lakutata_action=upgrade; else lakutata_action=install; fi`,
|
|
107
|
+
`if [ -n "$INSTALLER_TEMP" ]; then echo "$lakutata_action" > ${action}; fi`,
|
|
108
|
+
...scripts.hooks.preinstall ? embeddedShell(scripts.hooks.preinstall, prefix, '"$lakutata_action"') : [],
|
|
109
|
+
''
|
|
110
|
+
].join('\n'));
|
|
111
|
+
await chmod(path.join(directory, 'preinstall'), 0o755);
|
|
112
|
+
if (scripts.hooks.postinstall) {
|
|
113
|
+
await writeFile(path.join(directory, 'postinstall'), [
|
|
114
|
+
'#!/bin/sh',
|
|
115
|
+
'set -e',
|
|
116
|
+
...once('postinstall'),
|
|
117
|
+
`lakutata_action="$(cat ${action} 2>/dev/null || echo install)"`,
|
|
118
|
+
...embeddedShell(scripts.hooks.postinstall, prefix, '"$lakutata_action"'),
|
|
119
|
+
''
|
|
120
|
+
].join('\n'));
|
|
121
|
+
await chmod(path.join(directory, 'postinstall'), 0o755);
|
|
122
|
+
}
|
|
123
|
+
return directory;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The file of NSIS the installer of electron-builder includes: the one of the application, and its macros running the
|
|
127
|
+
* scripts of the application (from its temporary directory): customInit the preinstall one (an upgrade finds the
|
|
128
|
+
* application installed), customInstall the postinstall one, customUnInstall the preremove one (not the one of an
|
|
129
|
+
* upgrade, which runs the uninstaller of the version it replaces)
|
|
130
|
+
* @param scripts
|
|
131
|
+
* @param directory where it is written, with the scripts (marked as UTF-8 for Windows PowerShell)
|
|
132
|
+
*/
|
|
133
|
+
async function nsisInclude(scripts, directory) {
|
|
134
|
+
const hooks = scripts.hooks;
|
|
135
|
+
if (!hooks.preinstall && !hooks.postinstall && !hooks.preremove)
|
|
136
|
+
return scripts.include && path.resolve(scripts.include.file);
|
|
137
|
+
await mkdir(directory, { recursive: true });
|
|
138
|
+
const macros = { customInit: hooks.preinstall || hooks.postinstall ? 'preinstall' : undefined, customInstall: hooks.postinstall && 'postinstall', customUnInstall: hooks.preremove && 'preremove' };
|
|
139
|
+
for (const [macro, hook] of Object.entries(macros)) {
|
|
140
|
+
if (hook && scripts.include && new RegExp(`!macro\\s+${macro}\\b`).test(scripts.include.content))
|
|
141
|
+
throw new Error(`The file of NSIS ${scripts.include.file} defines the macro ${macro}, which runs the ${hook} script of the application: run its code from the script`);
|
|
142
|
+
}
|
|
143
|
+
const run = (hook, action) => {
|
|
144
|
+
const file = path.join(directory, `${hook}.ps1`);
|
|
145
|
+
return [
|
|
146
|
+
' InitPluginsDir',
|
|
147
|
+
' System::Call \'Kernel32::SetEnvironmentVariable(t "LAKUTATA_INSTALL_DIR", t "$INSTDIR")i\'',
|
|
148
|
+
` File "/oname=$PLUGINSDIR\\${hook}.ps1" "${file.replace(/\$/g, '$$$$')}"`,
|
|
149
|
+
` nsExec::ExecToLog 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$PLUGINSDIR\\${hook}.ps1" ${action}'`,
|
|
150
|
+
' Pop $0',
|
|
151
|
+
' ${If} $0 != "0"',
|
|
152
|
+
` Abort "The ${hook} script failed ($0)"`,
|
|
153
|
+
' ${EndIf}'
|
|
154
|
+
];
|
|
155
|
+
};
|
|
156
|
+
for (const hook of ['preinstall', 'postinstall', 'preremove']) {
|
|
157
|
+
if (hooks[hook])
|
|
158
|
+
await writeFile(path.join(directory, `${hook}.ps1`), `\ufeff${hooks[hook].content}`);
|
|
159
|
+
}
|
|
160
|
+
const lines = [
|
|
161
|
+
...scripts.include ? [`!include "${scripts.include.file}"`, ''] : [],
|
|
162
|
+
'; What the installation does: an upgrade replaces the application installed (the uninstaller, made apart, has none)',
|
|
163
|
+
'!ifndef BUILD_UNINSTALLER',
|
|
164
|
+
' Var /GLOBAL lakutataAction',
|
|
165
|
+
'!endif',
|
|
166
|
+
'!macro customInit',
|
|
167
|
+
' StrCpy $lakutataAction "install"',
|
|
168
|
+
' ${If} ${FileExists} "$INSTDIR\\${APP_EXECUTABLE_FILENAME}"',
|
|
169
|
+
' StrCpy $lakutataAction "upgrade"',
|
|
170
|
+
' ${EndIf}',
|
|
171
|
+
...hooks.preinstall ? run('preinstall', '$lakutataAction') : [],
|
|
172
|
+
'!macroend',
|
|
173
|
+
...hooks.postinstall ? ['', '!macro customInstall', ...run('postinstall', '$lakutataAction'), '!macroend'] : [],
|
|
174
|
+
...hooks.preremove ? ['', '!macro customUnInstall', ' ${IfNot} ${isUpdated}', ...run('preremove', 'remove').map((line) => ` ${line}`), ' ${EndIf}', '!macroend'] : [],
|
|
175
|
+
''
|
|
176
|
+
];
|
|
177
|
+
const file = path.join(directory, 'installer.nsh');
|
|
178
|
+
await writeFile(file, lines.join('\r\n'));
|
|
179
|
+
return file;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Make the installers of an Electron application (lakutata.installer.runtime: electron) for the platform it is made on,
|
|
183
|
+
* by electron-builder: its main process bundled with its dependencies (lakutata package bundle), Electron left out; its native
|
|
184
|
+
* addons and its resources (lakutata.resources: its renderer, its preload script) next to its archive (app.asar), in the
|
|
185
|
+
* resources of Electron. The configuration of electron-builder is made from lakutata.installer, lakutata.installer.electron
|
|
186
|
+
* merged over it; the scripts of the application are run by its installers
|
|
187
|
+
* @param project
|
|
188
|
+
* @param manifest
|
|
189
|
+
* @param config
|
|
190
|
+
* @param options
|
|
191
|
+
*/
|
|
192
|
+
export async function buildElectronInstaller(project, manifest, config, options) {
|
|
193
|
+
if (config.service)
|
|
194
|
+
throw new Error('An Electron application is not a service (lakutata.installer.service): it runs in the session of its user');
|
|
195
|
+
const platform = process.platform === 'darwin' ? 'mac' : process.platform === 'win32' ? 'win' : 'linux';
|
|
196
|
+
const warnings = [];
|
|
197
|
+
const targets = options.targets?.length ? options.targets : config[platform]?.targets ?? ELECTRON_TARGETS[platform];
|
|
198
|
+
const nodeRequire = createRequire(path.join(project, 'package.json'));
|
|
199
|
+
let builder;
|
|
200
|
+
try {
|
|
201
|
+
builder = nodeRequire.resolve('electron-builder');
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
throw new Error('The installers of the Electron applications are made by electron-builder: install it (npm install --save-dev electron-builder)');
|
|
205
|
+
}
|
|
206
|
+
const electronBuilder = createRequire(builder)('electron-builder');
|
|
207
|
+
const templates = path.join(path.dirname(createRequire(builder).resolve('app-builder-lib/package.json')), 'templates', 'linux');
|
|
208
|
+
const { sanitizeFileName } = createRequire(builder)('builder-util/out/filename');
|
|
209
|
+
let electron;
|
|
210
|
+
try {
|
|
211
|
+
electron = path.dirname(nodeRequire.resolve('electron/package.json'));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
throw new Error('The Electron application has no electron package: install it (npm install --save-dev electron)');
|
|
215
|
+
}
|
|
216
|
+
const version = String(manifest.version ?? '');
|
|
217
|
+
if (!/^\d+\.\d+\.\d+/.test(version))
|
|
218
|
+
throw new Error(`The version of the application "${version}" must be a version (1.2.3) of the package.json`);
|
|
219
|
+
const name = config.name ?? String(manifest.name ?? 'app').replace(/^@[^/]+\//, '').toLowerCase();
|
|
220
|
+
const productName = config.productName ?? (typeof manifest.productName === 'string' ? manifest.productName : name);
|
|
221
|
+
const appId = config.appId ?? name;
|
|
222
|
+
const description = config.description ?? manifest.description ?? productName;
|
|
223
|
+
const author = config.maintainer ?? (typeof manifest.author === 'string' ? manifest.author : manifest.author?.name && (manifest.author.email ? `${manifest.author.name} <${manifest.author.email}>` : manifest.author.name));
|
|
224
|
+
//Where the installers put the application
|
|
225
|
+
const prefix = platform === 'linux' ? `/opt/${sanitizeFileName(productName)}` : platform === 'mac' ? `/Applications/${productName}.app` : '';
|
|
226
|
+
const scripts = resolveScripts(project, platform, config[platform], targets, warnings, electronHooks(platform), false);
|
|
227
|
+
const work = await mkdtemp(path.join(os.tmpdir(), 'lakutata-electron-'));
|
|
228
|
+
try {
|
|
229
|
+
//The application of electron-builder: its main process, its package.json; its native addons and its resources next to it
|
|
230
|
+
const application = path.join(work, 'app');
|
|
231
|
+
const bundle = await bundleProject({
|
|
232
|
+
project: project,
|
|
233
|
+
entry: options.entry,
|
|
234
|
+
minify: options.minify,
|
|
235
|
+
external: ['electron', ...options.external ?? []],
|
|
236
|
+
outfile: path.join(application, 'main.cjs'),
|
|
237
|
+
directories: { native: path.join(work, 'main.native'), resources: path.join(work, 'main.resources') }
|
|
238
|
+
});
|
|
239
|
+
const maintainer = platform === 'linux' ? packageMaintainer(author, name, warnings) : author ?? name;
|
|
240
|
+
//The packages of fpm need a homepage
|
|
241
|
+
let homepage = config.homepage ?? (typeof manifest.homepage === 'string' ? manifest.homepage : undefined);
|
|
242
|
+
if (!homepage && platform === 'linux') {
|
|
243
|
+
warnings.push('The packages of Linux need the homepage of the application (the homepage of the package.json, or lakutata.installer.homepage): NONE is given');
|
|
244
|
+
homepage = 'NONE';
|
|
245
|
+
}
|
|
246
|
+
await writeFile(path.join(application, 'package.json'), `${JSON.stringify({
|
|
247
|
+
name: name,
|
|
248
|
+
productName: productName,
|
|
249
|
+
version: version,
|
|
250
|
+
description: description,
|
|
251
|
+
author: maintainer,
|
|
252
|
+
homepage: homepage,
|
|
253
|
+
license: config.license ?? manifest.license,
|
|
254
|
+
//The name of the windows on Linux, which associates them with the launcher of the application
|
|
255
|
+
desktopName: `${name}.desktop`,
|
|
256
|
+
main: 'main.cjs'
|
|
257
|
+
}, null, 2)}\n`);
|
|
258
|
+
await writeFile(path.join(work, 'package.json'), `${JSON.stringify({ name: name, version: version, private: true }, null, 2)}\n`);
|
|
259
|
+
const outdir = path.resolve(project, options.outdir ?? path.join('dist', 'installer'));
|
|
260
|
+
const own = {};
|
|
261
|
+
for (const target of targets) {
|
|
262
|
+
const resolved = scripts[target];
|
|
263
|
+
if (!resolved)
|
|
264
|
+
continue;
|
|
265
|
+
if (platform === 'linux' && (target === 'deb' || target === 'rpm') && Object.keys(resolved.hooks).length)
|
|
266
|
+
own[target] = await fpmScripts(target, resolved, prefix, templates, path.join(work, 'scripts'));
|
|
267
|
+
if (platform === 'mac' && target === 'pkg' && Object.keys(resolved.hooks).length)
|
|
268
|
+
own.pkg = { scripts: await pkgScripts(resolved, prefix, path.join(work, 'pkg-scripts')) };
|
|
269
|
+
if (platform === 'win' && target === 'nsis') {
|
|
270
|
+
const include = await nsisInclude({ ...resolved, include: resolved.include && { ...resolved.include, file: path.resolve(project, resolved.include.file) } }, path.join(work, 'nsis'));
|
|
271
|
+
if (include)
|
|
272
|
+
own.nsis = { include: include };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
//The icon of the application, in the formats of the platform: icns (macOS), ico (Windows), the PNG of each size (Linux)
|
|
276
|
+
const appIcon = applicationIcon(project, manifest);
|
|
277
|
+
const icons = {};
|
|
278
|
+
if (appIcon) {
|
|
279
|
+
const directory = path.join(work, 'icons');
|
|
280
|
+
await mkdir(directory, { recursive: true });
|
|
281
|
+
if (platform === 'mac') {
|
|
282
|
+
if (appIcon.image.width < 1024)
|
|
283
|
+
warnings.push(`The icon of the application ${path.relative(project, appIcon.file)} is ${appIcon.image.width} pixels: 1024 make the icons of the Retina screens of macOS`);
|
|
284
|
+
await writeFile(path.join(directory, 'icon.icns'), icnsFile(appIcon.image));
|
|
285
|
+
icons.mac = { icon: path.join(directory, 'icon.icns') };
|
|
286
|
+
}
|
|
287
|
+
else if (platform === 'win') {
|
|
288
|
+
await writeFile(path.join(directory, 'icon.ico'), icoFile(appIcon.image));
|
|
289
|
+
icons.win = { icon: path.join(directory, 'icon.ico') };
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
await writeLinuxIcons(appIcon.image, path.join(directory, 'linux'));
|
|
293
|
+
icons.linux = { icon: path.join(directory, 'linux') };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const electronDist = path.join(electron, 'dist');
|
|
297
|
+
const generated = {
|
|
298
|
+
appId: appId,
|
|
299
|
+
productName: productName,
|
|
300
|
+
electronVersion: JSON.parse(readFileSync(path.join(electron, 'package.json'), 'utf8')).version,
|
|
301
|
+
//The Electron of the project (the one of the platform): not downloaded again
|
|
302
|
+
...existsSync(electronDist) ? { electronDist: electronDist } : {},
|
|
303
|
+
directories: { app: 'app', output: outdir },
|
|
304
|
+
files: ['main.cjs', 'package.json'],
|
|
305
|
+
extraResources: [
|
|
306
|
+
...bundle.nativeDirectory ? [{ from: bundle.nativeDirectory, to: path.basename(bundle.nativeDirectory) }] : [],
|
|
307
|
+
...bundle.resourcesDirectory ? [{ from: bundle.resourcesDirectory, to: path.basename(bundle.resourcesDirectory) }] : []
|
|
308
|
+
],
|
|
309
|
+
//The dependencies are bundled
|
|
310
|
+
npmRebuild: false,
|
|
311
|
+
...platform === 'linux' ? { linux: { maintainer: maintainer, ...config.vendor ? { vendor: config.vendor } : {} } } : {},
|
|
312
|
+
...own
|
|
313
|
+
};
|
|
314
|
+
//Their own configurations (the one of Linux has its maintainer)
|
|
315
|
+
for (const [key, value] of Object.entries(icons))
|
|
316
|
+
generated[key] = { ...generated[key], ...value };
|
|
317
|
+
const electronConfig = config.electron ?? {};
|
|
318
|
+
if (!electronConfig || typeof electronConfig !== 'object' || Array.isArray(electronConfig))
|
|
319
|
+
throw new Error('The configuration of electron-builder (lakutata.installer.electron) must be an object');
|
|
320
|
+
for (const target of Object.keys(own)) {
|
|
321
|
+
for (const option of ['afterInstall', 'afterRemove', 'fpm', 'include', 'scripts']) {
|
|
322
|
+
if (electronConfig[target]?.[option] !== undefined && own[target][option] !== undefined)
|
|
323
|
+
throw new Error(`lakutata.installer.electron.${target}.${option} replaces what runs the scripts of the application: give the script to lakutata.installer.${platform}.scripts`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const merged = merge(generated, electronConfig);
|
|
327
|
+
//The 7-Zip of electron-builder filters the executables of ARM64 by its ARM64 filter, which the installers of NSIS
|
|
328
|
+
//do not extract (they leave them out): the BCJ filter is the one they extract
|
|
329
|
+
const filter = platform === 'win' && process.arch === 'arm64' && process.env.ELECTRON_BUILDER_7Z_FILTER === undefined;
|
|
330
|
+
if (filter)
|
|
331
|
+
process.env.ELECTRON_BUILDER_7Z_FILTER = 'BCJ';
|
|
332
|
+
let artifacts;
|
|
333
|
+
try {
|
|
334
|
+
artifacts = await electronBuilder.build({
|
|
335
|
+
projectDir: work,
|
|
336
|
+
targets: electronBuilder.Platform.current().createTarget(targets, electronBuilder.Arch[process.arch]),
|
|
337
|
+
config: merged,
|
|
338
|
+
publish: 'never'
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
finally {
|
|
342
|
+
if (filter)
|
|
343
|
+
delete process.env.ELECTRON_BUILDER_7Z_FILTER;
|
|
344
|
+
}
|
|
345
|
+
const files = [];
|
|
346
|
+
for (const file of artifacts.filter((artifact) => !/\.(blockmap|yml)$/.test(artifact))) {
|
|
347
|
+
const target = targets.find((candidate) => file.toLowerCase().endsWith(`.${candidate.toLowerCase()}`)) ?? path.extname(file).slice(1);
|
|
348
|
+
files.push({ target: target, file: file, size: (await stat(file)).size });
|
|
349
|
+
}
|
|
350
|
+
const electronVersion = merged.electronVersion;
|
|
351
|
+
const executable = {
|
|
352
|
+
name: productName,
|
|
353
|
+
file: application,
|
|
354
|
+
platform: `${process.platform}-${process.arch}`,
|
|
355
|
+
size: bundle.size,
|
|
356
|
+
native: bundle.native?.modules ?? [],
|
|
357
|
+
resources: bundle.resources,
|
|
358
|
+
runtime: `Electron v${electronVersion}`,
|
|
359
|
+
warnings: []
|
|
360
|
+
};
|
|
361
|
+
return { name: name, version: version, files: files, executable: executable, warnings: warnings };
|
|
362
|
+
}
|
|
363
|
+
finally {
|
|
364
|
+
await rm(work, { recursive: true, force: true });
|
|
365
|
+
}
|
|
366
|
+
}
|