@gtkx/config 1.0.0-rc.2 → 1.0.0-rc.4

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/README.md CHANGED
@@ -6,7 +6,8 @@
6
6
 
7
7
  <p align="center">
8
8
  The React framework for Linux.<br />
9
- Build GTK4 and Adwaita apps in TypeScript, with React components and hooks driving GNOME's own widgets. What you ship is a GNOME app.
9
+ Write native Linux applications with React and TypeScript.
10
+ Built on the GNOME stack and standard web tooling.
10
11
  </p>
11
12
 
12
13
  <p align="center">
@@ -20,15 +21,13 @@
20
21
 
21
22
  <p align="center">
22
23
  <a href="https://gtkx.dev">Homepage</a> &middot;
23
- <a href="https://gtkx.dev/guide/why-gtkx">Documentation</a> &middot;
24
+ <a href="https://gtkx.dev/guide/getting-started">Documentation</a> &middot;
24
25
  <a href="https://github.com/gtkx-org/gtkx/tree/main/examples">Examples</a> &middot;
25
26
  <a href="https://github.com/gtkx-org/gtkx/blob/main/CONTRIBUTING.md">Contributing</a>
26
27
  </p>
27
28
 
28
29
  ---
29
30
 
30
- GTKX generates fully typed bindings for the entire GTK4 and Adwaita surface directly from GObject-Introspection. On top of those bindings you get the React programming model: components and hooks driving GObject instances, with Fast Refresh while you develop.
31
-
32
31
  <p align="center">
33
32
  <img src="https://raw.githubusercontent.com/gtkx-org/gtkx/main/examples/tutorial/assets/screenshot.png" alt="The Tasks app: an Adwaita window with a sidebar of smart views and colored lists on the left, and a boxed task list on the right." />
34
33
  </p>
@@ -109,7 +108,7 @@ React Native and similar frameworks hide the native toolkit so one API can run e
109
108
 
110
109
  ### Why Node.js, and why generated bindings
111
110
 
112
- GTKX runs on Node.js, which puts native modules, the npm ecosystem, and the tooling built for Node.js APIs within reach. GJS is GNOME's own runtime, built on SpiderMonkey rather than V8; node-gtk runs on Node.js but is lightly maintained, on the older nan/V8 ABI rather than N-API, and still centered on GTK3. The [why-gtkx guide](https://gtkx.dev/guide/why-gtkx) covers the comparison in full.
111
+ GTKX runs on Node.js, which puts native modules, the npm ecosystem, and the tooling built for Node.js APIs within reach. GJS is GNOME's own runtime, built on SpiderMonkey rather than V8; node-gtk runs on Node.js but is lightly maintained, on the older nan/V8 ABI rather than N-API, and still centered on GTK3. The [Why GTKX guide](https://gtkx.dev/guide/why-gtkx) covers the comparison in full.
113
112
 
114
113
  GTKX generates the TypeScript types and the native FFI calls from the same GObject-Introspection data, so the types cannot drift from the calls they back. Codegen covers the whole GTK4 and Adwaita surface.
115
114
 
package/dist/config.d.ts CHANGED
@@ -1,44 +1,53 @@
1
1
  import { type DefineConfig } from "c12";
2
2
  import { z } from "zod";
3
+ /** Accepted `reactCompiler.compilationMode` values, choosing which functions the compiler processes. */
3
4
  type ReactCompilerCompilationMode = (typeof COMPILATION_MODES)[number];
5
+ /** Accepted `reactCompiler.panicThreshold` values, choosing which compiler diagnostics fail the build. */
4
6
  type ReactCompilerPanicThreshold = (typeof PANIC_THRESHOLDS)[number];
7
+ /** Object form of the `reactCompiler` config key, forwarded as-is to `babel-plugin-react-compiler`. */
5
8
  type ReactCompilerOptions = {
9
+ /** Which functions the compiler processes; left to the compiler's own default when omitted. */
6
10
  compilationMode?: ReactCompilerCompilationMode;
11
+ /** Which compiler diagnostics fail the build; left to the compiler's own default when omitted. */
7
12
  panicThreshold?: ReactCompilerPanicThreshold;
8
13
  };
9
14
  /**
10
- * React Compiler options resolved for the build, with the compilation target
11
- * fixed to React 19.
15
+ * The React Compiler options the build hands to `babel-plugin-react-compiler`, with the React version
16
+ * GTKX targets filled in.
12
17
  */
13
18
  type ResolvedReactCompilerOptions = ReactCompilerOptions & {
19
+ /** React major version the compiler emits for. */
14
20
  target: "19";
15
21
  };
16
22
  /**
17
- * User-facing configuration for a GTKX project, as authored in `gtkx.config.ts`:
18
- * the GIR libraries to bind, extra `.gir` search paths, the GApplication id,
19
- * a module of per-element configuration (lazy flags and custom behaviors),
20
- * per-element component wrappers keyed by GLib type name, the React Compiler and
21
- * codegen settings, and additional user event signals to suppress during React
22
- * commits.
23
+ * User-facing configuration for a GTKX project, as authored in `gtkx.config.ts`: the GIR libraries
24
+ * to bind and where to find them, the GApplication id, per-element configuration, and the React
25
+ * Compiler, codegen, and user event signal settings.
23
26
  */
24
27
  type Config = z.infer<typeof configSchema>;
25
- /**
26
- * Configuration reduced to the values needed at runtime: the GApplication
27
- * identifier, the resolved React Compiler options (`null` when disabled), the
28
- * user event signals suppressed while a React commit is in progress, and the
29
- * module path holding per-element configuration (`null` when unset).
30
- */
28
+ /** A named export referenced as plain data, so codegen can emit an import for it without loading the module. */
29
+ type ModuleExport = z.infer<typeof moduleExportSchema>;
30
+ /** Configuration reduced to the values the app runtime and the build need, with paths already resolved. */
31
31
  type ResolvedConfig = {
32
+ /** The GApplication identifier the app registers under. */
32
33
  applicationId: string;
34
+ /** React Compiler options for the build, or `null` when the compiler is disabled. */
33
35
  reactCompiler: ResolvedReactCompilerOptions | null;
36
+ /** Signal names, keyed by GLib type name, that stay suppressed while a React commit runs. */
34
37
  userEventSignals: Record<string, string[]>;
38
+ /** Path of the module exporting per-element configs, or `null` when none is configured. */
35
39
  elements: string | null;
40
+ /** GLib type names of the elements marked `isLazy`, whose GObject their parent container creates. */
36
41
  lazyElements: string[];
37
42
  };
38
- declare const LIBRARIES_WILDCARD = "*";
39
- declare const GIR_LIBRARY_PATTERN: RegExp;
40
43
  declare const COMPILATION_MODES: readonly ["infer", "syntax", "annotation", "all"];
41
44
  declare const PANIC_THRESHOLDS: readonly ["none", "critical_errors", "all_errors"];
45
+ /** Validates a `{ module, export }` reference to a named export. */
46
+ declare const moduleExportSchema: z.ZodObject<{
47
+ module: z.ZodString;
48
+ export: z.ZodString;
49
+ }, z.core.$strip>;
50
+ /** The shape every `gtkx.config.ts` is validated against, and the source of the {@link Config} type. */
42
51
  declare const configSchema: z.ZodObject<{
43
52
  libraries: z.ZodOptional<z.ZodCustom<string[] | "*", string[] | "*">>;
44
53
  girPath: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -57,25 +66,28 @@ declare const configSchema: z.ZodObject<{
57
66
  module: z.ZodString;
58
67
  export: z.ZodString;
59
68
  }, z.core.$strip>>;
60
- lazy: z.ZodOptional<z.ZodBoolean>;
69
+ isLazy: z.ZodOptional<z.ZodBoolean>;
70
+ omittedProps: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
71
  }, z.core.$strip>>>;
62
72
  }, z.core.$strip>>;
63
73
  }, z.core.$strip>;
64
74
  /**
65
- * Identity helper that returns the given configuration typed as {@link Config},
66
- * enabling editor autocompletion and type checking in `gtkx.config.ts`.
75
+ * Returns the given configuration unchanged, typed as {@link Config}, so `gtkx.config.ts` gets
76
+ * autocompletion and type checking.
67
77
  */
68
78
  declare const defineConfig: DefineConfig<Config>;
69
79
  declare const isValidApplicationId: (applicationId: string) => boolean;
70
80
  declare const resolveReactCompilerOptions: (setting: Config["reactCompiler"]) => ResolvedReactCompilerOptions | null;
71
81
  declare const validateConfig: (config: Config) => void;
72
82
  /**
73
- * Deep-merges two configurations, with `override` taking precedence over `base`.
74
- * @param base The lower-priority configuration.
75
- * @param override The higher-priority configuration whose values win on conflict.
83
+ * Deep-merges two configurations. `override` wins over `base` on conflicting scalar and object keys, while
84
+ * arrays are concatenated with the `override` entries first.
76
85
  */
77
86
  declare const mergeConfig: (base: Config, override: Config) => Config;
78
87
  declare const resolveLazyElements: (elements: Config["elements"]) => string[];
88
+ declare const resolveElementComponents: (elements: Config["elements"]) => Record<string, ModuleExport>;
89
+ declare const resolveElementProps: (elements: Config["elements"]) => Record<string, ModuleExport>;
90
+ declare const resolveOmittedProps: (elements: Config["elements"]) => Record<string, string[]>;
79
91
  declare const resolveConfig: (config: Config, root?: string) => ResolvedConfig;
80
- export { LIBRARIES_WILDCARD, GIR_LIBRARY_PATTERN, defineConfig, isValidApplicationId, resolveReactCompilerOptions, validateConfig, mergeConfig, resolveLazyElements, resolveConfig, type ResolvedReactCompilerOptions, type Config, type ResolvedConfig, };
92
+ export { defineConfig, isValidApplicationId, resolveReactCompilerOptions, validateConfig, mergeConfig, resolveLazyElements, resolveElementComponents, resolveElementProps, resolveOmittedProps, resolveConfig, type ResolvedReactCompilerOptions, type Config, type ModuleExport, type ResolvedConfig, };
81
93
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,KAAK,CAAC;AAG5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,KAAK,4BAA4B,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AACvE,KAAK,2BAA2B,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE,KAAK,oBAAoB,GAAG;IACxB,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C,cAAc,CAAC,EAAE,2BAA2B,CAAC;CAChD,CAAC;AAEF;;;GAGG;AACH,KAAK,4BAA4B,GAAG,oBAAoB,GAAG;IACvD,MAAM,EAAE,IAAI,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3C;;;;;GAKG;AACH,KAAK,cAAc,GAAG;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,4BAA4B,GAAG,IAAI,CAAC;IACnD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,QAAA,MAAM,kBAAkB,MAAM,CAAC;AAC/B,QAAA,MAAM,mBAAmB,QAAyC,CAAC;AAGnE,QAAA,MAAM,iBAAiB,mDAAoD,CAAC;AAC5E,QAAA,MAAM,gBAAgB,oDAAqD,CAAC;AAkH5E,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;iBAQhB,CAAC;AAEH;;;GAGG;AACH,QAAA,MAAM,YAAY,EAAE,YAAY,CAAC,MAAM,CAAgC,CAAC;AAsBxE,QAAA,MAAM,oBAAoB,GAAI,eAAe,MAAM,KAAG,OAMrD,CAAC;AAEF,QAAA,MAAM,2BAA2B,GAAI,SAAS,MAAM,CAAC,eAAe,CAAC,KAAG,4BAA4B,GAAG,IAQtG,CAAC;AAKF,QAAA,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,IAMxC,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,UAAU,MAAM,KAAG,MAA8B,CAAC;AAUrF,QAAA,MAAM,mBAAmB,GAAI,UAAU,MAAM,CAAC,UAAU,CAAC,KAAG,MAAM,EAGpC,CAAC;AAE/B,QAAA,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,cAMrD,CAAC;AAEH,OAAO,EACH,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,2BAA2B,EAC3B,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,KAAK,4BAA4B,EACjC,KAAK,MAAM,EACX,KAAK,cAAc,GACtB,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -13,6 +13,7 @@ const PANIC_THRESHOLDS = ["none", "critical_errors", "all_errors"];
13
13
  const REACT_COMPILER_TARGET = "19";
14
14
  const COMPILATION_MODE_SET = new Set(COMPILATION_MODES);
15
15
  const PANIC_THRESHOLD_SET = new Set(PANIC_THRESHOLDS);
