@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,525 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readdirSync } from 'node:fs';
|
|
3
|
+
import { copyFile, cp, mkdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { runCommand } from '../Processes.js';
|
|
6
|
+
import { ensureTool } from '../Tools.js';
|
|
7
|
+
/**
|
|
8
|
+
* The installers of Windows: the installer of NSIS (.exe), the package of Windows Installer (.msi), the zip archive of
|
|
9
|
+
* the directory of the executable
|
|
10
|
+
*/
|
|
11
|
+
export const WINDOWS_TARGETS = ['nsis', 'msi', 'zip'];
|
|
12
|
+
/**
|
|
13
|
+
* The architecture of the packages (the one of the executable)
|
|
14
|
+
*/
|
|
15
|
+
function packageArch() {
|
|
16
|
+
return process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A text of XML
|
|
20
|
+
* @param value
|
|
21
|
+
*/
|
|
22
|
+
function xml(value) {
|
|
23
|
+
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A value in double quotes, as the environment file of the application gives them (LAKUTATA_ENV_FILE)
|
|
27
|
+
* @param value
|
|
28
|
+
*/
|
|
29
|
+
function doubleQuoted(value) {
|
|
30
|
+
return `"${value.replace(/[\\"$`]/g, '\\$&')}"`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* An argument of a command line of Windows
|
|
34
|
+
* @param arg
|
|
35
|
+
*/
|
|
36
|
+
function argument(arg) {
|
|
37
|
+
if (arg && !/[\s"]/.test(arg))
|
|
38
|
+
return arg;
|
|
39
|
+
return `"${arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1')}"`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The name of the account of a service: a virtual account (NT SERVICE\<service>) by default, LocalService,
|
|
43
|
+
* NetworkService, LocalSystem
|
|
44
|
+
* @param service
|
|
45
|
+
*/
|
|
46
|
+
function serviceAccount(service) {
|
|
47
|
+
const user = service.user;
|
|
48
|
+
if (/^(LocalSystem|root)$/i.test(user))
|
|
49
|
+
return undefined;
|
|
50
|
+
if (/^(LocalService|NetworkService)$/i.test(user))
|
|
51
|
+
return { domain: 'NT AUTHORITY', user: user };
|
|
52
|
+
const [domain, name] = user.includes('\\') ? user.split('\\') : ['NT SERVICE', user];
|
|
53
|
+
return { domain: domain, user: name };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The paths of an application installed on Windows: in Program Files, its data in ProgramData
|
|
57
|
+
* @param plan
|
|
58
|
+
*/
|
|
59
|
+
function installPaths(plan) {
|
|
60
|
+
return {
|
|
61
|
+
wrapper: `${plan.name}-service`,
|
|
62
|
+
environment: `%ProgramData%\\${plan.name}\\${plan.name}.env`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The environment file of the service: its variables, which the administrators change (kept by the upgrades), read by
|
|
67
|
+
* the application at its start (LAKUTATA_ENV_FILE)
|
|
68
|
+
* @param service
|
|
69
|
+
*/
|
|
70
|
+
function environmentFile(service) {
|
|
71
|
+
return `# The environment of the service ${service.name}, read when it starts (Restart-Service ${service.name})\r\n${Object.entries(service.env).map(([name, value]) => `${name}=${doubleQuoted(value)}`).join('\r\n')}\r\n`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The configuration of WinSW, the wrapper running the application as a service: stopped by Ctrl+C (SIGINT for node,
|
|
75
|
+
* the application ends gracefully), its environment file, its directory, its account, its logs
|
|
76
|
+
* @param plan
|
|
77
|
+
* @param service
|
|
78
|
+
*/
|
|
79
|
+
function serviceConfiguration(plan, service) {
|
|
80
|
+
const account = serviceAccount(service);
|
|
81
|
+
return [
|
|
82
|
+
'<service>',
|
|
83
|
+
` <id>${xml(service.name)}</id>`,
|
|
84
|
+
` <name>${xml(plan.productName)}</name>`,
|
|
85
|
+
` <description>${xml(service.description)}</description>`,
|
|
86
|
+
` <executable>%BASE%\\${xml(plan.program)}.exe</executable>`,
|
|
87
|
+
...service.args.length ? [` <arguments>${xml(service.args.map(argument).join(' '))}</arguments>`] : [],
|
|
88
|
+
` <workingdirectory>%ProgramData%\\${xml(plan.name)}</workingdirectory>`,
|
|
89
|
+
` <env name="LAKUTATA_ENV_FILE" value="${xml(installPaths(plan).environment)}"/>`,
|
|
90
|
+
` <startmode>${service.autoStart ? 'Automatic' : 'Manual'}</startmode>`,
|
|
91
|
+
' <stoptimeout>30 sec</stoptimeout>',
|
|
92
|
+
...service.restart === 'no' ? [] : [' <onfailure action="restart" delay="5 sec"/>'],
|
|
93
|
+
...account ? [' <serviceaccount>', ` <domain>${xml(account.domain)}</domain>`, ` <user>${xml(account.user)}</user>`, ' </serviceaccount>'] : [],
|
|
94
|
+
` <logpath>%ProgramData%\\${xml(plan.name)}\\logs</logpath>`,
|
|
95
|
+
' <log mode="roll-by-size">',
|
|
96
|
+
' <sizeThreshold>10240</sizeThreshold>',
|
|
97
|
+
' <keepFiles>8</keepFiles>',
|
|
98
|
+
' </log>',
|
|
99
|
+
'</service>',
|
|
100
|
+
''
|
|
101
|
+
].join('\r\n');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The script of the installer of NSIS (Windows PowerShell): install (the data, the environment file written once, the
|
|
105
|
+
* PATH, the service registered), start (the service), stop (the service of the version replaced), uninstall
|
|
106
|
+
* @param plan
|
|
107
|
+
*/
|
|
108
|
+
function installerScript(plan) {
|
|
109
|
+
const service = plan.service;
|
|
110
|
+
const account = service && serviceAccount(service);
|
|
111
|
+
const quote = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
112
|
+
return [
|
|
113
|
+
'# The installation of the application by its installer (NSIS): install, start, stop (before an upgrade), uninstall',
|
|
114
|
+
'param([string]$Action, [string]$InstallDir)',
|
|
115
|
+
'$ErrorActionPreference = \'Stop\'',
|
|
116
|
+
`$data = Join-Path $env:ProgramData ${quote(plan.name)}`,
|
|
117
|
+
`$wrapper = Join-Path $InstallDir ${quote(`${installPaths(plan).wrapper}.exe`)}`,
|
|
118
|
+
'function Invoke-Wrapper([string]$command) { if (Test-Path $wrapper) { & $wrapper $command | Out-Null } }',
|
|
119
|
+
'if ($Action -eq \'install\') {',
|
|
120
|
+
' New-Item -ItemType Directory -Force $data, (Join-Path $data \'logs\') | Out-Null',
|
|
121
|
+
...service ? [
|
|
122
|
+
` $environment = Join-Path $data ${quote(`${plan.name}.env`)}`,
|
|
123
|
+
' #Kept by the upgrades: the administrators change it',
|
|
124
|
+
` if (-not (Test-Path $environment)) { [IO.File]::WriteAllText($environment, ${quote(environmentFile(service))}) }`
|
|
125
|
+
] : [],
|
|
126
|
+
...plan.addToPath ? [
|
|
127
|
+
' $path = [Environment]::GetEnvironmentVariable(\'Path\', \'Machine\')',
|
|
128
|
+
' if (($path -split \';\') -notcontains $InstallDir) { [Environment]::SetEnvironmentVariable(\'Path\', ($path.TrimEnd(\';\') + \';\' + $InstallDir), \'Machine\') }'
|
|
129
|
+
] : [],
|
|
130
|
+
...service ? [
|
|
131
|
+
' Invoke-Wrapper install',
|
|
132
|
+
...account ? [` icacls $data /grant ${quote(`${account.domain}\\${account.user}:(OI)(CI)M`)} /T /Q | Out-Null`] : []
|
|
133
|
+
] : [],
|
|
134
|
+
//After the postinstall script of the application
|
|
135
|
+
'} elseif ($Action -eq \'start\') {',
|
|
136
|
+
...service?.autoStart ? [' Invoke-Wrapper start'] : [],
|
|
137
|
+
'} else {',
|
|
138
|
+
...service ? [' try { Invoke-Wrapper stop; Invoke-Wrapper uninstall } catch { }'] : [],
|
|
139
|
+
...plan.addToPath ? [
|
|
140
|
+
' if ($Action -eq \'uninstall\') {',
|
|
141
|
+
' $path = [Environment]::GetEnvironmentVariable(\'Path\', \'Machine\')',
|
|
142
|
+
' [Environment]::SetEnvironmentVariable(\'Path\', (($path -split \';\' | Where-Object { $_ -and $_ -ne $InstallDir }) -join \';\'), \'Machine\')',
|
|
143
|
+
' }'
|
|
144
|
+
] : [],
|
|
145
|
+
'}',
|
|
146
|
+
''
|
|
147
|
+
].join('\r\n');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The version of the files of Windows (four numbers)
|
|
151
|
+
* @param version
|
|
152
|
+
*/
|
|
153
|
+
function numericVersion(version) {
|
|
154
|
+
const [major, minor, patch] = version.split(/[.+-]/).map((part) => Number.parseInt(part, 10) || 0);
|
|
155
|
+
return `${major}.${minor}.${patch}`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The script of NSIS of the installer: its files in Program Files, its uninstaller registered, its script run (the
|
|
159
|
+
* service of the version it replaces stopped first, its files being replaced); the scripts of the application run from
|
|
160
|
+
* its temporary directory (the preinstall one before its files are installed, the postinstall one before the service
|
|
161
|
+
* starts, the preremove one once it is stopped, the postremove one once the files are removed), the macros of the file
|
|
162
|
+
* of NSIS it includes inserted
|
|
163
|
+
* @param plan
|
|
164
|
+
* @param stage the files it installs
|
|
165
|
+
* @param file
|
|
166
|
+
* @param scripts the scripts of the application (written with the mark of UTF-8, which Windows PowerShell reads them by)
|
|
167
|
+
*/
|
|
168
|
+
function nsisScript(plan, stage, file, scripts) {
|
|
169
|
+
const uninstallKey = `Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${plan.appId}`;
|
|
170
|
+
//A text given (its $ are not variables of NSIS), a path of NSIS (its variables: $INSTDIR)
|
|
171
|
+
const string = (value) => `"${value.replace(/\$/g, '$$$$').replace(/"/g, '$\\"')}"`;
|
|
172
|
+
const variables = (value) => `"${value.replace(/"/g, '$\\"')}"`;
|
|
173
|
+
const script = (action) => `nsExec::ExecToLog 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\\installer.ps1" -Action ${action} -InstallDir "$INSTDIR"'`;
|
|
174
|
+
//A script of the application, extracted to the temporary directory of the installer, given what it does ($R9)
|
|
175
|
+
const hook = (name) => {
|
|
176
|
+
const source = scripts.hooks[name];
|
|
177
|
+
return source ? [
|
|
178
|
+
` ; The ${name} script of the application`,
|
|
179
|
+
' InitPluginsDir',
|
|
180
|
+
` File ${variables(`/oname=$PLUGINSDIR\\${name}.ps1`)} ${string(source)}`,
|
|
181
|
+
` nsExec::ExecToLog 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$PLUGINSDIR\\${name}.ps1" $R9'`,
|
|
182
|
+
' Pop $0',
|
|
183
|
+
' StrCmp $0 "0" +2',
|
|
184
|
+
` Abort "The ${name} script failed ($0)"`
|
|
185
|
+
] : [];
|
|
186
|
+
};
|
|
187
|
+
const macro = (name) => scripts.include ? [` !ifmacrodef ${name}`, ` !insertmacro ${name}`, ' !endif'] : [];
|
|
188
|
+
//The directory of the application, given to its scripts
|
|
189
|
+
const environment = ' System::Call \'Kernel32::SetEnvironmentVariable(t "LAKUTATA_INSTALL_DIR", t "$INSTDIR")i\'';
|
|
190
|
+
return [
|
|
191
|
+
'Unicode true',
|
|
192
|
+
'ManifestDPIAware true',
|
|
193
|
+
'!include "x64.nsh"',
|
|
194
|
+
`Name ${string(plan.productName)}`,
|
|
195
|
+
`OutFile ${string(file)}`,
|
|
196
|
+
//The icon of the application
|
|
197
|
+
...plan.icon ? [`Icon ${string(plan.icon)}`, `UninstallIcon ${string(plan.icon)}`] : [],
|
|
198
|
+
`InstallDir ${variables(`$PROGRAMFILES64\\${plan.name}`)}`,
|
|
199
|
+
'RequestExecutionLevel admin',
|
|
200
|
+
'SetCompressor /SOLID lzma',
|
|
201
|
+
'ShowInstDetails show',
|
|
202
|
+
'ShowUninstDetails show',
|
|
203
|
+
`VIProductVersion "${numericVersion(plan.version)}.0"`,
|
|
204
|
+
`VIAddVersionKey "ProductName" ${string(plan.productName)}`,
|
|
205
|
+
`VIAddVersionKey "ProductVersion" ${string(plan.version)}`,
|
|
206
|
+
`VIAddVersionKey "FileVersion" ${string(plan.version)}`,
|
|
207
|
+
`VIAddVersionKey "FileDescription" ${string(plan.description)}`,
|
|
208
|
+
`VIAddVersionKey "CompanyName" ${string(plan.vendor ?? plan.maintainer)}`,
|
|
209
|
+
...scripts.include ? ['', '; The file of NSIS of the application (win.nsis.include)', `!include ${string(scripts.include)}`, ...macro('customHeader').map((line) => line.trim())] : [],
|
|
210
|
+
'',
|
|
211
|
+
'; The 64-bit registry and system (the installer is a 32-bit program)',
|
|
212
|
+
'Function .onInit',
|
|
213
|
+
' SetRegView 64',
|
|
214
|
+
' ${DisableX64FSRedirection}',
|
|
215
|
+
...macro('customInit'),
|
|
216
|
+
'FunctionEnd',
|
|
217
|
+
'Function un.onInit',
|
|
218
|
+
' SetRegView 64',
|
|
219
|
+
' ${DisableX64FSRedirection}',
|
|
220
|
+
...macro('customUnInit'),
|
|
221
|
+
'FunctionEnd',
|
|
222
|
+
'',
|
|
223
|
+
'Section "Install"',
|
|
224
|
+
' SetShellVarContext all',
|
|
225
|
+
environment,
|
|
226
|
+
' ; An upgrade replaces the application installed',
|
|
227
|
+
' StrCpy $R9 "install"',
|
|
228
|
+
' IfFileExists "$INSTDIR\\installer.ps1" 0 +2',
|
|
229
|
+
' StrCpy $R9 "upgrade"',
|
|
230
|
+
...hook('preinstall'),
|
|
231
|
+
' ; The service of the version replaced is stopped: its files are replaced',
|
|
232
|
+
' IfFileExists "$INSTDIR\\installer.ps1" 0 +3',
|
|
233
|
+
` ${script('stop')}`,
|
|
234
|
+
' Pop $0',
|
|
235
|
+
' SetOutPath "$INSTDIR"',
|
|
236
|
+
` File /r ${string(`${stage}\\*.*`)}`,
|
|
237
|
+
' WriteUninstaller "$INSTDIR\\uninstall.exe"',
|
|
238
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "DisplayName" ${string(plan.productName)}`,
|
|
239
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "DisplayVersion" ${string(plan.version)}`,
|
|
240
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "Publisher" ${string(plan.vendor ?? plan.maintainer)}`,
|
|
241
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "InstallLocation" "$INSTDIR"`,
|
|
242
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "DisplayIcon" ${variables(`$INSTDIR\\${plan.program}.exe`)}`,
|
|
243
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "UninstallString" '"$INSTDIR\\uninstall.exe"'`,
|
|
244
|
+
` WriteRegStr HKLM ${string(uninstallKey)} "QuietUninstallString" '"$INSTDIR\\uninstall.exe" /S'`,
|
|
245
|
+
` WriteRegDWORD HKLM ${string(uninstallKey)} "NoModify" 1`,
|
|
246
|
+
` WriteRegDWORD HKLM ${string(uninstallKey)} "NoRepair" 1`,
|
|
247
|
+
` ${script('install')}`,
|
|
248
|
+
' Pop $0',
|
|
249
|
+
' StrCmp $0 "0" +2',
|
|
250
|
+
' Abort "The installation failed ($0)"',
|
|
251
|
+
...hook('postinstall'),
|
|
252
|
+
` ${script('start')}`,
|
|
253
|
+
' Pop $0',
|
|
254
|
+
...macro('customInstall'),
|
|
255
|
+
'SectionEnd',
|
|
256
|
+
'',
|
|
257
|
+
'Section "Uninstall"',
|
|
258
|
+
' SetShellVarContext all',
|
|
259
|
+
environment,
|
|
260
|
+
' StrCpy $R9 "remove"',
|
|
261
|
+
...macro('customUnInstall'),
|
|
262
|
+
` ${script('uninstall')}`,
|
|
263
|
+
' Pop $0',
|
|
264
|
+
...hook('preremove'),
|
|
265
|
+
' RMDir /r "$INSTDIR"',
|
|
266
|
+
` DeleteRegKey HKLM ${string(uninstallKey)}`,
|
|
267
|
+
...hook('postremove'),
|
|
268
|
+
'SectionEnd',
|
|
269
|
+
''
|
|
270
|
+
].join('\r\n');
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* A UUID named after a text (version 5): the upgrade code of the packages of an application, the same for its versions
|
|
274
|
+
* @param name
|
|
275
|
+
*/
|
|
276
|
+
function namedUuid(name) {
|
|
277
|
+
//The namespace of the URLs (RFC 4122)
|
|
278
|
+
const namespace = Buffer.from('6ba7b8119dad11d180b400c04fd430c8', 'hex');
|
|
279
|
+
const hash = createHash('sha1').update(Buffer.concat([namespace, Buffer.from(`lakutata:${name}`)])).digest();
|
|
280
|
+
hash[6] = (hash[6] & 0x0f) | 0x50;
|
|
281
|
+
hash[8] = (hash[8] & 0x3f) | 0x80;
|
|
282
|
+
const hex = hash.subarray(0, 16).toString('hex').toUpperCase();
|
|
283
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* The moments of Windows Installer the scripts of the application are run at (deferred, as the system): the preinstall
|
|
287
|
+
* one before the files are installed (an upgrade has removed the version it replaces), the postinstall one before the
|
|
288
|
+
* services start, the preremove one once they are stopped, the postremove one once the files are removed; not the ones
|
|
289
|
+
* of the removal of the version an upgrade replaces
|
|
290
|
+
*/
|
|
291
|
+
const MSI_HOOKS = {
|
|
292
|
+
preinstall: { id: 'LakutataPreinstall', at: 'Before="InstallFiles"', condition: 'NOT REMOVE' },
|
|
293
|
+
postinstall: { id: 'LakutataPostinstall', at: 'Before="StartServices"', condition: 'NOT REMOVE' },
|
|
294
|
+
preremove: { id: 'LakutataPreremove', at: 'After="StopServices"', condition: 'REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE' },
|
|
295
|
+
postremove: { id: 'LakutataPostremove', at: 'After="RemoveFiles"', condition: 'REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE' }
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* The longest command line of Windows
|
|
299
|
+
*/
|
|
300
|
+
const COMMAND_LINE_LIMIT = 32000;
|
|
301
|
+
/**
|
|
302
|
+
* The command running a script of the application from Windows Installer (a formatted text of it: its brackets are
|
|
303
|
+
* escaped, but the ones of its properties): the script, embedded, written to a temporary file and run by Windows
|
|
304
|
+
* PowerShell as the installer of NSIS runs it (given what it does, the directory of the application)
|
|
305
|
+
* @param script
|
|
306
|
+
* @param action
|
|
307
|
+
*/
|
|
308
|
+
function msiCommand(script, action) {
|
|
309
|
+
const encoded = Buffer.from(script.content, 'utf8').toString('base64');
|
|
310
|
+
const formatted = (text) => text.replace(/[[\]]/g, (bracket) => bracket === '[' ? '[\\[]' : '[\\]]');
|
|
311
|
+
const command = `"[System64Folder]WindowsPowerShell\\v1.0\\powershell.exe" -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$env:LAKUTATA_INSTALL_DIR='[INSTALLDIR]'.TrimEnd('\\'); ${formatted(`$f = [IO.Path]::Combine([IO.Path]::GetTempPath(), [Guid]::NewGuid().ToString() + '.ps1'); [IO.File]::WriteAllText($f, [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')), (New-Object Text.UTF8Encoding $true)); & powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $f ${action}; $c = $LASTEXITCODE; Remove-Item -Force $f; exit $c`)}"`;
|
|
312
|
+
if (command.length > COMMAND_LINE_LIMIT)
|
|
313
|
+
throw new Error(`The script ${script.file} is too long for Windows Installer, which runs it by a command line: make it shorter (the one it embeds is ${command.length} characters long, ${COMMAND_LINE_LIMIT} at most)`);
|
|
314
|
+
return command;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* The custom actions of Windows Installer running the scripts of the application: the command of each (install or
|
|
318
|
+
* upgrade: an upgrade has found the version it replaces), and when it is run
|
|
319
|
+
* @param hooks
|
|
320
|
+
*/
|
|
321
|
+
function msiScripts(hooks) {
|
|
322
|
+
const actions = [];
|
|
323
|
+
const sequence = [];
|
|
324
|
+
for (const [hook, script] of Object.entries(hooks)) {
|
|
325
|
+
const { id, at, condition } = MSI_HOOKS[hook];
|
|
326
|
+
const variants = hook === 'preinstall' || hook === 'postinstall'
|
|
327
|
+
? [['Install', 'install', `${condition} AND NOT WIX_UPGRADE_DETECTED`], ['Upgrade', 'upgrade', `${condition} AND WIX_UPGRADE_DETECTED`]]
|
|
328
|
+
: [['Remove', 'remove', condition]];
|
|
329
|
+
for (const [name, action, when] of variants)
|
|
330
|
+
actions.push(` <SetProperty Id="${id}" Action="Set${id}${name}" Value="${xml(msiCommand(script, action))}" Before="${id}" Sequence="execute">${xml(when)}</SetProperty>`);
|
|
331
|
+
actions.push(` <CustomAction Id="${id}" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="check" Impersonate="no"/>`);
|
|
332
|
+
sequence.push(` <Custom Action="${id}" ${at}>${xml(condition)}</Custom>`);
|
|
333
|
+
}
|
|
334
|
+
return { actions: actions, sequence: sequence };
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* The source of WiX of the package: its files (a component each, their directories), its service (WinSW) installed,
|
|
338
|
+
* started, stopped and removed by Windows Installer, its PATH, its data in ProgramData (its environment file kept by
|
|
339
|
+
* the upgrades and the removals), the versions replaced by the upgrades
|
|
340
|
+
* @param plan
|
|
341
|
+
* @param stage the files it installs
|
|
342
|
+
* @param serviceDirectory the files of its service (WinSW and its configuration)
|
|
343
|
+
* @param environment the environment file
|
|
344
|
+
*/
|
|
345
|
+
function wixSource(plan, stage, serviceDirectory, environment) {
|
|
346
|
+
const scripts = msiScripts(plan.scripts.msi?.hooks ?? {});
|
|
347
|
+
const id = (prefix, value) => `${prefix}${createHash('sha1').update(value).digest('hex').slice(0, 30)}`;
|
|
348
|
+
const components = [];
|
|
349
|
+
const directory = (absolute, relative, depth) => {
|
|
350
|
+
const indent = ' '.repeat(depth);
|
|
351
|
+
const lines = [];
|
|
352
|
+
for (const entry of readdirSync(absolute, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
353
|
+
const file = path.join(absolute, entry.name);
|
|
354
|
+
const name = relative ? `${relative}/${entry.name}` : entry.name;
|
|
355
|
+
if (entry.isDirectory()) {
|
|
356
|
+
lines.push(`${indent}<Directory Id="${id('d', name)}" Name="${xml(entry.name)}">`, ...directory(file, name, depth + 1), `${indent}</Directory>`);
|
|
357
|
+
}
|
|
358
|
+
else if (entry.isFile()) {
|
|
359
|
+
const component = id('c', name);
|
|
360
|
+
components.push(component);
|
|
361
|
+
lines.push(`${indent}<Component Id="${component}" Guid="*"><File Id="${id('f', name)}" Source="${xml(file)}" KeyPath="yes"/></Component>`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return lines;
|
|
365
|
+
};
|
|
366
|
+
const files = directory(stage, '', 6);
|
|
367
|
+
const service = plan.service;
|
|
368
|
+
const account = service && serviceAccount(service);
|
|
369
|
+
const wrapper = `${installPaths(plan).wrapper}.exe`;
|
|
370
|
+
const restart = service?.restart === 'no' ? 'none' : 'restart';
|
|
371
|
+
return [
|
|
372
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
373
|
+
'<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">',
|
|
374
|
+
` <Product Id="*" Name="${xml(plan.productName)}" Language="1033" Version="${numericVersion(plan.version)}" Manufacturer="${xml(plan.vendor ?? plan.maintainer)}" UpgradeCode="${namedUuid(plan.appId)}">`,
|
|
375
|
+
` <Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" Description="${xml(plan.description)}"/>`,
|
|
376
|
+
' <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is installed."/>',
|
|
377
|
+
' <MediaTemplate EmbedCab="yes"/>',
|
|
378
|
+
//The icon of the application in the programs of Windows
|
|
379
|
+
...plan.icon ? [` <Icon Id="ProductIcon.ico" SourceFile="${xml(plan.icon)}"/>`, ' <Property Id="ARPPRODUCTICON" Value="ProductIcon.ico"/>'] : [],
|
|
380
|
+
` <Property Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]"/>`,
|
|
381
|
+
' <Directory Id="TARGETDIR" Name="SourceDir">',
|
|
382
|
+
' <Directory Id="ProgramFiles64Folder">',
|
|
383
|
+
` <Directory Id="INSTALLDIR" Name="${xml(plan.name)}">`,
|
|
384
|
+
...files,
|
|
385
|
+
' </Directory>',
|
|
386
|
+
' </Directory>',
|
|
387
|
+
' <Directory Id="CommonAppDataFolder">',
|
|
388
|
+
` <Directory Id="DATADIR" Name="${xml(plan.name)}">`,
|
|
389
|
+
' <Directory Id="LOGSDIR" Name="logs"/>',
|
|
390
|
+
' </Directory>',
|
|
391
|
+
' </Directory>',
|
|
392
|
+
' </Directory>',
|
|
393
|
+
//The data, the logs and the environment file are kept by the removals
|
|
394
|
+
` <Component Id="Data" Directory="DATADIR" Guid="${namedUuid(`${plan.appId}:data`)}" Permanent="yes" KeyPath="yes"><CreateFolder/></Component>`,
|
|
395
|
+
` <Component Id="Logs" Directory="LOGSDIR" Guid="${namedUuid(`${plan.appId}:logs`)}" Permanent="yes" KeyPath="yes"><CreateFolder/></Component>`,
|
|
396
|
+
...environment ? [` <Component Id="Environment" Directory="DATADIR" Guid="${namedUuid(`${plan.appId}:environment`)}" Permanent="yes" NeverOverwrite="yes"><File Id="EnvironmentFile" Name="${xml(`${plan.name}.env`)}" Source="${xml(environment)}" KeyPath="yes"/></Component>`] : [],
|
|
397
|
+
...plan.addToPath ? [` <Component Id="Path" Directory="INSTALLDIR" Guid="${namedUuid(`${plan.appId}:path`)}" KeyPath="yes"><Environment Id="Path" Name="PATH" Value="[INSTALLDIR]" Permanent="no" Part="last" Action="set" System="yes"/></Component>`] : [],
|
|
398
|
+
...service ? [
|
|
399
|
+
` <Component Id="Service" Directory="INSTALLDIR" Guid="${namedUuid(`${plan.appId}:service`)}">`,
|
|
400
|
+
` <File Id="ServiceWrapper" Source="${xml(path.join(serviceDirectory, wrapper))}" KeyPath="yes"/>`,
|
|
401
|
+
` <File Id="ServiceConfiguration" Source="${xml(path.join(serviceDirectory, `${installPaths(plan).wrapper}.xml`))}"/>`,
|
|
402
|
+
` <ServiceInstall Id="ServiceInstall" Name="${xml(service.name)}" DisplayName="${xml(plan.productName)}" Description="${xml(service.description)}" Type="ownProcess" Start="${service.autoStart ? 'auto' : 'demand'}" ErrorControl="normal"${account ? ` Account="${xml(`${account.domain}\\${account.user}`)}"` : ''}>`,
|
|
403
|
+
` <util:ServiceConfig FirstFailureActionType="${restart}" SecondFailureActionType="${restart}" ThirdFailureActionType="${restart}" RestartServiceDelayInSeconds="5" ResetPeriodInDays="1"/>`,
|
|
404
|
+
' </ServiceInstall>',
|
|
405
|
+
` <ServiceControl Id="ServiceControl" Name="${xml(service.name)}"${service.autoStart ? ' Start="install"' : ''} Stop="both" Remove="uninstall" Wait="yes"/>`,
|
|
406
|
+
' </Component>',
|
|
407
|
+
...account ? [
|
|
408
|
+
//The data of the service written by its account, once it exists (the service installed), before it starts
|
|
409
|
+
` <SetProperty Id="GrantData" Value=""[System64Folder]icacls.exe" "[DATADIR]." /grant "${xml(`${account.domain}\\${account.user}`)}:(OI)(CI)M" /T /Q" Before="GrantData" Sequence="execute"/>`,
|
|
410
|
+
' <CustomAction Id="GrantData" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="check" Impersonate="no"/>'
|
|
411
|
+
] : []
|
|
412
|
+
] : [],
|
|
413
|
+
...scripts.actions,
|
|
414
|
+
...account || scripts.sequence.length ? [
|
|
415
|
+
' <InstallExecuteSequence>',
|
|
416
|
+
...account ? [' <Custom Action="GrantData" After="InstallServices">NOT REMOVE</Custom>'] : [],
|
|
417
|
+
...scripts.sequence,
|
|
418
|
+
' </InstallExecuteSequence>'
|
|
419
|
+
] : [],
|
|
420
|
+
' <Feature Id="Application" Level="1">',
|
|
421
|
+
...components.map((component) => ` <ComponentRef Id="${component}"/>`),
|
|
422
|
+
' <ComponentRef Id="Data"/>',
|
|
423
|
+
' <ComponentRef Id="Logs"/>',
|
|
424
|
+
...environment ? [' <ComponentRef Id="Environment"/>'] : [],
|
|
425
|
+
...plan.addToPath ? [' <ComponentRef Id="Path"/>'] : [],
|
|
426
|
+
...service ? [' <ComponentRef Id="Service"/>'] : [],
|
|
427
|
+
' </Feature>',
|
|
428
|
+
' </Product>',
|
|
429
|
+
'</Wix>',
|
|
430
|
+
''
|
|
431
|
+
].join('\r\n');
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Run a tool of the builder, which must succeed
|
|
435
|
+
* @param command
|
|
436
|
+
* @param args
|
|
437
|
+
* @param cwd
|
|
438
|
+
* @param what
|
|
439
|
+
*/
|
|
440
|
+
async function must(command, args, cwd, what) {
|
|
441
|
+
const result = await runCommand(command, args, cwd);
|
|
442
|
+
if (result.code !== 0)
|
|
443
|
+
throw new Error(`${what} cannot be made: ${result.output.trim()}`);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Make the installers of Windows of an application: the installer of NSIS, the package of Windows Installer (WiX), the
|
|
447
|
+
* zip archive of the directory of the executable; its service run by WinSW
|
|
448
|
+
* @param plan
|
|
449
|
+
* @param targets
|
|
450
|
+
* @param work
|
|
451
|
+
* @param outdir
|
|
452
|
+
*/
|
|
453
|
+
export async function buildWindowsPackages(plan, targets, work, outdir) {
|
|
454
|
+
await mkdir(work, { recursive: true });
|
|
455
|
+
const files = [];
|
|
456
|
+
const service = plan.service;
|
|
457
|
+
//The files of the service: WinSW named after the application, its configuration next to it
|
|
458
|
+
const serviceDirectory = path.join(work, 'service');
|
|
459
|
+
await rm(serviceDirectory, { recursive: true, force: true });
|
|
460
|
+
await mkdir(serviceDirectory, { recursive: true });
|
|
461
|
+
let environment;
|
|
462
|
+
if (service) {
|
|
463
|
+
await copyFile(await ensureTool('winsw'), path.join(serviceDirectory, `${installPaths(plan).wrapper}.exe`));
|
|
464
|
+
await writeFile(path.join(serviceDirectory, `${installPaths(plan).wrapper}.xml`), serviceConfiguration(plan, service));
|
|
465
|
+
environment = path.join(serviceDirectory, `${plan.name}.env`);
|
|
466
|
+
await writeFile(environment, environmentFile(service));
|
|
467
|
+
}
|
|
468
|
+
for (const target of targets) {
|
|
469
|
+
if (target === 'zip') {
|
|
470
|
+
const base = `${plan.name}-${plan.version}-win-${packageArch()}`;
|
|
471
|
+
await rm(path.join(work, base), { recursive: true, force: true });
|
|
472
|
+
await cp(plan.directory, path.join(work, base), { recursive: true });
|
|
473
|
+
const file = path.join(outdir, `${base}.zip`);
|
|
474
|
+
await rm(file, { force: true });
|
|
475
|
+
//The tar of Windows writes zip archives (-a)
|
|
476
|
+
await must('tar', ['-a', '-c', '-f', file, base], work, 'The zip archive');
|
|
477
|
+
files.push({ target: target, file: file, size: (await stat(file)).size });
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
if (target === 'nsis') {
|
|
481
|
+
//The files installed: the application, its service, its installation script
|
|
482
|
+
const stage = path.join(work, 'nsis');
|
|
483
|
+
await rm(stage, { recursive: true, force: true });
|
|
484
|
+
await cp(plan.directory, stage, { recursive: true });
|
|
485
|
+
if (service)
|
|
486
|
+
for (const name of readdirSync(serviceDirectory).filter((name) => !name.endsWith('.env')))
|
|
487
|
+
await copyFile(path.join(serviceDirectory, name), path.join(stage, name));
|
|
488
|
+
//Windows PowerShell reads the scripts of UTF-8 by their mark
|
|
489
|
+
await writeFile(path.join(stage, 'installer.ps1'), `\ufeff${installerScript(plan)}`);
|
|
490
|
+
//The scripts of the application, extracted by the installer when it runs them
|
|
491
|
+
const own = plan.scripts.nsis;
|
|
492
|
+
const hooks = {};
|
|
493
|
+
const directory = path.join(work, 'nsis-scripts');
|
|
494
|
+
await rm(directory, { recursive: true, force: true });
|
|
495
|
+
await mkdir(directory, { recursive: true });
|
|
496
|
+
for (const [hook, script] of Object.entries(own?.hooks ?? {})) {
|
|
497
|
+
hooks[hook] = path.join(directory, `${hook}.ps1`);
|
|
498
|
+
await writeFile(hooks[hook], `\ufeff${script.content}`);
|
|
499
|
+
}
|
|
500
|
+
let include;
|
|
501
|
+
if (own?.include) {
|
|
502
|
+
include = path.join(directory, 'include.nsh');
|
|
503
|
+
await writeFile(include, own.include.content);
|
|
504
|
+
}
|
|
505
|
+
const file = path.join(outdir, `${plan.name}-${plan.version}-${packageArch()}-setup.exe`);
|
|
506
|
+
await rm(file, { force: true });
|
|
507
|
+
const script = path.join(work, 'installer.nsi');
|
|
508
|
+
await writeFile(script, `\ufeff${nsisScript(plan, stage, file, { hooks: hooks, include: include })}`);
|
|
509
|
+
await must(await ensureTool('nsis'), ['/V2', '/INPUTCHARSET', 'UTF8', script], work, 'The installer of NSIS');
|
|
510
|
+
files.push({ target: target, file: file, size: (await stat(file)).size });
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
//msi: compiled by candle, linked by light
|
|
514
|
+
const source = path.join(work, 'package.wxs');
|
|
515
|
+
await writeFile(source, wixSource(plan, plan.directory, serviceDirectory, environment));
|
|
516
|
+
const candle = await ensureTool('wix');
|
|
517
|
+
const light = path.join(path.dirname(candle), 'light.exe');
|
|
518
|
+
const file = path.join(outdir, `${plan.name}-${plan.version}-${packageArch()}.msi`);
|
|
519
|
+
await rm(file, { force: true });
|
|
520
|
+
await must(candle, ['-nologo', '-arch', packageArch(), '-ext', 'WixUtilExtension', '-out', path.join(work, 'package.wixobj'), source], work, 'The package of Windows Installer');
|
|
521
|
+
await must(light, ['-nologo', '-ext', 'WixUtilExtension', '-spdb', '-out', file, path.join(work, 'package.wixobj')], work, 'The package of Windows Installer');
|
|
522
|
+
files.push({ target: target, file: file, size: (await stat(file)).size });
|
|
523
|
+
}
|
|
524
|
+
return files;
|
|
525
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The builder of the applications of lakutata: their bundles (one CommonJS file run by node) and their executables (the
|
|
3
|
+
* single executable applications of Node.js, or Bun), with their native addons and their resources
|
|
4
|
+
*/
|
|
5
|
+
export { bundleProject, programName, resourceFiles, type BundleOptions, type BundleResult, type ResourceDeclaration } from './lib/Bundle.js';
|
|
6
|
+
export { buildExecutable, type ExecutableOptions, type ExecutableResult } from './lib/Executable.js';
|
|
7
|
+
export { NativeCollector, packNativeTree, writeNativeTree, type NativeFile, type NativeIndex, type NativeLink, type NativeTree } from './lib/Native.js';
|
|
8
|
+
export { buildInstaller, type InstallerConfig, type InstallerOptions, type InstallerResult, type ServiceOptions } from './lib/Installer.js';
|
|
9
|
+
export { LINUX_TARGETS, type LinuxTarget } from './lib/linux/Linux.js';
|
|
10
|
+
export { INSTALLER_HOOKS, NATIVE_HOOKS, type InstallerHook, type InstallerScripts, type NativeHook, type NativeScripts } from './lib/Scripts.js';
|
|
11
|
+
export { detectLibc } from './lib/Platform.js';
|
|
12
|
+
export { WINDOWS_TARGETS, type WindowsTarget } from './lib/win/Windows.js';
|
|
13
|
+
export { MAC_TARGETS, type MacTarget } from './lib/mac/Mac.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { InstallerPlan } from './Installer.js';
|
|
2
|
+
/**
|
|
3
|
+
* The tar.gz archive of the directory of the executable (its native addons and its resources next to it), named after
|
|
4
|
+
* the package, its version, its platform and its architecture
|
|
5
|
+
* @param plan
|
|
6
|
+
* @param arch
|
|
7
|
+
* @param work
|
|
8
|
+
* @param outdir
|
|
9
|
+
*/
|
|
10
|
+
export declare function archiveDirectory(plan: InstallerPlan, arch: string, work: string, outdir: string): Promise<{
|
|
11
|
+
target: string;
|
|
12
|
+
file: string;
|
|
13
|
+
size: number;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type NativeFile, type NativeTree } from './Native.js';
|
|
2
|
+
/**
|
|
3
|
+
* The application bundled into one file
|
|
4
|
+
*/
|
|
5
|
+
export type BundleOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* The directory of the project (its package.json)
|
|
8
|
+
*/
|
|
9
|
+
project: string;
|
|
10
|
+
/**
|
|
11
|
+
* The compiled entry of the application, the "main" of the package.json by default
|
|
12
|
+
*/
|
|
13
|
+
entry?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The bundle, dist/<name>.cjs by default
|
|
16
|
+
*/
|
|
17
|
+
outfile?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Minify the bundle (the names of the classes and of the functions are kept)
|
|
20
|
+
*/
|
|
21
|
+
minify?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* The modules left out of the bundle, loaded at run time
|
|
24
|
+
*/
|
|
25
|
+
external?: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Where the native tree goes: next to the bundle (the bundles run by node, by default), or given back (the executables
|
|
28
|
+
* embed it)
|
|
29
|
+
*/
|
|
30
|
+
native?: 'directory' | 'embedded';
|
|
31
|
+
/**
|
|
32
|
+
* The runtime running the bundle: node (by default), or bun (the executables of Bun, which bundle it again)
|
|
33
|
+
*/
|
|
34
|
+
runtime?: 'node' | 'bun';
|
|
35
|
+
/**
|
|
36
|
+
* The directories of the native tree and of the resources (when they are not embedded), next to the bundle by
|
|
37
|
+
* default (<name>.native and <name>.resources): the bundle loads them by their names from its directory
|
|
38
|
+
*/
|
|
39
|
+
directories?: {
|
|
40
|
+
native?: string;
|
|
41
|
+
resources?: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* A resource of the application, declared by the "lakutata.resources" of its package.json (as the extraResources of
|
|
46
|
+
* electron-builder): a file or a directory of the project, and its path in the resources
|
|
47
|
+
*/
|
|
48
|
+
export type ResourceDeclaration = {
|
|
49
|
+
from: string;
|
|
50
|
+
to: string;
|
|
51
|
+
};
|
|
52
|
+
export type BundleResult = {
|
|
53
|
+
/**
|
|
54
|
+
* The name of the program: its bin, or the name of the package without its scope
|
|
55
|
+
*/
|
|
56
|
+
name: string;
|
|
57
|
+
entry: string;
|
|
58
|
+
outfile: string;
|
|
59
|
+
size: number;
|
|
60
|
+
/**
|
|
61
|
+
* The native addons of the application and what they load, undefined without addon
|
|
62
|
+
*/
|
|
63
|
+
native?: NativeTree;
|
|
64
|
+
/**
|
|
65
|
+
* The directory of the native tree next to the bundle
|
|
66
|
+
*/
|
|
67
|
+
nativeDirectory?: string;
|
|
68
|
+
/**
|
|
69
|
+
* The resources of the application (their paths in the resources), and their directory next to the bundle
|
|
70
|
+
*/
|
|
71
|
+
resources: string[];
|
|
72
|
+
resourcesDirectory?: string;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The name of the program of a package: its first bin, or the name of the package without its scope
|
|
76
|
+
* @param manifest
|
|
77
|
+
*/
|
|
78
|
+
export declare function programName(manifest: Record<string, any>): string;
|
|
79
|
+
/**
|
|
80
|
+
* The files of the resources of an application, declared by the "lakutata.resources" of its package.json: their paths in
|
|
81
|
+
* the resources ("/" separated) and their sources
|
|
82
|
+
* @param project
|
|
83
|
+
* @param manifest
|
|
84
|
+
*/
|
|
85
|
+
export declare function resourceFiles(project: string, manifest: Record<string, any>): {
|
|
86
|
+
declared: string[];
|
|
87
|
+
files: NativeFile[];
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Bundle the compiled application and its dependencies into one CommonJS file, run by node and embedded in the executables.
|
|
91
|
+
* The sources are compiled by tsc first: the metadata of the decorators (the injections, the DTOs) are emitted by it.
|
|
92
|
+
* The native addons cannot be bundled: their packages (and what they load) are gathered into a native tree, which the
|
|
93
|
+
* require of the bundle loads them from
|
|
94
|
+
* @param options
|
|
95
|
+
*/
|
|
96
|
+
export declare function bundleProject(options: BundleOptions): Promise<BundleResult>;
|