Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add typescript support #795

Merged
merged 40 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9cdb365
Update accountsArray types
macalinao Jun 16, 2021
c2fc4d8
Adds types for InstructionFn
macalinao Jun 16, 2021
6488b11
Adds more types
macalinao Jun 16, 2021
5980b9c
More granular types
macalinao Jun 16, 2021
65285cf
remove event parser export
macalinao Jun 16, 2021
e73f638
Account and state data types
macalinao Jun 16, 2021
aabe73c
Merge branch 'master' into igm/types
macalinao Jun 16, 2021
8b9464d
Merge remote-tracking branch 'origin/master' into igm/types
macalinao Jun 21, 2021
18d9989
Merge remote-tracking branch 'upstream/master' into igm/types
macalinao Jul 18, 2021
dfb9bd2
T
macalinao Jul 18, 2021
ea35d62
simulate.ts: remove duplicate event import
crispheaney Jul 21, 2021
c935f75
examples/escrow: refector tests to typescript
crispheaney Jul 21, 2021
1f949f1
example/escrow: bash script to generate escrow type for test
crispheaney Jul 21, 2021
26b6a04
examples/escrow: add idl typehint to Program
crispheaney Jul 21, 2021
e80c7db
.travis.yml: fix command for escrow test
crispheaney Jul 21, 2021
c377fbd
ts/account: use account names from IDL for keys in AccountNamespace
crispheaney Jul 27, 2021
0a47d0d
ts/namespace/index: fix undefined error from undefined idl.accounts
crispheaney Jul 28, 2021
3374f79
Add some fixes
ChewingGlass Sep 10, 2021
17c697f
Merge remote-tracking branch 'serum/master' into feature/types
ChewingGlass Sep 10, 2021
8be6d72
Merge remote-tracking branch 'serum/master' into feature/types
ChewingGlass Sep 25, 2021
d23f131
Update ts/package.json
ChewingGlass Sep 25, 2021
99a7405
Update ts/package.json
ChewingGlass Sep 25, 2021
9e17d95
Update ts/src/program/index.ts
ChewingGlass Sep 25, 2021
064da53
Update ts/src/program/index.ts
ChewingGlass Sep 25, 2021
20e88f7
Move idl parser
ChewingGlass Sep 25, 2021
8274802
Merge branch 'feature/types' of github.com:ChewingGlassFund/anchor in…
ChewingGlass Sep 25, 2021
1417b8e
Finish moving escrow
armaniferrante Sep 25, 2021
22c5609
Add idl
ChewingGlass Sep 25, 2021
39e9e1d
Merge branch 'feature/types' of github.com:ChewingGlassFund/anchor in…
ChewingGlass Sep 25, 2021
a044d0c
yarn lint:fix
armaniferrante Sep 25, 2021
b687944
Fix escrow and require defined provider
armaniferrante Sep 25, 2021
58cfc6a
Remove yarn.lock
armaniferrante Sep 25, 2021
d3e7de3
update
armaniferrante Sep 26, 2021
2731187
Generate ts file via anchor build
armaniferrante Oct 4, 2021
986afd9
Anchor init typescript with types
armaniferrante Oct 4, 2021
ddbab9a
Merge branch 'master' into feature/types
armaniferrante Oct 4, 2021
24950ae
ts lint
armaniferrante Oct 4, 2021
50e8a68
fix provider
armaniferrante Oct 4, 2021
5e3b45c
Update
armaniferrante Oct 5, 2021
340b45b
Update
armaniferrante Oct 5, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ incremented for features.

## [Unreleased]

### Features

