@gtkx/codegen 1.3.0 → 1.4.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.
Files changed (47) hide show
  1. package/README.md +5 -4
  2. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  3. package/dist/analysis/descriptor-render.js +1 -0
  4. package/dist/analysis/descriptor-render.js.map +1 -1
  5. package/dist/analysis/descriptor.d.ts +1 -0
  6. package/dist/analysis/descriptor.d.ts.map +1 -1
  7. package/dist/analysis/descriptor.js +1 -0
  8. package/dist/analysis/descriptor.js.map +1 -1
  9. package/dist/fingerprint.d.ts.map +1 -1
  10. package/dist/fingerprint.js +5 -1
  11. package/dist/fingerprint.js.map +1 -1
  12. package/dist/gi.d.ts.map +1 -1
  13. package/dist/gi.js +5 -1
  14. package/dist/gi.js.map +1 -1
  15. package/dist/guards.d.ts +2 -1
  16. package/dist/guards.d.ts.map +1 -1
  17. package/dist/guards.js +3 -1
  18. package/dist/guards.js.map +1 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/store/gi/generated-libraries.d.ts +26 -0
  24. package/dist/store/gi/generated-libraries.d.ts.map +1 -0
  25. package/dist/store/gi/generated-libraries.js +53 -0
  26. package/dist/store/gi/generated-libraries.js.map +1 -0
  27. package/dist/store/gi-store.d.ts +6 -1
  28. package/dist/store/gi-store.d.ts.map +1 -1
  29. package/dist/store/gi-store.js +6 -2
  30. package/dist/store/gi-store.js.map +1 -1
  31. package/dist/store/jsx/metadata.d.ts.map +1 -1
  32. package/dist/store/jsx/metadata.js +35 -60
  33. package/dist/store/jsx/metadata.js.map +1 -1
  34. package/dist/store/resolve-store.d.ts.map +1 -1
  35. package/dist/store/resolve-store.js +1 -0
  36. package/dist/store/resolve-store.js.map +1 -1
  37. package/package.json +5 -5
  38. package/src/analysis/descriptor-render.ts +1 -0
  39. package/src/analysis/descriptor.ts +2 -0
  40. package/src/fingerprint.ts +8 -1
  41. package/src/gi.ts +5 -1
  42. package/src/guards.ts +6 -1
  43. package/src/index.ts +2 -0
  44. package/src/store/gi/generated-libraries.ts +91 -0
  45. package/src/store/gi-store.ts +15 -2
  46. package/src/store/jsx/metadata.ts +46 -99
  47. package/src/store/resolve-store.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-store.js","sourceRoot":"","sources":["../../src/store/resolve-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { dirname, join, parse, relative, sep } from \"node:path\";\nimport type { StoreOptions } from \"./store-fs.js\";\nimport { sweepStagingDirs } from \"../staging.js\";\n\n/**\n * Where a project's generated stores live, ready to spread into `runCodegen`. `jsx` is null when the\n * project has no `@gtkx/react` installed, in which case only the `@gtkx/gi` store can be generated.\n */\ntype ResolvedStore = {\n /** Where the `@gtkx/gi` store goes, versioned by the installed `@gtkx/runtime`. */\n gi: StoreOptions;\n /** Where the `@gtkx/jsx` store goes, versioned by the installed `@gtkx/react`; null when it is absent. */\n jsx: StoreOptions | null;\n /** Subexport names of the installed `@gtkx/react`, empty when it is absent. */\n reactSubexports: string[];\n};\n\ntype ResolvedPackage = { dir: string; nodeModules: string; version: string };\ntype StoreConsumer = { name: string; nodeModules: string };\n\nconst STORE_DIR = \".gtkx\";\nconst SCOPE = \"@gtkx\";\nconst STORE_NAMES: string[] = [\"gi\", \"jsx\"];\n\nconst STORE_CONSUMERS: string[] = [\n \"@gtkx/animated\",\n \"@gtkx/cli\",\n \"@gtkx/components\",\n \"@gtkx/css\",\n \"@gtkx/react\",\n \"@gtkx/runtime\",\n \"@gtkx/testing\",\n];\n\nconst readManifest = (path: string): { version?: string; exports?: Record<string, unknown> } =>\n JSON.parse(readFileSync(path, \"utf8\")) as { version?: string; exports?: Record<string, unknown> };\n\nconst subexportNames = (packageDir: string): string[] =>\n Object.keys(readManifest(join(packageDir, \"package.json\")).exports ?? {})\n .filter((key) => key.startsWith(\"./\") && key !== \"./package.json\")\n .map((key) => key.slice(2));\n\nconst loadPackage = (manifest: string, nodeModules: string): ResolvedPackage | null => {\n if (!existsSync(manifest)) {\n return null;\n }\n\n const real = realpathSync(manifest);\n\n return { dir: dirname(real), nodeModules, version: readManifest(real).version ?? \"0.0.0\" };\n};\n\nconst nodeModulesChain = function* (projectRoot: string): Generator<string> {\n const { root } = parse(projectRoot);\n let current = projectRoot;\n\n while (current !== root) {\n yield join(current, \"node_modules\");\n current = dirname(current);\n }\n\n yield join(root, \"node_modules\");\n};\n\nconst resolvePackage = (projectRoot: string, packageName: string): ResolvedPackage | null => {\n for (const nodeModules of nodeModulesChain(projectRoot)) {\n const found = loadPackage(join(nodeModules, packageName, \"package.json\"), nodeModules);\n\n if (found !== null) {\n return found;\n }\n }\n\n const unscoped = packageName.replace(/^@[^/]+\\//, \"\");\n\n return loadPackage(join(projectRoot, \"packages\", unscoped, \"package.json\"), join(projectRoot, \"node_modules\"));\n};\n\nconst isWithin = (parent: string, child: string): boolean => {\n const path = relative(parent, child);\n\n return path === \"\" || (path !== \"..\" && !path.startsWith(`..${sep}`));\n};\n\nconst canImport = (fromNodeModules: string, targetNodeModules: string): boolean =>\n isWithin(dirname(targetNodeModules), dirname(fromNodeModules));\n\nconst storeOptions = (nodeModules: string, name: string, version: string): StoreOptions => ({\n storeDir: join(nodeModules, STORE_DIR, name),\n linkDir: join(nodeModules, SCOPE, name),\n version,\n});\n\nconst resolveRuntime = (projectRoot: string): ResolvedPackage => {\n const runtime = resolvePackage(projectRoot, \"@gtkx/runtime\");\n\n if (runtime === null) {\n throw new Error(`Cannot resolve @gtkx/runtime from ${projectRoot}; is it installed?`);\n }\n\n return runtime;\n};\n\nconst storeNodeModules = (runtime: ResolvedPackage, react: ResolvedPackage | null): string => {\n if (react !== null && canImport(react.nodeModules, runtime.nodeModules)) {\n return react.nodeModules;\n }\n\n return runtime.nodeModules;\n};\n\nconst findUnreachableConsumer = (projectRoot: string, nodeModules: string): StoreConsumer | null => {\n for (const name of STORE_CONSUMERS) {\n const consumer = resolvePackage(projectRoot, name);\n\n if (consumer !== null && !canImport(consumer.nodeModules, nodeModules)) {\n return { name, nodeModules: consumer.nodeModules };\n }\n }\n\n return null;\n};\n\nconst unreachableConsumerMessage = (consumer: StoreConsumer, nodeModules: string): string =>\n `Cannot write the generated store to ${nodeModules}: ${consumer.name} is installed in ` +\n `${consumer.nodeModules}, above it, so that copy can never import the generated @gtkx/gi. Install every ` +\n \"@gtkx package in one node_modules, then run gtkx codegen again.\";\n\nconst checkConsumers = (projectRoot: string, nodeModules: string): void => {\n const consumer = findUnreachableConsumer(projectRoot, nodeModules);\n\n if (consumer !== null) {\n throw new Error(unreachableConsumerMessage(consumer, nodeModules));\n }\n};\n\nconst jsxOptions = (react: ResolvedPackage | null, nodeModules: string): StoreOptions | null =>\n react === null ? null : storeOptions(nodeModules, \"jsx\", react.version);\n\nconst getReactSubexports = (react: ResolvedPackage | null): string[] =>\n react === null ? [] : subexportNames(react.dir);\n\nconst storePaths = (nodeModules: string): string[] =>\n STORE_NAMES.flatMap((name) => [join(nodeModules, STORE_DIR, name), join(nodeModules, SCOPE, name)]);\n\nconst findStoreNodeModules = (projectRoot: string): string | null => {\n const runtime = resolvePackage(projectRoot, \"@gtkx/runtime\");\n\n if (runtime === null) {\n return null;\n }\n\n return storeNodeModules(runtime, resolvePackage(projectRoot, \"@gtkx/react\"));\n};\n\nconst getShadowingStorePaths = (projectRoot: string): string[] => {\n const nodeModules = join(projectRoot, \"node_modules\");\n const anchored = findStoreNodeModules(projectRoot);\n\n if (anchored === null || anchored === nodeModules) {\n return [];\n }\n\n return storePaths(nodeModules);\n};\n\nconst stagingRoots = (projectRoot: string): string[] => {\n const nodeModules = join(projectRoot, \"node_modules\");\n const anchored = findStoreNodeModules(projectRoot);\n\n return anchored === null || anchored === nodeModules ? [nodeModules] : [nodeModules, anchored];\n};\n\nconst sweepProjectStaging = (projectRoot: string): void => {\n for (const nodeModules of stagingRoots(projectRoot)) {\n for (const name of STORE_NAMES) {\n sweepStagingDirs(join(nodeModules, STORE_DIR, name));\n }\n }\n};\n\n/**\n * Resolves where a project's `@gtkx/gi` and `@gtkx/jsx` stores belong, from the project root alone. The\n * result supplies every `runCodegen` input except `libraries` and `girPath`, so a caller that has\n * already resolved those can spread it straight into the call.\n *\n * Both stores go in one `node_modules`, found by walking the project's `node_modules` chain upwards:\n * the one `@gtkx/react` resolves from, or `@gtkx/runtime`'s when no React is installed. That is the\n * project's own directory when it installs those packages itself, and the workspace root when npm or\n * yarn hoisted them there. Keeping the pair together is what lets the jsx store import the gi store, and\n * anchoring them where the `@gtkx` packages sit is what lets `@gtkx/react`, the CLI, and the project's\n * sources all reach the bindings. A package manager that hoists therefore gives every project sharing\n * that `node_modules` one shared store.\n *\n * Store versions come from the installed `@gtkx/runtime` and `@gtkx/react`, so a dependency upgrade\n * invalidates the stores. Pass explicit `gi` or `jsx` options to `runCodegen` to override any of it.\n *\n * @param projectRoot Directory holding the project's `package.json`, whose `node_modules` chain is walked.\n * @returns The store locations and the React subexports that shape the jsx store.\n * @throws If `@gtkx/runtime` cannot be resolved from the project, or a `@gtkx` package that imports the\n * bindings is installed above the `node_modules` the stores would go in, since it could not reach them.\n */\nconst resolveStore = (projectRoot: string): ResolvedStore => {\n const runtime = resolveRuntime(projectRoot);\n const react = resolvePackage(projectRoot, \"@gtkx/react\");\n const nodeModules = storeNodeModules(runtime, react);\n checkConsumers(projectRoot, nodeModules);\n\n return {\n gi: storeOptions(nodeModules, \"gi\", runtime.version),\n jsx: jsxOptions(react, nodeModules),\n reactSubexports: getReactSubexports(react),\n };\n};\n\nexport { getShadowingStorePaths, nodeModulesChain, resolveStore, sweepProjectStaging, type ResolvedStore };\n"]}
