neo.mjs 4.0.51 → 4.0.52

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.
@@ -1,24 +1,42 @@
1
- import chalk from 'chalk';
2
- import { Command } from 'commander/esm.mjs';
3
- import envinfo from 'envinfo';
4
- import fs from 'fs-extra';
5
- import inquirer from 'inquirer';
6
- import os from 'os';
7
- import path from 'path';
8
-
9
- const __dirname = path.resolve(),
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
+ import {fileURLToPath} from 'url';
11
+
12
+ const
13
+ __dirname = fileURLToPath(new URL('../', import.meta.url)),
10
14
  cwd = process.cwd(),
11
15
  requireJson = path => JSON.parse(fs.readFileSync((path))),
12
16
  packageJson = requireJson(path.join(__dirname, 'package.json')),
13
17
  insideNeo = packageJson.name === 'neo.mjs',
14
18
  program = new Command(),
15
19
  programName = `${packageJson.name} create-class`,
16
- questions = [];
20
+ questions = [],
21
+ /**
22
+ * Maintain a list of dir-names recognized as source root directories.
23
+ * When not using dot notation with a class-name, the program assumes
24
+ * that we want to create the class inside the cwd. The proper namespace
25
+ * is then looked up by traversing the directory path up to the first
26
+ * folder that matches an entry in "sourceRootDirs". The owning
27
+ * folder (parent of cwd, child of sourceRootDirs[n]) will then be used as the
28
+ * namespace for this created class.
29
+ * Can be overwritten with the -s option.
30
+ * @type {string[]}
31
+ */
32
+ sourceRootDirs = ['apps'];
17
33
 
18
34
  program
19
35
  .name(programName)
20
36
  .version(packageJson.version)
21
37
  .option('-i, --info', 'print environment debug info')
38
+ .option('-d, --drop', 'drops class in the currently selected folder')
39
+ .option('-s, --source <value>', `name of the folder containing the project. Defaults to any of ${sourceRootDirs.join(',')}`)
22
40
  .option('-b, --baseClass <value>')
23
41
  .option('-c, --className <value>')
24
42
  .allowUnknownOption()
