Skip to content

feat: Tier 1 CLI improvements (v0.3.0)#3

Merged
ducdmdev merged 18 commits intomainfrom
feat/tier1-cli-improvements
Feb 26, 2026
Merged

feat: Tier 1 CLI improvements (v0.3.0)#3
ducdmdev merged 18 commits intomainfrom
feat/tier1-cli-improvements

Conversation

@ducdmdev
Copy link
Owner

@ducdmdev ducdmdev commented Feb 26, 2026

Summary

Implements all 6 Tier 1 recommendations from the CLI audit, plus 2 interactive enhancements, making apigen CI/CD-ready and beginner-friendly with zero breaking changes.

Features

  • allOf compositionresolveAllOf() merges properties from $ref and inline variants
  • Circular reference detection — IR safely handles self-referencing schemas
  • Config file loading--config flag + auto-search for apigen.config.ts/.js
  • Extended configsplit, baseURL, apiFetchImportPath fields added to ConfigInput
  • baseURL wiring — generators and writer pass baseURL through to generated fetch calls
  • Improved error messages — file-not-found, parse failures, empty spec warnings
  • --dry-run flag — preview generated files with sizes, TTY confirmation prompt
  • Interactive config wizard — prompts for all options, saves apigen.config.ts

Stats

  • 88 tests pass (up from 70), 323 assertions across 13 test files
  • 18 files changed, +2,114 lines
  • Typecheck clean, build successful
  • Version bumped to 0.3.0

Test plan

  • bun test — 88/88 pass, 0 fail
  • bun run typecheck — clean
  • bun run build — successful
  • Manual: interactive wizard — launches with 3 options (file/URL/discover), prompts work in TTY
  • Manual: --dry-run — shows file preview with sizes, exits without writing in non-TTY
  • Manual: --config apigen.config.js — loads config, generates correctly
  • Manual: --config apigen.config.ts — works via Bun; Node.js requires --experimental-strip-types (known limitation)
  • Manual: allOf spec — User correctly merges BaseEntity properties
  • Real-world: masterdata-tool (58 operations, 18 tags, 115 inline schemas) — 71 files, 0 type errors

Known limitations

  • .ts config files require Bun or Node.js --experimental-strip-types. .js config works everywhere.
  • Interactive wizard requires a TTY — non-TTY without -i or config file exits with ExitPromptError.

Includes interactive config init wizard and dry-run confirmation
prompt using @inquirer/prompts alongside the original 6 features:
config file loading, extended config, --dry-run, error messages,
circular ref detection, and allOf composition.
Verify that self-referencing (User.manager -> User) and recursive
(Node.children -> Node[]) schemas are handled without infinite loops.
The current IR extraction already handles this by keeping $ref as
string properties without recursion.
…ures

- Add existsSync check before reading, throws "Cannot find spec file: {path}"
- Wrap YAML/JSON parse in try/catch, throws "Failed to parse {path}: {error}"
- Improve unrecognized format message with expected formats hint
- Add tests for both error cases
Add resolveAllOf() function that merges properties from all allOf
variants. For $ref variants, it looks up the referenced schema and
collects its properties. For inline variants, it collects properties
directly. Required arrays are unioned from all variants.

Wire into extractIR() schema loop to detect schemaDef.allOf and
resolve before property extraction.
Add console.warn at the top of extractIR when the spec has no paths
defined, so users get a clear message about 0 operations being
extracted rather than silently generating empty output.
- Add baseURL and apiFetchImportPath to writeGeneratedFiles options
- Pass baseURL to generateHooks in flat mode
- Pass baseURL to generateApiFetch in split mode
- Pass apiFetchImportPath to generateHooks in flat mode
- Add tests for baseURL passthrough in both flat and split modes
Verify the full pipeline (load -> IR -> generate -> write) correctly
handles allOf specs. The test loads allof-composition.yaml, runs the
pipeline, and verifies that User interface has merged properties from
BaseEntity (id, createdAt) and inline (name, email), and that hooks
for listUsers and createUser are generated.
- Add loadConfigFile() for dynamic import of config files
- Add findConfigFile() to auto-search for apigen.config.ts/js
- Add -c/--config and --base-url CLI options
- Merge config: CLI flags override file config, file config overrides defaults
Change stringContaining('0 operations') to stringContaining("no 'paths'")
to match the actual warning message more precisely.
- Add FileInfo type and collectFileInfo() to writer for dry-run mode
- writeGeneratedFiles returns FileInfo[] when dryRun is true, writes nothing
- Add --dry-run CLI flag that previews files with sizes
- In TTY mode, prompts to confirm before proceeding with generation
- In non-TTY (CI), prints summary and exits
- Add promptForConfig() wizard: prompts for output dir, mock, split, baseURL
- Add writeConfigFile() to save apigen.config.ts with defineConfig
- When no config file and no -i flag, wizard runs after spec input prompt
- Wizard offers to save config for future runs
Update all docs to reflect new features: config file loading,
--config/--base-url/--dry-run flags, extended ConfigInput with
split/baseURL/apiFetchImportPath, interactive wizard, allOf
composition, and circular ref detection. Fix test count (87/13),
add missing test files and fixtures to contributing guide.
Properties typed as `object` but named `address`, `contact`, etc. were
incorrectly getting faker string values instead of `{}` because the
name-based heuristic ran before the type check. Move object/unknown
type guards above the fakerValueForField fallback in mockPropertyValue.

Found via real-world testing against masterdata-tool (58 operations).
@ducdmdev ducdmdev merged commit 4e47812 into main Feb 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant