Skip to content

Commit

Permalink
PATCH: Fix generators (merge pull request #4 from boostercloud/boost-…
Browse files Browse the repository at this point in the history
…280-fix-generators)
  • Loading branch information
alvaroloes authored Feb 4, 2020
2 parents eb841e4 + 77aae2c commit 093898a
Show file tree
Hide file tree
Showing 32 changed files with 286 additions and 160 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
project: './tsconfig.json',
project: 'tsconfig.eslint.json',
},
rules: {
indent: ['error', 2, { SwitchCase: 1 }],
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"lerna": "^3.20.2",
"eslint": "^6.8.0",
"prettier": "^1.19.1",
"eslint-config-prettier": "^6.9.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-prettier": "^3.1.2",
"@typescript-eslint/eslint-plugin": "^2.17.0",
"@typescript-eslint/parser": "^1.12.0",
"@typescript-eslint/eslint-plugin": "^2.18.0",
"@typescript-eslint/parser": "^2.18.0",
"typescript": "^3.7.5",
"chai": "^4.2.0",
"@types/chai": "^4.2.7",
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const runTasks = async (
})
)
.info('Deployment complete!')
.catch('SyntaxError', () => 'Unable to load project configuration. Are you in a booster project?')
.done()

export default class Deploy extends Command {
Expand Down
39 changes: 36 additions & 3 deletions packages/cli/src/commands/new/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ import * as Oclif from '@oclif/command'
import { Script } from '../../common/script'
import Brand from '../../common/brand'
import { generate } from '../../services/generator'
import { HasName, HasFields, joinParsers, parseName, parseFields } from '../../services/generator/target'
import {
HasName,
HasFields,
joinParsers,
parseName,
parseFields,
ImportDeclaration,
} from '../../services/generator/target'
import * as path from 'path'
import { templates } from '../../templates'
import { checkItIsABoosterProject } from "../../services/project-checker";

export default class Command extends Oclif.Command {
public static description = 'create a new command'
Expand Down Expand Up @@ -36,15 +44,40 @@ type CommandInfo = HasName & HasFields

const run = async (name: string, rawFields: Array<string>): Promise<void> =>
Script.init(`boost ${Brand.energize('new:command')} 🚧`, joinParsers(parseName(name), parseFields(rawFields)))
.step('creating new command', generateCommand)
.step('Verifying project', checkItIsABoosterProject)
.step('Creating new command', generateCommand)
.info('Command generated!')
.done()

function generateImports(info: CommandInfo): Array<ImportDeclaration> {
const commandFieldTypes = info.fields.map((f) => f.type)
const commandUsesUUID = commandFieldTypes.some((type) => type == 'UUID')

const componentsFromBoosterTypes = ['Register']
if (commandUsesUUID) {
componentsFromBoosterTypes.push('UUID')
}

return [
{
packagePath: '@boostercloud/framework-core',
commaSeparatedComponents: 'Command',
},
{
packagePath: '@boostercloud/framework-types',
commaSeparatedComponents: componentsFromBoosterTypes.join(', '),
},
]
}

const generateCommand = (info: CommandInfo): Promise<void> =>
generate({
name: info.name,
extension: '.ts',
placementDir: path.join('src', 'commands'),
template: templates.command,
info,
info: {
imports: generateImports(info),
...info,
},
})
42 changes: 36 additions & 6 deletions packages/cli/src/commands/new/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import {
parseName,
parseFields,
parseReaction,
ImportDeclaration,
} from '../../services/generator/target'
import * as path from 'path'
import { generate } from '../../services/generator'
import { templates } from '../../templates'
import { checkItIsABoosterProject } from "../../services/project-checker";

export default class Entity extends Oclif.Command {
public static description = 'create a new entity'
Expand All @@ -23,9 +25,9 @@ export default class Entity extends Oclif.Command {
description: 'fields that this entity will contain',
multiple: true,
}),
reactsTo: Oclif.flags.string({
char: 'r',
description: 'events that this entity will react to',
reduces: Oclif.flags.string({
char: 'p',
description: 'events that this entity will reduce to build its state',
multiple: true,
}),
}
Expand All @@ -39,7 +41,7 @@ export default class Entity extends Oclif.Command {
private async runWithErrors(): Promise<void> {
const { args, flags } = this.parse(Entity)
const fields = flags.fields || []
const events = flags.reactsTo || []
const events = flags.reduces || []
if (!args.entityName)
return Promise.reject("You haven't provided an entity name, but it is required, run with --help for usage")
return run(args.entityName, fields, events)
Expand All @@ -53,15 +55,43 @@ const run = async (name: string, rawFields: Array<string>, rawEvents: Array<stri
`boost ${Brand.energize('new:entity')} 🚧`,
joinParsers(parseName(name), parseFields(rawFields), parseReaction(rawEvents))
)
.step('creating new entity', generateEntity)
.step('Verifying project', checkItIsABoosterProject)
.step('Creating new entity', generateEntity)
.info('Entity generated!')
.done()

function generateImports(info: EntityInfo): Array<ImportDeclaration> {
const eventsImports: Array<ImportDeclaration> = info.events.map((eventData) => ({
packagePath: `../events/${eventData.eventName}`,
commaSeparatedComponents: eventData.eventName,
}))

const coreComponents = ['Entity']
if (info.events.length > 0) {
coreComponents.push('Reduces')
}

return [
{
packagePath: '@boostercloud/framework-core',
commaSeparatedComponents: coreComponents.join(', '),
},
{
packagePath: '@boostercloud/framework-types',
commaSeparatedComponents: 'UUID',
},
...eventsImports,
]
}

const generateEntity = (info: EntityInfo): Promise<void> =>
generate({
name: info.name,
extension: '.ts',
placementDir: path.join('src', 'entities'),
template: templates.entity,
info,
info: {
imports: generateImports(info),
...info,
},
})
29 changes: 27 additions & 2 deletions packages/cli/src/commands/new/event.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import * as Oclif from '@oclif/command'
import { Script } from '../../common/script'
import Brand from '../../common/brand'
import { HasName, HasFields, joinParsers, parseName, parseFields } from '../../services/generator/target'
import {
HasName,
HasFields,
joinParsers,
parseName,
parseFields,
ImportDeclaration,
} from '../../services/generator/target'
import { generate } from '../../services/generator'
import * as path from 'path'
import { templates } from '../../templates'
import { checkItIsABoosterProject } from "../../services/project-checker";

export default class Event extends Oclif.Command {
public static description = 'create a new event'
Expand Down Expand Up @@ -36,15 +44,32 @@ type EventInfo = HasName & HasFields

const run = async (name: string, rawFields: Array<string>): Promise<void> =>
Script.init(`boost ${Brand.energize('new:event')} 🚧`, joinParsers(parseName(name), parseFields(rawFields)))
.step('Verifying project', checkItIsABoosterProject)
.step('creating new event', generateEvent)
.info('Event generated!')
.done()

function generateImports(): Array<ImportDeclaration> {
return [
{
packagePath: '@boostercloud/framework-core',
commaSeparatedComponents: 'Event',
},
{
packagePath: '@boostercloud/framework-types',
commaSeparatedComponents: 'UUID',
},
]
}

const generateEvent = (info: EventInfo): Promise<void> =>
generate({
name: info.name,
extension: '.ts',
placementDir: path.join('src', 'events'),
template: templates.event,
info,
info: {
imports: generateImports(),
...info,
},
})
18 changes: 12 additions & 6 deletions packages/cli/src/commands/new/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,26 @@ export default class Project extends Command {
...flags,
}
parsedFlags.provider = (undefined as unknown) as Provider
await run(parsedFlags as Partial<ProjectInitializerConfig>, flags.provider)
await run(parsedFlags as Partial<ProjectInitializerConfig>, this.config.version, flags.provider)
}
}

const run = async (flags: Partial<ProjectInitializerConfig>, provider?: string): Promise<void> =>
Script.init(`boost ${Brand.energize('new')} 🚧`, parseConfig(new Prompter(), flags, provider))
.step('creating project root', generateRootDirectory)
.step('generating config files', generateConfigFiles)
.step('installing dependencies', installDependencies)
const run = async (
flags: Partial<ProjectInitializerConfig>,
boosterVersion: string,
provider?: string
): Promise<void> =>
Script.init(`boost ${Brand.energize('new')} 🚧`, parseConfig(new Prompter(), flags, boosterVersion, provider))
.step('Creating project root', generateRootDirectory)
.step('Generating config files', generateConfigFiles)
.step('Installing dependencies', installDependencies)
.info('Project generated!')
.done()

const parseConfig = async (
prompter: Prompter,
flags: Partial<ProjectInitializerConfig>,
boosterVersion: string,
providerName?: string
): Promise<ProjectInitializerConfig> => {
const description = await prompter.defaultOrPrompt(flags.description, "What's your project description?")
Expand All @@ -99,5 +104,6 @@ const parseConfig = async (
homepage,
license,
repository,
boosterVersion,
})
}
9 changes: 7 additions & 2 deletions packages/cli/src/commands/new/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HasFields, HasName, joinParsers, parseName, parseFields } from '../../s
import { templates } from '../../templates'
import { generate } from '../../services/generator'
import * as path from 'path'
import { checkItIsABoosterProject } from "../../services/project-checker";

export default class Type extends Oclif.Command {
public static description = 'create a new type'
Expand Down Expand Up @@ -36,7 +37,8 @@ type TypeInfo = HasName & HasFields

const run = async (name: string, rawFields: Array<string>): Promise<void> =>
Script.init(`boost ${Brand.energize('new:type')} 🚧`, joinParsers(parseName(name), parseFields(rawFields)))
.step('creating new type', generateType)
.step('Verifying project', checkItIsABoosterProject)
.step('Creating new type', generateType)
.info('Type generated!')
.done()

Expand All @@ -46,5 +48,8 @@ const generateType = (info: TypeInfo): Promise<void> =>
extension: '.ts',
placementDir: path.join('src', 'common'),
template: templates.type,
info,
info: {
imports: [],
...info,
},
})
8 changes: 6 additions & 2 deletions packages/cli/src/services/config-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ import { BoosterConfig } from '@boostercloud/framework-types'
import * as path from 'path'
import { exec } from 'child-process-promise'
import { wrapExecError } from '../common/errors'
import { checkItIsABoosterProject } from "./project-checker";
import { withinWorkingDirectory } from "./executor-service";

export async function compileProjectAndLoadConfig(): Promise<BoosterConfig> {
const userProjectPath = process.cwd()

await checkItIsABoosterProject()
await compileProject(userProjectPath)
return readProjectConfig(userProjectPath)
}

async function compileProject(projectPath: string): Promise<void> {
try {
await exec(`cd ${projectPath} && npm run compile`)
await withinWorkingDirectory(projectPath, () => {
return exec('npm run compile')
})
} catch (e) {
throw wrapExecError(e, 'Project contains compilation errors')
}
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/src/services/executor-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function withinWorkingDirectory<TReturn>(workingDirectory: string, actions: () => TReturn): TReturn {
const currentWorkingDirectory = process.cwd()
process.chdir(workingDirectory)
const result = actions()
process.chdir(currentWorkingDirectory)
return result
}
7 changes: 2 additions & 5 deletions packages/cli/src/services/generator/target/parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const parseFields = (fields: Array<string>): Promise<HasFields> =>
function parseField(rawField: string): Promise<Field> {
const splitInput = rawField.split(':')
if (splitInput.length != 2) {
return Promise.reject(fieldParsingError)
return Promise.reject(fieldParsingError(rawField))
} else {
return Promise.resolve({
name: splitInput[0],
Expand All @@ -28,10 +28,7 @@ export const parseReaction = (rawEvents: Array<string>): Promise<HasReaction> =>
const parseReactionEvent = (eventName: string): Promise<ReactionEvent> => Promise.resolve({ eventName })

const fieldParsingError = (field: string): Error =>
new Error(`Error parsing field ${field}.
Fields must be in the form of
\t <field name>:<field type>`)
new Error(`Error parsing field ${field}. Fields must be in the form of <field name>:<field type>`)

/**
* Joins parsers together used to generate target information for generators.
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/services/generator/target/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ export interface HasReaction {
export interface ReactionEvent {
eventName: string
}

export interface HasImports {
imports: Array<ImportDeclaration>
}

export interface ImportDeclaration {
packagePath: string
commaSeparatedComponents: string
}
26 changes: 26 additions & 0 deletions packages/cli/src/services/project-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as fs from 'fs-extra'
import * as path from 'path'

function checkIndexFileIsBooster(indexFilePath: string): void {
const contents = fs.readFileSync(indexFilePath)
if (!contents.includes('Booster.start()')) {
throw new Error(
'The main application file does not start a Booster application. Verify you are in the right project'
)
}
}

export async function checkItIsABoosterProject(): Promise<void> {
const currentPath = process.cwd()
try {
const tsConfigJsonContents = require(path.join(currentPath, 'tsconfig.json'))
const indexFilePath = path.normalize(
path.join(currentPath, tsConfigJsonContents.compilerOptions.rootDir, 'index.ts')
)
checkIndexFileIsBooster(indexFilePath)
} catch (e) {
throw new Error(
`There was an error when recognizing the application. Make sure you are in the root path of a Booster project:\n${e.message}`
)
}
}
Loading

0 comments on commit 093898a

Please sign in to comment.