16
+ /** Validates `libraries`: the `"*"` wildcard on its own, or a non-empty array of `Name-Version` identifiers. */
16
17
  const librariesSchema = z.custom().check((ctx) => {
17
18
  const value = ctx.value;
18
19
  if (value === LIBRARIES_WILDCARD) {
@@ -26,6 +27,7 @@ const librariesSchema = z.custom().check((ctx) => {
26
27
  ctx.issues.push(...libraryEntryIssues(value, index, entry));
27
28
  }
28
29
  });
30
+ /** Validates `applicationId` against the reverse-DNS form `g_application_id_is_valid` accepts. */
29
31
  const applicationIdSchema = z.custom().check((ctx) => {
30
32
  const value = ctx.value;
31
33
  if (typeof value !== "string" || !isValidApplicationId(value)) {
@@ -33,6 +35,7 @@ const applicationIdSchema = z.custom().check((ctx) => {
33
35
  '(e.g. "org.example.MyApp")', true));
34
36
  }
35
37
  });
38
+ /** Validates `reactCompiler`: a boolean, or an options object whose mode and threshold are known values. */
36
39
  const reactCompilerSchema = z.custom().check((ctx) => {
37
40
  const value = ctx.value;
38
41
  if (typeof value === "boolean") {
@@ -53,37 +56,71 @@ const reactCompilerSchema = z.custom().check((ctx) => {
53
56
  `must be one of ${PANIC_THRESHOLDS.join(", ")}`, true));
54
57
  }
55
58
  });
59
+ /** Validates `userEventSignals`: GLib type names mapped to arrays of non-empty signal names. */
56
60
  const userEventSignalsSchema = z.record(z.string(), z.array(z.string({ error: "must be a non-empty signal name" }).min(1, { error: "must be a non-empty signal name" }), {
57
61
  error: "must be an array of signal names",
58
62
  }), { error: "must be a record of GLib type names to signal name arrays" });
63
+ /** Validates a `{ module, export }` reference to a named export. */
59
64
  const moduleExportSchema = z.object({
65
+ /** Specifier the export is imported from. */
60
66
  module: z.string({ error: "must be a module specifier" }).min(1, { error: "must be a module specifier" }),
67
+ /** Identifier the module exports it under. */
61
68
  export: z.string({ error: "must be an export name" }).min(1, { error: "must be an export name" }),
62
69
  }, { error: "must be a { module, export } object" });
70
+ /** Validates one entry of `elements.config`. */
63
71
  const elementConfigSchema = z.object({
72
+ /** Component that wraps the generated element. */
64
73
  component: moduleExportSchema.optional(),
74
+ /** Base props interface the generated element props extend. */
65
75
  props: moduleExportSchema.optional(),
66
- lazy: z.boolean({ error: "must be a boolean" }).optional(),
76
+ /** Marks the element as having no GObject of its own, its parent container creating one instead. */
77
+ isLazy: z.boolean({ error: "must be a boolean" }).optional(),
78
+ /** GObject properties to leave out of the generated element props, such as those a behavior fills. */
79
+ omittedProps: z
80
+ .array(z.string({ error: "must be a non-empty property name" }).min(1, {
81
+ error: "must be a non-empty property name",
82
+ }), { error: "must be an array of property names" })
83
+ .optional(),
67
84
  });
85
+ /** Validates `elements`: the behaviors module the app loads at runtime, plus the per-type entries. */
68
86
  const elementsSchema = z.object({
87
+ /**
88
+ * Path, resolved against the project root, of a module default-exporting element configs keyed by
89
+ * GLib type name, as `defineElements` returns.
90
+ */
69
91
  behaviors: z
70
92
  .string({ error: "must be a path to a module exporting element behaviors" })
71
93
  .min(1, { error: "must be a path to a module exporting element behaviors" })
72
94
  .optional(),
95
+ /** Per-element entries, keyed by GLib type name. */
73
96
  config: z.record(z.string(), elementConfigSchema).optional(),
74
97
  });
98
+ /** The shape every `gtkx.config.ts` is validated against, and the source of the {@link Config} type. */
75
99
  const configSchema = z.object({
100
+ /**
101
+ * GIR namespaces to bind as `Name-Version`, or `"*"` for every one installed; omitting it gives
102
+ * `Gtk-4.0`, which is also prepended to any list that names no Gtk version of its own.
103
+ */
76
104
  libraries: librariesSchema.optional(),
105
+ /** Directories searched for `.gir` files ahead of `GTKX_GIR_PATH` and the system locations. */
77
106
  girPath: z.array(z.string(), { error: "must be an array of strings if provided" }).optional(),
107
+ /** Reverse-DNS GApplication identifier, and the default `applicationId` of the application element. */
78
108
  applicationId: applicationIdSchema,
109
+ /** React Compiler settings; the compiler runs unless this is `false`. */
79
110
  reactCompiler: reactCompilerSchema.optional(),
111
+ /** Whether to generate the binding stores; `false` makes the CLI resolve an already-installed one. */
80
112
  codegen: z.boolean({ error: "must be a boolean" }).optional(),
113
+ /**
114
+ * Signal names, keyed by GLib type name, that stay suppressed while a React commit applies props, so
115
+ * their handlers only ever report user interaction. Unioned with the built-in GTK4 and Adwaita table.
116
+ */
81
117
  userEventSignals: userEventSignalsSchema.optional(),
118
+ /** Per-element customization: where the behaviors module lives and how each type's element is shaped. */
82
119
  elements: elementsSchema.optional(),
83
120
  });
84
121
  /**
85
- * Identity helper that returns the given configuration typed as {@link Config},
86
- * enabling editor autocompletion and type checking in `gtkx.config.ts`.
122
+ * Returns the given configuration unchanged, typed as {@link Config}, so `gtkx.config.ts` gets
123
+ * autocompletion and type checking.
87
124
  */
88
125
  const defineConfig = createDefineConfig();
89
126
  const libraryEntryIssues = (value, index, entry) => {
@@ -120,9 +157,8 @@ const validateConfig = (config) => {
120
157
  }
121
158
  };
122
159
  /**
123
- * Deep-merges two configurations, with `override` taking precedence over `base`.
124
- * @param base The lower-priority configuration.
125
- * @param override The higher-priority configuration whose values win on conflict.
160
+ * Deep-merges two configurations. `override` wins over `base` on conflicting scalar and object keys, while
161
+ * arrays are concatenated with the `override` entries first.
126
162
  */
127
163
  const mergeConfig = (base, override) => defu(override, base);
128
164
  const resolveElementsModule = (behaviors, root) => {
@@ -132,8 +168,15 @@ const resolveElementsModule = (behaviors, root) => {
132
168
  return root === undefined ? behaviors : resolve(root, behaviors);
133
169
  };
134
170
  const resolveLazyElements = (elements) => Object.entries(elements?.config ?? {})
135
- .filter(([, entry]) => entry.lazy === true)
171
+ .filter(([, entry]) => entry.isLazy === true)
136
172
  .map(([type]) => type);
173
+ const elementEntryValues = (elements, pick) => Object.fromEntries(Object.entries(elements?.config ?? {}).flatMap(([type, entry]) => {
174
+ const value = pick(entry);
175
+ return value === undefined ? [] : [[type, value]];
176
+ }));
177
+ const resolveElementComponents = (elements) => elementEntryValues(elements, (entry) => entry.component);
178
+ const resolveElementProps = (elements) => elementEntryValues(elements, (entry) => entry.props);
179
+ const resolveOmittedProps = (elements) => elementEntryValues(elements, (entry) => entry.omittedProps);
137
180
  const resolveConfig = (config, root) => ({
138
181
  applicationId: config.applicationId,
139
182
  reactCompiler: resolveReactCompilerOptions(config.reactCompiler),
@@ -141,5 +184,5 @@ const resolveConfig = (config, root) => ({
141
184
  elements: resolveElementsModule(config.elements?.behaviors, root),
142
185
  lazyElements: resolveLazyElements(config.elements),
143
186
  });
144
- export { LIBRARIES_WILDCARD, GIR_LIBRARY_PATTERN, defineConfig, isValidApplicationId, resolveReactCompilerOptions, validateConfig, mergeConfig, resolveLazyElements, resolveConfig, };
187
+ export { defineConfig, isValidApplicationId, resolveReactCompilerOptions, validateConfig, mergeConfig, resolveLazyElements, resolveElementComponents, resolveElementProps, resolveOmittedProps, resolveConfig, };
145
188
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { createDefineConfig, type DefineConfig } from \"c12\";\nimport { defu } from \"defu\";\nimport { resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport { configError, isRecord, rawIssue } from \"./config-error.js\";\nimport { resolveUserEventSignals } from \"./user-event-signals.js\";\n\ntype ReactCompilerCompilationMode = (typeof COMPILATION_MODES)[number];\ntype ReactCompilerPanicThreshold = (typeof PANIC_THRESHOLDS)[number];\n\ntype ReactCompilerOptions = {\n compilationMode?: ReactCompilerCompilationMode;\n panicThreshold?: ReactCompilerPanicThreshold;\n};\n\n/**\n * React Compiler options resolved for the build, with the compilation target\n * fixed to React 19.\n */\ntype ResolvedReactCompilerOptions = ReactCompilerOptions & {\n target: \"19\";\n};\n\n/**\n * User-facing configuration for a GTKX project, as authored in `gtkx.config.ts`:\n * the GIR libraries to bind, extra `.gir` search paths, the GApplication id,\n * a module of per-element configuration (lazy flags and custom behaviors),\n * per-element component wrappers keyed by GLib type name, the React Compiler and\n * codegen settings, and additional user event signals to suppress during React\n * commits.\n */\ntype Config = z.infer<typeof configSchema>;\n\n/**\n * Configuration reduced to the values needed at runtime: the GApplication\n * identifier, the resolved React Compiler options (`null` when disabled), the\n * user event signals suppressed while a React commit is in progress, and the\n * module path holding per-element configuration (`null` when unset).\n */\ntype ResolvedConfig = {\n applicationId: string;\n reactCompiler: ResolvedReactCompilerOptions | null;\n userEventSignals: Record<string, string[]>;\n elements: string | null;\n lazyElements: string[];\n};\n\nconst LIBRARIES_WILDCARD = \"*\";\nconst GIR_LIBRARY_PATTERN = /^[A-Za-z][A-Za-z0-9]*-\\d+(?:\\.\\d+)*$/;\nconst APPLICATION_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_-]*)+$/;\nconst APPLICATION_ID_MAX_LENGTH = 255;\nconst COMPILATION_MODES = [\"infer\", \"syntax\", \"annotation\", \"all\"] as const;\nconst PANIC_THRESHOLDS = [\"none\", \"critical_errors\", \"all_errors\"] as const;\nconst REACT_COMPILER_TARGET = \"19\";\nconst COMPILATION_MODE_SET: Set<string> = new Set(COMPILATION_MODES);\nconst PANIC_THRESHOLD_SET: Set<string> = new Set(PANIC_THRESHOLDS);\n\nconst librariesSchema = z.custom<typeof LIBRARIES_WILDCARD | string[]>().check((ctx) => {\n const value = ctx.value;\n\n if (value === LIBRARIES_WILDCARD) {\n return;\n }\n\n if (!Array.isArray(value) || value.length === 0) {\n ctx.issues.push(rawIssue(value, [], `must be \"${LIBRARIES_WILDCARD}\", a non-empty string array, or omitted`));\n\n return;\n }\n\n for (const [index, entry] of value.entries()) {\n ctx.issues.push(...libraryEntryIssues(value, index, entry));\n }\n});\n\nconst applicationIdSchema = z.custom<string>().check((ctx) => {\n const value = ctx.value;\n\n if (typeof value !== \"string\" || !isValidApplicationId(value)) {\n ctx.issues.push(\n rawIssue(\n value,\n [],\n `invalid \\`applicationId\\` \"${value}\", must satisfy g_application_id_is_valid ` +\n '(e.g. \"org.example.MyApp\")',\n true,\n ),\n );\n }\n});\n\nconst reactCompilerSchema = z.custom<boolean | ReactCompilerOptions>().check((ctx) => {\n const value = ctx.value;\n\n if (typeof value === \"boolean\") {\n return;\n }\n\n if (!isRecord(value)) {\n ctx.issues.push(rawIssue(value, [], \"must be a boolean or an options object\"));\n\n return;\n }\n\n const compilationMode = value.compilationMode;\n\n if (!isValidReactCompilerOption(compilationMode, COMPILATION_MODE_SET)) {\n ctx.issues.push(\n rawIssue(\n value,\n [],\n `invalid \\`reactCompiler.compilationMode\\` \"${String(compilationMode)}\", ` +\n `must be one of ${COMPILATION_MODES.join(\", \")}`,\n true,\n ),\n );\n }\n\n const panicThreshold = value.panicThreshold;\n\n if (!isValidReactCompilerOption(panicThreshold, PANIC_THRESHOLD_SET)) {\n ctx.issues.push(\n rawIssue(\n value,\n [],\n `invalid \\`reactCompiler.panicThreshold\\` \"${String(panicThreshold)}\", ` +\n `must be one of ${PANIC_THRESHOLDS.join(\", \")}`,\n true,\n ),\n );\n }\n});\n\nconst userEventSignalsSchema = z.record(\n z.string(),\n z.array(\n z.string({ error: \"must be a non-empty signal name\" }).min(1, { error: \"must be a non-empty signal name\" }),\n {\n error: \"must be an array of signal names\",\n },\n ),\n { error: \"must be a record of GLib type names to signal name arrays\" },\n);\n\nconst moduleExportSchema = z.object(\n {\n module: z.string({ error: \"must be a module specifier\" }).min(1, { error: \"must be a module specifier\" }),\n export: z.string({ error: \"must be an export name\" }).min(1, { error: \"must be an export name\" }),\n },\n { error: \"must be a { module, export } object\" },\n);\n\nconst elementConfigSchema = z.object({\n component: moduleExportSchema.optional(),\n props: moduleExportSchema.optional(),\n lazy: z.boolean({ error: \"must be a boolean\" }).optional(),\n});\n\nconst elementsSchema = z.object({\n behaviors: z\n .string({ error: \"must be a path to a module exporting element behaviors\" })\n .min(1, { error: \"must be a path to a module exporting element behaviors\" })\n .optional(),\n config: z.record(z.string(), elementConfigSchema).optional(),\n});\n\nconst configSchema = z.object({\n libraries: librariesSchema.optional(),\n girPath: z.array(z.string(), { error: \"must be an array of strings if provided\" }).optional(),\n applicationId: applicationIdSchema,\n reactCompiler: reactCompilerSchema.optional(),\n codegen: z.boolean({ error: \"must be a boolean\" }).optional(),\n userEventSignals: userEventSignalsSchema.optional(),\n elements: elementsSchema.optional(),\n});\n\n/**\n * Identity helper that returns the given configuration typed as {@link Config},\n * enabling editor autocompletion and type checking in `gtkx.config.ts`.\n */\nconst defineConfig: DefineConfig<Config> = createDefineConfig<Config>();\n\nconst libraryEntryIssues = (value: unknown[], index: number, entry: unknown): ReturnType<typeof rawIssue>[] => {\n if (typeof entry === \"string\" && GIR_LIBRARY_PATTERN.test(entry)) {\n return [];\n }\n\n if (entry === LIBRARIES_WILDCARD) {\n const message =\n `to generate every library, set \\`libraries: \"${LIBRARIES_WILDCARD}\"\\` as a bare string, ` +\n \"not an array entry\";\n\n return [rawIssue(value, [index], message, true)];\n }\n\n const message =\n `invalid library identifier \"${String(entry)}\", must be of the form \"Name-Version\" ` +\n '(e.g. \"Gtk-4.0\")';\n\n return [rawIssue(value, [index], message, true)];\n};\n\nconst isValidApplicationId = (applicationId: string): boolean => {\n if (applicationId.length === 0 || applicationId.length > APPLICATION_ID_MAX_LENGTH) {\n return false;\n }\n\n return APPLICATION_ID_PATTERN.test(applicationId);\n};\n\nconst resolveReactCompilerOptions = (setting: Config[\"reactCompiler\"]): ResolvedReactCompilerOptions | null => {\n if (setting === false) {\n return null;\n }\n\n const overrides = setting === undefined || setting === true ? {} : setting;\n\n return { ...overrides, target: REACT_COMPILER_TARGET };\n};\n\nconst isValidReactCompilerOption = (value: unknown, allowed: Set<string>): boolean =>\n value === undefined || (typeof value === \"string\" && allowed.has(value));\n\nconst validateConfig = (config: Config): void => {\n const result = configSchema.safeParse(config);\n\n if (!result.success) {\n throw configError(result.error);\n }\n};\n\n/**\n * Deep-merges two configurations, with `override` taking precedence over `base`.\n * @param base The lower-priority configuration.\n * @param override The higher-priority configuration whose values win on conflict.\n */\nconst mergeConfig = (base: Config, override: Config): Config => defu(override, base);\n\nconst resolveElementsModule = (behaviors: string | undefined, root: string | undefined): string | null => {\n if (behaviors === undefined) {\n return null;\n }\n\n return root === undefined ? behaviors : resolve(root, behaviors);\n};\n\nconst resolveLazyElements = (elements: Config[\"elements\"]): string[] =>\n Object.entries(elements?.config ?? {})\n .filter(([, entry]) => entry.lazy === true)\n .map(([type]) => type);\n\nconst resolveConfig = (config: Config, root?: string): ResolvedConfig => ({\n applicationId: config.applicationId,\n reactCompiler: resolveReactCompilerOptions(config.reactCompiler),\n userEventSignals: resolveUserEventSignals(config.userEventSignals),\n elements: resolveElementsModule(config.elements?.behaviors, root),\n lazyElements: resolveLazyElements(config.elements),\n});\n\nexport {\n LIBRARIES_WILDCARD,\n GIR_LIBRARY_PATTERN,\n defineConfig,\n isValidApplicationId,\n resolveReactCompilerOptions,\n validateConfig,\n mergeConfig,\n resolveLazyElements,\n resolveConfig,\n type ResolvedReactCompilerOptions,\n type Config,\n type ResolvedConfig,\n};\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { createDefineConfig, type DefineConfig } from \"c12\";\nimport { defu } from \"defu\";\nimport { resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport { configError, isRecord, rawIssue } from \"./config-error.ts\";\nimport { resolveUserEventSignals } from \"./user-event-signals.ts\";\n\n/** Accepted `reactCompiler.compilationMode` values, choosing which functions the compiler processes. */\ntype ReactCompilerCompilationMode = (typeof COMPILATION_MODES)[number];\n/** Accepted `reactCompiler.panicThreshold` values, choosing which compiler diagnostics fail the build. */\ntype ReactCompilerPanicThreshold = (typeof PANIC_THRESHOLDS)[number];\n\n/** Object form of the `reactCompiler` config key, forwarded as-is to `babel-plugin-react-compiler`. */\ntype ReactCompilerOptions = {\n /** Which functions the compiler processes; left to the compiler's own default when omitted. */\n compilationMode?: ReactCompilerCompilationMode;\n /** Which compiler diagnostics fail the build; left to the compiler's own default when omitted. */\n panicThreshold?: ReactCompilerPanicThreshold;\n};\n\n/**\n * The React Compiler options the build hands to `babel-plugin-react-compiler`, with the React version\n * GTKX targets filled in.\n */\ntype ResolvedReactCompilerOptions = ReactCompilerOptions & {\n /** React major version the compiler emits for. */\n target: \"19\";\n};\n\n/**\n * User-facing configuration for a GTKX project, as authored in `gtkx.config.ts`: the GIR libraries\n * to bind and where to find them, the GApplication id, per-element configuration, and the React\n * Compiler, codegen, and user event signal settings.\n */\ntype Config = z.infer<typeof configSchema>;\n/** A named export referenced as plain data, so codegen can emit an import for it without loading the module. */\ntype ModuleExport = z.infer<typeof moduleExportSchema>;\n/** One `elements.config` entry, describing how a single GLib type's generated element is shaped. */\ntype ElementConfigEntry = z.infer<typeof elementConfigSchema>;\n\n/** Configuration reduced to the values the app runtime and the build need, with paths already resolved. */\ntype ResolvedConfig = {\n /** The GApplication identifier the app registers under. */\n applicationId: string;\n /** React Compiler options for the build, or `null` when the compiler is disabled. */\n reactCompiler: ResolvedReactCompilerOptions | null;\n /** Signal names, keyed by GLib type name, that stay suppressed while a React commit runs. */\n userEventSignals: Record<string, string[]>;\n /** Path of the module exporting per-element configs, or `null` when none is configured. */\n elements: string | null;\n /** GLib type names of the elements marked `isLazy`, whose GObject their parent container creates. */\n lazyElements: string[];\n};\n\nconst LIBRARIES_WILDCARD = \"*\";\nconst GIR_LIBRARY_PATTERN = /^[A-Za-z][A-Za-z0-9]*-\\d+(?:\\.\\d+)*$/;\nconst APPLICATION_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_-]*)+$/;\nconst APPLICATION_ID_MAX_LENGTH = 255;\nconst COMPILATION_MODES = [\"infer\", \"syntax\", \"annotation\", \"all\"] as const;\nconst PANIC_THRESHOLDS = [\"none\", \"critical_errors\", \"all_errors\"] as const;\nconst REACT_COMPILER_TARGET = \"19\";\nconst COMPILATION_MODE_SET: Set<string> = new Set(COMPILATION_MODES);\nconst PANIC_THRESHOLD_SET: Set<string> = new Set(PANIC_THRESHOLDS);\n\n/** Validates `libraries`: the `\"*\"` wildcard on its own, or a non-empty array of `Name-Version` identifiers. */\nconst librariesSchema = z.custom<typeof LIBRARIES_WILDCARD | string[]>().check((ctx) => {\n const value = ctx.value;\n\n if (value === LIBRARIES_WILDCARD) {\n return;\n }\n\n if (!Array.isArray(value) || value.length === 0) {\n ctx.issues.push(rawIssue(value, [], `must be \"${LIBRARIES_WILDCARD}\", a non-empty string array, or omitted`));\n\n return;\n }\n\n for (const [index, entry] of value.entries()) {\n ctx.issues.push(...libraryEntryIssues(value, index, entry));\n }\n});\n\n/** Validates `applicationId` against the reverse-DNS form `g_application_id_is_valid` accepts. */\nconst applicationIdSchema = z.custom<string>().check((ctx) => {\n const value = ctx.value;\n\n if (typeof value !== \"string\" || !isValidApplicationId(value)) {\n ctx.issues.push(\n rawIssue(\n value,\n [],\n `invalid \\`applicationId\\` \"${value}\", must satisfy g_application_id_is_valid ` +\n '(e.g. \"org.example.MyApp\")',\n true,\n ),\n );\n }\n});\n\n/** Validates `reactCompiler`: a boolean, or an options object whose mode and threshold are known values. */\nconst reactCompilerSchema = z.custom<boolean | ReactCompilerOptions>().check((ctx) => {\n const value = ctx.value;\n\n if (typeof value === \"boolean\") {\n return;\n }\n\n if (!isRecord(value)) {\n ctx.issues.push(rawIssue(value, [], \"must be a boolean or an options object\"));\n\n return;\n }\n\n const compilationMode = value.compilationMode;\n\n if (!isValidReactCompilerOption(compilationMode, COMPILATION_MODE_SET)) {\n ctx.issues.push(\n rawIssue(\n value,\n [],\n `invalid \\`reactCompiler.compilationMode\\` \"${String(compilationMode)}\", ` +\n `must be one of ${COMPILATION_MODES.join(\", \")}`,\n true,\n ),\n );\n }\n\n const panicThreshold = value.panicThreshold;\n\n if (!isValidReactCompilerOption(panicThreshold, PANIC_THRESHOLD_SET)) {\n ctx.issues.push(\n rawIssue(\n value,\n [],\n `invalid \\`reactCompiler.panicThreshold\\` \"${String(panicThreshold)}\", ` +\n `must be one of ${PANIC_THRESHOLDS.join(\", \")}`,\n true,\n ),\n );\n }\n});\n\n/** Validates `userEventSignals`: GLib type names mapped to arrays of non-empty signal names. */\nconst userEventSignalsSchema = z.record(\n z.string(),\n z.array(\n z.string({ error: \"must be a non-empty signal name\" }).min(1, { error: \"must be a non-empty signal name\" }),\n {\n error: \"must be an array of signal names\",\n },\n ),\n { error: \"must be a record of GLib type names to signal name arrays\" },\n);\n\n/** Validates a `{ module, export }` reference to a named export. */\nconst moduleExportSchema = z.object(\n {\n /** Specifier the export is imported from. */\n module: z.string({ error: \"must be a module specifier\" }).min(1, { error: \"must be a module specifier\" }),\n /** Identifier the module exports it under. */\n export: z.string({ error: \"must be an export name\" }).min(1, { error: \"must be an export name\" }),\n },\n { error: \"must be a { module, export } object\" },\n);\n\n/** Validates one entry of `elements.config`. */\nconst elementConfigSchema = z.object({\n /** Component that wraps the generated element. */\n component: moduleExportSchema.optional(),\n /** Base props interface the generated element props extend. */\n props: moduleExportSchema.optional(),\n /** Marks the element as having no GObject of its own, its parent container creating one instead. */\n isLazy: z.boolean({ error: \"must be a boolean\" }).optional(),\n /** GObject properties to leave out of the generated element props, such as those a behavior fills. */\n omittedProps: z\n .array(\n z.string({ error: \"must be a non-empty property name\" }).min(1, {\n error: \"must be a non-empty property name\",\n }),\n { error: \"must be an array of property names\" },\n )\n .optional(),\n});\n\n/** Validates `elements`: the behaviors module the app loads at runtime, plus the per-type entries. */\nconst elementsSchema = z.object({\n /**\n * Path, resolved against the project root, of a module default-exporting element configs keyed by\n * GLib type name, as `defineElements` returns.\n */\n behaviors: z\n .string({ error: \"must be a path to a module exporting element behaviors\" })\n .min(1, { error: \"must be a path to a module exporting element behaviors\" })\n .optional(),\n /** Per-element entries, keyed by GLib type name. */\n config: z.record(z.string(), elementConfigSchema).optional(),\n});\n\n/** The shape every `gtkx.config.ts` is validated against, and the source of the {@link Config} type. */\nconst configSchema = z.object({\n /**\n * GIR namespaces to bind as `Name-Version`, or `\"*\"` for every one installed; omitting it gives\n * `Gtk-4.0`, which is also prepended to any list that names no Gtk version of its own.\n */\n libraries: librariesSchema.optional(),\n /** Directories searched for `.gir` files ahead of `GTKX_GIR_PATH` and the system locations. */\n girPath: z.array(z.string(), { error: \"must be an array of strings if provided\" }).optional(),\n /** Reverse-DNS GApplication identifier, and the default `applicationId` of the application element. */\n applicationId: applicationIdSchema,\n /** React Compiler settings; the compiler runs unless this is `false`. */\n reactCompiler: reactCompilerSchema.optional(),\n /** Whether to generate the binding stores; `false` makes the CLI resolve an already-installed one. */\n codegen: z.boolean({ error: \"must be a boolean\" }).optional(),\n /**\n * Signal names, keyed by GLib type name, that stay suppressed while a React commit applies props, so\n * their handlers only ever report user interaction. Unioned with the built-in GTK4 and Adwaita table.\n */\n userEventSignals: userEventSignalsSchema.optional(),\n /** Per-element customization: where the behaviors module lives and how each type's element is shaped. */\n elements: elementsSchema.optional(),\n});\n\n/**\n * Returns the given configuration unchanged, typed as {@link Config}, so `gtkx.config.ts` gets\n * autocompletion and type checking.\n */\nconst defineConfig: DefineConfig<Config> = createDefineConfig<Config>();\n\nconst libraryEntryIssues = (value: unknown[], index: number, entry: unknown): ReturnType<typeof rawIssue>[] => {\n if (typeof entry === \"string\" && GIR_LIBRARY_PATTERN.test(entry)) {\n return [];\n }\n\n if (entry === LIBRARIES_WILDCARD) {\n const message =\n `to generate every library, set \\`libraries: \"${LIBRARIES_WILDCARD}\"\\` as a bare string, ` +\n \"not an array entry\";\n\n return [rawIssue(value, [index], message, true)];\n }\n\n const message =\n `invalid library identifier \"${String(entry)}\", must be of the form \"Name-Version\" ` +\n '(e.g. \"Gtk-4.0\")';\n\n return [rawIssue(value, [index], message, true)];\n};\n\nconst isValidApplicationId = (applicationId: string): boolean => {\n if (applicationId.length === 0 || applicationId.length > APPLICATION_ID_MAX_LENGTH) {\n return false;\n }\n\n return APPLICATION_ID_PATTERN.test(applicationId);\n};\n\nconst resolveReactCompilerOptions = (setting: Config[\"reactCompiler\"]): ResolvedReactCompilerOptions | null => {\n if (setting === false) {\n return null;\n }\n\n const overrides = setting === undefined || setting === true ? {} : setting;\n\n return { ...overrides, target: REACT_COMPILER_TARGET };\n};\n\nconst isValidReactCompilerOption = (value: unknown, allowed: Set<string>): boolean =>\n value === undefined || (typeof value === \"string\" && allowed.has(value));\n\nconst validateConfig = (config: Config): void => {\n const result = configSchema.safeParse(config);\n\n if (!result.success) {\n throw configError(result.error);\n }\n};\n\n/**\n * Deep-merges two configurations. `override` wins over `base` on conflicting scalar and object keys, while\n * arrays are concatenated with the `override` entries first.\n */\nconst mergeConfig = (base: Config, override: Config): Config => defu(override, base);\n\nconst resolveElementsModule = (behaviors: string | undefined, root: string | undefined): string | null => {\n if (behaviors === undefined) {\n return null;\n }\n\n return root === undefined ? behaviors : resolve(root, behaviors);\n};\n\nconst resolveLazyElements = (elements: Config[\"elements\"]): string[] =>\n Object.entries(elements?.config ?? {})\n .filter(([, entry]) => entry.isLazy === true)\n .map(([type]) => type);\n\nconst elementEntryValues = <T>(\n elements: Config[\"elements\"],\n pick: (entry: ElementConfigEntry) => T | undefined,\n): Record<string, T> =>\n Object.fromEntries(\n Object.entries(elements?.config ?? {}).flatMap(([type, entry]) => {\n const value = pick(entry);\n\n return value === undefined ? [] : [[type, value] as const];\n }),\n );\n\nconst resolveElementComponents = (elements: Config[\"elements\"]): Record<string, ModuleExport> =>\n elementEntryValues(elements, (entry) => entry.component);\n\nconst resolveElementProps = (elements: Config[\"elements\"]): Record<string, ModuleExport> =>\n elementEntryValues(elements, (entry) => entry.props);\n\nconst resolveOmittedProps = (elements: Config[\"elements\"]): Record<string, string[]> =>\n elementEntryValues(elements, (entry) => entry.omittedProps);\n\nconst resolveConfig = (config: Config, root?: string): ResolvedConfig => ({\n applicationId: config.applicationId,\n reactCompiler: resolveReactCompilerOptions(config.reactCompiler),\n userEventSignals: resolveUserEventSignals(config.userEventSignals),\n elements: resolveElementsModule(config.elements?.behaviors, root),\n lazyElements: resolveLazyElements(config.elements),\n});\n\nexport {\n defineConfig,\n isValidApplicationId,\n resolveReactCompilerOptions,\n validateConfig,\n mergeConfig,\n resolveLazyElements,\n resolveElementComponents,\n resolveElementProps,\n resolveOmittedProps,\n resolveConfig,\n type ResolvedReactCompilerOptions,\n type Config,\n type ModuleExport,\n type ResolvedConfig,\n};\n"]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
- export { type Config, defineConfig, mergeConfig, type ResolvedConfig, type ResolvedReactCompilerOptions, } from "./config.js";
2
- export { type LoadedConfig, loadConfig } from "./loader.js";
1
+ /** @public */
2
+ export { type Config, defineConfig, mergeConfig, type ResolvedConfig } from "./config.ts";
3
+ /** @public */
4
+ export { type LoadedConfig, loadConfig } from "./loader.ts";
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,MAAM,EACX,YAAY,EACZ,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,4BAA4B,GACpC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAE,KAAK,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC1F,cAAc;AACd,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
- export { defineConfig, mergeConfig, } from "./config.js";
1
+ /** @public */
2
+ export { defineConfig, mergeConfig } from "./config.js";
3
+ /** @public */
2
4
  export { loadConfig } from "./loader.js";
3
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EACZ,WAAW,GAGd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAqB,UAAU,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export {\n type Config,\n defineConfig,\n mergeConfig,\n type ResolvedConfig,\n type ResolvedReactCompilerOptions,\n} from \"./config.js\";\nexport { type LoadedConfig, loadConfig } from \"./loader.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAe,YAAY,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAC1F,cAAc;AACd,OAAO,EAAqB,UAAU,EAAE,MAAM,aAAa,CAAC","sourcesContent":["/** @public */\nexport { type Config, defineConfig, mergeConfig, type ResolvedConfig } from \"./config.ts\";\n/** @public */\nexport { type LoadedConfig, loadConfig } from \"./loader.ts\";\n"]}
@@ -1,3 +1,4 @@
1
- export { GIR_LIBRARY_PATTERN, isValidApplicationId, LIBRARIES_WILDCARD, resolveLazyElements } from "./config.js";
2
- export { type ConfigLoader, createConfigLoader } from "./loader.js";
1
+ export type { ResolvedReactCompilerOptions } from "./config.ts";
2
+ export { isValidApplicationId, resolveElementComponents, resolveElementProps, resolveLazyElements, resolveOmittedProps, } from "./config.ts";
3
+ export { type ConfigLoader, createConfigLoader } from "./loader.ts";
3
4
  //# sourceMappingURL=internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjH,OAAO,EAAE,KAAK,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EACH,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
package/dist/internal.js CHANGED
@@ -1,3 +1,3 @@
1
- export { GIR_LIBRARY_PATTERN, isValidApplicationId, LIBRARIES_WILDCARD, resolveLazyElements } from "./config.js";
1
+ export { isValidApplicationId, resolveElementComponents, resolveElementProps, resolveLazyElements, resolveOmittedProps, } from "./config.js";
2
2
  export { createConfigLoader } from "./loader.js";
3
3
  //# sourceMappingURL=internal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjH,OAAO,EAAqB,kBAAkB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export { GIR_LIBRARY_PATTERN, isValidApplicationId, LIBRARIES_WILDCARD, resolveLazyElements } from \"./config.js\";\nexport { type ConfigLoader, createConfigLoader } from \"./loader.js\";\n"]}
1
+ {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AACA,OAAO,EACH,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAqB,kBAAkB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export type { ResolvedReactCompilerOptions } from \"./config.ts\";\nexport {\n isValidApplicationId,\n resolveElementComponents,\n resolveElementProps,\n resolveLazyElements,\n resolveOmittedProps,\n} from \"./config.ts\";\nexport { type ConfigLoader, createConfigLoader } from \"./loader.ts\";\n"]}
package/dist/loader.d.ts CHANGED
@@ -1,23 +1,30 @@
1
- import { type Config, type ResolvedConfig } from "./config.js";
2
- /**
3
- * Result of loading a `gtkx.config.ts` file: the parsed configuration, the
4
- * resolved config file path (`undefined` when none was found), and the project
5
- * root it was loaded from.
6
- */
1
+ import { type Config, type ResolvedConfig } from "./config.ts";
2
+ /** Result of loading a project's `gtkx.config.ts` file. */
7
3
  type LoadedConfig = {
4
+ /** The parsed and validated configuration. */
8
5
  config: Config;
9
- configFile: string | undefined;
6
+ /** Absolute path of the configuration file that was loaded. */
7
+ configFile: string;
8
+ /** Absolute path of the project root the configuration was resolved against. */
10
9
  root: string;
11
10
  };
11
+ /** How a configuration file is read, shared by {@link loadConfig} and `createConfigLoader`. */
12
12
  type LoadConfigOptions = {
13
+ /**
14
+ * Environment name such as `development`: the config's matching `$<mode>` block is layered over the
15
+ * top-level values, and the name is passed to a config authored as a function.
16
+ */
13
17
  mode?: string | undefined;
14
18
  };
15
- type ConfigLoader = (cwd: string) => Promise<ResolvedConfig>;
19
+ type ConfigLoader = {
20
+ load: (cwd: string) => Promise<LoadedConfig>;
21
+ resolve: (cwd: string) => Promise<ResolvedConfig>;
22
+ };
16
23
  /**
17
- * Loads and validates the `gtkx.config.ts` file for a project, returning the
18
- * parsed configuration together with the config file path and project root.
19
- * @param cwd Directory from which to search for the configuration file.
20
- * @param options Loading options, such as the environment mode.
24
+ * Loads and validates the `gtkx.config.ts` file for a project.
25
+ * @param cwd Directory the configuration file is looked up in.
26
+ * @param options Loading options, such as the environment mode whose overrides are applied.
27
+ * @throws When the configuration fails validation, or when no configuration file is found.
21
28
  */
22
29
  declare const loadConfig: (cwd: string, options?: LoadConfigOptions) => Promise<LoadedConfig>;
23
30
  declare const createConfigLoader: (options?: LoadConfigOptions) => ConfigLoader;
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAiB,KAAK,cAAc,EAAkB,MAAM,aAAa,CAAC;AAE9F;;;;GAIG;AACH,KAAK,YAAY,GAAG;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF,KAAK,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7D;;;;;GAKG;AACH,QAAA,MAAM,UAAU,GAAU,KAAK,MAAM,EAAE,UAAS,iBAAsB,KAAG,OAAO,CAAC,YAAY,CAuB5F,CAAC;AAEF,QAAA,MAAM,kBAAkB,GAAI,UAAS,iBAAsB,KAAG,YAqB7D,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiB,KAAK,cAAc,EAAkB,MAAM,aAAa,CAAC;AAE9F,2DAA2D;AAC3D,KAAK,YAAY,GAAG;IAChB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,+FAA+F;AAC/F,KAAK,iBAAiB,GAAG;IACrB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF,KAAK,YAAY,GAAG;IAChB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACrD,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,UAAU,GAAU,KAAK,MAAM,EAAE,UAAS,iBAAsB,KAAG,OAAO,CAAC,YAAY,CAwB5F,CAAC;AAEF,QAAA,MAAM,kBAAkB,GAAI,UAAS,iBAAsB,KAAG,YAiB7D,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,CAAC"}
package/dist/loader.js CHANGED
@@ -1,12 +1,13 @@
1
+ import { getOrInsert } from "@gtkx/utils";
1
2
  import { loadConfig as loadConfigFile } from "c12";
2
3
  import { existsSync } from "node:fs";
3
4
  import { resolve } from "node:path";
4
5
  import { resolveConfig, validateConfig } from "./config.js";
5
6
  /**
6
- * Loads and validates the `gtkx.config.ts` file for a project, returning the
7
- * parsed configuration together with the config file path and project root.
8
- * @param cwd Directory from which to search for the configuration file.
9
- * @param options Loading options, such as the environment mode.
7
+ * Loads and validates the `gtkx.config.ts` file for a project.
8
+ * @param cwd Directory the configuration file is looked up in.
9
+ * @param options Loading options, such as the environment mode whose overrides are applied.
10
+ * @throws When the configuration fails validation, or when no configuration file is found.
10
11
  */
11
12
  const loadConfig = async (cwd, options = {}) => {
12
13
  const result = await loadConfigFile({
@@ -19,31 +20,28 @@ const loadConfig = async (cwd, options = {}) => {
19
20
  ...((options.mode !== undefined) && { envName: options.mode }),
20
21
  });
21
22
  const config = result.config;
22
- const isFound = result.configFile !== undefined && existsSync(resolve(cwd, result.configFile));
23
- if (isFound) {
24
- validateConfig(config);
23
+ const configFile = result.configFile;
24
+ validateConfig(config);
25
+ if (configFile === undefined || !existsSync(resolve(cwd, configFile))) {
26
+ throw new Error(`gtkx.config.ts: no configuration file was found from ${cwd}`);
25
27
  }
26
28
  return {
27
29
  config,
28
- configFile: isFound ? result.configFile : undefined,
30
+ configFile,
29
31
  root: result.cwd ?? cwd,
30
32
  };
31
33
  };
32
34
  const createConfigLoader = (options = {}) => {
33
- const cache = new Map();
34
- const loadResolved = async (root) => {
35
- const { config } = await loadConfig(root, options);
36
- validateConfig(config);
37
- return resolveConfig(config, root);
35
+ const loaded = new Map();
36
+ const resolved = new Map();
37
+ const load = (cwd) => getOrInsert(loaded, resolve(cwd), (root) => loadConfig(root, options));
38
+ const resolveAt = async (root) => {
39
+ const { config, root: configRoot } = await load(root);
40
+ return resolveConfig(config, configRoot);
38
41
  };
39
- return (cwd) => {
40
- const root = resolve(cwd);
41
- let pending = cache.get(root);
42
- if (!pending) {
43
- pending = loadResolved(root);
44
- cache.set(root, pending);
45
- }
46
- return pending;
42
+ return {
43
+ load,
44
+ resolve: (cwd) => getOrInsert(resolved, resolve(cwd), resolveAt),
47
45
  };
48
46
  };
49
47
  export { loadConfig, createConfigLoader };
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAe,aAAa,EAAuB,cAAc,EAAE,MAAM,aAAa,CAAC;AAmB9F;;;;;GAKG;AACH,MAAM,UAAU,GAAG,KAAK,EAAE,GAAW,EAAE,UAA6B,EAAE,EAAyB,EAAE;IAC7F,MAAM,MAAM,GAAG,MAAM,cAAc,CAAS;QACxC,IAAI,EAAE,MAAM;QACZ,GAAG;QACH,MAAM,EAAE,KAAK;QACb,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;QAC/B,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;KACjE,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAE/F,IAAI,OAAO,EAAE,CAAC;QACV,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO;QACH,MAAM;QACN,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QACnD,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG;KAC1B,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,UAA6B,EAAE,EAAgB,EAAE;IACzE,MAAM,KAAK,GAAyC,IAAI,GAAG,EAAE,CAAC;IAE9D,MAAM,YAAY,GAAG,KAAK,EAAE,IAAY,EAA2B,EAAE;QACjE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACnD,cAAc,CAAC,MAAM,CAAC,CAAC;QAEvB,OAAO,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,OAAO,CAAC,GAAW,EAA2B,EAAE;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE9B,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAC7B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7B,CAAC;QAED,OAAO,OAAO,CAAC;IACnB,CAAC,CAAC;AACN,CAAC,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAgE,CAAC","sourcesContent":["import { loadConfig as loadConfigFile } from \"c12\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { type Config, resolveConfig, type ResolvedConfig, validateConfig } from \"./config.js\";\n\n/**\n * Result of loading a `gtkx.config.ts` file: the parsed configuration, the\n * resolved config file path (`undefined` when none was found), and the project\n * root it was loaded from.\n */\ntype LoadedConfig = {\n config: Config;\n configFile: string | undefined;\n root: string;\n};\n\ntype LoadConfigOptions = {\n mode?: string | undefined;\n};\n\ntype ConfigLoader = (cwd: string) => Promise<ResolvedConfig>;\n\n/**\n * Loads and validates the `gtkx.config.ts` file for a project, returning the\n * parsed configuration together with the config file path and project root.\n * @param cwd Directory from which to search for the configuration file.\n * @param options Loading options, such as the environment mode.\n */\nconst loadConfig = async (cwd: string, options: LoadConfigOptions = {}): Promise<LoadedConfig> => {\n const result = await loadConfigFile<Config>({\n name: \"gtkx\",\n cwd,\n rcFile: false,\n globalRc: false,\n packageJson: false,\n context: { mode: options.mode },\n ...((options.mode !== undefined) && { envName: options.mode }),\n });\n\n const config = result.config;\n const isFound = result.configFile !== undefined && existsSync(resolve(cwd, result.configFile));\n\n if (isFound) {\n validateConfig(config);\n }\n\n return {\n config,\n configFile: isFound ? result.configFile : undefined,\n root: result.cwd ?? cwd,\n };\n};\n\nconst createConfigLoader = (options: LoadConfigOptions = {}): ConfigLoader => {\n const cache: Map<string, Promise<ResolvedConfig>> = new Map();\n\n const loadResolved = async (root: string): Promise<ResolvedConfig> => {\n const { config } = await loadConfig(root, options);\n validateConfig(config);\n\n return resolveConfig(config, root);\n };\n\n return (cwd: string): Promise<ResolvedConfig> => {\n const root = resolve(cwd);\n let pending = cache.get(root);\n\n if (!pending) {\n pending = loadResolved(root);\n cache.set(root, pending);\n }\n\n return pending;\n };\n};\n\nexport { loadConfig, createConfigLoader, type LoadedConfig, type LoadConfigOptions, type ConfigLoader };\n"]}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAe,aAAa,EAAuB,cAAc,EAAE,MAAM,aAAa,CAAC;AA0B9F;;;;;GAKG;AACH,MAAM,UAAU,GAAG,KAAK,EAAE,GAAW,EAAE,UAA6B,EAAE,EAAyB,EAAE;IAC7F,MAAM,MAAM,GAAG,MAAM,cAAc,CAAS;QACxC,IAAI,EAAE,MAAM;QACZ,GAAG;QACH,MAAM,EAAE,KAAK;QACb,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;QAC/B,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;KACjE,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvB,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,wDAAwD,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,OAAO;QACH,MAAM;QACN,UAAU;QACV,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG;KAC1B,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,UAA6B,EAAE,EAAgB,EAAE;IACzE,MAAM,MAAM,GAAuC,IAAI,GAAG,EAAE,CAAC;IAC7D,MAAM,QAAQ,GAAyC,IAAI,GAAG,EAAE,CAAC;IAEjE,MAAM,IAAI,GAAG,CAAC,GAAW,EAAyB,EAAE,CAChD,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAE3E,MAAM,SAAS,GAAG,KAAK,EAAE,IAAY,EAA2B,EAAE;QAC9D,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAEtD,OAAO,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,OAAO;QACH,IAAI;QACJ,OAAO,EAAE,CAAC,GAAW,EAA2B,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;KACpG,CAAC;AACN,CAAC,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAgE,CAAC","sourcesContent":["import { getOrInsert } from \"@gtkx/utils\";\nimport { loadConfig as loadConfigFile } from \"c12\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { type Config, resolveConfig, type ResolvedConfig, validateConfig } from \"./config.ts\";\n\n/** Result of loading a project's `gtkx.config.ts` file. */\ntype LoadedConfig = {\n /** The parsed and validated configuration. */\n config: Config;\n /** Absolute path of the configuration file that was loaded. */\n configFile: string;\n /** Absolute path of the project root the configuration was resolved against. */\n root: string;\n};\n\n/** How a configuration file is read, shared by {@link loadConfig} and `createConfigLoader`. */\ntype LoadConfigOptions = {\n /**\n * Environment name such as `development`: the config's matching `$<mode>` block is layered over the\n * top-level values, and the name is passed to a config authored as a function.\n */\n mode?: string | undefined;\n};\n\ntype ConfigLoader = {\n load: (cwd: string) => Promise<LoadedConfig>;\n resolve: (cwd: string) => Promise<ResolvedConfig>;\n};\n\n/**\n * Loads and validates the `gtkx.config.ts` file for a project.\n * @param cwd Directory the configuration file is looked up in.\n * @param options Loading options, such as the environment mode whose overrides are applied.\n * @throws When the configuration fails validation, or when no configuration file is found.\n */\nconst loadConfig = async (cwd: string, options: LoadConfigOptions = {}): Promise<LoadedConfig> => {\n const result = await loadConfigFile<Config>({\n name: \"gtkx\",\n cwd,\n rcFile: false,\n globalRc: false,\n packageJson: false,\n context: { mode: options.mode },\n ...((options.mode !== undefined) && { envName: options.mode }),\n });\n\n const config = result.config;\n const configFile = result.configFile;\n validateConfig(config);\n\n if (configFile === undefined || !existsSync(resolve(cwd, configFile))) {\n throw new Error(`gtkx.config.ts: no configuration file was found from ${cwd}`);\n }\n\n return {\n config,\n configFile,\n root: result.cwd ?? cwd,\n };\n};\n\nconst createConfigLoader = (options: LoadConfigOptions = {}): ConfigLoader => {\n const loaded: Map<string, Promise<LoadedConfig>> = new Map();\n const resolved: Map<string, Promise<ResolvedConfig>> = new Map();\n\n const load = (cwd: string): Promise<LoadedConfig> =>\n getOrInsert(loaded, resolve(cwd), (root) => loadConfig(root, options));\n\n const resolveAt = async (root: string): Promise<ResolvedConfig> => {\n const { config, root: configRoot } = await load(root);\n\n return resolveConfig(config, configRoot);\n };\n\n return {\n load,\n resolve: (cwd: string): Promise<ResolvedConfig> => getOrInsert(resolved, resolve(cwd), resolveAt),\n };\n};\n\nexport { loadConfig, createConfigLoader, type LoadedConfig, type LoadConfigOptions, type ConfigLoader };\n"]}
package/dist/virtual.d.ts CHANGED
@@ -1,10 +1,6 @@
1
- import type { ResolvedConfig } from "./config.js";
1
+ import type { ResolvedConfig } from "./config.ts";
2
2
  declare const GTKX_CONFIG_VIRTUAL_ID = "virtual:gtkx-config";
3
3
  declare const RESOLVED_GTKX_CONFIG_VIRTUAL_ID = "\0virtual:gtkx-config";
4
- /**
5
- * The runtime element config is the app's static per-element config (currently its lazy flags) merged
6
- * with its behaviors module, so a single `elements` map is registered.
7
- */
8
4
  declare const renderConfigModule: (config: ResolvedConfig) => string;
9
5
  export { GTKX_CONFIG_VIRTUAL_ID, RESOLVED_GTKX_CONFIG_VIRTUAL_ID, renderConfigModule };
10
6
  //# sourceMappingURL=virtual.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtual.d.ts","sourceRoot":"","sources":["../src/virtual.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,QAAA,MAAM,sBAAsB,wBAAwB,CAAC;AACrD,QAAA,MAAM,+BAA+B,0BAAgC,CAAC;AAMtE;;;GAGG;AACH,QAAA,MAAM,kBAAkB,GAAI,QAAQ,cAAc,KAAG,MAoBpD,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"virtual.d.ts","sourceRoot":"","sources":["../src/virtual.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,QAAA,MAAM,sBAAsB,wBAAwB,CAAC;AACrD,QAAA,MAAM,+BAA+B,0BAAgC,CAAC;AAMtE,QAAA,MAAM,kBAAkB,GAAI,QAAQ,cAAc,KAAG,MAoBpD,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/virtual.js CHANGED
@@ -1,11 +1,7 @@
1
1
  const GTKX_CONFIG_VIRTUAL_ID = "virtual:gtkx-config";
2
2
  const RESOLVED_GTKX_CONFIG_VIRTUAL_ID = `\0${GTKX_CONFIG_VIRTUAL_ID}`;
3
3
  const METADATA_SPECIFIER = "@gtkx/jsx/metadata";
4
- const lazyElementConfig = (lazyElements) => Object.fromEntries(lazyElements.map((type) => [type, { lazy: true }]));
5
- /**
6
- * The runtime element config is the app's static per-element config (currently its lazy flags) merged
7
- * with its behaviors module, so a single `elements` map is registered.
8
- */
4
+ const lazyElementConfig = (lazyElements) => Object.fromEntries(lazyElements.map((type) => [type, { isLazy: true }]));
9
5
  const renderConfigModule = (config) => {
10
6
  const lazyJson = JSON.stringify(lazyElementConfig(config.lazyElements));
11
7
  const behaviorImports = config.elements === null
@@ -1 +1 @@
1
- {"version":3,"file":"virtual.js","sourceRoot":"","sources":["../src/virtual.ts"],"names":[],"mappings":"AAEA,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AACrD,MAAM,+BAA+B,GAAG,KAAK,sBAAsB,EAAE,CAAC;AACtE,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEhD,MAAM,iBAAiB,GAAG,CAAC,YAAsB,EAAkC,EAAE,CACjF,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAE3E;;;GAGG;AACH,MAAM,kBAAkB,GAAG,CAAC,MAAsB,EAAU,EAAE;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAExE,MAAM,eAAe,GACjB,MAAM,CAAC,QAAQ,KAAK,IAAI;QACpB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACM,6DAA6D;YAC7D,kCAAkC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG;SACvE,CAAC;IAEd,OAAO;QACH,GAAG,eAAe;QAClB,iBAAiB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,GAAG;QACtD,gCAAgC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG;QACvE,mCAAmC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG;QAC7E,MAAM,CAAC,QAAQ,KAAK,IAAI;YACpB,CAAC,CAAC,2BAA2B,QAAQ,GAAG;YACxC,CAAC,CAAC,mEAAmE,QAAQ,IAAI;KACxF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["import type { ResolvedConfig } from \"./config.js\";\n\nconst GTKX_CONFIG_VIRTUAL_ID = \"virtual:gtkx-config\";\nconst RESOLVED_GTKX_CONFIG_VIRTUAL_ID = `\\0${GTKX_CONFIG_VIRTUAL_ID}`;\nconst METADATA_SPECIFIER = \"@gtkx/jsx/metadata\";\n\nconst lazyElementConfig = (lazyElements: string[]): Record<string, { lazy: true }> =>\n Object.fromEntries(lazyElements.map((type) => [type, { lazy: true }]));\n\n/**\n * The runtime element config is the app's static per-element config (currently its lazy flags) merged\n * with its behaviors module, so a single `elements` map is registered.\n */\nconst renderConfigModule = (config: ResolvedConfig): string => {\n const lazyJson = JSON.stringify(lazyElementConfig(config.lazyElements));\n\n const behaviorImports =\n config.elements === null\n ? []\n : [\n \"import { mergeElementConfigs } from \\\"@gtkx/react/config\\\";\",\n `import __elementBehaviors from ${JSON.stringify(config.elements)};`,\n ];\n\n return [\n ...behaviorImports,\n `export * from ${JSON.stringify(METADATA_SPECIFIER)};`,\n `export const applicationId = ${JSON.stringify(config.applicationId)};`,\n `export const userEventSignals = ${JSON.stringify(config.userEventSignals)};`,\n config.elements === null\n ? `export const elements = ${lazyJson};`\n : `export const elements = mergeElementConfigs(__elementBehaviors, ${lazyJson});`,\n ].join(\"\\n\");\n};\n\nexport { GTKX_CONFIG_VIRTUAL_ID, RESOLVED_GTKX_CONFIG_VIRTUAL_ID, renderConfigModule };\n"]}
1
+ {"version":3,"file":"virtual.js","sourceRoot":"","sources":["../src/virtual.ts"],"names":[],"mappings":"AAEA,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AACrD,MAAM,+BAA+B,GAAG,KAAK,sBAAsB,EAAE,CAAC;AACtE,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEhD,MAAM,iBAAiB,GAAG,CAAC,YAAsB,EAAuC,EAAE,CACtF,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAE7E,MAAM,kBAAkB,GAAG,CAAC,MAAsB,EAAU,EAAE;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAExE,MAAM,eAAe,GACjB,MAAM,CAAC,QAAQ,KAAK,IAAI;QACpB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACM,6DAA6D;YAC7D,kCAAkC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG;SACvE,CAAC;IAEd,OAAO;QACH,GAAG,eAAe;QAClB,iBAAiB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,GAAG;QACtD,gCAAgC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG;QACvE,mCAAmC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG;QAC7E,MAAM,CAAC,QAAQ,KAAK,IAAI;YACpB,CAAC,CAAC,2BAA2B,QAAQ,GAAG;YACxC,CAAC,CAAC,mEAAmE,QAAQ,IAAI;KACxF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["import type { ResolvedConfig } from \"./config.ts\";\n\nconst GTKX_CONFIG_VIRTUAL_ID = \"virtual:gtkx-config\";\nconst RESOLVED_GTKX_CONFIG_VIRTUAL_ID = `\\0${GTKX_CONFIG_VIRTUAL_ID}`;\nconst METADATA_SPECIFIER = \"@gtkx/jsx/metadata\";\n\nconst lazyElementConfig = (lazyElements: string[]): Record<string, { isLazy: boolean }> =>\n Object.fromEntries(lazyElements.map((type) => [type, { isLazy: true }]));\n\nconst renderConfigModule = (config: ResolvedConfig): string => {\n const lazyJson = JSON.stringify(lazyElementConfig(config.lazyElements));\n\n const behaviorImports =\n config.elements === null\n ? []\n : [\n \"import { mergeElementConfigs } from \\\"@gtkx/react/config\\\";\",\n `import __elementBehaviors from ${JSON.stringify(config.elements)};`,\n ];\n\n return [\n ...behaviorImports,\n `export * from ${JSON.stringify(METADATA_SPECIFIER)};`,\n `export const applicationId = ${JSON.stringify(config.applicationId)};`,\n `export const userEventSignals = ${JSON.stringify(config.userEventSignals)};`,\n config.elements === null\n ? `export const elements = ${lazyJson};`\n : `export const elements = mergeElementConfigs(__elementBehaviors, ${lazyJson});`,\n ].join(\"\\n\");\n};\n\nexport { GTKX_CONFIG_VIRTUAL_ID, RESOLVED_GTKX_CONFIG_VIRTUAL_ID, renderConfigModule };\n"]}
@@ -1,12 +1,5 @@
1
1
  import type { Plugin, UserConfig } from "vite";
2
- import { type ConfigLoader } from "./loader.js";
3
- /**
4
- * Creates the Vite plugin that resolves and serves the `virtual:gtkx-config`
5
- * module, exposing the JSX metadata and the resolved application ID from the
6
- * project's configuration.
7
- * @param options Plugin name, an optional custom {@link ConfigLoader}, and an
8
- * optional hook returning extra Vite user config.
9
- */
2
+ import { type ConfigLoader } from "./loader.ts";
10
3
  declare const createConfigPlugin: (options: {
11
4
  name: string;
12
5
  loadConfig?: ConfigLoader;
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAsBpE;;;;;;GAMG;AACH,QAAA,MAAM,kBAAkB,GAAI,SAAS;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;CAC9C,KAAG,MAcH,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAsBpE,QAAA,MAAM,kBAAkB,GAAI,SAAS;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;CAC9C,KAAG,MAcH,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -5,15 +5,8 @@ const loadVirtualModule = async (id, loadConfig, state) => {
5
5
  if (id !== RESOLVED_GTKX_CONFIG_VIRTUAL_ID) {
6
6
  return undefined;
7
7
  }
8
- return renderConfigModule(await loadConfig(state.root ?? process.cwd()));
8
+ return renderConfigModule(await loadConfig.resolve(state.root ?? process.cwd()));
9
9
  };
10
- /**
11
- * Creates the Vite plugin that resolves and serves the `virtual:gtkx-config`
12
- * module, exposing the JSX metadata and the resolved application ID from the
13
- * project's configuration.
14
- * @param options Plugin name, an optional custom {@link ConfigLoader}, and an
15
- * optional hook returning extra Vite user config.
16
- */
17
10
  const createConfigPlugin = (options) => {
18
11
  const loadConfig = options.loadConfig ?? createConfigLoader();
19
12
  const state = { root: undefined };
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAM3G,MAAM,gBAAgB,GAAG,CAAC,EAAU,EAAiB,EAAE,CACnD,EAAE,KAAK,sBAAsB,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,IAAI,CAAC;AAE3E,MAAM,iBAAiB,GAAG,KAAK,EAC3B,EAAU,EACV,UAAwB,EACxB,KAAkB,EACS,EAAE;IAC7B,IAAI,EAAE,KAAK,+BAA+B,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,OAAO,kBAAkB,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,CAAC,OAI3B,EAAU,EAAE;IACT,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,EAAE,CAAC;IAC9D,MAAM,KAAK,GAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAE/C,OAAO;QACH,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,CAAC,MAAkB;YACrB,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;YAEvC,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9B,CAAC;QACD,SAAS,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC/C,IAAI,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC;KACjE,CAAC;AACN,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC","sourcesContent":["import type { Plugin, UserConfig } from \"vite\";\nimport { type ConfigLoader, createConfigLoader } from \"./loader.js\";\nimport { GTKX_CONFIG_VIRTUAL_ID, renderConfigModule, RESOLVED_GTKX_CONFIG_VIRTUAL_ID } from \"./virtual.js\";\n\ntype PluginState = {\n root: string | undefined;\n};\n\nconst resolveVirtualId = (id: string): string | null =>\n id === GTKX_CONFIG_VIRTUAL_ID ? RESOLVED_GTKX_CONFIG_VIRTUAL_ID : null;\n\nconst loadVirtualModule = async (\n id: string,\n loadConfig: ConfigLoader,\n state: PluginState,\n): Promise<string | undefined> => {\n if (id !== RESOLVED_GTKX_CONFIG_VIRTUAL_ID) {\n return undefined;\n }\n\n return renderConfigModule(await loadConfig(state.root ?? process.cwd()));\n};\n\n/**\n * Creates the Vite plugin that resolves and serves the `virtual:gtkx-config`\n * module, exposing the JSX metadata and the resolved application ID from the\n * project's configuration.\n * @param options Plugin name, an optional custom {@link ConfigLoader}, and an\n * optional hook returning extra Vite user config.\n */\nconst createConfigPlugin = (options: {\n name: string;\n loadConfig?: ConfigLoader;\n config?: () => Omit<UserConfig, \"plugins\">;\n}): Plugin => {\n const loadConfig = options.loadConfig ?? createConfigLoader();\n const state: PluginState = { root: undefined };\n\n return {\n name: options.name,\n config(config: UserConfig) {\n state.root = config.root ?? state.root;\n\n return options.config?.();\n },\n resolveId: (id: string) => resolveVirtualId(id),\n load: (id: string) => loadVirtualModule(id, loadConfig, state),\n };\n};\n\nexport default createConfigPlugin;\n"]}
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAM3G,MAAM,gBAAgB,GAAG,CAAC,EAAU,EAAiB,EAAE,CACnD,EAAE,KAAK,sBAAsB,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,IAAI,CAAC;AAE3E,MAAM,iBAAiB,GAAG,KAAK,EAC3B,EAAU,EACV,UAAwB,EACxB,KAAkB,EACS,EAAE;IAC7B,IAAI,EAAE,KAAK,+BAA+B,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,OAAO,kBAAkB,CAAC,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACrF,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,OAI3B,EAAU,EAAE;IACT,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,EAAE,CAAC;IAC9D,MAAM,KAAK,GAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAE/C,OAAO;QACH,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,CAAC,MAAkB;YACrB,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;YAEvC,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9B,CAAC;QACD,SAAS,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC/C,IAAI,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC;KACjE,CAAC;AACN,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC","sourcesContent":["import type { Plugin, UserConfig } from \"vite\";\nimport { type ConfigLoader, createConfigLoader } from \"./loader.ts\";\nimport { GTKX_CONFIG_VIRTUAL_ID, renderConfigModule, RESOLVED_GTKX_CONFIG_VIRTUAL_ID } from \"./virtual.ts\";\n\ntype PluginState = {\n root: string | undefined;\n};\n\nconst resolveVirtualId = (id: string): string | null =>\n id === GTKX_CONFIG_VIRTUAL_ID ? RESOLVED_GTKX_CONFIG_VIRTUAL_ID : null;\n\nconst loadVirtualModule = async (\n id: string,\n loadConfig: ConfigLoader,\n state: PluginState,\n): Promise<string | undefined> => {\n if (id !== RESOLVED_GTKX_CONFIG_VIRTUAL_ID) {\n return undefined;\n }\n\n return renderConfigModule(await loadConfig.resolve(state.root ?? process.cwd()));\n};\n\nconst createConfigPlugin = (options: {\n name: string;\n loadConfig?: ConfigLoader;\n config?: () => Omit<UserConfig, \"plugins\">;\n}): Plugin => {\n const loadConfig = options.loadConfig ?? createConfigLoader();\n const state: PluginState = { root: undefined };\n\n return {\n name: options.name,\n config(config: UserConfig) {\n state.root = config.root ?? state.root;\n\n return options.config?.();\n },\n resolveId: (id: string) => resolveVirtualId(id),\n load: (id: string) => loadVirtualModule(id, loadConfig, state),\n };\n};\n\nexport default createConfigPlugin;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/config",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.4",
4
4
  "description": "gtkx.config.ts schema, loader, and the element-prop mapping language shared across the GTKX toolchain",
5
5
  "keywords": [
6
6
  "gtkx",
@@ -50,8 +50,9 @@
50
50
  "dependencies": {
51
51
  "c12": "^3.3.4",
52
52
  "defu": "^6.1.7",
53
- "vite": "^8.1.5",
54
- "zod": "^4.4.3"
53
+ "vite": "^8.2.0",
54
+ "zod": "^4.4.3",
55
+ "@gtkx/utils": "1.0.0-rc.4"
55
56
  },
56
57
  "scripts": {
57
58
  "release": "tsx ../../scripts/release-package.ts"
package/src/config.ts CHANGED
@@ -2,46 +2,53 @@ import { createDefineConfig, type DefineConfig } from "c12";
2
2
  import { defu } from "defu";
3
3
  import { resolve } from "node:path";
4
4
  import { z } from "zod";
5
- import { configError, isRecord, rawIssue } from "./config-error.js";
6
- import { resolveUserEventSignals } from "./user-event-signals.js";
5
+ import { configError, isRecord, rawIssue } from "./config-error.ts";
6
+ import { resolveUserEventSignals } from "./user-event-signals.ts";
7
7
 
8
+ /** Accepted `reactCompiler.compilationMode` values, choosing which functions the compiler processes. */
8
9
  type ReactCompilerCompilationMode = (typeof COMPILATION_MODES)[number];
10
+ /** Accepted `reactCompiler.panicThreshold` values, choosing which compiler diagnostics fail the build. */
9
11
  type ReactCompilerPanicThreshold = (typeof PANIC_THRESHOLDS)[number];
10
12
 
13
+ /** Object form of the `reactCompiler` config key, forwarded as-is to `babel-plugin-react-compiler`. */
11
14
  type ReactCompilerOptions = {
15
+ /** Which functions the compiler processes; left to the compiler's own default when omitted. */
12
16
  compilationMode?: ReactCompilerCompilationMode;
17
+ /** Which compiler diagnostics fail the build; left to the compiler's own default when omitted. */
13
18
  panicThreshold?: ReactCompilerPanicThreshold;
14
19
  };
15
20
 
16
21
  /**
17
- * React Compiler options resolved for the build, with the compilation target
18
- * fixed to React 19.
22
+ * The React Compiler options the build hands to `babel-plugin-react-compiler`, with the React version
23
+ * GTKX targets filled in.
19
24
  */
20
25
  type ResolvedReactCompilerOptions = ReactCompilerOptions & {
26
+ /** React major version the compiler emits for. */
21
27
  target: "19";
22
28
  };
23
29
 
24
30
  /**
25
- * User-facing configuration for a GTKX project, as authored in `gtkx.config.ts`:
26
- * the GIR libraries to bind, extra `.gir` search paths, the GApplication id,
27
- * a module of per-element configuration (lazy flags and custom behaviors),
28
- * per-element component wrappers keyed by GLib type name, the React Compiler and
29
- * codegen settings, and additional user event signals to suppress during React
30
- * commits.
31
+ * User-facing configuration for a GTKX project, as authored in `gtkx.config.ts`: the GIR libraries
32
+ * to bind and where to find them, the GApplication id, per-element configuration, and the React
33
+ * Compiler, codegen, and user event signal settings.
31
34
  */
32
35
  type Config = z.infer<typeof configSchema>;
36
+ /** A named export referenced as plain data, so codegen can emit an import for it without loading the module. */
37
+ type ModuleExport = z.infer<typeof moduleExportSchema>;
38
+ /** One `elements.config` entry, describing how a single GLib type's generated element is shaped. */
39
+ type ElementConfigEntry = z.infer<typeof elementConfigSchema>;
33
40
 
34
- /**
35
- * Configuration reduced to the values needed at runtime: the GApplication
36
- * identifier, the resolved React Compiler options (`null` when disabled), the
37
- * user event signals suppressed while a React commit is in progress, and the
38
- * module path holding per-element configuration (`null` when unset).
39
- */
41
+ /** Configuration reduced to the values the app runtime and the build need, with paths already resolved. */
40
42
  type ResolvedConfig = {
43
+ /** The GApplication identifier the app registers under. */
41
44
  applicationId: string;
45
+ /** React Compiler options for the build, or `null` when the compiler is disabled. */
42
46
  reactCompiler: ResolvedReactCompilerOptions | null;
47
+ /** Signal names, keyed by GLib type name, that stay suppressed while a React commit runs. */
43
48
  userEventSignals: Record<string, string[]>;
49
+ /** Path of the module exporting per-element configs, or `null` when none is configured. */
44
50
  elements: string | null;
51
+ /** GLib type names of the elements marked `isLazy`, whose GObject their parent container creates. */
45
52
  lazyElements: string[];
46
53
  };
47
54
 
@@ -55,6 +62,7 @@ const REACT_COMPILER_TARGET = "19";
55
62
  const COMPILATION_MODE_SET: Set<string> = new Set(COMPILATION_MODES);
56
63
  const PANIC_THRESHOLD_SET: Set<string> = new Set(PANIC_THRESHOLDS);
57
64
 
65
+ /** Validates `libraries`: the `"*"` wildcard on its own, or a non-empty array of `Name-Version` identifiers. */
58
66
  const librariesSchema = z.custom<typeof LIBRARIES_WILDCARD | string[]>().check((ctx) => {
59
67
  const value = ctx.value;
60
68
 
@@ -73,6 +81,7 @@ const librariesSchema = z.custom<typeof LIBRARIES_WILDCARD | string[]>().check((
73
81
  }
74
82
  });
75
83
 
84
+ /** Validates `applicationId` against the reverse-DNS form `g_application_id_is_valid` accepts. */
76
85
  const applicationIdSchema = z.custom<string>().check((ctx) => {
77
86
  const value = ctx.value;
78
87
 
@@ -89,6 +98,7 @@ const applicationIdSchema = z.custom<string>().check((ctx) => {
89
98
  }
90
99
  });
91
100
 
101
+ /** Validates `reactCompiler`: a boolean, or an options object whose mode and threshold are known values. */
92
102
  const reactCompilerSchema = z.custom<boolean | ReactCompilerOptions>().check((ctx) => {
93
103
  const value = ctx.value;
94
104
 
@@ -131,6 +141,7 @@ const reactCompilerSchema = z.custom<boolean | ReactCompilerOptions>().check((ct
131
141
  }
132
142
  });
133
143
 
144
+ /** Validates `userEventSignals`: GLib type names mapped to arrays of non-empty signal names. */
134
145
  const userEventSignalsSchema = z.record(
135
146
  z.string(),
136
147
  z.array(
@@ -142,41 +153,77 @@ const userEventSignalsSchema = z.record(
142
153
  { error: "must be a record of GLib type names to signal name arrays" },
143
154
  );
144
155
 
156
+ /** Validates a `{ module, export }` reference to a named export. */
145
157
  const moduleExportSchema = z.object(
146
158
  {
159
+ /** Specifier the export is imported from. */
147
160
  module: z.string({ error: "must be a module specifier" }).min(1, { error: "must be a module specifier" }),
161
+ /** Identifier the module exports it under. */
148
162
  export: z.string({ error: "must be an export name" }).min(1, { error: "must be an export name" }),
149
163
  },
150
164
  { error: "must be a { module, export } object" },
151
165
  );
152
166
 
167
+ /** Validates one entry of `elements.config`. */
153
168
  const elementConfigSchema = z.object({
169
+ /** Component that wraps the generated element. */
154
170
  component: moduleExportSchema.optional(),
171
+ /** Base props interface the generated element props extend. */
155
172
  props: moduleExportSchema.optional(),
156
- lazy: z.boolean({ error: "must be a boolean" }).optional(),
173
+ /** Marks the element as having no GObject of its own, its parent container creating one instead. */
174
+ isLazy: z.boolean({ error: "must be a boolean" }).optional(),
175
+ /** GObject properties to leave out of the generated element props, such as those a behavior fills. */
176
+ omittedProps: z
177
+ .array(
178
+ z.string({ error: "must be a non-empty property name" }).min(1, {
179
+ error: "must be a non-empty property name",
180
+ }),
181
+ { error: "must be an array of property names" },
182
+ )
183
+ .optional(),
157
184
  });
158
185
 
186
+ /** Validates `elements`: the behaviors module the app loads at runtime, plus the per-type entries. */
159
187
  const elementsSchema = z.object({
188
+ /**
189
+ * Path, resolved against the project root, of a module default-exporting element configs keyed by
190
+ * GLib type name, as `defineElements` returns.
191
+ */
160
192
  behaviors: z
161
193
  .string({ error: "must be a path to a module exporting element behaviors" })
162
194
  .min(1, { error: "must be a path to a module exporting element behaviors" })
163
195
  .optional(),
196
+ /** Per-element entries, keyed by GLib type name. */
164
197
  config: z.record(z.string(), elementConfigSchema).optional(),
165
198
  });
166
199
 
200
+ /** The shape every `gtkx.config.ts` is validated against, and the source of the {@link Config} type. */
167
201
  const configSchema = z.object({
202
+ /**
203
+ * GIR namespaces to bind as `Name-Version`, or `"*"` for every one installed; omitting it gives
204
+ * `Gtk-4.0`, which is also prepended to any list that names no Gtk version of its own.
205
+ */
168
206
  libraries: librariesSchema.optional(),
207
+ /** Directories searched for `.gir` files ahead of `GTKX_GIR_PATH` and the system locations. */
169
208
  girPath: z.array(z.string(), { error: "must be an array of strings if provided" }).optional(),
209
+ /** Reverse-DNS GApplication identifier, and the default `applicationId` of the application element. */
170
210
  applicationId: applicationIdSchema,
211
+ /** React Compiler settings; the compiler runs unless this is `false`. */
171
212
  reactCompiler: reactCompilerSchema.optional(),
213
+ /** Whether to generate the binding stores; `false` makes the CLI resolve an already-installed one. */
172
214
  codegen: z.boolean({ error: "must be a boolean" }).optional(),
215
+ /**
216
+ * Signal names, keyed by GLib type name, that stay suppressed while a React commit applies props, so
217
+ * their handlers only ever report user interaction. Unioned with the built-in GTK4 and Adwaita table.
218
+ */
173
219
  userEventSignals: userEventSignalsSchema.optional(),
220
+ /** Per-element customization: where the behaviors module lives and how each type's element is shaped. */
174
221
  elements: elementsSchema.optional(),
175
222
  });
176
223
 
177
224
  /**
178
- * Identity helper that returns the given configuration typed as {@link Config},
179
- * enabling editor autocompletion and type checking in `gtkx.config.ts`.
225
+ * Returns the given configuration unchanged, typed as {@link Config}, so `gtkx.config.ts` gets
226
+ * autocompletion and type checking.
180
227
  */
181
228
  const defineConfig: DefineConfig<Config> = createDefineConfig<Config>();
182
229
 
@@ -230,9 +277,8 @@ const validateConfig = (config: Config): void => {
230
277
  };
231
278
 
232
279
  /**
233
- * Deep-merges two configurations, with `override` taking precedence over `base`.
234
- * @param base The lower-priority configuration.
235
- * @param override The higher-priority configuration whose values win on conflict.
280
+ * Deep-merges two configurations. `override` wins over `base` on conflicting scalar and object keys, while
281
+ * arrays are concatenated with the `override` entries first.
236
282
  */
237
283
  const mergeConfig = (base: Config, override: Config): Config => defu(override, base);
238
284
 
@@ -246,9 +292,30 @@ const resolveElementsModule = (behaviors: string | undefined, root: string | und
246
292
 
247
293
  const resolveLazyElements = (elements: Config["elements"]): string[] =>
248
294
  Object.entries(elements?.config ?? {})
249
- .filter(([, entry]) => entry.lazy === true)
295
+ .filter(([, entry]) => entry.isLazy === true)
250
296
  .map(([type]) => type);
251
297
 
298
+ const elementEntryValues = <T>(
299
+ elements: Config["elements"],
300
+ pick: (entry: ElementConfigEntry) => T | undefined,
301
+ ): Record<string, T> =>
302
+ Object.fromEntries(
303
+ Object.entries(elements?.config ?? {}).flatMap(([type, entry]) => {
304
+ const value = pick(entry);
305
+
306
+ return value === undefined ? [] : [[type, value] as const];
307
+ }),
308
+ );
309
+
310
+ const resolveElementComponents = (elements: Config["elements"]): Record<string, ModuleExport> =>
311
+ elementEntryValues(elements, (entry) => entry.component);
312
+
313
+ const resolveElementProps = (elements: Config["elements"]): Record<string, ModuleExport> =>
314
+ elementEntryValues(elements, (entry) => entry.props);
315
+
316
+ const resolveOmittedProps = (elements: Config["elements"]): Record<string, string[]> =>
317
+ elementEntryValues(elements, (entry) => entry.omittedProps);
318
+
252
319
  const resolveConfig = (config: Config, root?: string): ResolvedConfig => ({
253
320
  applicationId: config.applicationId,
254
321
  reactCompiler: resolveReactCompilerOptions(config.reactCompiler),
@@ -258,16 +325,18 @@ const resolveConfig = (config: Config, root?: string): ResolvedConfig => ({
258
325
  });
259
326
 
260
327
  export {
261
- LIBRARIES_WILDCARD,
262
- GIR_LIBRARY_PATTERN,
263
328
  defineConfig,
264
329
  isValidApplicationId,
265
330
  resolveReactCompilerOptions,
266
331
  validateConfig,
267
332
  mergeConfig,
268
333
  resolveLazyElements,
334
+ resolveElementComponents,
335
+ resolveElementProps,
336
+ resolveOmittedProps,
269
337
  resolveConfig,
270
338
  type ResolvedReactCompilerOptions,
271
339
  type Config,
340
+ type ModuleExport,
272
341
  type ResolvedConfig,
273
342
  };
package/src/index.ts CHANGED
@@ -1,8 +1,4 @@
1
- export {
2
- type Config,
3
- defineConfig,
4
- mergeConfig,
5
- type ResolvedConfig,
6
- type ResolvedReactCompilerOptions,
7
- } from "./config.js";
8
- export { type LoadedConfig, loadConfig } from "./loader.js";
1
+ /** @public */
2
+ export { type Config, defineConfig, mergeConfig, type ResolvedConfig } from "./config.ts";
3
+ /** @public */
4
+ export { type LoadedConfig, loadConfig } from "./loader.ts";
package/src/internal.ts CHANGED
@@ -1,2 +1,9 @@
1
- export { GIR_LIBRARY_PATTERN, isValidApplicationId, LIBRARIES_WILDCARD, resolveLazyElements } from "./config.js";
2
- export { type ConfigLoader, createConfigLoader } from "./loader.js";
1
+ export type { ResolvedReactCompilerOptions } from "./config.ts";
2
+ export {
3
+ isValidApplicationId,
4
+ resolveElementComponents,
5
+ resolveElementProps,
6
+ resolveLazyElements,
7
+ resolveOmittedProps,
8
+ } from "./config.ts";
9
+ export { type ConfigLoader, createConfigLoader } from "./loader.ts";
package/src/loader.ts CHANGED
@@ -1,30 +1,38 @@
1
+ import { getOrInsert } from "@gtkx/utils";
1
2
  import { loadConfig as loadConfigFile } from "c12";
2
3
  import { existsSync } from "node:fs";
3
4
  import { resolve } from "node:path";
4
- import { type Config, resolveConfig, type ResolvedConfig, validateConfig } from "./config.js";
5
+ import { type Config, resolveConfig, type ResolvedConfig, validateConfig } from "./config.ts";
5
6
 
6
- /**
7
- * Result of loading a `gtkx.config.ts` file: the parsed configuration, the
8
- * resolved config file path (`undefined` when none was found), and the project
9
- * root it was loaded from.
10
- */
7
+ /** Result of loading a project's `gtkx.config.ts` file. */
11
8
  type LoadedConfig = {
9
+ /** The parsed and validated configuration. */
12
10
  config: Config;
13
- configFile: string | undefined;
11
+ /** Absolute path of the configuration file that was loaded. */
12
+ configFile: string;
13
+ /** Absolute path of the project root the configuration was resolved against. */
14
14
  root: string;
15
15
  };
16
16
 
17
+ /** How a configuration file is read, shared by {@link loadConfig} and `createConfigLoader`. */
17
18
  type LoadConfigOptions = {
19
+ /**
20
+ * Environment name such as `development`: the config's matching `$<mode>` block is layered over the
21
+ * top-level values, and the name is passed to a config authored as a function.
22
+ */
18
23
  mode?: string | undefined;
19
24
  };
20
25
 
21
- type ConfigLoader = (cwd: string) => Promise<ResolvedConfig>;
26
+ type ConfigLoader = {
27
+ load: (cwd: string) => Promise<LoadedConfig>;
28
+ resolve: (cwd: string) => Promise<ResolvedConfig>;
29
+ };
22
30
 
23
31
  /**
24
- * Loads and validates the `gtkx.config.ts` file for a project, returning the
25
- * parsed configuration together with the config file path and project root.
26
- * @param cwd Directory from which to search for the configuration file.
27
- * @param options Loading options, such as the environment mode.
32
+ * Loads and validates the `gtkx.config.ts` file for a project.
33
+ * @param cwd Directory the configuration file is looked up in.
34
+ * @param options Loading options, such as the environment mode whose overrides are applied.
35
+ * @throws When the configuration fails validation, or when no configuration file is found.
28
36
  */
29
37
  const loadConfig = async (cwd: string, options: LoadConfigOptions = {}): Promise<LoadedConfig> => {
30
38
  const result = await loadConfigFile<Config>({
@@ -38,39 +46,36 @@ const loadConfig = async (cwd: string, options: LoadConfigOptions = {}): Promise
38
46
  });
39
47
 
40
48
  const config = result.config;
41
- const isFound = result.configFile !== undefined && existsSync(resolve(cwd, result.configFile));
49
+ const configFile = result.configFile;
50
+ validateConfig(config);
42
51
 
43
- if (isFound) {
44
- validateConfig(config);
52
+ if (configFile === undefined || !existsSync(resolve(cwd, configFile))) {
53
+ throw new Error(`gtkx.config.ts: no configuration file was found from ${cwd}`);
45
54
  }
46
55
 
47
56
  return {
48
57
  config,
49
- configFile: isFound ? result.configFile : undefined,
58
+ configFile,
50
59
  root: result.cwd ?? cwd,
51
60
  };
52
61
  };
53
62
 
54
63
  const createConfigLoader = (options: LoadConfigOptions = {}): ConfigLoader => {
55
- const cache: Map<string, Promise<ResolvedConfig>> = new Map();
64
+ const loaded: Map<string, Promise<LoadedConfig>> = new Map();
65
+ const resolved: Map<string, Promise<ResolvedConfig>> = new Map();
56
66
 
57
- const loadResolved = async (root: string): Promise<ResolvedConfig> => {
58
- const { config } = await loadConfig(root, options);
59
- validateConfig(config);
67
+ const load = (cwd: string): Promise<LoadedConfig> =>
68
+ getOrInsert(loaded, resolve(cwd), (root) => loadConfig(root, options));
60
69
 
61
- return resolveConfig(config, root);
62
- };
70
+ const resolveAt = async (root: string): Promise<ResolvedConfig> => {
71
+ const { config, root: configRoot } = await load(root);
63
72
 
64
- return (cwd: string): Promise<ResolvedConfig> => {
65
- const root = resolve(cwd);
66
- let pending = cache.get(root);
67
-
68
- if (!pending) {
69
- pending = loadResolved(root);
70
- cache.set(root, pending);
71
- }
73
+ return resolveConfig(config, configRoot);
74
+ };
72
75
 
73
- return pending;
76
+ return {
77
+ load,
78
+ resolve: (cwd: string): Promise<ResolvedConfig> => getOrInsert(resolved, resolve(cwd), resolveAt),
74
79
  };
75
80
  };
76
81
 
package/src/virtual.ts CHANGED
@@ -1,16 +1,12 @@
1
- import type { ResolvedConfig } from "./config.js";
1
+ import type { ResolvedConfig } from "./config.ts";
2
2
 
3
3
  const GTKX_CONFIG_VIRTUAL_ID = "virtual:gtkx-config";
4
4
  const RESOLVED_GTKX_CONFIG_VIRTUAL_ID = `\0${GTKX_CONFIG_VIRTUAL_ID}`;
5
5
  const METADATA_SPECIFIER = "@gtkx/jsx/metadata";
6
6
 
7
- const lazyElementConfig = (lazyElements: string[]): Record<string, { lazy: true }> =>
8
- Object.fromEntries(lazyElements.map((type) => [type, { lazy: true }]));
7
+ const lazyElementConfig = (lazyElements: string[]): Record<string, { isLazy: boolean }> =>
8
+ Object.fromEntries(lazyElements.map((type) => [type, { isLazy: true }]));
9
9
 
10
- /**
11
- * The runtime element config is the app's static per-element config (currently its lazy flags) merged
12
- * with its behaviors module, so a single `elements` map is registered.
13
- */
14
10
  const renderConfigModule = (config: ResolvedConfig): string => {
15
11
  const lazyJson = JSON.stringify(lazyElementConfig(config.lazyElements));
16
12
 
@@ -1,6 +1,6 @@
1
1
  import type { Plugin, UserConfig } from "vite";
2
- import { type ConfigLoader, createConfigLoader } from "./loader.js";
3
- import { GTKX_CONFIG_VIRTUAL_ID, renderConfigModule, RESOLVED_GTKX_CONFIG_VIRTUAL_ID } from "./virtual.js";
2
+ import { type ConfigLoader, createConfigLoader } from "./loader.ts";
3
+ import { GTKX_CONFIG_VIRTUAL_ID, renderConfigModule, RESOLVED_GTKX_CONFIG_VIRTUAL_ID } from "./virtual.ts";
4
4
 
5
5
  type PluginState = {
6
6
  root: string | undefined;
@@ -18,16 +18,9 @@ const loadVirtualModule = async (
18
18
  return undefined;
19
19
  }
20
20
 
21
- return renderConfigModule(await loadConfig(state.root ?? process.cwd()));
21
+ return renderConfigModule(await loadConfig.resolve(state.root ?? process.cwd()));
22
22
  };
23
23
 
24
- /**
25
- * Creates the Vite plugin that resolves and serves the `virtual:gtkx-config`
26
- * module, exposing the JSX metadata and the resolved application ID from the
27
- * project's configuration.
28
- * @param options Plugin name, an optional custom {@link ConfigLoader}, and an
29
- * optional hook returning extra Vite user config.
30
- */
31
24
  const createConfigPlugin = (options: {
32
25
  name: string;
33
26
  loadConfig?: ConfigLoader;