Procházet zdrojové kódy

ts: Fix loading programs with numbers in their names using `workspace` (#3450)

acheron před 9 měsíci
rodič
revize
72c7e09538
2 změnil soubory, kde provedl 27 přidání a 21 odebrání
  1. 1 0
      CHANGELOG.md
  2. 26 21
      ts/packages/anchor/src/workspace.ts

+ 1 - 0
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
 

+ 26 - 21
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,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]?.[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;
@@ -58,7 +48,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)) {