@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
128 lines (109 loc) • 3.97 kB
text/typescript
import { SpruceSchemas } from '#spruce/schemas/schemas.types'
import bootSkillOptionsSchema from '#spruce/schemas/spruceCli/v2020_07_22/bootSkillOptions.schema'
import SpruceError from '../../../errors/SpruceError'
import CommandServiceImpl from '../../../services/CommandService'
import AbstractAction from '../../AbstractAction'
import { FeatureActionResponse } from '../../features.types'
export default class BootAction extends AbstractAction<OptionsSchema> {
public optionsSchema: OptionsSchema = bootSkillOptionsSchema
public commandAliases = ['boot']
public invocationMessage = 'Booting skill... ⚡️'
private onDataHandler: ((msg: string) => void) | null | undefined
private onErrorHandler: ((msg: string) => void) | null | undefined
public async execute(options: Options): Promise<FeatureActionResponse> {
const command = this.Service('command')
let script = 'boot'
this.onDataHandler = options.onData
this.onErrorHandler = options.onError
if (options.local) {
script += '.local'
}
let runningPromise: any
let bootPromise = new Promise((resolve, reject) => {
runningPromise = this.boot(command, script, resolve, reject)
})
const hints = ['Skill booted succesfully!', 'Skill torn down cleanly!']
const meta = {
isBooted: false,
kill: command.kill.bind(command),
pid: command.pid() as number,
promise: runningPromise,
bootPromise,
}
return new Promise((resolve, reject) => {
bootPromise = bootPromise
.then(() => {
meta.isBooted = true
return { meta, hints }
})
.catch((err) => {
reject(err)
return err
})
if (!options.shouldReturnImmediately) {
void bootPromise.then(() => resolve({ meta, hints }))
} else {
meta.bootPromise = bootPromise
resolve({
meta,
hints,
})
}
})
}
private async boot(
command: CommandServiceImpl,
script: string,
resolve: (value: unknown) => void,
reject: (reason?: any) => void
) {
let isBooted = false
try {
const results = await command.execute(`yarn ${script}`, {
onData: (data) => {
this.onDataHandler?.(data)
if (!isBooted && data.search(':: Skill booted') > -1) {
isBooted = true
resolve(undefined)
}
},
onError: (data) => {
this.onErrorHandler?.(data)
},
})
if (!isBooted) {
isBooted = true
resolve(undefined)
}
return results
} catch (err: any) {
let mappedErr = err
if (
mappedErr.message.search(
/Error: cannot find module.*?build\/index/gi
) > -1
) {
mappedErr = new SpruceError({
code: 'BOOT_ERROR',
friendlyMessage:
'You must build your skill before you can boot it!',
})
}
if (!isBooted) {
reject(mappedErr)
} else {
throw mappedErr
}
}
return null
}
}
type OptionsSchema = SpruceSchemas.SpruceCli.v2020_07_22.BootSkillOptionsSchema
type Options = SpruceSchemas.SpruceCli.v2020_07_22.BootSkillOptions
export interface BootMeta {
kill: () => void
pid: number
promise: Promise<void>
isBooted: boolean
bootPromise: Promise<void>
}