Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 79 additions & 69 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ compact-compiler [options]
| `--dir <directory>` | Compile specific subdirectory within src | (all) |
| `--src <directory>` | Source directory containing `.compact` files | `src` |
| `--out <directory>` | Output directory for compiled artifacts | `artifacts` |
| `--exclude <pattern>` | Glob pattern to exclude files (can be repeated) | (none) |
| `--dry-run` | Preview which files would be compiled without compiling | `false` |
| `--hierarchical` | Preserve source directory structure in output | `false` |
| `--verbose` | Show circuit compilation details | `false` |
| `--skip-zk` | Skip zero-knowledge proof generation | `false` |
| `+<version>` | Use specific toolchain version (e.g., `+0.28.0`) | (default) |

Expand Down Expand Up @@ -86,6 +89,52 @@ artifacts/ # Hierarchical output
Token/
```

### Excluding Files

Use `--exclude` to skip files matching glob patterns. This is useful for excluding mock contracts, test files, or any files you don't want to compile.

**Supported glob patterns:**
- `*` matches any characters except `/`
- `**` matches zero or more path segments

**Examples:**
```bash
# Exclude all mock contracts
compact-compiler --exclude "**/*.mock.compact"

# Exclude test directory
compact-compiler --exclude "**/test/**"

# Multiple patterns
compact-compiler --exclude "**/*.mock.compact" --exclude "**/test/**"

# Root-level only (no ** prefix)
compact-compiler --exclude "*.mock.compact" # Only matches root-level mocks
```

### Dry run

Use `--dry-run` to see which files would be compiled without running the compiler. No environment validation or compilation is performed. Useful to verify `--exclude` patterns or to see the file list before a full run.

**Usage:**
```bash
# Preview all files that would be compiled
compact-compiler --dry-run

# Preview with exclusions (verify your exclude patterns)
compact-compiler --exclude "**/*.mock.compact" --dry-run

# Dry run in a specific directory
compact-compiler --dir access --dry-run
```

**Example output:**
```
ℹ [DRY-RUN] Would compile 2 file(s):
Token.compact
AccessControl.compact
```
Comment on lines +131 to +136
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fenced code block missing language specifier

The markdownlint MD040 warning here is valid — the example output block has no language tag. Use text (or console) to satisfy the linter.

📝 Proposed fix
-```
+```text
 ℹ [DRY-RUN] Would compile 2 file(s):
     Token.compact
     AccessControl.compact
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
**Example output:**
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 131-131: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/README.md` around lines 130 - 135, The fenced code block showing
the example output (the block containing "ℹ [DRY-RUN] Would compile 2 file(s):"
and the two file lines) lacks a language specifier which triggers markdownlint
MD040; update that fenced block to include a language tag such as text or
console (e.g., replace ``` with ```text) so the linter is satisfied while
keeping the existing contents unchanged.


### Examples

```bash
Expand All @@ -112,6 +161,18 @@ compact-compiler --dir access --skip-zk --hierarchical

# Use environment variable
SKIP_ZK=true compact-compiler

# Exclude mock contracts
compact-compiler --exclude "**/*.mock.compact"

# Exclude multiple patterns
compact-compiler --exclude "**/*.mock.compact" --exclude "**/test/**"

# Preview which files would be compiled (dry run)
compact-compiler --dry-run

# Dry run with exclusions to verify patterns
compact-compiler --exclude "**/*.mock.compact" --dry-run
```

## Builder CLI
Expand All @@ -129,7 +190,19 @@ The builder runs the compiler as a prerequisite, then executes additional build
compact-builder [options]
```

Accepts all compiler options except `--skip-zk` (builds always include ZK proofs).
Accepts all compiler options except `--skip-zk` (builds always include ZK proofs). Use `--exclude` to skip mock contracts or test files during the build.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Correct me if I'm wrong here, but I'm not seeing any logic that filters --skip-zk for the Builder CLI. The docs should be updated or that logic should be added if we want to enforce this requirement


### Options

| Option | Description | Default |
|--------|-------------|---------|
| `--dir <directory>` | Compile specific subdirectory within src | (all) |
| `--src <directory>` | Source directory containing `.compact` files | `src` |
| `--out <directory>` | Output directory for compiled artifacts | `artifacts` |
| `--exclude <pattern>` | Glob pattern to exclude files (can be repeated) | (none) |
| `--hierarchical` | Preserve source directory structure in output | `false` |
| `--verbose` | Show circuit compilation details (requires TTY, run directly not via turbo) | `false` |
| `+<version>` | Use specific toolchain version (e.g., `+0.28.0`) | (default) |

### Examples

Expand All @@ -142,75 +215,12 @@ compact-builder --dir token

# Build with custom directories
compact-builder --src contracts --out build
```

