@google/dscc-gen
Version:
Create component & connector projects with sane defaults.
184 lines (161 loc) • 5.01 kB
text/typescript
/**
* @license
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as path from 'path';
import {PWD} from './index';
import * as util from './util';
import {Question} from 'inquirer';
import * as files from './files';
import * as argparse from 'argparse';
import * as vizQuestions from './viz/questions';
import * as connectorQuestions from './connector/questions';
import {VizAnswers} from './viz/questions';
import {ConnectorAnswers} from './connector/questions';
import {prompt} from './prompt';
export interface State {
projectPath: string;
templatePath: string;
}
export type Answers = ConnectorAnswers & VizAnswers & CommonAnswers & Args;
export interface Args {
yarn: boolean;
npm: boolean;
scriptId?: string;
}
export interface CommonAnswers {
projectChoice: ProjectChoice;
projectName: string;
basePath: string;
}
export enum ProjectChoice {
VIZ = 'community-viz',
CONNECTOR = 'community-connector',
}
const templateOptions: ProjectChoice[] = [
ProjectChoice.VIZ,
ProjectChoice.CONNECTOR,
];
interface QuestionName {
cmdName: string;
inquirerName: string;
}
const PROJECT_CHOICE: QuestionName = {
cmdName: '--project_choice',
inquirerName: 'projectChoice',
};
const PROJECT_NAME: QuestionName = {
cmdName: '--project_name',
inquirerName: 'projectName',
};
const projectNameRegEx = /^([-_A-Za-z\d])+$/;
const projectNameValidator = async (input: string) => {
if (!projectNameRegEx.test(input)) {
return 'Name may only include letters, numbers, dashes, and underscores.';
}
const projectPath = path.join(PWD, input);
if (await util.fileExists(projectPath)) {
return `The directory ${input} already exists.`;
}
return true;
};
export const COMMON_QUESTIONS: Array<Question<CommonAnswers>> = [
{
name: PROJECT_CHOICE.inquirerName,
type: 'list',
message: 'What project template would you like to use?',
choices: templateOptions,
},
{
name: PROJECT_NAME.inquirerName,
type: 'input',
message: 'Project name',
validate: projectNameValidator,
},
];
const getArgs = async (baseDir: string): Promise<Args> => {
const parser = new argparse.ArgumentParser({
version: (await files.getPackageJson(baseDir)).version,
addHelp: true,
description:
'Template-based project generator for Data Studio developer features',
});
parser.addArgument(['--yarn'], {
dest: 'yarn',
action: 'storeTrue',
help: 'Use yarn as the build tool.',
});
parser.addArgument(['--npm'], {
dest: 'npm',
action: 'storeTrue',
help: 'Use npm as the build tool.',
});
parser.addArgument([PROJECT_CHOICE.cmdName], {
dest: PROJECT_CHOICE.inquirerName,
choices: templateOptions,
help: 'Which template to use.',
});
parser.addArgument([PROJECT_NAME.cmdName], {
dest: PROJECT_NAME.inquirerName,
help: 'The name of your project',
});
parser.addArgument(['--script_id'], {
dest: 'scriptId',
help:
'Community Connector: Use this scriptId instead of creating a new script.',
});
const args = parser.parseArgs();
Object.keys(args).forEach((key) => {
if (args[key] === null) {
delete args[key];
}
});
return args;
};
const questionsWithArgs = async <T>(
args: Args,
questions: Array<Question<T>>
): Promise<Array<Question<T>>> => {
await Promise.all(
questions.map(async (question) => {
const argValue = (args as any)[question.name!];
if (argValue && question.validate) {
const isValid = await question.validate(argValue);
if (isValid !== true) {
throw new Error(
`Invalid response for ${question.name}: "${argValue}". ${isValid}`
);
}
}
})
);
return questions.filter((question) => {
return (args as any)[question.name!] === undefined;
});
};
export const getAnswers = async (baseDir: string): Promise<Answers> => {
const args: Args = await getArgs(baseDir);
const questions = await questionsWithArgs(args, COMMON_QUESTIONS);
const promptAnswers: CommonAnswers = await prompt(questions);
const commonAnswers = Object.assign({}, promptAnswers, args);
switch (commonAnswers.projectChoice) {
case ProjectChoice.VIZ:
return vizQuestions.getAnswers(args, commonAnswers);
case ProjectChoice.CONNECTOR:
return connectorQuestions.getAnswers(args, commonAnswers);
default:
throw new Error(`${commonAnswers.projectChoice} is not supported.`);
}
};