Skip to content

Commit

Permalink
Finished express action template
Browse files Browse the repository at this point in the history
- Added an express template to @sei-js/create-sei for an actions API using express
  • Loading branch information
codebycarson committed Aug 22, 2024
1 parent 10ed031 commit 0428698
Show file tree
Hide file tree
Showing 21 changed files with 3,431 additions and 106 deletions.
2 changes: 1 addition & 1 deletion packages/create-sei/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"create-sei": "./dist/main.js"
},
"scripts": {
"build": "tsc && cp -r ./templates ./dist/templates",
"build": "tsc && rm -rf ./dist/templates && cp -r ./templates ./dist/templates",
"dev": "node --loader ts-node/esm src/main.ts",
"lint": "eslint --ext .ts"
},
Expand Down
126 changes: 93 additions & 33 deletions packages/create-sei/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ const printWelcomeMessage = () => {
borderColor: '#932C23'
})
);
}

};

enum FrontendScaffolding {
Vite = 'vite',
Expand All @@ -40,15 +39,19 @@ export enum EVMLibrary {
Wagmi = 'wagmi'
}

interface WizardOptions {
interface AppWizardOptions {
name?: string;
framework?: FrontendScaffolding;
ecosystem?: RPCIntegrationType;
library?: EVMLibrary;
}

interface ActionsWizardOptions {
name?: string;
}

const promptFramework = async () => {
const {appFramework} = await inquirer.prompt([
const { appFramework } = await inquirer.prompt([
{
type: 'list',
name: 'appFramework',
Expand All @@ -58,10 +61,10 @@ const promptFramework = async () => {
]);

return appFramework;
}
};

const promptRpcIntegrations = async () => {
const {rpcIntegrationType} = await inquirer.prompt([
const { rpcIntegrationType } = await inquirer.prompt([
{
type: 'list',
name: 'rpcIntegrationType',
Expand All @@ -74,7 +77,7 @@ const promptRpcIntegrations = async () => {
};

const promptEVMLibrary = async () => {
const {evmLibrary} = await inquirer.prompt([
const { evmLibrary } = await inquirer.prompt([
{
type: 'list',
name: 'evmLibrary',
Expand All @@ -86,24 +89,24 @@ const promptEVMLibrary = async () => {
return evmLibrary;
};

function isValidDirectoryName(dirName) {
function isValidDirectoryName(dirName: string) {
const illegalRe = /[<>:"/\\|?*\x00-\x1F]/g;
const windowsReservedRe = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
const trailingRe = /[. ]+$/;
const validNpmPackageRe = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*)?[a-z0-9-~][a-z0-9-._~]*$/;

if (typeof dirName !== 'string' || dirName.length === 0) {
return false;
return false;
}

if (illegalRe.test(dirName) || windowsReservedRe.test(dirName) || trailingRe.test(dirName) || !validNpmPackageRe.test(dirName)) {
return false;
return false;
}

return true;
}

const validateOptions = (options: WizardOptions): boolean => {
const validateAppOptions = (options: AppWizardOptions): boolean => {
let valid = true;

if (options.name) {
Expand Down Expand Up @@ -138,20 +141,19 @@ const validateOptions = (options: WizardOptions): boolean => {
}

return valid;
}
};

async function runWizard(options: WizardOptions): Promise<void> {
if (!validateOptions(options)) {
async function runAppWizard(options: AppWizardOptions): Promise<void> {
if (!validateAppOptions(options)) {
return;
}

printWelcomeMessage();

let dAppName = '';
let dAppName: string;
if (options.name) {
dAppName = options.name
}
else {
dAppName = options.name;
} else {
const promptResult = await inquirer.prompt([
{
type: 'input',
Expand All @@ -163,38 +165,96 @@ async function runWizard(options: WizardOptions): Promise<void> {
}
]);

dAppName = promptResult.dAppName
}
dAppName = promptResult.dAppName;
}

const appFramework = options.framework || await promptFramework();
let appConnectionType = options.ecosystem || await promptRpcIntegrations();
const appFramework = options.framework || (await promptFramework());
let appConnectionType = options.ecosystem || (await promptRpcIntegrations());
if (appConnectionType == RPCIntegrationType.EVM) {
appConnectionType = options.library || await promptEVMLibrary();
appConnectionType = options.library || (await promptEVMLibrary());
}

const templateName = `${appFramework}-${appConnectionType}-template`;
const templatePath = path.join(__dirname, 'templates', templateName)
const dst = path.join(process.cwd(), dAppName)
await fs.promises.cp(templatePath, dst, {recursive: true})
const templatePath = path.join(__dirname, 'templates', templateName);
const dst = path.join(process.cwd(), dAppName);
await fs.promises.cp(templatePath, dst, { recursive: true });

console.log(`Project setup complete! Using template ${templateName}\n`);
console.log(`To start your app, run: \n > cd ${dAppName} \n > yarn \n > yarn dev\n`);
}

const validateActionsOptions = (options: ActionsWizardOptions): boolean => {
let valid = true;

if (options.name) {
if (!isValidDirectoryName(options.name)) {
console.log('Invalid package name. Please use a valid npm package name.');
valid = false;
}
}

return valid;
};

async function runActionsWizard(options: ActionsWizardOptions): Promise<void> {
if (!validateActionsOptions(options)) {
return;
}

printWelcomeMessage();

let dAppName: string;
if (options.name) {
dAppName = options.name;
} else {
const promptResult = await inquirer.prompt([
{
type: 'input',
name: 'dAppName',
message: 'What is your dApp (project) name?',
validate: (input: string) => {
return isValidDirectoryName(input) || 'Invalid package name. Please use a valid npm package name.';
}
}
]);

dAppName = promptResult.dAppName;
}

const templateName = `express-action-template`;
const templatePath = path.join(__dirname, 'templates', templateName);
const dst = path.join(process.cwd(), dAppName);
await fs.promises.cp(templatePath, dst, { recursive: true });

console.log(`Project setup complete! Using template ${templateName}\n`);
console.log(`To start your app, run: \n > cd ${dAppName} \n and read the README.md file for the project.\n`);
}

program
.command('app')
.description('Create a new SEI dApp')
.option('-n, --name <name>', `Specify the name of your dApp. Name must be a valid package name.`)
.option('-f, --framework <framework>', `Specify the app framework to use: [${Object.values(FrontendScaffolding).join(', ')}]`)
.option('-e, --ecosystem <ecosystem>', `Specify the ecosystem to use: [${Object.values(RPCIntegrationType).join(', ')}]`)
.option('-l, --library <library>', `Specify the EVM library to use: [${Object.values(EVMLibrary).join(', ')}]. Only used if ecosystem chosen is 'EVM'`)
.action(async (options: WizardOptions) => {
.action(async (options: AppWizardOptions) => {
try {
await runWizard(options);
await runAppWizard(options);
} catch (error) {
console.error('An error occurred:', error);
}
});

program.parse(process.argv);
program
.command('action-api')
.description('Create a new SEI action API')
.option('-n, --name <name>', `Specify the name of your API project. Name must be a valid package name.`)
.action(async (options: ActionsWizardOptions) => {
try {
await runActionsWizard(options);
} catch (error) {
console.error('An error occurred:', error);
}
});

program.parse(process.argv);
4 changes: 4 additions & 0 deletions packages/create-sei/templates/express-action-template/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Environment Configuration
NODE_ENV="development" # Options: 'development', 'production'
PORT="8000" # The port your server will listen on
HOST="localhost" # Hostname for the server
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v22
31 changes: 31 additions & 0 deletions packages/create-sei/templates/express-action-template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 🚀 Actions API using Express and TypeScript

## Setup
To install the dependencies required to run this project, run the following command:
```bash
yarn install
```

## Developing
To run the development server on the port defined in the `.env` file, run the following command:
```bash
yarn dev
```

## Testing
To run the tests with vitest, run the following command:
```bash
yarn test
```

## Building
To build the project, run the following command:
```bash
yarn build
```

## Serving
To serve the built project, run the following command:
```bash
yarn serve
```
25 changes: 25 additions & 0 deletions packages/create-sei/templates/express-action-template/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"formatter": {
"indentStyle": "space",
"lineWidth": 120
},
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off",
"noConfusingVoidType": "off"
},
"style": {
"noUselessElse": "off",
"noNonNullAssertion": "off"
},
"complexity": {
"noForEach": "off"
}
}
}
}
49 changes: 49 additions & 0 deletions packages/create-sei/templates/express-action-template/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "express-action-template",
"version": "1.0.0",
"description": "An Express boilerplate for a Sei action API written in Typescript",
"repository": "sei-protocol/sei-js",
"license": "MIT",
"main": "index.ts",
"private": true,
"scripts": {
"dev": "tsx watch --clear-screen=false src/index.ts",
"build": "tsup",
"start": "node dist/index.js",
"clean": "rimraf dist coverage",
"check": "biome check src/",
"fix": "biome check src/ --fix",
"format": "biome format src/ --fix",
"test": "vitest run"
},
"dependencies": {
"@sei-js/actions": "^1.0.0",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"helmet": "^7.1.0",
"http-status-codes": "^2.3.0"
},
"devDependencies": {
"@biomejs/biome": "1.8.3",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/supertest": "^6.0.2",
"lint-staged": "^15.2.2",
"rimraf": "^6.0.0",
"supertest": "^7.0.0",
"tsup": "^8.0.2",
"tsx": "^4.7.2",
"typescript": "^5.4.4",
"vitest": "^2.0.0"
},
"lint-staged": {
"*.{js,ts,cjs,mjs,d.cts,d.mts,json,jsonc}": ["biome check --apply --no-errors-on-unmatched"]
},
"tsup": {
"entry": ["src", "!src/**/__tests__/**", "!src/**/*.test.*"],
"splitting": false,
"sourcemap": true,
"clean": true
}
}
Loading

0 comments on commit 0428698

Please sign in to comment.