Skip to content

Commit

Permalink
Lazy load workspace programs and improve program name accessor
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto committed Jul 24, 2023
1 parent 4604fbe commit 0c796ac
Showing 1 changed file with 43 additions and 81 deletions.
124 changes: 43 additions & 81 deletions ts/packages/anchor/src/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,108 +1,70 @@
import camelCase from "camelcase";
import * as toml from "toml";
import { PublicKey } from "@solana/web3.js";
import { snakeCase } from "snake-case";
import { Program } from "./program/index.js";
import { Idl } from "./idl.js";
import { isBrowser } from "./utils/common.js";

let _populatedWorkspace = false;

/**
* The `workspace` namespace provides a convenience API to automatically
* search for and deserialize [[Program]] objects defined by compiled IDLs
* in an Anchor workspace.
*
* This API is for Node only.
*/
const workspace = new Proxy({} as any, {
get(workspaceCache: { [key: string]: Program }, programName: string) {
if (isBrowser) {
throw new Error("Workspaces aren't available in the browser");
}
const workspace = new Proxy(
{},
{
get(workspaceCache: { [key: string]: Program }, programName: string) {
if (isBrowser) {
throw new Error("Workspaces aren't available in the browser");
}

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

if (!_populatedWorkspace) {
const path = require("path");
// Return early if the program is in cache
if (workspaceCache[programName]) return workspaceCache[programName];

let projectRoot = process.cwd();
while (!fs.existsSync(path.join(projectRoot, "Anchor.toml"))) {
const parentDir = path.dirname(projectRoot);
if (parentDir === projectRoot) {
projectRoot = undefined;
}
projectRoot = parentDir;
}

if (projectRoot === undefined) {
throw new Error("Could not find workspace root.");
}
const fs = require("fs");
const path = require("path");

const idlFolder = `${projectRoot}/target/idl`;
const idlFolder = path.join("target", "idl");
if (!fs.existsSync(idlFolder)) {
throw new Error(
`${idlFolder} doesn't exist. Did you use "anchor build"?`
`${idlFolder} doesn't exist. Did you run \`anchor build\`?`
);
}

const idlMap = new Map<string, Idl>();
fs.readdirSync(idlFolder)
.filter((file) => file.endsWith(".json"))
.forEach((file) => {
const filePath = `${idlFolder}/${file}`;
const idlStr = fs.readFileSync(filePath);
const idl = JSON.parse(idlStr);
idlMap.set(idl.name, idl);
const name = camelCase(idl.name, { pascalCase: true });
if (idl.metadata && idl.metadata.address) {
workspaceCache[name] = new Program(
idl,
new PublicKey(idl.metadata.address)
);
}
});

// Override the workspace programs if the user put them in the config.
const anchorToml = toml.parse(
fs.readFileSync(path.join(projectRoot, "Anchor.toml"), "utf-8")
);
const anchorToml = toml.parse(fs.readFileSync("Anchor.toml"));
const clusterId = anchorToml.provider.cluster;
if (anchorToml.programs && anchorToml.programs[clusterId]) {
attachWorkspaceOverride(
workspaceCache,
anchorToml.programs[clusterId],
idlMap
);
}
const programEntry = anchorToml.programs?.[clusterId]?.[programName];

_populatedWorkspace = true;
}
let idlPath;
let programId;
if (typeof programEntry === "object" && programEntry.idl) {
idlPath = programEntry.idl;
programId = programEntry.address;
} else {
idlPath = path.join(idlFolder, `${programName}.json`);
}

return workspaceCache[programName];
},
});
const idl = JSON.parse(fs.readFileSync(idlPath));
if (!programId) {
if (!idl.metadata?.address) {
throw new Error(
`IDL for program \`${programName}\` does not have \`metadata.address\` field.\n` +
"To add the missing field, run `anchor deploy` or `anchor test`."
);
}
programId = idl.metadata.address;
}
workspaceCache[programName] = new Program(idl, programId);

function attachWorkspaceOverride(
workspaceCache: { [key: string]: Program },
overrideConfig: { [key: string]: string | { address: string; idl?: string } },
idlMap: Map<string, Idl>
) {
Object.keys(overrideConfig).forEach((programName) => {
const wsProgramName = camelCase(programName, { pascalCase: true });
const entry = overrideConfig[programName];
const overrideAddress = new PublicKey(
typeof entry === "string" ? entry : entry.address
);
let idl = idlMap.get(programName);
if (typeof entry !== "string" && entry.idl) {
idl = JSON.parse(require("fs").readFileSync(entry.idl, "utf-8"));
}
if (!idl) {
throw new Error(`Error loading workspace IDL for ${programName}`);
}
workspaceCache[wsProgramName] = new Program(idl, overrideAddress);
});
}
return workspaceCache[programName];
},
}
);

export default workspace;

0 comments on commit 0c796ac

Please sign in to comment.