@gjsify/vite-plugin-blueprint 0.34.0 → 0.36.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/lib/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export interface BlueprintPluginOptions {
3
3
  minify?: boolean;
4
4
  verbose?: boolean;
5
5
  }
6
+ export { resolveBlueprintCompiler, formatMissingBlueprintCompiler } from './resolve-compiler.js';
6
7
  export default function blueprintPlugin(options?: BlueprintPluginOptions): Plugin;
package/lib/index.js CHANGED
@@ -1,16 +1,29 @@
1
1
  import { execa } from 'execa';
2
2
  import minifyXML from 'minify-xml';
3
+ import { formatMissingBlueprintCompiler, resolveBlueprintCompiler } from './resolve-compiler.js';
4
+ export { resolveBlueprintCompiler, formatMissingBlueprintCompiler } from './resolve-compiler.js';
3
5
  export default function blueprintPlugin(options = {}) {
4
6
  const { minify = false, verbose = false } = options;
5
7
  return {
6
8
  name: 'vite-plugin-blueprint',
7
9
  async load(id) {
8
10
  if (id.endsWith('.blp')) {
11
+ // Resolved per load rather than once at plugin construction, so a
12
+ // compiler installed mid-watch is picked up without a restart —
13
+ // and so the "missing" diagnostic names the .blp that wanted it.
14
+ const compiler = resolveBlueprintCompiler();
15
+ if (!compiler) {
16
+ throw new Error(`${id}: ${formatMissingBlueprintCompiler()}`);
17
+ }
9
18
  try {
10
19
  // Compile .blp file and get XML output directly
11
- const { stdout } = await execa('blueprint-compiler', ['compile', id]);
20
+ const { stdout } = await execa(compiler.file, [...compiler.prefixArgs, 'compile', id], {
21
+ // Only ever an overlay (an MSYS2 PATH prepend); undefined
22
+ // inherits the environment unchanged.
23
+ env: compiler.env,
24
+ });
12
25
  if (verbose)
13
- console.log(`Compiled ${id}`);
26
+ console.log(`Compiled ${id} (blueprint-compiler via ${compiler.source})`);
14
27
  let xmlContent = stdout;
15
28
  // Minify XML if option is enabled
16
29
  if (minify) {
@@ -22,8 +35,12 @@ export default function blueprintPlugin(options = {}) {
22
35
  return `export default ${JSON.stringify(xmlContent)};`;
23
36
  }
24
37
  catch (error) {
25
- console.error(`Error processing ${id}:`, error);
26
- throw error;
38
+ // The compiler EXISTS and refused the file, so this is a
39
+ // blueprint syntax/type error and its own stderr is the
40
+ // useful part — surface that, not an install hint the reader
41
+ // has already satisfied.
42
+ const detail = error instanceof Error ? error.stderr || error.message : String(error);
43
+ throw new Error(`${id}: blueprint-compiler failed (${compiler.file})\n${detail}`);
27
44
  }
28
45
  }
29
46
  },
@@ -0,0 +1,27 @@
1
+ /** A spawnable blueprint-compiler, plus how it was found (for `verbose`). */
2
+ export interface ResolvedBlueprintCompiler {
3
+ /** The executable to spawn. */
4
+ file: string;
5
+ /** Arguments that must precede blueprint-compiler's own (e.g. the script path). */
6
+ prefixArgs: string[];
7
+ /** Environment overlay to spawn with, or undefined to inherit unchanged. */
8
+ env?: Record<string, string>;
9
+ source: 'env' | 'path' | 'msys2';
10
+ }
11
+ /**
12
+ * Locate a usable blueprint-compiler, or null when there is none.
13
+ *
14
+ * Order: `BLUEPRINT_COMPILER` (an explicit answer always wins, and is the escape
15
+ * hatch for an install none of the probes below know about) → `PATH` → an MSYS2
16
+ * install on win32.
17
+ */
18
+ export declare function resolveBlueprintCompiler(): ResolvedBlueprintCompiler | null;
19
+ /**
20
+ * What to tell someone who has no blueprint-compiler.
21
+ *
22
+ * Names the install command for THIS platform rather than listing every one:
23
+ * the reader is on one host and the other two lines are noise they have to
24
+ * filter. `BLUEPRINT_COMPILER` is mentioned everywhere because it is the answer
25
+ * for any install the probes miss.
26
+ */
27
+ export declare function formatMissingBlueprintCompiler(): string;
@@ -0,0 +1,145 @@
1
+ // Where is `blueprint-compiler`, and what do we say when it is nowhere?
2
+ //
3
+ // Split out of the plugin so it is answerable in a test without a compiler
4
+ // installed, and because the answer is genuinely platform-shaped on Windows.
5
+ //
6
+ // The failure this replaces: the plugin called `execa('blueprint-compiler', …)`
7
+ // and rethrew whatever came back, so a host without it got an ExecaError stack
8
+ // naming the COMMAND THAT FAILED and nothing about what to install. Measured on
9
+ // the win11-gjsify VM, that error was read as "blueprint-compiler is a GNOME
10
+ // Python tool, so it is unavailable on Windows" — a conclusion that reached a
11
+ // docs file and stopped anyone building a `.blp` showcase there for a day. It is
12
+ // available; nothing said how to get it.
13
+ import { existsSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ /**
16
+ * MSYS2 install roots to probe on win32, in order.
17
+ *
18
+ * MSYS2 is the ONLY way to get blueprint-compiler onto Windows today, and not
19
+ * for the reason the tool's Python-ness suggests: it is pure Python (PyPI ships
20
+ * a `py3-none-any` wheel), but `blueprintcompiler/gir.py` reads TYPELIBS through
21
+ * `GIRepository`, so it needs PyGObject — which publishes no Windows wheel, so
22
+ * `pip install blueprint-compiler` dies building it from source. MSYS2 is where
23
+ * Python, PyGObject and the typelibs arrive already fitted together.
24
+ *
25
+ * `C:\msys64` is the installer default; the `$USERPROFILE` entries cover the
26
+ * extracted-archive install, which is what a host without admin rights gets;
27
+ * `C:\tools\msys64` is Chocolatey's.
28
+ */
29
+ const MSYS2_ROOTS = [
30
+ process.env.MSYS2_ROOT,
31
+ 'C:\\msys64',
32
+ process.env.USERPROFILE && join(process.env.USERPROFILE, 'msys64'),
33
+ process.env.USERPROFILE && join(process.env.USERPROFILE, 'Tools', 'msys64'),
34
+ 'C:\\tools\\msys64',
35
+ ].filter((root) => typeof root === 'string' && root.length > 0);
36
+ /**
37
+ * MSYS2 environments, most-preferred first. `ucrt64` is MSYS2's own default for
38
+ * new installs and links the modern Universal CRT; the others are probed so an
39
+ * existing install is not rejected for being older.
40
+ */
41
+ const MSYS2_ENVS = ['ucrt64', 'mingw64', 'clang64'];
42
+ /** Executable suffixes to try for a bare command name. Windows needs them; POSIX does not. */
43
+ const EXE_SUFFIXES = process.platform === 'win32' ? ['.exe', '.cmd', '.bat', ''] : [''];
44
+ /** First existing `<dir>/<cmd><suffix>`, or null. */
45
+ function findOnPath(cmd) {
46
+ const pathVar = process.env.PATH;
47
+ if (!pathVar)
48
+ return null;
49
+ const sep = process.platform === 'win32' ? ';' : ':';
50
+ for (const dir of pathVar.split(sep)) {
51
+ if (!dir)
52
+ continue;
53
+ for (const suffix of EXE_SUFFIXES) {
54
+ const candidate = join(dir, cmd + suffix);
55
+ try {
56
+ if (existsSync(candidate))
57
+ return candidate;
58
+ }
59
+ catch {
60
+ // An unreadable PATH entry is not an answer about `cmd`.
61
+ }
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ /**
67
+ * Find an MSYS2-provided blueprint-compiler.
68
+ *
69
+ * MSYS2 installs it as a SHEBANG SCRIPT with no `.exe` beside it, which Windows
70
+ * cannot execute — so this returns MSYS2's own `python.exe` with the script as
71
+ * its first argument, rather than the script alone. `<env>/bin` is prepended to
72
+ * PATH because the script imports PyGObject, which loads MSYS2's libglib and its
73
+ * typelibs from there.
74
+ *
75
+ * Deliberately does NOT set `GI_TYPELIB_PATH` at the gjsify GTK runtime bundle:
76
+ * those typelibs are gvsbuild's and name `glib-2.0-0.dll` in their
77
+ * `shared_library` field while MSYS2 ships `libglib-2.0-0.dll`. Measured — it
78
+ * fails with "Failed to load shared library", and the obvious repair (put both
79
+ * on PATH) would load two GLib builds into one process, which is a worse problem
80
+ * than the one it solves.
81
+ */
82
+ function findInMsys2() {
83
+ if (process.platform !== 'win32')
84
+ return null;
85
+ for (const root of MSYS2_ROOTS) {
86
+ for (const env of MSYS2_ENVS) {
87
+ const bin = join(root, env, 'bin');
88
+ const script = join(bin, 'blueprint-compiler');
89
+ const python = join(bin, 'python.exe');
90
+ try {
91
+ if (!existsSync(script) || !existsSync(python))
92
+ continue;
93
+ }
94
+ catch {
95
+ continue;
96
+ }
97
+ return {
98
+ file: python,
99
+ prefixArgs: [script],
100
+ env: { PATH: `${bin};${process.env.PATH ?? ''}` },
101
+ source: 'msys2',
102
+ };
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ /**
108
+ * Locate a usable blueprint-compiler, or null when there is none.
109
+ *
110
+ * Order: `BLUEPRINT_COMPILER` (an explicit answer always wins, and is the escape
111
+ * hatch for an install none of the probes below know about) → `PATH` → an MSYS2
112
+ * install on win32.
113
+ */
114
+ export function resolveBlueprintCompiler() {
115
+ const override = process.env.BLUEPRINT_COMPILER;
116
+ if (override) {
117
+ return { file: override, prefixArgs: [], source: 'env' };
118
+ }
119
+ const onPath = findOnPath('blueprint-compiler');
120
+ if (onPath) {
121
+ return { file: onPath, prefixArgs: [], source: 'path' };
122
+ }
123
+ return findInMsys2();
124
+ }
125
+ /**
126
+ * What to tell someone who has no blueprint-compiler.
127
+ *
128
+ * Names the install command for THIS platform rather than listing every one:
129
+ * the reader is on one host and the other two lines are noise they have to
130
+ * filter. `BLUEPRINT_COMPILER` is mentioned everywhere because it is the answer
131
+ * for any install the probes miss.
132
+ */
133
+ export function formatMissingBlueprintCompiler() {
134
+ const install = process.platform === 'win32'
135
+ ? 'MSYS2 packages it prebuilt (it needs PyGObject, which has no Windows wheel, so pip cannot):\n' +
136
+ ' pacman -S mingw-w64-ucrt-x86_64-blueprint-compiler mingw-w64-ucrt-x86_64-gtk4 mingw-w64-ucrt-x86_64-libadwaita\n' +
137
+ ' An MSYS2 install under C:\\msys64, %USERPROFILE%\\msys64, %USERPROFILE%\\Tools\\msys64 or\n' +
138
+ ' C:\\tools\\msys64 is found automatically — it does NOT need to be on PATH.'
139
+ : process.platform === 'darwin'
140
+ ? 'brew install blueprint-compiler'
141
+ : 'sudo dnf install blueprint-compiler (Debian/Ubuntu: sudo apt install blueprint-compiler)';
142
+ return ('blueprint-compiler was not found, so a .blp template cannot be compiled.\n' +
143
+ ` ${install}\n` +
144
+ ' Or set BLUEPRINT_COMPILER to its full path.');
145
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/vite-plugin-blueprint",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "description": "Compile GNOME Blueprint .blp templates to XML strings via blueprint-compiler. Vite / Rollup / Rolldown compatible.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -11,6 +11,10 @@
11
11
  "types": "./lib/index.d.ts",
12
12
  "default": "./lib/index.js"
13
13
  },
14
+ "./resolve": {
15
+ "types": "./lib/resolve-compiler.d.ts",
16
+ "default": "./lib/resolve-compiler.js"
17
+ },
14
18
  "./types": {
15
19
  "types": "./types/blueprint.d.ts"
16
20
  }
@@ -64,6 +68,11 @@
64
68
  "vite": "^8.1.3"
65
69
  },
66
70
  "gjsify": {
67
- "tier": 1
71
+ "tier": 1,
72
+ "os": {
73
+ "linux": "supported",
74
+ "darwin": "supported",
75
+ "win32": "supported"
76
+ }
68
77
  }
69
78
  }