From 8f1dda57a089f3f69942d9343f68dd5b51a09112 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Tue, 12 May 2026 14:33:00 +0200 Subject: [PATCH 01/13] refactor: update compiler and builder as in the compact-contracts --- packages/cli/README.md | 62 +- packages/cli/src/Builder.ts | 213 ++++-- packages/cli/src/Compiler.ts | 627 ++---------------- packages/cli/src/runBuilder.ts | 43 +- packages/cli/src/services/CompilerService.ts | 102 +++ .../cli/src/services/EnvironmentValidator.ts | 93 +++ packages/cli/src/services/FileDiscovery.ts | 81 +++ packages/cli/src/services/UIService.ts | 91 +++ packages/cli/src/types/options.ts | 137 ++++ packages/cli/src/utils.ts | 70 ++ packages/cli/test/Builder.test.ts | 202 ++++++ packages/cli/test/Compiler.test.ts | 62 ++ packages/cli/tsconfig.json | 1 + 13 files changed, 1111 insertions(+), 673 deletions(-) create mode 100644 packages/cli/src/services/CompilerService.ts create mode 100644 packages/cli/src/services/EnvironmentValidator.ts create mode 100644 packages/cli/src/services/FileDiscovery.ts create mode 100644 packages/cli/src/services/UIService.ts create mode 100644 packages/cli/src/types/options.ts create mode 100644 packages/cli/src/utils.ts create mode 100644 packages/cli/test/Builder.test.ts diff --git a/packages/cli/README.md b/packages/cli/README.md index 966bb61..24c9067 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -51,6 +51,7 @@ compact-compiler [options] | `--src ` | Source directory containing `.compact` files | `src` | | `--out ` | Output directory for compiled artifacts | `artifacts` | | `--hierarchical` | Preserve source directory structure in output | `false` | +| `--exclude ` | Skip `.compact` files matching the glob (repeatable). Patterns with `/` match the path relative to `--src`; others match the filename only. | (none) | | `--skip-zk` | Skip zero-knowledge proof generation | `false` | | `+` | Use specific toolchain version (e.g., `+0.28.0`) | (default) | @@ -116,12 +117,15 @@ SKIP_ZK=true compact-compiler ## Builder CLI -The builder runs the compiler as a prerequisite, then executes additional build steps: +The builder runs the compiler as a prerequisite, then executes a configurable +pipeline of distribution steps: 1. Compile `.compact` files (via `compact-compiler`) -2. Compile TypeScript (`tsc --project tsconfig.build.json`) -3. Copy artifacts to `dist/artifacts/` -4. Copy and clean `.compact` files to `dist/` +2. *(optional)* `rm -rf dist && mkdir -p dist` — when `--clean-dist` is set +3. Compile TypeScript (`tsc --project tsconfig.build.json`) +4. Copy artifacts to `dist/artifacts/` +5. Copy `.compact` files to `dist/` (flat by default; tree-preserving with `--hierarchical`) +6. *(optional)* Copy extra files into `dist/` — one per `--copy ` ### Usage @@ -129,19 +133,44 @@ 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 plus the following **builder-only** options: + +| Option | Description | +| --- | --- | +| `--clean-dist` | Run `rm -rf dist && mkdir -p dist` before building. | +| `--copy ` | Copy an extra file into `dist/` for distribution (e.g. `package.json`, `../README.md`). Repeatable. | + +The compiler's `--hierarchical` and `--exclude` flags also affect the builder: + +- **`--hierarchical`** — when set, source files keep their directory structure + under `dist/` (e.g. `src/token/Foo.compact` → `dist/token/Foo.compact`) and + compiled artifacts likewise preserve their subdir. When unset (default), both + are flat. +- **`--exclude `** — applies to both the compiler's file discovery + AND the builder's `.compact` copy step. When unset, the builder falls back + to `['Mock*', '*.mock.compact']` so mocks are stripped from the dist by + default (even though the compiler with no `--exclude` still compiles them). + +Builds should normally include ZK proofs; avoid passing `--skip-zk` here. ### Examples ```bash -# Full build +# Default build (flat .compact copy, excludes Mock*) compact-builder -# Build specific directory +# Build specific subdirectory compact-builder --dir token -# Build with custom directories +# Custom source/output directories compact-builder --src contracts --out build + +# Library-publish build: clean dist, hierarchical layout, exclude mocks + archive, ship metadata +compact-builder \ + --clean-dist \ + --hierarchical \ + --exclude 'Mock*' --exclude '*/archive/*' \ + --copy package.json --copy ../README.md ``` ## Programmatic API @@ -187,12 +216,14 @@ class CompactCompiler { // Builder class class CompactBuilder { - constructor(options?: CompilerOptions); + constructor(options?: BuilderOptions); static fromArgs(args: string[], env?: NodeJS.ProcessEnv): CompactBuilder; + static parseArgs(args: string[], env?: NodeJS.ProcessEnv): BuilderOptions; build(): Promise; + getSteps(): readonly { cmd: string; msg: string; shell?: string }[]; } -// Options interface +// Compiler options interface CompilerOptions { flags?: string; // Compiler flags (e.g., '--skip-zk --verbose') targetDir?: string; // Subdirectory within srcDir to compile @@ -200,7 +231,18 @@ interface CompilerOptions { hierarchical?: boolean; // Preserve directory structure in output srcDir?: string; // Source directory (default: 'src') outDir?: string; // Output directory (default: 'artifacts') + exclude?: string[]; // .compact glob patterns to skip in discovery + // (and in the builder's copy step) } + +// Builder options (extends CompilerOptions with dist-layout controls) +// Note: `hierarchical` and `exclude` (from CompilerOptions) drive BOTH the +// compiler and the builder. When `exclude` is undefined, the builder +// substitutes ['Mock*', '*.mock.compact'] for its copy step. +type BuilderOptions = CompilerOptions & { + cleanDist?: boolean; // rm -rf dist before building + copyToDist?: string[]; // extra files to copy into dist/ (e.g. package.json) +}; ``` ### Error Types diff --git a/packages/cli/src/Builder.ts b/packages/cli/src/Builder.ts index f94027f..3b2a963 100755 --- a/packages/cli/src/Builder.ts +++ b/packages/cli/src/Builder.ts @@ -4,95 +4,64 @@ import { exec } from 'node:child_process'; import { promisify } from 'node:util'; import chalk from 'chalk'; import ora, { type Ora } from 'ora'; -import { CompactCompiler, type CompilerOptions } from './Compiler.ts'; +import { CompactCompiler } from './Compiler.ts'; import { isPromisifiedChildProcessError } from './types/errors.ts'; +import { + type BuilderOnlyOptions, + type BuilderOptions, + type BuildStep, + DEFAULT_EXCLUDE_PATTERNS, +} from './types/options.ts'; +import { buildFindExcludes, shellQuote } from './utils.ts'; + +// Re-export public types so consumers keep importing them from './Builder.js'. +export type { BuilderOnlyOptions, BuilderOptions }; // Promisified exec for async execution 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. - */ -export type BuilderOptions = Omit; - /** * A class to handle the build process for a project. * Runs CompactCompiler as a prerequisite, then executes build steps (TypeScript compilation, - * artifact copying, etc.) - * with progress feedback and colored output for success and error states. + * artifact copying, etc.) with progress feedback and colored output for success and error states. + * + * Build steps are derived from {@link BuilderOptions} so consumers can produce a + * publishable distribution that matches their package conventions + * (preserve source tree, copy metadata, clean dist, custom excludes). * * @notice `cmd` scripts discard `stderr` output and fail silently because this is * handled in `executeStep`. * * @example * ```typescript + * // Default: flatten .compact files, exclude Mock* * const builder = new CompactBuilder({ flags: '--skip-zk' }); - * builder.build().catch(err => console.error(err)); - * ``` - * - * @example Successful Build Output - * ``` - * ℹ [COMPILE] Found 2 .compact file(s) to compile - * ✔ [COMPILE] [1/2] Compiled AccessControl.compact - * Compactc version: 0.26.0 - * ✔ [COMPILE] [2/2] Compiled MockAccessControl.compact - * Compactc version: 0.26.0 - * ✔ [BUILD] [1/3] Compiling TypeScript - * ✔ [BUILD] [2/3] Copying artifacts - * ✔ [BUILD] [3/3] Copying and cleaning .compact files - * ``` - * - * @example Failed Compilation Output - * ``` - * ℹ [COMPILE] Found 2 .compact file(s) to compile - * ✖ [COMPILE] [1/2] Failed AccessControl.compact - * Compactc version: 0.26.0 - * Error: Expected ';' at line 5 in AccessControl.compact - * ``` * - * @example Failed Build Step Output - * ``` - * ℹ [COMPILE] Found 2 .compact file(s) to compile - * ✔ [COMPILE] [1/2] Compiled AccessControl.compact - * ✔ [COMPILE] [2/2] Compiled MockAccessControl.compact - * ✖ [BUILD] [1/3] Failed Compiling TypeScript - * error TS1005: ';' expected at line 10 in file.ts - * [BUILD] ❌ Build failed: Command failed: tsc --project tsconfig.build.json + * // Library publish: clean dist, hierarchical tree, exclude mocks + archive, copy metadata + * const builder = new CompactBuilder({ + * cleanDist: true, + * hierarchical: true, + * exclude: ['Mock*', '*\/archive\/*'], + * copyToDist: ['package.json', '../README.md'], + * }); + * builder.build().catch(err => console.error(err)); * ``` */ export class CompactBuilder { private readonly options: BuilderOptions; - private readonly steps: Array<{ cmd: string; msg: string; shell?: string }> = - [ - { - cmd: 'tsc --project tsconfig.build.json', - msg: 'Compiling TypeScript', - }, - { - cmd: 'mkdir -p dist/artifacts && cp -Rf src/artifacts/* dist/artifacts/ 2>/dev/null || true', - msg: 'Copying artifacts', - 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', - shell: '/bin/bash', - }, - ]; + private readonly steps: BuildStep[]; /** * Constructs a new CompactBuilder instance. - * @param options - Compiler options (flags, srcDir, outDir, hierarchical, etc.) + * @param options - Compiler + builder options (see {@link BuilderOptions}). */ - constructor(options: CompilerOptions = {}) { + constructor(options: BuilderOptions = {}) { this.options = options; + this.steps = this.buildSteps(); } /** * Factory method to create a CompactBuilder from command-line arguments. - * Reuses CompactCompiler.parseArgs for consistent argument parsing. * * @param args - Array of command-line arguments * @param env - Environment variables (defaults to process.env) @@ -100,12 +69,51 @@ export class CompactBuilder { */ static fromArgs( args: string[], - env: NodeJS.ProcessEnv = process.env, + env: typeof process.env = process.env, ): CompactBuilder { - const options = CompactCompiler.parseArgs(args, env); + const options = CompactBuilder.parseArgs(args, env); return new CompactBuilder(options); } + /** + * Parses command-line arguments into {@link BuilderOptions}. + * Builder-only flags are extracted here; remaining args are forwarded to + * {@link CompactCompiler.parseArgs} for compiler-side parsing. + * + * Builder-only flags (compiler flags like `--hierarchical` and `--exclude` + * are forwarded to {@link CompactCompiler.parseArgs}): + * - `--clean-dist` - rm -rf dist before building + * - `--copy ` - copy an extra file into dist/ (repeatable) + * + * @throws {Error} If `--copy` is provided without a value. + */ + static parseArgs( + args: string[], + env: typeof process.env = process.env, + ): BuilderOptions { + const builderOnly: BuilderOnlyOptions = {}; + const compilerArgs: string[] = []; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg === '--clean-dist') { + builderOnly.cleanDist = true; + } else if (arg === '--copy') { + const value = args[i + 1]; + if (value === undefined || value.startsWith('--')) { + throw new Error('--copy flag requires a path'); + } + (builderOnly.copyToDist ??= []).push(value); + i++; + } else { + compilerArgs.push(arg); + } + } + + const compilerOptions = CompactCompiler.parseArgs(compilerArgs, env); + return { ...compilerOptions, ...builderOnly }; + } + /** * Executes the full build process: compiles .compact files first, then runs build steps. * Displays progress with spinners and outputs results in color. @@ -114,16 +122,91 @@ export class CompactBuilder { * @throws Error if compilation or any build step fails */ public async build(): Promise { - // Run compact compilation as a prerequisite + // Run compact compilation as a prerequisite. CompactCompiler ignores + // BuilderOnlyOptions because they aren't in its resolved shape. const compiler = new CompactCompiler(this.options); await compiler.compile(); - // Proceed with build steps for (const [index, step] of this.steps.entries()) { await this.executeStep(step, index, this.steps.length); } } + /** + * Exposes the resolved build steps. Public for testing/introspection. + */ + public getSteps(): readonly BuildStep[] { + return this.steps; + } + + /** + * Assembles the build-step pipeline from the configured options. + */ + private buildSteps(): BuildStep[] { + const srcDir = this.options.srcDir ?? 'src'; + const quotedSrc = shellQuote(srcDir); + const excludes = buildFindExcludes( + this.options.exclude ?? DEFAULT_EXCLUDE_PATTERNS, + ); + const steps: BuildStep[] = []; + + if (this.options.cleanDist) { + steps.push({ + cmd: 'rm -rf dist && mkdir -p dist', + msg: 'Cleaning dist directory', + shell: '/bin/bash', + }); + } + + steps.push({ + cmd: 'tsc --project tsconfig.build.json', + msg: 'Compiling TypeScript', + }); + + steps.push({ + cmd: `mkdir -p dist/artifacts && cp -Rf ${quotedSrc}/artifacts/* dist/artifacts/ 2>/dev/null || true`, + msg: 'Copying artifacts', + shell: '/bin/bash', + }); + + if (this.options.hierarchical) { + steps.push({ + // biome-ignore-start lint/suspicious/noUselessEscapeInString: shell vars must survive JS template-literal interpolation + cmd: ` + SRC_DIR=${quotedSrc} + find "$SRC_DIR" -type f -name '*.compact' ${excludes} | while read -r file; do + rel_path="\${file#$SRC_DIR/}" + mkdir -p "dist/$(dirname "$rel_path")" + cp "$file" "dist/$rel_path" + done + `, + // biome-ignore-end lint/suspicious/noUselessEscapeInString: shell vars must survive JS template-literal interpolation + msg: 'Copying .compact files (preserving structure)', + shell: '/bin/bash', + }); + } else { + steps.push({ + cmd: `mkdir -p dist && find ${quotedSrc} -type f -name '*.compact' ${excludes} -exec cp {} dist/ \\; 2>/dev/null || true`, + msg: 'Copying .compact files', + shell: '/bin/bash', + }); + } + + const copyTargets = this.options.copyToDist ?? []; + if (copyTargets.length > 0) { + const copyCmds = copyTargets + .map((path) => `cp ${shellQuote(path)} dist/ 2>/dev/null || true`) + .join(' && '); + steps.push({ + cmd: `mkdir -p dist && ${copyCmds}`, + msg: 'Copying additional files to dist', + shell: '/bin/bash', + }); + } + + return steps; + } + /** * Executes a single build step. * Runs the command, shows a spinner, and prints output with indentation. @@ -135,7 +218,7 @@ export class CompactBuilder { * @throws Error if the step fails */ private async executeStep( - step: { cmd: string; msg: string; shell?: string }, + step: BuildStep, index: number, total: number, ): Promise { diff --git a/packages/cli/src/Compiler.ts b/packages/cli/src/Compiler.ts index e20d0fb..e2a322c 100755 --- a/packages/cli/src/Compiler.ts +++ b/packages/cli/src/Compiler.ts @@ -1,491 +1,40 @@ #!/usr/bin/env node -import { exec as execCallback } from 'node:child_process'; import { existsSync } from 'node:fs'; -import { readdir } from 'node:fs/promises'; -import { basename, dirname, join, relative } from 'node:path'; -import { promisify } from 'node:util'; +import { join } from 'node:path'; import chalk from 'chalk'; import ora from 'ora'; +import { CompilerService } from './services/CompilerService.ts'; +import { EnvironmentValidator } from './services/EnvironmentValidator.ts'; +import { FileDiscovery } from './services/FileDiscovery.ts'; +import { UIService } from './services/UIService.ts'; import { - CompactCliNotFoundError, CompilationError, DirectoryNotFoundError, isPromisifiedChildProcessError, } from './types/errors.ts'; - -/** Default source directory containing .compact files */ -const DEFAULT_SRC_DIR = 'src'; -/** Default output directory for compiled artifacts */ -const DEFAULT_OUT_DIR = 'artifacts'; - -/** - * Function type for executing shell commands. - * Allows dependency injection for testing and customization. - * - * @param command - The shell command to execute - * @returns Promise resolving to command output - */ -export type ExecFunction = ( - command: string, -) => Promise<{ stdout: string; stderr: string }>; - -/** - * Configuration options for the Compact compiler CLI. - * - * @interface CompilerOptions - * @example - * ```typescript - * const options: CompilerOptions = { - * flags: '--skip-zk --verbose', - * targetDir: 'security', - * version: '0.26.0', - * hierarchical: false, - * }; - * ``` - */ -export interface CompilerOptions { - /** Compiler flags to pass to the Compact CLI (e.g., '--skip-zk --verbose') */ - flags?: string; - /** Optional subdirectory within srcDir to compile (e.g., 'security', 'token') */ - targetDir?: string; - /** Optional toolchain version to use (e.g., '0.26.0') */ - version?: string; - /** - * Whether to preserve directory structure in artifacts output. - * - `false` (default): Flattened output - `//` - * - `true`: Hierarchical output - `///` - */ - hierarchical?: boolean; - /** Source directory containing .compact files (default: 'src') */ - srcDir?: string; - /** Output directory for compiled artifacts (default: 'artifacts') */ - outDir?: string; -} +import { + type CompilerOptions, + type CompilerServiceOptions, + DEFAULT_OUT_DIR, + DEFAULT_SRC_DIR, + type ExecFunction, +} from './types/options.ts'; + +// Re-export public types and services so consumers keep importing them +// from './Compiler.js' regardless of the internal file layout. +export { CompilerService } from './services/CompilerService.ts'; +export { EnvironmentValidator } from './services/EnvironmentValidator.ts'; +export { FileDiscovery } from './services/FileDiscovery.ts'; +export { UIService } from './services/UIService.ts'; +export type { CompilerOptions, CompilerServiceOptions, ExecFunction }; /** Resolved compiler options with defaults applied */ type ResolvedCompilerOptions = Required< - Pick + Pick > & Pick; -/** - * Service responsible for validating the Compact CLI environment. - * Checks CLI availability, retrieves version information, and ensures - * the toolchain is properly configured before compilation. - * - * @class EnvironmentValidator - * @example - * ```typescript - * const validator = new EnvironmentValidator(); - * await validator.validate('0.26.0'); - * const version = await validator.getDevToolsVersion(); - * ``` - */ -export class EnvironmentValidator { - private execFn: ExecFunction; - - /** - * Creates a new EnvironmentValidator instance. - * - * @param execFn - Function to execute shell commands (defaults to promisified child_process.exec) - */ - constructor(execFn: ExecFunction = promisify(execCallback)) { - this.execFn = execFn; - } - - /** - * Checks if the Compact CLI is available in the system PATH. - * - * @returns Promise resolving to true if CLI is available, false otherwise - * @example - * ```typescript - * const isAvailable = await validator.checkCompactAvailable(); - * if (!isAvailable) { - * throw new Error('Compact CLI not found'); - * } - * ``` - */ - async checkCompactAvailable(): Promise { - try { - await this.execFn('compact --version'); - return true; - } catch { - return false; - } - } - - /** - * Retrieves the version of the Compact developer tools. - * - * @returns Promise resolving to the version string - * @throws {Error} If the CLI is not available or command fails - * @example - * ```typescript - * const version = await validator.getDevToolsVersion(); - * console.log(`Using Compact ${version}`); - * ``` - */ - async getDevToolsVersion(): Promise { - const { stdout } = await this.execFn('compact --version'); - return stdout.trim(); - } - - /** - * Retrieves the version of the Compact toolchain/compiler. - * - * @param version - Optional specific toolchain version to query - * @returns Promise resolving to the toolchain version string - * @throws {Error} If the CLI is not available or command fails - * @example - * ```typescript - * const toolchainVersion = await validator.getToolchainVersion('0.26.0'); - * console.log(`Toolchain: ${toolchainVersion}`); - * ``` - */ - async getToolchainVersion(version?: string): Promise { - const versionFlag = version ? `+${version}` : ''; - const { stdout } = await this.execFn( - `compact compile ${versionFlag} --version`, - ); - return stdout.trim(); - } - - /** - * Validates the entire Compact environment and ensures it's ready for compilation. - * Checks CLI availability and retrieves version information. - * - * @param version - Optional specific toolchain version to validate - * @throws {CompactCliNotFoundError} If the Compact CLI is not available - * @throws {Error} If version commands fail - * @example - * ```typescript - * try { - * await validator.validate('0.26.0'); - * console.log('Environment validated successfully'); - * } catch (error) { - * if (error instanceof CompactCliNotFoundError) { - * console.error('Please install Compact CLI'); - * } - * } - * ``` - */ - async validate( - version?: string, - ): Promise<{ devToolsVersion: string; toolchainVersion: string }> { - const isAvailable = await this.checkCompactAvailable(); - if (!isAvailable) { - throw new CompactCliNotFoundError( - "'compact' CLI not found in PATH. Please install the Compact developer tools.", - ); - } - - const devToolsVersion = await this.getDevToolsVersion(); - const toolchainVersion = await this.getToolchainVersion(version); - - return { devToolsVersion, toolchainVersion }; - } -} - -/** - * Service responsible for discovering .compact files in the source directory. - * Recursively scans directories and filters for .compact file extensions. - * - * @class FileDiscovery - * @example - * ```typescript - * const discovery = new FileDiscovery('src'); - * const files = await discovery.getCompactFiles('src/security'); - * console.log(`Found ${files.length} .compact files`); - * ``` - */ -export class FileDiscovery { - private srcDir: string; - - /** - * Creates a new FileDiscovery instance. - * - * @param srcDir - Base source directory for relative path calculation (default: 'src') - */ - constructor(srcDir: string = DEFAULT_SRC_DIR) { - this.srcDir = srcDir; - } - - /** - * Recursively discovers all .compact files in a directory. - * Returns relative paths from the srcDir for consistent processing. - * - * @param dir - Directory path to search (relative or absolute) - * @returns Promise resolving to array of relative file paths - * @example - * ```typescript - * const files = await discovery.getCompactFiles('src'); - * // Returns: ['contracts/Token.compact', 'security/AccessControl.compact'] - * ``` - */ - async getCompactFiles(dir: string): Promise { - try { - const dirents = await readdir(dir, { withFileTypes: true }); - const filePromises = dirents.map(async (entry) => { - const fullPath = join(dir, entry.name); - try { - if (entry.isDirectory()) { - return await this.getCompactFiles(fullPath); - } - - if (entry.isFile() && fullPath.endsWith('.compact')) { - return [relative(this.srcDir, fullPath)]; - } - return []; - } catch (err) { - // biome-ignore lint/suspicious/noConsole: Needed to display error and file path - console.warn(`Error accessing ${fullPath}:`, err); - return []; - } - }); - - const results = await Promise.all(filePromises); - return results.flat(); - } catch (err) { - // biome-ignore lint/suspicious/noConsole: Needed to display error and dir path - console.error(`Failed to read dir: ${dir}`, err); - return []; - } - } -} - -/** - * Options for configuring the CompilerService. - * A subset of CompilerOptions relevant to the compilation process. - */ -export type CompilerServiceOptions = Pick< - CompilerOptions, - 'hierarchical' | 'srcDir' | 'outDir' ->; - -/** Resolved options for CompilerService with defaults applied */ -type ResolvedCompilerServiceOptions = Required; - -/** - * Service responsible for compiling individual .compact files. - * Handles command construction, execution, and error processing. - * - * @class CompilerService - * @example - * ```typescript - * const compiler = new CompilerService(); - * const result = await compiler.compileFile( - * 'contracts/Token.compact', - * '--skip-zk --verbose', - * '0.26.0' - * ); - * console.log('Compilation output:', result.stdout); - * ``` - */ -export class CompilerService { - private execFn: ExecFunction; - private options: ResolvedCompilerServiceOptions; - - /** - * Creates a new CompilerService instance. - * - * @param execFn - Function to execute shell commands (defaults to promisified child_process.exec) - * @param options - Compiler service options - */ - constructor( - execFn: ExecFunction = promisify(execCallback), - options: CompilerServiceOptions = {}, - ) { - this.execFn = execFn; - this.options = { - hierarchical: options.hierarchical ?? false, - srcDir: options.srcDir ?? DEFAULT_SRC_DIR, - outDir: options.outDir ?? DEFAULT_OUT_DIR, - }; - } - - /** - * Compiles a single .compact file using the Compact CLI. - * Constructs the appropriate command with flags and version, then executes it. - * - * By default, uses flattened output structure where all artifacts go to `//`. - * When `hierarchical` is true, preserves source directory structure: `///`. - * - * @param file - Relative path to the .compact file from srcDir - * @param flags - Space-separated compiler flags (e.g., '--skip-zk --verbose') - * @param version - Optional specific toolchain version to use - * @returns Promise resolving to compilation output (stdout/stderr) - * @throws {CompilationError} If compilation fails for any reason - * @example - * ```typescript - * try { - * const result = await compiler.compileFile( - * 'security/AccessControl.compact', - * '--skip-zk', - * '0.26.0' - * ); - * console.log('Success:', result.stdout); - * } catch (error) { - * if (error instanceof CompilationError) { - * console.error('Compilation failed for', error.file); - * } - * } - * ``` - */ - async compileFile( - file: string, - flags: string, - version?: string, - ): Promise<{ stdout: string; stderr: string }> { - const inputPath = join(this.options.srcDir, file); - const fileDir = dirname(file); - const fileName = basename(file, '.compact'); - - // Flattened (default): // - // Hierarchical: /// - const outputDir = - this.options.hierarchical && fileDir !== '.' - ? join(this.options.outDir, fileDir, fileName) - : join(this.options.outDir, fileName); - - const versionFlag = version ? `+${version}` : ''; - const flagsStr = flags ? ` ${flags}` : ''; - const command = `compact compile${versionFlag ? ` ${versionFlag}` : ''}${flagsStr} "${inputPath}" "${outputDir}"`; - - try { - return await this.execFn(command); - } catch (error: unknown) { - let message: string; - - if (error instanceof Error) { - message = error.message; - } else { - message = String(error); // fallback for strings, objects, numbers, etc. - } - - throw new CompilationError( - `Failed to compile ${file}: ${message}`, - file, - error, - ); - } - } -} - -/** - * Utility service for handling user interface output and formatting. - * Provides consistent styling and formatting for compiler messages and output. - * - * @class UIService - * @example - * ```typescript - * UIService.displayEnvInfo('compact 0.1.0', 'Compactc 0.26.0', 'security'); - * UIService.printOutput('Compilation successful', chalk.green); - * ``` - */ -export const UIService = { - /** - * Prints formatted output with consistent indentation and coloring. - * Filters empty lines and adds consistent indentation for readability. - * - * @param output - Raw output text to format - * @param colorFn - Chalk color function for styling - * @example - * ```typescript - * UIService.printOutput(stdout, chalk.cyan); - * UIService.printOutput(stderr, chalk.red); - * ``` - */ - printOutput(output: string, colorFn: (text: string) => string): void { - const lines = output - .split('\n') - .filter((line) => line.trim() !== '') - .map((line) => ` ${line}`); - console.log(colorFn(lines.join('\n'))); - }, - - /** - * Displays environment information including tool versions and configuration. - * Shows developer tools version, toolchain version, and optional settings. - * - * @param devToolsVersion - Version string of the Compact developer tools - * @param toolchainVersion - Version string of the Compact toolchain/compiler - * @param targetDir - Optional target directory being compiled - * @param version - Optional specific version being used - * @example - * ```typescript - * UIService.displayEnvInfo( - * 'compact 0.1.0', - * 'Compactc version: 0.26.0', - * 'security', - * '0.26.0' - * ); - * ``` - */ - displayEnvInfo( - devToolsVersion: string, - toolchainVersion: string, - targetDir?: string, - version?: string, - ): void { - const spinner = ora(); - - if (targetDir) { - spinner.info(chalk.blue(`[COMPILE] TARGET_DIR: ${targetDir}`)); - } - - spinner.info( - chalk.blue(`[COMPILE] Compact developer tools: ${devToolsVersion}`), - ); - spinner.info( - chalk.blue(`[COMPILE] Compact toolchain: ${toolchainVersion}`), - ); - - if (version) { - spinner.info(chalk.blue(`[COMPILE] Using toolchain version: ${version}`)); - } - }, - - /** - * Displays compilation start message with file count and optional location. - * - * @param fileCount - Number of files to be compiled - * @param targetDir - Optional target directory being compiled - * @example - * ```typescript - * UIService.showCompilationStart(5, 'security'); - * // Output: "Found 5 .compact file(s) to compile in security/" - * ``` - */ - showCompilationStart(fileCount: number, targetDir?: string): void { - const searchLocation = targetDir ? ` in ${targetDir}/` : ''; - const spinner = ora(); - spinner.info( - chalk.blue( - `[COMPILE] Found ${fileCount} .compact file(s) to compile${searchLocation}`, - ), - ); - }, - - /** - * Displays a warning message when no .compact files are found. - * - * @param targetDir - Optional target directory that was searched - * @example - * ```typescript - * UIService.showNoFiles('security'); - * // Output: "No .compact files found in security/." - * ``` - */ - showNoFiles(targetDir?: string): void { - const searchLocation = targetDir ? `${targetDir}/` : ''; - const spinner = ora(); - spinner.warn( - chalk.yellow(`[COMPILE] No .compact files found in ${searchLocation}.`), - ); - }, -}; - /** * Main compiler class that orchestrates the compilation process. * Coordinates environment validation, file discovery, and compilation services @@ -499,7 +48,6 @@ export const UIService = { * - Environment variable integration * - Configurable artifact output structure (flattened or hierarchical) * - * @class CompactCompiler * @example * ```typescript * // Basic usage with options object (flattened artifacts by default) @@ -538,25 +86,7 @@ export class CompactCompiler { * Creates a new CompactCompiler instance with specified configuration. * * @param options - Compiler configuration options - * @param execFn - Optional custom exec function for dependency injection - * @example - * ```typescript - * // Compile all files with flags (flattened artifacts) - * const compiler = new CompactCompiler({ flags: '--skip-zk --verbose' }); - * - * // Compile specific directory - * const compiler = new CompactCompiler({ targetDir: 'security' }); - * - * // Compile with specific version - * const compiler = new CompactCompiler({ flags: '--skip-zk', version: '0.26.0' }); - * - * // Compile with hierarchical artifacts structure - * const compiler = new CompactCompiler({ flags: '--skip-zk', hierarchical: true }); - * - * // For testing with custom exec function - * const mockExec = vi.fn(); - * const compiler = new CompactCompiler({}, mockExec); - * ``` + * @param execFn - Optional custom exec function for dependency injection */ constructor(options: CompilerOptions = {}, execFn?: ExecFunction) { this.options = { @@ -566,9 +96,13 @@ export class CompactCompiler { hierarchical: options.hierarchical ?? false, srcDir: options.srcDir ?? DEFAULT_SRC_DIR, outDir: options.outDir ?? DEFAULT_OUT_DIR, + exclude: options.exclude ?? [], }; this.environmentValidator = new EnvironmentValidator(execFn); - this.fileDiscovery = new FileDiscovery(this.options.srcDir); + this.fileDiscovery = new FileDiscovery( + this.options.srcDir, + this.options.exclude, + ); this.compilerService = new CompilerService(execFn, { hierarchical: this.options.hierarchical, srcDir: this.options.srcDir, @@ -584,18 +118,19 @@ export class CompactCompiler { * - `--src ` - Source directory containing .compact files (default: 'src') * - `--out ` - Output directory for artifacts (default: 'artifacts') * - `--hierarchical` - Preserve source directory structure in artifacts output + * - `--exclude ` - Skip `.compact` files matching the glob pattern (repeatable) * - `+` - Use specific toolchain version * - Other arguments - Treated as compiler flags * - `SKIP_ZK=true` environment variable - Adds --skip-zk flag * * @param args - Array of command-line arguments - * @param env - Environment variables (defaults to process.env) + * @param env - Environment variables (defaults to process.env) * @returns Parsed CompilerOptions object - * @throws {Error} If --dir, --src, or --out flag is provided without a value + * @throws {Error} If --dir, --src, --out, or --exclude is provided without a value */ static parseArgs( args: string[], - env: NodeJS.ProcessEnv = process.env, + env: typeof process.env = process.env, ): CompilerOptions { const options: CompilerOptions = { hierarchical: false, @@ -636,6 +171,15 @@ export class CompactCompiler { } } else if (args[i] === '--hierarchical') { options.hierarchical = true; + } else if (args[i] === '--exclude') { + const valueExists = + i + 1 < args.length && !args[i + 1].startsWith('--'); + if (valueExists) { + (options.exclude ??= []).push(args[i + 1]); + i++; + } else { + throw new Error('--exclude flag requires a pattern'); + } } else if (args[i].startsWith('+')) { options.version = args[i].slice(1); } else { @@ -652,56 +196,16 @@ export class CompactCompiler { /** * Factory method to create a CompactCompiler from command-line arguments. - * Parses various argument formats including flags, directories, versions, and environment variables. - * - * Supported argument patterns: - * - `--dir ` - Target specific subdirectory within srcDir - * - `--src ` - Source directory containing .compact files (default: 'src') - * - `--out ` - Output directory for artifacts (default: 'artifacts') - * - `--hierarchical` - Preserve source directory structure in artifacts output - * - `+` - Use specific toolchain version - * - Other arguments - Treated as compiler flags - * - `SKIP_ZK=true` environment variable - Adds --skip-zk flag + * See {@link CompactCompiler.parseArgs} for the supported argument shapes. * * @param args - Array of command-line arguments - * @param env - Environment variables (defaults to process.env) + * @param env - Environment variables (defaults to process.env) * @returns New CompactCompiler instance configured from arguments - * @throws {Error} If --dir, --src, or --out flag is provided without a value - * @example - * ```typescript - * // Parse command line: compact-compiler --dir security --skip-zk +0.26.0 - * const compiler = CompactCompiler.fromArgs([ - * '--dir', 'security', - * '--skip-zk', - * '+0.26.0' - * ]); - * - * // With custom source and output directories - * const compiler = CompactCompiler.fromArgs([ - * '--src', 'contracts', - * '--out', 'build/artifacts', - * '--skip-zk' - * ]); - * - * // With hierarchical artifacts structure - * const compiler = CompactCompiler.fromArgs([ - * '--hierarchical', - * '--skip-zk' - * ]); - * - * // With environment variable - * const compiler = CompactCompiler.fromArgs( - * ['--dir', 'token'], - * { SKIP_ZK: 'true' } - * ); - * - * // Empty args with environment - * const compiler = CompactCompiler.fromArgs([], { SKIP_ZK: 'true' }); - * ``` + * @throws {Error} If --dir, --src, --out, or --exclude is provided without a value */ static fromArgs( args: string[], - env: NodeJS.ProcessEnv = process.env, + env: typeof process.env = process.env, ): CompactCompiler { const options = CompactCompiler.parseArgs(args, env); return new CompactCompiler(options); @@ -709,27 +213,9 @@ export class CompactCompiler { /** * Validates the compilation environment and displays version information. - * Performs environment validation, retrieves toolchain versions, and shows configuration details. - * - * Process: - * - * 1. Validates CLI availability and toolchain compatibility - * 2. Retrieves developer tools and compiler versions - * 3. Displays environment configuration information * * @throws {CompactCliNotFoundError} If Compact CLI is not available in PATH * @throws {Error} If version retrieval or other validation steps fail - * @example - * ```typescript - * try { - * await compiler.validateEnvironment(); - * console.log('Environment ready for compilation'); - * } catch (error) { - * if (error instanceof CompactCliNotFoundError) { - * console.error('Please install Compact CLI'); - * } - * } - * ``` */ async validateEnvironment(): Promise { const { devToolsVersion, toolchainVersion } = @@ -745,30 +231,9 @@ export class CompactCompiler { /** * Main compilation method that orchestrates the entire compilation process. * - * Process flow: - * 1. Validates environment and shows configuration - * 2. Discovers .compact files in target directory - * 3. Compiles each file with progress reporting - * 4. Handles errors and provides user feedback - * * @throws {CompactCliNotFoundError} If Compact CLI is not available * @throws {DirectoryNotFoundError} If target directory doesn't exist * @throws {CompilationError} If any file compilation fails - * @example - * ```typescript - * const compiler = new CompactCompiler('--skip-zk', 'security'); - * - * try { - * await compiler.compile(); - * console.log('All files compiled successfully'); - * } catch (error) { - * if (error instanceof DirectoryNotFoundError) { - * console.error(`Directory not found: ${error.directory}`); - * } else if (error instanceof CompilationError) { - * console.error(`Failed to compile: ${error.file}`); - * } - * } - * ``` */ async compile(): Promise { await this.validateEnvironment(); @@ -801,13 +266,11 @@ export class CompactCompiler { /** * Compiles a single file with progress reporting and error handling. - * Private method used internally by the main compile() method. * - * @param file - Relative path to the .compact file + * @param file - Relative path to the .compact file * @param index - Current file index (0-based) for progress tracking * @param total - Total number of files being compiled * @throws {CompilationError} If compilation fails - * @private */ private async compileFile( file: string, diff --git a/packages/cli/src/runBuilder.ts b/packages/cli/src/runBuilder.ts index 09e589a..16b8490 100644 --- a/packages/cli/src/runBuilder.ts +++ b/packages/cli/src/runBuilder.ts @@ -8,27 +8,38 @@ import { CompactBuilder } from './Builder.ts'; * Executes the Compact builder CLI. * Builds projects using the `CompactBuilder` class with provided options, including compilation and additional steps. * - * Supports compiler options: - * - `--dir ` - Compile specific subdirectory - * - `--src ` - Source directory (default: src) - * - `--out ` - Output directory (default: artifacts) - * - `--hierarchical` - Preserve directory structure in output - * - `+` - Use specific toolchain version + * Compiler options (forwarded to `compact-compiler`): + * - `--dir ` - Compile specific subdirectory within srcDir + * - `--src ` - Source directory (default: src) + * - `--out ` - Output directory for artifacts (default: artifacts) + * - `--hierarchical` - Preserve source directory structure in BOTH the + * compiler artifacts output AND the builder's + * .compact copy into dist/ (default off: flat in both) + * - `--exclude ` - Skip .compact files matching pattern, in BOTH the + * compiler's file discovery AND the builder's + * .compact copy (repeatable). When unset, the builder + * falls back to ['Mock*', '*.mock.compact']; the + * compiler defaults to no excludes. + * - `+` - Use specific toolchain version + * + * Builder-only options (control dist/ layout): + * - `--clean-dist` - rm -rf dist before building (default off) + * - `--copy ` - copy an extra file into dist/ for distribution (repeatable; e.g. package.json) * * @example * ```bash + * # Minimal build * npx compact-builder + * + * # Custom source/output dirs * npx compact-builder --src contracts --out build - * ``` - * Expected output: - * ``` - * ℹ [BUILD] Compact builder started - * ℹ [COMPILE] Found 1 .compact file(s) to compile - * ✔ [COMPILE] [1/1] Compiled Foo.compact - * Compactc version: 0.26.0 - * ✔ [BUILD] [1/3] Compiling TypeScript - * ✔ [BUILD] [2/3] Copying artifacts - * ✔ [BUILD] [3/3] Copying and cleaning .compact files + * + * # Library-publish build: clean dist, hierarchical layout, exclude mocks + archive, ship metadata + * npx compact-builder \ + * --clean-dist \ + * --hierarchical \ + * --exclude 'Mock*' --exclude '*\/archive\/*' \ + * --copy package.json --copy ../README.md * ``` */ async function runBuilder(): Promise { diff --git a/packages/cli/src/services/CompilerService.ts b/packages/cli/src/services/CompilerService.ts new file mode 100644 index 0000000..ad6cd52 --- /dev/null +++ b/packages/cli/src/services/CompilerService.ts @@ -0,0 +1,102 @@ +import { exec as execCallback } from 'node:child_process'; +import { basename, dirname, join } from 'node:path'; +import { promisify } from 'node:util'; +import { CompilationError } from '../types/errors.ts'; +import { + type CompilerServiceOptions, + DEFAULT_OUT_DIR, + DEFAULT_SRC_DIR, + type ExecFunction, +} from '../types/options.ts'; + +/** Resolved options for CompilerService with defaults applied */ +type ResolvedCompilerServiceOptions = Required; + +/** + * Service responsible for compiling individual .compact files. + * Handles command construction, execution, and error processing. + * + * @example + * ```typescript + * const compiler = new CompilerService(); + * const result = await compiler.compileFile( + * 'contracts/Token.compact', + * '--skip-zk --verbose', + * '0.26.0' + * ); + * ``` + */ +export class CompilerService { + private execFn: ExecFunction; + private options: ResolvedCompilerServiceOptions; + + /** + * Creates a new CompilerService instance. + * + * @param execFn - Function to execute shell commands (defaults to promisified child_process.exec) + * @param options - Compiler service options + */ + constructor( + execFn: ExecFunction = promisify(execCallback), + options: CompilerServiceOptions = {}, + ) { + this.execFn = execFn; + this.options = { + hierarchical: options.hierarchical ?? false, + srcDir: options.srcDir ?? DEFAULT_SRC_DIR, + outDir: options.outDir ?? DEFAULT_OUT_DIR, + }; + } + + /** + * Compiles a single .compact file using the Compact CLI. + * Constructs the appropriate command with flags and version, then executes it. + * + * By default, uses flattened output structure where all artifacts go to `//`. + * When `hierarchical` is true, preserves source directory structure: `///`. + * + * @param file - Relative path to the .compact file from srcDir + * @param flags - Space-separated compiler flags (e.g., '--skip-zk --verbose') + * @param version - Optional specific toolchain version to use + * @returns Promise resolving to compilation output (stdout/stderr) + * @throws {CompilationError} If compilation fails for any reason + */ + async compileFile( + file: string, + flags: string, + version?: string, + ): Promise<{ stdout: string; stderr: string }> { + const inputPath = join(this.options.srcDir, file); + const fileDir = dirname(file); + const fileName = basename(file, '.compact'); + + // Flattened (default): // + // Hierarchical: /// + const outputDir = + this.options.hierarchical && fileDir !== '.' + ? join(this.options.outDir, fileDir, fileName) + : join(this.options.outDir, fileName); + + const versionFlag = version ? `+${version}` : ''; + const flagsStr = flags ? ` ${flags}` : ''; + const command = `compact compile${versionFlag ? ` ${versionFlag}` : ''}${flagsStr} "${inputPath}" "${outputDir}"`; + + try { + return await this.execFn(command); + } catch (error: unknown) { + let message: string; + + if (error instanceof Error) { + message = error.message; + } else { + message = String(error); // fallback for strings, objects, numbers, etc. + } + + throw new CompilationError( + `Failed to compile ${file}: ${message}`, + file, + error, + ); + } + } +} diff --git a/packages/cli/src/services/EnvironmentValidator.ts b/packages/cli/src/services/EnvironmentValidator.ts new file mode 100644 index 0000000..08a3b41 --- /dev/null +++ b/packages/cli/src/services/EnvironmentValidator.ts @@ -0,0 +1,93 @@ +import { exec as execCallback } from 'node:child_process'; +import { promisify } from 'node:util'; +import { CompactCliNotFoundError } from '../types/errors.ts'; +import type { ExecFunction } from '../types/options.ts'; + +/** + * Service responsible for validating the Compact CLI environment. + * Checks CLI availability, retrieves version information, and ensures + * the toolchain is properly configured before compilation. + * + * @example + * ```typescript + * const validator = new EnvironmentValidator(); + * await validator.validate('0.26.0'); + * const version = await validator.getDevToolsVersion(); + * ``` + */ +export class EnvironmentValidator { + private execFn: ExecFunction; + + /** + * Creates a new EnvironmentValidator instance. + * + * @param execFn - Function to execute shell commands (defaults to promisified child_process.exec) + */ + constructor(execFn: ExecFunction = promisify(execCallback)) { + this.execFn = execFn; + } + + /** + * Checks if the Compact CLI is available in the system PATH. + * + * @returns Promise resolving to true if CLI is available, false otherwise + */ + async checkCompactAvailable(): Promise { + try { + await this.execFn('compact --version'); + return true; + } catch { + return false; + } + } + + /** + * Retrieves the version of the Compact developer tools. + * + * @returns Promise resolving to the version string + * @throws {Error} If the CLI is not available or command fails + */ + async getDevToolsVersion(): Promise { + const { stdout } = await this.execFn('compact --version'); + return stdout.trim(); + } + + /** + * Retrieves the version of the Compact toolchain/compiler. + * + * @param version - Optional specific toolchain version to query + * @returns Promise resolving to the toolchain version string + * @throws {Error} If the CLI is not available or command fails + */ + async getToolchainVersion(version?: string): Promise { + const versionFlag = version ? `+${version}` : ''; + const { stdout } = await this.execFn( + `compact compile ${versionFlag} --version`, + ); + return stdout.trim(); + } + + /** + * Validates the entire Compact environment and ensures it's ready for compilation. + * Checks CLI availability and retrieves version information. + * + * @param version - Optional specific toolchain version to validate + * @throws {CompactCliNotFoundError} If the Compact CLI is not available + * @throws {Error} If version commands fail + */ + async validate( + version?: string, + ): Promise<{ devToolsVersion: string; toolchainVersion: string }> { + const isAvailable = await this.checkCompactAvailable(); + if (!isAvailable) { + throw new CompactCliNotFoundError( + "'compact' CLI not found in PATH. Please install the Compact developer tools.", + ); + } + + const devToolsVersion = await this.getDevToolsVersion(); + const toolchainVersion = await this.getToolchainVersion(version); + + return { devToolsVersion, toolchainVersion }; + } +} diff --git a/packages/cli/src/services/FileDiscovery.ts b/packages/cli/src/services/FileDiscovery.ts new file mode 100644 index 0000000..f451a6f --- /dev/null +++ b/packages/cli/src/services/FileDiscovery.ts @@ -0,0 +1,81 @@ +import { readdir } from 'node:fs/promises'; +import { join, relative } from 'node:path'; +import { DEFAULT_SRC_DIR } from '../types/options.ts'; +import { isExcluded } from '../utils.ts'; + +/** + * Service responsible for discovering .compact files in the source directory. + * Recursively scans directories and filters for .compact file extensions, + * applying user-supplied exclude patterns. + * + * @example + * ```typescript + * const discovery = new FileDiscovery('src', ['Mock*']); + * const files = await discovery.getCompactFiles('src/security'); + * ``` + */ +export class FileDiscovery { + private srcDir: string; + private excludes: readonly string[]; + + /** + * Creates a new FileDiscovery instance. + * + * @param srcDir - Base source directory for relative path calculation (default: 'src') + * @param excludes - Glob-style patterns of `.compact` files to skip. + * Patterns containing `/` match against the full path + * (as `find ` would emit it); others match against + * the filename only. Default: `[]`. + */ + constructor( + srcDir: string = DEFAULT_SRC_DIR, + excludes: readonly string[] = [], + ) { + this.srcDir = srcDir; + this.excludes = excludes; + } + + /** + * Recursively discovers all .compact files in a directory. + * Returns relative paths from the srcDir for consistent processing. + * + * @param dir - Directory path to search (relative or absolute) + * @returns Promise resolving to array of relative file paths + */ + async getCompactFiles(dir: string): Promise { + try { + const dirents = await readdir(dir, { withFileTypes: true }); + const filePromises = dirents.map(async (entry) => { + const fullPath = join(dir, entry.name); + try { + if (entry.isDirectory()) { + return await this.getCompactFiles(fullPath); + } + + if (entry.isFile() && fullPath.endsWith('.compact')) { + const relPath = relative(this.srcDir, fullPath); + // Match path-style patterns against fullPath (i.e. the path that + // `find srcDir` would emit) so users can write `*/archive/*` etc., + // identical to what they'd pass to `find -path`. + if (isExcluded(entry.name, fullPath, this.excludes)) { + return []; + } + return [relPath]; + } + return []; + } catch (err) { + // biome-ignore lint/suspicious/noConsole: Needed to display error and file path + console.warn(`Error accessing ${fullPath}:`, err); + return []; + } + }); + + const results = await Promise.all(filePromises); + return results.flat(); + } catch (err) { + // biome-ignore lint/suspicious/noConsole: Needed to display error and dir path + console.error(`Failed to read dir: ${dir}`, err); + return []; + } + } +} diff --git a/packages/cli/src/services/UIService.ts b/packages/cli/src/services/UIService.ts new file mode 100644 index 0000000..6839130 --- /dev/null +++ b/packages/cli/src/services/UIService.ts @@ -0,0 +1,91 @@ +import chalk from 'chalk'; +import ora from 'ora'; + +/** + * Utility service for handling user interface output and formatting. + * Provides consistent styling and formatting for compiler messages and output. + * + * @example + * ```typescript + * UIService.displayEnvInfo('compact 0.1.0', 'Compactc 0.26.0', 'security'); + * UIService.printOutput('Compilation successful', chalk.green); + * ``` + */ +export const UIService = { + /** + * Prints formatted output with consistent indentation and coloring. + * Filters empty lines and adds consistent indentation for readability. + * + * @param output - Raw output text to format + * @param colorFn - Chalk color function for styling + */ + printOutput(output: string, colorFn: (text: string) => string): void { + const lines = output + .split('\n') + .filter((line) => line.trim() !== '') + .map((line) => ` ${line}`); + console.log(colorFn(lines.join('\n'))); + }, + + /** + * Displays environment information including tool versions and configuration. + * Shows developer tools version, toolchain version, and optional settings. + * + * @param devToolsVersion - Version string of the Compact developer tools + * @param toolchainVersion - Version string of the Compact toolchain/compiler + * @param targetDir - Optional target directory being compiled + * @param version - Optional specific version being used + */ + displayEnvInfo( + devToolsVersion: string, + toolchainVersion: string, + targetDir?: string, + version?: string, + ): void { + const spinner = ora(); + + if (targetDir) { + spinner.info(chalk.blue(`[COMPILE] TARGET_DIR: ${targetDir}`)); + } + + spinner.info( + chalk.blue(`[COMPILE] Compact developer tools: ${devToolsVersion}`), + ); + spinner.info( + chalk.blue(`[COMPILE] Compact toolchain: ${toolchainVersion}`), + ); + + if (version) { + spinner.info(chalk.blue(`[COMPILE] Using toolchain version: ${version}`)); + } + }, + + /** + * Displays compilation start message with file count and optional location. + * + * @param fileCount - Number of files to be compiled + * @param targetDir - Optional target directory being compiled + */ + showCompilationStart(fileCount: number, targetDir?: string): void { + const searchLocation = targetDir ? ` in ${targetDir}/` : ''; + const spinner = ora(); + spinner.info( + chalk.blue( + `[COMPILE] Found ${fileCount} .compact file(s) to compile${searchLocation}`, + ), + ); + }, + + /** + * Displays a warning message when no .compact files are found. + * + * @param targetDir - Optional target directory that was searched + */ + showNoFiles(targetDir?: string): void { + const searchLocation = targetDir ? `${targetDir}/` : ''; + const spinner = ora(); + spinner.warn( + chalk.yellow(`[COMPILE] No .compact files found in ${searchLocation}.`), + ); + }, +}; diff --git a/packages/cli/src/types/options.ts b/packages/cli/src/types/options.ts new file mode 100644 index 0000000..87d0711 --- /dev/null +++ b/packages/cli/src/types/options.ts @@ -0,0 +1,137 @@ +/** + * Shared option/type definitions and defaults for the Compact CLI tools. + * + * This module is the canonical home for cross-cutting types (`CompilerOptions`, + * `BuilderOptions`, `ExecFunction`, …) plus their default constants. Splitting + * them out keeps `Compiler.ts` and `Builder.ts` focused on behaviour rather + * than data shapes. + */ + +/** Default source directory containing .compact files. */ +export const DEFAULT_SRC_DIR = 'src'; + +/** Default output directory for compiled artifacts. */ +export const DEFAULT_OUT_DIR = 'artifacts'; + +/** + * Default `.compact` glob patterns the builder strips from `dist/` when the + * user hasn't supplied an explicit `--exclude` list. Covers both common mock + * naming conventions. + */ +export const DEFAULT_EXCLUDE_PATTERNS: readonly string[] = [ + 'Mock*', + '*.mock.compact', +]; + +/** + * Function type for executing shell commands. + * Allows dependency injection for testing and customization. + * + * @param command - The shell command to execute + * @returns Promise resolving to command output + */ +export type ExecFunction = ( + command: string, +) => Promise<{ stdout: string; stderr: string }>; + +/** + * Configuration options for the Compact compiler CLI. + * + * @example + * ```typescript + * const options: CompilerOptions = { + * flags: '--skip-zk --verbose', + * targetDir: 'security', + * version: '0.26.0', + * hierarchical: false, + * }; + * ``` + */ +export interface CompilerOptions { + /** Compiler flags to pass to the Compact CLI (e.g., '--skip-zk --verbose') */ + flags?: string; + /** Optional subdirectory within srcDir to compile (e.g., 'security', 'token') */ + targetDir?: string; + /** Optional toolchain version to use (e.g., '0.26.0') */ + version?: string; + /** + * Whether to preserve directory structure in artifacts output. + * - `false` (default): Flattened output - `//` + * - `true`: Hierarchical output - `///` + */ + hierarchical?: boolean; + /** Source directory containing .compact files (default: 'src') */ + srcDir?: string; + /** Output directory for compiled artifacts (default: 'artifacts') */ + outDir?: string; + /** + * Glob-style patterns to exclude `.compact` files from both the compiler's + * file discovery and the builder's `.compact` copy step. + * - Patterns without `/` match against the filename only (e.g. `'Mock*'`). + * - Patterns with `/` match against the path as `find ` would emit + * it (e.g. `'*\/archive\/*'`, matching `src/archive/Foo.compact`). This is + * the same semantic as `find -path ''`. + * + * Default: `undefined` (no excludes for the compiler). The builder + * substitutes its own default ({@link DEFAULT_EXCLUDE_PATTERNS}) when + * undefined; pass an explicit `[]` to disable that too. + */ + exclude?: string[]; +} + +/** + * Subset of {@link CompilerOptions} consumed by `CompilerService` when + * compiling individual files. + */ +export type CompilerServiceOptions = Pick< + CompilerOptions, + 'hierarchical' | 'srcDir' | 'outDir' +>; + +/** + * Builder-only configuration options that don't apply to the compiler. + * + * These control the *distribution* layout produced by `compact-builder`, + * letting consumers ship a publishable `dist/` directory matching their + * package's conventions (e.g. copying metadata files for npm publish). + * + * Two inherited {@link CompilerOptions} fields also affect builder behaviour: + * - `hierarchical` — drives both compiler artifacts layout AND `.compact` copy layout. + * - `exclude` — drives both compiler file discovery AND `.compact` copy filtering. + * When `exclude` is undefined, the builder substitutes its own default + * ({@link DEFAULT_EXCLUDE_PATTERNS}) so mocks are stripped from the dist + * even when the compiler is told to consume them. + */ +export interface BuilderOnlyOptions { + /** + * If true, runs `rm -rf dist && mkdir -p dist` before building. + * Use when you want a fully clean `dist/` on every build. + * @default false + */ + cleanDist?: boolean; + /** + * Additional file paths to copy into `dist/` for distribution + * (e.g. `['package.json', '../README.md']`). Paths are relative to cwd. + * Each entry is copied individually with `cp dist/`. + * @default [] + */ + copyToDist?: string[]; +} + +/** + * Configuration options for the Builder CLI. + * Extends {@link CompilerOptions} with builder-only distribution controls. + */ +export type BuilderOptions = CompilerOptions & BuilderOnlyOptions; + +/** + * Single build step executed by `CompactBuilder`. + */ +export interface BuildStep { + /** Shell command to execute. */ + cmd: string; + /** Human-readable progress message. */ + msg: string; + /** Optional explicit shell (e.g. `'/bin/bash'`) when bash features are required. */ + shell?: string; +} diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts new file mode 100644 index 0000000..c60cd67 --- /dev/null +++ b/packages/cli/src/utils.ts @@ -0,0 +1,70 @@ +/** + * Internal helpers for the Compact CLI tools. + * + * - **Glob matching** ({@link globToRegex}, {@link isExcluded}) — used by + * `FileDiscovery` to skip `.compact` files matching user-supplied patterns. + * - **Shell quoting** ({@link shellQuote}, {@link buildFindExcludes}) — used by + * `CompactBuilder` to interpolate user-supplied values into bash commands + * safely. + */ + +/** + * Converts a simple glob pattern to a regular expression. + * Supports `*` (any sequence) and `?` (single char). All other glob features + * (brace expansion, character classes) are not supported — keep patterns simple. + */ +export function globToRegex(glob: string): RegExp { + const escaped = glob.replace(/[\\^$+|.()[\]{}]/g, '\\$&'); + const pattern = escaped.replace(/\*/g, '.*').replace(/\?/g, '.'); + return new RegExp(`^${pattern}$`); +} + +/** + * Returns true if `filename`/`fullPath` matches any of the given glob patterns. + * + * - Patterns containing `/` are matched against `fullPath` (the path as + * `find srcDir` would emit it, e.g. `'src/archive/Foo.compact'`). + * - Patterns without `/` are matched against `filename` only. + * + * This mirrors the semantic of `find -name ` vs `find -path `. + */ +export function isExcluded( + filename: string, + fullPath: string, + patterns: readonly string[], +): boolean { + return patterns.some((pattern) => { + const target = pattern.includes('/') ? fullPath : filename; + return globToRegex(pattern).test(target); + }); +} + +/** + * Shell-quotes a string for safe interpolation into a single-quoted bash arg. + * + * @example + * shellQuote("foo") // "'foo'" + * shellQuote("it's") // "'it'\\''s'" + */ +export function shellQuote(value: string): string { + return `'${value.replace(/'/g, `'\\''`)}'`; +} + +/** + * Builds the `find`-compatible exclusion fragment for the given patterns. + * Patterns containing `/` are emitted as `! -path ''`; others as + * `! -name ''`. Single-quoting ensures safe shell interpolation. + * + * @example + * buildFindExcludes(['Mock*', '*\/archive\/*']) + * // "! -name 'Mock*' ! -path '*\/archive\/*'" + */ +export function buildFindExcludes(patterns: readonly string[]): string { + return patterns + .map((pattern) => + pattern.includes('/') + ? `! -path ${shellQuote(pattern)}` + : `! -name ${shellQuote(pattern)}`, + ) + .join(' '); +} diff --git a/packages/cli/test/Builder.test.ts b/packages/cli/test/Builder.test.ts new file mode 100644 index 0000000..4c74bec --- /dev/null +++ b/packages/cli/test/Builder.test.ts @@ -0,0 +1,202 @@ +import { describe, expect, it } from 'vitest'; +import { CompactBuilder } from '../src/Builder.js'; + +describe('CompactBuilder.parseArgs', () => { + it('returns defaults for empty input', () => { + const options = CompactBuilder.parseArgs([]); + + expect(options.cleanDist).toBeUndefined(); + expect(options.exclude).toBeUndefined(); + expect(options.copyToDist).toBeUndefined(); + // Compiler-side defaults still apply + expect(options.hierarchical).toBe(false); + expect(options.flags).toBe(''); + }); + + it('parses --clean-dist', () => { + expect(CompactBuilder.parseArgs(['--clean-dist']).cleanDist).toBe(true); + }); + + it('forwards --hierarchical to the compiler parser', () => { + // `hierarchical` is parsed by CompactCompiler.parseArgs and drives both + // compiler artifacts layout and builder .compact copy layout. + expect(CompactBuilder.parseArgs(['--hierarchical']).hierarchical).toBe(true); + }); + + it('accumulates repeated --exclude patterns', () => { + const options = CompactBuilder.parseArgs([ + '--exclude', + 'Mock*', + '--exclude', + '*/archive/*', + ]); + + expect(options.exclude).toEqual(['Mock*', '*/archive/*']); + }); + + it('accumulates repeated --copy paths', () => { + const options = CompactBuilder.parseArgs([ + '--copy', + 'package.json', + '--copy', + '../README.md', + ]); + + expect(options.copyToDist).toEqual(['package.json', '../README.md']); + }); + + it('throws when --exclude is missing a pattern', () => { + expect(() => CompactBuilder.parseArgs(['--exclude'])).toThrow( + '--exclude flag requires a pattern', + ); + expect(() => + CompactBuilder.parseArgs(['--exclude', '--clean-dist']), + ).toThrow('--exclude flag requires a pattern'); + }); + + it('throws when --copy is missing a path', () => { + expect(() => CompactBuilder.parseArgs(['--copy'])).toThrow( + '--copy flag requires a path', + ); + expect(() => CompactBuilder.parseArgs(['--copy', '--clean-dist'])).toThrow( + '--copy flag requires a path', + ); + }); + + it('forwards unknown args to the compiler parser', () => { + const options = CompactBuilder.parseArgs([ + '--dir', + 'security', + '--skip-zk', + '+0.29.0', + ]); + + expect(options.targetDir).toBe('security'); + expect(options.flags).toBe('--skip-zk'); + expect(options.version).toBe('0.29.0'); + }); + + it('combines builder and compiler flags', () => { + const options = CompactBuilder.parseArgs([ + '--clean-dist', + '--dir', + 'token', + '--hierarchical', + '--exclude', + 'Mock*', + '--copy', + 'package.json', + '--src', + 'contracts', + '+0.29.0', + ]); + + expect(options.cleanDist).toBe(true); + expect(options.hierarchical).toBe(true); + expect(options.exclude).toEqual(['Mock*']); + expect(options.copyToDist).toEqual(['package.json']); + expect(options.targetDir).toBe('token'); + expect(options.srcDir).toBe('contracts'); + expect(options.version).toBe('0.29.0'); + }); +}); + +describe('CompactBuilder step pipeline', () => { + it('uses the legacy 3-step pipeline by default', () => { + const builder = new CompactBuilder(); + const steps = builder.getSteps(); + + expect(steps.map((s) => s.msg)).toEqual([ + 'Compiling TypeScript', + 'Copying artifacts', + 'Copying .compact files', + ]); + }); + + it('excludes Mock* and *.mock.compact by default in the flat copy step', () => { + const builder = new CompactBuilder(); + const copyStep = builder.getSteps().find((s) => s.msg === 'Copying .compact files'); + + expect(copyStep?.cmd).toContain("! -name 'Mock*'"); + expect(copyStep?.cmd).toContain("! -name '*.mock.compact'"); + }); + + it('prepends a clean-dist step when cleanDist is true', () => { + const builder = new CompactBuilder({ cleanDist: true }); + const steps = builder.getSteps(); + + expect(steps[0].msg).toBe('Cleaning dist directory'); + expect(steps[0].cmd).toBe('rm -rf dist && mkdir -p dist'); + expect(steps).toHaveLength(4); + }); + + it('uses the hierarchical copy step when hierarchical is true', () => { + const builder = new CompactBuilder({ hierarchical: true }); + const copyStep = builder + .getSteps() + .find((s) => s.msg === 'Copying .compact files (preserving structure)'); + + expect(copyStep).toBeDefined(); + expect(copyStep?.cmd).toContain('rel_path='); + expect(copyStep?.cmd).toContain('mkdir -p "dist/$(dirname "$rel_path")"'); + }); + + it('appends a copy-to-dist step for each entry in copyToDist', () => { + const builder = new CompactBuilder({ + copyToDist: ['package.json', '../README.md'], + }); + const lastStep = builder.getSteps().at(-1); + + expect(lastStep?.msg).toBe('Copying additional files to dist'); + expect(lastStep?.cmd).toContain("cp 'package.json' dist/"); + expect(lastStep?.cmd).toContain("cp '../README.md' dist/"); + }); + + it('classifies excludes into -name and -path', () => { + const builder = new CompactBuilder({ + hierarchical: true, + exclude: ['Mock*', '*/archive/*'], + }); + const copyStep = builder + .getSteps() + .find((s) => s.msg === 'Copying .compact files (preserving structure)'); + + expect(copyStep?.cmd).toContain("! -name 'Mock*'"); + expect(copyStep?.cmd).toContain("! -path '*/archive/*'"); + }); + + it('honours an explicit empty exclude list (disables the default Mock*)', () => { + const builder = new CompactBuilder({ exclude: [] }); + const copyStep = builder + .getSteps() + .find((s) => s.msg === 'Copying .compact files'); + + expect(copyStep?.cmd).not.toContain('! -name'); + }); + + it('shell-quotes srcDir into find paths', () => { + const builder = new CompactBuilder({ srcDir: 'my src' }); + const copyStep = builder + .getSteps() + .find((s) => s.msg === 'Copying .compact files'); + + expect(copyStep?.cmd).toContain("find 'my src'"); + }); + + it('produces the full pipeline for a library-publish configuration', () => { + const builder = new CompactBuilder({ + cleanDist: true, + hierarchical: true, + exclude: ['Mock*', '*/archive/*'], + copyToDist: ['package.json', '../README.md'], + }); + + expect(builder.getSteps().map((s) => s.msg)).toEqual([ + 'Cleaning dist directory', + 'Compiling TypeScript', + 'Copying artifacts', + 'Copying .compact files (preserving structure)', + 'Copying additional files to dist', + ]); + }); +}); diff --git a/packages/cli/test/Compiler.test.ts b/packages/cli/test/Compiler.test.ts index 3913df8..555c09d 100644 --- a/packages/cli/test/Compiler.test.ts +++ b/packages/cli/test/Compiler.test.ts @@ -237,6 +237,43 @@ describe('FileDiscovery', () => { expect(files).toEqual(['Ownable.compact']); }); + + it('should skip files matching name-only exclude patterns', async () => { + const excludingDiscovery = new FileDiscovery('src', ['Mock*', '*.mock.compact']); + const mockDirents = [ + { name: 'Token.compact', isFile: () => true, isDirectory: () => false }, + { name: 'MockToken.compact', isFile: () => true, isDirectory: () => false }, + { name: 'Token.mock.compact', isFile: () => true, isDirectory: () => false }, + ]; + + mockReaddir.mockResolvedValue(mockDirents as any); + + const files = await excludingDiscovery.getCompactFiles('src'); + + expect(files).toEqual(['Token.compact']); + }); + + it('should skip files matching path globs', async () => { + // Path-style patterns (containing `/`) are matched against the full + // path as `find srcDir` would emit it, so `*/archive/*` works the same + // way as `find -path '*/archive/*'`. + const excludingDiscovery = new FileDiscovery('src', ['*/archive/*']); + const mockDirents = [ + { name: 'Token.compact', isFile: () => true, isDirectory: () => false }, + { name: 'archive', isFile: () => false, isDirectory: () => true }, + ]; + const mockArchiveDirents = [ + { name: 'Legacy.compact', isFile: () => true, isDirectory: () => false }, + ]; + + mockReaddir + .mockResolvedValueOnce(mockDirents as any) + .mockResolvedValueOnce(mockArchiveDirents as any); + + const files = await excludingDiscovery.getCompactFiles('src'); + + expect(files).toEqual(['Token.compact']); + }); }); }); @@ -798,6 +835,31 @@ describe('CompactCompiler', () => { '--out flag requires a directory path', ); }); + + it('should accumulate repeated --exclude patterns', () => { + compiler = CompactCompiler.fromArgs([ + '--exclude', + 'Mock*', + '--exclude', + '*.mock.compact', + ]); + + expect(compiler.testOptions.exclude).toEqual(['Mock*', '*.mock.compact']); + }); + + it('should default exclude to empty array when not specified', () => { + compiler = CompactCompiler.fromArgs([]); + expect(compiler.testOptions.exclude).toEqual([]); + }); + + it('should throw for --exclude without a pattern', () => { + expect(() => CompactCompiler.fromArgs(['--exclude'])).toThrow( + '--exclude flag requires a pattern', + ); + expect(() => + CompactCompiler.fromArgs(['--exclude', '--skip-zk']), + ).toThrow('--exclude flag requires a pattern'); + }); }); describe('validateEnvironment', () => { diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 75c9772..f4bcef1 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "outDir": "dist", "rootDir": "src", + "types": ["node"], "declaration": true, "skipLibCheck": true, "sourceMap": true, From 52d658af05e456778ffa38141dbf0be339350c16 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Tue, 12 May 2026 14:44:11 +0200 Subject: [PATCH 02/13] chore: fix readme --- README.md | 59 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 781a1c6..1aa5951 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Generic badge](https://img.shields.io/badge/Compact%20Compiler-0.26.0-1abc9c.svg)](https://docs.midnight.network/relnotes/compact/minokawa-0-18-26-0) +[![Generic badge](https://img.shields.io/badge/Compact%20Compiler-0.29.0-1abc9c.svg)](https://docs.midnight.network/relnotes/compact/) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/OpenZeppelin/compact-tools/badge)](https://api.securityscorecards.dev/projects/github.com/OpenZeppelin/compact-tools) @@ -11,29 +11,41 @@ Tools for compiling, building, and testing Compact smart contracts. This is a mo - `packages/cli`: CLI utilities to run the Compact compiler and builder - `packages/simulator`: TypeScript simulator to run and test Compact contracts locally -## External usage (via git submodule until npm publish) +## Installation -Until packages are published to the npm registry, you can consume this repo from another project using a git submodule: +Install whichever package you need from npm: ```bash -# In your project -git submodule add https://github.com/OpenZeppelin/compact-tools -git submodule update --init --recursive +# Simulator only (test/runtime side) +yarn add --dev @openzeppelin/compact-tools-simulator -# Install and build the tools +# CLI utilities (compile + build) +yarn add --dev @openzeppelin/compact-tools-cli +``` + +With the CLI installed, the `compact-compiler` and `compact-builder` binaries +are on the package's `bin` PATH: + +```bash +yarn compact-compiler --help +yarn compact-builder --help +``` + +### Developing against unreleased changes + +If you need a not-yet-published change, you can consume this repo locally via a +git submodule + `file:` dependency: + +```bash +git submodule add https://github.com/OpenZeppelin/compact-tools tools/compact-tools yarn --cwd tools/compact-tools install yarn --cwd tools/compact-tools build -# Use the simulator as a local dependency -# package.json +# In your package.json "devDependencies": { - "@openzeppelin/compact-tools-simulator": "file:./compact-tools/packages/simulator" + "@openzeppelin/compact-tools-simulator": "file:./tools/compact-tools/packages/simulator", + "@openzeppelin/compact-tools-cli": "file:./tools/compact-tools/packages/cli" } -yarn install - -# Call the CLIs directly or via scripts -node compact-tools/packages/cli/dist/runCompiler.js --help -node compact-tools/packages/cli/dist/runBuilder.js --help ``` ## Requirements @@ -48,8 +60,8 @@ Confirm your Compact toolchain: ```bash $ compact compile --version -Compactc version: 0.28.0 -0.28.0 +Compactc version: 0.29.0 +0.29.0 ``` ## Getting started @@ -101,11 +113,20 @@ compact-compiler # Skip ZK proofs for faster development builds compact-compiler --skip-zk -# Compile specific directory +# Compile a specific subdirectory compact-compiler --dir security -# Full build (compile + TypeScript + copy artifacts) +# Skip mock files at discovery time +compact-compiler --exclude 'Mock*' --exclude '*.mock.compact' + +# Full build (compile + TypeScript + copy .compact files into dist/) compact-builder + +# Library-publish build: clean dist, preserve src tree, copy package metadata +compact-builder \ + --clean-dist \ + --hierarchical \ + --copy package.json --copy ../README.md ``` See [packages/cli/README.md](./packages/cli/README.md) for full documentation including all options, programmatic API, and examples. From b74c8ea331ff6f65fa15160fbc432f9a7975637c Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Tue, 12 May 2026 15:28:30 +0200 Subject: [PATCH 03/13] chore: fix readme --- packages/simulator/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/simulator/README.md b/packages/simulator/README.md index da41f19..80a429b 100644 --- a/packages/simulator/README.md +++ b/packages/simulator/README.md @@ -13,7 +13,7 @@ allowing you to simulate contract behavior locally without blockchain deployment ## Quick Start ```typescript -import { createSimulator } from '@openzeppelin-compact/contracts-simulator'; +import { createSimulator } from '@openzeppelin/compact-tools-simulator'; import { Contract, ledger } from './artifacts/MyContract/contract/index.js'; // 1. Define your contract arguments type @@ -44,7 +44,7 @@ const sim = new MySimulator([ownerAddress, 100n], { coinPK: deployerPK }); The base simulator acts as a configuration class that the actual simulator will extend: ```typescript -import { createSimulator } from '@openzeppelin-compact/contracts-simulator'; +import { createSimulator } from '@openzeppelin/compact-tools-simulator'; import { Contract as MyContract, ledger } from './artifacts/MyContract/contract/index.js'; import { MyContractWitnesses, MyContractPrivateState } from './MyContractWitnesses.js'; From 1f691af54fe7ecd05a3107bafa37b24c16c31162 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Tue, 12 May 2026 16:08:01 +0200 Subject: [PATCH 04/13] refactor: adding changelog --- .github/workflows/release.yml | 4 +++ README.md | 26 ++++++++++----- RELEASING.md | 51 ++++++++++++++++++++++++++++- package.json | 2 +- packages/cli/CHANGELOG.md | 32 +++++++++++++++++++ packages/cli/package.json | 16 +++++++++- packages/cli/src/index.ts | 23 ++++++++++++++ packages/simulator/CHANGELOG.md | 23 ++++++++++++++ packages/simulator/package.json | 5 +++ packages/tools/CHANGELOG.md | 18 +++++++++++ packages/tools/README.md | 43 +++++++++++++++++++++++++ packages/tools/package.json | 53 +++++++++++++++++++++++++++++++ packages/tools/src/cli.ts | 4 +++ packages/tools/src/runBuilder.ts | 7 ++++ packages/tools/src/runCompiler.ts | 7 ++++ packages/tools/src/simulator.ts | 4 +++ packages/tools/tsconfig.json | 21 ++++++++++++ yarn.lock | 31 +++++++++++++----- 18 files changed, 351 insertions(+), 19 deletions(-) create mode 100644 packages/cli/CHANGELOG.md create mode 100644 packages/cli/src/index.ts create mode 100644 packages/simulator/CHANGELOG.md create mode 100644 packages/tools/CHANGELOG.md create mode 100644 packages/tools/README.md create mode 100644 packages/tools/package.json create mode 100644 packages/tools/src/cli.ts create mode 100644 packages/tools/src/runBuilder.ts create mode 100644 packages/tools/src/runCompiler.ts create mode 100644 packages/tools/src/simulator.ts create mode 100644 packages/tools/tsconfig.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f55014c..9a3d2c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,7 @@ on: required: true type: choice options: + - compact-tools - compact-tools-cli - compact-tools-simulator version_bump: @@ -46,6 +47,9 @@ jobs: id: pkg run: | case "${{ inputs.package }}" in + "compact-tools") + echo "dir=tools" >> $GITHUB_OUTPUT + ;; "compact-tools-cli") echo "dir=cli" >> $GITHUB_OUTPUT ;; diff --git a/README.md b/README.md index 1aa5951..c36dd72 100644 --- a/README.md +++ b/README.md @@ -10,27 +10,37 @@ Tools for compiling, building, and testing Compact smart contracts. This is a mo - `packages/cli`: CLI utilities to run the Compact compiler and builder - `packages/simulator`: TypeScript simulator to run and test Compact contracts locally +- `packages/tools`: Umbrella package re-exporting both under subpath exports ## Installation -Install whichever package you need from npm: +The fastest path is the umbrella package, which gives you the CLI binaries and +the simulator under subpath exports in a single install: ```bash -# Simulator only (test/runtime side) -yarn add --dev @openzeppelin/compact-tools-simulator - -# CLI utilities (compile + build) -yarn add --dev @openzeppelin/compact-tools-cli +yarn add --dev @openzeppelin/compact-tools ``` -With the CLI installed, the `compact-compiler` and `compact-builder` binaries -are on the package's `bin` PATH: +```ts +import { createSimulator } from '@openzeppelin/compact-tools/simulator'; +import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools/cli'; +``` ```bash yarn compact-compiler --help yarn compact-builder --help ``` +If you want only one piece, install the corresponding constituent directly: + +```bash +# Simulator only (test/runtime side) +yarn add --dev @openzeppelin/compact-tools-simulator + +# CLI utilities only (compile + build) +yarn add --dev @openzeppelin/compact-tools-cli +``` + ### Developing against unreleased changes If you need a not-yet-published change, you can consume this repo locally via a diff --git a/RELEASING.md b/RELEASING.md index 500eae5..fc56f0a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,5 +1,16 @@ # Releasing +## Before triggering the workflow + +For each PR that lands in `main` since the last release, add a bullet to the +`## Unreleased` section of the relevant `packages//CHANGELOG.md`. At +release time, the maintainer renames the `## Unreleased` heading to +`## - YYYY-MM-DD` and opens a fresh `## Unreleased` block. This +step is currently manual — see the TODO below for the planned migration to +[changesets](https://github.com/changesets/changesets). + +## Running the workflow + 1. Go to "Release Package" in Actions. 2. Click on the "Run workflow" dropdown menu. 3. Choose the package to release and the version bump type. @@ -14,4 +25,42 @@ - Bump the version. - Create a git tag. - Publish the package to npm. -6. Once published, go to "Releases" and create a GitHub release using the generated tag. \ No newline at end of file +6. Once published, go to "Releases" and create a GitHub release using the generated tag. + +## First-release order + +The `@openzeppelin/compact-tools` umbrella package depends on +`@openzeppelin/compact-tools-cli` and `@openzeppelin/compact-tools-simulator` +via `workspace:^`, which yarn rewrites to the resolved version at pack time. +For the very first release, publish in this order so the umbrella's deps +already exist on npm by the time it's published: + +1. `compact-tools-cli` +2. `compact-tools-simulator` +3. `compact-tools` (umbrella) + +After the first release, the three packages version independently — bump any +one of them in isolation without re-publishing the others. + +## TODO: migrate to changesets + +Today the version bump (via `yarn version` in the workflow input) and the +`CHANGELOG.md` entries are maintained separately and manually. As PR volume +grows this gets brittle — easy to forget a CHANGELOG entry, easy to mis-bump. + +[Changesets](https://github.com/changesets/changesets) is the standard +monorepo tool for this: + +- Contributors run `yarn changeset` in their PR and pick which packages bump + and at what semver level, with a short description that becomes the + CHANGELOG entry. +- A release workflow consumes all accumulated changesets to: + - Compute correct per-package version bumps. + - Write per-package `CHANGELOG.md` entries. + - Open a "Version Packages" PR for review. + - On merge, tag and publish all affected packages in one run. + +The existing `release.yml` would be simplified: no manual `version_bump` +input, no per-run package selection — the accumulated changesets drive both. +Existing `CHANGELOG.md` files become the historical record; changesets manage +new entries from that point on. \ No newline at end of file diff --git a/package.json b/package.json index a601bc2..a9bb156 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@openzeppelin/compact-tools", + "name": "compact-tools-monorepo", "description": "Tools for compiling, building and testing Compact smart contracts", "private": true, "packageManager": "yarn@4.10.3", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md new file mode 100644 index 0000000..74b847a --- /dev/null +++ b/packages/cli/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +All notable changes to `@openzeppelin/compact-tools-cli` will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Added + +- Initial public release of `@openzeppelin/compact-tools-cli`. +- `compact-compiler` bin — orchestrates `compact compile` over a project's + `.compact` files with progress reporting and structured error handling. +- `compact-builder` bin — runs the compiler then assembles a publishable `dist/` + for npm. +- CLI options: + - `--dir ` — scope to a subdirectory + - `--src ` / `--out ` — customize source / artifact dirs + - `--hierarchical` — preserve source tree in artifacts and `.compact` copy + - `--exclude ` — skip `.compact` files matching a glob (repeatable); + applies to both the compiler's file discovery and the builder's copy step + - `--clean-dist` — `rm -rf dist` before building + - `--copy ` — copy extra files into `dist/` (repeatable; e.g. + `package.json`, `../README.md`) + - `+` — pin the Compact toolchain version per invocation + - `SKIP_ZK=true` env var — equivalent to `--skip-zk` +- Programmatic API exports under the package root (`CompactCompiler`, + `CompactBuilder`, `EnvironmentValidator`, `FileDiscovery`, `CompilerService`, + `UIService`, and the `CompilerOptions` / `BuilderOptions` / `ExecFunction` + types). Bin entry points are reachable via the `./run-compiler` and + `./run-builder` subpath exports for downstream shimming. diff --git a/packages/cli/package.json b/packages/cli/package.json index 5cf08ab..a2201bf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -12,7 +12,21 @@ "author": "", "license": "MIT", "type": "module", - "exports": "./index.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./run-builder": "./dist/runBuilder.js", + "./run-compiler": "./dist/runCompiler.js" + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], "engines": { "node": ">=20" }, diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 0000000..8a84d1c --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,23 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + CompactCompiler, + CompilerService, + EnvironmentValidator, + FileDiscovery, + UIService, +} from './Compiler.js'; +export type { + CompilerOptions, + CompilerServiceOptions, + ExecFunction, +} from './Compiler.js'; +export { CompactBuilder } from './Builder.js'; +export type { BuilderOnlyOptions, BuilderOptions } from './Builder.js'; +export { + CompactCliNotFoundError, + CompilationError, + DirectoryNotFoundError, + isPromisifiedChildProcessError, +} from './types/errors.js'; +export type { PromisifiedChildProcessError } from './types/errors.js'; +export type { BuildStep } from './types/options.js'; diff --git a/packages/simulator/CHANGELOG.md b/packages/simulator/CHANGELOG.md new file mode 100644 index 0000000..a8e068b --- /dev/null +++ b/packages/simulator/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes to `@openzeppelin/compact-tools-simulator` will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Added + +- Initial public release of `@openzeppelin/compact-tools-simulator`. +- `createSimulator` factory for building type-safe per-contract simulator + classes, with configurable private state, ledger extractor, witnesses and + contract constructor arguments. +- Core building blocks: `AbstractSimulator`, `ContractSimulator`, + `CircuitContextManager` for managing private state, public (ledger) state, + zswap local state and transaction context. +- Public types: `IContractSimulator`, `IMinimalContract`, + `ExtractPureCircuits`, `ExtractImpureCircuits`, `ContextlessCircuits`, + `BaseSimulatorOptions`, `SimulatorConfig`. +- Toolchain pin: `@midnight-ntwrk/compact-runtime` 0.14.0 (Compact compiler + 0.29.0 generation) and `@midnight-ntwrk/ledger-v7`. diff --git a/packages/simulator/package.json b/packages/simulator/package.json index ee59c16..80e45d9 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -20,6 +20,11 @@ "require": "./dist/index.js" } }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], "engines": { "node": ">=22" }, diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md new file mode 100644 index 0000000..98fb441 --- /dev/null +++ b/packages/tools/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to `@openzeppelin/compact-tools` (umbrella) will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Added + +- Initial public release of the `@openzeppelin/compact-tools` umbrella. +- Subpath exports re-exporting the constituent packages: + - `@openzeppelin/compact-tools/cli` → `@openzeppelin/compact-tools-cli` + - `@openzeppelin/compact-tools/simulator` → `@openzeppelin/compact-tools-simulator` +- `compact-compiler` and `compact-builder` bin entries that delegate to + `@openzeppelin/compact-tools-cli`, so a single install of the umbrella gives + consumers both the binaries and the programmatic API surface. diff --git a/packages/tools/README.md b/packages/tools/README.md new file mode 100644 index 0000000..a29f425 --- /dev/null +++ b/packages/tools/README.md @@ -0,0 +1,43 @@ +# @openzeppelin/compact-tools + +Umbrella package for the OpenZeppelin Compact developer tools. One install gives +you both the CLI compiler/builder and the TypeScript simulator, exposed under +subpath exports. + +## Install + +```bash +yarn add --dev @openzeppelin/compact-tools +``` + +## Use + +```ts +// Programmatic — testing/runtime side +import { createSimulator } from '@openzeppelin/compact-tools/simulator'; + +// Programmatic — build pipeline +import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools/cli'; +``` + +The package also exposes the `compact-compiler` and `compact-builder` binaries: + +```bash +yarn compact-compiler --help +yarn compact-builder --help +``` + +Both binaries delegate to [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — the umbrella simply re-exports the same entry points. + +## Want only one piece? + +You can install either constituent package directly and skip the umbrella: + +- [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — CLI only +- [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — simulator only + +See the [monorepo README](https://github.com/OpenZeppelin/compact-tools#readme) for the full developer guide. + +## License + +MIT diff --git a/packages/tools/package.json b/packages/tools/package.json new file mode 100644 index 0000000..63a2aae --- /dev/null +++ b/packages/tools/package.json @@ -0,0 +1,53 @@ +{ + "name": "@openzeppelin/compact-tools", + "description": "Umbrella package re-exporting @openzeppelin/compact-tools-cli and @openzeppelin/compact-tools-simulator under subpath exports", + "version": "0.0.1", + "keywords": [ + "compact", + "midnight", + "compiler", + "builder", + "simulator", + "testing" + ], + "author": "", + "license": "MIT", + "type": "module", + "exports": { + "./cli": { + "types": "./dist/cli.d.ts", + "import": "./dist/cli.js" + }, + "./simulator": { + "types": "./dist/simulator.d.ts", + "import": "./dist/simulator.js" + } + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "engines": { + "node": ">=22" + }, + "bin": { + "compact-builder": "dist/runBuilder.js", + "compact-compiler": "dist/runCompiler.js" + }, + "scripts": { + "build": "tsc -p .", + "types": "tsc -p tsconfig.json --noEmit", + "test": "echo 'barrel package — see @openzeppelin/compact-tools-cli and @openzeppelin/compact-tools-simulator for tests'", + "clean": "git clean -fXd" + }, + "dependencies": { + "@openzeppelin/compact-tools-cli": "workspace:^", + "@openzeppelin/compact-tools-simulator": "workspace:^" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.3", + "@types/node": "24.10.1", + "typescript": "^5.9.3" + } +} diff --git a/packages/tools/src/cli.ts b/packages/tools/src/cli.ts new file mode 100644 index 0000000..d84622e --- /dev/null +++ b/packages/tools/src/cli.ts @@ -0,0 +1,4 @@ +// Re-export everything from @openzeppelin/compact-tools-cli under the +// `@openzeppelin/compact-tools/cli` subpath. +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export * from '@openzeppelin/compact-tools-cli'; diff --git a/packages/tools/src/runBuilder.ts b/packages/tools/src/runBuilder.ts new file mode 100644 index 0000000..645700c --- /dev/null +++ b/packages/tools/src/runBuilder.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +// Thin shim that delegates to the @openzeppelin/compact-tools-cli bin entry. +// Declared on `@openzeppelin/compact-tools` so users who install the umbrella +// package get the `compact-builder` binary without also installing the CLI +// package separately. +import '@openzeppelin/compact-tools-cli/run-builder'; diff --git a/packages/tools/src/runCompiler.ts b/packages/tools/src/runCompiler.ts new file mode 100644 index 0000000..518e575 --- /dev/null +++ b/packages/tools/src/runCompiler.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +// Thin shim that delegates to the @openzeppelin/compact-tools-cli bin entry. +// Declared on `@openzeppelin/compact-tools` so users who install the umbrella +// package get the `compact-compiler` binary without also installing the CLI +// package separately. +import '@openzeppelin/compact-tools-cli/run-compiler'; diff --git a/packages/tools/src/simulator.ts b/packages/tools/src/simulator.ts new file mode 100644 index 0000000..59193b4 --- /dev/null +++ b/packages/tools/src/simulator.ts @@ -0,0 +1,4 @@ +// Re-export everything from @openzeppelin/compact-tools-simulator under the +// `@openzeppelin/compact-tools/simulator` subpath. +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export * from '@openzeppelin/compact-tools-simulator'; diff --git a/packages/tools/tsconfig.json b/packages/tools/tsconfig.json new file mode 100644 index 0000000..e2ab1e9 --- /dev/null +++ b/packages/tools/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "@tsconfig/node24/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["node"], + "declaration": true, + "skipLibCheck": true, + "sourceMap": true, + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/yarn.lock b/yarn.lock index b550ff3..606122d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -381,7 +381,7 @@ __metadata: languageName: node linkType: hard -"@openzeppelin/compact-tools-cli@workspace:packages/cli": +"@openzeppelin/compact-tools-cli@workspace:^, @openzeppelin/compact-tools-cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@openzeppelin/compact-tools-cli@workspace:packages/cli" dependencies: @@ -398,7 +398,7 @@ __metadata: languageName: unknown linkType: soft -"@openzeppelin/compact-tools-simulator@workspace:packages/simulator": +"@openzeppelin/compact-tools-simulator@workspace:^, @openzeppelin/compact-tools-simulator@workspace:packages/simulator": version: 0.0.0-use.local resolution: "@openzeppelin/compact-tools-simulator@workspace:packages/simulator" dependencies: @@ -412,16 +412,18 @@ __metadata: languageName: unknown linkType: soft -"@openzeppelin/compact-tools@workspace:.": +"@openzeppelin/compact-tools@workspace:packages/tools": version: 0.0.0-use.local - resolution: "@openzeppelin/compact-tools@workspace:." + resolution: "@openzeppelin/compact-tools@workspace:packages/tools" dependencies: - "@biomejs/biome": "npm:2.3.8" + "@openzeppelin/compact-tools-cli": "workspace:^" + "@openzeppelin/compact-tools-simulator": "workspace:^" + "@tsconfig/node24": "npm:^24.0.3" "@types/node": "npm:24.10.1" - ts-node: "npm:^10.9.2" - turbo: "npm:^2.6.1" typescript: "npm:^5.9.3" - vitest: "npm:^4.0.15" + bin: + compact-builder: dist/runBuilder.js + compact-compiler: dist/runCompiler.js languageName: unknown linkType: soft @@ -913,6 +915,19 @@ __metadata: languageName: node linkType: hard +"compact-tools-monorepo@workspace:.": + version: 0.0.0-use.local + resolution: "compact-tools-monorepo@workspace:." + dependencies: + "@biomejs/biome": "npm:2.3.8" + "@types/node": "npm:24.10.1" + ts-node: "npm:^10.9.2" + turbo: "npm:^2.6.1" + typescript: "npm:^5.9.3" + vitest: "npm:^4.0.15" + languageName: unknown + linkType: soft + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" From c67d01c9ae1298708cbc8908a9c01cc664e16654 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Tue, 12 May 2026 16:33:04 +0200 Subject: [PATCH 05/13] feat: split the cli and the builder --- .github/workflows/release.yml | 4 + README.md | 19 +- RELEASING.md | 32 +- packages/builder/CHANGELOG.md | 38 +++ packages/builder/README.md | 72 ++++ packages/builder/package.json | 49 +++ packages/{cli => builder}/src/Builder.ts | 0 packages/{cli => builder}/src/Compiler.ts | 0 packages/{cli => builder}/src/index.ts | 0 .../src/services/CompilerService.ts | 0 .../src/services/EnvironmentValidator.ts | 0 .../src/services/FileDiscovery.ts | 0 .../src/services/UIService.ts | 0 packages/{cli => builder}/src/types/errors.ts | 0 .../{cli => builder}/src/types/options.ts | 0 packages/{cli => builder}/src/utils.ts | 0 .../{cli => builder}/test/Builder.test.ts | 0 .../{cli => builder}/test/Compiler.test.ts | 0 packages/builder/tsconfig.json | 21 ++ packages/builder/vitest.config.ts | 10 + packages/cli/CHANGELOG.md | 19 +- packages/cli/README.md | 309 +++--------------- packages/cli/package.json | 9 +- packages/cli/src/runBuilder.ts | 2 +- packages/cli/src/runCompiler.ts | 4 +- packages/cli/test/runCompiler.test.ts | 22 +- packages/tools/CHANGELOG.md | 10 +- packages/tools/README.md | 28 +- packages/tools/package.json | 16 +- packages/tools/src/cli.ts | 4 - packages/tools/src/index.ts | 8 + packages/tools/src/simulator.ts | 4 - yarn.lock | 17 +- 33 files changed, 357 insertions(+), 340 deletions(-) create mode 100644 packages/builder/CHANGELOG.md create mode 100644 packages/builder/README.md create mode 100644 packages/builder/package.json rename packages/{cli => builder}/src/Builder.ts (100%) rename packages/{cli => builder}/src/Compiler.ts (100%) rename packages/{cli => builder}/src/index.ts (100%) rename packages/{cli => builder}/src/services/CompilerService.ts (100%) rename packages/{cli => builder}/src/services/EnvironmentValidator.ts (100%) rename packages/{cli => builder}/src/services/FileDiscovery.ts (100%) rename packages/{cli => builder}/src/services/UIService.ts (100%) rename packages/{cli => builder}/src/types/errors.ts (100%) rename packages/{cli => builder}/src/types/options.ts (100%) rename packages/{cli => builder}/src/utils.ts (100%) rename packages/{cli => builder}/test/Builder.test.ts (100%) rename packages/{cli => builder}/test/Compiler.test.ts (100%) create mode 100644 packages/builder/tsconfig.json create mode 100644 packages/builder/vitest.config.ts delete mode 100644 packages/tools/src/cli.ts create mode 100644 packages/tools/src/index.ts delete mode 100644 packages/tools/src/simulator.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a3d2c7..dac35ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,7 @@ on: type: choice options: - compact-tools + - compact-tools-builder - compact-tools-cli - compact-tools-simulator version_bump: @@ -50,6 +51,9 @@ jobs: "compact-tools") echo "dir=tools" >> $GITHUB_OUTPUT ;; + "compact-tools-builder") + echo "dir=builder" >> $GITHUB_OUTPUT + ;; "compact-tools-cli") echo "dir=cli" >> $GITHUB_OUTPUT ;; diff --git a/README.md b/README.md index c36dd72..adee169 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,26 @@ This project extends the Midnight Network with additional developer tooling. Tools for compiling, building, and testing Compact smart contracts. This is a monorepo containing: -- `packages/cli`: CLI utilities to run the Compact compiler and builder +- `packages/builder`: Programmatic library that drives the Compact compiler + builder +- `packages/cli`: Thin bin wrapper around the builder library (`compact-compiler`, `compact-builder`) - `packages/simulator`: TypeScript simulator to run and test Compact contracts locally -- `packages/tools`: Umbrella package re-exporting both under subpath exports +- `packages/tools`: Umbrella package re-exporting builder + simulator from a single root import, plus the CLI bins ## Installation The fastest path is the umbrella package, which gives you the CLI binaries and -the simulator under subpath exports in a single install: +the libraries from a single import path: ```bash yarn add --dev @openzeppelin/compact-tools ``` ```ts -import { createSimulator } from '@openzeppelin/compact-tools/simulator'; -import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools/cli'; +import { + createSimulator, + CompactCompiler, + CompactBuilder, +} from '@openzeppelin/compact-tools'; ``` ```bash @@ -34,10 +38,13 @@ yarn compact-builder --help If you want only one piece, install the corresponding constituent directly: ```bash +# Programmatic library only (no bins) +yarn add --dev @openzeppelin/compact-tools-builder + # Simulator only (test/runtime side) yarn add --dev @openzeppelin/compact-tools-simulator -# CLI utilities only (compile + build) +# CLI bins only (depends transitively on -builder) yarn add --dev @openzeppelin/compact-tools-cli ``` diff --git a/RELEASING.md b/RELEASING.md index fc56f0a..b68e91c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -29,17 +29,31 @@ step is currently manual — see the TODO below for the planned migration to ## First-release order -The `@openzeppelin/compact-tools` umbrella package depends on -`@openzeppelin/compact-tools-cli` and `@openzeppelin/compact-tools-simulator` -via `workspace:^`, which yarn rewrites to the resolved version at pack time. -For the very first release, publish in this order so the umbrella's deps -already exist on npm by the time it's published: +There's a dependency chain across the four published packages: -1. `compact-tools-cli` -2. `compact-tools-simulator` -3. `compact-tools` (umbrella) +``` +compact-tools (umbrella) + ├─ depends on compact-tools-builder + ├─ depends on compact-tools-cli + └─ depends on compact-tools-simulator +compact-tools-cli (bin wrapper) + └─ depends on compact-tools-builder +compact-tools-builder (library) +compact-tools-simulator (library) +``` -After the first release, the three packages version independently — bump any +Each `workspace:^` dep is rewritten by yarn into the resolved version at +`yarn pack` time. For the very first release, publish in dependency order so +each dependent finds its deps already on npm: + +1. `compact-tools-builder` (no internal deps) +2. `compact-tools-simulator` (no internal deps) +3. `compact-tools-cli` (depends on `-builder`; pull `main` first so the bump + commit is present locally) +4. `compact-tools` (umbrella; pull `main` first so the bumps for `-builder`, + `-cli`, and `-simulator` are present) + +After the first release, the four packages version independently — bump any one of them in isolation without re-publishing the others. ## TODO: migrate to changesets diff --git a/packages/builder/CHANGELOG.md b/packages/builder/CHANGELOG.md new file mode 100644 index 0000000..229e217 --- /dev/null +++ b/packages/builder/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +All notable changes to `@openzeppelin/compact-tools-builder` will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Added + +- Initial public release of `@openzeppelin/compact-tools-builder`. +- Programmatic API for orchestrating the Compact toolchain: + - `CompactCompiler` — compiles `.compact` files to artifacts. + - `CompactBuilder` — runs `CompactCompiler` then assembles a publishable + `dist/`. +- Composable service classes: `EnvironmentValidator`, `FileDiscovery`, + `CompilerService`, and the `UIService` helper object. +- Option types: `CompilerOptions`, `BuilderOptions`, `BuilderOnlyOptions`, + `CompilerServiceOptions`, `BuildStep`, `ExecFunction`. +- Configurable behaviours: + - `hierarchical` — preserve source tree in compiler artifacts and the + builder's `.compact` copy step. + - `exclude` — glob-style patterns to skip in file discovery and dist copy + (`Mock*` / `*.mock.compact` excluded by default). + - `cleanDist` — `rm -rf dist` before building. + - `copyToDist` — extra files (e.g. `package.json`, `README.md`) to copy into + `dist/` for publishable layouts. + - `srcDir` / `outDir` — customize source and artifact directories. +- Structured error types: `CompactCliNotFoundError`, `CompilationError`, + `DirectoryNotFoundError`, plus the `isPromisifiedChildProcessError` type guard. +- Dependency-injectable `ExecFunction` for testing. + +### Notes + +- This package contains the library code that previously lived under + `@openzeppelin/compact-tools-cli`. The CLI package now ships only the bin + entries (`compact-compiler`, `compact-builder`) and depends on this library. diff --git a/packages/builder/README.md b/packages/builder/README.md new file mode 100644 index 0000000..6471464 --- /dev/null +++ b/packages/builder/README.md @@ -0,0 +1,72 @@ +# @openzeppelin/compact-tools-builder + +Programmatic library for compiling and building Compact smart contracts on the +Midnight network. Drives the `compactc` toolchain with progress reporting, +structured error handling, and configurable output layouts. + +This is the **library** — it ships no CLI binaries. If you want the bins +(`compact-compiler`, `compact-builder`) for use in `package.json` scripts, +install [`@openzeppelin/compact-tools-cli`](../cli) instead, which is a thin +wrapper around this library. + +## Install + +```bash +yarn add --dev @openzeppelin/compact-tools-builder +``` + +## Quick Start + +```ts +import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools-builder'; + +// Compile all .compact files in src/ to artifacts/ +await new CompactCompiler({ flags: '--skip-zk' }).compile(); + +// Or run the full build pipeline (compile + dist assembly) +const builder = new CompactBuilder({ + cleanDist: true, + hierarchical: true, + exclude: ['Mock*', '*/archive/*'], + copyToDist: ['package.json', '../README.md'], +}); +await builder.build(); +``` + +## Public API + +```ts +// Orchestrators +export class CompactCompiler { /* … */ } +export class CompactBuilder { /* … */ } + +// Service classes (use for advanced custom pipelines) +export class EnvironmentValidator { /* … */ } +export class FileDiscovery { /* … */ } +export class CompilerService { /* … */ } +export const UIService = { /* … */ }; + +// Option types +export interface CompilerOptions { /* flags, targetDir, version, hierarchical, srcDir, outDir, exclude */ } +export type BuilderOptions = CompilerOptions & { + cleanDist?: boolean; + copyToDist?: string[]; +}; + +// Errors +export class CompactCliNotFoundError extends Error { /* … */ } +export class CompilationError extends Error { /* … */ } +export class DirectoryNotFoundError extends Error { /* … */ } +``` + +## See also + +- [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — bin wrapper around this library +- [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — TypeScript simulator for testing Compact contracts locally +- [`@openzeppelin/compact-tools`](https://www.npmjs.com/package/@openzeppelin/compact-tools) — umbrella package giving you everything under subpath exports + +See the [monorepo README](https://github.com/OpenZeppelin/compact-tools#readme) for the full developer guide. + +## License + +MIT diff --git a/packages/builder/package.json b/packages/builder/package.json new file mode 100644 index 0000000..4e258dd --- /dev/null +++ b/packages/builder/package.json @@ -0,0 +1,49 @@ +{ + "name": "@openzeppelin/compact-tools-builder", + "description": "Programmatic library for compiling and building Compact smart contracts", + "version": "0.0.1", + "keywords": [ + "compact", + "midnight", + "compiler", + "builder", + "library" + ], + "author": "", + "license": "MIT", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "CHANGELOG.md", + "LICENSE" + ], + "engines": { + "node": ">=20" + }, + "scripts": { + "build": "tsc -p .", + "types": "tsc -p tsconfig.json --noEmit", + "test": "yarn vitest run", + "clean": "git clean -fXd" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.3", + "@types/node": "24.10.1", + "typescript": "^5.9.3", + "vitest": "^4.0.15" + }, + "dependencies": { + "chalk": "^5.6.2", + "log-symbols": "^7.0.0", + "ora": "^9.0.0" + } +} diff --git a/packages/cli/src/Builder.ts b/packages/builder/src/Builder.ts similarity index 100% rename from packages/cli/src/Builder.ts rename to packages/builder/src/Builder.ts diff --git a/packages/cli/src/Compiler.ts b/packages/builder/src/Compiler.ts similarity index 100% rename from packages/cli/src/Compiler.ts rename to packages/builder/src/Compiler.ts diff --git a/packages/cli/src/index.ts b/packages/builder/src/index.ts similarity index 100% rename from packages/cli/src/index.ts rename to packages/builder/src/index.ts diff --git a/packages/cli/src/services/CompilerService.ts b/packages/builder/src/services/CompilerService.ts similarity index 100% rename from packages/cli/src/services/CompilerService.ts rename to packages/builder/src/services/CompilerService.ts diff --git a/packages/cli/src/services/EnvironmentValidator.ts b/packages/builder/src/services/EnvironmentValidator.ts similarity index 100% rename from packages/cli/src/services/EnvironmentValidator.ts rename to packages/builder/src/services/EnvironmentValidator.ts diff --git a/packages/cli/src/services/FileDiscovery.ts b/packages/builder/src/services/FileDiscovery.ts similarity index 100% rename from packages/cli/src/services/FileDiscovery.ts rename to packages/builder/src/services/FileDiscovery.ts diff --git a/packages/cli/src/services/UIService.ts b/packages/builder/src/services/UIService.ts similarity index 100% rename from packages/cli/src/services/UIService.ts rename to packages/builder/src/services/UIService.ts diff --git a/packages/cli/src/types/errors.ts b/packages/builder/src/types/errors.ts similarity index 100% rename from packages/cli/src/types/errors.ts rename to packages/builder/src/types/errors.ts diff --git a/packages/cli/src/types/options.ts b/packages/builder/src/types/options.ts similarity index 100% rename from packages/cli/src/types/options.ts rename to packages/builder/src/types/options.ts diff --git a/packages/cli/src/utils.ts b/packages/builder/src/utils.ts similarity index 100% rename from packages/cli/src/utils.ts rename to packages/builder/src/utils.ts diff --git a/packages/cli/test/Builder.test.ts b/packages/builder/test/Builder.test.ts similarity index 100% rename from packages/cli/test/Builder.test.ts rename to packages/builder/test/Builder.test.ts diff --git a/packages/cli/test/Compiler.test.ts b/packages/builder/test/Compiler.test.ts similarity index 100% rename from packages/cli/test/Compiler.test.ts rename to packages/builder/test/Compiler.test.ts diff --git a/packages/builder/tsconfig.json b/packages/builder/tsconfig.json new file mode 100644 index 0000000..e2ab1e9 --- /dev/null +++ b/packages/builder/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "@tsconfig/node24/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["node"], + "declaration": true, + "skipLibCheck": true, + "sourceMap": true, + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/packages/builder/vitest.config.ts b/packages/builder/vitest.config.ts new file mode 100644 index 0000000..d57e53a --- /dev/null +++ b/packages/builder/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['test/**/*.test.ts'], + reporters: 'verbose', + }, +}); diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 74b847a..e2b9740 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -9,11 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial public release of `@openzeppelin/compact-tools-cli`. +- Initial public release of `@openzeppelin/compact-tools-cli` as a thin bin + wrapper around [`@openzeppelin/compact-tools-builder`](../builder). - `compact-compiler` bin — orchestrates `compact compile` over a project's `.compact` files with progress reporting and structured error handling. -- `compact-builder` bin — runs the compiler then assembles a publishable `dist/` - for npm. +- `compact-builder` bin — runs the compiler then assembles a publishable + `dist/` for npm. - CLI options: - `--dir ` — scope to a subdirectory - `--src ` / `--out ` — customize source / artifact dirs @@ -25,8 +26,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `package.json`, `../README.md`) - `+` — pin the Compact toolchain version per invocation - `SKIP_ZK=true` env var — equivalent to `--skip-zk` -- Programmatic API exports under the package root (`CompactCompiler`, - `CompactBuilder`, `EnvironmentValidator`, `FileDiscovery`, `CompilerService`, - `UIService`, and the `CompilerOptions` / `BuilderOptions` / `ExecFunction` - types). Bin entry points are reachable via the `./run-compiler` and - `./run-builder` subpath exports for downstream shimming. + +### Notes + +- The programmatic API (CompactCompiler, CompactBuilder, services, types) + previously shipped from this package has moved to + `@openzeppelin/compact-tools-builder`. This package now ships only the bin + entries. diff --git a/packages/cli/README.md b/packages/cli/README.md index 24c9067..b11319c 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,292 +1,73 @@ # @openzeppelin/compact-tools-cli -CLI utilities for compiling and building Compact smart contracts. +CLI wrapper around [`@openzeppelin/compact-tools-builder`](../builder). +Provides the `compact-compiler` and `compact-builder` binaries for use in +`package.json` scripts. Contains no programmatic API of its own — if you want +to call the compiler/builder from TypeScript, use the library package directly. -## Installation - -Until published to npm, use via git submodule or local path: - -```bash -# As a local dependency -yarn add @openzeppelin/compact-tools-cli@file:./compact-tools/packages/cli - -# Or invoke directly after building -node compact-tools/packages/cli/dist/runCompiler.js -``` - -## Requirements - -- Node.js >= 20 -- Midnight Compact toolchain installed and available in `PATH` - -Verify your Compact installation: - -```bash -$ compact compile --version -Compactc version: 0.28.0 -``` - -## Binaries - -This package provides two CLI binaries: - -| Binary | Script | Description | -|--------|--------|-------------| -| `compact-compiler` | `dist/runCompiler.js` | Compile `.compact` files to artifacts | -| `compact-builder` | `dist/runBuilder.js` | Compile + build TypeScript + copy artifacts | - -## Compiler CLI - -### Usage - -```bash -compact-compiler [options] -``` - -### Options - -| Option | Description | Default | -|--------|-------------|---------| -| `--dir ` | Compile specific subdirectory within src | (all) | -| `--src ` | Source directory containing `.compact` files | `src` | -| `--out ` | Output directory for compiled artifacts | `artifacts` | -| `--hierarchical` | Preserve source directory structure in output | `false` | -| `--exclude ` | Skip `.compact` files matching the glob (repeatable). Patterns with `/` match the path relative to `--src`; others match the filename only. | (none) | -| `--skip-zk` | Skip zero-knowledge proof generation | `false` | -| `+` | Use specific toolchain version (e.g., `+0.28.0`) | (default) | - -### Environment Variables - -| Variable | Description | -|----------|-------------| -| `SKIP_ZK=true` | Equivalent to `--skip-zk` flag | - -### Artifact Output Structure - -**Default (flattened):** All contract artifacts go directly under the output directory. - -``` -src/ - access/ - AccessControl.compact - token/ - Token.compact - -artifacts/ # Flattened output - AccessControl/ - Token/ -``` - -**Hierarchical (`--hierarchical`):** Preserves source directory structure. - -``` -artifacts/ # Hierarchical output - access/ - AccessControl/ - token/ - Token/ -``` - -### Examples - -```bash -# Compile all contracts (flattened output) -compact-compiler - -# Compile with hierarchical artifact structure -compact-compiler --hierarchical - -# Compile specific directory only -compact-compiler --dir security - -# Skip ZK proof generation (faster, for development) -compact-compiler --skip-zk - -# Use specific toolchain version -compact-compiler +0.28.0 - -# Custom source and output directories -compact-compiler --src contracts --out build - -# Combine options -compact-compiler --dir access --skip-zk --hierarchical - -# Use environment variable -SKIP_ZK=true compact-compiler -``` - -## Builder CLI - -The builder runs the compiler as a prerequisite, then executes a configurable -pipeline of distribution steps: - -1. Compile `.compact` files (via `compact-compiler`) -2. *(optional)* `rm -rf dist && mkdir -p dist` — when `--clean-dist` is set -3. Compile TypeScript (`tsc --project tsconfig.build.json`) -4. Copy artifacts to `dist/artifacts/` -5. Copy `.compact` files to `dist/` (flat by default; tree-preserving with `--hierarchical`) -6. *(optional)* Copy extra files into `dist/` — one per `--copy ` - -### Usage +## Install ```bash -compact-builder [options] +yarn add --dev @openzeppelin/compact-tools-cli ``` -Accepts all compiler options plus the following **builder-only** options: - -| Option | Description | -| --- | --- | -| `--clean-dist` | Run `rm -rf dist && mkdir -p dist` before building. | -| `--copy ` | Copy an extra file into `dist/` for distribution (e.g. `package.json`, `../README.md`). Repeatable. | - -The compiler's `--hierarchical` and `--exclude` flags also affect the builder: - -- **`--hierarchical`** — when set, source files keep their directory structure - under `dist/` (e.g. `src/token/Foo.compact` → `dist/token/Foo.compact`) and - compiled artifacts likewise preserve their subdir. When unset (default), both - are flat. -- **`--exclude `** — applies to both the compiler's file discovery - AND the builder's `.compact` copy step. When unset, the builder falls back - to `['Mock*', '*.mock.compact']` so mocks are stripped from the dist by - default (even though the compiler with no `--exclude` still compiles them). - -Builds should normally include ZK proofs; avoid passing `--skip-zk` here. - -### Examples +## Use ```bash -# Default build (flat .compact copy, excludes Mock*) -compact-builder - -# Build specific subdirectory -compact-builder --dir token - -# Custom source/output directories -compact-builder --src contracts --out build - -# Library-publish build: clean dist, hierarchical layout, exclude mocks + archive, ship metadata -compact-builder \ - --clean-dist \ - --hierarchical \ - --exclude 'Mock*' --exclude '*/archive/*' \ - --copy package.json --copy ../README.md -``` - -## 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(); +yarn compact-compiler --help +yarn compact-builder --help ``` -### Classes and Types +Typical `package.json` scripts: -```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; - validateEnvironment(): Promise; +```json +{ + "scripts": { + "compact": "compact-compiler +0.29.0 --exclude '*/archive/*'", + "compact:access": "compact-compiler +0.29.0 --dir access", + "build": "compact-builder +0.29.0 --clean-dist --hierarchical --copy package.json --copy ../README.md", + "test": "compact-compiler +0.29.0 --skip-zk && vitest run" + } } - -// Builder class -class CompactBuilder { - constructor(options?: BuilderOptions); - static fromArgs(args: string[], env?: NodeJS.ProcessEnv): CompactBuilder; - static parseArgs(args: string[], env?: NodeJS.ProcessEnv): BuilderOptions; - build(): Promise; - getSteps(): readonly { cmd: string; msg: string; shell?: string }[]; -} - -// Compiler options -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') - exclude?: string[]; // .compact glob patterns to skip in discovery - // (and in the builder's copy step) -} - -// Builder options (extends CompilerOptions with dist-layout controls) -// Note: `hierarchical` and `exclude` (from CompilerOptions) drive BOTH the -// compiler and the builder. When `exclude` is undefined, the builder -// substitutes ['Mock*', '*.mock.compact'] for its copy step. -type BuilderOptions = CompilerOptions & { - cleanDist?: boolean; // rm -rf dist before building - copyToDist?: string[]; // extra files to copy into dist/ (e.g. package.json) -}; ``` -### Error Types +## Options -```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'; -``` +Both binaries accept the same compiler-side options (forwarded to the +underlying library); `compact-builder` additionally accepts dist-layout +options: -## Development +| Flag | Applies to | Description | +|---|---|---| +| `--dir ` | both | Scope to a subdirectory inside `--src`. | +| `--src ` | both | Source directory containing `.compact` files (default: `src`). | +| `--out ` | both | Output directory for compiled artifacts (default: `artifacts`). | +| `--hierarchical` | both | Preserve source directory structure in artifacts AND in the builder's `.compact` copy. | +| `--exclude ` | both | Skip `.compact` files matching the glob (repeatable). Default for the builder: `Mock*`, `*.mock.compact`. | +| `--skip-zk` | compiler | Skip zero-knowledge proof generation (also via `SKIP_ZK=true` env var). | +| `+` | both | Pin the Compact toolchain version (e.g. `+0.29.0`). | +| `--clean-dist` | builder | `rm -rf dist` before building. | +| `--copy ` | builder | Copy an extra file into `dist/` (repeatable; e.g. `package.json`, `../README.md`). | -```bash -cd packages/cli - -# Build -yarn build +See [`@openzeppelin/compact-tools-builder`](../builder) for the full +documentation, programmatic API, and behavioural details. -# Type-check only -yarn types +## Requirements -# Run tests -yarn test +- Node.js >= 20 +- Midnight Compact toolchain installed and available in `PATH` -# Clean -yarn clean +```bash +$ compact compile --version +Compactc version: 0.29.0 ``` -## Output Example +## See also -```bash -ℹ [COMPILE] Compact compiler started -ℹ [COMPILE] Compact developer tools: compact 0.2.0 -ℹ [COMPILE] Compact toolchain: Compactc version: 0.28.0 -ℹ [COMPILE] Found 2 .compact file(s) to compile -✔ [COMPILE] [1/2] Compiled AccessControl.compact - Compactc version: 0.28.0 -✔ [COMPILE] [2/2] Compiled Token.compact - Compactc version: 0.28.0 -``` +- [`@openzeppelin/compact-tools-builder`](https://www.npmjs.com/package/@openzeppelin/compact-tools-builder) — programmatic library backing this CLI +- [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — simulator for testing Compact contracts +- [`@openzeppelin/compact-tools`](https://www.npmjs.com/package/@openzeppelin/compact-tools) — umbrella package giving you all three under subpath exports ## License MIT - diff --git a/packages/cli/package.json b/packages/cli/package.json index a2201bf..d267e21 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -12,19 +12,14 @@ "author": "", "license": "MIT", "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, "./run-builder": "./dist/runBuilder.js", "./run-compiler": "./dist/runCompiler.js" }, "files": [ "dist", "README.md", + "CHANGELOG.md", "LICENSE" ], "engines": { @@ -47,8 +42,8 @@ "vitest": "^4.0.15" }, "dependencies": { + "@openzeppelin/compact-tools-builder": "workspace:^", "chalk": "^5.6.2", - "log-symbols": "^7.0.0", "ora": "^9.0.0" } } diff --git a/packages/cli/src/runBuilder.ts b/packages/cli/src/runBuilder.ts index 16b8490..76176d4 100644 --- a/packages/cli/src/runBuilder.ts +++ b/packages/cli/src/runBuilder.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import ora from 'ora'; -import { CompactBuilder } from './Builder.ts'; +import { CompactBuilder } from '@openzeppelin/compact-tools-builder'; /** * Executes the Compact builder CLI. diff --git a/packages/cli/src/runCompiler.ts b/packages/cli/src/runCompiler.ts index fda1a77..4a0f43e 100644 --- a/packages/cli/src/runCompiler.ts +++ b/packages/cli/src/runCompiler.ts @@ -2,11 +2,11 @@ import chalk from 'chalk'; import ora, { type Ora } from 'ora'; -import { CompactCompiler } from './Compiler.ts'; import { type CompilationError, + CompactCompiler, isPromisifiedChildProcessError, -} from './types/errors.ts'; +} from '@openzeppelin/compact-tools-builder'; /** * Executes the Compact compiler CLI with improved error handling and user feedback. diff --git a/packages/cli/test/runCompiler.test.ts b/packages/cli/test/runCompiler.test.ts index 58f064a..efcc9f1 100644 --- a/packages/cli/test/runCompiler.test.ts +++ b/packages/cli/test/runCompiler.test.ts @@ -1,25 +1,23 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { CompactCompiler } from '../src/Compiler.js'; import { CompactCliNotFoundError, + CompactCompiler, CompilationError, DirectoryNotFoundError, isPromisifiedChildProcessError, type PromisifiedChildProcessError, -} from '../src/types/errors.js'; +} from '@openzeppelin/compact-tools-builder'; -// Mock CompactCompiler -vi.mock('../src/Compiler.js', () => ({ - CompactCompiler: { - fromArgs: vi.fn(), - }, -})); - -// Mock error utilities -vi.mock('../src/types/errors.js', async () => { - const actual = await vi.importActual('../src/types/errors.js'); +// Mock the library so we can drive the CLI in isolation. +vi.mock('@openzeppelin/compact-tools-builder', async () => { + const actual = await vi.importActual< + typeof import('@openzeppelin/compact-tools-builder') + >('@openzeppelin/compact-tools-builder'); return { ...actual, + CompactCompiler: { + fromArgs: vi.fn(), + }, isPromisifiedChildProcessError: vi.fn(), }; }); diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 98fb441..3e86bb7 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -10,9 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial public release of the `@openzeppelin/compact-tools` umbrella. -- Subpath exports re-exporting the constituent packages: - - `@openzeppelin/compact-tools/cli` → `@openzeppelin/compact-tools-cli` - - `@openzeppelin/compact-tools/simulator` → `@openzeppelin/compact-tools-simulator` +- Single root export re-exporting everything from + `@openzeppelin/compact-tools-builder` and + `@openzeppelin/compact-tools-simulator`, so consumers can pull anything off + the umbrella with a single import: + ```ts + import { createSimulator, CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools'; + ``` - `compact-compiler` and `compact-builder` bin entries that delegate to `@openzeppelin/compact-tools-cli`, so a single install of the umbrella gives consumers both the binaries and the programmatic API surface. diff --git a/packages/tools/README.md b/packages/tools/README.md index a29f425..5ff0cd0 100644 --- a/packages/tools/README.md +++ b/packages/tools/README.md @@ -1,8 +1,8 @@ # @openzeppelin/compact-tools -Umbrella package for the OpenZeppelin Compact developer tools. One install gives -you both the CLI compiler/builder and the TypeScript simulator, exposed under -subpath exports. +Umbrella package for the OpenZeppelin Compact developer tools. One install +gives you the build library, the simulator, and the CLI binaries — all +reachable from a single import path. ## Install @@ -13,11 +13,13 @@ yarn add --dev @openzeppelin/compact-tools ## Use ```ts -// Programmatic — testing/runtime side -import { createSimulator } from '@openzeppelin/compact-tools/simulator'; - -// Programmatic — build pipeline -import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools/cli'; +import { + // simulator + createSimulator, + // builder library + CompactCompiler, + CompactBuilder, +} from '@openzeppelin/compact-tools'; ``` The package also exposes the `compact-compiler` and `compact-builder` binaries: @@ -27,13 +29,17 @@ yarn compact-compiler --help yarn compact-builder --help ``` -Both binaries delegate to [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — the umbrella simply re-exports the same entry points. +Both binaries delegate to +[`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli), +which in turn calls into +[`@openzeppelin/compact-tools-builder`](https://www.npmjs.com/package/@openzeppelin/compact-tools-builder). ## Want only one piece? -You can install either constituent package directly and skip the umbrella: +You can install constituents directly and skip the umbrella: -- [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — CLI only +- [`@openzeppelin/compact-tools-builder`](https://www.npmjs.com/package/@openzeppelin/compact-tools-builder) — programmatic library (no bins) +- [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — bin wrapper only - [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — simulator only See the [monorepo README](https://github.com/OpenZeppelin/compact-tools#readme) for the full developer guide. diff --git a/packages/tools/package.json b/packages/tools/package.json index 63a2aae..a535a58 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/compact-tools", - "description": "Umbrella package re-exporting @openzeppelin/compact-tools-cli and @openzeppelin/compact-tools-simulator under subpath exports", + "description": "Umbrella package re-exporting @openzeppelin/compact-tools-builder and @openzeppelin/compact-tools-simulator from a single root entry, plus the @openzeppelin/compact-tools-cli bins", "version": "0.0.1", "keywords": [ "compact", @@ -13,19 +13,18 @@ "author": "", "license": "MIT", "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { - "./cli": { - "types": "./dist/cli.d.ts", - "import": "./dist/cli.js" - }, - "./simulator": { - "types": "./dist/simulator.d.ts", - "import": "./dist/simulator.js" + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" } }, "files": [ "dist", "README.md", + "CHANGELOG.md", "LICENSE" ], "engines": { @@ -42,6 +41,7 @@ "clean": "git clean -fXd" }, "dependencies": { + "@openzeppelin/compact-tools-builder": "workspace:^", "@openzeppelin/compact-tools-cli": "workspace:^", "@openzeppelin/compact-tools-simulator": "workspace:^" }, diff --git a/packages/tools/src/cli.ts b/packages/tools/src/cli.ts deleted file mode 100644 index d84622e..0000000 --- a/packages/tools/src/cli.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Re-export everything from @openzeppelin/compact-tools-cli under the -// `@openzeppelin/compact-tools/cli` subpath. -// biome-ignore lint/performance/noBarrelFile: entrypoint module -export * from '@openzeppelin/compact-tools-cli'; diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts new file mode 100644 index 0000000..a9999dc --- /dev/null +++ b/packages/tools/src/index.ts @@ -0,0 +1,8 @@ +// Single root export — re-exports everything from the constituent libraries +// so consumers can pull anything off the umbrella with one import. +// +// import { createSimulator, CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools'; +// +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export * from '@openzeppelin/compact-tools-builder'; +export * from '@openzeppelin/compact-tools-simulator'; diff --git a/packages/tools/src/simulator.ts b/packages/tools/src/simulator.ts deleted file mode 100644 index 59193b4..0000000 --- a/packages/tools/src/simulator.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Re-export everything from @openzeppelin/compact-tools-simulator under the -// `@openzeppelin/compact-tools/simulator` subpath. -// biome-ignore lint/performance/noBarrelFile: entrypoint module -export * from '@openzeppelin/compact-tools-simulator'; diff --git a/yarn.lock b/yarn.lock index 606122d..867dfc4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -381,14 +381,28 @@ __metadata: languageName: node linkType: hard +"@openzeppelin/compact-tools-builder@workspace:^, @openzeppelin/compact-tools-builder@workspace:packages/builder": + version: 0.0.0-use.local + resolution: "@openzeppelin/compact-tools-builder@workspace:packages/builder" + dependencies: + "@tsconfig/node24": "npm:^24.0.3" + "@types/node": "npm:24.10.1" + chalk: "npm:^5.6.2" + log-symbols: "npm:^7.0.0" + ora: "npm:^9.0.0" + typescript: "npm:^5.9.3" + vitest: "npm:^4.0.15" + languageName: unknown + linkType: soft + "@openzeppelin/compact-tools-cli@workspace:^, @openzeppelin/compact-tools-cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@openzeppelin/compact-tools-cli@workspace:packages/cli" dependencies: + "@openzeppelin/compact-tools-builder": "workspace:^" "@tsconfig/node24": "npm:^24.0.3" "@types/node": "npm:24.10.1" chalk: "npm:^5.6.2" - log-symbols: "npm:^7.0.0" ora: "npm:^9.0.0" typescript: "npm:^5.9.3" vitest: "npm:^4.0.15" @@ -416,6 +430,7 @@ __metadata: version: 0.0.0-use.local resolution: "@openzeppelin/compact-tools@workspace:packages/tools" dependencies: + "@openzeppelin/compact-tools-builder": "workspace:^" "@openzeppelin/compact-tools-cli": "workspace:^" "@openzeppelin/compact-tools-simulator": "workspace:^" "@tsconfig/node24": "npm:^24.0.3" From f95ab43ac9c256caf9a109890e3cea0a24573e80 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Tue, 12 May 2026 16:56:07 +0200 Subject: [PATCH 06/13] chore: fix lint --- packages/builder/src/Builder.ts | 3 +- packages/builder/src/Compiler.ts | 9 +++-- packages/builder/src/index.ts | 18 +++++----- packages/builder/test/Builder.test.ts | 8 +++-- packages/builder/test/Compiler.test.ts | 23 +++++++++--- packages/cli/src/runBuilder.ts | 2 +- packages/cli/src/runCompiler.ts | 6 ++-- packages/cli/test/runCompiler.test.ts | 2 +- packages/tools/src/index.ts | 48 ++++++++++++++++++++++++-- 9 files changed, 93 insertions(+), 26 deletions(-) diff --git a/packages/builder/src/Builder.ts b/packages/builder/src/Builder.ts index 3b2a963..5f41f0d 100755 --- a/packages/builder/src/Builder.ts +++ b/packages/builder/src/Builder.ts @@ -103,7 +103,8 @@ export class CompactBuilder { if (value === undefined || value.startsWith('--')) { throw new Error('--copy flag requires a path'); } - (builderOnly.copyToDist ??= []).push(value); + builderOnly.copyToDist ??= []; + builderOnly.copyToDist.push(value); i++; } else { compilerArgs.push(arg); diff --git a/packages/builder/src/Compiler.ts b/packages/builder/src/Compiler.ts index e2a322c..7d77656 100755 --- a/packages/builder/src/Compiler.ts +++ b/packages/builder/src/Compiler.ts @@ -23,6 +23,7 @@ import { // Re-export public types and services so consumers keep importing them // from './Compiler.js' regardless of the internal file layout. +// biome-ignore lint/performance/noBarrelFile: package entrypoint export { CompilerService } from './services/CompilerService.ts'; export { EnvironmentValidator } from './services/EnvironmentValidator.ts'; export { FileDiscovery } from './services/FileDiscovery.ts'; @@ -31,7 +32,10 @@ export type { CompilerOptions, CompilerServiceOptions, ExecFunction }; /** Resolved compiler options with defaults applied */ type ResolvedCompilerOptions = Required< - Pick + Pick< + CompilerOptions, + 'flags' | 'hierarchical' | 'srcDir' | 'outDir' | 'exclude' + > > & Pick; @@ -175,7 +179,8 @@ export class CompactCompiler { const valueExists = i + 1 < args.length && !args[i + 1].startsWith('--'); if (valueExists) { - (options.exclude ??= []).push(args[i + 1]); + options.exclude ??= []; + options.exclude.push(args[i + 1]); i++; } else { throw new Error('--exclude flag requires a pattern'); diff --git a/packages/builder/src/index.ts b/packages/builder/src/index.ts index 8a84d1c..e1868ec 100644 --- a/packages/builder/src/index.ts +++ b/packages/builder/src/index.ts @@ -1,4 +1,11 @@ -// biome-ignore lint/performance/noBarrelFile: entrypoint module +export type { BuilderOnlyOptions, BuilderOptions } from './Builder.js'; +// biome-ignore lint/performance/noBarrelFile: package entrypoint +export { CompactBuilder } from './Builder.js'; +export type { + CompilerOptions, + CompilerServiceOptions, + ExecFunction, +} from './Compiler.js'; export { CompactCompiler, CompilerService, @@ -6,18 +13,11 @@ export { FileDiscovery, UIService, } from './Compiler.js'; -export type { - CompilerOptions, - CompilerServiceOptions, - ExecFunction, -} from './Compiler.js'; -export { CompactBuilder } from './Builder.js'; -export type { BuilderOnlyOptions, BuilderOptions } from './Builder.js'; +export type { PromisifiedChildProcessError } from './types/errors.js'; export { CompactCliNotFoundError, CompilationError, DirectoryNotFoundError, isPromisifiedChildProcessError, } from './types/errors.js'; -export type { PromisifiedChildProcessError } from './types/errors.js'; export type { BuildStep } from './types/options.js'; diff --git a/packages/builder/test/Builder.test.ts b/packages/builder/test/Builder.test.ts index 4c74bec..35c487a 100644 --- a/packages/builder/test/Builder.test.ts +++ b/packages/builder/test/Builder.test.ts @@ -20,7 +20,9 @@ describe('CompactBuilder.parseArgs', () => { it('forwards --hierarchical to the compiler parser', () => { // `hierarchical` is parsed by CompactCompiler.parseArgs and drives both // compiler artifacts layout and builder .compact copy layout. - expect(CompactBuilder.parseArgs(['--hierarchical']).hierarchical).toBe(true); + expect(CompactBuilder.parseArgs(['--hierarchical']).hierarchical).toBe( + true, + ); }); it('accumulates repeated --exclude patterns', () => { @@ -115,7 +117,9 @@ describe('CompactBuilder step pipeline', () => { it('excludes Mock* and *.mock.compact by default in the flat copy step', () => { const builder = new CompactBuilder(); - const copyStep = builder.getSteps().find((s) => s.msg === 'Copying .compact files'); + const copyStep = builder + .getSteps() + .find((s) => s.msg === 'Copying .compact files'); expect(copyStep?.cmd).toContain("! -name 'Mock*'"); expect(copyStep?.cmd).toContain("! -name '*.mock.compact'"); diff --git a/packages/builder/test/Compiler.test.ts b/packages/builder/test/Compiler.test.ts index 555c09d..e2c60d2 100644 --- a/packages/builder/test/Compiler.test.ts +++ b/packages/builder/test/Compiler.test.ts @@ -239,11 +239,22 @@ describe('FileDiscovery', () => { }); it('should skip files matching name-only exclude patterns', async () => { - const excludingDiscovery = new FileDiscovery('src', ['Mock*', '*.mock.compact']); + const excludingDiscovery = new FileDiscovery('src', [ + 'Mock*', + '*.mock.compact', + ]); const mockDirents = [ { name: 'Token.compact', isFile: () => true, isDirectory: () => false }, - { name: 'MockToken.compact', isFile: () => true, isDirectory: () => false }, - { name: 'Token.mock.compact', isFile: () => true, isDirectory: () => false }, + { + name: 'MockToken.compact', + isFile: () => true, + isDirectory: () => false, + }, + { + name: 'Token.mock.compact', + isFile: () => true, + isDirectory: () => false, + }, ]; mockReaddir.mockResolvedValue(mockDirents as any); @@ -263,7 +274,11 @@ describe('FileDiscovery', () => { { name: 'archive', isFile: () => false, isDirectory: () => true }, ]; const mockArchiveDirents = [ - { name: 'Legacy.compact', isFile: () => true, isDirectory: () => false }, + { + name: 'Legacy.compact', + isFile: () => true, + isDirectory: () => false, + }, ]; mockReaddir diff --git a/packages/cli/src/runBuilder.ts b/packages/cli/src/runBuilder.ts index 76176d4..0d1b26d 100644 --- a/packages/cli/src/runBuilder.ts +++ b/packages/cli/src/runBuilder.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node +import { CompactBuilder } from '@openzeppelin/compact-tools-builder'; import chalk from 'chalk'; import ora from 'ora'; -import { CompactBuilder } from '@openzeppelin/compact-tools-builder'; /** * Executes the Compact builder CLI. diff --git a/packages/cli/src/runCompiler.ts b/packages/cli/src/runCompiler.ts index 4a0f43e..cdbdbc3 100644 --- a/packages/cli/src/runCompiler.ts +++ b/packages/cli/src/runCompiler.ts @@ -1,12 +1,12 @@ #!/usr/bin/env node -import chalk from 'chalk'; -import ora, { type Ora } from 'ora'; import { - type CompilationError, CompactCompiler, + type CompilationError, isPromisifiedChildProcessError, } from '@openzeppelin/compact-tools-builder'; +import chalk from 'chalk'; +import ora, { type Ora } from 'ora'; /** * Executes the Compact compiler CLI with improved error handling and user feedback. diff --git a/packages/cli/test/runCompiler.test.ts b/packages/cli/test/runCompiler.test.ts index efcc9f1..90017dc 100644 --- a/packages/cli/test/runCompiler.test.ts +++ b/packages/cli/test/runCompiler.test.ts @@ -1,4 +1,3 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { CompactCliNotFoundError, CompactCompiler, @@ -7,6 +6,7 @@ import { isPromisifiedChildProcessError, type PromisifiedChildProcessError, } from '@openzeppelin/compact-tools-builder'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // Mock the library so we can drive the CLI in isolation. vi.mock('@openzeppelin/compact-tools-builder', async () => { diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts index a9999dc..414943e 100644 --- a/packages/tools/src/index.ts +++ b/packages/tools/src/index.ts @@ -3,6 +3,48 @@ // // import { createSimulator, CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools'; // -// biome-ignore lint/performance/noBarrelFile: entrypoint module -export * from '@openzeppelin/compact-tools-builder'; -export * from '@openzeppelin/compact-tools-simulator'; +// Each name is re-exported explicitly (rather than via `export *`) so the +// public surface is visible at a glance and biome's `noReExportAll` rule +// stays happy. When a new export is added in a constituent package, add it +// here too. + +// @openzeppelin/compact-tools-builder +export type { + BuilderOnlyOptions, + BuilderOptions, + BuildStep, + CompilerOptions, + CompilerServiceOptions, + ExecFunction, + PromisifiedChildProcessError, +} from '@openzeppelin/compact-tools-builder'; +// biome-ignore lint/performance/noBarrelFile: package entrypoint +export { + CompactBuilder, + CompactCliNotFoundError, + CompactCompiler, + CompilationError, + CompilerService, + DirectoryNotFoundError, + EnvironmentValidator, + FileDiscovery, + isPromisifiedChildProcessError, + UIService, +} from '@openzeppelin/compact-tools-builder'; + +// @openzeppelin/compact-tools-simulator +export type { + BaseSimulatorOptions, + ContextlessCircuits, + ExtractImpureCircuits, + ExtractPureCircuits, + IContractSimulator, + IMinimalContract, + SimulatorConfig, +} from '@openzeppelin/compact-tools-simulator'; +export { + AbstractSimulator, + CircuitContextManager, + ContractSimulator, + createSimulator, +} from '@openzeppelin/compact-tools-simulator'; From dd2b0235e11b669c1c93cc9e8ed9fe6e0f1cf915 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Wed, 13 May 2026 08:46:14 +0200 Subject: [PATCH 07/13] refactor: types depend on the build --- turbo.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/turbo.json b/turbo.json index 74b3c39..08dd369 100644 --- a/turbo.json +++ b/turbo.json @@ -26,8 +26,9 @@ "outputs": ["dist/**"] }, "types": { - "dependsOn": [], - "outputs": ["dist/**"] + "dependsOn": ["^build"], + "inputs": ["src/**/*.ts", "tsconfig.json"], + "outputs": [] }, "lint": {}, "lint:ci": {}, From 6225d51dd32c72b18934a9d51113807d2ca2342a Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Wed, 13 May 2026 09:08:19 +0200 Subject: [PATCH 08/13] refactor: remove the barrel package --- .github/workflows/release.yml | 16 +++----- README.md | 48 ++++++++++-------------- RELEASING.md | 28 ++++++-------- packages/builder/CHANGELOG.md | 6 +-- packages/builder/README.md | 13 +++---- packages/builder/package.json | 2 +- packages/cli/CHANGELOG.md | 8 ++-- packages/cli/README.md | 13 +++---- packages/cli/package.json | 4 +- packages/cli/src/runBuilder.ts | 2 +- packages/cli/src/runCompiler.ts | 2 +- packages/cli/test/runCompiler.test.ts | 8 ++-- packages/simulator/CHANGELOG.md | 4 +- packages/simulator/README.md | 4 +- packages/simulator/package.json | 2 +- packages/tools/CHANGELOG.md | 22 ----------- packages/tools/README.md | 49 ------------------------- packages/tools/package.json | 53 --------------------------- packages/tools/src/index.ts | 50 ------------------------- packages/tools/src/runBuilder.ts | 7 ---- packages/tools/src/runCompiler.ts | 7 ---- packages/tools/tsconfig.json | 21 ----------- yarn.lock | 30 ++++----------- 23 files changed, 77 insertions(+), 322 deletions(-) delete mode 100644 packages/tools/CHANGELOG.md delete mode 100644 packages/tools/README.md delete mode 100644 packages/tools/package.json delete mode 100644 packages/tools/src/index.ts delete mode 100644 packages/tools/src/runBuilder.ts delete mode 100644 packages/tools/src/runCompiler.ts delete mode 100644 packages/tools/tsconfig.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dac35ae..1a25ac4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,10 +8,9 @@ on: required: true type: choice options: - - compact-tools - - compact-tools-builder - - compact-tools-cli - - compact-tools-simulator + - compact-builder + - compact-cli + - compact-simulator version_bump: description: "Version bump type" required: true @@ -48,16 +47,13 @@ jobs: id: pkg run: | case "${{ inputs.package }}" in - "compact-tools") - echo "dir=tools" >> $GITHUB_OUTPUT - ;; - "compact-tools-builder") + "compact-builder") echo "dir=builder" >> $GITHUB_OUTPUT ;; - "compact-tools-cli") + "compact-cli") echo "dir=cli" >> $GITHUB_OUTPUT ;; - "compact-tools-simulator") + "compact-simulator") echo "dir=simulator" >> $GITHUB_OUTPUT ;; esac diff --git a/README.md b/README.md index adee169..7dbfe9f 100644 --- a/README.md +++ b/README.md @@ -11,41 +11,32 @@ Tools for compiling, building, and testing Compact smart contracts. This is a mo - `packages/builder`: Programmatic library that drives the Compact compiler + builder - `packages/cli`: Thin bin wrapper around the builder library (`compact-compiler`, `compact-builder`) - `packages/simulator`: TypeScript simulator to run and test Compact contracts locally -- `packages/tools`: Umbrella package re-exporting builder + simulator from a single root import, plus the CLI bins - ## Installation -The fastest path is the umbrella package, which gives you the CLI binaries and -the libraries from a single import path: +Pick the package that matches what you need: ```bash -yarn add --dev @openzeppelin/compact-tools -``` +# Programmatic library — call the compiler/builder from TypeScript +yarn add --dev @openzeppelin/compact-builder -```ts -import { - createSimulator, - CompactCompiler, - CompactBuilder, -} from '@openzeppelin/compact-tools'; +# CLI bins (compact-compiler, compact-builder) for use in package.json scripts +yarn add --dev @openzeppelin/compact-cli + +# Simulator — test Compact contracts locally +yarn add --dev @openzeppelin/compact-simulator ``` +`compact-cli` depends transitively on `compact-builder`, so installing the CLI +gives you both the binaries and the underlying library. + ```bash yarn compact-compiler --help yarn compact-builder --help ``` -If you want only one piece, install the corresponding constituent directly: - -```bash -# Programmatic library only (no bins) -yarn add --dev @openzeppelin/compact-tools-builder - -# Simulator only (test/runtime side) -yarn add --dev @openzeppelin/compact-tools-simulator - -# CLI bins only (depends transitively on -builder) -yarn add --dev @openzeppelin/compact-tools-cli +```ts +import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-builder'; +import { createSimulator } from '@openzeppelin/compact-simulator'; ``` ### Developing against unreleased changes @@ -60,8 +51,9 @@ yarn --cwd tools/compact-tools build # In your package.json "devDependencies": { - "@openzeppelin/compact-tools-simulator": "file:./tools/compact-tools/packages/simulator", - "@openzeppelin/compact-tools-cli": "file:./tools/compact-tools/packages/cli" + "@openzeppelin/compact-builder": "file:./tools/compact-tools/packages/builder", + "@openzeppelin/compact-cli": "file:./tools/compact-tools/packages/cli", + "@openzeppelin/compact-simulator": "file:./tools/compact-tools/packages/simulator" } ``` @@ -117,7 +109,7 @@ yarn clean ## Packages -### `@openzeppelin/compact-tools-cli` ([packages/cli](./packages/cli)) +### `@openzeppelin/compact-cli` ([packages/cli](./packages/cli)) CLI utilities for compiling and building Compact smart contracts. @@ -148,14 +140,14 @@ compact-builder \ See [packages/cli/README.md](./packages/cli/README.md) for full documentation including all options, programmatic API, and examples. -### `@openzeppelin/compact-tools-simulator` ([packages/simulator](./packages/simulator)) +### `@openzeppelin/compact-simulator` ([packages/simulator](./packages/simulator)) TypeScript simulator for testing Compact contracts locally. **Quickstart:** ```ts -import { createSimulator } from '@openzeppelin/compact-tools-simulator'; +import { createSimulator } from '@openzeppelin/compact-simulator'; const simulator = createSimulator({}); // Deploy and execute contract circuits, inspect state, etc. diff --git a/RELEASING.md b/RELEASING.md index b68e91c..0de78fd 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -29,31 +29,25 @@ step is currently manual — see the TODO below for the planned migration to ## First-release order -There's a dependency chain across the four published packages: +There's a one-step dependency chain across the three published packages: ``` -compact-tools (umbrella) - ├─ depends on compact-tools-builder - ├─ depends on compact-tools-cli - └─ depends on compact-tools-simulator -compact-tools-cli (bin wrapper) - └─ depends on compact-tools-builder -compact-tools-builder (library) -compact-tools-simulator (library) +compact-cli (bin wrapper) + └─ depends on compact-builder +compact-builder (library) +compact-simulator (library) ``` -Each `workspace:^` dep is rewritten by yarn into the resolved version at +The `workspace:^` dep is rewritten by yarn into the resolved version at `yarn pack` time. For the very first release, publish in dependency order so each dependent finds its deps already on npm: -1. `compact-tools-builder` (no internal deps) -2. `compact-tools-simulator` (no internal deps) -3. `compact-tools-cli` (depends on `-builder`; pull `main` first so the bump - commit is present locally) -4. `compact-tools` (umbrella; pull `main` first so the bumps for `-builder`, - `-cli`, and `-simulator` are present) +1. `compact-builder` (no internal deps) +2. `compact-simulator` (no internal deps) +3. `compact-cli` (depends on `-builder`; pull `main` first so the bump + commit is present locally before triggering) -After the first release, the four packages version independently — bump any +After the first release, the three packages version independently — bump any one of them in isolation without re-publishing the others. ## TODO: migrate to changesets diff --git a/packages/builder/CHANGELOG.md b/packages/builder/CHANGELOG.md index 229e217..4cf6c83 100644 --- a/packages/builder/CHANGELOG.md +++ b/packages/builder/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to `@openzeppelin/compact-tools-builder` will be documented in this file. +All notable changes to `@openzeppelin/compact-builder` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial public release of `@openzeppelin/compact-tools-builder`. +- Initial public release of `@openzeppelin/compact-builder`. - Programmatic API for orchestrating the Compact toolchain: - `CompactCompiler` — compiles `.compact` files to artifacts. - `CompactBuilder` — runs `CompactCompiler` then assembles a publishable @@ -34,5 +34,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Notes - This package contains the library code that previously lived under - `@openzeppelin/compact-tools-cli`. The CLI package now ships only the bin + `@openzeppelin/compact-cli`. The CLI package now ships only the bin entries (`compact-compiler`, `compact-builder`) and depends on this library. diff --git a/packages/builder/README.md b/packages/builder/README.md index 6471464..aeb58e8 100644 --- a/packages/builder/README.md +++ b/packages/builder/README.md @@ -1,4 +1,4 @@ -# @openzeppelin/compact-tools-builder +# @openzeppelin/compact-builder Programmatic library for compiling and building Compact smart contracts on the Midnight network. Drives the `compactc` toolchain with progress reporting, @@ -6,19 +6,19 @@ structured error handling, and configurable output layouts. This is the **library** — it ships no CLI binaries. If you want the bins (`compact-compiler`, `compact-builder`) for use in `package.json` scripts, -install [`@openzeppelin/compact-tools-cli`](../cli) instead, which is a thin +install [`@openzeppelin/compact-cli`](../cli) instead, which is a thin wrapper around this library. ## Install ```bash -yarn add --dev @openzeppelin/compact-tools-builder +yarn add --dev @openzeppelin/compact-builder ``` ## Quick Start ```ts -import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools-builder'; +import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-builder'; // Compile all .compact files in src/ to artifacts/ await new CompactCompiler({ flags: '--skip-zk' }).compile(); @@ -61,9 +61,8 @@ export class DirectoryNotFoundError extends Error { /* … */ } ## See also -- [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — bin wrapper around this library -- [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — TypeScript simulator for testing Compact contracts locally -- [`@openzeppelin/compact-tools`](https://www.npmjs.com/package/@openzeppelin/compact-tools) — umbrella package giving you everything under subpath exports +- [`@openzeppelin/compact-cli`](https://www.npmjs.com/package/@openzeppelin/compact-cli) — bin wrapper around this library +- [`@openzeppelin/compact-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-simulator) — TypeScript simulator for testing Compact contracts locally See the [monorepo README](https://github.com/OpenZeppelin/compact-tools#readme) for the full developer guide. diff --git a/packages/builder/package.json b/packages/builder/package.json index 4e258dd..b859d04 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,5 +1,5 @@ { - "name": "@openzeppelin/compact-tools-builder", + "name": "@openzeppelin/compact-builder", "description": "Programmatic library for compiling and building Compact smart contracts", "version": "0.0.1", "keywords": [ diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index e2b9740..69b6c47 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to `@openzeppelin/compact-tools-cli` will be documented in this file. +All notable changes to `@openzeppelin/compact-cli` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). @@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial public release of `@openzeppelin/compact-tools-cli` as a thin bin - wrapper around [`@openzeppelin/compact-tools-builder`](../builder). +- Initial public release of `@openzeppelin/compact-cli` as a thin bin + wrapper around [`@openzeppelin/compact-builder`](../builder). - `compact-compiler` bin — orchestrates `compact compile` over a project's `.compact` files with progress reporting and structured error handling. - `compact-builder` bin — runs the compiler then assembles a publishable @@ -31,5 +31,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The programmatic API (CompactCompiler, CompactBuilder, services, types) previously shipped from this package has moved to - `@openzeppelin/compact-tools-builder`. This package now ships only the bin + `@openzeppelin/compact-builder`. This package now ships only the bin entries. diff --git a/packages/cli/README.md b/packages/cli/README.md index b11319c..b6ffa49 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,6 +1,6 @@ -# @openzeppelin/compact-tools-cli +# @openzeppelin/compact-cli -CLI wrapper around [`@openzeppelin/compact-tools-builder`](../builder). +CLI wrapper around [`@openzeppelin/compact-builder`](../builder). Provides the `compact-compiler` and `compact-builder` binaries for use in `package.json` scripts. Contains no programmatic API of its own — if you want to call the compiler/builder from TypeScript, use the library package directly. @@ -8,7 +8,7 @@ to call the compiler/builder from TypeScript, use the library package directly. ## Install ```bash -yarn add --dev @openzeppelin/compact-tools-cli +yarn add --dev @openzeppelin/compact-cli ``` ## Use @@ -49,7 +49,7 @@ options: | `--clean-dist` | builder | `rm -rf dist` before building. | | `--copy ` | builder | Copy an extra file into `dist/` (repeatable; e.g. `package.json`, `../README.md`). | -See [`@openzeppelin/compact-tools-builder`](../builder) for the full +See [`@openzeppelin/compact-builder`](../builder) for the full documentation, programmatic API, and behavioural details. ## Requirements @@ -64,9 +64,8 @@ Compactc version: 0.29.0 ## See also -- [`@openzeppelin/compact-tools-builder`](https://www.npmjs.com/package/@openzeppelin/compact-tools-builder) — programmatic library backing this CLI -- [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — simulator for testing Compact contracts -- [`@openzeppelin/compact-tools`](https://www.npmjs.com/package/@openzeppelin/compact-tools) — umbrella package giving you all three under subpath exports +- [`@openzeppelin/compact-builder`](https://www.npmjs.com/package/@openzeppelin/compact-builder) — programmatic library backing this CLI +- [`@openzeppelin/compact-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-simulator) — simulator for testing Compact contracts ## License diff --git a/packages/cli/package.json b/packages/cli/package.json index d267e21..4045916 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,5 +1,5 @@ { - "name": "@openzeppelin/compact-tools-cli", + "name": "@openzeppelin/compact-cli", "description": "CLI for compiling and building Compact smart contracts", "version": "0.0.1", "keywords": [ @@ -42,7 +42,7 @@ "vitest": "^4.0.15" }, "dependencies": { - "@openzeppelin/compact-tools-builder": "workspace:^", + "@openzeppelin/compact-builder": "workspace:^", "chalk": "^5.6.2", "ora": "^9.0.0" } diff --git a/packages/cli/src/runBuilder.ts b/packages/cli/src/runBuilder.ts index 0d1b26d..74b1e1c 100644 --- a/packages/cli/src/runBuilder.ts +++ b/packages/cli/src/runBuilder.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { CompactBuilder } from '@openzeppelin/compact-tools-builder'; +import { CompactBuilder } from '@openzeppelin/compact-builder'; import chalk from 'chalk'; import ora from 'ora'; diff --git a/packages/cli/src/runCompiler.ts b/packages/cli/src/runCompiler.ts index cdbdbc3..99708c5 100644 --- a/packages/cli/src/runCompiler.ts +++ b/packages/cli/src/runCompiler.ts @@ -4,7 +4,7 @@ import { CompactCompiler, type CompilationError, isPromisifiedChildProcessError, -} from '@openzeppelin/compact-tools-builder'; +} from '@openzeppelin/compact-builder'; import chalk from 'chalk'; import ora, { type Ora } from 'ora'; diff --git a/packages/cli/test/runCompiler.test.ts b/packages/cli/test/runCompiler.test.ts index 90017dc..6da4192 100644 --- a/packages/cli/test/runCompiler.test.ts +++ b/packages/cli/test/runCompiler.test.ts @@ -5,14 +5,14 @@ import { DirectoryNotFoundError, isPromisifiedChildProcessError, type PromisifiedChildProcessError, -} from '@openzeppelin/compact-tools-builder'; +} from '@openzeppelin/compact-builder'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // Mock the library so we can drive the CLI in isolation. -vi.mock('@openzeppelin/compact-tools-builder', async () => { +vi.mock('@openzeppelin/compact-builder', async () => { const actual = await vi.importActual< - typeof import('@openzeppelin/compact-tools-builder') - >('@openzeppelin/compact-tools-builder'); + typeof import('@openzeppelin/compact-builder') + >('@openzeppelin/compact-builder'); return { ...actual, CompactCompiler: { diff --git a/packages/simulator/CHANGELOG.md b/packages/simulator/CHANGELOG.md index a8e068b..08ed6b8 100644 --- a/packages/simulator/CHANGELOG.md +++ b/packages/simulator/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to `@openzeppelin/compact-tools-simulator` will be documented in this file. +All notable changes to `@openzeppelin/compact-simulator` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial public release of `@openzeppelin/compact-tools-simulator`. +- Initial public release of `@openzeppelin/compact-simulator`. - `createSimulator` factory for building type-safe per-contract simulator classes, with configurable private state, ledger extractor, witnesses and contract constructor arguments. diff --git a/packages/simulator/README.md b/packages/simulator/README.md index 80a429b..c846d79 100644 --- a/packages/simulator/README.md +++ b/packages/simulator/README.md @@ -13,7 +13,7 @@ allowing you to simulate contract behavior locally without blockchain deployment ## Quick Start ```typescript -import { createSimulator } from '@openzeppelin/compact-tools-simulator'; +import { createSimulator } from '@openzeppelin/compact-simulator'; import { Contract, ledger } from './artifacts/MyContract/contract/index.js'; // 1. Define your contract arguments type @@ -44,7 +44,7 @@ const sim = new MySimulator([ownerAddress, 100n], { coinPK: deployerPK }); The base simulator acts as a configuration class that the actual simulator will extend: ```typescript -import { createSimulator } from '@openzeppelin/compact-tools-simulator'; +import { createSimulator } from '@openzeppelin/compact-simulator'; import { Contract as MyContract, ledger } from './artifacts/MyContract/contract/index.js'; import { MyContractWitnesses, MyContractPrivateState } from './MyContractWitnesses.js'; diff --git a/packages/simulator/package.json b/packages/simulator/package.json index 80e45d9..ac7b495 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -1,5 +1,5 @@ { - "name": "@openzeppelin/compact-tools-simulator", + "name": "@openzeppelin/compact-simulator", "description": "Simulator for testing Compact smart contracts", "version": "0.0.1", "keywords": [ diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md deleted file mode 100644 index 3e86bb7..0000000 --- a/packages/tools/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog - -All notable changes to `@openzeppelin/compact-tools` (umbrella) will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Unreleased - -### Added - -- Initial public release of the `@openzeppelin/compact-tools` umbrella. -- Single root export re-exporting everything from - `@openzeppelin/compact-tools-builder` and - `@openzeppelin/compact-tools-simulator`, so consumers can pull anything off - the umbrella with a single import: - ```ts - import { createSimulator, CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools'; - ``` -- `compact-compiler` and `compact-builder` bin entries that delegate to - `@openzeppelin/compact-tools-cli`, so a single install of the umbrella gives - consumers both the binaries and the programmatic API surface. diff --git a/packages/tools/README.md b/packages/tools/README.md deleted file mode 100644 index 5ff0cd0..0000000 --- a/packages/tools/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# @openzeppelin/compact-tools - -Umbrella package for the OpenZeppelin Compact developer tools. One install -gives you the build library, the simulator, and the CLI binaries — all -reachable from a single import path. - -## Install - -```bash -yarn add --dev @openzeppelin/compact-tools -``` - -## Use - -```ts -import { - // simulator - createSimulator, - // builder library - CompactCompiler, - CompactBuilder, -} from '@openzeppelin/compact-tools'; -``` - -The package also exposes the `compact-compiler` and `compact-builder` binaries: - -```bash -yarn compact-compiler --help -yarn compact-builder --help -``` - -Both binaries delegate to -[`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli), -which in turn calls into -[`@openzeppelin/compact-tools-builder`](https://www.npmjs.com/package/@openzeppelin/compact-tools-builder). - -## Want only one piece? - -You can install constituents directly and skip the umbrella: - -- [`@openzeppelin/compact-tools-builder`](https://www.npmjs.com/package/@openzeppelin/compact-tools-builder) — programmatic library (no bins) -- [`@openzeppelin/compact-tools-cli`](https://www.npmjs.com/package/@openzeppelin/compact-tools-cli) — bin wrapper only -- [`@openzeppelin/compact-tools-simulator`](https://www.npmjs.com/package/@openzeppelin/compact-tools-simulator) — simulator only - -See the [monorepo README](https://github.com/OpenZeppelin/compact-tools#readme) for the full developer guide. - -## License - -MIT diff --git a/packages/tools/package.json b/packages/tools/package.json deleted file mode 100644 index a535a58..0000000 --- a/packages/tools/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@openzeppelin/compact-tools", - "description": "Umbrella package re-exporting @openzeppelin/compact-tools-builder and @openzeppelin/compact-tools-simulator from a single root entry, plus the @openzeppelin/compact-tools-cli bins", - "version": "0.0.1", - "keywords": [ - "compact", - "midnight", - "compiler", - "builder", - "simulator", - "testing" - ], - "author": "", - "license": "MIT", - "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "files": [ - "dist", - "README.md", - "CHANGELOG.md", - "LICENSE" - ], - "engines": { - "node": ">=22" - }, - "bin": { - "compact-builder": "dist/runBuilder.js", - "compact-compiler": "dist/runCompiler.js" - }, - "scripts": { - "build": "tsc -p .", - "types": "tsc -p tsconfig.json --noEmit", - "test": "echo 'barrel package — see @openzeppelin/compact-tools-cli and @openzeppelin/compact-tools-simulator for tests'", - "clean": "git clean -fXd" - }, - "dependencies": { - "@openzeppelin/compact-tools-builder": "workspace:^", - "@openzeppelin/compact-tools-cli": "workspace:^", - "@openzeppelin/compact-tools-simulator": "workspace:^" - }, - "devDependencies": { - "@tsconfig/node24": "^24.0.3", - "@types/node": "24.10.1", - "typescript": "^5.9.3" - } -} diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts deleted file mode 100644 index 414943e..0000000 --- a/packages/tools/src/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Single root export — re-exports everything from the constituent libraries -// so consumers can pull anything off the umbrella with one import. -// -// import { createSimulator, CompactCompiler, CompactBuilder } from '@openzeppelin/compact-tools'; -// -// Each name is re-exported explicitly (rather than via `export *`) so the -// public surface is visible at a glance and biome's `noReExportAll` rule -// stays happy. When a new export is added in a constituent package, add it -// here too. - -// @openzeppelin/compact-tools-builder -export type { - BuilderOnlyOptions, - BuilderOptions, - BuildStep, - CompilerOptions, - CompilerServiceOptions, - ExecFunction, - PromisifiedChildProcessError, -} from '@openzeppelin/compact-tools-builder'; -// biome-ignore lint/performance/noBarrelFile: package entrypoint -export { - CompactBuilder, - CompactCliNotFoundError, - CompactCompiler, - CompilationError, - CompilerService, - DirectoryNotFoundError, - EnvironmentValidator, - FileDiscovery, - isPromisifiedChildProcessError, - UIService, -} from '@openzeppelin/compact-tools-builder'; - -// @openzeppelin/compact-tools-simulator -export type { - BaseSimulatorOptions, - ContextlessCircuits, - ExtractImpureCircuits, - ExtractPureCircuits, - IContractSimulator, - IMinimalContract, - SimulatorConfig, -} from '@openzeppelin/compact-tools-simulator'; -export { - AbstractSimulator, - CircuitContextManager, - ContractSimulator, - createSimulator, -} from '@openzeppelin/compact-tools-simulator'; diff --git a/packages/tools/src/runBuilder.ts b/packages/tools/src/runBuilder.ts deleted file mode 100644 index 645700c..0000000 --- a/packages/tools/src/runBuilder.ts +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env node - -// Thin shim that delegates to the @openzeppelin/compact-tools-cli bin entry. -// Declared on `@openzeppelin/compact-tools` so users who install the umbrella -// package get the `compact-builder` binary without also installing the CLI -// package separately. -import '@openzeppelin/compact-tools-cli/run-builder'; diff --git a/packages/tools/src/runCompiler.ts b/packages/tools/src/runCompiler.ts deleted file mode 100644 index 518e575..0000000 --- a/packages/tools/src/runCompiler.ts +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env node - -// Thin shim that delegates to the @openzeppelin/compact-tools-cli bin entry. -// Declared on `@openzeppelin/compact-tools` so users who install the umbrella -// package get the `compact-compiler` binary without also installing the CLI -// package separately. -import '@openzeppelin/compact-tools-cli/run-compiler'; diff --git a/packages/tools/tsconfig.json b/packages/tools/tsconfig.json deleted file mode 100644 index e2ab1e9..0000000 --- a/packages/tools/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": "@tsconfig/node24/tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "types": ["node"], - "declaration": true, - "skipLibCheck": true, - "sourceMap": true, - "rewriteRelativeImportExtensions": true, - "erasableSyntaxOnly": true, - "verbatimModuleSyntax": true - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/yarn.lock b/yarn.lock index 867dfc4..08914e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -381,9 +381,9 @@ __metadata: languageName: node linkType: hard -"@openzeppelin/compact-tools-builder@workspace:^, @openzeppelin/compact-tools-builder@workspace:packages/builder": +"@openzeppelin/compact-builder@workspace:^, @openzeppelin/compact-builder@workspace:packages/builder": version: 0.0.0-use.local - resolution: "@openzeppelin/compact-tools-builder@workspace:packages/builder" + resolution: "@openzeppelin/compact-builder@workspace:packages/builder" dependencies: "@tsconfig/node24": "npm:^24.0.3" "@types/node": "npm:24.10.1" @@ -395,11 +395,11 @@ __metadata: languageName: unknown linkType: soft -"@openzeppelin/compact-tools-cli@workspace:^, @openzeppelin/compact-tools-cli@workspace:packages/cli": +"@openzeppelin/compact-cli@workspace:packages/cli": version: 0.0.0-use.local - resolution: "@openzeppelin/compact-tools-cli@workspace:packages/cli" + resolution: "@openzeppelin/compact-cli@workspace:packages/cli" dependencies: - "@openzeppelin/compact-tools-builder": "workspace:^" + "@openzeppelin/compact-builder": "workspace:^" "@tsconfig/node24": "npm:^24.0.3" "@types/node": "npm:24.10.1" chalk: "npm:^5.6.2" @@ -412,9 +412,9 @@ __metadata: languageName: unknown linkType: soft -"@openzeppelin/compact-tools-simulator@workspace:^, @openzeppelin/compact-tools-simulator@workspace:packages/simulator": +"@openzeppelin/compact-simulator@workspace:packages/simulator": version: 0.0.0-use.local - resolution: "@openzeppelin/compact-tools-simulator@workspace:packages/simulator" + resolution: "@openzeppelin/compact-simulator@workspace:packages/simulator" dependencies: "@midnight-ntwrk/compact-runtime": "npm:0.14.0" "@midnight-ntwrk/ledger-v7": "npm:^7.0.0" @@ -426,22 +426,6 @@ __metadata: languageName: unknown linkType: soft -"@openzeppelin/compact-tools@workspace:packages/tools": - version: 0.0.0-use.local - resolution: "@openzeppelin/compact-tools@workspace:packages/tools" - dependencies: - "@openzeppelin/compact-tools-builder": "workspace:^" - "@openzeppelin/compact-tools-cli": "workspace:^" - "@openzeppelin/compact-tools-simulator": "workspace:^" - "@tsconfig/node24": "npm:^24.0.3" - "@types/node": "npm:24.10.1" - typescript: "npm:^5.9.3" - bin: - compact-builder: dist/runBuilder.js - compact-compiler: dist/runCompiler.js - languageName: unknown - linkType: soft - "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" From d8aca732ff89db366b15cd5ee93e183a19bd2ef6 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Wed, 13 May 2026 10:32:39 +0200 Subject: [PATCH 09/13] chore: ignore .claude/ directory --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 2a32ed6..075ffa5 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ coverage *~ *temp + +.claude/ From bec5468a42b567d96fabbd2cb645c04b71b9bf8d Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Wed, 13 May 2026 10:32:59 +0200 Subject: [PATCH 10/13] fix(builder): prevent shell injection by using execFile + shell-quote --- packages/builder/package.json | 4 +- .../builder/src/services/CompilerService.ts | 48 ++++-- .../src/services/EnvironmentValidator.ts | 20 +-- packages/builder/src/types/options.ts | 16 +- packages/builder/test/Compiler.test.ts | 139 ++++++++++++------ yarn.lock | 16 ++ 6 files changed, 169 insertions(+), 74 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index b859d04..77a2db6 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -38,12 +38,14 @@ "devDependencies": { "@tsconfig/node24": "^24.0.3", "@types/node": "24.10.1", + "@types/shell-quote": "^1.7.5", "typescript": "^5.9.3", "vitest": "^4.0.15" }, "dependencies": { "chalk": "^5.6.2", "log-symbols": "^7.0.0", - "ora": "^9.0.0" + "ora": "^9.0.0", + "shell-quote": "^1.8.3" } } diff --git a/packages/builder/src/services/CompilerService.ts b/packages/builder/src/services/CompilerService.ts index ad6cd52..9c9f046 100644 --- a/packages/builder/src/services/CompilerService.ts +++ b/packages/builder/src/services/CompilerService.ts @@ -1,6 +1,7 @@ -import { exec as execCallback } from 'node:child_process'; +import { execFile as execFileCallback } from 'node:child_process'; import { basename, dirname, join } from 'node:path'; import { promisify } from 'node:util'; +import { parse as parseShellArgs } from 'shell-quote'; import { CompilationError } from '../types/errors.ts'; import { type CompilerServiceOptions, @@ -12,9 +13,29 @@ import { /** Resolved options for CompilerService with defaults applied */ type ResolvedCompilerServiceOptions = Required; +const defaultExecFn: ExecFunction = (file, args) => + promisify(execFileCallback)(file, [...args]); + +/** + * Tokenizes a user-supplied `flags` string into discrete argv entries using + * `shell-quote` (the same rules a shell would apply for splitting). Any + * non-string tokens (e.g. operators like `;`, `&&`) are filtered out so they + * cannot leak into argv as data — defense in depth against command injection + * via the `flags` option. + */ +function tokenizeFlags(flags: string): string[] { + if (!flags) { + return []; + } + return parseShellArgs(flags).filter( + (token): token is string => typeof token === 'string', + ); +} + /** * Service responsible for compiling individual .compact files. - * Handles command construction, execution, and error processing. + * Builds argv arrays and invokes the Compact CLI via `child_process.execFile` + * (no shell), so user-supplied values cannot inject extra commands. * * @example * ```typescript @@ -33,11 +54,12 @@ export class CompilerService { /** * Creates a new CompilerService instance. * - * @param execFn - Function to execute shell commands (defaults to promisified child_process.exec) + * @param execFn - Function to invoke the Compact CLI binary (defaults to + * a promisified `child_process.execFile` — argv array, no shell). * @param options - Compiler service options */ constructor( - execFn: ExecFunction = promisify(execCallback), + execFn: ExecFunction = defaultExecFn, options: CompilerServiceOptions = {}, ) { this.execFn = execFn; @@ -50,13 +72,15 @@ export class CompilerService { /** * Compiles a single .compact file using the Compact CLI. - * Constructs the appropriate command with flags and version, then executes it. + * Builds the argv array (no shell interpolation) and invokes the binary. * * By default, uses flattened output structure where all artifacts go to `//`. * When `hierarchical` is true, preserves source directory structure: `///`. * * @param file - Relative path to the .compact file from srcDir - * @param flags - Space-separated compiler flags (e.g., '--skip-zk --verbose') + * @param flags - Space-separated compiler flags (e.g., '--skip-zk --verbose'). + * Tokenized via `shell-quote` so quoted whitespace is preserved + * and shell operators (`;`, `&&`, …) cannot inject commands. * @param version - Optional specific toolchain version to use * @returns Promise resolving to compilation output (stdout/stderr) * @throws {CompilationError} If compilation fails for any reason @@ -77,12 +101,16 @@ export class CompilerService { ? join(this.options.outDir, fileDir, fileName) : join(this.options.outDir, fileName); - const versionFlag = version ? `+${version}` : ''; - const flagsStr = flags ? ` ${flags}` : ''; - const command = `compact compile${versionFlag ? ` ${versionFlag}` : ''}${flagsStr} "${inputPath}" "${outputDir}"`; + const args: string[] = [ + 'compile', + ...(version ? [`+${version}`] : []), + ...tokenizeFlags(flags), + inputPath, + outputDir, + ]; try { - return await this.execFn(command); + return await this.execFn('compact', args); } catch (error: unknown) { let message: string; diff --git a/packages/builder/src/services/EnvironmentValidator.ts b/packages/builder/src/services/EnvironmentValidator.ts index 08a3b41..4c99821 100644 --- a/packages/builder/src/services/EnvironmentValidator.ts +++ b/packages/builder/src/services/EnvironmentValidator.ts @@ -1,8 +1,11 @@ -import { exec as execCallback } from 'node:child_process'; +import { execFile as execFileCallback } from 'node:child_process'; import { promisify } from 'node:util'; import { CompactCliNotFoundError } from '../types/errors.ts'; import type { ExecFunction } from '../types/options.ts'; +const defaultExecFn: ExecFunction = (file, args) => + promisify(execFileCallback)(file, [...args]); + /** * Service responsible for validating the Compact CLI environment. * Checks CLI availability, retrieves version information, and ensures @@ -21,9 +24,10 @@ export class EnvironmentValidator { /** * Creates a new EnvironmentValidator instance. * - * @param execFn - Function to execute shell commands (defaults to promisified child_process.exec) + * @param execFn - Function to execute the Compact CLI binary (defaults to + * a promisified `child_process.execFile` — argv array, no shell). */ - constructor(execFn: ExecFunction = promisify(execCallback)) { + constructor(execFn: ExecFunction = defaultExecFn) { this.execFn = execFn; } @@ -34,7 +38,7 @@ export class EnvironmentValidator { */ async checkCompactAvailable(): Promise { try { - await this.execFn('compact --version'); + await this.execFn('compact', ['--version']); return true; } catch { return false; @@ -48,7 +52,7 @@ export class EnvironmentValidator { * @throws {Error} If the CLI is not available or command fails */ async getDevToolsVersion(): Promise { - const { stdout } = await this.execFn('compact --version'); + const { stdout } = await this.execFn('compact', ['--version']); return stdout.trim(); } @@ -60,10 +64,8 @@ export class EnvironmentValidator { * @throws {Error} If the CLI is not available or command fails */ async getToolchainVersion(version?: string): Promise { - const versionFlag = version ? `+${version}` : ''; - const { stdout } = await this.execFn( - `compact compile ${versionFlag} --version`, - ); + const args = ['compile', ...(version ? [`+${version}`] : []), '--version']; + const { stdout } = await this.execFn('compact', args); return stdout.trim(); } diff --git a/packages/builder/src/types/options.ts b/packages/builder/src/types/options.ts index 87d0711..76cf464 100644 --- a/packages/builder/src/types/options.ts +++ b/packages/builder/src/types/options.ts @@ -24,14 +24,20 @@ export const DEFAULT_EXCLUDE_PATTERNS: readonly string[] = [ ]; /** - * Function type for executing shell commands. - * Allows dependency injection for testing and customization. + * Function type for executing a child process. * - * @param command - The shell command to execute - * @returns Promise resolving to command output + * Matches the shape of `promisify(child_process.execFile)`: the binary name + * (no shell), followed by positional arguments. This signature is injection- + * safe by construction — values flow as separate argv entries rather than + * being interpolated into a shell command string. + * + * @param file - The binary to invoke (e.g. `'compact'`) + * @param args - Positional arguments passed verbatim to the binary + * @returns Promise resolving to the captured stdout/stderr */ export type ExecFunction = ( - command: string, + file: string, + args: readonly string[], ) => Promise<{ stdout: string; stderr: string }>; /** diff --git a/packages/builder/test/Compiler.test.ts b/packages/builder/test/Compiler.test.ts index e2c60d2..32510b8 100644 --- a/packages/builder/test/Compiler.test.ts +++ b/packages/builder/test/Compiler.test.ts @@ -69,7 +69,7 @@ describe('EnvironmentValidator', () => { const result = await validator.checkCompactAvailable(); expect(result).toBe(true); - expect(mockExec).toHaveBeenCalledWith('compact --version'); + expect(mockExec).toHaveBeenCalledWith('compact', ['--version']); }); it('should return false when compact CLI is not available', async () => { @@ -78,7 +78,7 @@ describe('EnvironmentValidator', () => { const result = await validator.checkCompactAvailable(); expect(result).toBe(false); - expect(mockExec).toHaveBeenCalledWith('compact --version'); + expect(mockExec).toHaveBeenCalledWith('compact', ['--version']); }); }); @@ -89,7 +89,7 @@ describe('EnvironmentValidator', () => { const version = await validator.getDevToolsVersion(); expect(version).toBe('compact 0.1.0'); - expect(mockExec).toHaveBeenCalledWith('compact --version'); + expect(mockExec).toHaveBeenCalledWith('compact', ['--version']); }); it('should throw error when command fails', async () => { @@ -111,7 +111,10 @@ describe('EnvironmentValidator', () => { const version = await validator.getToolchainVersion(); expect(version).toBe('Compactc version: 0.26.0'); - expect(mockExec).toHaveBeenCalledWith('compact compile --version'); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--version', + ]); }); it('should get version with specific version flag', async () => { @@ -123,9 +126,11 @@ describe('EnvironmentValidator', () => { const version = await validator.getToolchainVersion('0.26.0'); expect(version).toBe('Compactc version: 0.26.0'); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile +0.26.0 --version', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '+0.26.0', + '--version', + ]); }); }); @@ -312,9 +317,12 @@ describe('CompilerService', () => { const result = await service.compileFile('MyToken.compact', '--skip-zk'); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "src/MyToken.compact" "artifacts/MyToken"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'src/MyToken.compact', + 'artifacts/MyToken', + ]); }); it('should compile file with version flag', async () => { @@ -330,9 +338,13 @@ describe('CompilerService', () => { ); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile +0.26.0 --skip-zk "src/MyToken.compact" "artifacts/MyToken"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '+0.26.0', + '--skip-zk', + 'src/MyToken.compact', + 'artifacts/MyToken', + ]); }); it('should handle empty flags', async () => { @@ -344,9 +356,11 @@ describe('CompilerService', () => { const result = await service.compileFile('MyToken.compact', ''); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile "src/MyToken.compact" "artifacts/MyToken"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + 'src/MyToken.compact', + 'artifacts/MyToken', + ]); }); it('should use flattened artifacts output by default', async () => { @@ -361,9 +375,12 @@ describe('CompilerService', () => { ); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "src/access/AccessControl.compact" "artifacts/AccessControl"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'src/access/AccessControl.compact', + 'artifacts/AccessControl', + ]); }); it('should flatten nested directory structure by default', async () => { @@ -378,9 +395,12 @@ describe('CompilerService', () => { ); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "src/access/test/AccessControl.mock.compact" "artifacts/AccessControl.mock"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'src/access/test/AccessControl.mock.compact', + 'artifacts/AccessControl.mock', + ]); }); it('should throw CompilationError when compilation fails', async () => { @@ -432,9 +452,12 @@ describe('CompilerService', () => { ); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "src/access/AccessControl.compact" "artifacts/access/AccessControl"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'src/access/AccessControl.compact', + 'artifacts/access/AccessControl', + ]); }); it('should preserve nested directory structure when hierarchical is true', async () => { @@ -449,9 +472,12 @@ describe('CompilerService', () => { ); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "src/access/test/AccessControl.mock.compact" "artifacts/access/test/AccessControl.mock"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'src/access/test/AccessControl.mock.compact', + 'artifacts/access/test/AccessControl.mock', + ]); }); it('should use flattened output for root-level files even when hierarchical is true', async () => { @@ -463,9 +489,12 @@ describe('CompilerService', () => { const result = await service.compileFile('MyToken.compact', '--skip-zk'); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "src/MyToken.compact" "artifacts/MyToken"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'src/MyToken.compact', + 'artifacts/MyToken', + ]); }); }); @@ -486,9 +515,12 @@ describe('CompilerService', () => { const result = await service.compileFile('MyToken.compact', '--skip-zk'); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "contracts/MyToken.compact" "build/MyToken"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'contracts/MyToken.compact', + 'build/MyToken', + ]); }); it('should use custom directories with hierarchical option', async () => { @@ -508,9 +540,12 @@ describe('CompilerService', () => { ); expect(result).toEqual({ stdout: 'Compilation successful', stderr: '' }); - expect(mockExec).toHaveBeenCalledWith( - 'compact compile --skip-zk "contracts/access/AccessControl.compact" "dist/artifacts/access/AccessControl"', - ); + expect(mockExec).toHaveBeenCalledWith('compact', [ + 'compile', + '--skip-zk', + 'contracts/access/AccessControl.compact', + 'dist/artifacts/access/AccessControl', + ]); }); }); }); @@ -903,12 +938,13 @@ describe('CompactCompiler', () => { // Check steps expect(mockExec).toHaveBeenCalledTimes(3); - expect(mockExec).toHaveBeenNthCalledWith(1, 'compact --version'); // validate() calls - expect(mockExec).toHaveBeenNthCalledWith(2, 'compact --version'); // getDevToolsVersion() - expect(mockExec).toHaveBeenNthCalledWith( - 3, - 'compact compile +0.26.0 --version', - ); // getToolchainVersion() + expect(mockExec).toHaveBeenNthCalledWith(1, 'compact', ['--version']); // validate() calls + expect(mockExec).toHaveBeenNthCalledWith(2, 'compact', ['--version']); // getDevToolsVersion() + expect(mockExec).toHaveBeenNthCalledWith(3, 'compact', [ + 'compile', + '+0.26.0', + '--version', + ]); // getToolchainVersion() // Verify passed args expect(displaySpy).toHaveBeenCalledWith( @@ -981,10 +1017,11 @@ describe('CompactCompiler', () => { await compiler.validateEnvironment(); // Verify version-specific toolchain call - expect(mockExec).toHaveBeenNthCalledWith( - 3, - 'compact compile +0.26.0 --version', - ); + expect(mockExec).toHaveBeenNthCalledWith(3, 'compact', [ + 'compile', + '+0.26.0', + '--version', + ]); expect(displaySpy).toHaveBeenCalledWith( 'compact 0.1.0', 'Compactc version: 0.26.0', @@ -1012,7 +1049,10 @@ describe('CompactCompiler', () => { await compiler.validateEnvironment(); // Verify default toolchain call (no version flag) - expect(mockExec).toHaveBeenNthCalledWith(3, 'compact compile --version'); + expect(mockExec).toHaveBeenNthCalledWith(3, 'compact', [ + 'compile', + '--version', + ]); expect(displaySpy).toHaveBeenCalledWith( 'compact 0.1.0', 'Compactc version: 0.26.0', @@ -1058,7 +1098,8 @@ describe('CompactCompiler', () => { await compiler.compile(); expect(mockExec).toHaveBeenCalledWith( - expect.stringContaining('compact compile --skip-zk'), + 'compact', + expect.arrayContaining(['compile', '--skip-zk']), ); }); diff --git a/yarn.lock b/yarn.lock index 08914e6..34e6944 100644 --- a/yarn.lock +++ b/yarn.lock @@ -387,9 +387,11 @@ __metadata: dependencies: "@tsconfig/node24": "npm:^24.0.3" "@types/node": "npm:24.10.1" + "@types/shell-quote": "npm:^1.7.5" chalk: "npm:^5.6.2" log-symbols: "npm:^7.0.0" ora: "npm:^9.0.0" + shell-quote: "npm:^1.8.3" typescript: "npm:^5.9.3" vitest: "npm:^4.0.15" languageName: unknown @@ -669,6 +671,13 @@ __metadata: languageName: node linkType: hard +"@types/shell-quote@npm:^1.7.5": + version: 1.7.5 + resolution: "@types/shell-quote@npm:1.7.5" + checksum: 10/32b4d697c7d23dbadf40713692c47f1595f083a3b3deea76cb18e30a05d197aa9205d2b87f6d92edb60cda120b51e35d32bda96ed9b0a7e32921eed2deb4559e + languageName: node + linkType: hard + "@vitest/expect@npm:4.0.15": version: 4.0.15 resolution: "@vitest/expect@npm:4.0.15" @@ -1769,6 +1778,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.8.3": + version: 1.8.3 + resolution: "shell-quote@npm:1.8.3" + checksum: 10/5473e354637c2bd698911224129c9a8961697486cff1fb221f234d71c153fc377674029b0223d1d3c953a68d451d79366abfe53d1a0b46ee1f28eb9ade928f4c + languageName: node + linkType: hard + "siginfo@npm:^2.0.0": version: 2.0.0 resolution: "siginfo@npm:2.0.0" From e981481f091500a841d58447f1bc12ffc1f8afb6 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Wed, 13 May 2026 11:27:29 +0200 Subject: [PATCH 11/13] fix: address coderabbit review (rethrow build errors, no flag dedup, unwrap CompilationError, parser-error routing) --- README.md | 3 ++- RELEASING.md | 4 ++-- packages/builder/src/Builder.ts | 10 +++++++-- packages/builder/src/Compiler.ts | 18 +++++++-------- packages/builder/test/Compiler.test.ts | 31 +++++++++++++++++++++----- packages/cli/src/runBuilder.ts | 5 +++-- packages/cli/src/runCompiler.ts | 22 +++++++++++++----- packages/cli/test/runCompiler.test.ts | 5 ++++- 8 files changed, 69 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 7dbfe9f..544d444 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,8 @@ compact-builder \ --copy package.json --copy ../README.md ``` -See [packages/cli/README.md](./packages/cli/README.md) for full documentation including all options, programmatic API, and examples. +See [packages/cli/README.md](./packages/cli/README.md) for full CLI documentation, all flags, and examples. +For the programmatic API (`CompactCompiler`, `CompactBuilder`, services and types), see [packages/builder/README.md](./packages/builder/README.md). ### `@openzeppelin/compact-simulator` ([packages/simulator](./packages/simulator)) diff --git a/RELEASING.md b/RELEASING.md index 0de78fd..2f323e9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -31,7 +31,7 @@ step is currently manual — see the TODO below for the planned migration to There's a one-step dependency chain across the three published packages: -``` +```text compact-cli (bin wrapper) └─ depends on compact-builder compact-builder (library) @@ -44,7 +44,7 @@ each dependent finds its deps already on npm: 1. `compact-builder` (no internal deps) 2. `compact-simulator` (no internal deps) -3. `compact-cli` (depends on `-builder`; pull `main` first so the bump +3. `compact-cli` (depends on `compact-builder`; pull `main` first so the bump commit is present locally before triggering) After the first release, the three packages version independently — bump any diff --git a/packages/builder/src/Builder.ts b/packages/builder/src/Builder.ts index 5f41f0d..1c10b69 100755 --- a/packages/builder/src/Builder.ts +++ b/packages/builder/src/Builder.ts @@ -37,7 +37,7 @@ const execAsync = promisify(exec); * // Default: flatten .compact files, exclude Mock* * const builder = new CompactBuilder({ flags: '--skip-zk' }); * - * // Library publish: clean dist, hierarchical tree, exclude mocks + archive, copy metadata + * // Library publish: clean dist, hierarchical tree, exclude mocks + archive, copy metadata. * const builder = new CompactBuilder({ * cleanDist: true, * hierarchical: true, @@ -246,7 +246,13 @@ export class CompactBuilder { console.error(chalk.red('[BUILD] ❌ Build failed:', error.message)); } - process.exit(1); + // Library code must not call process.exit — let the caller (CLI wrapper + // or programmatic consumer) decide how to react. We've already surfaced + // the failure to the user via the spinner + printOutput above. + if (error instanceof Error) { + throw error; + } + throw new Error('[BUILD] Build failed with a non-Error exception'); } } diff --git a/packages/builder/src/Compiler.ts b/packages/builder/src/Compiler.ts index 7d77656..cec51f2 100755 --- a/packages/builder/src/Compiler.ts +++ b/packages/builder/src/Compiler.ts @@ -188,10 +188,9 @@ export class CompactCompiler { } else if (args[i].startsWith('+')) { options.version = args[i].slice(1); } else { - // Only add flag if it's not already present - if (!flags.includes(args[i])) { - flags.push(args[i]); - } + // Forward flags in original order, no dedup — repeatable flags + // (e.g. `--define x=1 --define y=2`) must be preserved as given. + flags.push(args[i]); } } @@ -305,11 +304,12 @@ export class CompactCompiler { } catch (error) { spinner.fail(chalk.red(`[COMPILE] ${step} Failed ${file}`)); - if ( - error instanceof CompilationError && - isPromisifiedChildProcessError(error) - ) { - const execError = error; + // CompilationError wraps the underlying child-process error in `.cause`. + // The previous guard `isPromisifiedChildProcessError(error)` on a + // CompilationError instance was unreachable — unwrap via `.cause` to + // surface compactc's stdout/stderr to the user. + const execError = error instanceof CompilationError ? error.cause : error; + if (isPromisifiedChildProcessError(execError)) { // Filter out compactc version output from compact compile const filteredOutput = execError.stdout.split('\n').slice(1).join('\n'); diff --git a/packages/builder/test/Compiler.test.ts b/packages/builder/test/Compiler.test.ts index 32510b8..4287acf 100644 --- a/packages/builder/test/Compiler.test.ts +++ b/packages/builder/test/Compiler.test.ts @@ -781,12 +781,27 @@ describe('CompactCompiler', () => { expect(compiler.testOptions.flags).toBe('--skip-zk --verbose'); }); - it('should deduplicate flags when both env var and CLI flag are present', () => { + it('should preserve repeatable flags without deduplication', () => { + // Forwarding args in original order is important: repeatable flags + // (e.g. `--define x=1 --define y=2`) must reach the compiler unchanged. + // SKIP_ZK=true env + --skip-zk on the CLI consequently produces two + // `--skip-zk` entries, which is harmless for boolean flags. compiler = CompactCompiler.fromArgs(['--skip-zk', '--verbose'], { SKIP_ZK: 'true', }); - expect(compiler.testOptions.flags).toBe('--skip-zk --verbose'); + expect(compiler.testOptions.flags).toBe('--skip-zk --skip-zk --verbose'); + }); + + it('should forward repeated flags in order', () => { + compiler = CompactCompiler.fromArgs([ + '--define', + 'x=1', + '--define', + 'y=2', + ]); + + expect(compiler.testOptions.flags).toBe('--define x=1 --define y=2'); }); it('should throw error for --dir without argument', () => { @@ -1195,6 +1210,7 @@ describe('CompactCompiler', () => { '+0.26.0', ], env: { SKIP_ZK: 'true' }, + flags: '--skip-zk --no-communications-commitment', }, { name: 'with skip-zk flag only', @@ -1206,8 +1222,12 @@ describe('CompactCompiler', () => { '+0.26.0', ], env: { SKIP_ZK: 'false' }, + flags: '--skip-zk --no-communications-commitment', }, { + // CLI `--skip-zk` plus `SKIP_ZK=true` produces two `--skip-zk` entries + // because we no longer dedup forwarded flags — repeatable flags like + // `--define x=1 --define y=2` must be preserved as given. name: 'with both skip-zk flag and env var', args: [ '--dir', @@ -1217,13 +1237,12 @@ describe('CompactCompiler', () => { '+0.26.0', ], env: { SKIP_ZK: 'true' }, + flags: '--skip-zk --skip-zk --no-communications-commitment', }, - ])('should handle complex command $name', ({ args, env }) => { + ])('should handle complex command $name', ({ args, env, flags }) => { compiler = CompactCompiler.fromArgs(args, env); - expect(compiler.testOptions.flags).toBe( - '--skip-zk --no-communications-commitment', - ); + expect(compiler.testOptions.flags).toBe(flags); expect(compiler.testOptions.targetDir).toBe('security'); expect(compiler.testOptions.version).toBe('0.26.0'); }); diff --git a/packages/cli/src/runBuilder.ts b/packages/cli/src/runBuilder.ts index 74b1e1c..14ab201 100644 --- a/packages/cli/src/runBuilder.ts +++ b/packages/cli/src/runBuilder.ts @@ -34,11 +34,12 @@ import ora from 'ora'; * # Custom source/output dirs * npx compact-builder --src contracts --out build * - * # Library-publish build: clean dist, hierarchical layout, exclude mocks + archive, ship metadata + * # Library-publish build (the split-quoted '*'/'archive'/'*' is just JSDoc + * # — in your own scripts write '/archive/' literally): * npx compact-builder \ * --clean-dist \ * --hierarchical \ - * --exclude 'Mock*' --exclude '*\/archive\/*' \ + * --exclude 'Mock*' --exclude '*.mock.compact' --exclude '*'/'archive'/'*' \ * --copy package.json --copy ../README.md * ``` */ diff --git a/packages/cli/src/runCompiler.ts b/packages/cli/src/runCompiler.ts index 99708c5..4310e76 100644 --- a/packages/cli/src/runCompiler.ts +++ b/packages/cli/src/runCompiler.ts @@ -130,12 +130,17 @@ function handleError(error: unknown, spinner: Ora): void { return; } - // Arg parsing + // Arg parsing — recognize all parser-emitted "flag requires a value" errors, + // not just --dir, so users get usage help for any malformed invocation. const errorMessage = error instanceof Error ? error.message : String(error); - if (errorMessage.includes('--dir flag requires a directory name')) { - spinner.fail( - chalk.red('[COMPILE] Error: --dir flag requires a directory name'), - ); + const parserErrors = [ + '--dir flag requires a directory name', + '--src flag requires a directory path', + '--out flag requires a directory path', + '--exclude flag requires a pattern', + ]; + if (parserErrors.some((msg) => errorMessage.includes(msg))) { + spinner.fail(chalk.red(`[COMPILE] Error: ${errorMessage}`)); showUsageHelp(); return; } @@ -185,12 +190,17 @@ function showUsageHelp(): void { ' --hierarchical Preserve source directory structure in artifacts output', ), ); + console.log( + chalk.yellow( + ' --exclude Skip .compact files matching the glob (repeatable)', + ), + ); console.log( chalk.yellow(' --skip-zk Skip zero-knowledge proof generation'), ); console.log( chalk.yellow( - ' + Use specific toolchain version (e.g., +0.26.0)', + ' + Use specific toolchain version (e.g., +0.29.0)', ), ); console.log(chalk.yellow('\nArtifact Output Structure:')); diff --git a/packages/cli/test/runCompiler.test.ts b/packages/cli/test/runCompiler.test.ts index 6da4192..f62c4b7 100644 --- a/packages/cli/test/runCompiler.test.ts +++ b/packages/cli/test/runCompiler.test.ts @@ -332,11 +332,14 @@ describe('runCompiler CLI', () => { expect(mockConsoleLog).toHaveBeenCalledWith( ' --hierarchical Preserve source directory structure in artifacts output', ); + expect(mockConsoleLog).toHaveBeenCalledWith( + ' --exclude Skip .compact files matching the glob (repeatable)', + ); expect(mockConsoleLog).toHaveBeenCalledWith( ' --skip-zk Skip zero-knowledge proof generation', ); expect(mockConsoleLog).toHaveBeenCalledWith( - ' + Use specific toolchain version (e.g., +0.26.0)', + ' + Use specific toolchain version (e.g., +0.29.0)', ); expect(mockConsoleLog).toHaveBeenCalledWith( '\nArtifact Output Structure:', From 7f70dc9a9a62972823532c2de399b04752111926 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Fri, 15 May 2026 12:46:07 +0200 Subject: [PATCH 12/13] docs: address review feedback (drop premature changelogs, slim READMEs, generalize toolchain version) - Drop pre-baseline CHANGELOGs for builder/cli/simulator and remove them from package.json file lists (no released baseline yet) - RELEASING.md: drop the changesets-migration TODO section and the CHANGELOG preamble - README.md: remove submodule-consumption section; drop busy "## Packages" walkthrough (each package has its own README); rename "Getting started" -> "Development"; linkify package list - packages/cli/README.md: split em-dash sentence per review; replace hardcoded +0.29.0 with + placeholder - runCompiler.ts: generalize + in help text + JSDoc; update matching test assertions - runBuilder.ts: drop confusing JSDoc @example block; point to cli README for usage examples - Set author = "OpenZeppelin Community " across builder/cli/simulator package.json --- README.md | 80 +++------------------------ RELEASING.md | 32 ----------- packages/builder/CHANGELOG.md | 38 ------------- packages/builder/package.json | 3 +- packages/cli/CHANGELOG.md | 35 ------------ packages/cli/README.md | 15 ++--- packages/cli/package.json | 3 +- packages/cli/src/runBuilder.ts | 17 +----- packages/cli/src/runCompiler.ts | 6 +- packages/cli/test/runCompiler.test.ts | 4 +- packages/simulator/CHANGELOG.md | 23 -------- packages/simulator/package.json | 2 +- 12 files changed, 25 insertions(+), 233 deletions(-) delete mode 100644 packages/builder/CHANGELOG.md delete mode 100644 packages/cli/CHANGELOG.md delete mode 100644 packages/simulator/CHANGELOG.md diff --git a/README.md b/README.md index 544d444..954c236 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,12 @@ This project extends the Midnight Network with additional developer tooling. Tools for compiling, building, and testing Compact smart contracts. This is a monorepo containing: -- `packages/builder`: Programmatic library that drives the Compact compiler + builder -- `packages/cli`: Thin bin wrapper around the builder library (`compact-compiler`, `compact-builder`) -- `packages/simulator`: TypeScript simulator to run and test Compact contracts locally +- [`packages/builder`](./packages/builder) — programmatic library that drives the Compact compiler + builder +- [`packages/cli`](./packages/cli) — thin bin wrapper around the builder library (`compact-compiler`, `compact-builder`) +- [`packages/simulator`](./packages/simulator) — TypeScript simulator to run and test Compact contracts locally + +See each package's README for usage, options, and examples. + ## Installation Pick the package that matches what you need: @@ -39,24 +42,6 @@ import { CompactCompiler, CompactBuilder } from '@openzeppelin/compact-builder'; import { createSimulator } from '@openzeppelin/compact-simulator'; ``` -### Developing against unreleased changes - -If you need a not-yet-published change, you can consume this repo locally via a -git submodule + `file:` dependency: - -```bash -git submodule add https://github.com/OpenZeppelin/compact-tools tools/compact-tools -yarn --cwd tools/compact-tools install -yarn --cwd tools/compact-tools build - -# In your package.json -"devDependencies": { - "@openzeppelin/compact-builder": "file:./tools/compact-tools/packages/builder", - "@openzeppelin/compact-cli": "file:./tools/compact-tools/packages/cli", - "@openzeppelin/compact-simulator": "file:./tools/compact-tools/packages/simulator" -} -``` - ## Requirements - Node.js >= 20 (root and `packages/cli`), >= 22 for `packages/simulator` @@ -73,9 +58,9 @@ Compactc version: 0.29.0 0.29.0 ``` -## Getting started +## Development -Install dependencies at the repo root: +Clone the repo and install dependencies at the root: ```bash nvm install @@ -107,55 +92,6 @@ Clean generated artifacts: yarn clean ``` -## Packages - -### `@openzeppelin/compact-cli` ([packages/cli](./packages/cli)) - -CLI utilities for compiling and building Compact smart contracts. - -**Quickstart:** - -```bash -# Compile all .compact files -compact-compiler - -# Skip ZK proofs for faster development builds -compact-compiler --skip-zk - -# Compile a specific subdirectory -compact-compiler --dir security - -# Skip mock files at discovery time -compact-compiler --exclude 'Mock*' --exclude '*.mock.compact' - -# Full build (compile + TypeScript + copy .compact files into dist/) -compact-builder - -# Library-publish build: clean dist, preserve src tree, copy package metadata -compact-builder \ - --clean-dist \ - --hierarchical \ - --copy package.json --copy ../README.md -``` - -See [packages/cli/README.md](./packages/cli/README.md) for full CLI documentation, all flags, and examples. -For the programmatic API (`CompactCompiler`, `CompactBuilder`, services and types), see [packages/builder/README.md](./packages/builder/README.md). - -### `@openzeppelin/compact-simulator` ([packages/simulator](./packages/simulator)) - -TypeScript simulator for testing Compact contracts locally. - -**Quickstart:** - -```ts -import { createSimulator } from '@openzeppelin/compact-simulator'; - -const simulator = createSimulator({}); -// Deploy and execute contract circuits, inspect state, etc. -``` - -See package tests in `packages/simulator/src/integration` and `src/unit` for full examples. - ## Contributing Before opening a PR, please read `CODE_OF_CONDUCT.md`. Use the root scripts to build, test, and format. For targeted work inside a package, run the scripts in that package directory. diff --git a/RELEASING.md b/RELEASING.md index 2f323e9..c9635f6 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,14 +1,5 @@ # Releasing -## Before triggering the workflow - -For each PR that lands in `main` since the last release, add a bullet to the -`## Unreleased` section of the relevant `packages//CHANGELOG.md`. At -release time, the maintainer renames the `## Unreleased` heading to -`## - YYYY-MM-DD` and opens a fresh `## Unreleased` block. This -step is currently manual — see the TODO below for the planned migration to -[changesets](https://github.com/changesets/changesets). - ## Running the workflow 1. Go to "Release Package" in Actions. @@ -49,26 +40,3 @@ each dependent finds its deps already on npm: After the first release, the three packages version independently — bump any one of them in isolation without re-publishing the others. - -## TODO: migrate to changesets - -Today the version bump (via `yarn version` in the workflow input) and the -`CHANGELOG.md` entries are maintained separately and manually. As PR volume -grows this gets brittle — easy to forget a CHANGELOG entry, easy to mis-bump. - -[Changesets](https://github.com/changesets/changesets) is the standard -monorepo tool for this: - -- Contributors run `yarn changeset` in their PR and pick which packages bump - and at what semver level, with a short description that becomes the - CHANGELOG entry. -- A release workflow consumes all accumulated changesets to: - - Compute correct per-package version bumps. - - Write per-package `CHANGELOG.md` entries. - - Open a "Version Packages" PR for review. - - On merge, tag and publish all affected packages in one run. - -The existing `release.yml` would be simplified: no manual `version_bump` -input, no per-run package selection — the accumulated changesets drive both. -Existing `CHANGELOG.md` files become the historical record; changesets manage -new entries from that point on. \ No newline at end of file diff --git a/packages/builder/CHANGELOG.md b/packages/builder/CHANGELOG.md deleted file mode 100644 index 4cf6c83..0000000 --- a/packages/builder/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -# Changelog - -All notable changes to `@openzeppelin/compact-builder` will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Unreleased - -### Added - -- Initial public release of `@openzeppelin/compact-builder`. -- Programmatic API for orchestrating the Compact toolchain: - - `CompactCompiler` — compiles `.compact` files to artifacts. - - `CompactBuilder` — runs `CompactCompiler` then assembles a publishable - `dist/`. -- Composable service classes: `EnvironmentValidator`, `FileDiscovery`, - `CompilerService`, and the `UIService` helper object. -- Option types: `CompilerOptions`, `BuilderOptions`, `BuilderOnlyOptions`, - `CompilerServiceOptions`, `BuildStep`, `ExecFunction`. -- Configurable behaviours: - - `hierarchical` — preserve source tree in compiler artifacts and the - builder's `.compact` copy step. - - `exclude` — glob-style patterns to skip in file discovery and dist copy - (`Mock*` / `*.mock.compact` excluded by default). - - `cleanDist` — `rm -rf dist` before building. - - `copyToDist` — extra files (e.g. `package.json`, `README.md`) to copy into - `dist/` for publishable layouts. - - `srcDir` / `outDir` — customize source and artifact directories. -- Structured error types: `CompactCliNotFoundError`, `CompilationError`, - `DirectoryNotFoundError`, plus the `isPromisifiedChildProcessError` type guard. -- Dependency-injectable `ExecFunction` for testing. - -### Notes - -- This package contains the library code that previously lived under - `@openzeppelin/compact-cli`. The CLI package now ships only the bin - entries (`compact-compiler`, `compact-builder`) and depends on this library. diff --git a/packages/builder/package.json b/packages/builder/package.json index 77a2db6..5c4667b 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -9,7 +9,7 @@ "builder", "library" ], - "author": "", + "author": "OpenZeppelin Community ", "license": "MIT", "type": "module", "main": "./dist/index.js", @@ -23,7 +23,6 @@ "files": [ "dist", "README.md", - "CHANGELOG.md", "LICENSE" ], "engines": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md deleted file mode 100644 index 69b6c47..0000000 --- a/packages/cli/CHANGELOG.md +++ /dev/null @@ -1,35 +0,0 @@ -# Changelog - -All notable changes to `@openzeppelin/compact-cli` will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Unreleased - -### Added - -- Initial public release of `@openzeppelin/compact-cli` as a thin bin - wrapper around [`@openzeppelin/compact-builder`](../builder). -- `compact-compiler` bin — orchestrates `compact compile` over a project's - `.compact` files with progress reporting and structured error handling. -- `compact-builder` bin — runs the compiler then assembles a publishable - `dist/` for npm. -- CLI options: - - `--dir ` — scope to a subdirectory - - `--src ` / `--out ` — customize source / artifact dirs - - `--hierarchical` — preserve source tree in artifacts and `.compact` copy - - `--exclude ` — skip `.compact` files matching a glob (repeatable); - applies to both the compiler's file discovery and the builder's copy step - - `--clean-dist` — `rm -rf dist` before building - - `--copy ` — copy extra files into `dist/` (repeatable; e.g. - `package.json`, `../README.md`) - - `+` — pin the Compact toolchain version per invocation - - `SKIP_ZK=true` env var — equivalent to `--skip-zk` - -### Notes - -- The programmatic API (CompactCompiler, CompactBuilder, services, types) - previously shipped from this package has moved to - `@openzeppelin/compact-builder`. This package now ships only the bin - entries. diff --git a/packages/cli/README.md b/packages/cli/README.md index b6ffa49..b3fad84 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -2,7 +2,7 @@ CLI wrapper around [`@openzeppelin/compact-builder`](../builder). Provides the `compact-compiler` and `compact-builder` binaries for use in -`package.json` scripts. Contains no programmatic API of its own — if you want +`package.json` scripts. Contains no programmatic API of its own. If you want to call the compiler/builder from TypeScript, use the library package directly. ## Install @@ -18,15 +18,16 @@ yarn compact-compiler --help yarn compact-builder --help ``` -Typical `package.json` scripts: +Typical `package.json` scripts (replace `` with the Compact +toolchain release you want to pin, e.g. `+0.29.0`): ```json { "scripts": { - "compact": "compact-compiler +0.29.0 --exclude '*/archive/*'", - "compact:access": "compact-compiler +0.29.0 --dir access", - "build": "compact-builder +0.29.0 --clean-dist --hierarchical --copy package.json --copy ../README.md", - "test": "compact-compiler +0.29.0 --skip-zk && vitest run" + "compact": "compact-compiler + --exclude '*/archive/*'", + "compact:access": "compact-compiler + --dir access", + "build": "compact-builder + --clean-dist --hierarchical --copy package.json --copy ../README.md", + "test": "compact-compiler + --skip-zk && vitest run" } } ``` @@ -45,7 +46,7 @@ options: | `--hierarchical` | both | Preserve source directory structure in artifacts AND in the builder's `.compact` copy. | | `--exclude ` | both | Skip `.compact` files matching the glob (repeatable). Default for the builder: `Mock*`, `*.mock.compact`. | | `--skip-zk` | compiler | Skip zero-knowledge proof generation (also via `SKIP_ZK=true` env var). | -| `+` | both | Pin the Compact toolchain version (e.g. `+0.29.0`). | +| `+` | both | Pin the Compact toolchain version (e.g `+0.29.0`). | | `--clean-dist` | builder | `rm -rf dist` before building. | | `--copy ` | builder | Copy an extra file into `dist/` (repeatable; e.g. `package.json`, `../README.md`). | diff --git a/packages/cli/package.json b/packages/cli/package.json index 4045916..11c8d91 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -9,7 +9,7 @@ "builder", "testing" ], - "author": "", + "author": "OpenZeppelin Community ", "license": "MIT", "type": "module", "exports": { @@ -19,7 +19,6 @@ "files": [ "dist", "README.md", - "CHANGELOG.md", "LICENSE" ], "engines": { diff --git a/packages/cli/src/runBuilder.ts b/packages/cli/src/runBuilder.ts index 14ab201..3719973 100644 --- a/packages/cli/src/runBuilder.ts +++ b/packages/cli/src/runBuilder.ts @@ -26,22 +26,7 @@ import ora from 'ora'; * - `--clean-dist` - rm -rf dist before building (default off) * - `--copy ` - copy an extra file into dist/ for distribution (repeatable; e.g. package.json) * - * @example - * ```bash - * # Minimal build - * npx compact-builder - * - * # Custom source/output dirs - * npx compact-builder --src contracts --out build - * - * # Library-publish build (the split-quoted '*'/'archive'/'*' is just JSDoc - * # — in your own scripts write '/archive/' literally): - * npx compact-builder \ - * --clean-dist \ - * --hierarchical \ - * --exclude 'Mock*' --exclude '*.mock.compact' --exclude '*'/'archive'/'*' \ - * --copy package.json --copy ../README.md - * ``` + * See `packages/cli/README.md` for usage examples. */ async function runBuilder(): Promise { const spinner = ora(chalk.blue('[BUILD] Compact Builder started')).info(); diff --git a/packages/cli/src/runCompiler.ts b/packages/cli/src/runCompiler.ts index 4310e76..aed54eb 100644 --- a/packages/cli/src/runCompiler.ts +++ b/packages/cli/src/runCompiler.ts @@ -40,7 +40,7 @@ import ora, { type Ora } from 'ora'; * * @example Version specification * ```bash - * npx compact-compiler --dir security --skip-zk +0.26.0 + * npx compact-compiler --dir security --skip-zk + * ``` */ async function runCompiler(): Promise { @@ -200,7 +200,7 @@ function showUsageHelp(): void { ); console.log( chalk.yellow( - ' + Use specific toolchain version (e.g., +0.29.0)', + ' + Pin the Compact toolchain version', ), ); console.log(chalk.yellow('\nArtifact Output Structure:')); @@ -241,7 +241,7 @@ function showUsageHelp(): void { ); console.log( chalk.yellow( - ' compact-compiler --skip-zk +0.26.0 # Use specific version', + ' compact-compiler --skip-zk + # Pin toolchain version', ), ); console.log(chalk.yellow('\nTurbo integration:')); diff --git a/packages/cli/test/runCompiler.test.ts b/packages/cli/test/runCompiler.test.ts index f62c4b7..d07e5d1 100644 --- a/packages/cli/test/runCompiler.test.ts +++ b/packages/cli/test/runCompiler.test.ts @@ -339,7 +339,7 @@ describe('runCompiler CLI', () => { ' --skip-zk Skip zero-knowledge proof generation', ); expect(mockConsoleLog).toHaveBeenCalledWith( - ' + Use specific toolchain version (e.g., +0.29.0)', + ' + Pin the Compact toolchain version', ); expect(mockConsoleLog).toHaveBeenCalledWith( '\nArtifact Output Structure:', @@ -370,7 +370,7 @@ describe('runCompiler CLI', () => { ' SKIP_ZK=true compact-compiler --dir token # Use environment variable', ); expect(mockConsoleLog).toHaveBeenCalledWith( - ' compact-compiler --skip-zk +0.26.0 # Use specific version', + ' compact-compiler --skip-zk + # Pin toolchain version', ); expect(mockConsoleLog).toHaveBeenCalledWith('\nTurbo integration:'); expect(mockConsoleLog).toHaveBeenCalledWith( diff --git a/packages/simulator/CHANGELOG.md b/packages/simulator/CHANGELOG.md deleted file mode 100644 index 08ed6b8..0000000 --- a/packages/simulator/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -All notable changes to `@openzeppelin/compact-simulator` will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Unreleased - -### Added - -- Initial public release of `@openzeppelin/compact-simulator`. -- `createSimulator` factory for building type-safe per-contract simulator - classes, with configurable private state, ledger extractor, witnesses and - contract constructor arguments. -- Core building blocks: `AbstractSimulator`, `ContractSimulator`, - `CircuitContextManager` for managing private state, public (ledger) state, - zswap local state and transaction context. -- Public types: `IContractSimulator`, `IMinimalContract`, - `ExtractPureCircuits`, `ExtractImpureCircuits`, `ContextlessCircuits`, - `BaseSimulatorOptions`, `SimulatorConfig`. -- Toolchain pin: `@midnight-ntwrk/compact-runtime` 0.14.0 (Compact compiler - 0.29.0 generation) and `@midnight-ntwrk/ledger-v7`. diff --git a/packages/simulator/package.json b/packages/simulator/package.json index ac7b495..e73b723 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -8,7 +8,7 @@ "simulator", "testing" ], - "author": "", + "author": "OpenZeppelin Community ", "license": "MIT", "type": "module", "main": "./dist/index.js", From e7b457d6094052f0959d0d9449f9300898e18de2 Mon Sep 17 00:00:00 2001 From: 0xisk <0xisk@proton.me> Date: Fri, 15 May 2026 12:50:16 +0200 Subject: [PATCH 13/13] chore: fix lint --- packages/cli/src/runCompiler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli/src/runCompiler.ts b/packages/cli/src/runCompiler.ts index aed54eb..83e4622 100644 --- a/packages/cli/src/runCompiler.ts +++ b/packages/cli/src/runCompiler.ts @@ -199,9 +199,7 @@ function showUsageHelp(): void { chalk.yellow(' --skip-zk Skip zero-knowledge proof generation'), ); console.log( - chalk.yellow( - ' + Pin the Compact toolchain version', - ), + chalk.yellow(' + Pin the Compact toolchain version'), ); console.log(chalk.yellow('\nArtifact Output Structure:')); console.log(chalk.yellow(' Default (flattened): //'));