supergraphql/graphql-cli-prepare

View on GitHub
src/prepare.ts

Summary

Maintainability
D
2 days
Test Coverage
Missing semicolon
import chalk from 'chalk'
Missing semicolon
import * as fs from 'fs-extra'
Missing semicolon
import { GraphQLConfig, GraphQLProjectConfig } from 'graphql-config'
Missing semicolon
import { importSchema } from 'graphql-import'
Missing semicolon
import { generateCode } from 'graphql-static-binding'
Missing semicolon
import { get, has, merge } from 'lodash'
Missing semicolon
import * as path from 'path'
Missing semicolon
import { Arguments } from 'yargs'
 
export class Prepare {
Missing semicolon
private config: GraphQLConfig
Missing semicolon
private bundleExtensionConfig: { 'prepare-bundle': string } | undefined
Missing semicolon
private projectName: string
Missing semicolon
private project: GraphQLProjectConfig
 
constructor(private context: any, private argv: Arguments) {}
 
public async handle() {
Missing semicolon
this.config = await this.context.getConfig()
 
// Get projects
Missing semicolon
const projects: { [name: string]: GraphQLProjectConfig } = this.getProjectConfig()
 
// Process each project
for (const projectName of Object.keys(projects)) {
Missing semicolon
const project: GraphQLProjectConfig = projects[projectName]
 
Missing semicolon
this.setCurrentProject(project, projectName)
if (this.argv.bundle) {
Missing semicolon
this.bundle()
}
if (this.argv.bindings) {
Missing semicolon
this.bindings()
}
Missing semicolon
this.save()
}
}
 
private setCurrentProject(project: GraphQLProjectConfig, projectName: string): void {
Missing semicolon
this.project = project
Missing semicolon
this.projectName = projectName
Missing semicolon
this.bundleExtensionConfig = undefined
}
 
Function `bindings` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.
private bindings() {
Missing semicolon
let bindingExtensionConfig: { 'prepare-binding': { output: string; generator: string } } | undefined
 
Similar blocks of code found in 2 locations. Consider refactoring.
if (
this.argv.project ||
(!this.argv.project &&
(has(this.project.config, 'extensions.prepare-binding') ||
has(this.project.config, 'extensions.binding')))
) {
Missing semicolon
this.context.spinner.start(`Generating bindings for project ${this.projectDisplayName()}...`)
bindingExtensionConfig = this.processBindings(
this.bundleExtensionConfig ? this.bundleExtensionConfig['prepare-bundle'] : undefined
Missing semicolon
)
Missing semicolon
merge(this.project.extensions, bindingExtensionConfig)
this.context.spinner.succeed(
`Bindings for project ${this.projectDisplayName()} written to ${chalk.green(
bindingExtensionConfig['prepare-binding'].output
)}`
Missing semicolon
)
} else if (this.argv.verbose) {
this.context.spinner.info(
`Binding not configured for project ${this.projectDisplayName()}. Skipping`
Missing semicolon
)
}
}
 
private bundle() {
Similar blocks of code found in 2 locations. Consider refactoring.
if (
this.argv.project ||
(!this.argv.project &&
(has(this.project.config, 'extensions.prepare-bundle') ||
has(this.project.config, 'extensions.bundle')))
) {
Missing semicolon
this.context.spinner.start(`Processing schema imports for project ${this.projectDisplayName()}...`)
Missing semicolon
this.bundleExtensionConfig = this.processBundle()
Missing semicolon
merge(this.project.extensions, this.bundleExtensionConfig)
this.context.spinner.succeed(
`Bundled schema for project ${this.projectDisplayName()} written to ${chalk.green(
this.bundleExtensionConfig['prepare-bundle']
)}`
Missing semicolon
)
} else if (this.argv.verbose) {
this.context.spinner.info(
`Bundling not configured for project ${this.projectDisplayName()}. Skipping`
Missing semicolon
)
}
}
 
private save() {
if (this.argv.save) {
Missing semicolon
const configFile = path.basename(this.config.configPath)
Similar blocks of code found in 2 locations. Consider refactoring.
this.context.spinner.start(
`Saving configuration for project ${this.projectDisplayName()} to ${chalk.green(configFile)}...`
Missing semicolon
)
Missing semicolon
this.saveConfig()
Similar blocks of code found in 2 locations. Consider refactoring.
this.context.spinner.succeed(
`Configuration for project ${this.projectDisplayName()} saved to ${chalk.green(configFile)}`
Missing semicolon
)
}
}
 
Function `getProjectConfig` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
private getProjectConfig(): { [name: string]: GraphQLProjectConfig } {
Missing semicolon
let projects: { [name: string]: GraphQLProjectConfig } | undefined
if (this.argv.project) {
if (Array.isArray(this.argv.project)) {
Missing semicolon
projects = {}
Missing semicolon
this.argv.project.map((p: string) => merge(projects, { [p]: this.config.getProjectConfig(p) }))
} else {
// Single project mode
Missing semicolon
projects = { [this.argv.project]: this.config.getProjectConfig(this.argv.project) }
}
} else {
// Process all projects
Missing semicolon
projects = this.config.getProjects()
}
 
if (!projects) {
Missing semicolon
throw new Error('No projects defined in config file')
}
 
Missing semicolon
return projects
}
 
private processBundle(): { 'prepare-bundle': string } {
Missing semicolon
const outputPath: string = this.determineBundleOutputPath()
Missing semicolon
const schemaPath: string = this.determineSchemaPath()
 
Missing semicolon
const finalSchema = importSchema(schemaPath)
 
Missing semicolon
fs.writeFileSync(outputPath, finalSchema, { flag: 'w' })
 
Missing semicolon
return { 'prepare-bundle': outputPath }
}
 
private processBindings(
schemaPath: string | undefined
): { 'prepare-binding': { output: string; generator: string } } {
Missing semicolon
const generator: string = this.determineGenerator()
// TODO: This does not support custom generators
Missing semicolon
const extension = generator.endsWith('ts') ? 'ts' : 'js'
Missing semicolon
const outputPath: string = this.determineBindingOutputPath(extension)
Missing semicolon
const schema: string = this.determineInputSchema(schemaPath)
 
Missing semicolon
const schemaContents: string = importSchema(schema)
Missing semicolon
const finalSchema: string = generateCode(schemaContents, generator)
Missing semicolon
fs.writeFileSync(outputPath, finalSchema, { flag: 'w' })
 
Expected property shorthand in object literal ('{generator}').
Missing semicolon
return { 'prepare-binding': { output: outputPath, generator: generator } }
}
 
private saveConfig() {
if (has(this.project.config, 'extensions.bundle')) {
Missing semicolon
delete this.project.config.extensions!.bundle
}
if (has(this.project.config, 'extensions.binding')) {
Missing semicolon
delete this.project.config.extensions!.binding
}
Missing semicolon
this.config.saveConfig(this.project.config, this.projectName)
}
 
/**
* Determine input schema path for binding. It uses the resulting schema from bundling (if available),
* then looks at bundle extension (in case bundling ran before), then takes the project schemaPath.
* Also checks if the file exists, otherwise it throws and error.
*
* @param {(string | undefined)} schemaPath Schema path from bundling
* @returns {string} Input schema path to be used for binding generatio.
*/
Function `determineInputSchema` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.
private determineInputSchema(schemaPath: string | undefined): string {
Missing semicolon
const bundleDefined = has(this.project.config, 'extensions.prepare-bundle.output')
Missing semicolon
const oldBundleDefined = has(this.project.config, 'extensions.bundle.output')
// schemaPath is only set when bundle ran
if (!schemaPath) {
if (bundleDefined) {
// Otherwise, use bundle output schema if defined
Missing semicolon
schemaPath = get(this.project.config, 'extensions.prepare-bundle.output')
} else if (oldBundleDefined) {
Missing semicolon
schemaPath = get(this.project.config, 'extensions.bundle.output')
} else if (this.project.schemaPath) {
// Otherwise, use project schemaPath
Missing semicolon
schemaPath = this.project.schemaPath
} else {
Missing semicolon
throw new Error(`Input schema cannot be determined.`)
}
}
 
if (fs.existsSync(schemaPath!)) {
Missing semicolon
return schemaPath!
} else {
throw new Error(
`Schema '${schemaPath!}' not found.${bundleDefined ? ' Did you run bundle first?' : ''}`
Missing semicolon
)
}
}
 
/**
* Determine input schema path for bundling.
*
* @returns {string} Input schema path for bundling
*/
private determineSchemaPath(): string {
if (this.project.schemaPath) {
Missing semicolon
return this.project.schemaPath
}
Missing semicolon
throw new Error(`No schemaPath defined for project '${this.projectName}' in config file.`)
}
 
/**
* Determine generator. Provided generator takes precedence over value from config
*
* @param {string} generator Command line parameter for generator
* @returns {string} Generator to be used
*/
private determineGenerator(): string {
if (this.argv.generator) {
Missing semicolon
return this.argv.generator
}
if (has(this.project.config, 'extensions.binding.generator')) {
if (!this.argv.save) {
this.context.spinner.warn(
`Deprecated extension key 'binding.generator' found in config file. Use '--save' to update to 'prepare-binding.generator'.`
Missing semicolon
)
}
Missing semicolon
return get(this.project.config, 'extensions.binding.generator')
}
if (has(this.project.config, 'extensions.prepare-binding.generator')) {
Missing semicolon
return get(this.project.config, 'extensions.prepare-binding.generator')
}
throw new Error(
'Generator cannot be determined. No existing configuration found and no generator parameter specified.'
Missing semicolon
)
}
 
/**
* Determine output path for binding. Provided path takes precedence over value from config
*
* @param {string} extension File extension for output file
* @returns Output path
*/
Function `determineBindingOutputPath` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
private determineBindingOutputPath(extension: string) {
Missing semicolon
let outputPath: string
Similar blocks of code found in 2 locations. Consider refactoring.
if (this.argv.output) {
Missing semicolon
outputPath = path.join(this.argv.output, `${this.projectName}.${extension}`)
} else if (has(this.project.config, `extensions.binding.output`)) {
if (!this.argv.save) {
this.context.spinner.warn(
`Deprecated extension key 'binding.output' found in config file. Use '--save' to update to 'prepare-binding.output'.`
Missing semicolon
)
}
Missing semicolon
outputPath = get(this.project.config, `extensions.binding.output`)
} else if (has(this.project.config, `extensions.prepare-binding.output`)) {
Missing semicolon
outputPath = get(this.project.config, `extensions.prepare-binding.output`)
} else {
throw new Error(
'Output path cannot be determined. No existing configuration found and no output parameter specified.'
Missing semicolon
)
}
 
Missing semicolon
fs.ensureDirSync(path.dirname(outputPath))
Missing semicolon
return outputPath
}
 
/**
* Determine output path for bundle. Provided path takes precedence over value from config
*
* @returns Output path
*/
Function `determineBundleOutputPath` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
private determineBundleOutputPath() {
Missing semicolon
let outputPath: string
Similar blocks of code found in 2 locations. Consider refactoring.
if (this.argv.output) {
Missing semicolon
outputPath = path.join(this.argv.output, `${this.projectName}.graphql`)
} else if (has(this.project.config, `extensions.bundle`)) {
if (!this.argv.save) {
this.context.spinner.warn(
`Deprecated extension key 'bundle' found in config file. Use '--save' to update to 'prepare-bundle'.`
Missing semicolon
)
}
Missing semicolon
outputPath = get(this.project.config, `extensions.bundle`)
} else if (has(this.project.config, `extensions.prepare-bundle`)) {
Missing semicolon
outputPath = get(this.project.config, `extensions.prepare-bundle`)
} else {
throw new Error(
'Output path cannot be determined. No existing configuration found and no output parameter specified.'
Missing semicolon
)
}
 
Missing semicolon
fs.ensureDirSync(path.dirname(outputPath))
Missing semicolon
return outputPath
}
 
Missing semicolon
private projectDisplayName = () => chalk.green(this.projectName)
}