Skip to content

Conversation

@0xisk
Copy link
Member

@0xisk 0xisk commented Dec 3, 2025

Types of changes

What types of changes does your code introduce to OpenZeppelin Midnight Contracts?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update (if none of the other choices apply)

Fixes #???

Problem:
The compiler flattened all compiled artifacts into a single artifacts/ directory, losing the source directory structure. This made it hard to navigate artifacts that mirror the source organization (e.g., src/arccess/, src/math/, src/tokens/, ...).

Solution:
Updated CompilerService.compileFile() to preserve the source directory structure in the artifacts output. The output path now mirrors the source path relative to src/.

Changes:

  • Added dirname import to extract directory paths
  • Modified output directory construction to include the source directory structure
  • Added test cases to verify directory structure preservation

Before/After Diff

Artifacts Structure

Before:

artifacts/
├── AccessControl/
├── AccessControl.mock/
├── IAccessControl/
├── Index/
├── Types/
└── ... (all flattened)

After:

artifacts/
├── access/
│   ├── AccessControl/
│   ├── test/
│   │   └── AccessControl.mock/
│   ├── interfaces/
│   │   └── IAccessControl/
│   └── Types/
├── math/
│   ├── Index/
│   ├── Uint64/
│   └── ...
├── shielded-token/
│   └── ...
└── ... (preserves source structure)

This change improves artifact organization and makes it easier to locate compiled contracts that match the source structure.

PR Checklist

  • I have read the Contributing Guide
  • I have added tests that prove my fix is effective or that my feature works
  • I have added documentation of new methods and any new behavior or changes to existing behavior
  • CI Workflows Are Passing

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for custom source and output directories via new CLI options (--src, --out)
    • Added hierarchical output option to preserve source directory structure in artifacts
  • Documentation

    • Updated CLI help text with clarified option descriptions
    • Added new examples demonstrating various compilation scenarios
    • Expanded documentation showing default flattened vs. hierarchical artifact layouts

✏️ Tip: You can customize this high-level summary in your review settings.

@0xisk 0xisk requested a review from a team as a code owner December 3, 2025 12:27
@0xisk 0xisk self-assigned this Dec 3, 2025
@0xisk 0xisk requested a review from andrew-fleming December 3, 2025 12:27
@0xisk
Copy link
Member Author

0xisk commented Dec 3, 2025

Blocked by: #28

@emnul emnul requested a review from a team as a code owner December 5, 2025 23:44
Copy link
Contributor

@emnul emnul left a comment

Choose a reason for hiding this comment

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

