Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(add): add handling of merge option #797

Merged
merged 38 commits into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ce51a0a
fix(add): add handling of merge option
rishabh3112 Mar 20, 2019
d901d49
chore(refactor): add generator
rishabh3112 Mar 20, 2019
eb43443
fix(add): add handling of merge option
rishabh3112 Mar 20, 2019
66bde9f
chore(refactor): add generator
rishabh3112 Mar 20, 2019
ff67423
Merge branch 'fix/add' of https://github.com/rishabh3112/webpack-cli …
rishabh3112 Mar 20, 2019
e5c7f67
chore(refactor): update package list
rishabh3112 Mar 21, 2019
2299848
chore(refactor): move schema to utils
rishabh3112 Mar 22, 2019
ccf0dce
fix(add): apply suggestions
rishabh3112 Mar 26, 2019
e839614
chore: merge master
rishabh3112 May 28, 2019
915c4ab
chore(refactor): move questions to utils
rishabh3112 May 28, 2019
5778bdf
chore: lint
rishabh3112 May 28, 2019
0782944
chore: lint
rishabh3112 May 28, 2019
cb5a15f
chore: lint
rishabh3112 May 28, 2019
e023d23
chore: add JSDoc descriptions
rishabh3112 May 28, 2019
248b9cc
feat: add mergeHandler
rishabh3112 May 29, 2019
1323bbf
chore: update variable name
rishabh3112 May 29, 2019
a2c49e2
chore: update types of the config
rishabh3112 May 29, 2019
88eec7c
chore: made condition strict
rishabh3112 May 29, 2019
cf85535
chore: update parseMerge
rishabh3112 May 30, 2019
445ab31
chore: make config const
rishabh3112 May 30, 2019
b6a438d
chore: update parseMerge
rishabh3112 May 31, 2019
b7ef7a4
chore: merge with master
rishabh3112 May 31, 2019
55d237b
chore: update prop name
rishabh3112 May 31, 2019
6a7e662
chore: use replaceWith
rishabh3112 May 31, 2019
a89645a
chore: create isImportPresent
rishabh3112 May 31, 2019
5e23da2
chore: remove trivial type
rishabh3112 May 31, 2019
27c6198
chore: add errors for invalid params
rishabh3112 Jun 4, 2019
8b88980
chore: add types to import functions
rishabh3112 Jun 4, 2019
cf8e3c9
chore: merge master
rishabh3112 Jun 5, 2019
7481974
chore: create questions.ts
rishabh3112 Jun 5, 2019
8609b2b
chore: update error message
rishabh3112 Jun 5, 2019
8e3f4ae
chore: update variable name
rishabh3112 Jun 5, 2019
5ee4169
chore: use import instead of require
rishabh3112 Jun 5, 2019
d72ac08
chore: remove eslint disable comments
rishabh3112 Jun 5, 2019
57b47c3
chore: reorder imports
rishabh3112 Jun 6, 2019
8a66c21
chore: reorder imports
rishabh3112 Jun 6, 2019
0e0ba8a
chore: reorder imports
rishabh3112 Jun 6, 2019
7fe04e9
chore: group imports
rishabh3112 Jun 6, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 36 additions & 87 deletions packages/generators/add-generator.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,36 @@
import Generator = require("yeoman-generator");
import {
replaceAt,
traverseAndGetProperties,
webpackDevServerSchema,
webpackSchema,
} from "@webpack-cli/utils/generators/add";

import * as glob from "glob-all";
import * as autoComplete from "inquirer-autocomplete-prompt";
import * as path from "path";
import {
actionTypeQuestion,
entryTypeQuestion,
manualOrListInput,
mergeFileQuestion,
topScopeQuestion,
} from "@webpack-cli/utils/generators/add/questions";

import npmExists from "@webpack-cli/utils/npm-exists";
import { getPackageManager } from "@webpack-cli/utils/package-manager";
import PROP_TYPES from "@webpack-cli/utils/prop-types";
import {
AutoComplete,
Confirm,
IInquirerInput,
Input,
InputValidate,
List,
} from "@webpack-cli/webpack-scaffold";

import { existsSync } from "fs";
import * as glob from "glob-all";
import * as autoComplete from "inquirer-autocomplete-prompt";
import { resolve } from "path";
import Generator = require("yeoman-generator");
import { ISchemaProperties, IWebpackOptions } from "./types";
import entryQuestions from "./utils/entry";