## Programmatic API

The compiler can be used programmatically:

```typescript
import { CompactCompiler } from '@openzeppelin/compact-tools-cli';

// Using options object
const compiler = new CompactCompiler({
flags: '--skip-zk',
targetDir: 'security',
version: '0.28.0',
hierarchical: true,
srcDir: 'src',
outDir: 'artifacts',
});

await compiler.compile();

// Using factory method (parses CLI-style args)
const compiler = CompactCompiler.fromArgs([
'--dir', 'security',
'--skip-zk',
'+0.28.0'
]);

await compiler.compile();
```

### Classes and Types

```typescript
// Main compiler class
class CompactCompiler {
constructor(options?: CompilerOptions, execFn?: ExecFunction);
static fromArgs(args: string[], env?: NodeJS.ProcessEnv): CompactCompiler;
static parseArgs(args: string[], env?: NodeJS.ProcessEnv): CompilerOptions;
compile(): Promise<void>;
validateEnvironment(): Promise<void>;
}

// Builder class
class CompactBuilder {
constructor(options?: CompilerOptions);
static fromArgs(args: string[], env?: NodeJS.ProcessEnv): CompactBuilder;
build(): Promise<void>;
}

// Options interface
interface CompilerOptions {
flags?: string; // Compiler flags (e.g., '--skip-zk --verbose')
targetDir?: string; // Subdirectory within srcDir to compile
version?: string; // Toolchain version (e.g., '0.28.0')
hierarchical?: boolean; // Preserve directory structure in output
srcDir?: string; // Source directory (default: 'src')
outDir?: string; // Output directory (default: 'artifacts')
}
```

### Error Types

```typescript
import {
CompactCliNotFoundError, // Compact CLI not in PATH
CompilationError, // Compilation failed (includes file path)
DirectoryNotFoundError, // Target directory doesn't exist
} from '@openzeppelin/compact-tools-cli';
# Build excluding mock contracts
compact-builder --exclude "**/*.mock.compact"

# Show circuit compilation details (verbose mode)
compact-builder --verbose
```

## Development
Expand Down
1 change: 0 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"author": "",
"license": "MIT",
"type": "module",
"exports": "./index.js",
"engines": {
"node": ">=20"
},
Expand Down
15 changes: 7 additions & 8 deletions packages/cli/src/Builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const execAsync = promisify(exec);

/**
* Configuration options for the Builder CLI.
* Inherits from CompilerOptions but excludes `flags` (which would allow --skip-zk).
* Builds should always include ZK proofs.
* Accepts all CompilerOptions including flags (e.g. --verbose for circuit compilation details).
* Note: builds should always include ZK proofs — avoid using --skip-zk.
*/
export type BuilderOptions = Omit<CompilerOptions, 'flags'>;
export type BuilderOptions = CompilerOptions;

