📜/skills/fuz-stack/references/code-generation
  • docs
  • skills
  • fuz-stack
    • Async Patterns
    • Code Generation
    • Common Utilities
    • CSS Patterns
    • Dependency Injection
    • Documentation System
    • Rust Conventions for the Fuz Ecosystem
    • Svelte 5 Patterns
    • Task Patterns
    • Testing Patterns
    • TSDoc Comment Style Guide
    • Type Utilities
    • WASM Patterns for the Fuz Ecosystem
    • Zod Schemas
  • grimoire
  • tools
  • hash

Code Generation

Gro's code generation system (.gen.* files) in @fuzdev/gro.

Gen files produce source code at build time. Discovered by the .gen. pattern in filenames, executed by gro gen, output committed alongside source. gro gen --check verifies no drift.

File Naming

Output file is produced by dropping the .gen. segment:

| Gen file | Output file | | ------------------------------------ | -------------------------- | | library.gen.ts | library.ts | | fuz.gen.css.ts | fuz.css | | theme.gen.css.ts | theme.css | | css_classes_fixture.gen.json.ts | css_classes_fixture.json | | README.gen.md.ts | README.md | | auth_attack_surface.gen.json.ts | auth_attack_surface.json |

The gen file always has a .ts (or .js) extension. An optional extension between .gen. and .ts overrides the output extension.

Naming rules

- Exactly one .gen. segment per filename (duplicates are invalid) - At most one additional extension after .gen. (e.g., .gen.css.ts is valid, .gen.foo.bar.ts is not) - Output filename cannot equal the gen filename

Gen Types

A gen file exports a gen value — either a function or a config object:

type Gen = GenFunction | GenConfig;

Both importable from @fuzdev/gro or @fuzdev/gro/gen.js.

GenFunction (simple form)

type GenFunction = (ctx: GenContext) => RawGenResult | Promise<RawGenResult>;// theme.gen.css.ts — simple form import type {Gen} from '@fuzdev/gro'; export const gen: Gen = ({origin_path}) => { const banner = `/* generated by ${origin_path} */`; return `${banner}\n:root { --my-var: 1; }\n`; };

GenConfig (with dependencies)

interface GenConfig { generate: GenFunction; dependencies?: GenDependencies; }// highlight_priorities.gen.ts — config form with dependencies import type {Gen} from '@fuzdev/gro'; export const gen: Gen = { generate: ({origin_path}) => { return `// generated by ${origin_path}\nexport const data = {};\n`; }, dependencies: {files: ['src/lib/theme_highlight.css']}, };

GenContext

| Property | Type | Description | | ----------------- | ----------------------- | --------------------------------------------------- | | origin_id | PathId | absolute path of the gen file | | origin_path | string | origin_id relative to the project root | | config | GroConfig | the project's Gro configuration | | svelte_config | ParsedSvelteConfig | parsed svelte.config.js | | filer | Filer | filesystem tracker (file contents, dependency graph) | | log | Logger | scoped logger | | timings | Timings | performance measurement | | invoke_task | InvokeTask | invoke other Gro tasks | | changed_file_id | PathId \| undefined | set during dependency resolution; undefined during generation |

Most commonly used: origin_path (generated-by banners), log, and filer (reading source files).

Return Values

type RawGenResult = string | RawGenFile | null | Array<RawGenResult>;

String — single file with default name

export const gen: Gen = () => { return '// generated content\n'; }; // theme.gen.css.ts → writes theme.css

RawGenFile — single file with options

