-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(creator): MVP of plugin creator CLI (#208)
* inital commit * file writes * locking turbo version * dynamic loading available chains * loading actiontypes from the sdk * templating out some contents of the files * iterating on genereating project file * removing tests from pnpm lock * some cleanup * adding changeset * pr cleanup * cleaning up merge build * adding build utils command and expanding readme * Update README.md * resolve conflicts, update pnpm, remove bu task * updating registry and starting feedback * Update package.json Co-authored-by: Quazia <alf40k@gmail.com> * Update apps/create-plugin/src/index.ts Co-authored-by: Quazia <alf40k@gmail.com> * regen pnpm lock * regen pnpm lock * updating pnpm new lockfile * Chore(creator): sync ts build target * Chore(global): lock down viem dep target * Chore(creator): use internal utils chains * Chore(creator): pin dep versions * Fix(creator): handle name validation correctly --------- Co-authored-by: Chris Cashwell <ccashwell@gmail.com> Co-authored-by: Quazia <alf40k@gmail.com>
- Loading branch information
1 parent
ba3ec29
commit 43245a5
Showing
56 changed files
with
2,665 additions
and
2,352 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,5 @@ | ||
--- | ||
"@rabbitholegg/create-plugin": major | ||
--- | ||
|
||
Introducing the MVP first draft of the Plugin Creator, a tool to generate all the boilerplate code necessary for a new plugin |
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,47 @@ | ||
# BoostDK Plugin Creator | ||
|
||
## Using it | ||
|
||
from the root of the project run: | ||
|
||
```bash | ||
pnpm build | ||
cd apps/create-plugin | ||
pnpm run create | ||
``` | ||
|
||
then you can enter your plugins name, the supported chains, and the actions. | ||
|
||
The script will create a template for you in the packages directory structure. | ||
|
||
|
||
## Templates | ||
|
||
the following actions will be performed, assuming the tool is used to collect [project] name, [chain] ids, and [actions]: | ||
|
||
1. Create a new directory in the packages directory with the name `@rabbitholegg/questdk-plugin-<project>` | ||
2. Create index.ts, [project].ts, [project].test.ts, and test-transaction.ts files in the new directory as well as readme, package.json, etc with the [project] name | ||
3. index.ts imports and interface definition is written based on selected [action] | ||
4. [project].ts imports, a function declaration for each action, and `getSupportedChainIds` are populated | ||
|
||
|
||
## Development | ||
|
||
the following command will both compile and run the plugin creator | ||
|
||
first `cd apps/create-plugin` | ||
|
||
`pnpm run dev` | ||
|
||
|
||
### Summary | ||
|
||
The `src/index.ts` file is the entry point for the plugin creator. This file renders and parses the arguments from the CLI. | ||
|
||
The `src/builder.ts` file is the main logic for creating the plugin. It uses mustache templates and file operations to copy the `/template` directory to the `../../packages` directory and populate it appropriately. | ||
|
||
so: | ||
|
||
* if you want to change the output plugin, modify the source in the /template directory | ||
* if you want to change the CLI, modify the source of src/index.ts | ||
* if you want to change the logic of the plugin creator, modify the source of src/builder.ts |
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,27 @@ | ||
{ | ||
"name": "@rabbitholegg/create-plugin", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"dev": "pnpm run build && node dist/index.js", | ||
"create": "node dist/index.js" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"commander": "11.1.0", | ||
"figlet": "1.7.0" | ||
}, | ||
"devDependencies": { | ||
"@types/prompt": "1.1.8", | ||
"fs-extra": "11.2.0", | ||
"handlebars": "4.7.8", | ||
"picocolors": "1.0.0", | ||
"prompts": "2.4.2", | ||
"@rabbitholegg/questdk-plugin-utils": "workspace:*" | ||
} | ||
} |
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 @@ | ||
export const ABI_TO_DO = [] |
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,217 @@ | ||
const fs = require('fs-extra') | ||
const path = require('path') | ||
import Handlebars from 'handlebars' | ||
import { cyan, red } from 'picocolors' | ||
|
||
type BuilderParams = { | ||
projectName: string | ||
chains: string[] | ||
tx: string | ||
actionTypes: string[] | ||
} | ||
|
||
const arrow = '=>' | ||
const logo = '✛' // not the logo but pretty close | ||
|
||
export async function createPlugin(params: BuilderParams) { | ||
const result = await copyDirectory(params) | ||
if (!result) { | ||
return | ||
} | ||
|
||
registerHelpers() | ||
await replaceProjectName(params) | ||
await setActionNames(params) | ||
await replaceFileNames(params) | ||
logBoostStars() | ||
console.log('Created a plugin for', cyan(`"${params.projectName}"`)) | ||
console.log() | ||
console.log('run the following commands:') | ||
console.log('cd ../..') | ||
console.log('pnpm i') | ||
console.log('pnpm run build') | ||
console.log() | ||
console.log( | ||
'View your new plugin todo list in the README, located at', | ||
cyan(`packages/${params.projectName}/README.md`), | ||
) | ||
|
||
console.log('\n\n\n') | ||
} | ||
|
||
export function logBoostStars() { | ||
console.log() | ||
console.log(`--------------------------- ${logo} ${logo} ${logo} `) | ||
console.log(`--------------------------- ${logo} ${logo} ${logo} `) | ||
console.log(`--------------------------- ${logo} ${logo} ${logo} `) | ||
console.log() | ||
} | ||
|
||
function registerHelpers() { | ||
Handlebars.registerHelper('lowercase', function (aString) { | ||
return aString.toLowerCase() | ||
}) | ||
Handlebars.registerHelper('capitalize', function (aString) { | ||
return aString.charAt(0).toUpperCase() + aString.slice(1) | ||
}) | ||
} | ||
|
||
/** | ||
* This function creates the directory structure and copies over the template | ||
* | ||
* @param params | ||
* @returns | ||
*/ | ||
async function copyDirectory(params: BuilderParams) { | ||
if (params.projectName === undefined) { | ||
console.log(` ${red('exiting')} `) | ||
return false | ||
} | ||
// get the target directory location | ||
const dest = path.join(__dirname, `../../../packages/${params.projectName}`) | ||
// if there is already a directory with the name throw an error | ||
if (await fs.pathExists(dest)) { | ||
console.error( | ||
`Could not create a plugin called ${red( | ||
`"${params.projectName}"`, | ||
)} because it already exists!`, | ||
) | ||
return false | ||
} | ||
// create the directory | ||
try { | ||
await fs.ensureDir(dest) | ||
} catch (_e) { | ||
console.error( | ||
`Could not create a plugin called ${red( | ||
`"${params.projectName}"`, | ||
)} because of ${_e}`, | ||
) | ||
return false | ||
} | ||
|
||
// copy the template files to the new directory | ||
const _source = path.join(__dirname, '../template') | ||
try { | ||
await fs.copy(_source, dest) | ||
console.log( | ||
`\t ${arrow} Created directory for ${cyan(`"${params.projectName}"`)}!`, | ||
) | ||
return true | ||
} catch (_e) { | ||
console.error( | ||
`Could not create a plugin called ${red( | ||
`"${params.projectName}"`, | ||
)} because of ${_e}`, | ||
) | ||
return false | ||
} | ||
} | ||
|
||
/** | ||
* This function replaces multiple instances of the project name | ||
* | ||
* package.json | ||
* readme | ||
* changelog | ||
* src/project.ts | ||
* src/project.test.ts | ||
* index.ts | ||
* | ||
* @param params | ||
* @returns | ||
*/ | ||
async function replaceProjectName(params: BuilderParams) { | ||
// get the target directory location | ||
const dest = path.join(__dirname, `../../../packages/${params.projectName}`) | ||
|
||
// replace the project name in the package.json | ||
const packageJsonPath = path.join(dest, 'package.json') | ||
const packageJson = await fs.readJson(packageJsonPath) | ||
packageJson.name = params.projectName | ||
packageJson.description = `Plugin for ${params.projectName}` | ||
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }) | ||
console.log(`\t ${arrow} Updated file ${cyan('package.json')}!`) | ||
|
||
//replace the project name in the readme | ||
const readmePath = path.join(dest, 'README.md') | ||
const readme = await fs.readFile(readmePath, 'utf8') | ||
const readmeTemplate = Handlebars.compile(readme) | ||
await fs.writeFile(readmePath, readmeTemplate(params)) | ||
console.log(`\t ${arrow} Updated file ${cyan('README.md')}!`) | ||
|
||
//replace the project name in the changelog | ||
const changelogPath = path.join(dest, 'CHANGELOG.md') | ||
const changelog = await fs.readFile(changelogPath, 'utf8') | ||
const changelogTemplate = Handlebars.compile(changelog) | ||
await fs.writeFile(changelogPath, changelogTemplate(params)) | ||
console.log(`\t ${arrow} Updated file ${cyan('CHANGELOG.md')}!`) | ||
|
||
//replace the project name in index.ts.t | ||
const indexPath = path.join(dest, 'src/index.ts.t') | ||
const index = await fs.readFile(indexPath, 'utf8') | ||
const indexTemplate = Handlebars.compile(index) | ||
await fs.writeFile(indexPath, indexTemplate(params)) | ||
console.log(`\t ${arrow} Updated file ${cyan('index.ts')}!`) | ||
} | ||
|
||
/** | ||
* This function renames filenames to match the project name -and- it removes the .t(emplate) extension | ||
* | ||
* src/project.ts | ||
* src/project.test.ts | ||
* index.ts | ||
* | ||
* @param params | ||
* @returns | ||
*/ | ||
async function replaceFileNames(params: BuilderParams) { | ||
// get the target directory location | ||
const dest = path.join(__dirname, `../../../packages/${params.projectName}`) | ||
|
||
//rename index.ts | ||
const indexPath = path.join(dest, 'src/index.ts.t') | ||
await fs.rename(indexPath, path.join(dest, 'src/index.ts')) | ||
console.log(`\t ${arrow} finalized file ${cyan('index.ts')}!`) | ||
|
||
//rename project.ts | ||
const projectPath = path.join(dest, 'src/Project.ts.t') | ||
await fs.rename(projectPath, path.join(dest, `src/${params.projectName}.ts`)) | ||
console.log(`\t ${arrow} finalized file ${cyan(`${params.projectName}.ts`)}!`) | ||
|
||
//rename project.test.ts | ||
const testPath = path.join(dest, 'src/Project.test.ts.t') | ||
await fs.rename( | ||
testPath, | ||
path.join(dest, `src/${params.projectName}.test.ts`), | ||
) | ||
console.log( | ||
`\t ${arrow} finalized file ${cyan(`${params.projectName}.test.ts`)}!`, | ||
) | ||
} | ||
|
||
/** | ||
* This function creates the code for the specified actions | ||
* | ||
* | ||
* @param params | ||
* @returns | ||
*/ | ||
async function setActionNames(params: BuilderParams) { | ||
// get the target directory location | ||
const dest = path.join(__dirname, `../../../packages/${params.projectName}`) | ||
|
||
//replace the action names in the index | ||
const indexPath = path.join(dest, 'src/index.ts.t') | ||
const index = await fs.readFile(indexPath, 'utf8') | ||
const indexTemplate = Handlebars.compile(index) | ||
await fs.writeFile(indexPath, indexTemplate(params)) | ||
console.log(`\t ${arrow} created actions in file ${cyan('index.ts')}!`) | ||
|
||
//replace the action names in the project file | ||
const projectPath = path.join(dest, 'src/Project.ts.t') | ||
const project = await fs.readFile(projectPath, 'utf8') | ||
const projectTemplate = Handlebars.compile(project) | ||
await fs.writeFile(projectPath, projectTemplate(params)) | ||
console.log(`\t ${arrow} created actions in file ${cyan('Project.ts')}!`) | ||
} |
Oops, something went wrong.