@gjsify/cli 0.45.0 → 0.46.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.
@@ -21,7 +21,8 @@
21
21
  // * lays the program directory `windows-dir` produces under
22
22
  // `%ProgramFiles%\<App>`, overridable with `msiexec INSTALLDIR=…`;
23
23
  // * gives the user something to start — one Start-Menu shortcut at the top
24
- // level, aimed at the same `.cmd` launcher the zip's user double-clicks;
24
+ // level, aimed at the GUI-subsystem `.exe` launcher the zip's user
25
+ // double-clicks (see the last paragraph of this header);
25
26
  // * comes off again: Add/Remove Programs gets its entry from the MSI's own
26
27
  // `ProductName`/`ProductVersion`/`Manufacturer`, and `msiexec /x` removes
27
28
  // every file the package installed. The `windows-dir-selfcontained` leg's
@@ -57,15 +58,20 @@
57
58
  // bought for nothing. A directory whose id is a public property is settable
58
59
  // from a command line either way.
59
60
  //
60
- // AND ONE THING IT DOES NOT FIX. `node.exe` is a CONSOLE-subsystem image and the
61
- // Node release ships no `nodew.exe` (`status/open-todos.md` carries the offsets),
62
- // so the shortcut this installer writes pops a console window behind the GUI
63
- // exactly as the `.cmd` does. Nothing here changes that, and no leg can observe
64
- // it — every Windows CI leg starts the app from a shell and already has one.
61
+ // AND ONE THING IT USED NOT TO FIX. `node.exe` is a CONSOLE-subsystem image and
62
+ // the Node release ships no `nodew.exe`, so a Start-Menu shortcut to the `.cmd`
63
+ // popped a console window behind the GUI exactly as a double-click did. The
64
+ // shortcut now points at the GUI-subsystem launcher the windows LAYOUT stages
65
+ // (`utils/ship/pe-launcher.ts`), which runs the same `.cmd` with no window —
66
+ // measured on `win11-gjsify` in session 1, where the `.cmd` produces two new
67
+ // visible console-host windows and the `.exe` produces none. Still no CI leg can
68
+ // observe it: every Windows job starts the app from a shell and already has a
69
+ // console.
65
70
  import { createHash } from 'node:crypto';
66
71
  import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
67
72
  import { join, posix } from 'node:path';
68
73
  import { describeExit, spawnToCompletion } from '../spawn.js';
74
+ import { windowsGuiLauncherPath } from './layout.js';
69
75
  import { xmlEscape } from './xml.js';