* cli: `target/types` directory now created on build to store a TypeScript types file for each program's IDL ([#795](https://github.com/project-serum/anchor/pull/795)).
* ts: `Program<T>` can now be typed with an IDL type ([#795](https://github.com/project-serum/anchor/pull/795)).

## [0.17.0] - 2021-10-03

### Features
Expand Down
66 changes: 46 additions & 20 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,19 +520,18 @@ pub fn build(
}

let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
let cfg_parent = cfg.path().parent().expect("Invalid Anchor.toml");

let cargo = Manifest::discover()?;

fs::create_dir_all(cfg_parent.join("target/idl"))?;
fs::create_dir_all(cfg_parent.join("target/types"))?;

let idl_out = match idl {
Some(idl) => Some(PathBuf::from(idl)),
None => {
let cfg_parent = match cfg.path().parent() {
None => return Err(anyhow!("Invalid Anchor.toml")),
Some(parent) => parent,
};
fs::create_dir_all(cfg_parent.join("target/idl"))?;
Some(cfg_parent.join("target/idl"))
}
None => Some(cfg_parent.join("target/idl")),
};
let idl_ts_out = Some(cfg_parent.join("target/types"));

let solana_version = match solana_version.is_some() {
true => solana_version,
Expand All @@ -545,6 +544,7 @@ pub fn build(
&cfg,
cfg.path(),
idl_out,
idl_ts_out,
verifiable,
solana_version,
stdout,
Expand All @@ -556,6 +556,7 @@ pub fn build(
&cfg,
cfg.path(),
idl_out,
idl_ts_out,
verifiable,
solana_version,
stdout,
Expand All @@ -567,6 +568,7 @@ pub fn build(
&cfg,
cargo.path().to_path_buf(),
idl_out,
idl_ts_out,
verifiable,
solana_version,
stdout,
Expand All @@ -585,6 +587,7 @@ fn build_all(
cfg: &WithPath<Config>,
cfg_path: &Path,
idl_out: Option<PathBuf>,
idl_ts_out: Option<PathBuf>,
verifiable: bool,
solana_version: Option<String>,
stdout: Option<File>, // Used for the package registry server.
Expand All @@ -600,6 +603,7 @@ fn build_all(
cfg,
p.join("Cargo.toml"),
idl_out.clone(),
idl_ts_out.clone(),
verifiable,
solana_version.clone(),
stdout.as_ref().map(|f| f.try_clone()).transpose()?,
Expand All @@ -620,6 +624,7 @@ fn build_cwd(
cfg: &WithPath<Config>,
cargo_toml: PathBuf,
idl_out: Option<PathBuf>,
idl_ts_out: Option<PathBuf>,
verifiable: bool,
solana_version: Option<String>,
stdout: Option<File>,
Expand All @@ -631,7 +636,7 @@ fn build_cwd(
Some(p) => std::env::set_current_dir(&p)?,
};
match verifiable {
false => _build_cwd(idl_out, cargo_args),
false => _build_cwd(idl_out, idl_ts_out, cargo_args),
true => build_cwd_verifiable(cfg, cargo_toml, solana_version, stdout, stderr),
}
}
Expand Down Expand Up @@ -696,8 +701,14 @@ fn build_cwd_verifiable(
// Build the idl.
if let Ok(Some(idl)) = extract_idl("src/lib.rs") {
println!("Extracting the IDL");

// Write out the JSON file.
let out_file = workspace_dir.join(format!("target/idl/{}.json", idl.name));
write_idl(&idl, OutFile::File(out_file))?;

// Write out the TypeScript type.
let ts_file = format!("target/types/{}.ts", idl.name);
fs::write(&ts_file, template::idl_ts(&idl)?)?;
}

result
Expand Down Expand Up @@ -860,7 +871,11 @@ fn docker_build(
Ok(())
}

fn _build_cwd(idl_out: Option<PathBuf>, cargo_args: Vec<String>) -> Result<()> {
fn _build_cwd(
idl_out: Option<PathBuf>,
idl_ts_out: Option<PathBuf>,
cargo_args: Vec<String>,
) -> Result<()> {
let exit = std::process::Command::new("cargo")
.arg("build-bpf")
.args(cargo_args)
Expand All @@ -874,12 +889,22 @@ fn _build_cwd(idl_out: Option<PathBuf>, cargo_args: Vec<String>) -> Result<()> {

// Always assume idl is located ar src/lib.rs.
if let Some(idl) = extract_idl("src/lib.rs")? {
// JSON out path.
let out = match idl_out {
None => PathBuf::from(".").join(&idl.name).with_extension("json"),
Some(o) => PathBuf::from(&o.join(&idl.name).with_extension("json")),
};
// TS out path.
let ts_out = match idl_ts_out {
None => PathBuf::from(".").join(&idl.name).with_extension("ts"),
Some(o) => PathBuf::from(&o.join(&idl.name).with_extension("ts")),
};

// Write out the JSON file.
write_idl(&idl, OutFile::File(out))?;

// Write out the TypeScript type.
fs::write(ts_out, template::idl_ts(&idl)?)?;
}

Ok(())
Expand Down Expand Up @@ -1115,7 +1140,7 @@ fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: Str
with_workspace(cfg_override, |cfg| {
let keypair = cfg.provider.wallet.to_string();

let bytes = std::fs::read(idl_filepath)?;
let bytes = fs::read(idl_filepath)?;
let idl: Idl = serde_json::from_reader(&*bytes)?;

let idl_address = create_idl_account(cfg, &keypair, &program_id, &idl)?;
Expand All @@ -1133,7 +1158,7 @@ fn idl_write_buffer(
with_workspace(cfg_override, |cfg| {
let keypair = cfg.provider.wallet.to_string();

let bytes = std::fs::read(idl_filepath)?;
let bytes = fs::read(idl_filepath)?;
let idl: Idl = serde_json::from_reader(&*bytes)?;

let idl_buffer = create_idl_buffer(cfg, &keypair, &program_id, &idl)?;
Expand Down Expand Up @@ -1383,8 +1408,9 @@ fn write_idl(idl: &Idl, out: OutFile) -> Result<()> {
let idl_json = serde_json::to_string_pretty(idl)?;
match out {
OutFile::Stdout => println!("{}", idl_json),
OutFile::File(out) => std::fs::write(out, idl_json)?,
OutFile::File(out) => fs::write(out, idl_json)?,
};

Ok(())
}

Expand Down Expand Up @@ -1543,7 +1569,7 @@ fn genesis_flags(cfg: &WithPath<Config>) -> Result<Vec<String>> {
fn stream_logs(config: &WithPath<Config>) -> Result<Vec<std::process::Child>> {
let program_logs_dir = ".anchor/program-logs";
if Path::new(program_logs_dir).exists() {
std::fs::remove_dir_all(program_logs_dir)?;
fs::remove_dir_all(program_logs_dir)?;
}
fs::create_dir_all(program_logs_dir)?;
let mut handles = vec![];
Expand Down Expand Up @@ -1604,10 +1630,10 @@ fn start_test_validator(
let test_ledger_log_filename = ".anchor/test-ledger-log.txt";

if Path::new(test_ledger_filename).exists() {
std::fs::remove_dir_all(test_ledger_filename)?;
fs::remove_dir_all(test_ledger_filename)?;
}
if Path::new(test_ledger_log_filename).exists() {
std::fs::remove_file(test_ledger_log_filename)?;
fs::remove_file(test_ledger_log_filename)?;
}

// Start a validator for testing.
Expand Down Expand Up @@ -1906,7 +1932,7 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
let module_path = cur_dir.join("migrations/deploy.ts");
let deploy_script_host_str =
template::deploy_ts_script_host(&url, &module_path.display().to_string());
std::fs::write("deploy.ts", deploy_script_host_str)?;
fs::write("deploy.ts", deploy_script_host_str)?;
std::process::Command::new("ts-node")
.arg("deploy.ts")
.env("ANCHOR_WALLET", cfg.provider.wallet.to_string())
Expand All @@ -1917,7 +1943,7 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
let module_path = cur_dir.join("migrations/deploy.js");
let deploy_script_host_str =
template::deploy_js_script_host(&url, &module_path.display().to_string());
std::fs::write("deploy.js", deploy_script_host_str)?;
fs::write("deploy.js", deploy_script_host_str)?;
std::process::Command::new("node")
.arg("deploy.js")
.env("ANCHOR_WALLET", cfg.provider.wallet.to_string())
Expand Down Expand Up @@ -2019,7 +2045,7 @@ fn shell(cfg_override: &ConfigOverride) -> Result<()> {
.map(|(name, pd)| {
if let Some(idl_fp) = &pd.idl {
let file_str =
std::fs::read_to_string(idl_fp).expect("Unable to read IDL file");
fs::read_to_string(idl_fp).expect("Unable to read IDL file");
let idl = serde_json::from_str(&file_str).expect("Idl not readable");
idls.insert(name.clone(), idl);
}
Expand Down Expand Up @@ -2188,7 +2214,7 @@ fn publish(
// Skip target dir.
if !path_str.contains("target/") && !path_str.contains("/target") {
// Only add the file if it's not empty.
let metadata = std::fs::File::open(&e)?.metadata()?;
let metadata = fs::File::open(&e)?.metadata()?;
if metadata.len() > 0 {
println!("PACKING: {}", e.display().to_string());
if e.is_dir() {
Expand Down
32 changes: 29 additions & 3 deletions cli/src/template.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::config::ProgramWorkspace;
use crate::VERSION;
use anchor_syn::idl::Idl;
use anyhow::Result;
use heck::{CamelCase, SnakeCase};
use heck::{CamelCase, MixedCase, SnakeCase};
use solana_sdk::pubkey::Pubkey;

pub fn default_program_id() -> Pubkey {
Expand All @@ -27,6 +28,25 @@ token = "{}"
)
}

pub fn idl_ts(idl: &Idl) -> Result<String> {
let mut idl = idl.clone();
idl.accounts = idl
.accounts
.into_iter()
.map(|acc| {
let mut acc = acc;
acc.name = acc.name.to_mixed_case();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed a problem with this only now when started updating the project on which I work. Why do we change the account name to mixed case? @ChewingGlass

acc
})
.collect();
let idl_json = serde_json::to_string_pretty(&idl)?;
Ok(format!(
"export type {} = {}",
idl.name.to_camel_case(),
idl_json
))
}

pub fn cargo_toml(name: &str) -> String {
format!(
r#"[package]
Expand Down Expand Up @@ -229,22 +249,28 @@ pub fn ts_package_json() -> String {
pub fn ts_mocha(name: &str) -> String {
format!(
r#"import * as anchor from '@project-serum/anchor';
import {{ Program }} from '@project-serum/anchor';
import {{ {} }} from '../target/types/{}';

describe('{}', () => {{

// Configure the client to use the local cluster.
anchor.setProvider(anchor.Provider.env());

const program = anchor.workspace.{} as Program<{}>;

it('Is initialized!', async () => {{
// Add your test here.
const program = anchor.workspace.{};
const tx = await program.rpc.initialize();
const tx = await program.rpc.initialize({{}});
console.log("Your transaction signature", tx);
}});
}});
"#,
name.to_camel_case(),
name.to_snake_case(),
name,
name.to_camel_case(),
name.to_camel_case(),
)
}

Expand Down
2 changes: 1 addition & 1 deletion tests/escrow/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ wallet = "~/.config/solana/id.json"
escrow = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"

[scripts]
test = "mocha -t 1000000 tests/"
test = "ts-mocha -t 1000000 tests/*.ts"
16 changes: 9 additions & 7 deletions tests/escrow/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"dependencies": {
"@project-serum/anchor": "^0.9.0",
"@project-serum/serum": "0.13.38",
"@solana/web3.js": "^1.18.0",
"@solana/spl-token": "^0.1.6"
},
"devDependencies": {
"ts-mocha": "^8.0.0"
"@project-serum/anchor": "../../ts",
"@project-serum/serum": "latest",
"@solana/spl-token": "latest",
"@solana/web3.js": "latest",
"@types/mocha": "^9.0.0",
"@types/node": "^14.14.37",
"bn.js": "^5.2.0",
"camelcase": "^6.2.0",
"chai": "^4.3.4"
}
}
Loading