react-mfe-gen 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/constants.js +280 -0
- package/creation/container-creation.js +75 -0
- package/creation/index.js +5 -0
- package/creation/new-pro-creation.js +109 -0
- package/creation/single-mfe-creation.js +49 -0
- package/index.js +4 -0
- package/mfe-gen.js +22 -0
- package/package.json +31 -0
- package/templates/container/app-comp-template.js +10 -0
- package/templates/container/container-comp-template.js +28 -0
- package/templates/container/env-template.js +8 -0
- package/templates/container/heart-template.js +60 -0
- package/templates/container/index.js +17 -0
- package/templates/container/mfe-comp-template.js +8 -0
- package/templates/mfe/app-comp-template.js +14 -0
- package/templates/mfe/config-overrides-template.js +13 -0
- package/templates/mfe/index.js +9 -0
- package/templates/mfe/mfe-index-template.js +36 -0
- package/utility.js +231 -0
package/README.md
ADDED
package/constants.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
export const QUESTION = {
|
|
2
|
+
ACTION: "Select an action:",
|
|
3
|
+
LANGUAGE: "Choose your preferred language:",
|
|
4
|
+
PROJECT_NAME: "Enter your project name:",
|
|
5
|
+
PROJECT_DESCRIPTION: "Enter your project description:",
|
|
6
|
+
NUMBER_OF_MFES: "How many micro-frontends do you plan to include?",
|
|
7
|
+
MFE_NAME:
|
|
8
|
+
"Enter your micro-frontend name:\n**Note:** This will be used as a prefix in window functions.\n:",
|
|
9
|
+
MFE_DESCRIPTION: "Enter a description for your micro-frontend:",
|
|
10
|
+
CONTAINER_NAME:
|
|
11
|
+
"Enter your container app name:\n**Note:** This should be prefix word in your microfrontends window functions.\n:",
|
|
12
|
+
CONTAINER_DESCRIPTION: "Enter a description for your container app:",
|
|
13
|
+
STYLING: "Select a styling solution:",
|
|
14
|
+
STATE_MANAGEMENT: "Select a state management library:",
|
|
15
|
+
FORM_MANAGEMENT: "Select a form management library:",
|
|
16
|
+
CONDITIONAL_MFE_NAME: "Enter your micro-frontend name:",
|
|
17
|
+
CONTAINER_PATH:
|
|
18
|
+
"Please enter a path to create container:\ne.g: G:\\workspace\\sample-container\n:",
|
|
19
|
+
MFE_PATH:
|
|
20
|
+
"Please enter a path to create microfront end:\ne.g: G:\\workspace\\sample-mfe\n:",
|
|
21
|
+
PATH: "Please enter a path to create ",
|
|
22
|
+
};
|
|
23
|
+
export const CHOICE_CONSTANTS = {
|
|
24
|
+
ACTION: {
|
|
25
|
+
NEW_PROJECT: "Create a new project 🚀",
|
|
26
|
+
CONTAINER: "Create only a container 📦",
|
|
27
|
+
SINGLE_MFE: "Create a single micro-frontend 🌐",
|
|
28
|
+
},
|
|
29
|
+
STYLING: {
|
|
30
|
+
SASS: "Sass 🎨",
|
|
31
|
+
TAILWIND: "Tailwind 🌊",
|
|
32
|
+
MATERIAL_UI: "Material UI 🧩",
|
|
33
|
+
BOOTSTRAP: "Bootstrap 🥾",
|
|
34
|
+
STYLED_COMPONENTS: "Styled Components ✍️",
|
|
35
|
+
},
|
|
36
|
+
LANGUAGE: {
|
|
37
|
+
JAVA_SCRIPT: "JavaScript 🟨",
|
|
38
|
+
TYPE_SCRIPT: "TypeScript 🔷",
|
|
39
|
+
},
|
|
40
|
+
STATE_MANAGEMENT: {
|
|
41
|
+
REDUX: "Redux 🔄",
|
|
42
|
+
ZUSTAND: "Zustand 🐻",
|
|
43
|
+
},
|
|
44
|
+
FORM_MANAGEMENT: {
|
|
45
|
+
REACT_HOOK_FORM: "React-hook-form 🪝",
|
|
46
|
+
FORMIK: "Formik 📝",
|
|
47
|
+
},
|
|
48
|
+
NONE: "None ❌",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const CHOICES = {
|
|
52
|
+
ACTION: [
|
|
53
|
+
CHOICE_CONSTANTS.ACTION.NEW_PROJECT,
|
|
54
|
+
CHOICE_CONSTANTS.ACTION.CONTAINER,
|
|
55
|
+
CHOICE_CONSTANTS.ACTION.SINGLE_MFE,
|
|
56
|
+
],
|
|
57
|
+
STYLING: [
|
|
58
|
+
CHOICE_CONSTANTS.NONE,
|
|
59
|
+
CHOICE_CONSTANTS.STYLING.SASS,
|
|
60
|
+
CHOICE_CONSTANTS.STYLING.BOOTSTRAP,
|
|
61
|
+
CHOICE_CONSTANTS.STYLING.MATERIAL_UI,
|
|
62
|
+
CHOICE_CONSTANTS.STYLING.TAILWIND,
|
|
63
|
+
CHOICE_CONSTANTS.STYLING.STYLED_COMPONENTS,
|
|
64
|
+
],
|
|
65
|
+
LANGUAGE: [
|
|
66
|
+
CHOICE_CONSTANTS.LANGUAGE.JAVA_SCRIPT,
|
|
67
|
+
CHOICE_CONSTANTS.LANGUAGE.TYPE_SCRIPT,
|
|
68
|
+
],
|
|
69
|
+
STATE_MANAGEMENT: [
|
|
70
|
+
CHOICE_CONSTANTS.NONE,
|
|
71
|
+
CHOICE_CONSTANTS.STATE_MANAGEMENT.REDUX,
|
|
72
|
+
CHOICE_CONSTANTS.STATE_MANAGEMENT.ZUSTAND,
|
|
73
|
+
],
|
|
74
|
+
FORM_MANAGEMENT: [
|
|
75
|
+
CHOICE_CONSTANTS.NONE,
|
|
76
|
+
CHOICE_CONSTANTS.FORM_MANAGEMENT.REACT_HOOK_FORM,
|
|
77
|
+
CHOICE_CONSTANTS.FORM_MANAGEMENT.FORMIK,
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const PROMPT = {
|
|
82
|
+
USER_NEED: [
|
|
83
|
+
{
|
|
84
|
+
message: QUESTION.ACTION,
|
|
85
|
+
type: "list",
|
|
86
|
+
name: "typeOfAction",
|
|
87
|
+
choices: CHOICES.ACTION,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
message: QUESTION.LANGUAGE,
|
|
91
|
+
type: "list",
|
|
92
|
+
name: "language",
|
|
93
|
+
choices: CHOICES.LANGUAGE,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
ENTIRE_PROJECT: [
|
|
97
|
+
{
|
|
98
|
+
message: QUESTION.PROJECT_NAME,
|
|
99
|
+
type: "input",
|
|
100
|
+
name: "projectName",
|
|
101
|
+
validate(value) {
|
|
102
|
+
if (!/^[a-z-]+$/.test(value)) {
|
|
103
|
+
return "Please use only lowercase letters and '-' (numbers, capital letters, and other symbols are not allowed).";
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
message: QUESTION.PROJECT_DESCRIPTION,
|
|
110
|
+
type: "input",
|
|
111
|
+
name: "projectDescription",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: "input",
|
|
115
|
+
name: "numberOfMfes",
|
|
116
|
+
message: QUESTION.NUMBER_OF_MFES,
|
|
117
|
+
validate(value) {
|
|
118
|
+
const isNumber = !Number.isNaN(Number.parseFloat(value));
|
|
119
|
+
return isNumber || "Please enter a valid number";
|
|
120
|
+
},
|
|
121
|
+
filter: Number,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
ONE_MFE: [
|
|
125
|
+
{
|
|
126
|
+
message: QUESTION.MFE_NAME,
|
|
127
|
+
type: "input",
|
|
128
|
+
name: "mfeName",
|
|
129
|
+
validate(value) {
|
|
130
|
+
if (!/^[a-z-]+$/.test(value)) {
|
|
131
|
+
return "Please use only lowercase letters and '-' (numbers, capital letters, and other symbols are not allowed).";
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
message: QUESTION.MFE_DESCRIPTION,
|
|
138
|
+
type: "input",
|
|
139
|
+
name: "mfeDescription",
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
CONTAINER: [
|
|
143
|
+
{
|
|
144
|
+
message: QUESTION.CONTAINER_NAME,
|
|
145
|
+
type: "input",
|
|
146
|
+
name: "containerName",
|
|
147
|
+
validate(value) {
|
|
148
|
+
if (!/^[a-z-]+$/.test(value)) {
|
|
149
|
+
return "Please use only lowercase letters and '-' (numbers, capital letters, and other symbols are not allowed).";
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
message: QUESTION.CONTAINER_DESCRIPTION,
|
|
156
|
+
type: "input",
|
|
157
|
+
name: "containerDescription",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
type: "input",
|
|
161
|
+
name: "numberOfMfes",
|
|
162
|
+
message: QUESTION.NUMBER_OF_MFES,
|
|
163
|
+
validate(value) {
|
|
164
|
+
const isNumber = !Number.isNaN(Number.parseFloat(value));
|
|
165
|
+
return isNumber || "Please enter a valid number";
|
|
166
|
+
},
|
|
167
|
+
filter: Number,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
COMMON: [
|
|
171
|
+
{
|
|
172
|
+
message: QUESTION.STYLING,
|
|
173
|
+
type: "list",
|
|
174
|
+
name: "styling",
|
|
175
|
+
choices: CHOICES.STYLING,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
message: QUESTION.STATE_MANAGEMENT,
|
|
179
|
+
type: "list",
|
|
180
|
+
name: "stateManagement",
|
|
181
|
+
choices: CHOICES.STATE_MANAGEMENT,
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
CONDITIONAL: {
|
|
185
|
+
MICROFRONT_END_NAME: {
|
|
186
|
+
message: QUESTION.CONDITIONAL_MFE_NAME,
|
|
187
|
+
type: "input",
|
|
188
|
+
name: "mfeName",
|
|
189
|
+
validate(value) {
|
|
190
|
+
if (!/^[a-z-]+$/.test(value)) {
|
|
191
|
+
return "Please use only lowercase letters and '-' (numbers, capital letters, and other symbols are not allowed).";
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
CONTAINER_PATH: {
|
|
197
|
+
message: QUESTION.CONTAINER_PATH,
|
|
198
|
+
type: "input",
|
|
199
|
+
name: "containerPath",
|
|
200
|
+
},
|
|
201
|
+
MFE_PATH: {
|
|
202
|
+
message: QUESTION.MFE_PATH,
|
|
203
|
+
type: "input",
|
|
204
|
+
name: "mfePath",
|
|
205
|
+
},
|
|
206
|
+
PATH: {
|
|
207
|
+
message: QUESTION.PATH,
|
|
208
|
+
type: "input",
|
|
209
|
+
name: "path",
|
|
210
|
+
},
|
|
211
|
+
MICROFRONT_END_NAME: {
|
|
212
|
+
message: QUESTION.CONDITIONAL_MFE_NAME,
|
|
213
|
+
type: "input",
|
|
214
|
+
name: "mfeName",
|
|
215
|
+
},
|
|
216
|
+
FORM_MANAGEMENT: {
|
|
217
|
+
message: QUESTION.FORM_MANAGEMENT,
|
|
218
|
+
type: "list",
|
|
219
|
+
name: "formManagement",
|
|
220
|
+
choices: CHOICES.FORM_MANAGEMENT,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const INFO_MESSAGE = {
|
|
226
|
+
DISCLAIMER:
|
|
227
|
+
"Hello there! 👋\n\nThis tool currently supports micro-frontend creation using runtime integration via custom script injection ⚙️.\nPlease keep this limitation in mind when developing your MFE applications 📌.\n",
|
|
228
|
+
CREATE_APP: "Let's create ",
|
|
229
|
+
CONFIGURE_CONTAINER: "Configuring the container... 🛠️",
|
|
230
|
+
APP_CREATION: "Creating your React app... ⚛️ ",
|
|
231
|
+
i_DEPENDENCIES: "Installing dependencies... 📦 ",
|
|
232
|
+
i_DEV_DEPENDENCIES: "Installing dev dependencies... 🧩 ",
|
|
233
|
+
SUCCESS: {
|
|
234
|
+
NEW_PRO: "New project created successfully! 🎉",
|
|
235
|
+
CONTAINER: "Container created successfully! 📦",
|
|
236
|
+
ONE_MFE: "Microfront end created successfully! 🌐",
|
|
237
|
+
},
|
|
238
|
+
HAPPY_CODING: "Happy coding! 💻✨",
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export const LIBRARY_PAIR = {
|
|
242
|
+
STYLING: {
|
|
243
|
+
[CHOICE_CONSTANTS.STYLING.TAILWIND]: [
|
|
244
|
+
"tailwindcss",
|
|
245
|
+
"postcss",
|
|
246
|
+
"autoprefixer",
|
|
247
|
+
],
|
|
248
|
+
[CHOICE_CONSTANTS.STYLING.SASS]: ["sass"],
|
|
249
|
+
[CHOICE_CONSTANTS.STYLING.MATERIAL_UI]: [
|
|
250
|
+
"@mui/material",
|
|
251
|
+
"@emotion/react",
|
|
252
|
+
"@emotion/styled",
|
|
253
|
+
"@mui/icons-material",
|
|
254
|
+
],
|
|
255
|
+
[CHOICE_CONSTANTS.STYLING.BOOTSTRAP]: ["bootstrap"],
|
|
256
|
+
[CHOICE_CONSTANTS.STYLING.STYLED_COMPONENTS]: ["styled-components"],
|
|
257
|
+
[CHOICE_CONSTANTS.NONE]: [],
|
|
258
|
+
},
|
|
259
|
+
STATE_MANAGEMENT: {
|
|
260
|
+
[CHOICE_CONSTANTS.NONE]: [],
|
|
261
|
+
[CHOICE_CONSTANTS.STATE_MANAGEMENT.REDUX]: [
|
|
262
|
+
"@reduxjs/toolkit",
|
|
263
|
+
"react-redux",
|
|
264
|
+
],
|
|
265
|
+
[CHOICE_CONSTANTS.STATE_MANAGEMENT.ZUSTAND]: ["zustand"],
|
|
266
|
+
},
|
|
267
|
+
FORM_MANAGEMENT: {
|
|
268
|
+
[CHOICE_CONSTANTS.NONE]: [],
|
|
269
|
+
[CHOICE_CONSTANTS.FORM_MANAGEMENT.REACT_HOOK_FORM]: [
|
|
270
|
+
"react-hook-form",
|
|
271
|
+
"@hookform/resolvers",
|
|
272
|
+
"yup",
|
|
273
|
+
],
|
|
274
|
+
[CHOICE_CONSTANTS.FORM_MANAGEMENT.FORMIK]: ["formik", "yup"],
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
export const DEFAULT_DEPENDENCIES = ["axios", "react-router-dom"];
|
|
279
|
+
|
|
280
|
+
export const DEFAULT_DEV_DEPENDENCIES = ["cross-env", "react-app-rewired"];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { PROMPT, INFO_MESSAGE, CHOICE_CONSTANTS } from "../constants.js";
|
|
3
|
+
import utils from "../utility.js";
|
|
4
|
+
|
|
5
|
+
const containerCreation = async (language) => {
|
|
6
|
+
// To store different working dir
|
|
7
|
+
const wrokingDirectories = [];
|
|
8
|
+
try {
|
|
9
|
+
// Get typescript flag
|
|
10
|
+
const isTypeScript = language === CHOICE_CONSTANTS.LANGUAGE.TYPE_SCRIPT;
|
|
11
|
+
// Declare array to store list of mfe names
|
|
12
|
+
const mfeNames = [];
|
|
13
|
+
|
|
14
|
+
const { containerName, containerDescription, numberOfMfes } =
|
|
15
|
+
await inquirer.prompt(PROMPT.CONTAINER);
|
|
16
|
+
|
|
17
|
+
// Interate to get each mfe name
|
|
18
|
+
for (let i = 0; i < numberOfMfes; i++) {
|
|
19
|
+
const { mfeName } = await inquirer.prompt([
|
|
20
|
+
{
|
|
21
|
+
message: `Enter your name of Microfront end ${i + 1}:`,
|
|
22
|
+
type: "input",
|
|
23
|
+
name: "mfeName",
|
|
24
|
+
validate(value) {
|
|
25
|
+
if (!/^[a-z-]+$/.test(value)) {
|
|
26
|
+
return "Please use only lowercase letters and '-' (numbers, capital letters, and other symbols are not allowed).";
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
]);
|
|
32
|
+
mfeNames.push(mfeName);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`${INFO_MESSAGE.CREATE_APP}${containerName} as container`);
|
|
36
|
+
|
|
37
|
+
// Get container path and app requirements
|
|
38
|
+
const commonInfo = await inquirer.prompt([
|
|
39
|
+
PROMPT.CONDITIONAL.CONTAINER_PATH,
|
|
40
|
+
...PROMPT.COMMON,
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// store working dir
|
|
44
|
+
wrokingDirectories.push(commonInfo.containerPath);
|
|
45
|
+
|
|
46
|
+
// Go inside user specified dir
|
|
47
|
+
process.chdir(commonInfo.containerPath);
|
|
48
|
+
|
|
49
|
+
// Array to store CRA command
|
|
50
|
+
const appCommand = utils.getLanguageTemplate(containerName, isTypeScript);
|
|
51
|
+
|
|
52
|
+
// Create container react app
|
|
53
|
+
await utils.createReactApp(appCommand);
|
|
54
|
+
|
|
55
|
+
// Make normal react app into MFE container
|
|
56
|
+
await utils.configureContainer(
|
|
57
|
+
{
|
|
58
|
+
projectName: containerName,
|
|
59
|
+
projectDescription: containerDescription,
|
|
60
|
+
...commonInfo,
|
|
61
|
+
isTypeScript,
|
|
62
|
+
},
|
|
63
|
+
mfeNames
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Let user know container created status
|
|
67
|
+
console.log(
|
|
68
|
+
`${INFO_MESSAGE.SUCCESS.CONTAINER}\n${INFO_MESSAGE.HAPPY_CODING}`
|
|
69
|
+
);
|
|
70
|
+
} catch {
|
|
71
|
+
utils.cleanupProject(wrokingDirectories);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default containerCreation;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import {
|
|
3
|
+
PROMPT,
|
|
4
|
+
INFO_MESSAGE,
|
|
5
|
+
QUESTION,
|
|
6
|
+
CHOICE_CONSTANTS,
|
|
7
|
+
} from "../constants.js";
|
|
8
|
+
import utils from "../utility.js";
|
|
9
|
+
|
|
10
|
+
const newProjectCreation = async (language) => {
|
|
11
|
+
// To store different working dir
|
|
12
|
+
const wrokingDirectories = [];
|
|
13
|
+
try {
|
|
14
|
+
// Get typescript flag
|
|
15
|
+
const isTypeScript = language === CHOICE_CONSTANTS.LANGUAGE.TYPE_SCRIPT;
|
|
16
|
+
// Declare array to store list of mfe names
|
|
17
|
+
const mfeNames = [];
|
|
18
|
+
// Get basic project info
|
|
19
|
+
const projectInfo = await inquirer.prompt(PROMPT.ENTIRE_PROJECT);
|
|
20
|
+
|
|
21
|
+
// Interate to get each mfe name
|
|
22
|
+
for (let i = 0; i < projectInfo.numberOfMfes; i++) {
|
|
23
|
+
const { mfeName } = await inquirer.prompt([
|
|
24
|
+
{
|
|
25
|
+
message: `Enter your name of Microfront end ${i + 1}:`,
|
|
26
|
+
type: "input",
|
|
27
|
+
name: "mfeName",
|
|
28
|
+
validate(value) {
|
|
29
|
+
if (!/^[a-z-]+$/.test(value)) {
|
|
30
|
+
return "Please use only lowercase letters and '-' (numbers, capital letters, and other symbols are not allowed).";
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
]);
|
|
36
|
+
mfeNames.push(mfeName);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(
|
|
40
|
+
`${INFO_MESSAGE.CREATE_APP}${projectInfo.projectName} as container`
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Get container path and app requirements
|
|
44
|
+
const commonInfo = await inquirer.prompt([
|
|
45
|
+
PROMPT.CONDITIONAL.CONTAINER_PATH,
|
|
46
|
+
...PROMPT.COMMON,
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// store working dir
|
|
50
|
+
wrokingDirectories.push(commonInfo.containerPath);
|
|
51
|
+
// Go inside user specified dir
|
|
52
|
+
process.chdir(commonInfo.containerPath);
|
|
53
|
+
|
|
54
|
+
// Array to store CRA command
|
|
55
|
+
const appCommand = utils.getLanguageTemplate(
|
|
56
|
+
projectInfo.projectName,
|
|
57
|
+
isTypeScript
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Create container react app
|
|
61
|
+
await utils.createReactApp(appCommand);
|
|
62
|
+
|
|
63
|
+
// Make normal react app into MFE container
|
|
64
|
+
await utils.configureContainer(
|
|
65
|
+
{ ...projectInfo, ...commonInfo, isTypeScript },
|
|
66
|
+
mfeNames
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Get mfe requirements for each mfe
|
|
70
|
+
for (let i = 0; i < mfeNames.length; i++) {
|
|
71
|
+
const mfeName = mfeNames[i];
|
|
72
|
+
|
|
73
|
+
console.log(`${INFO_MESSAGE.CREATE_APP}${mfeName}\n`);
|
|
74
|
+
|
|
75
|
+
const mfeInfo = await inquirer.prompt([
|
|
76
|
+
{
|
|
77
|
+
message: `${QUESTION.PATH}${mfeName} as microfront end\ne.g: G:\\workspace\\sample-mfe\n:`,
|
|
78
|
+
type: "input",
|
|
79
|
+
name: "path",
|
|
80
|
+
},
|
|
81
|
+
PROMPT.CONDITIONAL.FORM_MANAGEMENT,
|
|
82
|
+
...PROMPT.COMMON,
|
|
83
|
+
]);
|
|
84
|
+
// store working dir
|
|
85
|
+
wrokingDirectories.push(mfeInfo.path);
|
|
86
|
+
// Go inside user specified mfe dir
|
|
87
|
+
process.chdir(mfeInfo.path);
|
|
88
|
+
const mfeAppCommand = utils.getLanguageTemplate(mfeName, isTypeScript);
|
|
89
|
+
|
|
90
|
+
// Create mfe react app
|
|
91
|
+
await utils.createReactApp(mfeAppCommand);
|
|
92
|
+
|
|
93
|
+
// Make normal react app into MFE container
|
|
94
|
+
await utils.configureMfe(
|
|
95
|
+
{ ...mfeInfo, projectName: projectInfo.projectName, isTypeScript },
|
|
96
|
+
mfeName,
|
|
97
|
+
i
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(
|
|
102
|
+
`${INFO_MESSAGE.SUCCESS.NEW_PRO}\n${INFO_MESSAGE.HAPPY_CODING}`
|
|
103
|
+
);
|
|
104
|
+
} catch {
|
|
105
|
+
utils.cleanupProject(wrokingDirectories);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default newProjectCreation;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { PROMPT, INFO_MESSAGE, CHOICE_CONSTANTS } from "../constants.js";
|
|
3
|
+
import utils from "../utility.js";
|
|
4
|
+
|
|
5
|
+
const singleMfeCreation = async (language) => {
|
|
6
|
+
// To store different working dir
|
|
7
|
+
const wrokingDirectories = [];
|
|
8
|
+
try {
|
|
9
|
+
// Get typescript flag
|
|
10
|
+
const isTypeScript = language === CHOICE_CONSTANTS.LANGUAGE.TYPE_SCRIPT;
|
|
11
|
+
|
|
12
|
+
const { mfeName, mfeDescription } = await inquirer.prompt(PROMPT.ONE_MFE);
|
|
13
|
+
|
|
14
|
+
console.log(`${INFO_MESSAGE.CREATE_APP}${mfeName} as microfront end`);
|
|
15
|
+
|
|
16
|
+
const mfeInfo = await inquirer.prompt([
|
|
17
|
+
PROMPT.CONDITIONAL.MFE_PATH,
|
|
18
|
+
...PROMPT.COMMON,
|
|
19
|
+
PROMPT.CONDITIONAL.FORM_MANAGEMENT,
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
// store working dir
|
|
23
|
+
wrokingDirectories.push(mfeInfo.mfePath);
|
|
24
|
+
|
|
25
|
+
// Go inside user specified dir
|
|
26
|
+
process.chdir(mfeInfo.mfePath);
|
|
27
|
+
|
|
28
|
+
// Array to store CRA command
|
|
29
|
+
const appCommand = utils.getLanguageTemplate(mfeName, isTypeScript);
|
|
30
|
+
|
|
31
|
+
// Create container react app
|
|
32
|
+
await utils.createReactApp(appCommand);
|
|
33
|
+
|
|
34
|
+
// Make normal react app into MFE container
|
|
35
|
+
await utils.configureMfe(
|
|
36
|
+
{ ...mfeInfo, projectName: mfeName, isTypeScript },
|
|
37
|
+
mfeName,
|
|
38
|
+
0
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
console.log(
|
|
42
|
+
`${INFO_MESSAGE.SUCCESS.ONE_MFE}\n${INFO_MESSAGE.HAPPY_CODING}`
|
|
43
|
+
);
|
|
44
|
+
} catch {
|
|
45
|
+
utils.cleanupProject(wrokingDirectories)
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default singleMfeCreation;
|
package/index.js
ADDED
package/mfe-gen.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { PROMPT, INFO_MESSAGE, CHOICE_CONSTANTS } from "./constants.js";
|
|
3
|
+
import {newProjectCreation,containerCreation,singleMfeCreation} from "./creation/index.js";
|
|
4
|
+
|
|
5
|
+
const mfeGen = async () => {
|
|
6
|
+
console.log(INFO_MESSAGE.DISCLAIMER);
|
|
7
|
+
|
|
8
|
+
const { typeOfAction, language } = await inquirer.prompt(PROMPT.USER_NEED);
|
|
9
|
+
switch (typeOfAction) {
|
|
10
|
+
case CHOICE_CONSTANTS.ACTION.NEW_PROJECT:
|
|
11
|
+
newProjectCreation(language);
|
|
12
|
+
break;
|
|
13
|
+
case CHOICE_CONSTANTS.ACTION.CONTAINER:
|
|
14
|
+
containerCreation(language);
|
|
15
|
+
break;
|
|
16
|
+
case CHOICE_CONSTANTS.ACTION.SINGLE_MFE:
|
|
17
|
+
singleMfeCreation(language);
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default mfeGen;
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-mfe-gen",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for react Microfront end initial setup",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/VengadeshRaj/react-mfe-gen.git"
|
|
12
|
+
},
|
|
13
|
+
"author": "Vengadesh Raj",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/VengadeshRaj/react-mfe-gen/issues"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/VengadeshRaj/react-mfe-gen#readme",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"cli-spinner": "^0.2.10",
|
|
21
|
+
"execa": "^9.6.0",
|
|
22
|
+
"inquirer": "^12.9.4"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"react-mfe-gen": "./index.js"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import utils from "../../utility.js";
|
|
2
|
+
|
|
3
|
+
export const getContainerCompContent = (containerCompName, mfes) => {
|
|
4
|
+
let containerComp = `import React from "react";\n`;
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < mfes.length; i++) {
|
|
7
|
+
containerComp += `import {${utils.toCompName(mfes[i])}} from "./microfrontends/${utils.toCompName(mfes[i])}";\n`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
containerComp += `\n\nfunction ${containerCompName}() {
|
|
11
|
+
const getMfeOrigin =(port:string|undefined='')=> \`\${window.location.protocol}//\${window.location.hostname}:\${port}\`;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div>
|
|
15
|
+
<h1>Hello from ${containerCompName}</h1>\n`;
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < mfes.length; i++) {
|
|
18
|
+
containerComp += ` <${utils.toCompName(mfes[i]) } name={"${utils.toCompName(mfes[i])}"} host={getMfeOrigin(process.env.REACT_APP_${mfes[i].toUpperCase()})} />\n`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
containerComp += `</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default ${containerCompName};`;
|
|
26
|
+
|
|
27
|
+
return containerComp;
|
|
28
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const getHeartContent=(containerName='container')=>{
|
|
2
|
+
return `
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
class MicroFrontend extends React.Component {
|
|
6
|
+
componentDidMount() {
|
|
7
|
+
const { name, host, document } = this.props;
|
|
8
|
+
const scriptId = \`micro-frontend-script-\${name}\`;
|
|
9
|
+
|
|
10
|
+
if (document.getElementById(scriptId)) {
|
|
11
|
+
this.renderMicroFrontend();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fetch(\`\${host}/asset-manifest.json\`)
|
|
16
|
+
.then((res) => res.json())
|
|
17
|
+
.then((manifest) => {
|
|
18
|
+
const script = document.createElement("script");
|
|
19
|
+
script.id = scriptId;
|
|
20
|
+
script.crossOrigin = "";
|
|
21
|
+
script.src = \`\${host}\${manifest["files"]["main.js"]}\`;
|
|
22
|
+
script.onload = this.renderMicroFrontend;
|
|
23
|
+
document.head.appendChild(script);
|
|
24
|
+
const link = document.createElement("link");
|
|
25
|
+
link.id = scriptId;
|
|
26
|
+
link.href = \`\${host}\${manifest["files"]["main.css"]}\`;
|
|
27
|
+
link.onload = this.renderMicroFrontend;
|
|
28
|
+
link.rel = "stylesheet";
|
|
29
|
+
document.head.appendChild(link);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
componentWillUnmount() {
|
|
34
|
+
const { name, window } = this.props;
|
|
35
|
+
|
|
36
|
+
window[\`unmount\${name}\`] && window[\`unmount\${name}\`](
|
|
37
|
+
\`\${name}-${containerName}\`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
renderMicroFrontend = () => {
|
|
42
|
+
const { name, window, history } = this.props;
|
|
43
|
+
|
|
44
|
+
window[\`render\${name}\`] &&
|
|
45
|
+
window[\`render\${name}\`](\`\${name}-${containerName}\`, history);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
render() {
|
|
49
|
+
return <main id={\`\${this.props.name}-${containerName}\`} />;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
MicroFrontend.defaultProps = {
|
|
54
|
+
document,
|
|
55
|
+
window,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default MicroFrontend;
|
|
59
|
+
`
|
|
60
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getAppContent } from "./app-comp-template.js";
|
|
2
|
+
|
|
3
|
+
import { getContainerCompContent } from "./container-comp-template.js";
|
|
4
|
+
|
|
5
|
+
import { getEnvContent } from "./env-template.js";
|
|
6
|
+
|
|
7
|
+
import { getHeartContent } from "./heart-template.js";
|
|
8
|
+
|
|
9
|
+
import { getMfeCompContent } from "./mfe-comp-template.js";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
getAppContent,
|
|
13
|
+
getContainerCompContent,
|
|
14
|
+
getEnvContent,
|
|
15
|
+
getHeartContent,
|
|
16
|
+
getMfeCompContent,
|
|
17
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const getMfeCompContent = (mfeName, isTypeScript) => {
|
|
2
|
+
return `import MicroFrontend from "../MicroFrontend";
|
|
3
|
+
|
|
4
|
+
export const ${mfeName} = (props${isTypeScript ? ": any" : ""}) => {
|
|
5
|
+
return <MicroFrontend name={props.name} host={props.host} />;
|
|
6
|
+
};
|
|
7
|
+
`;
|
|
8
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const getConfigOverridesContent =()=>{
|
|
2
|
+
return`module.exports = {
|
|
3
|
+
webpack: (config, env) => {
|
|
4
|
+
config.optimization.runtimeChunk = false;
|
|
5
|
+
config.optimization.splitChunks = {
|
|
6
|
+
cacheGroups: {
|
|
7
|
+
default: false,
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
return config;
|
|
11
|
+
},
|
|
12
|
+
};`
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getMfeAppContent } from "./app-comp-template.js";
|
|
2
|
+
import { getConfigOverridesContent } from "./config-overrides-template.js";
|
|
3
|
+
import { getmfeIndexContent } from "./mfe-index-template.js";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
getMfeAppContent,
|
|
7
|
+
getConfigOverridesContent,
|
|
8
|
+
getmfeIndexContent
|
|
9
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const getmfeIndexContent=(mfeName='mfe',container='',isTs)=>{
|
|
2
|
+
return`import React from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import App from "./App";
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
render${mfeName}: (containerId${isTs?": string":""}, history${isTs?"?: any":""}) => void;
|
|
9
|
+
unmount${mfeName}: (containerId${isTs?": string":""}) => void;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
window.render${mfeName} = (containerId${isTs?": string":""}, history${isTs?"?: any":""}) => {
|
|
14
|
+
const container${isTs?": any":""} = document.getElementById(containerId);
|
|
15
|
+
if (container) {
|
|
16
|
+
const root = createRoot(container);
|
|
17
|
+
root.render(<App history={history} />);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
window.unmount${mfeName} = (containerId${isTs?": string":""}) => {
|
|
22
|
+
const container = document.getElementById(containerId);
|
|
23
|
+
if (container) {
|
|
24
|
+
const root = createRoot(container);
|
|
25
|
+
root.unmount();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const rootContainer = document.getElementById("root");
|
|
30
|
+
if (rootContainer && !document.getElementById("${mfeName}-${container}")) {
|
|
31
|
+
createRoot(rootContainer).render(<App />);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
`
|
|
36
|
+
}
|
package/utility.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Spinner } from "cli-spinner";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import {
|
|
4
|
+
INFO_MESSAGE,
|
|
5
|
+
LIBRARY_PAIR,
|
|
6
|
+
DEFAULT_DEPENDENCIES,
|
|
7
|
+
DEFAULT_DEV_DEPENDENCIES,
|
|
8
|
+
} from "./constants.js";
|
|
9
|
+
import {
|
|
10
|
+
getHeartContent,
|
|
11
|
+
getAppContent,
|
|
12
|
+
getMfeCompContent,
|
|
13
|
+
getContainerCompContent,
|
|
14
|
+
getEnvContent,
|
|
15
|
+
} from "./templates/container/index.js";
|
|
16
|
+
import { writeFile, unlink, mkdir, readFile, rm } from "fs/promises";
|
|
17
|
+
import {
|
|
18
|
+
getConfigOverridesContent,
|
|
19
|
+
getMfeAppContent,
|
|
20
|
+
getmfeIndexContent,
|
|
21
|
+
} from "./templates/mfe/index.js";
|
|
22
|
+
class utils {
|
|
23
|
+
static async runTask(logMessage, task) {
|
|
24
|
+
const spinner = new Spinner(logMessage + "%s");
|
|
25
|
+
spinner.setSpinnerString("⠋⠙⠹⠸⠼⠴⠦⠧⠏");
|
|
26
|
+
spinner.setSpinnerDelay(40);
|
|
27
|
+
spinner.start();
|
|
28
|
+
try {
|
|
29
|
+
await task();
|
|
30
|
+
spinner.stop(true);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
spinner.stop(true);
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
static async cleanupProject(dirs) {
|
|
37
|
+
console.log(`Cleaning up the project directory: ${dirs}`);
|
|
38
|
+
try {
|
|
39
|
+
for (let i = 0; i < dirs.length; i++) {
|
|
40
|
+
await rm(dirs[i], { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.log(`Failed to clean project directory\n Error:${err}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
static async createReactApp(appCommand) {
|
|
47
|
+
try {
|
|
48
|
+
await utils.runTask(INFO_MESSAGE.APP_CREATION, () =>
|
|
49
|
+
execa("npx", ["create-react-app", ...appCommand])
|
|
50
|
+
);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
static async installPackages(packages, isDev = false) {
|
|
56
|
+
try {
|
|
57
|
+
let message = INFO_MESSAGE.i_DEPENDENCIES;
|
|
58
|
+
if (isDev) {
|
|
59
|
+
message = INFO_MESSAGE.i_DEV_DEPENDENCIES;
|
|
60
|
+
packages = [...packages, "--save-dev"];
|
|
61
|
+
}
|
|
62
|
+
await utils.runTask(message, () =>
|
|
63
|
+
execa("npm", ["install", ...packages])
|
|
64
|
+
);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
static getLanguageTemplate(appName, isTs) {
|
|
70
|
+
return isTs ? [appName, "--template", "typescript"] : [appName];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static withExt(name, isTs) {
|
|
74
|
+
return isTs ? `${name}.tsx` : `${name}.jsx`;
|
|
75
|
+
}
|
|
76
|
+
static withScript(name, isTs) {
|
|
77
|
+
return isTs ? `${name}.ts` : `${name}.js`;
|
|
78
|
+
}
|
|
79
|
+
static toCompName(AppName) {
|
|
80
|
+
return AppName.replace(/[-\s]+/g, " ")
|
|
81
|
+
.split(" ")
|
|
82
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
83
|
+
.join("");
|
|
84
|
+
}
|
|
85
|
+
static async updateScripts(dir, newScripts) {
|
|
86
|
+
const rawPackageJson = await readFile(dir);
|
|
87
|
+
const packageJSON = JSON.parse(rawPackageJson, "utf8");
|
|
88
|
+
|
|
89
|
+
packageJSON.scripts = newScripts;
|
|
90
|
+
|
|
91
|
+
await writeFile(dir, JSON.stringify(packageJSON, null, 2), "utf8");
|
|
92
|
+
}
|
|
93
|
+
static async configureContainer(info, mfeNames) {
|
|
94
|
+
// Destructure inputs
|
|
95
|
+
const {
|
|
96
|
+
projectName,
|
|
97
|
+
projectDescription,
|
|
98
|
+
isTypeScript,
|
|
99
|
+
styling,
|
|
100
|
+
stateManagement,
|
|
101
|
+
} = info;
|
|
102
|
+
|
|
103
|
+
// Go inside src
|
|
104
|
+
process.chdir(`${process.cwd()}\\${projectName}\\src`);
|
|
105
|
+
|
|
106
|
+
// Create heart of container
|
|
107
|
+
await utils.runTask(INFO_MESSAGE.CONFIGURE_CONTAINER, () =>
|
|
108
|
+
writeFile("MicroFrontend.js", getHeartContent(projectName))
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Convert project name into component name format
|
|
112
|
+
const containerCompName = utils.toCompName(projectName);
|
|
113
|
+
|
|
114
|
+
// Create container component
|
|
115
|
+
await utils.runTask(INFO_MESSAGE.CONFIGURE_CONTAINER, () =>
|
|
116
|
+
writeFile(
|
|
117
|
+
utils.withExt(containerCompName, isTypeScript),
|
|
118
|
+
getContainerCompContent(containerCompName, mfeNames)
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Modify App.jsx or .tsx file
|
|
123
|
+
await utils.runTask(INFO_MESSAGE.CONFIGURE_CONTAINER, () =>
|
|
124
|
+
writeFile(
|
|
125
|
+
utils.withExt("App", isTypeScript),
|
|
126
|
+
getAppContent(containerCompName)
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Drop unnecessary files
|
|
131
|
+
await unlink("App.css");
|
|
132
|
+
await unlink("logo.svg");
|
|
133
|
+
|
|
134
|
+
// Create MFE folders
|
|
135
|
+
await mkdir("microfrontends");
|
|
136
|
+
|
|
137
|
+
// Go inside MFE folder
|
|
138
|
+
process.chdir(`${process.cwd()}\\microfrontends`);
|
|
139
|
+
|
|
140
|
+
// Run loop and create number of mfe components
|
|
141
|
+
for (let i = 0; i < mfeNames.length; i++) {
|
|
142
|
+
const mfeCompName = utils.toCompName(mfeNames[i]);
|
|
143
|
+
await writeFile(
|
|
144
|
+
utils.withExt(mfeCompName, isTypeScript),
|
|
145
|
+
getMfeCompContent(mfeCompName, isTypeScript)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Go to root
|
|
150
|
+
process.chdir("../../");
|
|
151
|
+
// Create env file with specified MFE default ports
|
|
152
|
+
await writeFile(".env", getEnvContent(mfeNames));
|
|
153
|
+
|
|
154
|
+
// Install common and user defined packages
|
|
155
|
+
const packagesList = [
|
|
156
|
+
...DEFAULT_DEPENDENCIES,
|
|
157
|
+
...LIBRARY_PAIR.STYLING[styling],
|
|
158
|
+
...LIBRARY_PAIR.STATE_MANAGEMENT[stateManagement],
|
|
159
|
+
];
|
|
160
|
+
await utils.installPackages(packagesList);
|
|
161
|
+
|
|
162
|
+
// Create readme
|
|
163
|
+
await writeFile("README.md", `# ${projectName}\n${projectDescription}`);
|
|
164
|
+
|
|
165
|
+
console.log(`${projectName} created ✅`);
|
|
166
|
+
}
|
|
167
|
+
static async configureMfe(info, mfeName, index) {
|
|
168
|
+
// Destructure inputs
|
|
169
|
+
const {
|
|
170
|
+
projectName,
|
|
171
|
+
formManagement,
|
|
172
|
+
styling,
|
|
173
|
+
stateManagement,
|
|
174
|
+
isTypeScript,
|
|
175
|
+
} = info;
|
|
176
|
+
// Go inside src
|
|
177
|
+
process.chdir(`${process.cwd()}\\${mfeName}\\src`);
|
|
178
|
+
|
|
179
|
+
// Modify App.jsx or .tsx file
|
|
180
|
+
await writeFile(
|
|
181
|
+
utils.withExt("App", isTypeScript),
|
|
182
|
+
getMfeAppContent(utils.toCompName(mfeName), isTypeScript)
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
// Modify index.jsx or .tsx file
|
|
186
|
+
await writeFile(
|
|
187
|
+
utils.withExt("index", isTypeScript),
|
|
188
|
+
getmfeIndexContent(utils.toCompName(mfeName), projectName, isTypeScript)
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// Drop unnecessary files
|
|
192
|
+
await unlink("App.css");
|
|
193
|
+
await unlink("logo.svg");
|
|
194
|
+
await unlink("index.css");
|
|
195
|
+
|
|
196
|
+
// Go to root
|
|
197
|
+
process.chdir("../");
|
|
198
|
+
|
|
199
|
+
// add config override file
|
|
200
|
+
await writeFile("config-overrides.js", getConfigOverridesContent());
|
|
201
|
+
|
|
202
|
+
const updatedScript = {
|
|
203
|
+
start: `cross-env PORT=900${index} react-app-rewired start`,
|
|
204
|
+
build: "react-app-rewired build",
|
|
205
|
+
test: "react-app-rewired test",
|
|
206
|
+
eject: "react-app-rewired eject",
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// modify package.json scripts
|
|
210
|
+
utils.updateScripts(`${process.cwd()}\\package.json`, updatedScript);
|
|
211
|
+
|
|
212
|
+
// Install common and user defined packages
|
|
213
|
+
const packagesList = [
|
|
214
|
+
...DEFAULT_DEPENDENCIES,
|
|
215
|
+
...LIBRARY_PAIR.STYLING[styling],
|
|
216
|
+
...LIBRARY_PAIR.STATE_MANAGEMENT[stateManagement],
|
|
217
|
+
...LIBRARY_PAIR.FORM_MANAGEMENT[formManagement],
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
await utils.installPackages(packagesList);
|
|
221
|
+
|
|
222
|
+
await utils.installPackages(DEFAULT_DEV_DEPENDENCIES, true);
|
|
223
|
+
|
|
224
|
+
// Create readme
|
|
225
|
+
await writeFile("README.md", `# ${mfeName}`);
|
|
226
|
+
|
|
227
|
+
console.log(`${mfeName} created ✅`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export default utils;
|