interface RawGenFile { content: string; filename?: string; // override output name (can be relative or absolute path) format?: boolean; // run Prettier (default: true) }export const gen: Gen = () => { return {content: '{"key": "value"}', filename: 'data.json', format: false}; };

Relative filename resolves from the gen file's directory. Absolute paths write to that exact location (e.g., blog.gen.ts writes static/blog/feed.xml).

null — skip generation

export const gen: Gen = (ctx) => { if (some_condition) return null; // produce no output return 'content'; };

Array — multiple output files

Nested arrays are flattened:

export const gen: Gen = () => { return [ {content: 'export const A = 1;', filename: 'a.ts'}, {content: 'export const B = 2;', filename: 'b.ts'}, ]; };

Duplicate output file IDs within a single gen file are invalid.

A single gen file can produce many output files — e.g., skill_docs.gen.ts generates a manifest, per-skill data files, and per-page +page.svelte routes.

Dependencies

Control when a gen file re-runs during watch mode. Without dependencies, re-runs only when the gen file or its imports change (tracked by filer). Use GenConfig for broader triggers:

type GenDependencies = 'all' | GenDependenciesConfig | GenDependenciesResolver;

'all' — re-run on any change

Used by library_gen since it analyzes all source files:

export const gen: Gen = { generate: async (ctx) => { /* ... */ }, dependencies: 'all', };

Config — patterns and files

export const gen: Gen = { generate: ({origin_path}) => { /* ... */ }, dependencies: { patterns: [/\.svelte$/, /\.ts$/], files: ['src/lib/theme_highlight.css'], }, };

patterns are tested against absolute paths. files can be relative (resolved to absolute) or absolute.

Function — dynamic resolution

Receives GenContext and returns a config, 'all', or null. changed_file_id is set on context during dependency resolution:

type GenDependenciesResolver = ( ctx: GenContext, ) => GenDependenciesConfig | 'all' | null | Promise<GenDependenciesConfig | 'all' | null>;

CLI Usage

gro gen # run all gen files in src/ gro gen src/lib/ # run gen files in a specific directory gro gen src/lib/foo.gen.ts # run a specific gen file gro gen --check # verify no drift (used by gro check and CI)

| Arg | Default | Description | | ------------ | --------------- | ------------------------------------------------- | | _ | ['src'] | input paths (files or directories to scan) | | --root_dirs| [process.cwd()] | root directories to resolve input paths against | | --check | false | exit nonzero if any generated files have changed |

gro gen --check compares generated output against existing files. If any file is new or changed, it fails with a message to run gro gen. Called by gro check as part of CI.

Common Patterns

CSS generation

Every project with fuz_css has a fuz.gen.css.ts (typically in src/routes/):

import {gen_fuz_css} from '@fuzdev/fuz_css/gen_fuz_css.js'; export const gen = gen_fuz_css();

Returns a GenConfig that scans source files, extracts CSS class usage via AST, and generates a bundled fuz.css with only the classes, base styles, and theme variables actually used. Accepts GenFuzCssOptions for customization.

Theme CSS generation

fuz_css uses theme.gen.css.ts to generate the full base theme:

import type {Gen} from '@fuzdev/gro'; import {default_themes} from './themes.js'; import {render_theme_style} from './theme.js'; export const gen: Gen = ({origin_path}) => { const banner = `/* generated by ${origin_path} */`; const theme = default_themes[0]!; const theme_style = render_theme_style(theme, { comments: true, empty_default_theme: false, specificity: 1, }); return `${banner}\n${theme_style}\n`; };

Library metadata

Every project uses library.gen.ts (typically in src/routes/) for API documentation metadata. Analyzes TypeScript and Svelte source files and produces library.ts and library.json:

import {library_gen} from '@fuzdev/fuz_ui/library_gen.js'; import {library_throw_on_duplicates} from '@fuzdev/fuz_ui/library_generate.js'; export const gen = library_gen({on_duplicates: library_throw_on_duplicates});

Returns a GenConfig with dependencies: 'all' (re-runs on any source change). library_throw_on_duplicates enforces the flat namespace convention by throwing on duplicate export names across modules.

Blog feed generation

fuz_blog provides blog.gen.ts for Atom feeds, feed data, and slug routes:

export * from '@fuzdev/fuz_blog/blog.gen.js';

Consumer projects re-export the gen. Returns an array with feed.xml (at an absolute path in static/), feed.ts, and one +page.svelte per slug route.

Fixture generation

Test fixtures can use gen files for snapshot data:

import type {Gen} from '@fuzdev/gro'; import {create_tx_app_surface_spec} from './auth_attack_surface_helpers.js'; export const gen: Gen = () => { return JSON.stringify(create_tx_app_surface_spec().surface); }; // auth_attack_surface.gen.json.ts → auth_attack_surface.json

Action codegen (zzz)

Gen files can generate TypeScript types from runtime registries. zzz reads action specs and produces typed collections, metatypes, and handler interfaces:

import type {Gen} from '@fuzdev/gro/gen.js'; import * as action_specs from './action_specs.js'; import {is_action_spec} from './action_spec.js'; import {ActionRegistry} from './action_registry.js'; export const gen: Gen = ({origin_path}) => { const registry = new ActionRegistry( Object.values(action_specs).filter((s) => is_action_spec(s)), ); return ` // generated by ${origin_path} export const ActionMethods = [ ${registry.methods.map((m) => `'${m}'`).join(',\n')} ] as const; `; };

Multi-file route generation

A single gen file can generate entire route trees. skill_docs.gen.ts auto-discovers skills and generates manifests, data files, and +page.svelte routes:

import type {Gen} from '@fuzdev/gro/gen.js'; export const gen: Gen = ({origin_path}) => { // ... discover skills, read markdown ... return [ {content: manifest_content, filename: 'skills_manifest.ts'}, {content: skill_data, filename: join(skill_route_dir, 'skill_data.ts')}, {content: page_content, filename: join(skill_route_dir, '+page.svelte')}, // ... more files ]; };

Quick Reference

| Export | Type | Source | Purpose | | ---------------------- | --------- | --------------------- | ------------------------------------------------ | | Gen | Type | @fuzdev/gro/gen.js | GenFunction or GenConfig | | GenFunction | Type | @fuzdev/gro/gen.js | (ctx: GenContext) => RawGenResult | | GenConfig | Interface | @fuzdev/gro/gen.js | generate + optional dependencies | | GenContext | Interface | @fuzdev/gro/gen.js | context passed to gen functions | | RawGenResult | Type | @fuzdev/gro/gen.js | string, RawGenFile, null, or nested array | | RawGenFile | Interface | @fuzdev/gro/gen.js | output file with content, filename, format | | GenDependencies | Type | @fuzdev/gro/gen.js | 'all', config object, or resolver function | | GenDependenciesConfig| Interface | @fuzdev/gro/gen.js | patterns? (RegExp[]) and files? (PathId[]) |

Gen and GenContext are also re-exported from @fuzdev/gro (the package index).