@gjsify/vite-plugin-blueprint 0.52.0 → 0.54.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
@@ -2,6 +2,14 @@ import { type Plugin } from 'vite';
2
2
  export interface BlueprintPluginOptions {
3
3
  minify?: boolean;
4
4
  verbose?: boolean;
5
+ /**
6
+ * Write each `.blp`'s `x.d.blp.ts` sidecar beside it during `load` (ADR 0088 § 4). On by
7
+ * default: the types are what the named exports are FOR, and a build that emits `build()`
8
+ * without them leaves every consumer on the ambient `*.blp` wildcard.
9
+ *
10
+ * A `.blp` under `node_modules` is never written, whatever this says — see `writeSidecar`.
11
+ */
12
+ sidecars?: boolean;
5
13
  }
6
14
  export { BlueprintProjectionError } from './projection-error.js';
7
15
  /** The specifier an import site writes: `./x.blp?shared-tree`. */
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { accessibilityElement, accessibilityValue, emitGtkBuilderXml, enumOrFlagsTypeOf, gtypeName, parseBlueprint, projectToSharedNode, resolveIdent, } from '@gjsify/blueprint';
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { basename } from 'node:path';
3
+ import { accessibilityElement, accessibilityValue, emitGtkBuilderXml, emitTypedModule, emitTypedSidecar, enumOrFlagsTypeOf, gtypeName, parseBlueprint, projectToSharedNode, resolveIdent, sidecarPathFor, } from '@gjsify/blueprint';
3
4
  import minifyXML from 'minify-xml';
4
5
  // Blueprint's OWN two error types are not re-exported here, and that is the shape rather than
5
6
  // an omission. What a parse or an emit throws is `@gjsify/blueprint`'s `BlueprintSyntaxError` /
@@ -86,8 +87,37 @@ function readId(id) {
86
87
  return { file, sharedTree: false };
87
88
  return { file, sharedTree: new URLSearchParams(id.slice(queryAt + 1)).has(SHARED_TREE_PARAM) };
88
89
  }
90
+ /**
91
+ * Write the sidecar beside its `.blp`, unless nothing would change.
92
+ *
93
+ * ONLY ON A DIFFERENCE, for two reasons that both bite in watch mode: a write with identical
94
+ * bytes still moves the mtime, which the watcher sees as a change and turns into a rebuild
95
+ * loop; and a `.blp` whose sidecar is already correct should leave the working tree untouched,
96
+ * so `git status` after a build stays a statement about what the author changed.
97
+ *
98
+ * NEVER UNDER `node_modules`. A `.blp` shipped inside a dependency is not this project's to
99
+ * edit, and the tree may be read-only or a store symlink. A failure to write anywhere else is
100
+ * reported and not thrown: the sidecar is the developer's convenience and
101
+ * `scripts/check-blueprint-sidecars.mjs` is the enforcement, so a read-only checkout should
102
+ * still build.
103
+ */
104
+ async function writeSidecar(file, text, verbose) {
105
+ if (file.includes('node_modules'))
106
+ return;
107
+ const path = sidecarPathFor(file);
108
+ try {
109
+ if ((await readFile(path, 'utf8').catch(() => undefined)) === text)
110
+ return;
111
+ await writeFile(path, text, 'utf8');
112
+ if (verbose)
113
+ console.log(`Wrote ${path} (@gjsify/vite-plugin-blueprint)`);
114
+ }
115
+ catch (error) {
116
+ console.warn(`vite-plugin-blueprint: could not write ${path}: ${error.message}`);
117
+ }
118
+ }
89
119
  export default function blueprintPlugin(options = {}) {
90
- const { minify = false, verbose = false } = options;
120
+ const { minify = false, verbose = false, sidecars = true } = options;
91
121
  return {
92
122
  name: 'vite-plugin-blueprint',
93
123
  // The query has to be resolved by hand: the default resolver stats the specifier, and
@@ -152,8 +182,21 @@ export default function blueprintPlugin(options = {}) {
152
182
  if (verbose)
153
183
  console.log(`Minified XML for ${asked.file}`);
154
184
  }
155
- // Return the XML content as a string
156
- return `export default ${JSON.stringify(xmlContent)};`;
185
+ // ADR 0088's named exports, beside the `default` that does not move. The module text
186
+ // is `@gjsify/blueprint`'s, not this file's, because the sidecar DECLARES what this
187
+ // implements and the two have to be written together — `typed-exports.mjs` § the
188
+ // MODULE a bundler gets says why they share a file.
189
+ //
190
+ // The sidecar is written AFTER the module is emitted, so a `.blp` the derivation
191
+ // refuses leaves no sidecar behind claiming exports the module does not have.
192
+ const moduleText = emitTypedModule(ast, xmlContent);
193
+ // `basename` from `node:path`, and this plugin is the one caller for which the HOST is
194
+ // the authority: a vite/rolldown plugin only ever runs in the Node process driving the
195
+ // build, on the machine whose paths `asked.file` is a path on. `@gjsify/blueprint`
196
+ // cannot do this itself — see `typed-exports.mjs` § `emitTypedSidecar`.
197
+ if (sidecars)
198
+ await writeSidecar(asked.file, emitTypedSidecar(ast, basename(asked.file)), verbose);
199
+ return moduleText;
157
200
  },
158
201
  };
159
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/vite-plugin-blueprint",
3
- "version": "0.52.0",
3
+ "version": "0.54.0",
4
4
  "description": "Compile GNOME Blueprint .blp templates to GtkBuilder-XML strings with the in-repo @gjsify/blueprint parser: no blueprint-compiler, no spawn. Vite / Rollup / Rolldown compatible.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -47,7 +47,7 @@
47
47
  ],
48
48
  "license": "MIT",
49
49
  "dependencies": {
50
- "@gjsify/blueprint": "^0.52.0",
50
+ "@gjsify/blueprint": "^0.54.0",
51
51
  "minify-xml": "^4.5.2"
52
52
  },
53
53
  "peerDependencies": {
@@ -63,8 +63,8 @@
63
63
  }
64
64
  },
65
65
  "devDependencies": {
66
- "@gjsify/cli": "^0.52.0",
67
- "@gjsify/unit": "^0.52.0",
66
+ "@gjsify/cli": "^0.54.0",
67
+ "@gjsify/unit": "^0.54.0",
68
68
  "@types/node": "^25.9.2",
69
69
  "typescript": "^6.0.3",
70
70
  "vite": "^8.1.3"
@@ -1,3 +1,16 @@
1
+ /**
2
+ * The FALLBACK, and only the fallback, since ADR 0088.
3
+ *
4
+ * A `.blp` with an `x.d.blp.ts` sidecar beside it is typed by that sidecar instead — measured on
5
+ * TypeScript 6.0.3, a sidecar read under `allowArbitraryExtensions` wins over this wildcard. This
6
+ * stays because the migration is per-file and must be: a `.blp` with no sidecar keeps typing as
7
+ * the XML string it has always been, so nothing had to move when the named exports landed.
8
+ *
9
+ * It is also what makes the missing-sidecar case LOUD rather than silent. A consumer writing
10
+ * `import { build } from './x.blp'` against a file with no sidecar lands here, where only
11
+ * `default` exists, and `tsc` reports TS2614 naming each missing member — which is why
12
+ * `scripts/check-blueprint-sidecars.mjs` does not check for that case itself.
13
+ */
1
14
  declare module '*.blp' {
2
15
  const content: string;
3
16
  export default content;