@gtkx/cli 2.0.0-beta.6 → 2.0.0-beta.7

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 (114) hide show
  1. package/README.md +43 -40
  2. package/dist/cli.js +1 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/codegen/freshness.d.ts.map +1 -1
  5. package/dist/codegen/freshness.js +5 -1
  6. package/dist/codegen/freshness.js.map +1 -1
  7. package/dist/deploy/settings/libraries.d.ts.map +1 -1
  8. package/dist/deploy/settings/libraries.js +2 -1
  9. package/dist/deploy/settings/libraries.js.map +1 -1
  10. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  11. package/dist/deploy/targets/flatpak-source.js +2 -0
  12. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  13. package/dist/dev/font-dir.d.ts +3 -0
  14. package/dist/dev/font-dir.d.ts.map +1 -0
  15. package/dist/dev/font-dir.js +12 -0
  16. package/dist/dev/font-dir.js.map +1 -0
  17. package/dist/dev/runner-main.d.ts.map +1 -1
  18. package/dist/dev/runner-main.js +2 -0
  19. package/dist/dev/runner-main.js.map +1 -1
  20. package/dist/internal/agent-rules.d.ts.map +1 -1
  21. package/dist/internal/agent-rules.js +6 -2
  22. package/dist/internal/agent-rules.js.map +1 -1
  23. package/dist/internal/banner.d.ts.map +1 -1
  24. package/dist/internal/banner.js +3 -0
  25. package/dist/internal/banner.js.map +1 -1
  26. package/dist/internal/font-path.d.ts +4 -0
  27. package/dist/internal/font-path.d.ts.map +1 -0
  28. package/dist/internal/font-path.js +12 -0
  29. package/dist/internal/font-path.js.map +1 -0
  30. package/dist/internal/font-staging.d.ts +3 -0
  31. package/dist/internal/font-staging.d.ts.map +1 -0
  32. package/dist/internal/font-staging.js +43 -0
  33. package/dist/internal/font-staging.js.map +1 -0
  34. package/dist/internal/source-imports.d.ts +2 -1
  35. package/dist/internal/source-imports.d.ts.map +1 -1
  36. package/dist/internal/source-imports.js +7 -2
  37. package/dist/internal/source-imports.js.map +1 -1
  38. package/dist/internal/xdg-banner.d.ts +4 -0
  39. package/dist/internal/xdg-banner.d.ts.map +1 -0
  40. package/dist/internal/xdg-banner.js +11 -0
  41. package/dist/internal/xdg-banner.js.map +1 -0
  42. package/dist/internal/xdg-data-dirs.d.ts +2 -1
  43. package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
  44. package/dist/internal/xdg-data-dirs.js +1 -1
  45. package/dist/internal/xdg-data-dirs.js.map +1 -1
  46. package/dist/settings/schema.d.ts.map +1 -1
  47. package/dist/settings/schema.js +1 -6
  48. package/dist/settings/schema.js.map +1 -1
  49. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  50. package/dist/vite-plugins/asset-extensions.js +2 -0
  51. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  52. package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
  53. package/dist/vite-plugins/asset-imports.js +7 -2
  54. package/dist/vite-plugins/asset-imports.js.map +1 -1
  55. package/dist/vite-plugins/asset-specifier.d.ts +5 -1
  56. package/dist/vite-plugins/asset-specifier.d.ts.map +1 -1
  57. package/dist/vite-plugins/asset-specifier.js +10 -1
  58. package/dist/vite-plugins/asset-specifier.js.map +1 -1
  59. package/dist/vite-plugins/css.d.ts.map +1 -1
  60. package/dist/vite-plugins/css.js +3 -2
  61. package/dist/vite-plugins/css.js.map +1 -1
  62. package/dist/vite-plugins/font-name.d.ts +3 -0
  63. package/dist/vite-plugins/font-name.d.ts.map +1 -0
  64. package/dist/vite-plugins/font-name.js +406 -0
  65. package/dist/vite-plugins/font-name.js.map +1 -0
  66. package/dist/vite-plugins/font-worker-env.d.ts +4 -0
  67. package/dist/vite-plugins/font-worker-env.d.ts.map +1 -0
  68. package/dist/vite-plugins/font-worker-env.js +22 -0
  69. package/dist/vite-plugins/font-worker-env.js.map +1 -0
  70. package/dist/vite-plugins/font.d.ts +4 -0
  71. package/dist/vite-plugins/font.d.ts.map +1 -0
  72. package/dist/vite-plugins/font.js +87 -0
  73. package/dist/vite-plugins/font.js.map +1 -0
  74. package/dist/vite-plugins/icons.d.ts.map +1 -1
  75. package/dist/vite-plugins/icons.js +3 -8
  76. package/dist/vite-plugins/icons.js.map +1 -1
  77. package/dist/vite-plugins/index.d.ts.map +1 -1
  78. package/dist/vite-plugins/index.js +2 -0
  79. package/dist/vite-plugins/index.js.map +1 -1
  80. package/dist/vite-plugins/resources.js +1 -1
  81. package/dist/vite-plugins/resources.js.map +1 -1
  82. package/dist/vite-plugins/undeclared-library.js +3 -3
  83. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  84. package/dist/vitest-plugin.d.ts.map +1 -1
  85. package/dist/vitest-plugin.js +2 -0
  86. package/dist/vitest-plugin.js.map +1 -1
  87. package/env.d.ts +9 -0
  88. package/package.json +14 -12
  89. package/src/cli.ts +1 -1
  90. package/src/codegen/freshness.ts +5 -1
  91. package/src/deploy/settings/libraries.ts +2 -1
  92. package/src/deploy/targets/flatpak-source.ts +2 -0
  93. package/src/dev/font-dir.ts +16 -0
  94. package/src/dev/runner-main.ts +2 -0
  95. package/src/internal/agent-rules.ts +6 -2
  96. package/src/internal/banner.ts +4 -0
  97. package/src/internal/font-path.ts +15 -0
  98. package/src/internal/font-staging.ts +57 -0
  99. package/src/internal/source-imports.ts +17 -2
  100. package/src/internal/xdg-banner.ts +13 -0
  101. package/src/internal/xdg-data-dirs.ts +1 -1
  102. package/src/settings/schema.ts +1 -8
  103. package/src/vite-plugins/asset-extensions.ts +2 -0
  104. package/src/vite-plugins/asset-imports.ts +9 -1
  105. package/src/vite-plugins/asset-specifier.ts +17 -0
  106. package/src/vite-plugins/css.ts +4 -2
  107. package/src/vite-plugins/font-name.ts +582 -0
  108. package/src/vite-plugins/font-worker-env.ts +29 -0
  109. package/src/vite-plugins/font.ts +147 -0
  110. package/src/vite-plugins/icons.ts +4 -10
  111. package/src/vite-plugins/index.ts +2 -0
  112. package/src/vite-plugins/resources.ts +1 -1
  113. package/src/vite-plugins/undeclared-library.ts +3 -3
  114. package/src/vitest-plugin.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"undeclared-library.js","sourceRoot":"","sources":["../../src/vite-plugins/undeclared-library.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StoreOptions } from \"@gtkx/codegen\";\nimport type { ConfigLoader } from \"@gtkx/config\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { discoverGirNamespaces, resolveGirPath, resolveStore } from \"@gtkx/codegen\";\nimport { createConfigLoader } from \"@gtkx/config/internal\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\ntype PluginState = {\n root: string;\n loadConfig: ConfigLoader;\n girPath: string[] | null;\n};\n\ntype GeneratedModule = {\n source: string;\n kind: string;\n namespace: string;\n importer: string;\n};\n\nconst GENERATED_MODULE_PATTERN = /^@gtkx\\/(gi|jsx)\\/([a-z0-9]+)$/;\nconst DEFAULT_LIBRARY_NAMESPACES: Set<string> = new Set([\"gtk\", \"adw\"]);\n\nconst girSearchPaths = async (state: PluginState): Promise<string[]> => {\n if (state.girPath === null) {\n const { config } = await state.loadConfig.load(state.root);\n state.girPath = resolveGirPath(config.girPath);\n }\n\n return state.girPath;\n};\n\nconst getNamespace = (identifier: string): string => identifier.slice(0, identifier.indexOf(\"-\")).toLowerCase();\n\nconst findGirIdentifier = (girPath: string[], namespace: string): string | undefined =>\n discoverGirNamespaces(girPath).find((identifier) => getNamespace(identifier) === namespace);\n\nconst parseGeneratedModule = (source: string, importer: string | undefined): GeneratedModule | null => {\n const matched = GENERATED_MODULE_PATTERN.exec(source);\n const kind = matched?.[1];\n const namespace = matched?.[2];\n\n if (kind === undefined || namespace === undefined) {\n return null;\n }\n\n return { source, kind, namespace, importer: importer ?? \"the project's sources\" };\n};\n\nconst getStoreOptions = (root: string, kind: string): StoreOptions | null => {\n try {\n const store = resolveStore(root);\n\n return kind === \"gi\" ? store.gi : store.jsx;\n } catch {\n return null;\n }\n};\n\nconst hasStoreModule = (storeDir: string, exportKey: string): boolean => {\n const manifest = join(storeDir, \"package.json\");\n\n if (!existsSync(manifest)) {\n return false;\n }\n\n const { exports } = JSON.parse(readFileSync(manifest, \"utf8\")) as { exports?: Record<string, unknown> };\n\n return exports?.[exportKey] !== undefined;\n};\n\nconst unreachableStoreError = (generated: GeneratedModule, options: StoreOptions): Error => {\n const provided = `\"${generated.namespace}\"`;\n\n return new Error(\n `Cannot resolve \"${generated.source}\": the generated store in ${options.storeDir} does provide ` +\n `${provided}, but its link at ${options.linkDir} is not on the module resolution path of ` +\n `${generated.importer}. Codegen writes the store into the node_modules the installed @gtkx packages ` +\n \"resolve from; install them where the importing file reaches them, then run gtkx codegen again.\",\n );\n};\n\nconst undeclaredLibraryError = (source: string, namespace: string, girPath: string[]): Error => {\n const identifier = findGirIdentifier(girPath, namespace);\n\n if (identifier === undefined) {\n return new Error(\n `Cannot resolve \"${source}\": the binding store has no \"${namespace}\" module, and no ` +\n `GIR data for it was found in [${girPath.join(\", \")}]. If \"${namespace}\" is a library, install ` +\n \"its gobject-introspection data package. Gtk-4.0 and Adw-1 are bound by default; add only other \" +\n \"GIR identifiers to `libraries` in gtkx.config.ts. Otherwise run gtkx codegen to regenerate the store.\",\n );\n }\n\n if (DEFAULT_LIBRARY_NAMESPACES.has(namespace)) {\n return new Error(\n `Cannot resolve \"${source}\": the \"${identifier}\" bindings have not been generated. ` +\n \"The Gtk and Adwaita namespaces are bound by default; run gtkx dev or gtkx build again.\",\n );\n }\n\n return new Error(\n `Cannot resolve \"${source}\": the \"${identifier}\" bindings have not been generated. ` +\n `Add \"${identifier}\" to \\`libraries\\` in gtkx.config.ts, then run gtkx dev or gtkx build again.`,\n );\n};\n\nconst unresolvedModuleError = async (state: PluginState, generated: GeneratedModule): Promise<Error> => {\n const options = getStoreOptions(state.root, generated.kind);\n const exportKey = `./${generated.namespace}`;\n\n if (options !== null && hasStoreModule(options.storeDir, exportKey)) {\n return unreachableStoreError(generated, options);\n }\n\n return undeclaredLibraryError(generated.source, generated.namespace, await girSearchPaths(state));\n};\n\nfunction gtkxUndeclaredLibrary(loadConfig: ConfigLoader = createConfigLoader()): Plugin {\n const state: PluginState = {\n root: \"\",\n loadConfig,\n girPath: null,\n };\n\n return {\n name: \"gtkx:undeclared-library\",\n enforce: \"pre\",\n\n config(config: UserConfig) {\n state.root = config.root ?? process.cwd();\n },\n\n async resolveId(source, importer, options) {\n const generated = parseGeneratedModule(source, importer);\n\n if (generated === null) {\n return;\n }\n\n const resolved = await this.resolve(source, importer, { ...options, skipSelf: true });\n\n if (resolved !== null) {\n return;\n }\n\n throw await unresolvedModuleError(state, generated);\n },\n };\n}\n\nexport { gtkxUndeclaredLibrary };\n"]}
