@harmoniclabs/pebble-cli 0.1.0-dev2 → 0.1.1-dev0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,2 @@
1
1
  import { CliCompileOptions } from "./completeCompileOptions.js";
2
2
  export declare function compilePebbleProject(opts: CliCompileOptions): Promise<void>;
3
- export default compilePebbleProject;
@@ -1,61 +1,7 @@
1
1
  import * as path from "node:path";
2
2
  import * as fsp from "node:fs/promises";
3
- import { existsSync } from "node:fs";
4
3
  import { Compiler } from "@harmoniclabs/pebble";
5
- function resolvePath(filename, baseDir) {
6
- if (!filename)
7
- return baseDir;
8
- if (path.isAbsolute(filename))
9
- return filename;
10
- return path.resolve(baseDir, filename.replace(/^\/+/, ""));
11
- }
12
- function createFsIo(root) {
13
- const stdout = process.stdout;
14
- const stderr = process.stderr;
15
- return {
16
- stdout,
17
- stderr,
18
- async readFile(filename, baseDir) {
19
- const full = resolvePath(filename, typeof baseDir === "string" ? baseDir : root);
20
- try {
21
- const buf = await fsp.readFile(full);
22
- return buf.toString("utf8");
23
- }
24
- catch {
25
- return undefined;
26
- }
27
- },
28
- async writeFile(filename, contents, baseDir) {
29
- const full = resolvePath(filename, baseDir || root);
30
- await fsp.mkdir(path.dirname(full), { recursive: true });
31
- if (typeof contents === "string") {
32
- await fsp.writeFile(full, contents, "utf8");
33
- }
34
- else {
35
- await fsp.writeFile(full, Buffer.from(contents));
36
- }
37
- },
38
- exsistSync(filename) {
39
- const full = resolvePath(filename, root);
40
- const exsists = existsSync(full);
41
- console.log("checking exists:", full, filename, exsists);
42
- return existsSync(full);
43
- },
44
- async listFiles(dirname, baseDir) {
45
- const full = resolvePath(dirname, baseDir || root);
46
- try {
47
- const entries = await fsp.readdir(full, { withFileTypes: true });
48
- return entries.map(e => e.name);
49
- }
50
- catch {
51
- return undefined;
52
- }
53
- },
54
- reportDiagnostic(d) {
55
- stderr.write(String(d) + "\n");
56
- }
57
- };
58
- }
4
+ import { createFsIo } from "../utils/crateFsIo.js";
59
5
  export async function compilePebbleProject(opts) {
60
6
  const { root, entry, outDir, output, config } = opts;
61
7
  const io = createFsIo(root);
@@ -73,4 +19,3 @@ export async function compilePebbleProject(opts) {
73
19
  await fsp.copyFile(generated, target);
74
20
  }
75
21
  }
76
- export default compilePebbleProject;
@@ -12,8 +12,4 @@ export interface CliCompileOptions {
12
12
  config: CompilerOptions;
13
13
  configPath?: string;
14
14
  }
15
- export declare function normalizeRoot(root?: string): string;
16
- export declare function isRecord(x: any): x is Record<string, unknown>;
17
- export declare function fromJsonMaybeBoolean<T>(value: any, fallback: T): T;
18
15
  export declare function completeCompileOptions(flags: CliCompileFlags): CliCompileOptions;
