neo.mjs 4.1.2 → 4.2.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.
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env node
2
+
3
+ import chalk from 'chalk';
4
+ import { Command } from 'commander/esm.mjs';
5
+ import envinfo from 'envinfo';
6
+ import fs from 'fs-extra';
7
+ import inquirer from 'inquirer';
8
+ import os from 'os';
9
+ import path from 'path';
10
+
11
+ const
12
+ __dirname = path.resolve(),
13
+ cwd = process.cwd(),
14
+ requireJson = path => JSON.parse(fs.readFileSync((path))),
15
+ packageJson = requireJson(path.join(__dirname, 'package.json')),
16
+ program = new Command(),
17
+ programName = `${packageJson.name} add-config`;
18
+
19
+ /**
20
+ * Adds a comma to the last element of the contentArray
21
+ * @param {String[]} contentArray
22
+ * @param {Number} index=contentArray.length - 1
23
+ * @returns {String[]}
24
+ */
25
+ function addComma(contentArray, index=contentArray.length - 1) {
26
+ contentArray[index] += ',';
27
+ return contentArray;
28
+ }
29
+
30
+ /**
31
+ * Adds a config to the given index of the contentArray
32
+ * @param {Object} opts
33
+ * @param {String} opts.configName
34
+ * @param {String} opts.defaultValue
35
+ * @param {String[]} opts.contentArray
36
+ * @param {Boolean} opts.isLastConfig
37
+ * @param {Number} opts.index
38
+ * @param {String} opts.type
39
+ * @returns {String[]}
40
+ */
41
+ function addConfig(opts) {
42
+ if (opts.type === 'String' && opts.defaultValue !== 'null') {
43
+ opts.defaultValue = `'${opts.defaultValue}'`;
44
+ }
45
+
46
+ const config = [
47
+ ' /**',
48
+ ` * @member {${opts.type}} ${opts.configName}_=${opts.defaultValue}`,
49
+ ' */',
50
+ ` ${opts.configName}_: ${opts.defaultValue}`
51
+ ];
52
+
53
+ !opts.isLastConfig && addComma(config);
54
+
55
+ opts.contentArray.splice(opts.index, 0, config.join(os.EOL));
56
+ return opts.contentArray;
57
+ }
58
+
59
+ /**
60
+ * Adds a config hook at the matching index
61
+ * @param {Object} opts
62
+ * @param {String} opts.comment
63
+ * @param {String[]} opts.contentArray
64
+ * @param {String} opts.name
65
+ * @param {Boolean} opts.oldValueParam
66
+ * @param {Boolean} opts.returnValue
67
+ * @param {String} opts.type
68
+ * @returns {String[]}
69
+ */
70
+ function addHook(opts) {
71
+ let contentArray = opts.contentArray,
72
+ i = 0,
73
+ inserted = false,
74
+ len = contentArray.length,
75
+ j, methodName, nextLine,
76
+
77
+ method = [
78
+ '',
79
+ ' /**',
80
+ ` * ${opts.comment}`,
81
+ ` * @param {${opts.type}} value`
82
+ ];
83
+
84
+ if (opts.oldValueParam) {
85
+ method.push(
86
+ ` * @param {${opts.type}} oldValue`
87
+ );
88
+ }
89
+
90
+ if (opts.returnValue) {
91
+ method.push(
92
+ ` * @returns {${opts.type}}`
93
+ );
94
+ }
95
+
96
+ method.push(
97
+ ' * @protected',
98
+ ' */'
99
+ );
100
+
101
+ if (opts.oldValueParam) {
102
+ method.push(
103
+ ` ${opts.name}(value, oldValue) {`
104
+ );
105
+ } else {
106
+ method.push(
107
+ ` ${opts.name}(value) {`
108
+ );
109
+ }
110
+
111
+ if (opts.returnValue) {
112
+ method.push(
113
+ ' return value;'
114
+ );
115
+ } else {
116
+ method.push(
117
+ ' '
118
+ );
119
+ }
120
+
121
+ method.push(
122
+ ' }'
123
+ );
124
+
125
+ for (; i < len; i++) {
126
+ if (contentArray[i].includes('}}')) {
127
+ break;
128
+ }
129
+ }
130
+
131
+ for (; i < len; i++) {
132
+ if (contentArray[i].includes('*/')) {
133
+ nextLine = contentArray[i + 1]
134
+ methodName = nextLine.substring(0, nextLine.indexOf('(')).trim();
135
+
136
+ if (methodName === 'construct') {
137
+ continue;
138
+ }
139
+
140
+ if (methodName > opts.name) {
141
+ for (j=i; j > 0; j--) {
142
+ if (contentArray[j].includes('/**')) {
143
+ contentArray.splice(j - 1, 0, method.join(os.EOL));
144
+ inserted = true;
145
+ break;
146
+ }
147
+ }
148
+ break;
149
+ }
150
+ }
151
+ }
152
+
153
+ if (!inserted) {
154
+ for (i=contentArray.length - 1; i > 0; i--) {
155
+ if (contentArray[i].includes('}')) {
156
+ contentArray.splice(i, 0, method.join(os.EOL));
157
+ break;
158
+ }
159
+ }
160
+ }
161
+
162
+ return contentArray;
163
+ }
164
+
165
+
166
+ /**
167
+ * Makes the first character of a string uppercase
168
+ * @param {String} value
169
+ * @returns {Boolean|String} Returns false for non string inputs
170
+ */
171
+ function capitalize(value) {
172
+ return value[0].toUpperCase() + value.slice(1);
173
+ }
174
+
175
+ program
176
+ .name(programName)
177
+ .version(packageJson.version)
178
+ .option('-i, --info', 'print environment debug info')
179
+ .option('-c, --className <value>')
180
+ .option('-d, --defaultValue <value>')
181
+ .option('-h, --hooks <value>')
182
+ .option('-n, --configName <value>')
183
+ .option('-t, --type <value>')
184
+ .allowUnknownOption()
185
+ .on('--help', () => {
186
+ console.log('\nIn case you have any issues, please create a ticket here:');
187
+ console.log(chalk.cyan(process.env.npm_package_bugs_url));
188
+ })
189
+ .parse(process.argv);
190
+
191
+ const programOpts = program.opts();
192
+
193
+ if (programOpts.info) {
194
+ console.log(chalk.bold('\nEnvironment Info:'));
195
+ console.log(`\n current version of ${packageJson.name}: ${packageJson.version}`);
196
+ console.log(` running from ${cwd}`);
197
+
198
+ envinfo
199
+ .run({
200
+ System : ['OS', 'CPU'],
201
+ Binaries : ['Node', 'npm', 'Yarn'],
202
+ Browsers : ['Chrome', 'Edge', 'Firefox', 'Safari'],
203
+ npmPackages: ['neo.mjs']
204
+ }, {
205
+ duplicates : true,
206
+ showNotFound: true
207
+ })
208
+ .then(console.log);
209
+ } else {
210
+ console.log(chalk.green(programName));
211
+
212
+ let answers = {},
213
+ answer;
214
+
215
+ if (!programOpts.className) {
216
+ answer = await inquirer.prompt({
217
+ type : 'input',
218
+ name : 'className',
219
+ message: 'Please choose the namespace of your class:',
220
+ default: 'Covid.view.MainContainer'
221
+ });
222
+
223
+ Object.assign(answers, answer);
224
+ }
225
+
226
+ let className = programOpts.className || answers.className,
227
+ ns = className.split('.'),
228
+ root = ns.shift().toLowerCase(),
229
+ classPath = path.resolve(cwd, root === 'neo' ? 'src' : `apps/${root}`, `${ns.join('/')}.mjs`);
230
+
231
+ if (!fs.existsSync(path.resolve(classPath))) {
232
+ console.log(chalk.red(`File not found for ${className} => ${classPath}`));
233
+ process.exit(1);
234
+ }
235
+
236
+ if (!programOpts.configName) {
237
+ answer = await inquirer.prompt({
238
+ type : 'input',
239
+ name : 'configName',
240
+ message: 'Please enter a name for your class config:'
241
+ });
242
+
243
+ Object.assign(answers, answer);
244
+ }
245
+
246
+ let configName = programOpts.configName || answers.configName;
247
+
248
+ if (configName.endsWith('_')) {
249
+ configName = configName.slice(0, -1);
250
+ }
251
+
252
+ let uConfigName = capitalize(configName);
253
+
254
+ if (!programOpts.type) {
255
+ answer = await inquirer.prompt({
256
+ type : 'list',
257
+ name : 'type',
258
+ message: 'Please choose a type for your class config:',
259
+ default: 'Custom',
260
+ choices: [
261
+ 'Custom',
262
+ 'Object',
263
+ 'Object[]',
264
+ 'Number',
265
+ 'Number[]',
266
+ 'String',
267
+ 'String[]'
268
+ ]
269
+ });
270
+
271
+ Object.assign(answers, answer);
272
+ }
273
+
274
+ if (answers.type === 'Custom') {
275
+ answer = await inquirer.prompt({
276
+ type : 'input',
277
+ name : 'type',
278
+ message: 'Please enter the type for your class config:'
279
+ });
280
+
281
+ Object.assign(answers, answer);
282
+ }
283
+
284
+ if (!programOpts.defaultValue) {
285
+ answer = await inquirer.prompt({
286
+ type : 'input',
287
+ name : 'defaultValue',
288
+ message: 'Please enter a defaultValue:',
289
+ default: 'null'
290
+ });
291
+
292
+ Object.assign(answers, answer);
293
+ }
294
+
295
+ if (!programOpts.hooks) {
296
+ answer = await inquirer.prompt({
297
+ type : 'checkbox',
298
+ name : 'hooks',
299
+ message: 'Please choose the hooks for your class config:',
300
+ choices: [`afterSet${uConfigName}()`, `beforeGet${uConfigName}()`, `beforeSet${uConfigName}()`],
301
+ default: [`afterSet${uConfigName}()`]
302
+ });
303
+
304
+ Object.assign(answers, answer);
305
+ }
306
+
307
+ let defaultValue = programOpts.defaultValue || answers.defaultValue,
308
+ hooks = programOpts.hooks || answers.hooks,
309
+ type = programOpts.type || answers.type,
310
+ contentArray = fs.readFileSync(classPath).toString().split(os.EOL),
311
+ i = 0,
312
+ len = contentArray.length,
313
+ codeLine, existingConfigName, j, nextLine;
314
+
315
+ for (; i < len; i++) {
316
+ if (contentArray[i].includes('static getConfig')) {
317
+ break;
318
+ }
319
+ }
320
+
321
+ for (; i < len; i++) {
322
+ codeLine = contentArray[i];
323
+
324
+ if (codeLine.includes('}}')) {
325
+ addComma(contentArray, i - 1);
326
+ addConfig({
327
+ configName,
328
+ defaultValue,
329
+ contentArray,
330
+ index : i,
331
+ isLastConfig: true,
332
+ type
333
+ });
334
+ break;
335
+ }
336
+
337
+ if (codeLine.includes('*/')) {
338
+ nextLine = contentArray[i + 1]
339
+ existingConfigName = nextLine.substring(0, nextLine.indexOf(':')).trim();
340
+
341
+ if (existingConfigName === 'className' || existingConfigName === 'ntype') {
342
+ continue;
343
+ }
344
+
345
+ if (existingConfigName > configName) {
346
+ for (j=i; j > 0; j--) {
347
+ if (contentArray[j].includes('/**')) {
348
+ addConfig({
349
+ configName,
350
+ contentArray,
351
+ defaultValue,
352
+ index : j,
353
+ isLastConfig: false,
354
+ type
355
+ });
356
+ break;
357
+ }
358
+ }
359
+ break;
360
+ }
361
+ }
362
+ }
363
+
364
+ if (hooks.includes(`afterSet${uConfigName}()`)) {
365
+ addHook({
366
+ comment : `Triggered after the ${configName} config got changed`,
367
+ contentArray,
368
+ name : `afterSet${uConfigName}`,
369
+ oldValueParam: true,
370
+ returnValue : false,
371
+ type
372
+ });
373
+ }
374
+
375
+ if (hooks.includes(`beforeGet${uConfigName}()`)) {
376
+ addHook({
377
+ comment : `Gets triggered when accessing the value of the ${configName} config`,
378
+ contentArray,
379
+ name : `beforeGet${uConfigName}`,
380
+ oldValueParam: false,
381
+ returnValue : true,
382
+ type
383
+ });
384
+ }
385
+
386
+ if (hooks.includes(`beforeSet${uConfigName}()`)) {
387
+ addHook({
388
+ comment : `Triggered before the ${configName} config gets changed`,
389
+ contentArray,
390
+ name : `beforeSet${uConfigName}`,
391
+ oldValueParam: true,
392
+ returnValue : true,
393
+ type
394
+ });
395
+ }
396
+
397
+ fs.writeFileSync(classPath, contentArray.join(os.EOL));
398
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -11,6 +11,7 @@
11
11
  "neo-cc": "./buildScripts/createClass.mjs"
12
12
  },
13
13
  "scripts": {
14
+ "add-config": "node ./buildScripts/addConfig.mjs",
14
15
  "build-all": "node ./buildScripts/buildAll.mjs -f -n",
15
16
  "build-all-questions": "node ./buildScripts/buildAll.mjs -f",
16
17
  "build-my-apps": "node ./buildScripts/webpack/buildMyApps.mjs -f",