1
+ {"version":3,"file":"undeclared-library.js","sourceRoot":"","sources":["../../src/vite-plugins/undeclared-library.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StoreOptions } from \"@gtkx/codegen\";\nimport type { ConfigLoader } from \"@gtkx/config\";\nimport type { Plugin, UserConfig } from \"vite\";\nimport { discoverGirNamespaces, resolveGirPath, resolveStore } from \"@gtkx/codegen\";\nimport { createConfigLoader } from \"@gtkx/config/internal\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\ntype PluginState = {\n root: string;\n loadConfig: ConfigLoader;\n girPath: string[] | null;\n};\n\ntype GeneratedModule = {\n source: string;\n kind: string;\n namespace: string;\n importer: string;\n};\n\nconst GENERATED_MODULE_PATTERN = /^@gtkx\\/(gi|jsx)\\/([a-z0-9]+)$/;\nconst DEFAULT_LIBRARY_NAMESPACES: Set<string> = new Set([\"adw\", \"gtk\"]);\n\nconst girSearchPaths = async (state: PluginState): Promise<string[]> => {\n if (state.girPath === null) {\n const { config } = await state.loadConfig.load(state.root);\n state.girPath = resolveGirPath(config.girPath);\n }\n\n return state.girPath;\n};\n\nconst getNamespace = (identifier: string): string => identifier.slice(0, identifier.indexOf(\"-\")).toLowerCase();\n\nconst findGirIdentifier = (girPath: string[], namespace: string): string | undefined =>\n discoverGirNamespaces(girPath).find((identifier) => getNamespace(identifier) === namespace);\n\nconst parseGeneratedModule = (source: string, importer: string | undefined): GeneratedModule | null => {\n const matched = GENERATED_MODULE_PATTERN.exec(source);\n const kind = matched?.[1];\n const namespace = matched?.[2];\n\n if (kind === undefined || namespace === undefined) {\n return null;\n }\n\n return { source, kind, namespace, importer: importer ?? \"the project's sources\" };\n};\n\nconst getStoreOptions = (root: string, kind: string): StoreOptions | null => {\n try {\n const store = resolveStore(root);\n\n return kind === \"gi\" ? store.gi : store.jsx;\n } catch {\n return null;\n }\n};\n\nconst hasStoreModule = (storeDir: string, exportKey: string): boolean => {\n const manifest = join(storeDir, \"package.json\");\n\n if (!existsSync(manifest)) {\n return false;\n }\n\n const { exports } = JSON.parse(readFileSync(manifest, \"utf8\")) as { exports?: Record<string, unknown> };\n\n return exports?.[exportKey] !== undefined;\n};\n\nconst unreachableStoreError = (generated: GeneratedModule, options: StoreOptions): Error => {\n const provided = `\"${generated.namespace}\"`;\n\n return new Error(\n `Cannot resolve \"${generated.source}\": the generated store in ${options.storeDir} does provide ` +\n `${provided}, but its link at ${options.linkDir} is not on the module resolution path of ` +\n `${generated.importer}. Codegen writes the store into the node_modules the installed @gtkx packages ` +\n \"resolve from; install them where the importing file reaches them, then run gtkx codegen again.\",\n );\n};\n\nconst undeclaredLibraryError = (source: string, namespace: string, girPath: string[]): Error => {\n const identifier = findGirIdentifier(girPath, namespace);\n\n if (identifier === undefined) {\n return new Error(\n `Cannot resolve \"${source}\": the binding store has no \"${namespace}\" module, and no ` +\n `GIR data for it was found in [${girPath.join(\", \")}]. If \"${namespace}\" is a library, install ` +\n \"its gobject-introspection data package. Adw-1 is bound by default and pulls in Gtk-4.0; add only other \" +\n \"GIR identifiers to `libraries` in gtkx.config.ts. Otherwise run gtkx codegen to regenerate the store.\",\n );\n }\n\n if (DEFAULT_LIBRARY_NAMESPACES.has(namespace)) {\n return new Error(\n `Cannot resolve \"${source}\": the \"${identifier}\" bindings have not been generated. ` +\n \"The Adwaita and GTK namespaces are generated implicitly; run gtkx dev or gtkx build again.\",\n );\n }\n\n return new Error(\n `Cannot resolve \"${source}\": the \"${identifier}\" bindings have not been generated. ` +\n `Add \"${identifier}\" to \\`libraries\\` in gtkx.config.ts, then run gtkx dev or gtkx build again.`,\n );\n};\n\nconst unresolvedModuleError = async (state: PluginState, generated: GeneratedModule): Promise<Error> => {\n const options = getStoreOptions(state.root, generated.kind);\n const exportKey = `./${generated.namespace}`;\n\n if (options !== null && hasStoreModule(options.storeDir, exportKey)) {\n return unreachableStoreError(generated, options);\n }\n\n return undeclaredLibraryError(generated.source, generated.namespace, await girSearchPaths(state));\n};\n\nfunction gtkxUndeclaredLibrary(loadConfig: ConfigLoader = createConfigLoader()): Plugin {\n const state: PluginState = {\n root: \"\",\n loadConfig,\n girPath: null,\n };\n\n return {\n name: \"gtkx:undeclared-library\",\n enforce: \"pre\",\n\n config(config: UserConfig) {\n state.root = config.root ?? process.cwd();\n },\n\n async resolveId(source, importer, options) {\n const generated = parseGeneratedModule(source, importer);\n\n if (generated === null) {\n return;\n }\n\n const resolved = await this.resolve(source, importer, { ...options, skipSelf: true });\n\n if (resolved !== null) {\n return;\n }\n\n throw await unresolvedModuleError(state, generated);\n },\n };\n}\n\nexport { gtkxUndeclaredLibrary };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"vitest-plugin.d.ts","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAK9D;;;;;;GAMG;AACH,QAAA,MAAM,IAAI,GAAI,UAAS,aAAkB,KAAG,MAAM,EAKjD,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"vitest-plugin.d.ts","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAM9D;;;;;;GAMG;AACH,QAAA,MAAM,IAAI,GAAI,UAAS,aAAkB,KAAG,MAAM,EAMjD,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import gtkxVitest, {} from "@gtkx/vitest";
2
2
  import { gtkxEnsureStore } from "./vite-plugins/ensure-store.js";
3
+ import { gtkxFontWorkerEnv } from "./vite-plugins/font-worker-env.js";
3
4
  import { gtkxVitePlugins } from "./vite-plugins/index.js";
4
5
  import { gtkxSettingsWorkerEnv } from "./vite-plugins/settings-worker-env.js";
5
6
  /**
@@ -13,6 +14,7 @@ const gtkx = (options = {}) => [
13
14
  gtkxEnsureStore(options.configFile),
14
15
  ...gtkxVitePlugins({ configFile: options.configFile }),
15
16
  gtkxSettingsWorkerEnv(options.configFile),
17
+ gtkxFontWorkerEnv(options.configFile),
16
18
  gtkxVitest(options),
17
19
  ];
18
20
  export default gtkx;
@@ -1 +1 @@
1
- {"version":3,"file":"vitest-plugin.js","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,UAAyB,EAAE,EAAY,EAAE,CAAC;IACpD,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;IACnC,GAAG,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IACtD,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,UAAU,CAAC,OAAO,CAAC;CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type { Plugin } from \"vite\";\nimport gtkxVitest, { type PluginOptions } from \"@gtkx/vitest\";\nimport { gtkxEnsureStore } from \"./vite-plugins/ensure-store.js\";\nimport { gtkxVitePlugins } from \"./vite-plugins/index.js\";\nimport { gtkxSettingsWorkerEnv } from \"./vite-plugins/settings-worker-env.js\";\n\n/**\n * Vite plugin stack for testing a GTKX application: the generated bindings the tests import,\n * the plugins the app is built with, the project's compiled GSettings schemas exposed to every\n * test worker, and a headless display per worker.\n *\n * @returns The plugins to spread into a Vitest config.\n */\nconst gtkx = (options: PluginOptions = {}): Plugin[] => [\n gtkxEnsureStore(options.configFile),\n ...gtkxVitePlugins({ configFile: options.configFile }),\n gtkxSettingsWorkerEnv(options.configFile),\n gtkxVitest(options),\n];\n\nexport default gtkx;\n"]}
1
+ {"version":3,"file":"vitest-plugin.js","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,UAAyB,EAAE,EAAY,EAAE,CAAC;IACpD,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;IACnC,GAAG,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IACtD,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC;IACrC,UAAU,CAAC,OAAO,CAAC;CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type { Plugin } from \"vite\";\nimport gtkxVitest, { type PluginOptions } from \"@gtkx/vitest\";\nimport { gtkxEnsureStore } from \"./vite-plugins/ensure-store.js\";\nimport { gtkxFontWorkerEnv } from \"./vite-plugins/font-worker-env.js\";\nimport { gtkxVitePlugins } from \"./vite-plugins/index.js\";\nimport { gtkxSettingsWorkerEnv } from \"./vite-plugins/settings-worker-env.js\";\n\n/**\n * Vite plugin stack for testing a GTKX application: the generated bindings the tests import,\n * the plugins the app is built with, the project's compiled GSettings schemas exposed to every\n * test worker, and a headless display per worker.\n *\n * @returns The plugins to spread into a Vitest config.\n */\nconst gtkx = (options: PluginOptions = {}): Plugin[] => [\n gtkxEnsureStore(options.configFile),\n ...gtkxVitePlugins({ configFile: options.configFile }),\n gtkxSettingsWorkerEnv(options.configFile),\n gtkxFontWorkerEnv(options.configFile),\n gtkxVitest(options),\n];\n\nexport default gtkx;\n"]}
package/env.d.ts CHANGED
@@ -14,6 +14,15 @@ declare module "*?icon" {
14
14
  export default iconName;
15
15
  }
16
16
 
17
+ declare module "*?font" {
18
+ /**
19
+ * Font family name of the bundled font. Importing the module bundles the font file with the application and
20
+ * puts it where fontconfig finds it, so the family is available to Pango without any runtime registration.
21
+ */
22
+ const fontFamily: string;
23
+ export default fontFamily;
24
+ }
25
+
17
26
  declare module "*.css?url" {
18
27
  /**
19
28
  * Absolute filesystem path of the source stylesheet under `gtkx dev` or the emitted stylesheet once built,
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@gtkx/cli",
3
- "version": "2.0.0-beta.6",
4
- "description": "GTK app toolchain for React: scaffold, dev server, build, docs.",
3
+ "version": "2.0.0-beta.7",
4
+ "description": "Adwaita-first GNOME app toolchain for React and TypeScript.",
5
5
  "keywords": [
6
6
  "gtkx",
7
7
  "gtk",
8
8
  "gtk4",
9
+ "adwaita",
10
+ "gnome",
9
11
  "react",
10
12
  "typescript",
11
13
  "native",
@@ -70,18 +72,18 @@
70
72
  "dependencies": {
71
73
  "@babel/core": "^8.0.1",
72
74
  "@babel/preset-typescript": "^8.0.1",
73
- "@gtkx/codegen": "2.0.0-beta.6",
74
- "@gtkx/config": "2.0.0-beta.6",
75
- "@gtkx/mcp": "2.0.0-beta.6",
76
- "@gtkx/native": "2.0.0-beta.6",
77
- "@gtkx/runtime": "2.0.0-beta.6",
78
- "@gtkx/utils": "2.0.0-beta.6",
79
- "@gtkx/vitest": "2.0.0-beta.6",
80
- "@swc/core": "^1.16.1",
75
+ "@gtkx/codegen": "2.0.0-beta.7",
76
+ "@gtkx/config": "2.0.0-beta.7",
77
+ "@gtkx/mcp": "2.0.0-beta.7",
78
+ "@gtkx/native": "2.0.0-beta.7",
79
+ "@gtkx/runtime": "2.0.0-beta.7",
80
+ "@gtkx/utils": "2.0.0-beta.7",
81
+ "@gtkx/vitest": "2.0.0-beta.7",
82
+ "@swc/core": "^1.16.2",
81
83
  "babel-plugin-react-compiler": "^1.0.0",
82
84
  "citty": "^0.2.2",
83
85
  "confbox": "^0.2.4",
84
- "create-gtkx": "2.0.0-beta.6",
86
+ "create-gtkx": "2.0.0-beta.7",
85
87
  "fast-xml-builder": "^1.3.1",
86
88
  "fast-xml-parser": "^5.11.1",
87
89
  "hosted-git-info": "^10.1.1",
@@ -92,7 +94,7 @@
92
94
  "yaml": "^2.9.0"
93
95
  },
94
96
  "devDependencies": {
95
- "@gtkx/testing": "2.0.0-beta.6",
97
+ "@gtkx/testing": "2.0.0-beta.7",
96
98
  "@types/babel__core": "^7.20.5",
97
99
  "@types/hosted-git-info": "^3.0.5",
98
100
  "@types/react-refresh": "^0.14.7",
package/src/cli.ts CHANGED
@@ -18,7 +18,7 @@ const main = defineCommand({
18
18
  meta: {
19
19
  name: "gtkx",
20
20
  version,
21
- description: "CLI for GTKX: create and develop GTK4 React applications",
21
+ description: "CLI for GTKX: create and develop Adwaita-first GNOME applications",
22
22
  },
23
23
  subCommands: {
24
24
  dev: async () => {
@@ -1,5 +1,6 @@
1
1
  import type { Config } from "@gtkx/config";
2
2
  import { resolveGirPath, resolveLibraries } from "@gtkx/codegen";
3
+ import { resolveBoundLibraries } from "@gtkx/codegen/internal";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
5
  import { basename, dirname, join } from "node:path";
5
6
  import { type CodegenStore, resolveCodegenStore } from "./store-resolver.js";
@@ -86,7 +87,10 @@ const hasStoreExports = (storeDir: string, required: string[], forbidden: string
86
87
  };
87
88
 
88
89
  const isGiStoreStale = (store: CodegenStore, libraries: string[]): boolean => {
89
- const required = [PACKAGE_EXPORT, ...libraries.map((library) => namespaceExport(library))];
90
+ const required = [
91
+ PACKAGE_EXPORT,
92
+ ...resolveBoundLibraries(libraries).map((library) => namespaceExport(library)),
93
+ ];
90
94
 
91
95
  return !hasStoreExports(store.giStoreDir, required);
92
96
  };
@@ -5,6 +5,7 @@ import {
5
5
  readGeneratedLibraries,
6
6
  resolveStore,
7
7
  } from "@gtkx/codegen";
8
+ import { resolveBoundLibraries } from "@gtkx/codegen/internal";
8
9
 
9
10
  type ResolvedLibraries = {
10
11
  libraries: string[];
@@ -21,7 +22,7 @@ const generatedLibraries = (root: string): GeneratedLibraries | null => {
21
22
 
22
23
  const resolveLibraries = (root: string, config: Config): ResolvedLibraries => {
23
24
  const generated = generatedLibraries(root);
24
- const libraries = generated?.libraries ?? expandLibraries(config.libraries);
25
+ const libraries = resolveBoundLibraries(generated?.libraries ?? expandLibraries(config.libraries));
25
26
  const deploy = config.deploy ?? {};
26
27
 
27
28
  return {
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, realpathSync } from "node:fs";
3
3
  import { posix, relative, resolve } from "node:path";
4
4
  import type { DeployPayload, DeploySettings } from "../types.js";
5
5
  import { LOCALE_DIRNAME } from "../../i18n/catalogs.js";
6
+ import { FONTS_DIR } from "../../internal/font-path.js";
6
7
  import { listFilesRecursive } from "../../internal/list-files.js";
7
8
  import { BUNDLE_FILENAME } from "../../vite-plugins/esm-extension.js";
8
9
  import { renderDbusService } from "../freedesktop/dbus-service.js";
@@ -68,6 +69,7 @@ const runtimeInstallCommands = (settings: DeploySettings, nodeExtensionPath: str
68
69
  "test ! -f dist/gschemas.compiled || " +
69
70
  installCommand("dist/gschemas.compiled", `${lib}/gschemas.compiled`, "m644"),
70
71
  `test ! -d dist/assets || cp -a dist/assets ${lib}/assets`,
72
+ `test ! -d dist/${FONTS_DIR} || cp -a dist/${FONTS_DIR} ${lib}/${FONTS_DIR}`,
71
73
  `test ! -d dist/${LOCALE_DIRNAME} || { mkdir -p ${locale} && cp -a dist/${LOCALE_DIRNAME}/. ${locale}/; }`,
72
74
  installCommand(LAUNCHER_FILENAME, `${DESTINATION}/bin/${settings.binaryName}`, "m755"),
73
75
  ];
@@ -0,0 +1,16 @@
1
+ import { stageProjectFonts } from "../internal/font-staging.js";
2
+ import { prependXdgDataDir } from "../internal/xdg-data-dirs.js";
3
+
4
+ const prepareDevFontDir = (root: string): string | null => {
5
+ const shareDir = stageProjectFonts(root);
6
+
7
+ if (shareDir === null) {
8
+ return null;
9
+ }
10
+
11
+ process.env.XDG_DATA_DIRS = prependXdgDataDir(shareDir, process.env.XDG_DATA_DIRS);
12
+
13
+ return shareDir;
14
+ };
15
+
16
+ export { prepareDevFontDir };
@@ -2,6 +2,7 @@ import { loadConfig } from "@gtkx/config";
2
2
  import { error } from "@gtkx/utils";
3
3
  import { resolve } from "node:path";
4
4
  import { DEV_CONFIG_ENV, DEV_ENTRY_ENV } from "./entry-env.js";
5
+ import { prepareDevFontDir } from "./font-dir.js";
5
6
  import { prepareDevIconDir } from "./icon-dir.js";
6
7
  import { prepareDevLocaleDir } from "./locale-dir.js";
7
8
  import { createDevRunner } from "./runner.js";
@@ -33,6 +34,7 @@ const main = async (): Promise<void> => {
33
34
  prepareDevLocaleDir(root, config.applicationId);
34
35
  prepareDevSchemaDir(root);
35
36
  prepareDevIconDir(root, config.applicationId, config.applicationIcon);
37
+ prepareDevFontDir(root);
36
38
  const entryPath = resolve(cwd, entryArg);
37
39
  const { defaultDevRunnerDeps } = await import("./runner-deps.js");
38
40
  const runner = createDevRunner(defaultDevRunnerDeps(configFile));
@@ -18,9 +18,13 @@ const TOP_LEVEL_HEADING = /^# /m;
18
18
  const rulesBody = (heading: string): string[] => [
19
19
  `${heading} GTKX`,
20
20
  "",
21
- "This is not the GTK you have seen before. Most GTK code in your training data is C, PyGObject, Vala or " +
22
- "GJS, and almost none of it is valid here. Check the rules below against what you are about to write.",
21
+ "GTKX applications are Adwaita-first, with GTK4 underneath. Most GNOME UI code in your training data is C, " +
22
+ "PyGObject, Vala or GJS, and almost none of it is valid here. Check the rules below against what you are " +
23
+ "about to write.",
23
24
  "",
25
+ "- Start application shells with `AdwApplication` and `AdwApplicationWindow`. Freeform windows normally use " +
26
+ "`AdwToolbarView` and `AdwHeaderBar`; navigation containers own their Adwaita chrome. Prefer Adwaita for " +
27
+ "structure, navigation, dialogs, rows, and adaptive patterns; use GTK4 for lower-level primitives.",
24
28
  "- Children are JSX, never `.append()`, `pack_start()`, `set_child()` or `add()`.",
25
29
  '- Signals are props: `onClicked`, not `widget.connect("clicked", ...)`.',
26
30
  "- Props are camelCase: `marginTop`, not `margin-top` or `margin_top`.",
@@ -15,6 +15,10 @@ const prependBanner = (options: Rollup.OutputOptions, banner: Banner): Rollup.Ou
15
15
  const prefix = await renderBanner(banner, chunk);
16
16
  const suffix = existing === undefined ? "" : await renderBanner(existing, chunk);
17
17
 
18
+ if (prefix.length === 0) {
19
+ return suffix;
20
+ }
21
+
18
22
  return suffix.length === 0 ? prefix : `${prefix}\n${suffix}`;
19
23
  },
20
24
  };
@@ -0,0 +1,15 @@
1
+ import { createHash } from "node:crypto";
2
+ import { basename, extname } from "node:path";
3
+
4
+ const FONTS_DIR = "fonts";
5
+ const DIGEST_LENGTH = 8;
6
+
7
+ const fontFileName = (sourcePath: string, content: Buffer): string => {
8
+ const extension = extname(sourcePath).toLowerCase();
9
+ const stem = basename(sourcePath, extname(sourcePath));
10
+ const digest = createHash("sha1").update(content).digest("hex").slice(0, DIGEST_LENGTH);
11
+
12
+ return `${stem}-${digest}${extension}`;
13
+ };
14
+
15
+ export { fontFileName, FONTS_DIR };
@@ -0,0 +1,57 @@
1
+ import { sortStrings } from "@gtkx/utils";
2
+ import { mkdirSync, mkdtempSync, readFileSync, symlinkSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { parseFontSpecifier } from "../vite-plugins/asset-specifier.js";
6
+ import { fontFileName, FONTS_DIR } from "./font-path.js";
7
+ import { discoverSourceImports, sourceDirFor, type SourceImport } from "./source-imports.js";
8
+ import { removeTempDir } from "./staging-dir.js";
9
+
10
+ const FONT_STAGING_PREFIX = "gtkx-fonts-";
11
+
12
+ const isRelativeImport = (source: string): boolean => source.startsWith("./") || source.startsWith("../");
13
+
14
+ const fontFileFor = ({ importer, source }: SourceImport): string | null => {
15
+ const specifier = parseFontSpecifier(source);
16
+
17
+ return specifier === null || !isRelativeImport(source)
18
+ ? null
19
+ : resolve(dirname(importer), specifier.assetSource);
20
+ };
21
+
22
+ const findImportedFontFiles = (imports: SourceImport[]): string[] => {
23
+ const files = imports
24
+ .map((entry) => fontFileFor(entry))
25
+ .filter((path): path is string => path !== null);
26
+
27
+ return sortStrings(new Set(files));
28
+ };
29
+
30
+ const stageFont = (fontsDir: string, sourcePath: string): void => {
31
+ const fileName = fontFileName(sourcePath, readFileSync(sourcePath));
32
+
33
+ symlinkSync(sourcePath, join(fontsDir, fileName), "file");
34
+ };
35
+
36
+ const stageProjectFonts = (root: string): string | null => {
37
+ const fontFiles = findImportedFontFiles(discoverSourceImports(sourceDirFor(root)));
38
+
39
+ if (fontFiles.length === 0) {
40
+ return null;
41
+ }
42
+
43
+ const shareDir = mkdtempSync(join(tmpdir(), FONT_STAGING_PREFIX));
44
+ mkdirSync(join(shareDir, FONTS_DIR));
45
+
46
+ for (const sourcePath of fontFiles) {
47
+ stageFont(join(shareDir, FONTS_DIR), sourcePath);
48
+ }
49
+
50
+ process.once("exit", () => {
51
+ removeTempDir(shareDir);
52
+ });
53
+
54
+ return shareDir;
55
+ };
56
+
57
+ export { stageProjectFonts };
@@ -1,6 +1,6 @@
1
1
  import type { ESTree, ParseResult, ParserOptions } from "vite";
2
2
  import { sortStringsBy } from "@gtkx/utils";
3
- import { globSync, readFileSync } from "node:fs";
3
+ import { existsSync, globSync, readFileSync } from "node:fs";
4
4
  import { extname, join } from "node:path";
5
5
  import { parseSync, Visitor } from "vite";
6
6
 
@@ -29,6 +29,14 @@ const sourceLanguage = (path: string): SourceLanguage | undefined => {
29
29
  return SOURCE_LANGUAGES.get(extname(path).toLowerCase());
30
30
  };
31
31
 
32
+ const SOURCE_DIR = "src";
33
+
34
+ const sourceDirFor = (root: string): string => {
35
+ const sourceDir = join(root, SOURCE_DIR);
36
+
37
+ return existsSync(sourceDir) ? sourceDir : root;
38
+ };
39
+
32
40
  const discoverSourceFiles = (dir: string): string[] =>
33
41
  sortStringsBy(
34
42
  globSync("**/{*,.*}", {
@@ -132,4 +140,11 @@ const discoverSourceImports = (dir: string): SourceImport[] => {
132
140
  return sortStringsBy(imports, importKey);
133
141
  };
134
142
 
135
- export { discoverSourceFiles, discoverSourceImports, parseRuntimeImportsIn, type SourceImport, sourceLanguage };
143
+ export {
144
+ discoverSourceFiles,
145
+ discoverSourceImports,
146
+ parseRuntimeImportsIn,
147
+ sourceDirFor,
148
+ sourceLanguage,
149
+ type SourceImport,
150
+ };
@@ -0,0 +1,13 @@
1
+ import type { Rollup } from "vite";
2
+ import { outputRootUrlExpression } from "./banner.js";
3
+ import { XDG_DATA_DIRS_DEFAULT } from "./xdg-data-dirs.js";
4
+
5
+ const xdgDataDirsBanner = (chunk: Rollup.RenderedChunk): string => [
6
+ "globalThis.process.env.XDG_DATA_DIRS = (() => {",
7
+ ` const root = ${outputRootUrlExpression(chunk)};`,
8
+ ` const dirs = globalThis.process.env.XDG_DATA_DIRS || ${JSON.stringify(XDG_DATA_DIRS_DEFAULT)};`,
9
+ " return dirs.split(\":\").includes(root) ? dirs : `${root}:${dirs}`;",
10
+ "})();",
11
+ ].join("\n");
12
+
13
+ export { xdgDataDirsBanner };
@@ -10,4 +10,4 @@ const prependXdgDataDir = (dir: string, existing: string | undefined): string =>
10
10
  return `${dir}:${base}`;
11
11
  };
12
12
 
13
- export { prependXdgDataDir };
13
+ export { prependXdgDataDir, XDG_DATA_DIRS_DEFAULT };
@@ -12,7 +12,7 @@ import {
12
12
  import { tmpdir } from "node:os";
13
13
  import { basename, dirname, join, relative, resolve } from "node:path";
14
14
  import { I18N_TYPES_FILENAME, i18nTypesPath } from "../i18n/types.js";
15
- import { discoverSourceImports, type SourceImport } from "../internal/source-imports.js";
15
+ import { discoverSourceImports, sourceDirFor, type SourceImport } from "../internal/source-imports.js";
16
16
  import { removeTempDir } from "../internal/staging-dir.js";
17
17
  import {
18
18
  isBareRelativeAsset,
@@ -30,7 +30,6 @@ type SchemaEnvResult = {
30
30
 
31
31
  const SCHEMA_SUFFIX = ".gschema.xml";
32
32
  const STAGED_NAME_LENGTH = 16;
33
- const SOURCE_DIR = "src";
34
33
  const RESOURCE_QUERY = "?resource=";
35
34
  const ICON_QUERY = "?icon=";
36
35
 
@@ -69,12 +68,6 @@ const projectRelativeSchemaPath = (root: string, filePath: string): string | nul
69
68
 
70
69
  const getRelativeModuleSpecifier = (filePath: string): string => `*/${basename(filePath)}`;
71
70
 
72
- const sourceDirFor = (root: string): string => {
73
- const sourceDir = join(root, SOURCE_DIR);
74
-
75
- return existsSync(sourceDir) ? sourceDir : root;
76
- };
77
-
78
71
  const isRelativeImport = (source: string): boolean => source.startsWith("./") || source.startsWith("../");
79
72
 
80
73
  const schemaFileFor = ({ importer, source }: SourceImport): string | null =>
@@ -18,6 +18,8 @@ const ASSET_EXTENSIONS = [
18
18
  "eot",
19
19
  "ttf",
20
20
  "otf",
21
+ "ttc",
22
+ "otc",
21
23
  "ico",
22
24
  "avif",
23
25
  "data",
@@ -5,6 +5,7 @@ import { ASSET_MENTION_RE } from "./asset-extensions.js";
5
5
  import {
6
6
  isAssetSpecifier,
7
7
  isUrlSpecifier,
8
+ parseFontSpecifier,
8
9
  parseIconSpecifier,
9
10
  parseResourceSpecifier,
10
11
  } from "./asset-specifier.js";
@@ -31,8 +32,11 @@ const RESOURCE_ADVICE =
31
32
 
32
33
  const ICON_ADVICE = "An icon asset exports only its default icon-theme name, and nothing else.";
33
34
 
35
+ const FONT_ADVICE = "A font asset exports only its default font family name, and nothing else.";
36
+
34
37
  const RELATIVE_ADVICE =
35
- "Add ?resource for a GResource, ?icon for a themed icon, or ?url for an emitted file.";
38
+ "Add ?resource for a GResource, ?icon for a themed icon, ?font for a bundled font, or ?url for an " +
39
+ "emitted file.";
36
40
 
37
41
  const boundName = (entry: BindingEntry): string | null => {
38
42
  const { name } = entry.importName;
@@ -87,6 +91,10 @@ const adviceFor = (source: string): string => {
87
91
  return ICON_ADVICE;
88
92
  }
89
93
 
94
+ if (parseFontSpecifier(source) !== null) {
95
+ return FONT_ADVICE;
96
+ }
97
+
90
98
  if (isUrlSpecifier(source)) {
91
99
  return QUERY_ADVICE;
92
100
  }
@@ -11,9 +11,14 @@ type IconSpecifier = {
11
11
  iconName: string | null;
12
12
  };
13
13
 
14
+ type FontSpecifier = {
15
+ assetSource: string;
16
+ };
17
+
14
18
  const RELATIVE_PREFIX_RE = /^\.\.?(?:\/|$)/;
15
19
  const RESOURCE_QUERY_RE = /^resource(?:=([^&#]+))?$/;
16
20
  const ICON_QUERY_RE = /^icon(?:=([^&#]+))?$/;
21
+ const FONT_QUERY_RE = /^font$/;
17
22
  const URL_QUERY_RE = /(?:^|&)url(?:&|$)/;
18
23
 
19
24
  const getQuery = (source: string): string | null => {
@@ -61,6 +66,17 @@ const parseIconSpecifier = (source: string): IconSpecifier | null => {
61
66
  return { assetSource, iconName: match[1] ?? null };
62
67
  };
63
68
 
69
+ const parseFontSpecifier = (source: string): FontSpecifier | null => {
70
+ const query = getQuery(source);
71
+ const assetSource = stripQuery(source);
72
+
73
+ if (query === null || !ASSET_RE.test(assetSource) || !FONT_QUERY_RE.test(query)) {
74
+ return null;
75
+ }
76
+
77
+ return { assetSource };
78
+ };
79
+
64
80
  const isUrlSpecifier = (source: string): boolean => {
65
81
  const query = getQuery(source);
66
82
 
@@ -71,6 +87,7 @@ export {
71
87
  isAssetSpecifier,
72
88
  isBareRelativeAsset,
73
89
  isUrlSpecifier,
90
+ parseFontSpecifier,
74
91
  parseIconSpecifier,
75
92
  parseResourceSpecifier,
76
93
  };
@@ -4,6 +4,7 @@ import { createVirtualNamespace } from "./virtual-module.js";
4
4
 
5
5
  type CssResolveContext = Parameters<typeof resolveToVirtual>[0];
6
6
  type CssResolveRequest = Parameters<typeof resolveToVirtual>[1];
7
+ type CssLoadContext = { addWatchFile: (file: string) => void };
7
8
 
8
9
  const CSS_RE = /\.css$/i;
9
10
  const INJECT_SUFFIX = "?inject";
@@ -24,13 +25,14 @@ const resolveCssId = async (ctx: CssResolveContext, request: CssResolveRequest):
24
25
  return virtualId + INJECT_SUFFIX;
25
26
  };
26
27
 
27
- const loadInjectedCss = (id: string): string | undefined => {
28
+ const loadInjectedCss = (ctx: CssLoadContext, id: string): string | undefined => {
28
29
  if (!isVirtual(id) || !id.endsWith(INJECT_SUFFIX)) {
29
30
  return;
30
31
  }
31
32
 
32
33
  const filePath = fromVirtualId(id.slice(0, -INJECT_SUFFIX.length));
33
34
  const content = readFileSync(filePath, "utf8");
35
+ ctx.addWatchFile(filePath);
34
36
 
35
37
  return ["import { injectGlobal } from \"@gtkx/css\";", `injectGlobal(${JSON.stringify(content)});`].join("\n");
36
38
  };
@@ -45,7 +47,7 @@ function gtkxCss(): Plugin {
45
47
  },
46
48
 
47
49
  load(id) {
48
- return loadInjectedCss(id);
50
+ return loadInjectedCss(this, id);
49
51
  },
50
52
  };
51
53
  }