// tslint:disable:no-var-requires
const webpackDevServerSchema = require("webpack-dev-server/lib/options.json");
const webpackSchema = require("./utils/optionsSchema.json");
const PROPS: string[] = Array.from(PROP_TYPES.keys());

/**
*
* Replaces the string with a substring at the given index
* https://gist.github.com/efenacigiray/9367920
*
* @param {String} string - string to be modified
* @param {Number} index - index to replace from
* @param {String} replace - string to replace starting from index
*
* @returns {String} string - The newly mutated string
*
*/
function replaceAt(str: string, index: number, replace: string): string {
return str.substring(0, index) + replace + str.substring(index + 1);
}

/**
*
* Checks if the given array has a given property
*
* @param {Array} arr - array to check
* @param {String} prop - property to check existence of
*
* @returns {Boolean} hasProp - Boolean indicating if the property
* is present
*/
const traverseAndGetProperties = (arr: object[], prop: string): boolean => {
let hasProp = false;
arr.forEach((p: object): void => {
if (p[prop]) {
hasProp = true;
}
});
return hasProp;
};

/**
*
* Search config properties
*
* @param {Object} answers Prompt answers object
* @param {String} input Input search string
*
* @returns {Promise} Returns promise which resolves to filtered props
*
*/
const searchProps = (answers: object, input: string): Promise<string[]> => {
input = input || "";
return Promise.resolve(
PROPS.filter((prop: string): boolean =>
prop.toLowerCase().includes(input.toLowerCase()),
),
);
};
import validate from "./utils/validate";

/**
*
Expand All @@ -93,7 +47,8 @@ export default class AddGenerator extends Generator {
config: {
configName?: string,
topScope?: string[],
item?: string;
item?: string,
merge?: IWebpackOptions,
webpackOptions?: IWebpackOptions,
rishabh3112 marked this conversation as resolved.
Show resolved Hide resolved
},
};
Expand All @@ -115,24 +70,12 @@ export default class AddGenerator extends Generator {
const done: (_?: void) => void | boolean = this.async();
let action: string;
const self: this = this;
const manualOrListInput: (promptAction: string) => IInquirerInput = (promptAction: string) =>
Input("actionAnswer", `What do you want to add to ${promptAction}?`);
let inputPrompt: IInquirerInput;

// first index indicates if it has a deep prop, 2nd indicates what kind of
const isDeepProp: any[] = [false, false];

return this.prompt([
AutoComplete(
"actionType",
"What property do you want to add to?",
{
pageSize: 7,
source: searchProps,
suggestOnly: false,
},
),
])
return this.prompt([actionTypeQuestion])
.then((actionTypeAnswer: {
actionType: string,
}) => {
Expand All @@ -145,9 +88,7 @@ export default class AddGenerator extends Generator {
})
.then((_: void) => {
if (action === "entry") {
return this.prompt([
Confirm("entryType", "Will your application have multiple bundles?", false),
])
return this.prompt([entryTypeQuestion])
.then((entryTypeAnswer: {
entryType: boolean,
}) => {
Expand All @@ -162,16 +103,24 @@ export default class AddGenerator extends Generator {
});
} else {
if (action === "topScope") {
return this.prompt([
Input("topScope", "What do you want to add to topScope?"),
])
return this.prompt([topScopeQuestion])
.then((topScopeAnswer: {
topScope: string;
}) => {
this.configuration.config.topScope.push(topScopeAnswer.topScope);
done();
});
}
if (action === "merge") {
return this.prompt([mergeFileQuestion])
.then((mergeFileAnswer: {
mergeFile: string;
}) => {
const resolvedPath = resolve(process.cwd(), mergeFileAnswer.mergeFile);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to parse this yourself. Extract this logic into a function parseMergeConfig, where you read the config, the webpackOptions is the module export of the config itself, you already have the filename from basename(resolvedPath) . I don't think that merge actually works like this. Merge takes a config name and attaches the name to a function.

What you will need to do, is to make merge take in either <Array>[name, path] and add a require on top if the require is not in topScope (which I think is the best solution) or just add the name (bad DX).

So what you'll need to do, is to have this in the object of the generator:

{
merge: ['webpackBaseConfig', 'path/to/foo'],
config {...}
}

and make the function parseMerge ( https://github.com/webpack/webpack-cli/blob/master/packages/utils/ast-utils.ts#L608-L640 ) accept an array that will convert the first idx to the function parameter, the second one to a topScope property. You could use the existing API to make this happen more easily, and that is to make a require statement based on the indices and add that to topScope before running the transform. This will leave you with the same API as it is, but providing support for merge in add.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Have a look at the comment further down

const mergeConfig = require(resolvedPath);
this.configuration.config.merge = mergeConfig;
});
}
}
const temp = action;
if (action === "resolveLoader") {
Expand Down Expand Up @@ -366,7 +315,7 @@ export default class AddGenerator extends Generator {
pluginsSchemaPath.indexOf("optimize") >= 0
? "webpack.optimize"
: "webpack";
const resolvePluginsPath: string = path.resolve(pluginsSchemaPath);
const resolvePluginsPath: string = resolve(pluginsSchemaPath);
const pluginSchema: object = resolvePluginsPath
? require(resolvePluginsPath)
: null;
Expand Down
62 changes: 62 additions & 0 deletions packages/utils/generators/add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import PROP_TYPES from "../../prop-types";

const PROPS: string[] = Array.from(PROP_TYPES.keys());

// tslint:disable:no-var-requires
export const webpackDevServerSchema = require("webpack-dev-server/lib/options.json");
export const webpackSchema = require("../optionsSchema.json");

/**
*
* Replaces the string with a substring at the given index
* https://gist.github.com/efenacigiray/9367920
*
* @param {String} string - string to be modified
* @param {Number} index - index to replace from
* @param {String} replace - string to replace starting from index
*
* @returns {String} string - The newly mutated string
*
*/
export function replaceAt(str: string, index: number, replace: string): string {
return str.substring(0, index) + replace + str.substring(index + 1);
}

