-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR adds a brand new `@codama/cli` package which is then imported (**not** re-exported) by the main `codama` library in order to use the `codama` keyword as CLI binary. The CLI currently supports the following two commands: - `codama init`: which initialises a Codama configuration file by using prompts and selected script presets. - `codama run`: which runs any script defined in your configuration file. You can learn more about the CLI commands and the Codama config file from the `@codama/cli` README copy/pasted below: --- This package provides a CLI for the Codama library that can be used to run scripts on Codama IDLs. Note that, whilst the CLI code is located in the `@codama/cli` package, the CLI binary is directly provided by the main `codama` library. ## Getting started To get started with Codama, simply install `codama` to your project and run the `init` command like so: ```sh pnpm install codama codama init ``` You will be prompted for the path of your IDL and asked to select any script presets you would like to use. ## `codama run` Once you have your codama config file, you can run your Codama scripts using the `codama run` command as follows: ```sh codama run # Only runs your before visitors. codama run js rust # Runs your before visitors followed by the `js` and `rust` scripts. codama run --all # Runs your before visitors followed by all your scripts. ``` ## The configuration file The codama config file defines an object containing the following fields: - `idl` (string): The path to the IDL file. This can be a Codama IDL or an Anchor IDL which will be automatically converted to a Codama IDL. - `before` (array): An array of visitors that will run before every script. - `scripts` (object): An object defining the available Codama scripts. The keys identify the scripts and the values are arrays of visitors that make up the script. Whether it is in the `before` array or in the `scripts` values, when defining a visitor you may either provide: - an object with the following fields: - `from` (string): The import path to the visitor. - `args` (array): An array of arguments to pass to the visitor. - a string: The import path to the visitor. This is equivalent to providing an object with a `from` field and an empty `args` array. Visitor import paths can either be local paths (pointing to JavaScript files exporting visitors) or npm package names. By default, the `default` export will be used but you may specify a nammed export by appending a `#` followed by the export name. When resolved, the imported element inside the module should either be a `Visitor<any, 'rootNode'>` or a function that returns a `Visitor<any, 'rootNode'>` given the arguments provided. Here are some examples of valid visitor import paths: ```js './my-visitor.js'; // Relative local path to a visitor module. '/Users/me/my-visitor.js'; // Absolute local path to a visitor module. 'some-library'; // npm package name. '@acme/some-library'; // Scoped npm package name. './my-visitor.js#myExport'; // Named export from a local path. '@acme/some-library#myExport'; // Named export from an npm package. ``` Here is an example of what a Codama configuration file might look like: ```json { "idl": "path/to/idl", "before": [ "./my-before-visitor.js", { "from": "some-library#removeTypes", "args": [["internalFoo", "internalBar"]] } ], "scripts": { "js": [ { "from": "@codama/renderers-js", "args": ["clients/js/src/generated"] } ] } } ``` Note that you can use the `--js` flag to generate a `.js` configuration file when running the `init` command.
- Loading branch information
1 parent
bc2e306
commit 7bb6920
Showing
42 changed files
with
990 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
'@codama/renderers-vixen-parser': patch | ||
'@codama/renderers-js-umi': patch | ||
'@codama/renderers-rust': patch | ||
'@codama/renderers-js': patch | ||
--- | ||
|
||
Export `renderVisitor` function of all renderers packages as `default` export. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'codama': patch | ||
'@codama/cli': patch | ||
--- | ||
|
||
Add new Codama CLI |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
dist/ | ||
e2e/ | ||
test-ledger/ | ||
target/ | ||
CHANGELOG.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Codama | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# Codama ➤ CLI | ||
|
||
[![npm][npm-image]][npm-url] | ||
[![npm-downloads][npm-downloads-image]][npm-url] | ||
|
||
[npm-downloads-image]: https://img.shields.io/npm/dm/@codama/cli.svg?style=flat | ||
[npm-image]: https://img.shields.io/npm/v/@codama/cli.svg?style=flat&label=%40codama%2Fcli | ||
[npm-url]: https://www.npmjs.com/package/@codama/cli | ||
|
||
This package provides a CLI for the Codama library that can be used to run scripts on Codama IDLs. | ||
|
||
Note that, whilst the CLI code is located in the `@codama/cli` package, the CLI binary is directly provided by the main `codama` library. | ||
|
||
## Getting started | ||
|
||
To get started with Codama, simply install `codama` to your project and run the `init` command like so: | ||
|
||
```sh | ||
pnpm install codama | ||
codama init | ||
``` | ||
|
||
You will be prompted for the path of your IDL and asked to select any script presets you would like to use. | ||
|
||
## `codama run` | ||
|
||
Once you have your codama config file, you can run your Codama scripts using the `codama run` command as follows: | ||
|
||
```sh | ||
codama run # Only runs your before visitors. | ||
codama run js rust # Runs your before visitors followed by the `js` and `rust` scripts. | ||
codama run --all # Runs your before visitors followed by all your scripts. | ||
``` | ||
|
||
## The configuration file | ||
|
||
The codama config file defines an object containing the following fields: | ||
|
||
- `idl` (string): The path to the IDL file. This can be a Codama IDL or an Anchor IDL which will be automatically converted to a Codama IDL. | ||
- `before` (array): An array of visitors that will run before every script. | ||
- `scripts` (object): An object defining the available Codama scripts. The keys identify the scripts and the values are arrays of visitors that make up the script. | ||
|
||
Whether it is in the `before` array or in the `scripts` values, when defining a visitor you may either provide: | ||
|
||
- an object with the following fields: | ||
- `from` (string): The import path to the visitor. | ||
- `args` (array): An array of arguments to pass to the visitor. | ||
- a string: The import path to the visitor. This is equivalent to providing an object with a `from` field and an empty `args` array. | ||
|
||
Visitor import paths can either be local paths (pointing to JavaScript files exporting visitors) or npm package names. By default, the `default` export will be used but you may specify a named export by appending a `#` followed by the export name. When resolved, the imported element inside the module should either be a `Visitor<any, 'rootNode'>` or a function that returns a `Visitor<any, 'rootNode'>` given the arguments provided. Here are some examples of valid visitor import paths: | ||
|
||
```js | ||
'./my-visitor.js'; // Relative local path to a visitor module. | ||
'/Users/me/my-visitor.js'; // Absolute local path to a visitor module. | ||
'some-library'; // npm package name. | ||
'@acme/some-library'; // Scoped npm package name. | ||
'./my-visitor.js#myExport'; // Named export from a local path. | ||
'@acme/some-library#myExport'; // Named export from an npm package. | ||
``` | ||
|
||
Here is an example of what a Codama configuration file might look like: | ||
|
||
```json | ||
{ | ||
"idl": "path/to/idl", | ||
"before": [ | ||
"./my-before-visitor.js", | ||
{ "from": "some-library#removeTypes", "args": [["internalFoo", "internalBar"]] } | ||
], | ||
"scripts": { | ||
"js": [ | ||
{ | ||
"from": "@codama/renderers-js", | ||
"args": ["clients/js/src/generated"] | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
Note that you can use the `--js` flag to generate a `.js` configuration file when running the `init` command. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
{ | ||
"name": "@codama/cli", | ||
"version": "1.0.0", | ||
"description": "The package that provides a CLI for the Codama standard", | ||
"exports": { | ||
"types": "./dist/types/index.d.ts", | ||
"node": { | ||
"import": "./dist/index.node.mjs", | ||
"require": "./dist/index.node.cjs" | ||
} | ||
}, | ||
"main": "./dist/index.node.cjs", | ||
"module": "./dist/index.node.mjs", | ||
"types": "./dist/types/index.d.ts", | ||
"type": "commonjs", | ||
"files": [ | ||
"./dist/types", | ||
"./dist/index.*" | ||
], | ||
"sideEffects": false, | ||
"keywords": [ | ||
"codama", | ||
"standard", | ||
"cli" | ||
], | ||
"scripts": { | ||
"build": "rimraf dist && pnpm build:src && pnpm build:types", | ||
"build:src": "zx ../../node_modules/@codama/internals/scripts/build-src.mjs node", | ||
"build:types": "zx ../../node_modules/@codama/internals/scripts/build-types.mjs", | ||
"dev": "zx ../../node_modules/@codama/internals/scripts/test-unit.mjs node --watch", | ||
"lint": "zx ../../node_modules/@codama/internals/scripts/lint.mjs", | ||
"lint:fix": "zx ../../node_modules/@codama/internals/scripts/lint.mjs --fix", | ||
"test": "pnpm test:types && pnpm test:treeshakability && pnpm test:node", | ||
"test:node": "zx ../../node_modules/@codama/internals/scripts/test-unit.mjs node", | ||
"test:treeshakability": "zx ../../node_modules/@codama/internals/scripts/test-treeshakability.mjs", | ||
"test:types": "zx ../../node_modules/@codama/internals/scripts/test-types.mjs" | ||
}, | ||
"dependencies": { | ||
"@codama/nodes": "workspace:*", | ||
"@codama/nodes-from-anchor": "workspace:*", | ||
"@codama/renderers": "workspace:*", | ||
"@codama/renderers-js": "workspace:*", | ||
"@codama/renderers-js-umi": "workspace:*", | ||
"@codama/renderers-rust": "workspace:*", | ||
"@codama/visitors": "workspace:*", | ||
"@codama/visitors-core": "workspace:*", | ||
"chalk": "^5.4.1", | ||
"commander": "^13.1.0", | ||
"prompts": "^2.4.2" | ||
}, | ||
"devDependencies": { | ||
"@types/prompts": "^2.4.9" | ||
}, | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/codama-idl/codama" | ||
}, | ||
"bugs": { | ||
"url": "http://github.com/codama-idl/codama/issues" | ||
}, | ||
"browserslist": [ | ||
"supports bigint and not dead", | ||
"maintained node versions" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './init'; | ||
export * from './run'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { Command } from 'commander'; | ||
import prompts, { PromptType } from 'prompts'; | ||
|
||
import { Config, ScriptConfig, ScriptName } from '../config'; | ||
import { canRead, logBanner, logSuccess, resolveRelativePath, writeFile } from '../utils'; | ||
|
||
export function setInitCommand(program: Command): void { | ||
program | ||
.command('init') | ||
.argument('[output]', 'Optional path used to output the configuration file') | ||
.option('-d, --default', 'Bypass prompts and select all defaults options') | ||
.option('--js', 'Forces the output to be a JavaScript file') | ||
.action(doInit); | ||
} | ||
|
||
type InitOptions = { | ||
default?: boolean; | ||
js?: boolean; | ||
}; | ||
|
||
async function doInit(explicitOutput: string | undefined, options: InitOptions) { | ||
const output = getOutputPath(explicitOutput, options); | ||
const useJsFile = options.js || output.endsWith('.js'); | ||
if (await canRead(output)) { | ||
throw new Error(`Configuration file already exists at "${output}".`); | ||
} | ||
|
||
logBanner(); | ||
const result = await getPromptResult(options); | ||
const content = getContentFromPromptResult(result, useJsFile); | ||
await writeFile(output, content); | ||
logSuccess(`Configuration file created at "${output}".`); | ||
} | ||
|
||
function getOutputPath(explicitOutput: string | undefined, options: Pick<InitOptions, 'js'>): string { | ||
if (explicitOutput) { | ||
return resolveRelativePath(explicitOutput); | ||
} | ||
return resolveRelativePath(options.js ? 'codama.js' : 'codama.json'); | ||
} | ||
|
||
type PromptResult = { | ||
idlPath: string; | ||
jsPath?: string; | ||
rustCrate?: string; | ||
rustPath?: string; | ||
scripts: string[]; | ||
}; | ||
|
||
async function getPromptResult(options: Pick<InitOptions, 'default'>): Promise<PromptResult> { | ||
const defaults = getDefaultPromptResult(); | ||
if (options.default) { | ||
return defaults; | ||
} | ||
|
||
const hasScript = | ||
(script: string, type: PromptType = 'text') => | ||
(_: unknown, values: { scripts: string[] }) => | ||
values.scripts.includes(script) ? type : null; | ||
const result: PromptResult = await prompts( | ||
[ | ||
{ | ||
initial: defaults.idlPath, | ||
message: 'Where is your IDL located? (Supports Codama and Anchor IDLs).', | ||
name: 'idlPath', | ||
type: 'text', | ||
}, | ||
{ | ||
choices: [ | ||
{ selected: true, title: 'Generate JavaScript client', value: 'js' }, | ||
{ selected: true, title: 'Generate Rust client', value: 'rust' }, | ||
], | ||
instructions: '[space] to toggle / [a] to toggle all / [enter] to submit', | ||
message: 'Which script preset would you like to use?', | ||
name: 'scripts', | ||
type: 'multiselect', | ||
}, | ||
{ | ||
initial: defaults.jsPath, | ||
message: '[js] Where should the JavaScript code be generated?', | ||
name: 'jsPath', | ||
type: hasScript('js'), | ||
}, | ||
{ | ||
initial: defaults.rustCrate, | ||
message: '[rust] Where is the Rust client crate located?', | ||
name: 'rustCrate', | ||
type: hasScript('rust'), | ||
}, | ||
{ | ||
initial: (prev: string) => `${prev}/src/generated`, | ||
message: '[rust] Where should the Rust code be generated?', | ||
name: 'rustPath', | ||
type: hasScript('rust'), | ||
}, | ||
], | ||
{ | ||
onCancel: () => { | ||
throw new Error('Operation cancelled.'); | ||
}, | ||
}, | ||
); | ||
|
||
return result; | ||
} | ||
|
||
function getDefaultPromptResult(): PromptResult { | ||
return { | ||
idlPath: 'program/idl.json', | ||
jsPath: 'clients/js/src/generated', | ||
rustCrate: 'clients/rust', | ||
rustPath: 'clients/rust/src/generated', | ||
scripts: ['js', 'rust'], | ||
}; | ||
} | ||
|
||
function getContentFromPromptResult(result: PromptResult, useJsFile: boolean): string { | ||
const scripts: Record<ScriptName, ScriptConfig> = {}; | ||
if (result.scripts.includes('js')) { | ||
scripts.js = { | ||
from: '@codama/renderers-js', | ||
args: [result.jsPath], | ||
}; | ||
} | ||
if (result.scripts.includes('rust')) { | ||
scripts.rust = { | ||
from: '@codama/renderers-rust', | ||
args: [result.rustPath, { crateFolder: result.rustCrate, formatCode: true }], | ||
}; | ||
} | ||
const content: Config = { idl: result.idlPath, before: [], scripts }; | ||
|
||
if (!useJsFile) { | ||
return JSON.stringify(content, null, 4); | ||
} | ||
|
||
return ( | ||
'export default ' + | ||
JSON.stringify(content, null, 4) | ||
// Remove quotes around property names | ||
.replace(/"([^"]+)":/g, '$1:') | ||
// Convert double-quoted strings to single quotes | ||
.replace(/"([^"]*)"/g, "'$1'") | ||
); | ||
} |
Oops, something went wrong.