@gjsify/cli 0.54.0 → 0.55.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.
@@ -13,8 +13,9 @@
13
13
  // `scripts/` of ours, so without this flag it could generate sidecars and never hold them.
14
14
  // The two share the derivation; what is duplicated is the byte comparison.
15
15
  import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
16
- import { basename, isAbsolute, join, resolve } from 'node:path';
16
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
17
17
  import { emitTypedSidecar, parseBlueprint, sidecarPathFor } from '@gjsify/blueprint';
18
+ import { emitFormatForTree } from '@gjsify/blueprint/oxfmt';
18
19
  /** What a walk never descends into: build output, and other projects' sources. */
19
20
  const SKIP = new Set(['node_modules', 'dist', 'lib', '.git']);
20
21
  /**
@@ -44,6 +45,29 @@ function blueprintsUnder(path, found) {
44
45
  }
45
46
  return found;
46
47
  }
48
+ /**
49
+ * The emitter options for the tree a `.blp` lives in, memoized per directory.
50
+ *
51
+ * ONE directory of the walk is a sidecar's, not the CWD's, and that is the difference between
52
+ * right and right-by-luck in a monorepo: a package with its own `.oxfmtrc` gets that package's
53
+ * width, and running the command from the repo root still writes that package's indent. The walk
54
+ * itself is `emitFormatForTree`'s — the same one the drift gate and the bundler plugin use, so
55
+ * three callers cannot answer "which config does the formatter read" three ways (ADR 0088 §
56
+ * Consequences: the emitter once read `printWidth` and hardcoded a 4-space indent beside it, which
57
+ * is a `tabWidth: 2` consumer's red `oxfmt --check`).
58
+ *
59
+ * Memoized because the walk touches the filesystem once per `.blp` and a tree of a thousand
60
+ * `.blp` files would do that a thousand times; one read per distinct directory, not per file.
61
+ */
62
+ const formatCache = new Map();
63
+ const formatFor = (dir) => {
64
+ const cached = formatCache.get(dir);
65
+ if (cached !== undefined)
66
+ return cached;
67
+ const format = emitFormatForTree(dir);
68
+ formatCache.set(dir, format);
69
+ return format;
70
+ };
47
71
  export const blueprintTypesCommand = {
48
72
  command: 'types [paths..]',
49
73
  description: 'Generate the `<name>.d.blp.ts` type sidecar for each .blp (ADR 0088), so `import { build }` / ' +
@@ -84,7 +108,7 @@ export const blueprintTypesCommand = {
84
108
  // init, and the path here is the user's, on their machine. `@gjsify/blueprint` cannot
85
109
  // take the name itself — see `typed-exports.mjs` § `emitTypedSidecar`.
86
110
  const ast = parseBlueprint(readFileSync(file, 'utf8'), file);
87
- const expected = emitTypedSidecar(ast, basename(file));
111
+ const expected = emitTypedSidecar(ast, basename(file), formatFor(dirname(file)));
88
112
  let current;
89
113
  try {
90
114
  current = readFileSync(sidecar, 'utf8');
@@ -34,6 +34,7 @@ import { isGtkRuntimeTarget, stageAppRuntime } from '../utils/ship/app-runtime.j
34
34
  import { discoverPayload } from '../utils/ship/discover.js';
35
35
  import { appDirFor, appDirPayload, appImageHostRequirements, appImageRuntimeNotice, buildAppImage, findPinnedRuntime, stampAppDirTimes, } from '../utils/ship/appimage.js';
36
36
  import { buildDmgImage, dmgVolumeDir, dmgVolumeName } from '../utils/ship/dmg.js';
37
+ import { assertNoDroppedFlatpakModules } from '../utils/ship/flatpak-config.js';
37
38
  import { buildFlatpakBundle } from '../utils/ship/flatpak.js';
38
39
  import { iconSizes, resolveAppIcon } from '../utils/ship/icons.js';
39
40
  import { localizeMetadata } from '../utils/ship/localize-metadata.js';
@@ -252,6 +253,10 @@ async function assemble(args) {
252
253
  'thing as an error.');
253
254
  }
254
255
  }
256
+ // Under `--stage` too: the stage records the Flatpak settings, and a stage
257
+ // that quietly omits the project's modules packs without them later.
258
+ if (formats.some((format) => format.id === 'flatpak'))
259
+ assertNoDroppedFlatpakModules(flatpak);
255
260
  if (!args.stage)
256
261
  assertPackable(formats, layout, args.os === undefined ? 'host' : 'positional', unusable);
257
262
  // BEFORE the build, beside `assertCanPack` and for the same reason: a
@@ -706,6 +706,11 @@ export interface ShipSignOptions {
706
706
  * the one thing ADR 0024 § 8 gates the whole migration on. A project that has
707
707
  * to BUILD something inside the sandbox still has `gjsify flatpak init` +
708
708
  * `gjsify flatpak build`, unchanged.
709
+ *
710
+ * Setting `gjsify.flatpak.modules` / `extraModules` and shipping the `flatpak`
711
+ * target is REFUSED (`assertNoDroppedFlatpakModules`) rather than ignored:
712
+ * those keys are read by `flatpak init` only, and dropping them at exit 0 hid an
713
+ * `extra-data` module until the app ran without its downloaded binary.
709
714
  */
710
715
  export interface ShipFlatpakOptions {
711
716
  /**
@@ -64,3 +64,19 @@ export interface ResolvedShipFlatpak {
64
64
  * has somewhere else to point at.
65
65
  */
66
66
  export declare function resolveShipFlatpakSettings(input: ShipFlatpakInput): ResolvedShipFlatpak;
67
+ /**
68
+ * Refuse a Flatpak ship that would silently drop the project's own modules.
69
+ *
70
+ * `gjsify.flatpak.modules` / `extraModules` are read by `gjsify flatpak init`
71
+ * only; `ship` renders exactly one module, the staged payload (ADR 0024 § 2),
72
+ * and the keys' absence from {@link ShipFlatpakOptions} is deliberate. The
73
+ * defect was the silence: a project that moved to `ship` kept the keys, got a
74
+ * manifest without its `extra-data` module and exit 0, and found out when the
75
+ * app started without the binary the module downloads. A key `ship` does not
76
+ * read must not be one it ignores quietly, so the run stops BEFORE the build.
77
+ *
78
+ * Called only when `flatpak` is among the formats being made: the same keys
79
+ * still drive `gjsify flatpak init`, and a `ship --target deb` has nothing to
80
+ * drop them from.
81
+ */
82
+ export declare function assertNoDroppedFlatpakModules(flatpak: ConfigDataFlatpak): void;
@@ -120,6 +120,32 @@ export function resolveShipFlatpakSettings(input) {
120
120
  };
121
121
  return { settings, warnings: fromLegacy.length === 0 ? [] : [deprecationWarning(fromLegacy)] };
122
122
  }
123
+ /**
124
+ * Refuse a Flatpak ship that would silently drop the project's own modules.
125
+ *
126
+ * `gjsify.flatpak.modules` / `extraModules` are read by `gjsify flatpak init`
127
+ * only; `ship` renders exactly one module, the staged payload (ADR 0024 § 2),
128
+ * and the keys' absence from {@link ShipFlatpakOptions} is deliberate. The
129
+ * defect was the silence: a project that moved to `ship` kept the keys, got a
130
+ * manifest without its `extra-data` module and exit 0, and found out when the
131
+ * app started without the binary the module downloads. A key `ship` does not
132
+ * read must not be one it ignores quietly, so the run stops BEFORE the build.
133
+ *
134
+ * Called only when `flatpak` is among the formats being made: the same keys
135
+ * still drive `gjsify flatpak init`, and a `ship --target deb` has nothing to
136
+ * drop them from.
137
+ */
138
+ export function assertNoDroppedFlatpakModules(flatpak) {
139
+ const dropped = ['modules', 'extraModules'].filter((key) => (flatpak[key]?.length ?? 0) > 0);
140
+ if (dropped.length === 0)
141
+ return;
142
+ const list = dropped.map((key) => `gjsify.flatpak.${key}`).join(' and ');
143
+ throw new Error(`gjsify ship: ${list} ${dropped.length > 1 ? 'are' : 'is'} set, but the \`flatpak\` target renders ` +
144
+ 'ONE module, the staged payload, and would drop ' +
145
+ `${dropped.length > 1 ? 'them' : 'it'} (ADR 0024 § 2). Build this Flatpak with ` +
146
+ '`gjsify flatpak init` + `gjsify flatpak build`, which honour the key, or leave `flatpak` out of ' +
147
+ '`--target` / `gjsify.ship.targets`.');
148
+ }
123
149
  /** One line, naming every key still read from the old block and where each goes. */
124
150
  function deprecationWarning(keys) {
125
151
  const list = keys.map((key) => `gjsify.flatpak.${key}`).join(', ');
@@ -132,12 +132,14 @@ function macosArch(arch, _archIndependent) {
132
132
  /**
133
133
  * `process.arch` → the name Windows and everything that targets it use.
134
134
  *
135
- * ONE ROW, and it is not caution: `wingtk/gvsbuild` hardcodes
136
- * `self.platform = "x64"` and publishes no arm64 GTK, so there is nothing to build
137
- * `@gjsify/gtk-runtime-win32-arm64` out of and no GTK for a Windows/ARM artifact
138
- * to load (#1117). `Layout.arches` refuses the same value one phase earlier, at
139
- * stage time, with that reason spelled out; this table is the second half — the
140
- * one that stops a stage assembled by an older gjsify from acquiring a label here.
135
+ * ONE ROW, and it is not caution: there is no arm64 GTK CLOSURE to put on it yet.
136
+ * `wingtk/gvsbuild` publishes no arm64 assets, which is why ADR 0089 builds that
137
+ * prefix from our `gjsify/gvsbuild` fork in CI instead — but what a Windows
138
+ * artifact stages is `@gjsify/gtk-runtime-*`, and the arm64 one is not built, so
139
+ * there is nothing for a Windows/ARM artifact to load (#1117). `Layout.arches`
140
+ * refuses the same value one phase earlier, at stage time, with that reason spelled
141
+ * out; this table is the second half — the one that stops a stage assembled by an
142
+ * older gjsify from acquiring a label here.
141
143
  *
142
144
  * `x64` maps to itself, and unlike the macOS table that is not a coincidence to be
143
145
  * caught later: `x64` is the spelling Node's own release archives use
@@ -734,7 +736,8 @@ export const FORMATS = {
734
736
  // * there is no `Info.plist` and no metadata file at all: what a Windows
735
737
  // installer says about an application lives in the `.msi`'s own tables
736
738
  // (#1354 M5), which is why `Layout.metadata` answers `[]` here.
737
- // * `x64` alone, and the blocker is upstream — see `WINDOWS_ARCH`.
739
+ // * `x64` alone, and the blocker is a package of ours we have not built —
740
+ // see `WINDOWS_ARCH`.
738
741
  //
739
742
  // NEITHER IS SIGNED BY DEFAULT, and Windows is the softer of the two asymmetries
740
743
  // ADR 0024 § A5 records: Gatekeeper BLOCKS an unsigned `.app`, while SmartScreen
@@ -230,9 +230,10 @@ export interface Layout {
230
230
  *
231
231
  * * a REASON. `archName` answers "no Windows architecture is known for
232
232
  * `process.arch` \"arm64\"", which reads as a gap in our table. The truth is
233
- * that gvsbuild publishes no arm64 GTK, the blocker is upstream, and #1117
234
- * is where it is tracked — and the difference between "unsupported" and
235
- * "here is what would have to change" is the whole value of the message.
233
+ * a package this project has not built yet — ordered in
234
+ * `status/open-todos/windows.md` and tracked in #1117 — and the difference
235
+ * between "unsupported" and "here is what would have to change" is the whole
236
+ * value of the message.
236
237
  * * a STAGE-TIME warning at the flag that caused it (`commands/ship.ts`), for
237
238
  * a phase that deliberately does not refuse — `tests/e2e/ship-layout`
238
239
  * assembles all three layouts from ONE payload on purpose, and that
@@ -314,9 +315,9 @@ export declare function hostLayout(platform?: string): Layout;
314
315
  * AT STAGE TIME, which is the whole point — see {@link Layout.arches}. The
315
316
  * message carries the row's own `why` rather than a generic "unsupported", because
316
317
  * the two refusals a reader can act on are different jobs: `x64` is what a Windows
317
- * artifact is built for TODAY, and `win32-arm64` is blocked in a repository we do
318
- * not own. Telling an author "unknown architecture" for the second one would send
319
- * them to look for a flag.
318
+ * artifact is built for TODAY, and `win32-arm64` waits on a package of ours that
319
+ * does not exist yet. Telling an author "unknown architecture" for the second one
320
+ * would send them to look for a flag.
320
321
  *
321
322
  * `process.arch` spelling on both sides, so the value quoted back is the one the
322
323
  * user typed after `--arch`.
@@ -378,19 +378,22 @@ export const LAYOUTS = {
378
378
  ]
379
379
  : [];
380
380
  },
381
- // ONE, and the blocker is a project we do not own. `wingtk/gvsbuild`
382
- // hardcodes `self.platform = "x64"` in `utils/base_project.py` and its last
383
- // five releases publish exactly two assets each, both x64 — so there is no
384
- // arm64 GTK to build `@gjsify/gtk-runtime-win32-arm64` OUT OF, and on
385
- // Windows that bundle is the only GTK there is. `@gjsify/node-gi` declares
386
- // `win32-x64` only for the same reason. Tracked in #1117, which also
387
- // records why an exploratory arm64 leg must NOT be added: its first step
388
- // downloads a ZIP that does not exist, so it would be red by construction.
381
+ // ONE, and the blocker is OURS now. `wingtk/gvsbuild` hardcodes
382
+ // `self.platform = "x64"` and publishes x64 assets only — but we stopped
383
+ // waiting for it: ADR 0089 builds the arm64 GTK prefix from
384
+ // `gjsify/gvsbuild` in our own CI and `@gjsify/node-gi` declares
385
+ // `win32-arm64` against it (#2003). What is missing is
386
+ // `@gjsify/gtk-runtime-win32-arm64`, the closure this layout stages and on
387
+ // Windows the only GTK there is, so a Windows/ARM artifact still has
388
+ // nothing to load. Adding arm64 to `only` is the PR that builds that
389
+ // package, not this one.
389
390
  arches: {
390
391
  only: ['x64'],
391
- why: 'gvsbuild publishes no arm64 GTK (it hardcodes `self.platform = "x64"`), so there is nothing ' +
392
- 'to build `@gjsify/gtk-runtime-win32-arm64` out of and no GTK for a Windows/ARM artifact to ' +
393
- 'load — the blocker is upstream and is tracked in gjsify/gjsify#1117',
392
+ why: 'the arm64 GTK closure `@gjsify/gtk-runtime-win32-arm64` does not exist yet — the prefix it ' +
393
+ 'would be built from is no longer missing, because ADR 0089 builds it from our `gjsify/gvsbuild` ' +
394
+ 'fork in CI and `@gjsify/node-gi` ships a win32-arm64 prebuild against it — so what is left is ' +
395
+ 'the runtime package this layout would stage, ordered in `status/open-todos/windows.md` and ' +
396
+ 'tracked in gjsify/gjsify#1117',
394
397
  },
395
398
  },
396
399
  };
@@ -502,9 +505,9 @@ export function hostLayout(platform = process.platform) {
502
505
  * AT STAGE TIME, which is the whole point — see {@link Layout.arches}. The
503
506
  * message carries the row's own `why` rather than a generic "unsupported", because
504
507
  * the two refusals a reader can act on are different jobs: `x64` is what a Windows
505
- * artifact is built for TODAY, and `win32-arm64` is blocked in a repository we do
506
- * not own. Telling an author "unknown architecture" for the second one would send
507
- * them to look for a flag.
508
+ * artifact is built for TODAY, and `win32-arm64` waits on a package of ours that
509
+ * does not exist yet. Telling an author "unknown architecture" for the second one
510
+ * would send them to look for a flag.
508
511
  *
509
512
  * `process.arch` spelling on both sides, so the value quoted back is the one the
510
513
  * user typed after `--arch`.
@@ -423,6 +423,8 @@ export interface PackSettings {
423
423
  * model back in the tree — which is the one thing § 8 gates the whole migration
424
424
  * on. A project that genuinely needs to BUILD something inside the sandbox
425
425
  * still has `gjsify flatpak init` + `gjsify flatpak build`, unchanged.
426
+ * A project that sets those keys and ships `flatpak` anyway is refused up front
427
+ * (`assertNoDroppedFlatpakModules`), never silently stripped of them.
426
428
  */
427
429
  export interface ShipFlatpakSettings {
428
430
  /** Runtime id, e.g. `org.gnome.Platform`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/cli",
3
- "version": "0.54.0",
3
+ "version": "0.55.0",
4
4
  "description": "CLI for Gjsify",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -142,22 +142,22 @@
142
142
  "cli"
143
143
  ],
144
144
  "dependencies": {
145
- "@gjsify/blueprint": "^0.54.0",
146
- "@gjsify/buffer": "^0.54.0",
147
- "@gjsify/create-app": "^0.54.0",
148
- "@gjsify/node-globals": "^0.54.0",
149
- "@gjsify/node-polyfills": "^0.54.0",
150
- "@gjsify/npm-registry": "^0.54.0",
151
- "@gjsify/resolve-npm": "^0.54.0",
152
- "@gjsify/rolldown-plugin-gjsify": "^0.54.0",
153
- "@gjsify/vite-plugin-blueprint": "^0.54.0",
154
- "@gjsify/rolldown-plugin-pnp": "^0.54.0",
155
- "@gjsify/semver": "^0.54.0",
156
- "@gjsify/tar": "^0.54.0",
157
- "@gjsify/tsc": "^0.54.0",
158
- "@gjsify/utils": "^0.54.0",
159
- "@gjsify/web-polyfills": "^0.54.0",
160
- "@gjsify/workspace": "^0.54.0",
145
+ "@gjsify/blueprint": "^0.55.0",
146
+ "@gjsify/buffer": "^0.55.0",
147
+ "@gjsify/create-app": "^0.55.0",
148
+ "@gjsify/node-globals": "^0.55.0",
149
+ "@gjsify/node-polyfills": "^0.55.0",
150
+ "@gjsify/npm-registry": "^0.55.0",
151
+ "@gjsify/resolve-npm": "^0.55.0",
152
+ "@gjsify/rolldown-plugin-gjsify": "^0.55.0",
153
+ "@gjsify/vite-plugin-blueprint": "^0.55.0",
154
+ "@gjsify/rolldown-plugin-pnp": "^0.55.0",
155
+ "@gjsify/semver": "^0.55.0",
156
+ "@gjsify/tar": "^0.55.0",
157
+ "@gjsify/tsc": "^0.55.0",
158
+ "@gjsify/utils": "^0.55.0",
159
+ "@gjsify/web-polyfills": "^0.55.0",
160
+ "@gjsify/workspace": "^0.55.0",
161
161
  "acorn": "^8.17.0",
162
162
  "cosmiconfig": "^9.0.2",
163
163
  "get-tsconfig": "^4.14.0",
@@ -166,13 +166,13 @@
166
166
  "yargs": "^18.0.0"
167
167
  },
168
168
  "devDependencies": {
169
- "@gjsify/unit": "^0.54.0",
169
+ "@gjsify/unit": "^0.55.0",
170
170
  "@types/yargs": "^17.0.35",
171
171
  "typescript": "^6.0.3"
172
172
  },
173
173
  "peerDependencies": {
174
- "@gjsify/oxfmt-native": "^0.54.0",
175
- "@gjsify/rolldown-native": "^0.54.0"
174
+ "@gjsify/oxfmt-native": "^0.55.0",
175
+ "@gjsify/rolldown-native": "^0.55.0"
176
176
  },
177
177
  "peerDependenciesMeta": {
178
178
  "@gjsify/oxfmt-native": {