Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
},
"scripts": {
"postinstall": "yarn build",
"build": "rm -rf dist && tsc",
"build": "rm -rf dist && tsc && yarn copyfiles -u 1 src/templates/** dist",
"start": "yarn build && node ./bin/run.js",
"test": "yarn build && ts-mocha ./tests --recursive --extension .spec.ts --exit --timeout 5000",
"lint": "eslint ."
},
"dependencies": {
"@inquirer/prompts": "^7.2.4",
"@oclif/core": "^4.2.2",
"@oclif/plugin-not-found": "^3.2.38",
"assemblyscript": "^0.27.1",
Expand All @@ -29,6 +30,7 @@
"@types/mocha": "^10.0.1",
"@types/node": "^22.10.5",
"chai": "^4.3.7",
"copyfiles": "^2.4.1",
"eslint-config-mimic": "^0.0.3",
"mocha": "^10.2.0",
"sinon": "^18.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as ts from 'typescript'
import { ZodError } from 'zod'

import { DuplicateEntryError, EmptyManifestError, MoreThanOneEntryError } from '../errors'
import log from '../logger'
import log from '../log'
import { validateManifest } from '../ManifestValidator'

export default class Compile extends Command {
Expand Down
41 changes: 40 additions & 1 deletion packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { confirm } from '@inquirer/prompts'
import { Command, Flags } from '@oclif/core'
import * as fs from 'fs'
import * as path from 'path'

import log from '../log'

export default class Init extends Command {
static override description = 'Initializes a new Mimic-compatible project structure in the specified directory'
Expand All @@ -13,7 +18,41 @@ export default class Init extends Command {
public async run(): Promise<void> {
const { flags } = await this.parse(Init)
const { directory, force } = flags
const fullDirectory = path.resolve(directory)
const templateDirectory = path.join(__dirname, '../templates')

if (force) {
const shouldDelete = await confirm({
message: `Are you sure you want to ${log.warnText('delete')} all the contents in ${log.highlightText(fullDirectory)}. This action is ${log.warnText('irreversible')}`,
default: false,
})
if (!shouldDelete) {
console.log('You can remove the --force flag from your command')
console.log('Stopping initialization...')
this.exit(0)
}
log.startAction(`Deleting contents of ${fullDirectory}`)
if (fs.existsSync(fullDirectory)) fs.rmSync(fullDirectory, { recursive: true })
}

log.startAction('Creating files')

if (fs.existsSync(fullDirectory)) {
this.error(`Directory ${log.highlightText(fullDirectory)} is not empty`, {
code: 'DirectoryNotEmpty',
suggestions: [
'You can specify the directory with --directory',
`You can ${log.warnText('overwrite')} an existing directory with --force`,
],
})
}

console.log(directory, force)
const srcPath = path.join(fullDirectory, 'src/')
const manifestPath = path.join(fullDirectory, 'manifest.yaml')
fs.mkdirSync(srcPath, { recursive: true })
fs.copyFileSync(`${templateDirectory}/task.ts`, path.join(srcPath, 'task.ts'))
fs.copyFileSync(`${templateDirectory}/manifest.yaml`, manifestPath)
log.stopAction()
console.log('New project initialized!')
}
}
File renamed without changes.
10 changes: 10 additions & 0 deletions packages/cli/src/templates/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 1.0.0
name: Exmaple Task
description: Autogenerated Example Task
trigger:
type: cron
schedule: "0 0 * * *"
delta: "1h"
inputs:
- chainId: 1
- tokenIn: 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
8 changes: 8 additions & 0 deletions packages/cli/src/templates/task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { environment } from '@mimicprotocol/lib-ts'

export default function main(): void {
// Replace this with your task code
const firstNumber = environment.getValue()
const firstIntent = firstNumber * 2
environment.createIntent(firstIntent)
}
98 changes: 98 additions & 0 deletions packages/cli/tests/commands/init.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { runCommand } from '@oclif/test'
import { expect } from 'chai'
import { spawnSync } from 'child_process'
import * as fs from 'fs'
import { join } from 'path'

describe('init', () => {
const commandPath = join(__dirname, 'new-project')

afterEach('delete generated files', () => {
if (fs.existsSync(commandPath)) fs.rmSync(commandPath, { recursive: true })
})

context('when force flag is not passed', () => {
const command = ['init', `--directory ${commandPath}`]

context('when the directory does not exist', () => {
it('creates the correct files', async () => {
const { stdout, error } = await runCommand(command)
expect(error).to.be.undefined
expect(stdout).to.include('New project initialized!')
expect(fs.existsSync(`${commandPath}/src/task.ts`)).to.be.true
expect(fs.existsSync(`${commandPath}/manifest.yaml`)).to.be.true
})
})

context('when the directory exists', () => {
beforeEach('create directory', () => {
fs.mkdirSync(commandPath)
})

it('throws an error', async () => {
const { error } = await runCommand(command)

expect(error?.message).to.be.equal(`Directory ${commandPath} is not empty`)
expect(error?.code).to.be.equal('DirectoryNotEmpty')
expect(error?.suggestions?.length).to.be.eq(2)
})
})
})

context('when force flag is passed', () => {
let userResponse
const command = ['init', `--directory ${commandPath}`, '--force']

context('when the user accepts the confirmation', () => {
beforeEach('stub user input', () => {
userResponse = 'Y'
})

context('when the directory exists', () => {
beforeEach('create directory', () => {
fs.mkdirSync(commandPath)
})

it("deletes the folder and it's contents", async () => {
const { stdout, status } = runCommandWithUserInput(command, userResponse)
expect(status).to.be.equal(0)
expect(stdout).to.include('New project initialized!')
expect(fs.existsSync(`${commandPath}/src/task.ts`)).to.be.true
expect(fs.existsSync(`${commandPath}/manifest.yaml`)).to.be.true
})
})

context('when the directory does not exist', () => {
it("deletes the folder and it's contents", async () => {
const { stdout, status } = runCommandWithUserInput(command, userResponse)
expect(status).to.be.equal(0)
expect(stdout).to.include('New project initialized!')
expect(fs.existsSync(`${commandPath}/src/task.ts`)).to.be.true
expect(fs.existsSync(`${commandPath}/manifest.yaml`)).to.be.true
})
})
})

context('when the user rejects the confirmation', () => {
beforeEach('stub user input', () => {
userResponse = 'N'
})

it('stops execution', async () => {
const { stdout, status } = runCommandWithUserInput(command, userResponse)
expect(status).to.be.equal(0)
expect(stdout).to.include('You can remove the --force flag from your command')
expect(stdout).to.include('Stopping initialization...')
})
})
})
})

const runCommandWithUserInput = (command: string[], userResponse: string) => {
const parsedCommand = command.flatMap((c) => c.split(' '))
return spawnSync('yarn', ['mimic', ...parsedCommand], {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
input: `${userResponse}\n`,
})
}
3 changes: 2 additions & 1 deletion packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"strict": true,
"types": ["node"]
},
"include": [ "src/**/*" ]
"include": [ "src/**/*" ],
"exclude": [ "src/templates/**/*" ]
}
Loading