This structure is definitely much more intuitive and makes sense for more complex applications. It also works fine in the most naive case eg src/doStuff.compact compiles to artifacts/doStuff/**, however that means if a user has the following directory structure:

src
├── doStuff
│   └── otherStuff.compact
└── doStuff.compact

then artifacts will look like:

artifacts/doStuff
├── compiler // doStuff.compact related files
│   └── contract-info.json
├── contract // doStuff.compact related files
│   ├── index.cjs
│   ├── index.cjs.map
│   └── index.d.cts
├── otherStuff
│   ├── compiler
│   │   └── contract-info.json
│   ├── contract
│   │   ├── index.cjs
│   │   ├── index.cjs.map
│   │   └── index.d.cts
│   └── zkir
│       └── otherStuff.zkir
└── zkir
    └── doStuffffff.zkir

Beyond being a little confusing I don't think it's worth handling this edge case, but I thought it was worth pointing out.

@emnul
Copy link
Contributor

emnul commented Dec 6, 2025

@0xisk what do you think about leaving the flattened structure as the default case and allowing a hierarchical structure via a flag? Flattened works best for the simplest use case, likely the majority of consumers will only have a handful of contracts. Making the default hierarchical seems overly opinionated by preferring the most complex apps. We can have the best of both worlds with a flag 🚩

Creds to @andrew-fleming for the idea.

Copy link
Contributor

@andrew-fleming andrew-fleming left a comment

Choose a reason for hiding this comment

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

jk @emnul beat me to it :)

@0xisk 0xisk force-pushed the refactor/structured-artifacts branch from f7c4a88 to 39fd5ad Compare December 9, 2025 12:59
@0xisk 0xisk requested a review from a team as a code owner December 9, 2025 12:59
@0xisk 0xisk changed the title refactor(cli): structure the output of the artifacts to follow with the src/ refactor(cli): introduce cli options including --hierarchical flag Dec 9, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR refactors the compiler's configuration model from positional parameters to an options-driven architecture. It introduces CompilerOptions interface, updates FileDiscovery and CompilerService to accept configurable paths, restructures CompactCompiler's constructor and API, and updates CLI help text with new --src, --out, and --hierarchical options.

Changes

Cohort / File(s) Summary
Documentation
README.md
Replaces "Useful commands" block with detailed "Compiler CLI Options" section; adds "Artifact Output Structure" and "Examples" sections documenting CLI options (--dir, --src, --out, --hierarchical, --skip-zk, +<version>), default artifact layout, and command examples.
Core Compiler Configuration
packages/cli/src/Compiler.ts
Introduces CompilerOptions interface and ResolvedCompilerOptions type; replaces hard-coded path constants with defaults (DEFAULT_SRC_DIR, DEFAULT_OUT_DIR); adds srcDir state to FileDiscovery; injects options into CompilerService to support configurable output paths; restructures CompactCompiler constructor to accept CompilerOptions object instead of positional parameters; adds static parseArgs() and fromArgs() methods for CLI argument parsing; introduces testOptions getter replacing individual test property accessors.
CLI Help & Usage
packages/cli/src/runCompiler.ts
Updates usage text to document new --src and --out options with defaults; clarifies --dir as subdirectory within src; adds --hierarchical description; expands examples section to show flattened output, hierarchical output, and custom directory usage; adds "Artifact Output Structure" section.
Unit Tests
packages/cli/test/Compiler.test.ts
Updates all CompactCompiler constructor calls to pass single options object instead of positional parameters; refactors test property access from direct fields (testFlags, testTargetDir, etc.) to nested testOptions object; adds test cases for new options combinations (custom srcDir/outDir, hierarchical with custom dirs).
CLI Tests
packages/cli/test/runCompiler.test.ts
Updates test expectations for usage help text to match new CLI options and documentation structure (--src, --out, --hierarchical descriptions and examples).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Compiler.ts: Multiple interconnected type and class changes requiring validation of new CompilerOptions model, path resolution logic, and options threading through FileDiscoveryCompilerServiceCompactCompiler
  • Constructor and API breaking changes: Public API shift from positional to options-driven requires verification that all call sites are properly migrated
  • Extensive test updates in Compiler.test.ts: Indicates substantial behavioral and signature changes; review should verify test coverage adequately reflects new options combinations and default resolution logic
  • Path handling changes: New srcDir and outDir handling in FileDiscovery.listCompactFiles() and CompilerService.compileFile() requires attention to ensure relative path computation is correct

Poem

🐰 Once scattered params, now unified and clean,
Options bundled snug in a cohesive scene!
From positional chaos to a structured way,
Configuration flows more gracefully today.
CLI speaks clearer, paths align their dance—
A refactor refined, given proper chance! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring the CLI to introduce configuration options including the --hierarchical flag, which is the core feature enabling flexible artifact output structuring.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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: 0

🧹 Nitpick comments (5)
packages/cli/src/Compiler.ts (1)

609-646: Argument parsing handles edge cases, but consider positional argument values starting with '+'.

The parsing logic correctly handles --dir, --src, --out, --hierarchical, and version (+<version>) flags. The check !args[i + 1].startsWith('--') prevents treating another flag as a value, but doesn't account for values starting with + (e.g., --dir +something would be misinterpreted as missing value since + is handled separately).

This is a minor edge case unlikely to occur in practice with directory names.

Consider also checking for + prefix to provide a clearer error message:

-        const valueExists =
-          i + 1 < args.length && !args[i + 1].startsWith('--');
+        const valueExists =
+          i + 1 < args.length && 
+          !args[i + 1].startsWith('--') && 
+          !args[i + 1].startsWith('+');
README.md (2)

114-118: Add language specifier to fenced code blocks for better rendering.

Per markdownlint, fenced code blocks should have a language specified. These blocks show directory structures.

-```
+```text
 <out>/
   ContractA/
   ContractB/

---

`128-134`: **Add language specifier to this fenced code block as well.**

Same issue as above for the hierarchical output example.


```diff
-```
+```text
 <out>/
   subdir/
     ContractA/
   another/
     ContractB/

</blockquote></details>
<details>
<summary>packages/cli/src/runCompiler.ts (1)</summary><blockquote>

`133-141`: **Error handling for --dir is present, but missing for --src and --out.**

The argument parsing error handling only checks for `--dir flag requires a directory name`. Similar errors can be thrown for `--src` and `--out` flags, but they won't trigger the usage help display.


Update the error check to handle all directory flag errors:

```diff
   // Arg parsing
   const errorMessage = error instanceof Error ? error.message : String(error);
-  if (errorMessage.includes('--dir flag requires a directory name')) {
+  if (
+    errorMessage.includes('--dir flag requires a directory name') ||
+    errorMessage.includes('--src flag requires a directory path') ||
+    errorMessage.includes('--out flag requires a directory path')
+  ) {
     spinner.fail(
-      chalk.red('[COMPILE] Error: --dir flag requires a directory name'),
+      chalk.red(`[COMPILE] Error: ${errorMessage}`),
     );
     showUsageHelp();
     return;
   }
packages/cli/test/runCompiler.test.ts (1)

215-230: Consider adding tests for --src and --out argument parsing errors.

Currently, only the --dir parsing error is tested. Given the new --src and --out flags can also throw errors, consider adding test coverage.

Add tests for the new flag validation:

it('should handle --src parsing errors', async () => {
  const error = new Error('--src flag requires a directory path');
  mockFromArgs.mockImplementation(() => {
    throw error;
  });

  await import('../src/runCompiler.js');

  expect(mockSpinner.fail).toHaveBeenCalledWith(
    '[COMPILE] Error: --src flag requires a directory path',
  );
  expect(mockConsoleLog).toHaveBeenCalledWith(
    '\nUsage: compact-compiler [options]',
  );
  expect(mockExit).toHaveBeenCalledWith(1);
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91b88ea and 39fd5ad.

📒 Files selected for processing (5)
  • README.md (2 hunks)
  • packages/cli/src/Compiler.ts (17 hunks)
  • packages/cli/src/runCompiler.ts (3 hunks)
  • packages/cli/test/Compiler.test.ts (18 hunks)
  • packages/cli/test/runCompiler.test.ts (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
README.md

114-114: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


128-128: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (15)
packages/cli/src/Compiler.ts (7)

17-20: LGTM! Clear default constants.

Good practice defining these as module-level constants for maintainability.


33-70: Well-structured options interface with good documentation.

The CompilerOptions interface is well-documented with clear descriptions and examples. The ResolvedCompilerOptions type correctly combines required defaults with optional fields.


203-237: LGTM! FileDiscovery correctly computes relative paths from srcDir.

The refactored FileDiscovery properly uses relative(this.srcDir, fullPath) to compute paths relative to the source directory, which is essential for the hierarchical output feature to work correctly.


561-577: Well-structured constructor with clear defaults.

The constructor properly resolves defaults and wires up the services with the appropriate options. Good separation of concerns.


776-786: Good validation: target directory check only when specified.

The condition this.options.targetDir && !existsSync(searchDir) correctly validates that the target directory exists only when one is specified, avoiding false errors for default full-project compilation.


858-863: Test accessor exposes resolved options.

The testOptions getter provides a clean way for tests to verify the internal state without exposing mutable access.


340-349: Edge case for root-level files is correctly handled.

The condition this.options.hierarchical && fileDir !== '.' properly handles root-level files. Verification confirms that dirname('MyToken.compact') returns '.', causing root-level files to fall back to flattened output regardless of the hierarchical setting, while nested files use hierarchical structure when enabled.

README.md (1)

96-162: Comprehensive documentation of CLI options.

The new documentation clearly explains all CLI options, artifact structures, and provides useful examples. Good alignment with the implementation.

packages/cli/src/runCompiler.ts (1)

169-236: Help text is comprehensive and well-structured.

The updated showUsageHelp function provides clear documentation for all new options with good examples. The artifact output structure explanation is helpful for users.

packages/cli/test/runCompiler.test.ts (1)

311-384: Test coverage for usage help is comprehensive.

The test properly verifies all new CLI options, the artifact output structure section, and updated examples. Good alignment with the implementation.

packages/cli/test/Compiler.test.ts (5)

300-332: Excellent test coverage for flattened output behavior.

These tests properly verify that the default behavior flattens directory structure, handling both single-level and nested paths correctly.


366-418: Good hierarchical output tests with edge case coverage.

The tests correctly verify:

  1. Single-level directory preservation
  2. Nested directory preservation
  3. Root-level files remain flattened even with hierarchical: true

This edge case (Line 405-417) is particularly important to ensure root-level files don't get incorrect paths.


420-463: Comprehensive tests for custom srcDir and outDir.

Tests verify custom source/output directories work correctly, both alone and in combination with hierarchical output. Good coverage.


716-800: Thorough argument parsing tests for new flags.

Tests cover:

  • --hierarchical flag alone and with other options
  • --src and --out flags with valid values
  • Combined --src and --out usage
  • Default values when flags not specified
  • Error cases for missing arguments

Excellent coverage of the new CLI options.


585-623: Constructor tests properly updated for options-based API.

Tests verify both default values and explicit parameter passing through the new options object pattern.

@0xisk 0xisk requested a review from andrew-fleming December 9, 2025 13:14
@0xisk
Copy link
Member Author

0xisk commented Dec 9, 2025

@andrew-fleming @emnul thanks for your suggestions, the recent changes added are for supporting a list of options for the compiler command line including the hierarchical option.

Copy link
Contributor

@andrew-fleming andrew-fleming left a comment

Choose a reason for hiding this comment

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

Great work, @0xisk! I left a comment and a question

@0xisk 0xisk requested a review from andrew-fleming December 11, 2025 09:29
Copy link
Contributor

@andrew-fleming andrew-fleming left a comment

Choose a reason for hiding this comment

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

LGTM!

@andrew-fleming andrew-fleming merged commit ed4131c into main Dec 11, 2025
10 checks passed
@andrew-fleming andrew-fleming deleted the refactor/structured-artifacts branch December 11, 2025 15:22
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.

4 participants