@qooxdoo/framework 8.0.0-beta.1 → 8.0.0-beta.2

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/Manifest.json +1 -1
  3. package/lib/compiler/compile-info.json +55 -55
  4. package/lib/compiler/index.js +18497 -22975
  5. package/lib/resource/qx/tool/compiler/cli/templates/class/default.tmpl.js +6 -7
  6. package/lib/resource/qx/tool/compiler/cli/templates/class/singleton.tmpl.js +5 -6
  7. package/lib/resource/qx/tool/compiler/schema/compile-1-0-0.json +6 -2
  8. package/package.json +8 -10
  9. package/source/class/qx/Class.js +3 -3
  10. package/source/class/qx/core/BaseInit.js +14 -13
  11. package/source/class/qx/core/MObjectId.js +16 -0
  12. package/source/class/qx/core/MProperty.js +147 -175
  13. package/source/class/qx/core/check/DynamicTypeCheck.js +4 -0
  14. package/source/class/qx/core/property/Property.js +20 -19
  15. package/source/class/qx/data/SingleValueBinding.js +5 -7
  16. package/source/class/qx/data/binding/AbstractSegment.js +1 -1
  17. package/source/class/qx/data/binding/ArrayIndexSegment.js +17 -10
  18. package/source/class/qx/data/binding/IInputReceiver.js +1 -1
  19. package/source/class/qx/data/binding/PropNameSegment.js +1 -1
  20. package/source/class/qx/dev/unit/TestCase.js +4 -1
  21. package/source/class/qx/event/handler/Focus.js +2 -1
  22. package/source/class/qx/html/Jsx.js +2 -3
  23. package/source/class/qx/html/Node.js +3 -3
  24. package/source/class/qx/io/jsonrpc/Client.js +1 -1
  25. package/source/class/qx/promise/NativeWrapper.js +1 -1
  26. package/source/class/qx/test/core/Property.js +30 -2
  27. package/source/class/qx/test/data/singlevalue/Simple.js +6 -0
  28. package/source/class/qx/test/locale/Date.js +2 -2
  29. package/source/class/qx/theme/classic/Appearance.js +21 -0
  30. package/source/class/qx/theme/modern/Appearance.js +21 -0
  31. package/source/class/qx/theme/simple/Appearance.js +21 -0
  32. package/source/class/qx/theme/tangible/Appearance.js +2 -0
  33. package/source/class/qx/tool/cli/AbstractCliApp.js +18 -2
  34. package/source/class/qx/tool/compiler/ClassFile.js +0 -4
  35. package/source/class/qx/tool/compiler/MetaDatabase.js +47 -0
  36. package/source/class/qx/tool/compiler/cli/commands/Compile.js +139 -8
  37. package/source/class/qx/tool/compiler/cli/commands/Create.js +1 -1
  38. package/source/class/qx/tool/compiler/cli/commands/Serve.js +1 -1
  39. package/source/class/qx/tool/compiler/cli/commands/Typescript.js +26 -39
  40. package/source/class/qx/tool/compiler/cli/commands/add/Script.js +1 -1
  41. package/source/class/qx/tool/compiler/cli/commands/package/Publish.js +3 -2
  42. package/source/class/qx/tool/compiler/cli/commands/package/Update.js +2 -2
  43. package/source/class/qx/tool/compiler/targets/TypeScriptWriter.js +3 -0
  44. package/source/class/qx/tool/compiler/targets/meta/Browserify.js +142 -80
  45. package/source/class/qx/tool/migration/M8_0_0.js +4 -4
  46. package/source/class/qx/ui/toolbar/ToolBar.js +4 -4
  47. package/source/resource/qx/tool/compiler/cli/templates/class/default.tmpl.js +6 -7
  48. package/source/resource/qx/tool/compiler/cli/templates/class/singleton.tmpl.js +5 -6
  49. package/source/resource/qx/tool/compiler/schema/compile-1-0-0.json +6 -2
@@ -13,22 +13,13 @@
13
13
 
14
14
  Authors:
15
15
  * John Spackman (github.com/johnspackman)
16
+ * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski116@gmail.com)
16
17
 
17
18
  ************************************************************************ */
18
19
 
