/skills/fuz-stack/references/code-generation
  • docs
  • skills
  • fuz-stack
    • Async Patterns
    • Code Generation
    • Common Utilities
    • CSS 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
auth_attack_surface.gen.json.tsauth_attack_surface.json

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_configParsedSvelteConfigparsed svelte.config.js
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)
ArgDefaultDescription
_['src']input paths (files or directories to scan)
--root_dirs[process.cwd()]root directories to resolve input paths against
--checkfalseexit nonzero if any generated files have changed

gro gen --check compares generated output against existing files; if any 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

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} */`; 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

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, library.json, or library.ts.

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:

import type {Gen} from '@fuzdev/gro'; import {create_tx_app_surface_spec} from './auth_attack_surface_helpers.ts'; 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 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 ]; };

Quick Reference

ExportTypeSourcePurpose
GenType@fuzdev/gro/gen.tsGenFunction or GenConfig
GenFunctionType@fuzdev/gro/gen.ts(ctx: GenContext) => RawGenResult
GenConfigInterface@fuzdev/gro/gen.tsgenerate + optional dependencies
GenContextInterface@fuzdev/gro/gen.tscontext passed to gen functions
RawGenResultType@fuzdev/gro/gen.tsstring, RawGenFile, null, or nested array
RawGenFileInterface@fuzdev/gro/gen.tsoutput file with content, filename, format
GenDependenciesType@fuzdev/gro/gen.ts'all', config object, or resolver function
GenDependenciesConfigInterface@fuzdev/gro/gen.tspatterns? (RegExp[]) and files? (PathId[])

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