Skip to content

Commit

Permalink
Conditional Prompts (#117)
Browse files Browse the repository at this point in the history
* added comment explaining why node_modules is included in vsix despite using webpack

* conditional prompts

* dynamic prompts list

* update code

* code fix

* use log and output channel

* update testscd

* notify about error on next

* add more logs

* add tests

* merge fixes

* merge with master

* update coverage

* update coverage

* Update package.json

* add description

* description

* fixes

* update coverage and add test

* remove unused code

* remove unused data parameter

* improve logic

* fix bug

* fix dependencies

* fix logger

* update coverage

* add type string to the CWD parameter

* fix splice method

* update and add tests

* improve generator logic

* update generator

* update foodq generator

* update subgenerator

* merge latest changes

* update coverage

* add types

* expose IPrompt interface

* Update index.js

* Update index.js

* CR fixes

* CR-Fixes

* CR-Fixes

* update types

* compile types

* add types script

* update foodq generator and circleci config

* update circleci config

* Update config.yml

* update circleci config

* update dependencies

* update package.json

* update config.yml

* update foodq

* add dependency to types version

* remove types package from circleci

* Update package.json

Co-authored-by: Eyal Barlev <[email protected]>
Co-authored-by: Tomer Epstein <[email protected]>
Co-authored-by: Tomer Epstein <[email protected]>
  • Loading branch information
4 people authored Mar 11, 2020
1 parent 33ba3bd commit 1ec4b93
Show file tree
Hide file tree
Showing 16 changed files with 320 additions and 241 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ workflows:
only: /^v.*/
branches:
ignore: /.*/

8 changes: 4 additions & 4 deletions backend/.nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"temp-dir": "./reports/.nyc_output",
"report-dir": "./reports/coverage",
"check-coverage": true,
"branches": 42.9,
"lines": 52.8,
"functions": 39.9,
"statements": 52.7
"branches": 51.5,
"lines": 56.2,
"functions": 43.3,
"statements": 56.1
}
32 changes: 17 additions & 15 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "Apache 2.0",
"description": "Provide rich user experience for Yeoman generators using VSCode extension or the browser",
"repository": "https://github.com/SAP/yeoman-ui",
"version": "0.0.51",
"version": "0.0.53",
"engines": {
"vscode": "^1.39.2"
},
Expand Down Expand Up @@ -76,7 +76,8 @@
}
},
"scripts": {
"backend": "npm install && npm run compile",
"backend": "npm i && npm run compile",
"types": "cd ../types && npm i && npm run compile",
"frontend": "npm run frontend:install && npm run frontend:build && npm run frontend:copy",
"frontend:install": "cd ../frontend && npm i",
"frontend:build": "cd ../frontend && npm run build",
Expand All @@ -94,24 +95,25 @@
},
"dependencies": {
"@sap-devx/webview-rpc": "^0.2.0",
"@vscode-logging/logger": "^0.1.2",
"chalk": "^3.0.0",
"datauri": "^2.0.0",
"fs-extra": "^8.1.0",
"humanize-string": "^1.0.2",
"lodash": "^4.17.15",
"strip-ansi": "^6.0.0",
"ws": "^7.2.0",
"yeoman-environment": "^2.7.0",
"titleize": "^1.0.1",
"humanize-string": "^1.0.2",
"fs-extra": "^8.1.0",
"chalk": "^3.0.0",
"@vscode-logging/logger": "^0.1.2"
"ws": "^7.2.0",
"yeoman-environment": "^2.8.0",
"@sap-devx/yeoman-ui-types": "0.0.1"
},
"devDependencies": {
"@types/chai": "^4.2.5",
"@types/fs-extra": "^8.0.1",
"@types/chai": "^4.2.9",
"@types/fs-extra": "^8.1.0",
"@types/inquirer": "^6.5.0",
"@types/lodash": "^4.14.145",
"@types/mocha": "^5.2.7",
"@types/node": "^10.12.21",
"@types/node": "^10.17.16",
"@types/sinon": "^7.5.0",
"@types/strip-ansi": "^5.2.1",
"@types/ws": "^6.0.3",
Expand All @@ -123,15 +125,15 @@
"nyc": "^14.1.1",
"sinon": "^7.5.0",
"ts-loader": "^6.2.1",
"ts-node": "^8.5.4",
"ts-node": "^8.6.2",
"tslint": "^5.20.1",
"tslint-config-prettier": "^1.18.0",
"tslint-no-unused-expression-chai": "^0.1.4",
"typescript": "^3.8.2",
"utf-8-validate": "^5.0.2",
"vsce": "^1.68.0",
"vsce": "^1.73.0",
"vscode": "^1.1.28",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11"
}
}
4 changes: 2 additions & 2 deletions backend/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ export class YeomanUIPanel {
}
const uiMessages = _.assign({}, backendMessages, _.get(YeomanUIPanel, "messages", {}));
this.panel.title = _.get(uiMessages, "panel_title");

