Skip to content

Commit 60d53db

Browse files
authored
feat(cli): add --verbose flag and logger.debug; document Babel ESM interop (#47)
- Add --verbose CLI flag (also activated by REACT_SPEC_GEN_DEBUG env var) - Implement logger.debug() that no-ops unless verbose mode is on - Trace key pipeline events from runGenerate - Document the @babel/traverse default-export ESM interop in component.ts and props.ts - Add .gitattributes (eol=lf) to stop CRLF/LF churn between Windows dev and CI - Update README flags table
1 parent d8aa96d commit 60d53db

7 files changed

Lines changed: 44 additions & 2 deletions

File tree

.gitattributes

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Force LF line endings for text files across all platforms.
2+
# Prevents EOL churn between Windows (autocrlf=true) and CI Linux runners.
3+
* text=auto eol=lf
4+
5+
# Binary assets — never normalize.
6+
*.png binary
7+
*.jpg binary
8+
*.jpeg binary
9+
*.gif binary
10+
*.ico binary
11+
*.svg text eol=lf
12+
*.woff binary
13+
*.woff2 binary

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Expected output:
5555
|---|---|
5656
| `-y`, `--yes` | Skip overwrite prompts |
5757
| `--ai` | Enhance inferred values and edge cases using an AI provider (optional) |
58+
| `--verbose` | Print debug traces (also enabled by `REACT_SPEC_GEN_DEBUG=1`) |
5859

5960
The CLI prints a verification checklist after writing — review inferred prop values, assertion meaningfulness, and event-handler coverage before committing.
6061

src/cli/commands/generate.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ export interface GenerateOptions {
1515

1616
export async function runGenerate(file: string, opts: GenerateOptions): Promise<number> {
1717
try {
18+
logger.debug(`runGenerate: file=${file}, ai=${opts.ai}, yes=${opts.yes}`);
1819
const enhancer = opts.ai ? withSpinner(buildEnhancer(new MockProvider())) : undefined;
1920

2021
const result = await run(file, { enhancer });
2122
const { testFilePath, testSource, storyFilePath, storySource } = result.outputs;
23+
logger.debug(
24+
`pipeline result: component=${result.model.name}, props=${result.model.props.length}, warnings=${result.model.warnings.length}`,
25+
);
2226

2327
for (const w of result.model.warnings) logger.warn(w);
2428

src/cli/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Command } from "commander";
22
import pkg from "../../package.json" with { type: "json" };
3+
import { logger } from "../utils/logger.js";
34
import { runGenerate } from "./commands/generate.js";
45

56
export function buildProgram(): Command {
@@ -12,7 +13,9 @@ export function buildProgram(): Command {
1213
.argument("<file>", "Path to a .tsx or .jsx component file")
1314
.option("--ai", "Enable AI-powered enhancement (mock provider for now)", false)
1415
.option("-y, --yes", "Overwrite existing files without prompting", false)
15-
.action(async (file: string, opts: { ai: boolean; yes: boolean }) => {
16+
.option("--verbose", "Print debug traces (also enabled by REACT_SPEC_GEN_DEBUG=1)", false)
17+
.action(async (file: string, opts: { ai: boolean; yes: boolean; verbose: boolean }) => {
18+
logger.setVerbose(opts.verbose);
1619
const code = await runGenerate(file, opts);
1720
process.exit(code);
1821
});

src/core/analyzer/component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import * as t from "@babel/types";
44
import { AnalysisError } from "../errors.js";
55
import type { ParsedSource } from "./parser.js";
66

7-
// @babel/traverse default export interop under ESM.
7+
// @babel/traverse ships as CJS but exposes its function as `module.exports.default`
8+
// when consumed from ESM, so we unwrap the default export at runtime. The
9+
// `?? _traverse` fallback covers older bundlers that already give us the function.
810
const traverse = (_traverse as unknown as { default: typeof _traverse }).default ?? _traverse;
911

1012
export interface ComponentInfo {

src/core/analyzer/props.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import type { PropDescriptor, PropKind } from "../model.js";
44
import type { ComponentInfo } from "./component.js";
55
import type { ParsedSource } from "./parser.js";
66

7+
// @babel/traverse ships as CJS but exposes its function as `module.exports.default`
8+
// when consumed from ESM, so we unwrap the default export at runtime. The
9+
// `?? _traverse` fallback covers older bundlers that already give us the function.
710
const traverse = (_traverse as unknown as { default: typeof _traverse }).default ?? _traverse;
811

912
const EVENT_HANDLER_RE = /^on[A-Z]/;

src/utils/logger.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
import chalk from "chalk";
22

3+
/**
4+
* Verbose mode: enabled by --verbose CLI flag or by setting the
5+
* REACT_SPEC_GEN_DEBUG environment variable to a non-empty value.
6+
* When disabled, `logger.debug()` is a no-op.
7+
*/
8+
let verboseEnabled = Boolean(process.env.REACT_SPEC_GEN_DEBUG);
9+
310
export const logger = {
11+
setVerbose(enabled: boolean): void {
12+
verboseEnabled = enabled || Boolean(process.env.REACT_SPEC_GEN_DEBUG);
13+
},
14+
isVerbose(): boolean {
15+
return verboseEnabled;
16+
},
417
info(msg: string): void {
518
console.log(msg);
619
},
@@ -16,4 +29,7 @@ export const logger = {
1629
dim(msg: string): void {
1730
console.log(chalk.dim(msg));
1831
},
32+
debug(msg: string): void {
33+
if (verboseEnabled) console.log(chalk.gray("[debug] ") + msg);
34+
},
1935
};

0 commit comments

Comments
 (0)