1
+ {"version":3,"file":"resolve-store.js","sourceRoot":"","sources":["../../src/store/resolve-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { dirname, join, parse, relative, sep } from \"node:path\";\nimport type { StoreOptions } from \"./store-fs.js\";\nimport { sweepStagingDirs } from \"../staging.js\";\n\n/**\n * Where a project's generated stores live, ready to spread into `runCodegen`. `jsx` is null when the\n * project has no `@gtkx/react` installed, in which case only the `@gtkx/gi` store can be generated.\n */\ntype ResolvedStore = {\n /** Where the `@gtkx/gi` store goes, versioned by the installed `@gtkx/runtime`. */\n gi: StoreOptions;\n /** Where the `@gtkx/jsx` store goes, versioned by the installed `@gtkx/react`; null when it is absent. */\n jsx: StoreOptions | null;\n /** Subexport names of the installed `@gtkx/react`, empty when it is absent. */\n reactSubexports: string[];\n};\n\ntype ResolvedPackage = { dir: string; nodeModules: string; version: string };\ntype StoreConsumer = { name: string; nodeModules: string };\n\nconst STORE_DIR = \".gtkx\";\nconst SCOPE = \"@gtkx\";\nconst STORE_NAMES: string[] = [\"gi\", \"jsx\"];\n\nconst STORE_CONSUMERS: string[] = [\n \"@gtkx/animated\",\n \"@gtkx/cli\",\n \"@gtkx/components\",\n \"@gtkx/css\",\n \"@gtkx/navigation\",\n \"@gtkx/react\",\n \"@gtkx/runtime\",\n \"@gtkx/testing\",\n];\n\nconst readManifest = (path: string): { version?: string; exports?: Record<string, unknown> } =>\n JSON.parse(readFileSync(path, \"utf8\")) as { version?: string; exports?: Record<string, unknown> };\n\nconst subexportNames = (packageDir: string): string[] =>\n Object.keys(readManifest(join(packageDir, \"package.json\")).exports ?? {})\n .filter((key) => key.startsWith(\"./\") && key !== \"./package.json\")\n .map((key) => key.slice(2));\n\nconst loadPackage = (manifest: string, nodeModules: string): ResolvedPackage | null => {\n if (!existsSync(manifest)) {\n return null;\n }\n\n const real = realpathSync(manifest);\n\n return { dir: dirname(real), nodeModules, version: readManifest(real).version ?? \"0.0.0\" };\n};\n\nconst nodeModulesChain = function* (projectRoot: string): Generator<string> {\n const { root } = parse(projectRoot);\n let current = projectRoot;\n\n while (current !== root) {\n yield join(current, \"node_modules\");\n current = dirname(current);\n }\n\n yield join(root, \"node_modules\");\n};\n\nconst resolvePackage = (projectRoot: string, packageName: string): ResolvedPackage | null => {\n for (const nodeModules of nodeModulesChain(projectRoot)) {\n const found = loadPackage(join(nodeModules, packageName, \"package.json\"), nodeModules);\n\n if (found !== null) {\n return found;\n }\n }\n\n const unscoped = packageName.replace(/^@[^/]+\\//, \"\");\n\n return loadPackage(join(projectRoot, \"packages\", unscoped, \"package.json\"), join(projectRoot, \"node_modules\"));\n};\n\nconst isWithin = (parent: string, child: string): boolean => {\n const path = relative(parent, child);\n\n return path === \"\" || (path !== \"..\" && !path.startsWith(`..${sep}`));\n};\n\nconst canImport = (fromNodeModules: string, targetNodeModules: string): boolean =>\n isWithin(dirname(targetNodeModules), dirname(fromNodeModules));\n\nconst storeOptions = (nodeModules: string, name: string, version: string): StoreOptions => ({\n storeDir: join(nodeModules, STORE_DIR, name),\n linkDir: join(nodeModules, SCOPE, name),\n version,\n});\n\nconst resolveRuntime = (projectRoot: string): ResolvedPackage => {\n const runtime = resolvePackage(projectRoot, \"@gtkx/runtime\");\n\n if (runtime === null) {\n throw new Error(`Cannot resolve @gtkx/runtime from ${projectRoot}; is it installed?`);\n }\n\n return runtime;\n};\n\nconst storeNodeModules = (runtime: ResolvedPackage, react: ResolvedPackage | null): string => {\n if (react !== null && canImport(react.nodeModules, runtime.nodeModules)) {\n return react.nodeModules;\n }\n\n return runtime.nodeModules;\n};\n\nconst findUnreachableConsumer = (projectRoot: string, nodeModules: string): StoreConsumer | null => {\n for (const name of STORE_CONSUMERS) {\n const consumer = resolvePackage(projectRoot, name);\n\n if (consumer !== null && !canImport(consumer.nodeModules, nodeModules)) {\n return { name, nodeModules: consumer.nodeModules };\n }\n }\n\n return null;\n};\n\nconst unreachableConsumerMessage = (consumer: StoreConsumer, nodeModules: string): string =>\n `Cannot write the generated store to ${nodeModules}: ${consumer.name} is installed in ` +\n `${consumer.nodeModules}, above it, so that copy can never import the generated @gtkx/gi. Install every ` +\n \"@gtkx package in one node_modules, then run gtkx codegen again.\";\n\nconst checkConsumers = (projectRoot: string, nodeModules: string): void => {\n const consumer = findUnreachableConsumer(projectRoot, nodeModules);\n\n if (consumer !== null) {\n throw new Error(unreachableConsumerMessage(consumer, nodeModules));\n }\n};\n\nconst jsxOptions = (react: ResolvedPackage | null, nodeModules: string): StoreOptions | null =>\n react === null ? null : storeOptions(nodeModules, \"jsx\", react.version);\n\nconst getReactSubexports = (react: ResolvedPackage | null): string[] =>\n react === null ? [] : subexportNames(react.dir);\n\nconst storePaths = (nodeModules: string): string[] =>\n STORE_NAMES.flatMap((name) => [join(nodeModules, STORE_DIR, name), join(nodeModules, SCOPE, name)]);\n\nconst findStoreNodeModules = (projectRoot: string): string | null => {\n const runtime = resolvePackage(projectRoot, \"@gtkx/runtime\");\n\n if (runtime === null) {\n return null;\n }\n\n return storeNodeModules(runtime, resolvePackage(projectRoot, \"@gtkx/react\"));\n};\n\nconst getShadowingStorePaths = (projectRoot: string): string[] => {\n const nodeModules = join(projectRoot, \"node_modules\");\n const anchored = findStoreNodeModules(projectRoot);\n\n if (anchored === null || anchored === nodeModules) {\n return [];\n }\n\n return storePaths(nodeModules);\n};\n\nconst stagingRoots = (projectRoot: string): string[] => {\n const nodeModules = join(projectRoot, \"node_modules\");\n const anchored = findStoreNodeModules(projectRoot);\n\n return anchored === null || anchored === nodeModules ? [nodeModules] : [nodeModules, anchored];\n};\n\nconst sweepProjectStaging = (projectRoot: string): void => {\n for (const nodeModules of stagingRoots(projectRoot)) {\n for (const name of STORE_NAMES) {\n sweepStagingDirs(join(nodeModules, STORE_DIR, name));\n }\n }\n};\n\n/**\n * Resolves where a project's `@gtkx/gi` and `@gtkx/jsx` stores belong, from the project root alone. The\n * result supplies every `runCodegen` input except `libraries` and `girPath`, so a caller that has\n * already resolved those can spread it straight into the call.\n *\n * Both stores go in one `node_modules`, found by walking the project's `node_modules` chain upwards:\n * the one `@gtkx/react` resolves from, or `@gtkx/runtime`'s when no React is installed. That is the\n * project's own directory when it installs those packages itself, and the workspace root when npm or\n * yarn hoisted them there. Keeping the pair together is what lets the jsx store import the gi store, and\n * anchoring them where the `@gtkx` packages sit is what lets `@gtkx/react`, the CLI, and the project's\n * sources all reach the bindings. A package manager that hoists therefore gives every project sharing\n * that `node_modules` one shared store.\n *\n * Store versions come from the installed `@gtkx/runtime` and `@gtkx/react`, so a dependency upgrade\n * invalidates the stores. Pass explicit `gi` or `jsx` options to `runCodegen` to override any of it.\n *\n * @param projectRoot Directory holding the project's `package.json`, whose `node_modules` chain is walked.\n * @returns The store locations and the React subexports that shape the jsx store.\n * @throws If `@gtkx/runtime` cannot be resolved from the project, or a `@gtkx` package that imports the\n * bindings is installed above the `node_modules` the stores would go in, since it could not reach them.\n */\nconst resolveStore = (projectRoot: string): ResolvedStore => {\n const runtime = resolveRuntime(projectRoot);\n const react = resolvePackage(projectRoot, \"@gtkx/react\");\n const nodeModules = storeNodeModules(runtime, react);\n checkConsumers(projectRoot, nodeModules);\n\n return {\n gi: storeOptions(nodeModules, \"gi\", runtime.version),\n jsx: jsxOptions(react, nodeModules),\n reactSubexports: getReactSubexports(react),\n };\n};\n\nexport { getShadowingStorePaths, nodeModulesChain, resolveStore, sweepProjectStaging, type ResolvedStore };\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gtkx/codegen",
3
- "version": "1.3.0",
4
- "description": "Generates the typed GTKX bindings and API reference from GIR and OpenGL registry data",
3
+ "version": "1.4.0",
4
+ "description": "Generates typed GTK bindings and API docs from GIR and GL registries.",
5
5
  "keywords": [
6
6
  "gtkx",
7
7
  "gtk",
@@ -58,9 +58,9 @@
58
58
  "fast-xml-parser": "^5.11.0",
59
59
  "fast-xml-validator": "^1.4.2",
60
60
  "typescript": "^6.0.3",
61
- "@gtkx/cairo": "1.3.0",
62
- "@gtkx/react": "1.3.0",
63
- "@gtkx/utils": "1.3.0"
61
+ "@gtkx/cairo": "1.4.0",
62
+ "@gtkx/utils": "1.4.0",
63
+ "@gtkx/react": "1.4.0"
64
64
  },
65
65
  "scripts": {
66
66
  "release": "tsx ../../scripts/release-package.ts"
@@ -619,6 +619,7 @@ const boxedRecordExpression = (options: {
619
619
  ownership,
620
620
  sharedLibrary: resolved.namespace.sharedLibrary,
621
621
  getTypeFnName: typeFnName,
622
+ freeFnName: record.freeFunc,
622
623
  isCallerAllocated,
623
624
  isInline,
624
625
  size: size > 0 ? size : undefined,
@@ -66,6 +66,7 @@ type BoxedOptions = {
66
66
  ownership: Ownership;
67
67
  sharedLibrary: string | undefined;
68
68
  getTypeFnName: string;
69
+ freeFnName: string | undefined;
69
70
  isCallerAllocated: boolean;
70
71
  isInline?: boolean;
71
72
  size: number | undefined;
@@ -193,6 +194,7 @@ const tBoxed = (glibName: string, options: BoxedOptions): string =>
193
194
  ? undefined
194
195
  : `sharedLibrary: ${sourceStringLiteral(options.sharedLibrary)}`,
195
196
  `getTypeFnName: ${sourceStringLiteral(options.getTypeFnName)}`,
197
+ options.freeFnName === undefined ? undefined : `freeFnName: ${sourceStringLiteral(options.freeFnName)}`,
196
198
  options.isCallerAllocated ? "isCallerAllocated: true" : undefined,
197
199
  options.isInline === true ? "isInline: true" : undefined,
198
200
  options.size === undefined ? undefined : `size: ${String(options.size)}`,
@@ -150,7 +150,14 @@ const dependencyVersions = (): string[] =>
150
150
  );
151
151
 
152
152
  const hashPackageCode = (hash: Hash, label: string, root: string): void => {
153
- hashTree(hash, join(label, "src"), join(root, "src"));
153
+ const source = join(root, "src");
154
+
155
+ if (existsSync(source)) {
156
+ hashTree(hash, join(label, "src"), source);
157
+
158
+ return;
159
+ }
160
+
154
161
  hashTree(hash, join(label, "dist"), join(root, "dist"));
155
162
  };
156
163
 
package/src/gi.ts CHANGED
@@ -4,6 +4,7 @@ import { computeGiFingerprint } from "./fingerprint.js";
4
4
  import { externalPackageFor } from "./gir/external-namespaces.js";
5
5
  import { namespaceDirectory } from "./gir/namespace.js";
6
6
  import { type GiExternalNamespaceInput, type GiNamespaceInput, writeGiStore } from "./store/gi-store.js";
7
+ import { collectGeneratedLibraries } from "./store/gi/generated-libraries.js";
7
8
  import { generateNamespaceModule } from "./store/gi/pipeline.js";
8
9
 
9
10
  type GiCodegenOptions = {
@@ -63,7 +64,10 @@ const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
63
64
  isInoutInPlace,
64
65
  });
65
66
 
66
- writeGiStore(gi, namespaces, externalNamespaces, fingerprint);
67
+ writeGiStore(gi, namespaces, externalNamespaces, {
68
+ fingerprint,
69
+ libraries: collectGeneratedLibraries(library.namespaces, libraries),
70
+ });
67
71
 
68
72
  return library.namespaces.size;
69
73
  };
package/src/guards.ts CHANGED
@@ -12,6 +12,11 @@ const isString = (value: unknown): value is string => typeof value === "string";
12
12
  const arrayGuard = <T>(isEntry: Guard<T>): Guard<T[]> =>
13
13
  (value: unknown): value is T[] => Array.isArray(value) && value.every((entry: unknown) => isEntry(entry));
14
14
 
15
+ const recordGuard = <T>(isEntry: Guard<T>): Guard<Record<string, T>> =>
16
+ (value: unknown): value is Record<string, T> =>
17
+ isRecord(value) && !Array.isArray(value) &&
18
+ Object.values(value).every((entry: unknown) => isEntry(entry));
19
+
15
20
  const optionalGuard = <T>(isPresent: Guard<T>): OptionalGuard<T> =>
16
21
  Object.assign((value: unknown): value is T | undefined => value === undefined || isPresent(value), {
17
22
  __optional__: true as const,
@@ -20,4 +25,4 @@ const optionalGuard = <T>(isPresent: Guard<T>): OptionalGuard<T> =>
20
25
  const hasFields = <T extends object>(value: unknown, guards: FieldGuards<T>): value is T =>
21
26
  isRecord(value) && Object.entries(guards).every(([name, guard]) => (guard as Guard<unknown>)(value[name]));
22
27
 
23
- export { arrayGuard, hasFields, isBoolean, isNumber, isString, optionalGuard };
28
+ export { arrayGuard, hasFields, isBoolean, isNumber, isString, optionalGuard, recordGuard };
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ export { type BuiltinElements, type ModuleExport, readBuiltinElements } from "./
20
20
  /** @public */
21
21
  export { type CodegenRunnerOptions, type CodegenRunnerResult, runCodegen } from "./runner.js";
22
22
  /** @public */
23
+ export { type GeneratedLibraries, readGeneratedLibraries } from "./store/gi/generated-libraries.js";
24
+ /** @public */
23
25
  export type { ElementProps } from "./store/jsx/element-prop-imports.js";
24
26
  /** @public */
25
27
  export { type GeneratedElement, readGeneratedElements } from "./store/jsx/generated-elements.js";
@@ -0,0 +1,91 @@
1
+ import { sortStrings } from "@gtkx/utils";
2
+ import { join } from "node:path";
3
+ import type { GirNamespace } from "../../gir/namespace.js";
4
+ import { arrayGuard, hasFields, isString, recordGuard } from "../../guards.js";
5
+ import { readJsonFile } from "../../json.js";
6
+
7
+ /** What a generated `@gtkx/gi` store binds, and the library releases it was generated against. */
8
+ type GeneratedLibraries = {
9
+ /** GIR identifiers the store binds, such as `Gtk-4.0`, expanded from the project's `libraries`. */
10
+ libraries: string[];
11
+ /**
12
+ * `MAJOR.MINOR` release, keyed by GIR identifier. A library whose GIR declares no `MAJOR_VERSION`
13
+ * and `MINOR_VERSION` is absent, so an entry is present only for those that name their release.
14
+ */
15
+ versions: Record<string, string>;
16
+ };
17
+
18
+ const LIBRARIES_FILENAME = "libraries.json";
19
+ const MAJOR_CONSTANT = "MAJOR_VERSION";
20
+ const MINOR_CONSTANT = "MINOR_VERSION";
21
+ const DIGITS = /^\d+$/;
22
+
23
+ const constantValue = (namespace: GirNamespace, name: string): string | undefined => {
24
+ const constant = namespace.constants.find((entry) => entry.name === name);
25
+
26
+ return constant !== undefined && DIGITS.test(constant.value) ? constant.value : undefined;
27
+ };
28
+
29
+ const namespaceVersion = (namespace: GirNamespace): string | undefined => {
30
+ const major = constantValue(namespace, MAJOR_CONSTANT);
31
+ const minor = constantValue(namespace, MINOR_CONSTANT);
32
+
33
+ return major === undefined || minor === undefined ? undefined : `${major}.${minor}`;
34
+ };
35
+
36
+ const getNamespaceName = (library: string): string => {
37
+ const separator = library.indexOf("-");
38
+
39
+ return separator === -1 ? library : library.slice(0, separator);
40
+ };
41
+
42
+ const collectGeneratedLibraries = (
43
+ namespaces: Map<string, GirNamespace>,
44
+ libraries: string[],
45
+ ): GeneratedLibraries => {
46
+ const sorted = sortStrings(libraries);
47
+ const versions: Record<string, string> = {};
48
+
49
+ for (const library of sorted) {
50
+ const namespace = namespaces.get(getNamespaceName(library));
51
+ const version = namespace === undefined ? undefined : namespaceVersion(namespace);
52
+
53
+ if (version !== undefined) {
54
+ versions[library] = version;
55
+ }
56
+ }
57
+
58
+ return { libraries: sorted, versions };
59
+ };
60
+
61
+ const renderGeneratedLibraries = (generated: GeneratedLibraries): string =>
62
+ `${JSON.stringify(generated, null, 2)}\n`;
63
+
64
+ const isGeneratedLibraries = (value: unknown): value is GeneratedLibraries =>
65
+ hasFields<GeneratedLibraries>(value, {
66
+ libraries: arrayGuard(isString),
67
+ versions: recordGuard(isString),
68
+ });
69
+
70
+ /**
71
+ * Reads which GIR libraries a generated `@gtkx/gi` store binds and which release of each its GIR declared,
72
+ * out of the inventory codegen writes into the store. Answers "what does this project bind, and how new does
73
+ * the installed library have to be" without parsing a `.gir` file or querying pkg-config.
74
+ *
75
+ * @param giStoreDir The gi store directory, as given by `resolveStore(projectRoot).gi.storeDir`.
76
+ * @returns What the store recorded, or null when it holds no inventory codegen wrote, which covers an absent,
77
+ * unreadable, unparseable and structurally foreign store file alike.
78
+ */
79
+ const readGeneratedLibraries = (giStoreDir: string): GeneratedLibraries | null => {
80
+ const parsed = readJsonFile(join(giStoreDir, LIBRARIES_FILENAME));
81
+
82
+ return isGeneratedLibraries(parsed) ? parsed : null;
83
+ };
84
+
85
+ export {
86
+ LIBRARIES_FILENAME,
87
+ collectGeneratedLibraries,
88
+ type GeneratedLibraries,
89
+ readGeneratedLibraries,
90
+ renderGeneratedLibraries,
91
+ };
@@ -4,6 +4,11 @@ import { dirname, join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import type { SourceModule } from "../compile.js";
6
6
  import { FINGERPRINT_FILENAME, type GiFingerprint } from "../fingerprint.js";
7
+ import {
8
+ type GeneratedLibraries,
9
+ LIBRARIES_FILENAME,
10
+ renderGeneratedLibraries,
11
+ } from "./gi/generated-libraries.js";
7
12
  import { buildManifest, namespaceBarrel, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
8
13
 
9
14
  type GiNamespaceInput = {
@@ -18,6 +23,11 @@ type GiExternalNamespaceInput = {
18
23
  girFile: string;
19
24
  };
20
25
 
26
+ type GiStoreRecords = {
27
+ fingerprint: GiFingerprint;
28
+ libraries: GeneratedLibraries;
29
+ };
30
+
21
31
  const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "overrides");
22
32
 
23
33
  const overrideFiles = (directory: string): SourceModule[] => {
@@ -94,7 +104,7 @@ const writeGiStore = (
94
104
  options: StoreOptions,
95
105
  namespaces: GiNamespaceInput[],
96
106
  externalNamespaces: GiExternalNamespaceInput[],
97
- fingerprint: GiFingerprint,
107
+ records: GiStoreRecords,
98
108
  ): void => {
99
109
  const { collected, exportsMap } = collectStoreSources(namespaces, externalNamespaces);
100
110
 
@@ -108,7 +118,10 @@ const writeGiStore = (
108
118
  exports: exportsMap,
109
119
  peerDependencies: storePeerDependencies(externalNamespaces),
110
120
  }),
111
- rawFiles: [{ relativePath: FINGERPRINT_FILENAME, content: `${JSON.stringify(fingerprint, null, 2)}\n` }],
121
+ rawFiles: [
122
+ { relativePath: FINGERPRINT_FILENAME, content: `${JSON.stringify(records.fingerprint, null, 2)}\n` },
123
+ { relativePath: LIBRARIES_FILENAME, content: renderGeneratedLibraries(records.libraries) },
124
+ ],
112
125
  });
113
126
  };
114
127
 
@@ -17,22 +17,17 @@ import {
17
17
  type IntrinsicElementEntry = {
18
18
  glibName: string;
19
19
  signals: [string, string][];
20
- constructOnly: string[];
21
- constructable: string[];
22
- defaults: [string, string][];
20
+ properties: [string, string][];
23
21
  };
24
22
 
25
- type PropNameCollector = {
26
- shouldKeep: (property: GirProperty) => boolean;
27
- seen: Set<string>;
28
- names: string[];
29
- };
23
+ const READABLE = 1;
24
+ const WRITABLE = 2;
25
+ const CONSTRUCT = 4;
26
+ const CONSTRUCT_ONLY = 8;
30
27
 
31
- type DefaultPropsCollector = {
32
- library: Library;
33
- seen: Set<string>;
34
- defaults: [string, string][];
35
- };
28
+ const PROPERTY_ENTRY_TYPE =
29
+ "/** The GObject name of a property, what a write may do to it, and the value it resets to. */\n" +
30
+ "export type PropertyEntry = [name: string, flags: number, defaultValue?: unknown];";
36
31
 
37
32
  const INTEGER_PATTERN = /^-?\d+$/;
38
33
  const FLOAT_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
@@ -44,23 +39,15 @@ const generateMetadata = (library: Library): string => {
44
39
  ({ glibName, signals }) => ` "${glibName}": ${renderSignalsObject(signals)},`,
45
40
  );
46
41
 
47
- const constructOnlyEntries = intrinsicElements
48
- .filter(({ constructOnly }) => constructOnly.length > 0)
49
- .map(({ glibName, constructOnly }) => ` "${glibName}": ${renderStringSet(constructOnly)},`);
50
-
51
- const constructableEntries = intrinsicElements
52
- .filter(({ constructable }) => constructable.length > 0)
53
- .map(({ glibName, constructable }) => ` "${glibName}": ${renderStringSet(constructable)},`);
54
-
55
- const defaultsEntries = intrinsicElements
56
- .filter(({ defaults }) => defaults.length > 0)
57
- .map(({ glibName, defaults }) => ` "${glibName}": ${renderDefaultsObject(defaults)},`);
42
+ const propertyEntries = intrinsicElements
43
+ .filter(({ properties }) => properties.length > 0)
44
+ .map(({ glibName, properties }) => ` "${glibName}": ${renderPropertiesObject(properties)},`);
58
45
 
59
46
  return `${[
47
+ PROPERTY_ENTRY_TYPE,
60
48
  `export const signals: Record<string, Record<string, string>> = {\n${signalsEntries.join("\n")}\n};`,
61
- `export const constructOnlyProps: Record<string, Set<string>> = {\n${constructOnlyEntries.join("\n")}\n};`,
62
- `export const constructProps: Record<string, Set<string>> = {\n${constructableEntries.join("\n")}\n};`,
63
- `export const defaultProps: Record<string, Record<string, unknown>> = {\n${defaultsEntries.join("\n")}\n};`,
49
+ "export const properties: Record<string, Record<string, PropertyEntry>> = " +
50
+ `{\n${propertyEntries.join("\n")}\n};`,
64
51
  ].join("\n\n")}\n`;
65
52
  };
66
53
 
@@ -83,9 +70,7 @@ const collectIntrinsicElements = (library: Library): IntrinsicElementEntry[] =>
83
70
  entries.push({
84
71
  glibName,
85
72
  signals: collectSignals(sources),
86
- constructOnly: collectConstructOnly(sources),
87
- constructable: collectConstructable(sources),
88
- defaults: collectDefaultProps(library, sources),
73
+ properties: collectProperties(library, sources),
89
74
  });
90
75
  }
91
76
 
@@ -116,41 +101,52 @@ const collectSignals = (sources: GirClass[]): [string, string][] => {
116
101
  return signals;
117
102
  };
118
103
 
119
- const collectPropNamesFromSource = (source: GirClass, collector: PropNameCollector): void => {
120
- const { shouldKeep, seen, names } = collector;
104
+ const propertyFlags = (property: GirProperty): number => {
105
+ const construct = isConstructableProperty(property) && !property.constructOnly ? CONSTRUCT : 0;
106
+ const constructOnly = property.constructOnly ? CONSTRUCT_ONLY : 0;
121
107
 
122
- for (const property of source.properties) {
123
- if (!shouldKeep(property)) {
124
- continue;
125
- }
108
+ return (property.readable ? READABLE : 0) |
109
+ (property.writable ? WRITABLE : 0) |
110
+ construct |
111
+ constructOnly;
112
+ };
113
+
114
+ const propertyEntryLiteral = (library: Library, klass: GirClass, property: GirProperty): string => {
115
+ const head = `${sourceStringLiteral(property.name)},${String(propertyFlags(property))}`;
116
+ const literal = isDefaultCandidate(property) ? renderDefaultLiteral(library, klass, property) : undefined;
117
+
118
+ return literal === undefined ? `[${head}]` : `[${head},${literal}]`;
119
+ };
126
120
 
121
+ const collectPropertiesFromSource = (
122
+ library: Library,
123
+ source: GirClass,
124
+ seen: Set<string>,
125
+ entries: [string, string][],
126
+ ): void => {
127
+ for (const property of source.properties) {
127
128
  const jsName = toCamelIdentifier(property.name);
128
129
 
129
- if (seen.has(jsName)) {
130
+ if (!property.introspectable || seen.has(jsName)) {
130
131
  continue;
131
132
  }
132
133
 
133
134
  seen.add(jsName);
134
- names.push(jsName);
135
+ entries.push([jsName, propertyEntryLiteral(library, source, property)]);
135
136
  }
136
137
  };
137
138
 
138
- const collectPropNames = (sources: GirClass[], shouldKeep: (property: GirProperty) => boolean): string[] => {
139
- const collector: PropNameCollector = { shouldKeep, seen: new Set<string>(), names: [] };
139
+ const collectProperties = (library: Library, sources: GirClass[]): [string, string][] => {
140
+ const seen: Set<string> = new Set();
141
+ const entries: [string, string][] = [];
140
142
 
141
143
  for (const source of sources) {
142
- collectPropNamesFromSource(source, collector);
144
+ collectPropertiesFromSource(library, source, seen, entries);
143
145
  }
144
146
 
145
- return collector.names;
147
+ return entries;
146
148
  };
147
149
 
148
- const collectConstructOnly = (sources: GirClass[]): string[] =>
149
- collectPropNames(sources, (property) => property.constructOnly && property.introspectable);
150
-
151
- const collectConstructable = (sources: GirClass[]): string[] =>
152
- collectPropNames(sources, (property) => isConstructableProperty(property) && property.introspectable);
153
-
154
150
  const renderObjectLiteral = (entries: [string, string][], renderValue: (value: string) => string): string => {
155
151
  if (entries.length === 0) {
156
152
  return "{}";
@@ -161,60 +157,14 @@ const renderObjectLiteral = (entries: [string, string][], renderValue: (value: s
161
157
  return `{\n${lines.join(",\n")}\n }`;
162
158
  };
163
159
 
164
- const renderStringSet = (names: string[]): string =>
165
- `new Set([${names.map((name) => sourceStringLiteral(name)).join(",")}])`;
160
+ const renderPropertiesObject = (entries: [string, string][]): string =>
161
+ renderObjectLiteral(entries, (value) => value);
166
162
 
167
163
  const renderSignalsObject = (entries: [string, string][]): string => renderObjectLiteral(entries, sourceStringLiteral);
168
164
 
169
- const collectDefaultsFromClass = (klass: GirClass, collector: DefaultPropsCollector): void => {
170
- for (const property of klass.properties) {
171
- const entry = defaultPropEntry(collector.library, klass, property, collector.seen);
172
-
173
- if (entry !== undefined) {
174
- collector.defaults.push(entry);
175
- }
176
- }
177
- };
178
-
179
- const collectDefaultProps = (library: Library, sources: GirClass[]): [string, string][] => {
180
- const collector: DefaultPropsCollector = { library, seen: new Set<string>(), defaults: [] };
181
-
182
- for (const klass of sources) {
183
- collectDefaultsFromClass(klass, collector);
184
- }
185
-
186
- return collector.defaults;
187
- };
188
-
189
165
  const isDefaultCandidate = (property: GirProperty): boolean =>
190
166
  (property.writable || property.construct) && !property.constructOnly && property.introspectable;
191
167
 
192
- const defaultPropEntry = (
193
- library: Library,
194
- klass: GirClass,
195
- property: GirProperty,
196
- seen: Set<string>,
197
- ): [string, string] | undefined => {
198
- if (!isDefaultCandidate(property)) {
199
- return undefined;
200
- }
201
-
202
- const jsName = toCamelIdentifier(property.name);
203
-
204
- if (seen.has(jsName)) {
205
- return undefined;
206
- }
207
-
208
- seen.add(jsName);
209
- const literal = renderDefaultLiteral(library, klass, property);
210
-
211
- if (literal === undefined) {
212
- return undefined;
213
- }
214
-
215
- return [jsName, literal];
216
- };
217
-
218
168
  const willSetterRejectNull = (library: Library, klass: GirClass, property: GirProperty): boolean => {
219
169
  if (property.setter === undefined) {
220
170
  return false;
@@ -382,7 +332,4 @@ const enumDefaultLiteral = (enumType: GirEnum, raw: string): string | undefined
382
332
  return member?.value;
383
333
  };
384
334
 
385
- const renderDefaultsObject = (entries: [string, string][]): string =>
386
- renderObjectLiteral(entries, (literal) => literal);
387
-
388
335
  export { generateMetadata };
@@ -28,6 +28,7 @@ const STORE_CONSUMERS: string[] = [
28
28
  "@gtkx/cli",
29
29
  "@gtkx/components",
30
30
  "@gtkx/css",
31
+ "@gtkx/navigation",
31
32
  "@gtkx/react",
32
33
  "@gtkx/runtime",
33
34
  "@gtkx/testing",