/**
*
* Checks if the given array has a given property
*
* @param {Array} arr - array to check
* @param {String} prop - property to check existence of
*
* @returns {Boolean} hasProp - Boolean indicating if the property
* is present
*/
export const traverseAndGetProperties = (arr: object[], prop: string): boolean => {
let hasProp: boolean = false;
arr.forEach((p: object): void => {
if (p[prop]) {
hasProp = true;
}
});
return hasProp;
};

/**
*
* Search config properties
*
* @param {Object} answers Prompt answers object
* @param {String} input Input search string
*
* @returns {Promise} Returns promise which resolves to filtered props
*
*/
export const searchProps = (answers: object, input: string): Promise<string[]> => {
input = input || "";
return Promise.resolve(
PROPS.filter((prop: string): boolean =>
prop.toLowerCase().includes(input.toLowerCase()),
),
);
};
62 changes: 62 additions & 0 deletions packages/utils/generators/add/questions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
AutoComplete,
Confirm,
IInquirerInput,
Input,
InputValidate,
List,
} from "@webpack-cli/webpack-scaffold";
import { existsSync } from "fs";
import { resolve } from "path";
import {
searchProps,
} from "../index";

/**
* Returns Inquirer question for given action
* @param action string
*/
export const manualOrListInput: (action: string) => IInquirerInput = (action: string) => {
const actionQuestion = `What do you want to add to ${action}?`;
return Input("actionAnswer", actionQuestion);
};

export const actionTypeQuestion = AutoComplete(
"actionType",
"What property do you want to add to?",
{
pageSize: 7,
source: searchProps,
suggestOnly: false,
},
);

export const entryTypeQuestion = Confirm(
"entryType",
"Will your application have multiple bundles?",
false,
);

export const topScopeQuestion = Input(
"topScope",
"What do you want to add to topScope?",
);

const mergeFileQuestionFunction = () => {
const question = "What is the location of webpack configuration with which you want to merge current configuration?";
const validator = (path: string) => {
const resolvedPath = resolve(process.cwd(), path);
if (existsSync(resolvedPath)) {
if (/\.js$/.test(path)) {
if (typeof require(resolvedPath) !== "object") {
return "Given file doesn't export an Object";
}
return true;
}
return "Path doesn't corresponds to a javascript file";
}
return "Invalid path provided";
};
return InputValidate("mergeFile", question, validator);
};
export const mergeFileQuestion = mergeFileQuestionFunction();
Loading