19
- export default completeCompileOptions;
@@ -1,28 +1,23 @@
1
- import { defaultOptions } from "@harmoniclabs/pebble";
1
+ import { productionOptions } from "@harmoniclabs/pebble";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import * as path from "node:path";
4
- export function normalizeRoot(root) {
5
- const r = root && root.trim().length > 0 ? root : process.cwd();
6
- return path.resolve(r);
7
- }
8
- export function isRecord(x) {
9
- return typeof x === "object" && x !== null && !Array.isArray(x);
10
- }
11
- export function fromJsonMaybeBoolean(value, fallback) {
12
- return (typeof value === typeof fallback ? value : fallback);
13
- }
4
+ import { normalizeRoot, isRecord } from "../utils/miscellaneous.js";
14
5
  export function completeCompileOptions(flags) {
15
6
  const root = normalizeRoot();
16
7
  const configPath = path.resolve(root, flags.config ?? "./pebble.config.json");
17
- let config = defaultOptions;
8
+ let config = productionOptions;
18
9
  if (existsSync(configPath)) {
19
10
  try {
20
11
  const txt = readFileSync(configPath, "utf8");
21
12
  const parsed = JSON.parse(txt);
22
13
  if (isRecord(parsed))
23
14
  config = {
24
- ...defaultOptions,
15
+ ...productionOptions,
25
16
  ...parsed,
17
+ uplcOptimizations: {
18
+ ...productionOptions.uplcOptimizations,
19
+ ...parsed.uplcOptimizations
20
+ }
26
21
  };
27
22
  }
28
23
  catch {
@@ -47,4 +42,3 @@ export function completeCompileOptions(flags) {
47
42
  configPath: existsSync(configPath) ? configPath : undefined,
48
43
  };
49
44
  }
50
- export default completeCompileOptions;
@@ -0,0 +1,17 @@
1
+ import { CompilerOptions } from "@harmoniclabs/pebble";
2
+ export interface CliExportFlags {
3
+ config?: string;
4
+ entry?: string;
5
+ output?: string;
6
+ functionName?: string;
7
+ }
8
+ export interface CliExportOptions {
9
+ root: string;
10
+ entry: string;
11
+ functionName: string;
12
+ outDir: string;
13
+ output?: string;
14
+ config: CompilerOptions;
15
+ configPath?: string;
16
+ }
17
+ export declare function completeExportOptions(flags: CliExportFlags): CliExportOptions;
@@ -0,0 +1,48 @@
1
+ import { productionOptions } from "@harmoniclabs/pebble";
2
+ import * as path from "node:path";
3
+ import { normalizeRoot, isRecord } from "../utils/miscellaneous.js";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ export function completeExportOptions(flags) {
6
+ const root = normalizeRoot();
7
+ if (typeof flags.functionName !== "string")
8
+ throw new Error("exported function name must be provided via '--function-name <name>' flag");
9
+ const functionName = flags.functionName.trim();
10
+ const configPath = path.resolve(root, flags.config ?? "./pebble.config.json");
11
+ let config = productionOptions;
12
+ if (existsSync(configPath)) {
13
+ try {
14
+ const txt = readFileSync(configPath, "utf8");
15
+ const parsed = JSON.parse(txt);
16
+ if (isRecord(parsed))
17
+ config = {
18
+ ...productionOptions,
19
+ ...parsed,
20
+ uplcOptimizations: {
21
+ ...productionOptions.uplcOptimizations,
22
+ ...parsed.uplcOptimizations
23
+ }
24
+ };
25
+ }
26
+ catch {
27
+ // ignore malformed config; proceed with flags/defaults
28
+ }
29
+ }
30
+ const cfgEntry = typeof config?.entry === "string" ? String(config.entry) : undefined;
31
+ const entry = (flags.entry ?? (cfgEntry ?? "./src/index.pebble")).trim();
32
+ const desiredOutput = flags.output;
33
+ const cfgOutDir = typeof config?.outDir === "string" ? String(config.outDir) : undefined;
34
+ let outDir = cfgOutDir ?? (desiredOutput ? path.dirname(desiredOutput) : "./out");
35
+ if (desiredOutput === "./out.flat") {
36
+ // keep sane default when user didn't set anything explicitly
37
+ outDir = cfgOutDir ?? "./out";
38
+ }
39
+ return {
40
+ root,
41
+ entry,
42
+ functionName,
43
+ outDir,
44
+ output: desiredOutput,
45
+ config,
46
+ configPath: existsSync(configPath) ? configPath : undefined,
47
+ };
48
+ }
@@ -0,0 +1,2 @@
1
+ import { CliExportOptions } from "./completeExportOptions.js";
2
+ export declare function exportPebbleFunction(opts: CliExportOptions): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import * as path from "node:path";
2
+ import * as fsp from "node:fs/promises";
3
+ import { Compiler } from "@harmoniclabs/pebble";
4
+ import { createFsIo } from "../utils/crateFsIo.js";
5
+ export async function exportPebbleFunction(opts) {
6
+ const { root, entry, functionName, outDir, output, config } = opts;
7
+ const io = createFsIo(root);
8
+ const compiler = new Compiler(io, config);
9
+ // Mirror test behavior: pass overrides via compile()
10
+ await compiler.export({
11
+ functionName,
12
+ root,
13
+ entry,
14
+ outDir
15
+ });
16
+ if (output && output !== "./out.flat") {
17
+ const generated = path.resolve(root, outDir, "out.flat");
18
+ const target = path.isAbsolute(output) ? output : path.resolve(root, output);
19
+ await fsp.mkdir(path.dirname(target), { recursive: true });
20
+ await fsp.copyFile(generated, target);
21
+ }
22
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node --max-old-space-size=8192
1
+ #!/bin/sh
2
2
  export {};
package/dist/index.js CHANGED
@@ -1,9 +1,13 @@
1
- #!/usr/bin/env node --max-old-space-size=8192
1
+ #!/bin/sh
2
+ ':'; //# ; command -v bun >/dev/null 2>&1 && exec bun "$0" "$@" || exec node "$0" "$@"
2
3
  import { Command } from "commander";
3
4
  import { PEBBLE_VERSION, PEBBLE_LIB_VERSION, PEBBLE_COMMIT_HASH } from "./version.generated.js";
4
5
  import { initPebbleProject } from "./init/initPebbleProject.js";
5
6
  import { compilePebbleProject } from "./compile/compilePebbleProject.js";
6
7
  import { completeCompileOptions } from "./compile/completeCompileOptions.js";
8
+ import { completeExportOptions } from "./export/completeExportOptions.js";
9
+ import { exportPebbleFunction } from "./export/exportPebbleFunction.js";
10
+ import { prettyPrintUplcFromFile } from "./uplc/pretty/prettyPrintUplcFromFile.js";
7
11
  const program = new Command();
8
12
  const versionOutput = (`pebble-cli version: ${PEBBLE_VERSION}
9
13
  pebble language version: ${PEBBLE_LIB_VERSION}
@@ -38,6 +42,31 @@ program.command("compile")
38
42
  .action(async (opts) => {
39
43
  await compilePebbleProject(completeCompileOptions(opts));
40
44
  });
45
+ program.command("export")
46
+ .description("Compiles and exports a single function from a pebble project")
47
+ .option("--function-name <string>", "The name of the function to export")
48
+ .option("-c, --config <string>", "The config file path", "./pebble.config.json")
49
+ .option("--entry <string>", "The entry file path .pebble file (will overwrite if present in the configuration)")
50
+ .option("-o, --output <string>", "The output file path (will overwrite if present in the configuration)")
51
+ .action(async (opts) => {
52
+ await exportPebbleFunction(completeExportOptions(opts));
53
+ });
54
+ const uplcSubcommand = program.command("uplc")
55
+ .description("Utilities for UPLC programs stored in flat-encoded files");
56
+ uplcSubcommand.command("pretty")
57
+ .description("Pretty prints a UPLC program from a flat UPLC file")
58
+ .option("-i, --input <string>", "The input flat-encoded UPLC file path", "./out/out.flat")
59
+ .option("-o, --output <string>", "The output file path (extension: .uplc) (if missing, prints to console)")
60
+ .action(prettyPrintUplcFromFile);
61
+ /*
62
+ TODO:
63
+
64
+ uplcSubcommand.command("apply")
65
+ .description("Applies arguments to a UPLC program from a flat-encoded UPLC file")
66
+
67
+ uplcSubcommand.command("eval")
68
+ .description("Evaluates a UPLC program from a flat-encoded UPLC file")
69
+ */
41
70
  program.command("init")
42
71
  .description("Creates a new directory with a fresh pebble project")
43
72
  .action(initPebbleProject);
@@ -162,9 +162,8 @@ ${includeOffchain ? "- `offchain/`: optional offchain scaffolding\n" : ""}
162
162
  await writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n");
163
163
  // src/index.pebble template
164
164
  const indexPebblePath = path.join(srcDir, "index.pebble");
165
- const indexPebble = (`
166
- // if no methods are defined
167
- // a simple always failing contract is generated
165
+ const indexPebble = (`// if no methods are defined
166
+ // the contract is interpreted as always failing
168
167
  contract MyContract {
169
168
 
170
169
  param owner: PubKeyHash;
@@ -172,7 +171,7 @@ contract MyContract {
172
171
  spend ownerAllowsIt() {
173
172
  const { tx } = context;
174
173
 
175
- assert tx.signatories.includes( this.owner );
174
+ assert tx.requiredSigners.includes( this.owner );
176
175
  }
177
176
 
178
177
  spend sendToOwner( amount: int ) {
@@ -182,7 +181,7 @@ contract MyContract {
182
181
 
183
182
  const output = tx.outputs[0];
184
183
 
185
- assert output.address.credential.hash() == this.owner;
184
+ assert output.address.payment.hash() == this.owner;
186
185
  assert output.value.lovelaces() >= amount;
187
186
  }
188
187
 
@@ -0,0 +1,5 @@
1
+ export interface CliPrettyUplcFlags {
2
+ input: string;
3
+ output?: string;
4
+ }
5
+ export declare function prettyPrintUplcFromFile(opts: CliPrettyUplcFlags): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import * as path from "node:path";
2
+ import * as fsp from "node:fs/promises";
3
+ import { parseUPLC, prettyUPLC } from "@harmoniclabs/uplc";
4
+ export async function prettyPrintUplcFromFile(opts) {
5
+ const { input, output } = opts;
6
+ const inputPath = path.resolve(input.trim());
7
+ const uplcBytes = await fsp.readFile(inputPath);
8
+ const uplcProgram = parseUPLC(uplcBytes, "flat");
9
+ const result = "(program " + uplcProgram.version.toString() + "\n"
10
+ + prettyUPLC(uplcProgram.body, 2) + "\n)";
11
+ if (!output) {
12
+ console.log(result);
13
+ return;
14
+ }
15
+ const outputPath = path.resolve(output.trim());
16
+ await fsp.writeFile(outputPath, result);
17
+ }
@@ -0,0 +1,2 @@
1
+ import { CompilerIoApi } from "@harmoniclabs/pebble";
2
+ export declare function createFsIo(root: string): CompilerIoApi;
@@ -0,0 +1,57 @@
1
+ import path from "node:path";
2
+ import * as fsp from "node:fs/promises";
3
+ import { existsSync } from "node:fs";
4
+ function resolvePath(filename, baseDir) {
5
+ if (!filename)
6
+ return baseDir;
7
+ if (path.isAbsolute(filename))
8
+ return filename;
9
+ return path.resolve(baseDir, filename.replace(/^\/+/, ""));
10
+ }
11
+ export function createFsIo(root) {
12
+ const stdout = process.stdout;
13
+ const stderr = process.stderr;
14
+ return {
15
+ stdout,
16
+ stderr,
17
+ async readFile(filename, baseDir) {
18
+ const full = resolvePath(filename, typeof baseDir === "string" ? baseDir : root);
19
+ try {
20
+ const buf = await fsp.readFile(full);
21
+ return buf.toString("utf8");
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ },
27
+ async writeFile(filename, contents, baseDir) {
28
+ const full = resolvePath(filename, baseDir || root);
29
+ await fsp.mkdir(path.dirname(full), { recursive: true });
30
+ if (typeof contents === "string") {
31
+ await fsp.writeFile(full, contents, "utf8");
32
+ }
33
+ else {
34
+ await fsp.writeFile(full, Buffer.from(contents));
35
+ }
36
+ },
37
+ exsistSync(filename) {
38
+ const full = resolvePath(filename, root);
39
+ const exsists = existsSync(full);
40
+ // console.log("checking exists:", full, filename, exsists );
41
+ return existsSync(full);
42
+ },
43
+ async listFiles(dirname, baseDir) {
44
+ const full = resolvePath(dirname, baseDir || root);
45
+ try {
46
+ const entries = await fsp.readdir(full, { withFileTypes: true });
47
+ return entries.map(e => e.name);
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ },
53
+ reportDiagnostic(d) {
54
+ stderr.write(String(d) + "\n");
55
+ }
56
+ };
57
+ }
@@ -0,0 +1,3 @@
1
+ export declare function normalizeRoot(root?: string): string;
2
+ export declare function isRecord(x: any): x is Record<string, unknown>;
3
+ export declare function fromJsonMaybeBoolean<T>(value: any, fallback: T): T;
@@ -0,0 +1,11 @@
1
+ import * as path from "node:path";
2
+ export function normalizeRoot(root) {
3
+ const r = root && root.trim().length > 0 ? root : process.cwd();
4
+ return path.resolve(r);
5
+ }
6
+ export function isRecord(x) {
7
+ return typeof x === "object" && x !== null && !Array.isArray(x);
8
+ }
9
+ export function fromJsonMaybeBoolean(value, fallback) {
10
+ return (typeof value === typeof fallback ? value : fallback);
11
+ }
@@ -1,3 +1,3 @@
1
- export declare const PEBBLE_VERSION = "0.1.0-dev2";
2
- export declare const PEBBLE_LIB_VERSION = "0.1.0-dev7";
3
- export declare const PEBBLE_COMMIT_HASH = "df91965e1d3ebf2eeb0cda4c4a71ac5688197e2e";
1
+ export declare const PEBBLE_VERSION = "0.1.1-dev0";
2
+ export declare const PEBBLE_LIB_VERSION = "0.1.1";
3
+ export declare const PEBBLE_COMMIT_HASH = "61b900fbd67de7283a60e45c9a98516fa4660d53";
@@ -1,4 +1,4 @@
1
1
  // This file is auto-generated by scripts/genVersions.js. Do not edit.
2
- export const PEBBLE_VERSION = "0.1.0-dev2";
3
- export const PEBBLE_LIB_VERSION = "0.1.0-dev7";
4
- export const PEBBLE_COMMIT_HASH = "df91965e1d3ebf2eeb0cda4c4a71ac5688197e2e";
2
+ export const PEBBLE_VERSION = "0.1.1-dev0";
3
+ export const PEBBLE_LIB_VERSION = "0.1.1";
4
+ export const PEBBLE_COMMIT_HASH = "61b900fbd67de7283a60e45c9a98516fa4660d53";
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@harmoniclabs/pebble-cli",
3
- "version": "0.1.0-dev2",
3
+ "version": "0.1.1-dev0",
4
4
  "description": "A simple, yet rock solid, functional language with an imperative bias, targeting UPLC",
5
- "bin": "./dist/index.js",
5
+ "bin": {
6
+ "pebble": "./dist/index.js",
7
+ "pebble-cli": "./dist/index.js"
8
+ },
6
9
  "types": "./dist/index.d.ts",
7
10
  "engines": {
8
11
  "node": ">=22",
@@ -15,8 +18,10 @@
15
18
  "scripts": {
16
19
  "buidl": "npm run build",
17
20
  "build": "rm -rf ./dist && npm run build:light",
21
+ "test": "jest",
18
22
  "genVersions": "node ./scripts/genVersions.js",
19
23
  "build:light": "npm run --silent genVersions && tsc --project ./tsconfig.json && tsc-alias -p ./tsconfig.json",
24
+ "build:no-gen": "tsc --project ./tsconfig.json && tsc-alias -p ./tsconfig.json",
20
25
  "start:light": "node --max-old-space-size=8192 ./dist/index.js",
21
26
  "start": "npm run build && npm run start:light",
22
27
  "build-exe": "bun run genVersions && bun build src/index.ts --compile --production --target=node --outfile=out/pebble --packages=bundle"
@@ -45,13 +50,13 @@
45
50
  "dependencies": {
46
51
  "@harmoniclabs/crypto": "^0.3.0",
47
52
  "@harmoniclabs/obj-utils": "^1.0.0",
48
- "@harmoniclabs/pebble": "^0.1.0-dev7",
53
+ "@harmoniclabs/pebble": "^0.1.1",
49
54
  "@harmoniclabs/plutus-machine": "^2.1.1",
50
55
  "@harmoniclabs/uint8array-utils": "^1.0.4",
51
56
  "@harmoniclabs/uplc": "^1.4.0",
52
- "@inquirer/prompts": "^7.8.4",
53
- "chalk": "^5.6.0",
54
- "commander": "^14.0.0"
57
+ "@inquirer/prompts": "^7.9.0",
58
+ "chalk": "^5.6.2",
59
+ "commander": "^14.0.2"
55
60
  },
56
61
  "devDependencies": {
57
62
  "@types/jest": "^28.1.4",