/**
* A class to handle the build process for a project.
Expand All @@ -41,7 +41,7 @@ export type BuilderOptions = Omit<CompilerOptions, 'flags'>;
* Compactc version: 0.26.0
* ✔ [BUILD] [1/3] Compiling TypeScript
* ✔ [BUILD] [2/3] Copying artifacts
* ✔ [BUILD] [3/3] Copying and cleaning .compact files
* ✔ [BUILD] [3/3] Copying .compact files
* ```
*
* @example <caption>Failed Compilation Output</caption>
Expand Down Expand Up @@ -76,8 +76,8 @@ export class CompactBuilder {
shell: '/bin/bash',
},
{
cmd: 'mkdir -p dist && find src -type f -name "*.compact" -exec cp {} dist/ \\; 2>/dev/null && rm dist/Mock*.compact 2>/dev/null || true',
msg: 'Copying and cleaning .compact files',
cmd: 'mkdir -p dist && find src -type f -name "*.compact" -exec cp {} dist/ \\; 2>/dev/null || true',
msg: 'Copying .compact files',
shell: '/bin/bash',
},
Comment on lines 78 to 82
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

--exclude patterns are not honoured by the .compact copy step

The shell command at line 79 copies every .compact file found under src/ regardless of this.options.exclude. Exclude patterns are propagated to CompactCompiler (so mock files aren't compiled), but they never reach this step, so the excluded source files are still written to dist/. The old rm dist/Mock*.compact cleanup that kept them out of dist/ was removed without an equivalent filter here, leaving dist/ populated with the very mock contracts issue #66 aimed to exclude.

Additionally, because find ... -exec cp {} dist/ flattens all .compact files into a single directory, same-named files from different subdirectories (e.g., src/access/Token.compact and src/token/Token.compact) would silently overwrite each other.

The cleanest fix is to perform the copy in TypeScript, reusing the already-filtered file list from FileDiscovery, rather than relying on a raw shell command.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/Builder.ts` around lines 78 - 82, The current shell task in
Builder that runs 'find src -name "*.compact" -exec cp {} dist/' ignores
this.options.exclude and flattens files; replace that shell task with a
TypeScript copy that uses the already-filtered list from FileDiscovery (the same
list CompactCompiler uses) so excluded patterns are respected, create target
subdirectories under dist preserving each file's relative path (avoid name
collisions), and ensure directories are created before writing; update the
Builder task definition to call this new copy routine instead of the shell
command.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

@emnul, agreed — the copy step needs to be fixed. I'll plan the TypeScript-based replacement now.

Task: Fix .compact copy step to respect exclude patterns and preserve directory structure

  • Add copyCompactFiles() private method to CompactBuilder using FileDiscovery
  • Replace the raw shell copy step with a call to this new method
  • Preserve relative paths under dist/ to avoid name collisions

🏁 Script executed:

#!/bin/bash
# Check current imports and FileDiscovery.getCompactFiles signature
rg -n "getCompactFiles\|DEFAULT_SRC_DIR" packages/cli/src/Compiler.ts | head -40
rg -n "^import" packages/cli/src/Builder.ts

Length of output: 296


🏁 Script executed:

#!/bin/bash
rg -n "getCompactFiles\|DEFAULT_SRC_DIR\|class FileDiscovery" packages/cli/src/Compiler.ts | head -40
rg -n "^import" packages/cli/src/Builder.ts

Length of output: 296


🏁 Script executed:

#!/bin/bash
# Get FileDiscovery class, getCompactFiles, and DEFAULT_SRC_DIR from Compiler.ts
ast-grep --pattern 'class FileDiscovery {
  $$$
}' packages/cli/src/Compiler.ts

rg -n "DEFAULT_SRC_DIR|getCompactFiles|srcDir" packages/cli/src/Compiler.ts | head -30

Length of output: 7098


@emnul, here's the concrete fix. The plan replaces the raw shell copy step with a TypeScript method that reuses FileDiscovery (honoring exclude patterns) and preserves each file's relative path under dist/ to prevent name collisions.

Task: Fix .compact copy step in Builder.ts

  • Import FileDiscovery + Node.js fs/path utilities
  • Extend step type to support an async fn callback alongside cmd
  • Replace 3rd shell step with a fn pointing to a new copyCompactFiles() method
  • Update executeStep to dispatch correctly on fn vs cmd
  • Add private copyCompactFiles() using FileDiscovery with exclude patterns

Failed to handle agent chat message. Please try again.

];
Expand All @@ -86,7 +86,7 @@ export class CompactBuilder {
* Constructs a new CompactBuilder instance.
* @param options - Compiler options (flags, srcDir, outDir, hierarchical, etc.)
*/
constructor(options: CompilerOptions = {}) {
constructor(options: BuilderOptions = {}) {
this.options = options;
}

Expand Down Expand Up @@ -114,7 +114,6 @@ export class CompactBuilder {
* @throws Error if compilation or any build step fails
*/
public async build(): Promise<void> {
// Run compact compilation as a prerequisite
const compiler = new CompactCompiler(this.options);
await compiler.compile();

Expand Down
Loading
Loading