@@ -33,7 +51,7 @@ const programOpts = program.opts();
33
51
  if (programOpts.info) {
34
52
  console.log(chalk.bold('\nEnvironment Info:'));
35
53
  console.log(`\n current version of ${packageJson.name}: ${packageJson.version}`);
36
- console.log(` running from ${__dirname}`);
54
+ console.log(` running from ${cwd}`);
37
55
 
38
56
  envinfo
39
57
  .run({
@@ -49,6 +67,28 @@ if (programOpts.info) {
49
67
  } else {
50
68
  console.log(chalk.green(programName));
51
69
 
70
+ if (programOpts.drop) {
71
+ // change source folder if the user wants to
72
+ if (programOpts.source) {
73
+ while (sourceRootDirs.length) {
74
+ sourceRootDirs.pop();
75
+ }
76
+ sourceRootDirs.push(programOpts.source);
77
+ }
78
+
79
+ if (!programOpts.className || !programOpts.baseClass) {
80
+ console.error(chalk.red('-d is non interactive. Please provide name base class, and optionally the source parent for the class to create'));
81
+ console.info(chalk.bgCyan('Usage: createClass -d -c <className> -b <baseClass> [-s sourceParent]'));
82
+ process.exit(1);
83
+ }
84
+
85
+ if (programOpts.className.indexOf('.') !== -1) {
86
+ console.error(chalk.red('No .dot-notation avcailable when -d option is selected.'));
87
+ console.info(chalk.bgCyan('Usage: createClass -d -c <className> -b <baseClass> [-s sourceParent]'));
88
+ process.exit(1);
89
+ }
90
+ }
91
+
52
92
  if (!programOpts.className) {
53
93
  questions.push({
54
94
  type : 'input',
@@ -71,6 +111,7 @@ if (programOpts.info) {
71
111
  inquirer.prompt(questions).then(answers => {
72
112
  let baseClass = programOpts.baseClass || answers.baseClass,
73
113
  className = programOpts.className || answers.className,
114
+ isDrop = programOpts.drop,
74
115
  startDate = new Date(),
75
116
  classFolder, file, folderDelta, index, ns, root, rootLowerCase, viewFile;
76
117
 
@@ -78,36 +119,97 @@ if (programOpts.info) {
78
119
  className = className.slice(0, -4);
79
120
  }
80
121
 
81
- ns = className.split('.');
82
- file = ns.pop();
83
- root = ns.shift();
84
- rootLowerCase = root.toLowerCase();
122
+ if (!isDrop) {
123
+ ns = className.split('.');
124
+ file = ns.pop();
125
+ root = ns.shift();
126
+ rootLowerCase = root.toLowerCase();
127
+ }
85
128
 
86
129
  if (root === 'Neo') {
87
130
  console.log('todo: create the file inside the src folder');
88
131
  } else {
89
- if (fs.existsSync(path.resolve(cwd, 'apps', rootLowerCase))) {
90
- classFolder = path.resolve(cwd, 'apps', rootLowerCase, ns.join('/'));
132
+ if (isDrop === true) {
133
+ ns = [];
134
+
135
+ let pathInfo = path.parse(cwd),
136
+ sep = path.sep,
137
+ baseName, loc = baseName = '',
138
+ tmpNs;
139
+
140
+ sourceRootDirs.some(dir => {
141
+ loc = cwd;
142
+ tmpNs = [];
143
+
144
+ while (pathInfo.root !== loc) {
145
+ baseName = path.resolve(loc, './').split(sep).pop();
146
+
147
+ if (baseName === dir) {
148
+ ns = tmpNs.reverse();
149
+ classFolder = path.resolve(loc, ns.join(sep));
150
+ file = className;
151
+ className = ns.concat(className).join('.');
152
+ loc = path.resolve(loc, ns.join(sep));
153
+ return true;
154
+ }
155
+
156
+ tmpNs.push(baseName);
157
+ loc = path.resolve(loc, '../');
158
+ }
159
+ });
160
+
161
+ if (!ns.length) {
162
+ console.error(chalk.red(
163
+ 'Could not determine namespace for application. Did you provide the ' +
164
+ `correct source parent with -s? (was: ${sourceRootDirs.join(',')}`));
165
+ process.exit(1);
166
+ }
167
+
168
+ console.info(
169
+ chalk.yellow(`Creating ${chalk.bgGreen(className)} extending ${chalk.bgGreen(baseClass)} in ${loc}${sep}${file}.mjs`)
170
+ );
171
+
172
+ let delta_l = path.normalize(__dirname),
173
+ delta_r = path.normalize(loc);
174
+
175
+ if (delta_r.indexOf(delta_l) !== 0) {
176
+ console.error(chalk.red(`Could not determine ${loc} being a child of ${__dirname}`));
177
+ process.exit(1);
178
+ }
179
+
180
+ let delta = delta_r.replace(delta_l, ''),
181
+ parts = delta.split(sep);
182
+
183
+ folderDelta = parts.length;
184
+ }
185
+
186
+ if (isDrop !== true) {
187
+ if (fs.existsSync(path.resolve(__dirname, 'apps', rootLowerCase))) {
188
+ classFolder = path.resolve(__dirname, 'apps', rootLowerCase, ns.join('/'));
189
+ } else {
190
+ console.log('\nNon existing neo app name:', chalk.red(root));
191
+ process.exit(1);
192
+ }
193
+ }
194
+
195
+ if (folderDelta === undefined) {
91
196
  folderDelta = ns.length + 2;
197
+ }
92
198
 
93
- fs.mkdirpSync(classFolder);
199
+ fs.mkdirpSync(classFolder);
94
200
 
95
- fs.writeFileSync(path.join(classFolder, file + '.mjs'), createContent({baseClass, className, file, folderDelta, ns, root}));
201
+ fs.writeFileSync(path.join(classFolder, file + '.mjs'), createContent({baseClass, className, file, folderDelta, ns, root}));
96
202
 
97
- if (baseClass === 'controller.Component') {
98
- index = file.indexOf('Controller');
203
+ if (baseClass === 'controller.Component') {
204
+ index = file.indexOf('Controller');
99
205
 
100
- if (index > 0) {
101
- viewFile = path.join(classFolder, file.substr(0, index) + '.mjs');
206
+ if (index > 0) {
207
+ viewFile = path.join(classFolder, file.substr(0, index) + '.mjs');
102
208
 
103
- if (fs.existsSync(viewFile)) {
104
- adjustView({file, viewFile});
105
- }
209
+ if (fs.existsSync(viewFile)) {
210
+ adjustView({file, viewFile});
106
211
  }
107
212
  }
108
- } else {
109
- console.log('\nNon existing neo app name:', chalk.red(root));
110
- process.exit(1);
111
213
  }
112
214
  }
113
215
 
@@ -97,6 +97,22 @@ class MainContainer extends ConfigurationViewport {
97
97
  minValue : 50,
98
98
  stepSize : 5,
99
99
  value : me.exampleComponent.labelWidth
100
+ }, {
101
+ module : NumberField,
102
+ labelText: 'maxLength',
103
+ listeners: {change: me.onConfigChange.bind(me, 'maxLength')},
104
+ maxValue : 50,
105
+ minValue : 1,
106
+ stepSize : 1,
107
+ value : me.exampleComponent.maxLength
108
+ }, {
109
+ module : NumberField,
110
+ labelText: 'minLength',
111
+ listeners: {change: me.onConfigChange.bind(me, 'minLength')},
112
+ maxValue : 50,
113
+ minValue : 1,
114
+ stepSize : 1,
115
+ value : me.exampleComponent.minLength
100
116
  }, {
101
117
  module : TextField,
102
118
  clearable: true,
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "4.0.51",
3
+ "version": "4.0.52",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/neomjs/neo.git"
9
9
  },
10
+ "bin": {
11
+ "neo-cc": "./buildScripts/createClass.mjs"
12
+ },
10
13
  "scripts": {
11
14
  "build-all": "node ./buildScripts/buildAll.mjs -f -n",
12
15
  "build-all-questions": "node ./buildScripts/buildAll.mjs -f",
@@ -51,7 +54,7 @@
51
54
  "neo-jsdoc": "^1.0.1",
52
55
  "neo-jsdoc-x": "^1.0.4",
53
56
  "postcss": "^8.4.14",
54
- "sass": "^1.52.3",
57
+ "sass": "^1.53.0",
55
58
  "webpack": "^5.73.0",
56
59
  "webpack-cli": "^4.10.0",
57
60
  "webpack-dev-server": "4.9.2",
@@ -59,7 +62,8 @@
59
62
  "webpack-node-externals": "^3.0.0"
60
63
  },
61
64
  "devDependencies": {
62
- "siesta-lite": "^5.5.2"
65
+ "siesta-lite": "^5.5.2",
66
+ "url": "^0.11.0"
63
67
  },
64
68
  "funding": {
65
69
  "type": "GitHub Sponsors",
@@ -28,9 +28,9 @@ class RecordFactory extends Base {
28
28
  */
29
29
  ovPrefix: 'ov_',
30
30
  /**
31
- * @member {String} recordNamespace='Neo.data.record.'
31
+ * @member {String} recordNamespace='Neo.data.record'
32
32
  */
33
- recordNamespace: 'Neo.data.record.'
33
+ recordNamespace: 'Neo.data.record'
34
34
  }}
35
35
 
36
36
  /**
@@ -39,7 +39,7 @@ class RecordFactory extends Base {
39
39
  * @returns {Object}
40
40
  */
41
41
  createRecord(model, config) {
42
- let recordClass = Neo.ns(this.recordNamespace + model.className);
42
+ let recordClass = Neo.ns(`${this.recordNamespace}.${model.className}.${model.id}`);
43
43
 
44
44
  if (!recordClass) {
45
45
  recordClass = this.createRecordClass(model);
@@ -54,7 +54,7 @@ class RecordFactory extends Base {
54
54
  */
55
55
  createRecordClass(model) {
56
56
  if (model instanceof Model) {
57
- let className = this.recordNamespace + model.className,
57
+ let className = `${this.recordNamespace}.${model.className}.${model.id}`,
58
58
  ns = Neo.ns(className),
59
59
  key, nsArray;
60
60
 
@@ -76,7 +76,7 @@ class RecordFactory extends Base {
76
76
 
77
77
  if (Array.isArray(model.fields)) {
78
78
  model.fields.forEach(field => {
79
- let parsedValue = instance.parseRecordValue(field, config[field.name], config),
79
+ let parsedValue = instance.parseRecordValue(me, field, config[field.name], config),
80
80
  symbol = Symbol.for(field.name);
81
81
 
82
82
  properties = {
@@ -97,9 +97,9 @@ class RecordFactory extends Base {
97
97
  let me = this,
98
98
  oldValue = me[symbol];
99
99
 
100
- if (!Neo.isEqual(value, oldValue)) {
101
- value = instance.parseRecordValue(field, value, null);
100
+ value = instance.parseRecordValue(me, field, value);
102
101
 
102
+ if (!Neo.isEqual(value, oldValue)) {
103
103
  me[symbol] = value;
104
104
 
105
105
  me._isModified = true;
@@ -221,14 +221,18 @@ class RecordFactory extends Base {
221
221
 
222
222
  /**
223
223
  * todo: parse value for more field types
224
+ * @param {Object} record
224
225
  * @param {Object} field
225
226
  * @param {*} value
226
- * @param {Object} recordConfig
227
+ * @param {Object} recordConfig=null
227
228
  * @returns {*}
228
229
  */
229
- parseRecordValue(field, value, recordConfig) {
230
- let mapping = field.mapping,
231
- type = field.type?.toLowerCase();
230
+ parseRecordValue(record, field, value, recordConfig=null) {
231
+ let mapping = field.mapping,
232
+ maxLength = field.maxLength,
233
+ minLength = field.minLength,
234
+ oldValue = recordConfig?.[field.name] || record[field.name],
235
+ type = field.type?.toLowerCase();
232
236
 
233
237
  // only trigger mappings for initial values
234
238
  // dynamic changes of a field will not pass the recordConfig
@@ -240,7 +244,21 @@ class RecordFactory extends Base {
240
244
  value = ns[key];
241
245
  }
242
246
 
243
- if (type === 'date') {
247
+ if (Object.hasOwn(field, maxLength)) {
248
+ if (value?.toString() > maxLength) {
249
+ console.warn(`Setting record field: ${field} value: ${value} conflicts with the maxLength: ${maxLength}`);
250
+ return oldValue;
251
+ }
252
+ }
253
+
254
+ if (Object.hasOwn(field, minLength)) {
255
+ if (value?.toString() < minLength) {
256
+ console.warn(`Setting record field: ${field} value: ${value} conflicts with the minLength: ${minLength}`);
257
+ return oldValue;
258
+ }
259
+ }
260
+
261
+ if (type === 'date' && Neo.typeOf(value) !== 'Date') {
244
262
  return new Date(value);
245
263
  }
246
264
 
@@ -259,6 +277,7 @@ class RecordFactory extends Base {
259
277
 
260
278
  Object.entries(fields).forEach(([key, value]) => {
261
279
  oldValue = record[key];
280
+ value = instance.parseRecordValue(record, model.getField(key), value);
262
281
 
263
282
  if (!Neo.isEqual(oldValue, value)) {
264
283
  record[Symbol.for(key)] = value; // silent update
@@ -237,12 +237,13 @@ class Number extends Text {
237
237
  */
238
238
  onSpinButtonDownClick() {
239
239
  let me = this,
240
- oldValue = me.value || (me.maxValue + me.stepSize),
241
- value = Math.max(me.minValue, oldValue - me.stepSize);
240
+ stepSize = me.stepSize,
241
+ oldValue = Neo.isNumber(me.value) ? me.value : me.minValue,
242
+ value = (oldValue - stepSize) < me.minValue ? me.maxValue : (oldValue - stepSize);
242
243
 
243
244
  if (me.excludedValues) {
244
245
  while(me.excludedValues.includes(value)) {
245
- value = Math.max(me.minValue, value - me.stepSize);
246
+ value = Math.max(me.minValue, value - stepSize);
246
247
  }
247
248
  }
248
249
 
@@ -256,12 +257,13 @@ class Number extends Text {
256
257
  */
257
258
  onSpinButtonUpClick() {
258
259
  let me = this,
259
- oldValue = me.value || (me.minValue - me.stepSize),
260
- value = Math.min(me.maxValue, oldValue + me.stepSize);
260
+ stepSize = me.stepSize,
261
+ oldValue = Neo.isNumber(me.value) ? me.value : me.maxValue,
262
+ value = (oldValue + stepSize) > me.maxValue ? me.minValue : (oldValue + stepSize);
261
263
 
262
264
  if (me.excludedValues) {
263
265
  while(me.excludedValues.includes(value)) {
264
- value = Math.min(me.maxValue, value + me.stepSize);
266
+ value = Math.min(me.maxValue, value + stepSize);
265
267
  }
266
268
  }
267
269
 
@@ -186,11 +186,15 @@ class DeltaUpdates extends Base {
186
186
  }
187
187
  } else if (key === 'id') {
188
188
  node[Neo.config.useDomIds ? 'id' : 'data-neo-id'] = val;
189
- }else if (key === 'spellcheck' && val === 'false') {
189
+ } else if (key === 'spellcheck' && val === 'false') {
190
190
  // see https://github.com/neomjs/neo/issues/1922
191
191
  node[key] = false;
192
192
  } else {
193
- node[key] = val;
193
+ if (key === 'value') {
194
+ node[key] = val;
195
+ } else {
196
+ node.setAttribute(key, val);
197
+ }
194
198
  }
195
199
  });
196
200
  break;
@@ -247,9 +247,9 @@ class Container extends BaseContainer {
247
247
  }
248
248
 
249
249
  if (value) {
250
- let me = this;
250
+ let me = this,
251
251
 
252
- const listeners = {
252
+ listeners = {
253
253
  filter : me.onStoreFilter,
254
254
  load : me.onStoreLoad,
255
255
  recordChange: me.onStoreRecordChange,
@@ -258,13 +258,10 @@ class Container extends BaseContainer {
258
258
 
259
259
  if (value instanceof Store) {
260
260
  value.on(listeners);
261
-
262
- if (value.getCount() > 0) {
263
- me.onStoreLoad(value.items);
264
- }
261
+ value.getCount() > 0 && me.onStoreLoad(value.items);
265
262
  } else {
266
263
  value = ClassSystemUtil.beforeSetInstance(value, Store, {
267
- listeners: listeners
264
+ listeners
268
265
  });
269
266
  }
270
267