/skills/fuz-stack/references/code-generation
  • docs
  • skills
  • fuz-stack
    • Async Patterns
    • Code Generation
    • Common Utilities
    • CSS Patterns
    • Database Query Patterns
    • Dependency Injection
    • Documentation System
    • File Organization
    • mdz — Strict Markdown Dialect
    • Approved npm Dependencies
    • Path References in Documentation
    • Approved Rust Dependencies
    • Rust Patterns for the Fuz Ecosystem
    • Rust Performance Patterns
    • Rust Spine & Consumer Servers
    • Svelte 5 Patterns
    • Task Patterns
    • Testing Patterns
    • TSDoc Comment Style Guide
    • Twin Implementations (TS ↔ Rust)
    • 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. filename pattern, 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 fileOutput file
theme.gen.css.tstheme.css
css_classes_fixture.gen.json.tscss_classes_fixture.json
README.gen.md.tsREADME.md

The gen source file always has a .ts extension (.gen.ts, .gen.css.ts, …). 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 extension after .gen. (.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.ts.

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

PropertyTypeDescription
origin_idPathIdabsolute path of the gen file
origin_pathstringorigin_id relative to the project root
configGroConfigthe project's Gro configuration
svelte_configPromise<ParsedSvelteConfig>parsed svelte.config.js (lazy — resolved on first access)
filerFilerfilesystem tracker (file contents, dependency graph)
logLoggerscoped logger
timingsTimingsperformance measurement
invoke_taskInvokeTaskinvoke other Gro tasks
changed_file_idPathId \| undefinedset during dependency resolution; undefined during generation

Most 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 the formatter (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, it 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

For gen tasks that depend on the entire source tree rather than specific 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)

Positional args default to ['src']; --root_dirs (default [process.cwd()]) resolves them. gro gen --check compares generated output against existing files and fails if any is new or changed — called by gro check as part of CI.

Common Patterns

CSS generation

fuz_css utility classes are no longer a gen task in most projects — the vite_plugin_fuz_css Vite plugin scans source files, extracts CSS class usage via AST, and exposes a bundled virtual:fuz.css module (with HMR) containing only the classes, base styles, and theme variables actually used. See ./css-patterns §Project Setup.

The Gro generator equivalent, gen_fuz_css() in a fuz.gen.css.ts (accepts GenFuzCssOptions), still writes a committed fuz.css file, but the plugin is preferred.

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.ts'; import { render_theme_style } from './theme.ts'; export const gen: Gen = ({ origin_path }) => { const banner = `/* generated by ${origin_path} -- DO NOT EDIT DIRECTLY! */`; const theme = default_themes[0]!; // the default theme's variables are the system's defaults, so they render // into the base layer; runtime theme overrides beat them from fuz.theme const theme_style = render_theme_style(theme, { comments: true, empty_default_theme: false, layer: 'fuz.base' }); return `${banner}\n${theme_style}\n`; };

Library metadata

API documentation metadata is no longer produced by a gen task. Instead the svelte-docinfo Vite plugin analyzes TypeScript and Svelte source files at build/dev time and exposes the result through virtual:svelte-docinfo. Add the plugin in vite.config.ts and build a LibraryJson at runtime with library_json_from_modules — see ./documentation-system for the full setup.

// vite.config.ts import svelte_docinfo from 'svelte-docinfo/vite.js'; // ...plugins: [sveltekit(), svelte_docinfo()]

There is no committed library.gen.ts or library.json; the only committed artifact is a small src/routes/library.ts that adapts the virtual module (see ./documentation-system).

Blog feed generation

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

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

Consumer projects re-export the gen. Returns an array of 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 — fuz_css generates a JSON fixture of its class definitions this way:

import type { Gen } from '@fuzdev/gro'; import { css_class_definitions } from '$lib/css_class_definitions.ts'; export const gen: Gen = { dependencies: 'all', generate: () => { return JSON.stringify(css_class_definitions); } }; // css_classes_fixture.gen.json.ts → css_classes_fixture.json

Action codegen (zzz)

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

import type { Gen } from '@fuzdev/gro/gen.ts'; import { all_action_specs } from './action_specs.ts'; export const gen: Gen = ({ origin_path }) => ` // generated by ${origin_path} export const ActionMethods = [ ${all_action_specs.map((s) => `'${s.method}'`).join(',\n')} ] as const; `;

zzz's real generators delegate the heavy lifting to fuz_app's @fuzdev/fuz_app/actions/action_codegen.ts helpers (compose_gen_file, generate_action_method_enums, …) over all_action_specs.

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.ts'; 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 ]; };

All gen types import from @fuzdev/gro/gen.ts; Gen and GenContext are also re-exported from @fuzdev/gro (the package index).