70
76
  /**
71
77
  * The UUID namespace every GUID in a generated `.msi` is derived under.
@@ -236,7 +242,18 @@ export function renderWxs(input) {
236
242
  // `Name <email>` is `PackSettings.maintainer`'s shape; ARP shows `Publisher`
237
243
  // to a human, and an address in that field reads as a mistake.
238
244
  const manufacturer = settings.maintainer.replace(/\s*<[^>]*>\s*$/, '').trim() || settings.maintainer;
239
- const launcher = `${settings.binaryName}.cmd`;
245
+ // THE SHORTCUT POINTS AT THE `.exe`, NOT THE `.cmd` — this installer's half of
246
+ // the console-window fix. A Start-Menu shortcut to a batch file starts
247
+ // `cmd.exe`, a console-subsystem image, so Windows allocates a console for it
248
+ // and a black window sits behind the app (ADR 0024 § M3). The GUI-subsystem
249
+ // launcher beside it — `utils/ship/pe-launcher.ts`, staged by the windows
250
+ // LAYOUT — runs the same `.cmd` and has no window of its own.
251
+ //
252
+ // Derived from `windowsGuiLauncherPath` and never spelled again here: the stub
253
+ // finds its `.cmd` by rewriting the last three characters of its own filename,
254
+ // so the two names are ONE decision, and a second literal is where they would
255
+ // come apart.
256
+ const launcher = windowsGuiLauncherPath(settings);
240
257
  const identifiers = new Map();
241
258
  const claim = (id, what) => {
242
259
  const previous = identifiers.get(id);
@@ -440,7 +440,15 @@ export function assertLauncherMatchesInterpreter(payload, layout, identity, inte
440
440
  ' An installed package that depends on one interpreter and runs another installs cleanly and ' +
441
441
  'fails\n' +
442
442
  " at first launch, on the user's machine.\n" +
443
- ` Either set \`gjsify.app\` to "${other}", or fix the launcher — if it comes from ` +
443
+ // THIS TARGET's key, and not `gjsify.app`. Since #1486 the runtime is
444
+ // resolved per target, so sending the reader to the project field to
445
+ // fix ONE layout's launcher moves every other layout's `Depends:` with
446
+ // it — the defect § A22 exists to close, reintroduced by the message
447
+ // that reports it. Named as a project edit because `--from-stage`
448
+ // reaches here too, and that host has no config to change.
449
+ ` Either set \`gjsify.ship.app.${layout.os}\` to "${other}" in the project this was staged ` +
450
+ 'from — it overrides\n' +
451
+ ' `gjsify.app` for that target alone — or fix the launcher: if it comes from ' +
444
452
  '`gjsify.ship.extraFiles`,\n' +
445
453
  ' that override is what decides which interpreter runs and it must match the declaration.');
446
454
  }
@@ -488,19 +496,30 @@ const SHARE_VERDICTS = [
488
496
  /**
489
497
  * Paths that survive the trip, so the rule below can be an INVERSE one.
490
498
  *
491
- * Two entries and they are different SHAPES, which is why the check below tests
499
+ * Three entries and they are different SHAPES, which is why the check below tests
492
500
  * both containment and equality:
493
501
  *
494
502
  * - `share/locale` is a DIRECTORY. A `.mo` is read straight off disk by
495
503
  * `bindtextdomain`, with no install step anywhere, and the launcher hands the
496
504
  * directory over on all three layouts.
505
+ * - `share/fonts` is a directory too, and it is here for a NARROWER reason than
506
+ * the one next to it (ADR 0038). What this list means is "no package install
507
+ * step makes this correct", and that is true of a face on every layout: nothing
508
+ * compiles it, nothing reindexes it, and on Linux fontconfig scans the
509
+ * directory and caches lazily per user. What it deliberately does NOT claim is
510
+ * that a face is FOUND on the other two — macOS reaches it through
511
+ * `Info.plist`'s `ATSApplicationFontsPath` and Windows only through a call the
512
+ * app makes itself. Those are `Layout.fontGap`, printed separately, because
513
+ * they are facts about an OS rather than about a missing scriptlet, and folding
514
+ * them in here would report the answer under a heading that names the wrong
515
+ * question.
497
516
  * - `gschemas.compiled` is one FILE, and it is here because it is not a cost —
498
517
  * it is the thing that removes one. Reported under a warning headed "whose
499
518
  * Linux correctness comes from a package install step" it would name the fix
500
519
  * as if it were the problem, and it would be the sixth line of a five-line
501
520
  * list nobody asked to grow.
502
521
  */
503
- const SHARE_PORTABLE = [SHARE.locale, SCHEMA_CACHE];
522
+ const SHARE_PORTABLE = [SHARE.locale, SHARE.fonts, SCHEMA_CACHE];
504
523
  /**
505
524
  * Every `share/` entry a non-Linux layout carries, classified — including the
506
525
  * ones nothing here has a rule for.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `IMAGE_SUBSYSTEM_WINDOWS_GUI` — the one field this whole file exists to set.
3
+ *
4
+ * The counterpart is 3, `_CUI`, which is what `node.exe` and `cmd.exe` are and
5
+ * what makes Windows allocate a console when there is none to inherit.
6
+ */
7
+ export declare const PE_SUBSYSTEM_GUI = 2;
8
+ /** Where `Subsystem` lands in the emitted image: `e_lfanew` + 4 + 20 + 68. */
9
+ export declare const PE_SUBSYSTEM_OFFSET: number;
10
+ export interface GuiLauncherInput {
11
+ /**
12
+ * The file the stub writes the child's output to under `%TEMP%`, when there
13
+ * is no console to inherit.
14
+ *
15
+ * A LEAF and not a path: `%TEMP%` is the one directory a program can write to
16
+ * on every Windows install, and the program directory is not — `C:\Program
17
+ * Files\<App>` is read-only for the user who runs the app.
18
+ */
19
+ logLeaf: string;
20
+ }
21
+ /**
22
+ * The GUI-subsystem launcher, as a complete PE32+ image.
23
+ *
24
+ * Deterministic: the same input always produces the same bytes, with no
25
+ * timestamp, no checksum and no build-host string anywhere in the file — which
26
+ * is what lets `tests/e2e/ship-windows` compare two runs and what keeps the
27
+ * artifact reproducible for anyone who repacks a stage (ADR 0024 § A2).
28
+ */
29
+ export declare function buildGuiLauncher(input: GuiLauncherInput): Uint8Array;