Skip to content

Commit

Permalink
initial scaffolding for generate command and validation in place
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Markon committed Apr 22, 2020
1 parent af338d9 commit 6b4868b
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 7 deletions.
26 changes: 26 additions & 0 deletions packages/cli/src/command.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
import {Command as OclifCommand} from '@oclif/command'
import Enquirer = require('enquirer')
import chalk = require('chalk')

export type LogOptions = {
pad?: boolean
indent?: boolean
}

abstract class Command extends OclifCommand {
protected enquirer = new Enquirer()
protected themeColor = '6700AB'
protected indent = false

logTheme(str: string, options?: LogOptions) {
this.log(chalk.bgWhite.hex(this.themeColor).bold(str), options)
}

log(formattedStr: string, options: LogOptions = {}) {
const verticalPad = options.pad ? '\n' : ''
const indent = this.indent || options.indent ? '\t' : ''
super.log(`${verticalPad}${indent}${formattedStr}${verticalPad}`)
}

setLogIndent() {
this.indent = true
}

stopLogIndent() {
this.indent = false
}
}

export default Command
150 changes: 150 additions & 0 deletions packages/cli/src/commands/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import Command from '../command'
import {flags} from '@oclif/command'
import * as fs from 'fs'
import * as path from 'path'
import chalk from 'chalk'
import enquirer from 'enquirer'
const debug = require('debug')('blitz:generate')

enum ResourceType {
Resource = 'resource',
Model = 'model',
Page = 'page',
Mutation = 'mutation',
Query = 'query',
}

interface Flags {
path?: string
'dry-run'?: boolean
}

interface Args {
type: ResourceType
name: string
}

export default class Generate extends Command {
static description = 'Generate new files for your Blitz project'

static args = [
{
name: 'type',
required: true,
description: 'Type of files to generate',
options: [
ResourceType.Model,
ResourceType.Mutation,
ResourceType.Page,
ResourceType.Query,
ResourceType.Resource,
],
},
{
name: 'name',
required: true,
description: 'Name of resource to generate',
},
]

static flags = {
help: flags.help({char: 'h'}),
path: flags.string({
char: 'p',
description: 'Path to app directory where files should be generated',
}),
'dry-run': flags.boolean({
char: 'd',
description: 'Show what files will be created without writing them to disk',
}),
}

static examples = [
`# We can just generate a single file type, for example a query:
blitz generate query task
`,
`# Or, we can generate a full set of models, mutations, pages, etc.
blitz generate resource task
`,
`# If we have multiple entries in /app, we must specify where the files belong
blitz generate resource task -p=taskManager`,
]

async promptForTargetDirectory(paths: string[]): Promise<string> {
return enquirer
.prompt<{directory: string}>({
name: 'directory',
type: 'select',
message: 'Please select a target directory:',
choices: paths,
})
.then((resp) => resp.directory)
}

async genericConfirmPrompt(message: string): Promise<boolean> {
return enquirer
.prompt<{continue: string}>({
name: 'continue',
type: 'select',
message: message,
choices: ['Yes', 'No'],
})
.then((resp) => resp.continue === 'Yes')
}

async run() {
const {args, flags}: {args: Args; flags: Flags} = this.parse(Generate)
debug('args: ', args)
debug('flags: ', flags)

console.log(args, flags)

const isInRoot = fs.existsSync(path.resolve('blitz.config.js'))

if (!isInRoot) {
this.error(chalk.red('No blitz.config.js found. `generate` must be run from the root of the project.'))
}

// we can only assume the output directory if /app has a single child directory,
// otherwise we should prompt the user
let appPaths = fs.readdirSync(path.resolve('app'))
let outputTarget: string
if (appPaths.length === 1) {
// if there's only one path, we can ignore the path flag safely
const appPath = appPaths[0]
if (flags.path && flags.path !== appPath) {
// if the path provided isn't the only path, warn the user
this.log(
chalk.yellow(
`Warning: supplied path '${flags.path}' doesn't exist in this project. The only found path was '${appPath}.'`,
),
{pad: true},
)
const shouldContinue = await this.genericConfirmPrompt(
`Would you like to place files in '/app/${appPath}' instead?`,
)
if (!shouldContinue) {
this.exit(0)
}
}
outputTarget = appPaths[0]
} else {
// otherwise, validate the provided path, prompting the user if it's absent or invalid
if (!flags.path) {
this.log(
chalk.yellow(
`No path flag (--path, -p) was detected, but multiple roots under /app were present (${appPaths.join()})`,
),
)
outputTarget = await this.promptForTargetDirectory(appPaths)
} else if (!appPaths.includes(flags.path)) {
this.log(chalk.yellow(`Given path '${flags.path}' doesn't exist in /app. Please select a valid path`))
outputTarget = await this.promptForTargetDirectory(appPaths)
} else {
outputTarget = flags.path
}
}
this.logTheme(`Outputting files to ${outputTarget}`, {pad: true})
this.logTheme('Generator completed successfully 🎉✅')
}
}
14 changes: 7 additions & 7 deletions packages/cli/src/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ export default class New extends Command {
yarn: flags.yarn,
})

const themeColor = '6700AB'

try {
this.log('\n' + chalk.hex(themeColor).bold('Hang tight while we set up your new Blitz app!') + '\n')
this.logTheme('Hang tight while we set up your new Blitz app!', {pad: true})
await generator.run()
this.log('\n' + chalk.hex(themeColor).bold('Your new Blitz app is ready! Next steps:') + '\n')
this.log(chalk.yellow(` 1. cd ${args.name}`))
this.log(chalk.yellow(` 2. blitz start`))
this.log(chalk.yellow(` 3. You create new pages by placing components inside app/pages/\n`))
this.logTheme('Your new Blitz app is ready! Next steps:', {pad: true})
this.setLogIndent()
this.log(chalk.yellow(`1. cd ${args.name}`))
this.log(chalk.yellow(`2. blitz start`))
this.log(chalk.yellow(`3. You create new pages by placing components inside app/pages/\n`))
this.stopLogIndent()
} catch (err) {
if (err instanceof PromptAbortedError) this.exit(0)

Expand Down

0 comments on commit 6b4868b

Please sign in to comment.