Skip to content

Conversation

maltsev
Copy link
Member

@maltsev maltsev commented Sep 29, 2025

For now, I made it quite simple: the options can be passed only via the configuration file.

Later, we might add CLI args for the options.

The help section:

# ./dist/bin.js --help
Usage: htmlnano [options] [input]

Minify HTML with htmlnano

Arguments:
  input                  input file (default: "-")

Options:
  -o, --output <file>    output file (default: "-")
  -p, --preset <preset>  preset to use (default: "safe")
  -c, --config <file>    path to config file
  -h, --help             display help for command

It relies on #383 bug fix.

Original issue: #3

Summary by CodeRabbit

  • New Features

    • Introduced a command-line interface to run htmlnano from the terminal, supporting stdin/stdout, file input/output, presets, and external config files with clear errors for invalid presets.
    • Exposed the list of presets for programmatic use.
    • Added support for specifying a custom config file path via options.
  • Documentation

    • Added CLI usage instructions and examples.
    • Documented custom configuration file paths.
  • Tests

    • Added end-to-end tests covering CLI I/O, presets (valid/invalid), and config-file workflows.

@maltsev
Copy link
Member Author

maltsev commented Oct 3, 2025

@coderabbitai review

Copy link

coderabbitai bot commented Oct 3, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

coderabbitai bot commented Oct 3, 2025

Walkthrough

Adds a new CLI entry point and related tests, updates package.json to expose the binary and add a dependency, exports presets, shifts configPath into HtmlnanoOptions, updates types and tests accordingly, and expands documentation (including a duplicated CLI section in docs/020-usage.md).

Changes

Cohort / File(s) Summary
CLI introduction
package.json, src/bin/index.ts
Introduces a Commander-based CLI at src/bin/index.ts; declares dist/bin.js as the executable via bin; adds postbuild chmod script; no library runtime changes in CLI file.
Core API and types updates
src/index.ts, src/types.ts
Exports presets. Refactors loadConfig to read options.configPath (removes separate configPath param). Adds configPath?: string to HtmlnanoOptions; updates HtmlnanoPreset and HtmlnanoOptionsConfigFile to omit configPath.
CLI tests
test/usage_cli.ts
Adds end-to-end tests covering stdin/stdout, file I/O, presets (valid/invalid), and -c config application, invoking dist/bin.js.
Updated unit tests
test/htmlnano.ts
Updates tests to pass configPath via options; adds a test ensuring config path isn’t treated as a module name; adjusts expectations with new loadConfig signature.
Documentation updates
README.md, docs/docs/020-usage.md, docs/docs/030-config.md
Adds CLI usage examples in README and docs; docs/020-usage.md contains the CLI section twice; docs/030-config.md documents “Custom path” via options.configPath.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant C as CLI (Commander)
  participant H as htmlnano.process
  participant L as loadConfig
  participant FS as File System

  U->>C: Run htmlnano [--preset/-p] [--config/-c] [-i/-o]
  C->>C: Validate preset against exported presets
  alt invalid preset
    C-->>U: Print error with available presets
    C-->C: set process.exitCode = 1
  else valid preset
    opt read input
      alt -i provided
        C->>FS: Read input file
        FS-->>C: HTML
      else stdin
        U-->>C: HTML via STDIN
      end
    end
    C->>H: processHtml(html, {configPath, ...}, selectedPreset)
    H->>L: loadConfig(options.configPath, options)
    L-->>H: Resolved options
    H-->>C: Minified HTML
    opt write output
      alt -o provided
        C->>FS: Write output file
        FS-->>C: Done
      else stdout
        C-->>U: Minified HTML via STDOUT
      end
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I nibbled through flags with whiskered delight,
A bin sprang to life in the moonlit night.
Presets parade, configs find their track,
Stdin in, shiny HTML back.
Thump-thump! Tests dance, docs hum along—
A rabbit’s CLI, swift and strong. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title refers to introducing a CLI, which is indeed the main change, but using the term “version” is misleading and does not clearly communicate that a new command-line tool is being added. Please revise the title to clearly describe adding a CLI tool, for example: “Add command-line interface for htmlnano.”
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cli

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/bin/index.ts (1)

15-45: Handle async action errors via parseAsync() (or explicit try/catch).

program.parse() does not await or catch the promise returned by the async action. Any rejection from processHtml (e.g. invalid config) will surface as an unhandled promise rejection and skip Commander’s error handling. Switch to program.parseAsync(process.argv) or wrap the action body in try/catch and set process.exitCode.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 108e4a3 and c8bb52d.

📒 Files selected for processing (9)
  • README.md (1 hunks)
  • docs/docs/020-usage.md (1 hunks)
  • docs/docs/030-config.md (1 hunks)
  • package.json (3 hunks)
  • src/bin/index.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/types.ts (2 hunks)
  • test/htmlnano.ts (2 hunks)
  • test/usage_cli.ts (1 hunks)
🔇 Additional comments (8)
package.json (1)

60-60: Verify Node.js support before adopting commander v14.

Commander 14 requires Node.js 20+. htmlnano previously supported older LTS versions, so this dependency effectively raises the minimum Node version. Confirm that the project is ready to drop support for older runtimes or stick to commander 13.x.

Based on learnings

src/index.ts (2)

11-15: LGTM!

Exporting the presets constant provides a clean public API for the CLI and external consumers to discover available presets.


17-21: All loadConfig calls now use the new signature; no instances of a third configPath argument remain. This is a breaking change—add a note to the CHANGELOG.

test/usage_cli.ts (5)

14-20: LGTM!

The test correctly validates stdin/stdout processing by passing input via execFileSync and asserting the minified output.


22-28: LGTM!

The test correctly validates the --preset max option by asserting the more aggressively minified output.


30-40: LGTM!

The test correctly validates error handling for an invalid preset by asserting non-zero exit status, empty stdout, and an informative error message listing available presets.


42-61: LGTM!

The test correctly validates config file loading via the -c flag. The temporary file is properly cleaned up in the finally block, ensuring no test artifacts remain.


63-84: LGTM!

The test correctly validates file-based I/O by reading from an input file and writing to an output file. The assertions comprehensively cover exit status, stdout emptiness, and output file content. Cleanup is properly handled for both temporary files.

"scripts": {
"clean": "rimraf dist",
"build": "npm run clean && bunchee",
"postbuild": "chmod +x dist/bin.js",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid platform-specific chmod in npm scripts.

Running npm run build on Windows will fail because chmod is not available in the default shell, breaking local builds and CI on that platform. Please switch to a cross-platform approach, e.g. invoking node -e "require('fs').chmodSync('dist/bin.js', 0o755)" or configuring Bunchee to preserve the executable bit.

🤖 Prompt for AI Agents
In package.json around line 10, the "postbuild" script uses a platform-specific
chmod command which fails on Windows; replace it with a cross-platform
Node-based chmod (or configure Bunchee to preserve executable bit) by invoking a
small node.js one-liner that calls fs.chmodSync on dist/bin.js so the executable
bit is set on all platforms.

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