@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
262 lines (220 loc) • 7.13 kB
text/typescript
import globby from '@sprucelabs/globby'
import { diskUtil } from '@sprucelabs/spruce-skill-utils'
import {
VcTemplateItem,
ViewControllerPluginItem,
ViewsOptions,
} from '@sprucelabs/spruce-templates'
import SpruceError from '../../../errors/SpruceError'
import AbstractWriter from '../../../writers/AbstractWriter'
export default class ViewWriter extends AbstractWriter {
public writeSkillViewController(
cwd: string,
options: {
namePascal: string
nameKebab: string
}
) {
const { path } = this.buildViewControllerPath(
cwd,
'skillView',
options.namePascal
)
return this.writeController(path, { ...options, viewType: 'skillView' })
}
public async writeCombinedViewsFile(cwd: string, options: ViewsOptions) {
let {
vcTemplateItems,
svcTemplateItems,
viewPluginItems,
appTemplateItem,
...rest
} = options
const destinationDir = diskUtil.resolveHashSprucePath(cwd, 'views')
const destination = diskUtil.resolvePath(destinationDir, 'views.ts')
vcTemplateItems = this.removeFileExtensionsFromTemplateItems(
vcTemplateItems,
destinationDir
)
svcTemplateItems = this.removeFileExtensionsFromTemplateItems(
svcTemplateItems,
destinationDir
)
viewPluginItems = this.removeFileExtensionsFromTemplateItems(
viewPluginItems,
destinationDir
)
if (appTemplateItem) {
appTemplateItem = this.makePathRelative(
appTemplateItem,
destinationDir
)
}
const contents = this.templates.views({
vcTemplateItems,
svcTemplateItems,
viewPluginItems,
appTemplateItem,
...rest,
})
const results = await this.writeFileIfChangedMixinResults(
destination,
contents,
'Used to export your controllers to Heartwood.'
)
return results
}
private removeFileExtensionsFromTemplateItems<
T extends VcTemplateItem | ViewControllerPluginItem,
>(vcTemplateItems: T[], destinationDir: string): T[] {
return vcTemplateItems.map((i) =>
this.makePathRelative<T>(i, destinationDir)
)
}
private makePathRelative<
T extends VcTemplateItem | ViewControllerPluginItem,
>(i: T, destinationDir: string): T & { path: string } {
return {
...i,
path: diskUtil
.resolveRelativePath(destinationDir, i.path)
.replace('.ts', ''),
}
}
public writeViewController(
cwd: string,
options: {
viewType: string
namePascal: string
viewModel: string
nameKebab: string
}
) {
const { path } = this.buildViewControllerPath(
cwd,
'view',
options.namePascal
)
return this.writeController(path, options)
}
private async writeController(path: string, options: any) {
const { namePascal, viewModel, viewType, nameKebab } = options
if (diskUtil.doesFileExist(path)) {
throw new SpruceError({
code: 'SKILL_VIEW_EXISTS',
name: namePascal,
})
}
const contents =
viewType === 'skillView'
? this.templates.skillViewController({ namePascal, nameKebab })
: this.templates.viewController({
namePascal,
viewModel,
nameKebab,
})
const results = this.writeFileIfChangedMixinResults(
path,
contents,
'Your new view controller!'
)
return results
}
public async doesRootControllerExist(cwd: string) {
const matches = await globby('**/Root.svc.ts', { cwd })
return matches.length > 0
}
public async writeViewControllerPlugin(options: {
cwd: string
nameCamel: string
namePascal: string
}) {
const { nameCamel, namePascal, cwd } = options
const destination = diskUtil.resolvePath(
cwd,
'src',
'viewPlugins',
`${nameCamel}.view.plugin.ts`
)
const contents = this.templates.viewControllerPlugin({
nameCamel,
namePascal,
})
if (diskUtil.doesFileExist(destination)) {
throw new SpruceError({
code: 'VIEW_PLUGIN_ALREADY_EXISTS',
name: nameCamel,
})
}
return this.writeFileIfChangedMixinResults(
destination,
contents,
`Your new view plugin!`
)
}
public writePlugin(cwd: string) {
const destination = diskUtil.resolveHashSprucePath(
cwd,
'features',
'view.plugin.ts'
)
const pluginContents = this.templates.viewPlugin()
const results = this.writeFileIfChangedMixinResults(
destination,
pluginContents,
'Supports your skill with rendering views.'
)
return results
}
public writeTheme(cwd: string) {
const destination = this.buildThemePath(cwd)
const contents = this.templates.theme()
const results = this.writeFileIfChangedMixinResults(
destination,
contents,
'Your brand new theme file!'
)
return results
}
private buildThemePath(cwd: string) {
return diskUtil.resolvePath(cwd, 'src', 'themes', 'skill.theme.ts')
}
public doesThemeFileExist(cwd: string) {
const destination = this.buildThemePath(cwd)
return diskUtil.doesFileExist(destination)
}
private buildViewControllerPath(
cwd: string,
viewType: 'skillView' | 'view',
namePascal: string
) {
const ext = viewType === 'skillView' ? '.svc.ts' : '.vc.ts'
const filename = namePascal + ext
const path = diskUtil.resolvePath(
cwd,
'src',
viewType + 'Controllers',
filename
)
return { path, filename }
}
public async writeAppController(
cwd: string,
id: string,
namespacePascal: string
) {
const match = await globby(cwd + '/**/App.ac.ts')
if (match.length > 0) {
throw new SpruceError({
code: 'APP_CONTROLLER_ALREADY_EXISTS',
})
}
const destination = diskUtil.resolvePath(cwd, 'src', 'App.ac.ts')
const contents = this.templates.appController({ id, namespacePascal })
return this.writeFileIfChangedMixinResults(
destination,
contents,
'Your new app view controller!'
)
}
}