Skip to content

Commit

Permalink
ts: Fix loading programs with numbers in their names using workspace (
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto authored Dec 25, 2024
1 parent 6ff6655 commit 72c7e09
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ The minor version will be incremented upon a breaking change and the patch versi
- lang: Deduplicate `zero` accounts against `init` accounts ([#3422](https://github.com/coral-xyz/anchor/pull/3422)).
- cli: Fix custom `provider.cluster` ([#3428](https://github.com/coral-xyz/anchor/pull/3428)).
- cli: Ignore non semver solana/agave releases to avoid panic ([#3432](https://github.com/coral-xyz/anchor/pull/3432)).
- ts: Fix loading programs with numbers in their names using `workspace` ([#3450](https://github.com/coral-xyz/anchor/pull/3450)).

### Breaking

Expand Down
47 changes: 26 additions & 21 deletions ts/packages/anchor/src/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as toml from "toml";
import camelcase from "camelcase";
import { snakeCase } from "snake-case";
import { Program } from "./program/index.js";
import { isBrowser } from "./utils/common.js";
Expand All @@ -19,27 +20,10 @@ const workspace = new Proxy(
throw new Error("Workspaces aren't available in the browser");
}

// Converting `programName` to snake_case enables the ability to use any
// Converting `programName` to camelCase enables the ability to use any
// of the following to access the workspace program:
// `workspace.myProgram`, `workspace.MyProgram`, `workspace["my-program"]`...
programName = snakeCase(programName);

// Check whether the program name contains any digits
if (/\d/.test(programName)) {
// Numbers cannot be properly converted from camelCase to snake_case,
// e.g. if the `programName` is `myProgram2`, the actual program name could
// be `my_program2` or `my_program_2`. This implementation assumes the
// latter as the default and always converts to `_numbers`.
//
// A solution to the conversion of program names with numbers in them
// would be to always convert the `programName` to camelCase instead of
// snake_case. The problem with this approach is that it would require
// converting everything else e.g. program names in Anchor.toml and IDL
// file names which are both snake_case.
programName = programName
.replace(/\d+/g, (match) => "_" + match)
.replace("__", "_");
}
programName = camelcase(programName);

// Return early if the program is in cache
if (workspaceCache[programName]) return workspaceCache[programName];
Expand All @@ -50,15 +34,36 @@ const workspace = new Proxy(
// Override the workspace programs if the user put them in the config.
const anchorToml = toml.parse(fs.readFileSync("Anchor.toml"));
const clusterId = anchorToml.provider.cluster;
const programEntry = anchorToml.programs?.[clusterId]?.[programName];
const programs = anchorToml.programs?.[clusterId];
let programEntry;
if (programs) {
programEntry = Object.entries(programs).find(
([key]) => camelcase(key) === programName
)?.[1];
}

let idlPath: string;
let programId;
if (typeof programEntry === "object" && programEntry.idl) {
idlPath = programEntry.idl;
programId = programEntry.address;
} else {
idlPath = path.join("target", "idl", `${programName}.json`);
// Assuming the IDL file's name to be the snake_case name of the
// `programName` with `.json` extension results in problems when
// numbers are involved due to the nature of case conversion from
// camelCase to snake_case being lossy.
//
// To avoid the above problem with numbers, read the `idl` directory and
// compare the camelCased version of both file names and `programName`.
const idlDirPath = path.join("target", "idl");
const fileName = fs
.readdirSync(idlDirPath)
.find((name) => camelcase(path.parse(name).name) === programName);
if (!fileName) {
throw new Error(`Failed to find IDL of program \`${programName}\``);
}

idlPath = path.join(idlDirPath, fileName);
}

if (!fs.existsSync(idlPath)) {
Expand Down

0 comments on commit 72c7e09

Please sign in to comment.