19
20
  /**
20
21
  * Property implementation for actual properties
21
22
  *
22
- * TODO:
23
- *
24
- * `validate` implementation
25
- * `delegate` implementation
26
- * `inheritable` implementation (pass onto `obj._getChildren`; check for special `inherit` value)
27
- * Array check
28
- * FunctionCheck
29
- *
30
- * how does init of property values work? `init` per class and `initFunction` per instance?
31
- *
32
23
  */
33
24
  qx.Bootstrap.define("qx.core.property.Property", {
34
25
  implement: qx.core.property.IProperty,
@@ -54,7 +45,7 @@ qx.Bootstrap.define("qx.core.property.Property", {
54
45
  * If set to true, then properties with init values will have their apply method called during construction.
55
46
  * Default is false to maintain backward compatibility with v7 behavior.
56
47
  */
57
- "qx.core.property.Property.applyDuringConstruct": false,
48
+ "qx.core.property.Property.applyDuringConstruct": false,//TODO should this be true?
58
49
 
59
50
  /**
60
51
  * Only relevant when applyDuringConstruct is true.
@@ -83,14 +74,14 @@ qx.Bootstrap.define("qx.core.property.Property", {
83
74
  /** @type {String} the name of the property */
84
75
  __propertyName: null,
85
76
 
86
- /** @type {qx.Class} the class that defined the property */
77
+ /** @type {new () => qx.core.Object} the class that defined the property */
87
78
  __clazz: null,
88
79
 
89
80
  /** @type {Boolean} whether this is a pseudo property or not */
90
81
  __pseudoProperty: false,
91
82
 
92
83
  /**
93
- * @type {qx.Class} the class that original defined this property, before it was cloned and
84
+ * @type {new () => qx.core.Object} the class that original defined this property, before it was cloned and
94
85
  * refined for the current `__clazz`
95
86
  */
96
87
  __superClass: null,
@@ -134,11 +125,9 @@ qx.Bootstrap.define("qx.core.property.Property", {
134
125
  /** @type {Boolean} whether the property needs to be dereferenced */
135
126
  __needsDereference: false,
136
127
 
137
- isRefineAllowed(def) {},
138
-
139
128
  /**
140
129
  * The class where this property is defined
141
- * @returns {Function}
130
+ * @returns {new () => qx.core.Object}
142
131
  */
143
132
  getClass() {
144
133
  return this.__clazz;
@@ -274,11 +263,21 @@ qx.Bootstrap.define("qx.core.property.Property", {
274
263
  }
275
264
  }
276
265
 
277
- if (def.init !== undefined) {
266
+ let hasInitVal = def.init !== undefined;
267
+
268
+ if (qx.core.Environment.get("qx.debug")) {
269
+ if (hasInitVal && def.initFunction) {
270
+ throw new Error(`${this}: init and initFunction are mutually exclusive. Please specify only one of them.`);
271
+ }
272
+ }
273
+
274
+ if (hasInitVal) {
278
275
  this.__initValue = def.init;
276
+ this.__initFunction = undefined;
279
277
  }
280
278
  if (def.initFunction) {
281
279
  this.__initFunction = def.initFunction;
280
+ this.__initValue = undefined;
282
281
  }
283
282
 
284
283
  if (qx.core.Environment.get("qx.debug")) {
@@ -433,6 +432,8 @@ qx.Bootstrap.define("qx.core.property.Property", {
433
432
  let initValue = this.__initValue;
434
433
  if (initValue !== undefined) {
435
434
  clazz.prototype["$$init_" + propertyName] = initValue;
435
+ } else if (this.__initFunction) {
436
+ clazz.prototype["$$init_" + propertyName] = undefined;
436
437
  }
437
438
 
438
439
  addMethod("init" + upname, function (...args) {
@@ -567,9 +568,9 @@ qx.Bootstrap.define("qx.core.property.Property", {
567
568
  }
568
569
  state.initMethodCalled = true;
569
570
 
570
- if (value !== undefined && this.__definition?.init !== undefined) {
571
+ if (value !== undefined && (this.__initValue !== undefined || this.__initFunction !== undefined)) {
571
572
  this.warn(
572
- `${this}: init() called with a value, ignoring - use deferredInit and do not specify an init value in the property definition`
573
+ `${this}: init() called with a value, ignoring - use deferredInit and do not specify an init/function value in the property definition`
573
574
  );
574
575
  value = undefined;
575
576
  }
@@ -13,7 +13,7 @@
13
13
 
14
14
  Authors:
15
15
  * John Spackman (https://github.com/johnspackman, john.spackman@zenesis.com)
16
- * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski@vmn.digital)
16
+ * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski116@gmail.com)
17
17
 
18
18
  ************************************************************************ */
19
19
 
@@ -172,7 +172,7 @@ qx.Class.define("qx.data.SingleValueBinding", {
172
172
  /**
173
173
  * @type {*}
174
174
  */
175
- __value: null,
175
+ __value: undefined,
176
176
  /**
177
177
  * @type {qx.core.Object}
178
178
  */
@@ -284,7 +284,7 @@ qx.Class.define("qx.data.SingleValueBinding", {
284
284
  SingleValueBinding.__bindingsBySource[value.toHashCode()][this.toHashCode()] = this;
285
285
 
286
286
  //Set the input on the first segment of the source path
287
- let promise = this.__sourceSegments[0].setInput(value);
287
+ let out = this.__sourceSegments[0].setInput(value);
288
288
  const cb = () => {
289
289
  if (qx.core.Environment.get("qx.debug")) {
290
290
  this.assertNotNull(
@@ -294,10 +294,8 @@ qx.Class.define("qx.data.SingleValueBinding", {
294
294
  }
295
295
  };
296
296
 
297
- let out;
298
-
299
- if (qx.Promise.isPromise(promise)) {
300
- out = promise.then(cb);
297
+ if (qx.Promise.isPromise(out)) {
298
+ out = out.then(cb);
301
299
  } else {
302
300
  out = cb();
303
301
  }
@@ -12,7 +12,7 @@
12
12
  See the LICENSE file in the project's top-level directory for details.
13
13
 
14
14
  Authors:
15
- * John Spackman (https://github.com/johnspackman, john.spackman@zenesis.com)
15
+ * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski116@gmail.com)
16
16
 
17
17
  ************************************************************************ */
18
18
 
@@ -12,7 +12,7 @@
12
12
  See the LICENSE file in the project's top-level directory for details.
13
13
 
14
14
  Authors:
15
- * John Spackman (https://github.com/johnspackman, john.spackman@zenesis.com)
15
+ * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski116@gmail.com)
16
16
 
17
17
  ************************************************************************ */
18
18
 
@@ -51,7 +51,7 @@ qx.Class.define("qx.data.binding.ArrayIndexSegment", {
51
51
  destruct() {
52
52
  let input = this.getInput();
53
53
  if (input) {
54
- if (this.getInput() instanceof qx.data.Array) {
54
+ if (this.__isListData(this.getInput())) {
55
55
  this.getInput().removeListener("change", this.__onChangeContents, this);
56
56
  }
57
57
  }
@@ -71,19 +71,23 @@ qx.Class.define("qx.data.binding.ArrayIndexSegment", {
71
71
  return this.__string;
72
72
  },
73
73
 
74
+ __isListData(input) {
75
+ return qx.Interface.objectImplements(input, qx.data.IListData);
76
+ },
77
+
74
78
  /**
75
79
  * @override
76
80
  */
77
81
  _applyInput(value, oldValue) {
78
82
  if (oldValue) {
79
83
  this.setEventName(null);
80
- if (oldValue instanceof qx.data.Array) {
84
+ if (this.__isListData(oldValue)) {
81
85
  oldValue.removeListener("change", this.__onChangeContents, this);
82
86
  }
83
87
  }
84
88
 
85
89
  if (value) {
86
- if (value instanceof qx.data.Array) {
90
+ if (this.__isListData(value)) {
87
91
  value.addListener("change", this.__onChangeContents, this);
88
92
  this.setEventName("change");
89
93
  }
@@ -108,8 +112,10 @@ qx.Class.define("qx.data.binding.ArrayIndexSegment", {
108
112
  return undefined;
109
113
  }
110
114
 
111
- if (input instanceof qx.data.Array) {
112
- input = input.toArray();
115
+ if (this.__isListData(input)) {
116
+ const len = input.getLength();
117
+ const idx = this.__index === -1 ? len - 1 : this.__index;
118
+ return idx >= len ? undefined : input.getItem(idx);
113
119
  }
114
120
 
115
121
  if (this.__index >= input.length) {
@@ -126,11 +132,12 @@ qx.Class.define("qx.data.binding.ArrayIndexSegment", {
126
132
  if (!input) {
127
133
  return;
128
134
  }
129
- let index = this.__index == -1 ? input.length - 1 : this.__index;
130
- if (input instanceof qx.data.Array) {
131
- input.setItem(index, targetValue);
135
+ if (this.__isListData(input)) {
136
+ const idx = this.__index === -1 ? input.getLength() - 1 : this.__index;
137
+ input.setItem(idx, targetValue);
132
138
  } else {
133
- input[index] = targetValue;
139
+ const idx = this.__index === -1 ? input.length - 1 : this.__index;
140
+ input[idx] = targetValue;
134
141
  }
135
142
  },
136
143
 
@@ -7,7 +7,7 @@ qx.Interface.define("qx.data.binding.IInputReceiver", {
7
7
  *
8
8
  * @param {*} value
9
9
  * @returns {Promise?} If the operation is asynchronous, it should return a Promise which resolves when it has completed.
10
- * If it's synchrous, it should return null.
10
+ * If it's synchronous, it should return null.
11
11
  */
12
12
  setInput(input) {}
13
13
  }
@@ -12,7 +12,7 @@
12
12
  See the LICENSE file in the project's top-level directory for details.
13
13
 
14
14
  Authors:
15
- * John Spackman (https://github.com/johnspackman, john.spackman@zenesis.com)
15
+ * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski116@gmail.com)
16
16
 
17
17
  ************************************************************************ */
18
18
 
@@ -201,7 +201,10 @@ qx.Class.define("qx.dev.unit.TestCase", {
201
201
  if (this.__autoDispose) {
202
202
  this.__autoDispose.forEach(function (obj) {
203
203
  if (!obj.isDisposed()) {
204
- if (obj instanceof qx.ui.core.Widget) {
204
+ // Do not make an actual reference to qx.ui.core.Widget otherwise the compiler will load
205
+ // dependencies and that prevents this class from being used in node-only builds
206
+ const Widget = qx.Class.getByName("qx.ui.core.Widget");
207
+ if (obj instanceof Widget) {
205
208
  obj.destroy();
206
209
  } else if (obj instanceof qx.core.Object) {
207
210
  obj.dispose();
@@ -1344,8 +1344,9 @@ qx.Class.define("qx.event.handler.Focus", {
1344
1344
  return focusedElement;
1345
1345
  }
1346
1346
  if (qx.Class.isClass("qx.ui.core.Widget")) {
1347
+ const Widget = qx.Class.getByName("qx.ui.core.Widget");
1347
1348
  // Check compound widgets
1348
- var widget = qx.ui.core.Widget.getWidgetByElement(focusedElement),
1349
+ var widget = Widget.getWidgetByElement(focusedElement),
1349
1350
  textField =
1350
1351
  widget &&
1351
1352
  widget.getChildControl &&
@@ -48,11 +48,10 @@ qx.Class.define("qx.html.Jsx", {
48
48
  *
49
49
  * @param tagname {String|Function} the name of the tag
50
50
  * @param attributes {Record<string, any>} map of attribute values
51
- * @param children {qx.html.Node[]} array of children
51
+ * @param children {...qx.html.Node} children
52
52
  * @return {qx.html.Element|qx.data.Array}
53
53
  */
54
- createElement(tagname, attributes) {
55
- const children = qx.lang.Array.fromArguments(arguments, 2);
54
+ createElement(tagname, attributes, ...children) {
56
55
  attributes ??= {};
57
56
 
58
57
  // CSS CUSTOM PROPERTIES
@@ -889,10 +889,10 @@ qx.Class.define("qx.html.Node", {
889
889
  /**
890
890
  * Append all given children at the end of this element.
891
891
  *
892
- * @param varargs {qx.html.Element} elements to insert
892
+ * @param {...qx.html.Element} children elements to insert
893
893
  * @return {qx.html.Element} this object (for chaining support)
894
894
  */
895
- add(varargs) {
895
+ add(...children) {
896
896
  var self = this;
897
897
  function addImpl(arr) {
898
898
  arr.forEach(function (child) {
@@ -917,7 +917,7 @@ qx.Class.define("qx.html.Node", {
917
917
  self._children.push(child);
918
918
  });
919
919
  }
920
- addImpl(qx.lang.Array.fromArguments(arguments));
920
+ addImpl(children);
921
921
 
922
922
  // Chaining support
923
923
  return this;
@@ -406,6 +406,6 @@ qx.Class.define("qx.io.jsonrpc.Client", {
406
406
  * @deprecated
407
407
  * Behavior in v8 will be as if the environment variable value is `true`, but the environment variable will no longer be available.
408
408
  */
409
- "qx.io.jsonrpc.forwardTransportPromiseRejectionToRequest": false
409
+ "qx.io.jsonrpc.forwardTransportPromiseRejectionToRequest": true
410
410
  }
411
411
  });
@@ -13,7 +13,7 @@
13
13
 
14
14
  Authors:
15
15
  * John Spackman (john.spackman@zenesis.com)
16
- * Patryk Malinowski (pmalinowski@vmn.digital)
16
+ * Patryk Malinowski (https://github.com/patryk-m-malinowski, pmalinowski116@gmail.com)
17
17
 
18
18
  ************************************************************************ */
19
19
 
@@ -36,7 +36,7 @@ qx.Class.define("qx.test.core.Property", {
36
36
  extend: qx.core.Object
37
37
  });
38
38
 
39
- qx.Class.define("qx.test.cpnfv8.Superclass", {
39
+ qx.Class.define("qx.test.cpnfv8.Superclass", {//cbh
40
40
  extend: qx.test.cpnfv8.Object,
41
41
 
42
42
  construct(bRunning = true) {
@@ -107,7 +107,18 @@ qx.Class.define("qx.test.core.Property", {
107
107
 
108
108
  readOnly: {
109
109
  initFunction: () => 42,
110
- immutable: "readonly"
110
+ immutable: "readonly",
111
+ apply() {
112
+ this.appliedReadOnly = true;
113
+ }
114
+ },
115
+
116
+ noAutoApply: {
117
+ initFunction:() => 24,
118
+ autoApply: false,
119
+ apply() {
120
+ throw new Error("Should not call here!")
121
+ }
111
122
  }
112
123
  },
113
124
 
@@ -540,6 +551,7 @@ qx.Class.define("qx.test.core.Property", {
540
551
 
541
552
  testApply() {
542
553
  let obj = new qx.test.cpnfv8.Superclass();
554
+ this.assertTrue(obj.appliedReadOnly, "Expected apply method for property 'readOnly' to be called, but wasn't");
543
555
  this.assertEquals(3, obj.applyCount, "initial apply count should be 3");
544
556
  this.assertArrayEquals([null, undefined], obj.lastApply, "last apply call during construction");
545
557
  obj.setRunning(false);
@@ -574,6 +586,7 @@ qx.Class.define("qx.test.core.Property", {
574
586
  //old, deprecated behaviour
575
587
  qx.core.Environment.set("qx.core.property.Property.applyDuringConstruct", false);
576
588
  obj = new qx.test.cpnfv8.Superclass();
589
+ this.assertTrue(obj.appliedReadOnly, "Expected apply method for property 'readOnly' to be called, but wasn't");
577
590
  this.assertEquals(1, obj.applyCount, "old behaviour: initial apply count should be 1");
578
591
  obj.resetAnyProp();
579
592
  this.assertEquals(1, obj.applyCount, "old behaviour: apply count after reset should still be 1");
@@ -821,6 +834,21 @@ qx.Class.define("qx.test.core.Property", {
821
834
  let second = inst.getJsdocProp();
822
835
  this.assertArrayEquals([10, 20, 30], initial, "properties created with initFunction returned by getter should be the same");
823
836
  this.assertIdentical(initial, second, "properties created with initFunction returned by getter should be the same");
837
+
838
+ try {
839
+ qx.Class.define(null, {
840
+ extend: qx.core.Object,
841
+ properties: {
842
+ badProp: {
843
+ init: 5,
844
+ initFunction: () => 10
845
+ }
846
+ }
847
+ });
848
+ this.fail("Defining a property with both init and initFunction should throw an error");
849
+ } catch (e) {
850
+ // expected
851
+ }
824
852
  },
825
853
 
826
854
  testDefinesThanSubClassWithInterface() {
@@ -791,6 +791,12 @@ qx.Class.define("qx.test.data.singlevalue.Simple", {
791
791
  person2.setFriend(null);
792
792
  this.assertEquals(3, timesCalled, "Converter should not be called when chain is broken and ignoreConverter is set and matches.");
793
793
  this.assertEquals("Patryk", person3.getName(), "Target should be reset when chain is broken");
794
+
795
+ //ensure that during the initial set, if the chain is incomplete (i.e. has a null value),
796
+ //the target property is reset (not set to null)
797
+ let person4 = new Person("Harry");
798
+ person3.bind("friend.friend.name", person4, "name");
799
+ this.assertEquals("Patryk", person4.getName(), "Target should be reset when chain has null element during initial set");
794
800
  }
795
801
  }
796
802
  });
@@ -32,7 +32,7 @@ qx.Class.define("qx.test.locale.Date", {
32
32
  testGetAmMarker() {
33
33
  var Date = qx.locale.Date;
34
34
  var items = [
35
- { locale: "ko", expected: "오전" },
35
+ { locale: "zh", expected: "上午" },
36
36
  { locale: "fr", expected: "AM" },
37
37
  { locale: "af", expected: "vm." }
38
38
  ];
@@ -46,7 +46,7 @@ qx.Class.define("qx.test.locale.Date", {
46
46
  testGetPMMarker() {
47
47
  var Date = qx.locale.Date;
48
48
  var items = [
49
- { locale: "ko", expected: "오후" },
49
+ { locale: "zh", expected: "下午" },
50
50
  { locale: "fr", expected: "PM" },
51
51
  { locale: "af", expected: "nm." }
52
52
  ];
@@ -1465,6 +1465,27 @@ qx.Theme.define("qx.theme.classic.Appearance", {
1465
1465
  }
1466
1466
  },
1467
1467
 
1468
+ "selectbox-arrow-button": "widget",
1469
+
1470
+ /*
1471
+ ---------------------------------------------------------------------------
1472
+ CHECKED SELECT BOX
1473
+ ---------------------------------------------------------------------------
1474
+ */
1475
+
1476
+ "checked-selectbox": "selectbox",
1477
+
1478
+ "checked-selectbox/allNone": {
1479
+ include: "button"
1480
+ },
1481
+
1482
+ "checked-selectbox/tag": "tag",
1483
+
1484
+ tag: {
1485
+ alias: "button",
1486
+ include: "button"
1487
+ },
1488
+
1468
1489
  /*
1469
1490
  ---------------------------------------------------------------------------
1470
1491
  DATE CHOOSER
@@ -1728,6 +1728,27 @@ qx.Theme.define("qx.theme.modern.Appearance", {
1728
1728
  }
1729
1729
  },
1730
1730
 
1731
+ "selectbox-arrow-button": "widget",
1732
+
1733
+ /*
1734
+ ---------------------------------------------------------------------------
1735
+ CHECKED SELECT BOX
1736
+ ---------------------------------------------------------------------------
1737
+ */
1738
+
1739
+ "checked-selectbox": "selectbox",
1740
+
1741
+ "checked-selectbox/allNone": {
1742
+ include: "button"
1743
+ },
1744
+
1745
+ "checked-selectbox/tag": "tag",
1746
+
1747
+ tag: {
1748
+ alias: "button",
1749
+ include: "button"
1750
+ },
1751
+
1731
1752
  /*
1732
1753
  ---------------------------------------------------------------------------
1733
1754
  DATE CHOOSER
@@ -1204,6 +1204,27 @@ qx.Theme.define("qx.theme.simple.Appearance", {
1204
1204
  }
1205
1205
  },
1206
1206
 
1207
+ "selectbox-arrow-button": "widget",
1208
+
1209
+ /*
1210
+ ---------------------------------------------------------------------------
1211
+ CHECKED SELECT BOX
1212
+ ---------------------------------------------------------------------------
1213
+ */
1214
+
1215
+ "checked-selectbox": "selectbox",
1216
+
1217
+ "checked-selectbox/allNone": {
1218
+ include: "button"
1219
+ },
1220
+
1221
+ "checked-selectbox/tag": "tag",
1222
+
1223
+ tag: {
1224
+ alias: "button",
1225
+ include: "button"
1226
+ },
1227
+
1207
1228
  /*
1208
1229
  ---------------------------------------------------------------------------
1209
1230
  COMBO BOX
@@ -1313,6 +1313,8 @@ qx.Theme.define("qx.theme.tangible.Appearance", {
1313
1313
  }
1314
1314
  },
1315
1315
 
1316
+ "selectbox-arrow-button": "widget",
1317
+
1316
1318
  /*
1317
1319
  ---------------------------------------------------------------------------
1318
1320
  CHECKED SELECT BOX
@@ -41,12 +41,28 @@ qx.Class.define("qx.tool.cli.AbstractCliApp", {
41
41
  console.log((cmd || rootCmd).usage());
42
42
  process.exit((!cmd || errors) ? 1 : 0);
43
43
  }
44
+ let exitCode = 0;
44
45
  try {
45
- process.exit(await run.call(cmd, cmd) ?? 0);
46
+ exitCode = await run.call(cmd, cmd) ?? 0;
46
47
  } catch (ex) {
47
48
  console.error("ERROR:\n" + (ex.stack ?? ex.message) + "\n");
48
- process.exit(1);
49
+ exitCode = 1;
49
50
  }
51
+ // Close undici's global connection pool before exiting.
52
+ // Native fetch (undici) keeps HTTP keep-alive connections open which would prevent
53
+ // natural process exit. node:undici is available as a built-in from Node.js 22+.
54
+ try {
55
+ const { getGlobalDispatcher } = require("node:undici");
56
+ await getGlobalDispatcher().close();
57
+ } catch (e) {
58
+ // node:undici not available on this Node.js version — proceed
59
+ }
60
+ // Use process.exitCode instead of process.exit() to avoid a libuv race on Windows + Node 24:
61
+ // close() resolves, but undici's internal uv_async_t handles are still tearing down; calling
62
+ // process.exit() immediately marks them UV_HANDLE_CLOSING, then a background task fires
63
+ // uv_async_send() on one → assertion fails (libuv 1.50.x made this a hard crash, not a no-op).
64
+ // Setting exitCode and returning lets the event loop drain naturally instead.
65
+ process.exitCode = exitCode;
50
66
  },
51
67
 
52
68
  /**
@@ -3164,10 +3164,6 @@ qx.Class.define("qx.tool.compiler.ClassFile", {
3164
3164
  * These options are passed to Babel for JSX compilation; they can be changed by the CLI etc
3165
3165
  * as needed.
3166
3166
  *
3167
- * Note that at the moment they use a class that does not exist! `qx.html.Jsx` is coming soon
3168
- * to a PR near you, but in the mean time you could use the compile.json `jsx` setting to
3169
- * change these to something else, eg `{ pragma: "jsx.dom", pragmaFrag: "jsx.Fragment }` and
3170
- * use https://github.com/alecsgone/jsx-render in your application's code.
3171
3167
  */
3172
3168
  JSX_OPTIONS: {
3173
3169
  pragma: "qx.html.Jsx.createElement",
@@ -211,6 +211,53 @@ qx.Class.define("qx.tool.compiler.MetaDatabase", {
211
211
  }
212
212
  },
213
213
 
214
+ /**
215
+ * Scans directories recursively for .js files and populates the database.
216
+ * Calls addFile() for each discovered file, then reparseAll().
217
+ *
218
+ * @param {String[]} dirs directories to scan
219
+ * @param {Object} [options]
220
+ * @param {Boolean} [options.force] reparse even if up to date (default: false)
221
+ * @param {Object} [options.ignore] an `ignore` instance for exclude patterns
222
+ * @param {Boolean} [options.verbose] log each processed file
223
+ */
224
+ async loadFromDirectories(dirs, options = {}) {
225
+ const classFiles = [];
226
+
227
+ const scanImpl = async filename => {
228
+ let basename = path.basename(filename);
229
+
230
+ if (options.ignore) {
231
+ let relativePath = path.relative(process.cwd(), filename);
232
+ if (options.ignore.ignores(relativePath)) {
233
+ return;
234
+ }
235
+ }
236
+
237
+ let stat = await fs.promises.stat(filename);
238
+ if (stat.isFile() && basename.match(/\.js$/)) {
239
+ classFiles.push(filename);
240
+ } else if (stat.isDirectory() && (basename == "." || basename[0] != ".")) {
241
+ let files = await fs.promises.readdir(filename);
242
+ for (let file of files) {
243
+ await scanImpl(path.join(filename, file));
244
+ }
245
+ }
246
+ };
247
+
248
+ for (let dir of dirs) {
249
+ await scanImpl(dir);
250
+ }
251
+
252
+ for (let filename of classFiles) {
253
+ if (options.verbose) {
254
+ qx.tool.compiler.Console.info(`Processing ${filename} ...`);
255
+ }
256
+ await this.addFile(filename, options.force || false);
257
+ }
258
+ await this.reparseAll();
259
+ },
260
+
214
261
  __createDerivedClassLookup() {
215
262
  const lookup = {};
216
263