neo.mjs 4.0.63 → 4.0.66
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 +3 -3
- package/buildScripts/createClass.mjs +221 -47
- package/package.json +1 -1
- package/src/core/Util.mjs +4 -4
- package/src/form/field/Select.mjs +29 -19
package/README.md
CHANGED
@@ -236,12 +236,12 @@ More infos: <a href="./BACKERS.md">Sponsors & Backers</a>
|
|
236
236
|
</br></br>
|
237
237
|
<h2 id="jobs">14. Jobs</h2>
|
238
238
|
Accenture is hiring multiple neo.mjs developers for the new Cloud Technology Studio in Kaiserslauern (Germany):</br>
|
239
|
-
|
240
|
-
|
239
|
+
</br>
|
241
240
|
These full-time roles are based on German contracts, so they require living in (or relocating to) Germany.
|
241
|
+
Ping us on LinkedIn or Slack for details.
|
242
242
|
|
243
243
|
</br></br>
|
244
|
-
Logo contributed by <a href="https://www.linkedin.com/in/dinkheller/">Torsten Dinkheller</a>.
|
244
|
+
Logo contributed by <a href="https://www.linkedin.com/in/torsten-dinkheller-614516231/">Torsten Dinkheller</a>.
|
245
245
|
|
246
246
|
</br></br>
|
247
247
|
Build with :heart: in Germany.
|
@@ -14,7 +14,7 @@ const
|
|
14
14
|
cwd = process.cwd(),
|
15
15
|
requireJson = path => JSON.parse(fs.readFileSync((path))),
|
16
16
|
packageJson = requireJson(path.join(__dirname, 'package.json')),
|
17
|
-
insideNeo =
|
17
|
+
insideNeo = process.env.npm_package_name === 'neo.mjs',
|
18
18
|
program = new Command(),
|
19
19
|
programName = `${packageJson.name} create-class`,
|
20
20
|
questions = [],
|
@@ -36,6 +36,7 @@ program
|
|
36
36
|
.version(packageJson.version)
|
37
37
|
.option('-i, --info', 'print environment debug info')
|
38
38
|
.option('-d, --drop', 'drops class in the currently selected folder')
|
39
|
+
.option('-n, --singleton <value>', 'Create a singleton? Pick "yes" or "no"')
|
39
40
|
.option('-s, --source <value>', `name of the folder containing the project. Defaults to any of ${sourceRootDirs.join(',')}`)
|
40
41
|
.option('-b, --baseClass <value>')
|
41
42
|
.option('-c, --className <value>')
|
@@ -94,7 +95,7 @@ if (programOpts.info) {
|
|
94
95
|
type : 'input',
|
95
96
|
name : 'className',
|
96
97
|
message: 'Please choose the namespace for your class:',
|
97
|
-
default: 'Covid.view.
|
98
|
+
default: 'Covid.view.MyContainer'
|
98
99
|
});
|
99
100
|
}
|
100
101
|
|
@@ -103,17 +104,38 @@ if (programOpts.info) {
|
|
103
104
|
type : 'list',
|
104
105
|
name : 'baseClass',
|
105
106
|
message: 'Please pick the base class, which you want to extend:',
|
106
|
-
|
107
|
-
|
107
|
+
default: 'container.Base',
|
108
|
+
|
109
|
+
choices: [
|
110
|
+
'component.Base',
|
111
|
+
'container.Base',
|
112
|
+
'controller.Component',
|
113
|
+
'core.Base',
|
114
|
+
'data.Model',
|
115
|
+
'data.Store',
|
116
|
+
'model.Component'
|
117
|
+
]
|
118
|
+
});
|
119
|
+
}
|
120
|
+
|
121
|
+
if (!programOpts.singleton) {
|
122
|
+
questions.push({
|
123
|
+
type : 'list',
|
124
|
+
name : 'singleton',
|
125
|
+
message: 'Singleton?',
|
126
|
+
default: 'no',
|
127
|
+
choices: ['yes', 'no']
|
108
128
|
});
|
109
129
|
}
|
110
130
|
|
111
131
|
inquirer.prompt(questions).then(answers => {
|
112
|
-
let baseClass
|
113
|
-
className
|
114
|
-
|
115
|
-
|
116
|
-
|
132
|
+
let baseClass = programOpts.baseClass || answers.baseClass,
|
133
|
+
className = programOpts.className || answers.className,
|
134
|
+
singleton = programOpts.singleton || answers.singleton || 'no',
|
135
|
+
isDrop = programOpts.drop,
|
136
|
+
isSingleton = singleton === 'yes',
|
137
|
+
startDate = new Date(),
|
138
|
+
baseFileName, baseType, classFolder, configName, file, folderDelta, importName, importPath, index, ns, root, rootLowerCase, viewFile;
|
117
139
|
|
118
140
|
if (className.endsWith('.mjs')) {
|
119
141
|
className = className.slice(0, -4);
|
@@ -184,8 +206,8 @@ if (programOpts.info) {
|
|
184
206
|
}
|
185
207
|
|
186
208
|
if (isDrop !== true) {
|
187
|
-
if (fs.existsSync(path.resolve(
|
188
|
-
classFolder = path.resolve(
|
209
|
+
if (fs.existsSync(path.resolve(cwd, 'apps', rootLowerCase))) {
|
210
|
+
classFolder = path.resolve(cwd, 'apps', rootLowerCase, ns.join('/'));
|
189
211
|
} else {
|
190
212
|
console.log('\nNon existing neo app name:', chalk.red(root));
|
191
213
|
process.exit(1);
|
@@ -198,17 +220,86 @@ if (programOpts.info) {
|
|
198
220
|
|
199
221
|
fs.mkdirpSync(classFolder);
|
200
222
|
|
201
|
-
|
223
|
+
baseFileName = baseClass.split('.').pop();
|
224
|
+
|
225
|
+
if (baseFileName === file) {
|
226
|
+
baseFileName = baseClass.split('.');
|
227
|
+
baseFileName = baseFileName.map(e => capitalize(e)).join('');
|
228
|
+
}
|
229
|
+
|
230
|
+
fs.writeFileSync(path.join(classFolder, file + '.mjs'), createContent({
|
231
|
+
baseClass,
|
232
|
+
baseFileName,
|
233
|
+
className,
|
234
|
+
isSingleton,
|
235
|
+
file,
|
236
|
+
folderDelta,
|
237
|
+
ns,
|
238
|
+
root
|
239
|
+
}));
|
240
|
+
|
241
|
+
switch(baseClass) {
|
242
|
+
case 'controller.Component': {
|
243
|
+
baseType = 'Neo.controller.Component';
|
244
|
+
configName = 'controller';
|
245
|
+
importName = file;
|
246
|
+
importPath = `./${importName}.mjs`;
|
247
|
+
index = file.indexOf('Controller');
|
248
|
+
|
249
|
+
if (index > 0) {
|
250
|
+
viewFile = path.join(classFolder, file.substr(0, index) + '.mjs');
|
251
|
+
|
252
|
+
if (fs.existsSync(viewFile)) {
|
253
|
+
adjustView({baseType, configName, importName, importPath, viewFile});
|
254
|
+
}
|
255
|
+
}
|
256
|
+
break;
|
257
|
+
}
|
202
258
|
|
203
|
-
|
204
|
-
|
259
|
+
case 'data.Store': {
|
260
|
+
baseType = 'Neo.data.Model';
|
261
|
+
configName = 'model';
|
262
|
+
importName = className.replace('.store.', '.model.');
|
205
263
|
|
206
|
-
|
207
|
-
|
264
|
+
if (importName.endsWith('ies')) {
|
265
|
+
importName.replace(new RegExp('ies$'), 'y')
|
266
|
+
} else {
|
267
|
+
importName = importName.slice(0, -1);
|
268
|
+
}
|
269
|
+
|
270
|
+
viewFile = importName.split('.');
|
271
|
+
viewFile.shift();
|
208
272
|
|
273
|
+
importPath = `../${viewFile.join('/')}.mjs`;
|
274
|
+
viewFile = path.join(classFolder, importPath);
|
275
|
+
|
276
|
+
// checking for the data.Model file
|
209
277
|
if (fs.existsSync(viewFile)) {
|
210
|
-
|
278
|
+
// adjusting the data.Store file
|
279
|
+
viewFile = path.join(classFolder, file + '.mjs');
|
280
|
+
importName = importName.split('.');
|
281
|
+
importName = importName.pop();
|
282
|
+
|
283
|
+
adjustView({baseType, configName, importName, importPath, viewFile});
|
211
284
|
}
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
|
288
|
+
case 'model.Component': {
|
289
|
+
baseType = 'Neo.model.Component';
|
290
|
+
configName = 'model';
|
291
|
+
importName = file;
|
292
|
+
importPath = `./${importName}.mjs`;
|
293
|
+
index = file.indexOf('Model');
|
294
|
+
|
295
|
+
if (index > 0) {
|
296
|
+
viewFile = path.join(classFolder, file.substr(0, index) + '.mjs');
|
297
|
+
|
298
|
+
if (fs.existsSync(viewFile)) {
|
299
|
+
adjustView({baseType, configName, importName, importPath, viewFile});
|
300
|
+
}
|
301
|
+
}
|
302
|
+
break;
|
212
303
|
}
|
213
304
|
}
|
214
305
|
}
|
@@ -222,41 +313,58 @@ if (programOpts.info) {
|
|
222
313
|
/**
|
223
314
|
* Adds a comma to the last element of the contentArray
|
224
315
|
* @param {String[]} contentArray
|
316
|
+
* @param {Number} index=contentArray.length - 1
|
225
317
|
* @returns {String[]}
|
226
318
|
*/
|
227
|
-
function addComma(contentArray) {
|
228
|
-
contentArray[
|
319
|
+
function addComma(contentArray, index=contentArray.length - 1) {
|
320
|
+
contentArray[index] += ',';
|
229
321
|
return contentArray;
|
230
322
|
}
|
231
323
|
|
232
|
-
|
324
|
+
/**
|
325
|
+
* Adds a config to the given index of the contentArray
|
326
|
+
* @param {Object} opts
|
327
|
+
* @param {String} opts.baseType
|
328
|
+
* @param {String} opts.className
|
329
|
+
* @param {String} opts.configName
|
330
|
+
* @param {String[]} opts.contentArray
|
331
|
+
* @param {Boolean} opts.isLastConfig
|
332
|
+
* @param {Number} opts.index
|
333
|
+
* @returns {String[]}
|
334
|
+
*/
|
335
|
+
function addConfig(opts) {
|
233
336
|
const config = [
|
234
337
|
' /**',
|
235
|
-
` * @member {
|
338
|
+
` * @member {${opts.baseType}} ${opts.configName}=${opts.className}`,
|
236
339
|
' */',
|
237
|
-
`
|
340
|
+
` ${opts.configName}: ${opts.className}`
|
238
341
|
];
|
239
342
|
|
240
|
-
!isLastConfig && addComma(config);
|
343
|
+
!opts.isLastConfig && addComma(config);
|
241
344
|
|
242
|
-
contentArray.splice(index, 0, config.join(os.EOL));
|
243
|
-
return contentArray;
|
345
|
+
opts.contentArray.splice(opts.index, 0, config.join(os.EOL));
|
346
|
+
return opts.contentArray;
|
244
347
|
}
|
245
348
|
|
246
349
|
/**
|
247
350
|
* Adjusts the views related to controller.Component or model.Component
|
248
351
|
* @param {Object} opts
|
249
|
-
* @param {String} opts.
|
352
|
+
* @param {String} opts.baseType
|
353
|
+
* @param {String} opts.configName
|
354
|
+
* @param {String} opts.importName
|
355
|
+
* @param {String} opts.importPath
|
250
356
|
* @param {String} opts.viewFile
|
251
357
|
*/
|
252
358
|
function adjustView(opts) {
|
253
|
-
let
|
359
|
+
let baseType = opts.baseType,
|
360
|
+
configName = opts.configName,
|
361
|
+
importName = opts.importName,
|
254
362
|
viewFile = opts.viewFile,
|
255
363
|
content = fs.readFileSync(viewFile).toString().split(os.EOL),
|
256
364
|
fromMaxPosition = 0,
|
257
365
|
i = 0,
|
258
366
|
len = content.length,
|
259
|
-
adjustSpaces, className, codeLine, fromPosition, importLength,
|
367
|
+
adjustSpaces, className, codeLine, existingImportName, fromPosition, importLength, j, nextLine, spaces;
|
260
368
|
|
261
369
|
// find the index where we want to insert our import statement
|
262
370
|
for (; i < len; i++) {
|
@@ -266,16 +374,16 @@ if (programOpts.info) {
|
|
266
374
|
break;
|
267
375
|
}
|
268
376
|
|
269
|
-
|
270
|
-
|
271
|
-
importLength
|
377
|
+
existingImportName = codeLine.substr(7);
|
378
|
+
existingImportName = existingImportName.substr(0, existingImportName.indexOf(' '));
|
379
|
+
importLength = existingImportName.length;
|
272
380
|
|
273
|
-
if (
|
381
|
+
if (existingImportName > importName) {
|
274
382
|
break;
|
275
383
|
}
|
276
384
|
}
|
277
385
|
|
278
|
-
content.splice(i, 0, `import ${
|
386
|
+
content.splice(i, 0, `import ${importName} from '${opts.importPath}';`);
|
279
387
|
|
280
388
|
// find the longest import module name
|
281
389
|
for (i=0; i < len; i++) {
|
@@ -324,7 +432,15 @@ if (programOpts.info) {
|
|
324
432
|
codeLine = content[i];
|
325
433
|
|
326
434
|
if (codeLine.includes('}}')) {
|
327
|
-
|
435
|
+
addComma(content, i - 1);
|
436
|
+
addConfig({
|
437
|
+
baseType,
|
438
|
+
className : importName,
|
439
|
+
configName,
|
440
|
+
contentArray: content,
|
441
|
+
index : i,
|
442
|
+
isLastConfig: true
|
443
|
+
});
|
328
444
|
break;
|
329
445
|
}
|
330
446
|
|
@@ -336,10 +452,17 @@ if (programOpts.info) {
|
|
336
452
|
continue;
|
337
453
|
}
|
338
454
|
|
339
|
-
if (className >
|
455
|
+
if (className > configName) {
|
340
456
|
for (j=i; j > 0; j--) {
|
341
457
|
if (content[j].includes('/**')) {
|
342
|
-
addConfig(
|
458
|
+
addConfig({
|
459
|
+
baseType,
|
460
|
+
className : importName,
|
461
|
+
configName,
|
462
|
+
contentArray: content,
|
463
|
+
index : j,
|
464
|
+
isLastConfig: false
|
465
|
+
});
|
343
466
|
break;
|
344
467
|
}
|
345
468
|
}
|
@@ -351,11 +474,22 @@ if (programOpts.info) {
|
|
351
474
|
fs.writeFileSync(viewFile, content.join(os.EOL));
|
352
475
|
}
|
353
476
|
|
477
|
+
/**
|
478
|
+
* Makes the first character of a string uppercase
|
479
|
+
* @param {String} value
|
480
|
+
* @returns {Boolean|String} Returns false for non string inputs
|
481
|
+
*/
|
482
|
+
function capitalize(value) {
|
483
|
+
return typeof value === 'string' && value[0].toUpperCase() + value.slice(1);
|
484
|
+
}
|
485
|
+
|
354
486
|
/**
|
355
487
|
* Creates the content of the neo-class .mjs file
|
356
488
|
* @param {Object} opts
|
357
489
|
* @param {String} opts.baseClass
|
490
|
+
* @param {String} opts.baseFileName
|
358
491
|
* @param {String} opts.className
|
492
|
+
* @param {Boolean} opts.isSingleton
|
359
493
|
* @param {String} opts.file
|
360
494
|
* @param {String} opts.folderDelta
|
361
495
|
* @param {String} opts.ns
|
@@ -363,24 +497,32 @@ if (programOpts.info) {
|
|
363
497
|
* @returns {String}
|
364
498
|
*/
|
365
499
|
function createContent(opts) {
|
366
|
-
let baseClass
|
367
|
-
|
368
|
-
|
369
|
-
className
|
370
|
-
|
371
|
-
|
372
|
-
|
500
|
+
let baseClass = opts.baseClass,
|
501
|
+
baseFileName = opts.baseFileName,
|
502
|
+
baseClassPath = baseClass.split('.').join('/'),
|
503
|
+
className = opts.className,
|
504
|
+
isSingleton = opts.isSingleton,
|
505
|
+
file = opts.file,
|
506
|
+
i = 0,
|
507
|
+
importDelta = '';
|
373
508
|
|
374
509
|
for (; i < opts.folderDelta; i++) {
|
375
510
|
importDelta += '../';
|
376
511
|
}
|
377
512
|
|
378
513
|
let classContent = [
|
379
|
-
`import ${baseFileName} from '${importDelta}${(insideNeo ? '' : 'node_modules/neo.mjs/')}src/${
|
514
|
+
`import ${baseFileName} from '${importDelta}${(insideNeo ? '' : 'node_modules/neo.mjs/')}src/${baseClassPath}.mjs';`,
|
380
515
|
"",
|
381
516
|
"/**",
|
382
517
|
` * @class ${className}`,
|
383
|
-
` * @extends Neo.${baseClass}
|
518
|
+
` * @extends Neo.${baseClass}`
|
519
|
+
];
|
520
|
+
|
521
|
+
isSingleton && classContent.push(
|
522
|
+
" * @singleton"
|
523
|
+
);
|
524
|
+
|
525
|
+
classContent.push(
|
384
526
|
" */",
|
385
527
|
`class ${file} extends ${baseFileName} {`,
|
386
528
|
" static getConfig() {return {",
|
@@ -389,7 +531,17 @@ if (programOpts.info) {
|
|
389
531
|
" * @protected",
|
390
532
|
" */",
|
391
533
|
` className: '${className}'`
|
392
|
-
|
534
|
+
);
|
535
|
+
|
536
|
+
baseClass === 'data.Model' && addComma(classContent).push(
|
537
|
+
" /*",
|
538
|
+
" * @member {Object[]} fields",
|
539
|
+
" */",
|
540
|
+
" fields: [{",
|
541
|
+
" name: 'id',",
|
542
|
+
" type: 'String'",
|
543
|
+
" }]"
|
544
|
+
);
|
393
545
|
|
394
546
|
baseClass === 'container.Base' && addComma(classContent).push(
|
395
547
|
" /*",
|
@@ -398,6 +550,14 @@ if (programOpts.info) {
|
|
398
550
|
" items: []"
|
399
551
|
);
|
400
552
|
|
553
|
+
isSingleton && addComma(classContent).push(
|
554
|
+
" /*",
|
555
|
+
" * @member {Boolean} singleton=true",
|
556
|
+
" * @protected",
|
557
|
+
" */",
|
558
|
+
" singleton: true"
|
559
|
+
);
|
560
|
+
|
401
561
|
baseClass === 'component.Base' && addComma(classContent).push(
|
402
562
|
" /*",
|
403
563
|
" * @member {Object} _vdom",
|
@@ -411,8 +571,22 @@ if (programOpts.info) {
|
|
411
571
|
"}",
|
412
572
|
"",
|
413
573
|
`Neo.applyClassConfig(${file});`,
|
574
|
+
""
|
575
|
+
);
|
576
|
+
|
577
|
+
isSingleton && classContent.push(
|
578
|
+
`let instance = Neo.create(${file});`,
|
579
|
+
"",
|
580
|
+
"Neo.applyToGlobalNs(instance);",
|
414
581
|
"",
|
415
|
-
|
582
|
+
"export default instance;"
|
583
|
+
);
|
584
|
+
|
585
|
+
!isSingleton && classContent.push(
|
586
|
+
`export default ${file};`
|
587
|
+
);
|
588
|
+
|
589
|
+
classContent.push(
|
416
590
|
""
|
417
591
|
);
|
418
592
|
|
package/package.json
CHANGED
package/src/core/Util.mjs
CHANGED
@@ -40,11 +40,11 @@ class Util extends Base {
|
|
40
40
|
|
41
41
|
/**
|
42
42
|
* Makes the first character of a string uppercase
|
43
|
-
* @param {String}
|
43
|
+
* @param {String} value
|
44
44
|
* @returns {Boolean|String} Returns false for non string inputs
|
45
45
|
*/
|
46
|
-
static capitalize(
|
47
|
-
return Util.isString(
|
46
|
+
static capitalize(value) {
|
47
|
+
return Util.isString(value) && value[0].toUpperCase() + value.slice(1);
|
48
48
|
}
|
49
49
|
|
50
50
|
/**
|
@@ -100,7 +100,7 @@ class Util extends Base {
|
|
100
100
|
}
|
101
101
|
|
102
102
|
/**
|
103
|
-
* Transforms all uppercase characters of a string into lowercase.
|
103
|
+
* Transforms all uppercase characters of a string into -lowercase.
|
104
104
|
* Does not touch special characters.
|
105
105
|
* @param {String} value The input containing uppercase characters
|
106
106
|
* @returns {String} The lowercase output
|
@@ -463,24 +463,8 @@ class Select extends Picker {
|
|
463
463
|
* @protected
|
464
464
|
*/
|
465
465
|
onListItemClick(record) {
|
466
|
-
|
467
|
-
|
468
|
-
oldValue = me.value,
|
469
|
-
value = record[displayField];
|
470
|
-
|
471
|
-
if (me.value !== value) {
|
472
|
-
me.hintRecordId = null;
|
473
|
-
me.record = record;
|
474
|
-
me._value = value;
|
475
|
-
me.getInputHintEl().value = null;
|
476
|
-
|
477
|
-
me.afterSetValue(value, oldValue, true); // prevent the list from getting filtered
|
478
|
-
|
479
|
-
me.fire('select', {
|
480
|
-
record,
|
481
|
-
value: record[displayField]
|
482
|
-
});
|
483
|
-
}
|
466
|
+
this.onListItemChange(record);
|
467
|
+
this.hidePicker();
|
484
468
|
}
|
485
469
|
|
486
470
|
/**
|
@@ -518,12 +502,38 @@ class Select extends Picker {
|
|
518
502
|
this.focusInputEl();
|
519
503
|
}
|
520
504
|
|
505
|
+
|
506
|
+
/**
|
507
|
+
* @param {Object} record
|
508
|
+
* @protected
|
509
|
+
*/
|
510
|
+
onListItemChange(record) {
|
511
|
+
let me = this,
|
512
|
+
displayField = me.displayField,
|
513
|
+
oldValue = me.value,
|
514
|
+
value = record[displayField];
|
515
|
+
|
516
|
+
if (me.value !== value) {
|
517
|
+
me.hintRecordId = null;
|
518
|
+
me.record = record;
|
519
|
+
me._value = value;
|
520
|
+
me.getInputHintEl().value = null;
|
521
|
+
|
522
|
+
me.afterSetValue(value, oldValue, true); // prevent the list from getting filtered
|
523
|
+
|
524
|
+
me.fire('select', {
|
525
|
+
record,
|
526
|
+
value: record[displayField]
|
527
|
+
});
|
528
|
+
}
|
529
|
+
}
|
530
|
+
|
521
531
|
/**
|
522
532
|
* @param {Object} record
|
523
533
|
* @protected
|
524
534
|
*/
|
525
535
|
onListItemNavigate(record) {
|
526
|
-
this.
|
536
|
+
this.onListItemChange(record);
|
527
537
|
}
|
528
538
|
|
529
539
|
/**
|