From 7da2e63dcd755dea5cda45b5917a07a46b5c38fd Mon Sep 17 00:00:00 2001 From: acheron Date: Tue, 24 Dec 2024 08:39:11 +0100 Subject: [PATCH 1/3] ts: Fix loading programs with numbers in their names using `workspace` --- ts/packages/anchor/src/workspace.ts | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ts/packages/anchor/src/workspace.ts b/ts/packages/anchor/src/workspace.ts index 7ae3e8ed2f..d2e3067888 100644 --- a/ts/packages/anchor/src/workspace.ts +++ b/ts/packages/anchor/src/workspace.ts @@ -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"; @@ -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]; @@ -50,7 +34,8 @@ 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 programEntry = + anchorToml.programs?.[clusterId]?.[snakeCase(programName)]; let idlPath: string; let programId; @@ -58,7 +43,22 @@ const workspace = new Proxy( 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)) { From 06304a0974a14569f8eeacbef14aaf0d86192f08 Mon Sep 17 00:00:00 2001 From: acheron Date: Wed, 25 Dec 2024 08:39:37 +0100 Subject: [PATCH 2/3] ts: Fix getting the program from `Anchor.toml` entries --- ts/packages/anchor/src/workspace.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ts/packages/anchor/src/workspace.ts b/ts/packages/anchor/src/workspace.ts index d2e3067888..3587bade42 100644 --- a/ts/packages/anchor/src/workspace.ts +++ b/ts/packages/anchor/src/workspace.ts @@ -34,8 +34,13 @@ 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]?.[snakeCase(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; From 29f2cd53562ce0c6d4d742e48466873af6e73789 Mon Sep 17 00:00:00 2001 From: acheron Date: Wed, 25 Dec 2024 08:39:47 +0100 Subject: [PATCH 3/3] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 081be7f9a4..95859c0c25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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