this.setMessages(uiMessages);

this.panel.webview.html = indexHtml;
}
}
Expand Down
93 changes: 42 additions & 51 deletions backend/src/yeomanui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import * as fsextra from "fs-extra";
import * as _ from "lodash";
import * as Environment from "yeoman-environment";
import * as inquirer from "inquirer";
const datauri = require("datauri");
const titleize = require('titleize');
const humanizeString = require('humanize-string');
const datauri = require("datauri");
import * as defaultImage from "./defaultImage";
import { YouiAdapter } from "./youi-adapter";
import { YouiLog } from "./youi-log";
Expand All @@ -15,6 +15,7 @@ import { IRpc } from "@sap-devx/webview-rpc/out.ext/rpc-common";
import Generator = require("yeoman-generator");
import { GeneratorType, GeneratorFilter } from "./filter";
import { IChildLogger } from "@vscode-logging/logger";
import {IPrompt} from "@sap-devx/yeoman-ui-types";

export interface IGeneratorChoice {
name: string;
Expand All @@ -31,9 +32,7 @@ export interface IGeneratorQuestion {
choices: IGeneratorChoice[];
}

export interface IPrompt {
name: string;
description: string;
export interface IQuestionsPrompt extends IPrompt{
questions: any[];
}

Expand All @@ -42,7 +41,7 @@ export class YeomanUI {
"Some quick example text of the generator description. This is a long text so that the example will look good.";
private static YEOMAN_PNG = "yeoman.png";
private static isWin32 = (process.platform === 'win32');
private static CWD = path.join(os.homedir(), 'projects');
private static CWD: string = path.join(os.homedir(), 'projects');
private static NODE_MODULES = 'node_modules';

private static funcReplacer(key: any, value: any) {
Expand Down Expand Up @@ -115,11 +114,11 @@ export class YeomanUI {
return errorMessage;
}

public async getGenerators(): Promise<IPrompt> {
public async getGenerators(): Promise<IQuestionsPrompt> {
// optimization: looking up generators takes a long time, so if generators are already loaded don't bother
// on the other hand, we never look for newly installed generators...

const promise: Promise<IPrompt> = new Promise(resolve => {
const promise: Promise<IQuestionsPrompt> = new Promise(resolve => {
const env: Environment.Options = this.getEnv();
env.lookup(async () => this.onEnvLookup(env, resolve, this.genFilter));
});
Expand All @@ -137,27 +136,13 @@ export class YeomanUI {
const meta: Environment.GeneratorMeta = this.getGenMetadata(generatorName);
// TODO: support sub-generators
env.register(meta.resolved);
const gen: any = env.create(meta.namespace, {});
// check if generator defined a helper function called getPrompts()
const genGetPrompts = _.get(gen, "getPrompts");
if (genGetPrompts) {
const promptNames: any[] = genGetPrompts();
const prompts: IPrompt[] = promptNames.map(value => {
return _.assign({ questions: [], name: "", description: "" }, value);
});
this.setPrompts(prompts);
}

const genGetImage = _.get(gen, "getImage");
if (genGetImage) {
const image: any = genGetImage();
if (image.then) {
image.then((contents: string) => {
this.logger.debug(`image contents: ${contents}`);
});
} else if (image !== undefined) {
this.logger.debug(`image contents: ${image}`);
}
const genNamespace = this.getGenNamespace(generatorName);
const gen: any = env.create(genNamespace, {});
// check if generator defined a helper function called setPromptsCallback()
const setPromptsCallback = _.get(gen, "setPromptsCallback");
if (setPromptsCallback) {
setPromptsCallback(this.setPromptList.bind(this));
}

this.setGenInstall(gen);
Expand Down Expand Up @@ -216,7 +201,7 @@ export class YeomanUI {
public async receiveIsWebviewReady() {
try {
// TODO: loading generators takes a long time; consider prefetching list of generators
const generators: IPrompt = await this.getGenerators();
const generators: IQuestionsPrompt = await this.getGenerators();
const response: any = await this.rpc.invoke("showPrompt", [generators.questions, "select_generator"]);
await this.runGenerator(response.name);
} catch (error) {
Expand All @@ -235,18 +220,20 @@ export class YeomanUI {
public async showPrompt(questions: Environment.Adapter.Questions<any>): Promise<inquirer.Answers> {
this.currentQuestions = questions;

this.promptCount++;
const firstQuestionName = _.get(questions, "[0].name");
let promptName: string = `Step ${this.promptCount}`;
if (firstQuestionName) {
promptName = _.startCase(firstQuestionName);
}
const mappedQuestions: Environment.Adapter.Questions<any> = this.normalizeFunctions(questions);
if (_.isEmpty(mappedQuestions)) {
return {};
}

return this.rpc.invoke("showPrompt", [mappedQuestions, promptName]);
this.promptCount++;

const promptName: string = this.getPromptName(questions);
const mappedQuestions: Environment.Adapter.Questions<any> = this.normalizeFunctions(questions);
if (_.isEmpty(mappedQuestions)) {
return {};
}

return this.rpc.invoke("showPrompt", [mappedQuestions, promptName]);
}

private getPromptName(questions: Environment.Adapter.Questions<any>): string {
const firstQuestionName = _.get(questions, "[0].name");
return (firstQuestionName ? _.startCase(firstQuestionName) : `Step ${this.promptCount}`);
}

private onGeneratorSuccess(generatorName: string, destinationRoot: string) {
Expand Down Expand Up @@ -337,12 +324,12 @@ export class YeomanUI {
this.logError(error);
return Promise.resolve(undefined);
}

const genFilter: GeneratorFilter = GeneratorFilter.create(_.get(packageJson, ["generator-filter"]));
const typeEqual: boolean = (filter.type === GeneratorType.all || filter.type === genFilter.type);
const categoriesHasIntersection: boolean = (_.isEmpty(filter.categories) || !_.isEmpty(_.intersection(filter.categories, genFilter.categories)));
if (typeEqual && categoriesHasIntersection) {
return this.createGeneratorChoice(genName, genPackagePath, packageJson);
return this.createGeneratorChoice(genName, genPackagePath, packageJson);
}

return Promise.resolve(undefined);
Expand Down Expand Up @@ -382,10 +369,10 @@ export class YeomanUI {
}

private getGenMetadata(genName: string): Environment.GeneratorMeta {
const namespace = this.getGenNamespace(genName);
const genMetadata = _.get(this, ["genMeta", namespace]);
const genNamespace = this.getGenNamespace(genName);
const genMetadata = _.get(this, ["genMeta", genNamespace]);
if (_.isNil(genMetadata)) {
const debugMessage = `${namespace} generator metadata was not found.`;
const debugMessage = `${genNamespace} generator metadata was not found.`;
this.logger.debug(debugMessage);
}
return genMetadata;
Expand All @@ -408,19 +395,23 @@ export class YeomanUI {
return JSON.parse(JSON.stringify(questions, YeomanUI.funcReplacer));
}

private setPromptList(prompts: IPrompt[]): Promise<void> {
const promptsToDisplay: IPrompt[] = prompts.map((prompt: IPrompt) => {
return _.assign({ questions: [], name: "", description: ""}, prompt);
});

return this.rpc.invoke("setPromptList", [promptsToDisplay]);
}

private addCustomQuestionEventHandlers(questions: Environment.Adapter.Questions<any>): void {
for (const index in questions) {
for (let index in questions) {
const question = (questions as any[])[Number.parseInt(index)];
const questionHandlers = this.customQuestionEventHandlers.get((question as any)["guiType"]);
const questionHandlers = this.customQuestionEventHandlers.get(question.guiType);
if (questionHandlers) {
questionHandlers.forEach((handler, methodName) => {
(question as any)[methodName] = handler;
});
}
}
}

private setPrompts(prompts: IPrompt[]): Promise<void> {
return this.rpc.invoke("setPrompts", [prompts]);
}
}
6 changes: 3 additions & 3 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ module.exports = {
],
coverageThreshold: {
"global": {
"branches": 94.2,
"branches": 95.8,
"functions": 100,
"lines": 96.9,
"statements": 96.9
"lines": 98.1,
"statements": 98.1
}
}
}
Loading

0 comments on commit 1ec4b93

Please sign in to comment.