@qooxdoo/framework 7.7.1 → 7.8.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.
Files changed (68) hide show
  1. package/Manifest.json +1 -1
  2. package/lib/compiler/compile-info.json +69 -69
  3. package/lib/compiler/index.js +542 -380
  4. package/lib/resource/qx/tool/schema/compile-1-0-0.json +13 -0
  5. package/package.json +2 -2
  6. package/source/class/qx/Class.js +1 -1
  7. package/source/class/qx/Mixin.js +7 -5
  8. package/source/class/qx/bom/Input.js +24 -13
  9. package/source/class/qx/bom/Label.js +82 -2
  10. package/source/class/qx/bom/webfonts/WebFont.js +1 -0
  11. package/source/class/qx/core/Environment.js +82 -1
  12. package/source/class/qx/data/controller/List.js +50 -21
  13. package/source/class/qx/data/controller/MSelection.js +45 -12
  14. package/source/class/qx/dev/unit/AsyncWrapper.js +8 -0
  15. package/source/class/qx/event/Manager.js +163 -124
  16. package/source/class/qx/io/ImageLoader.js +6 -3
  17. package/source/class/qx/io/exception/Transport.js +1 -0
  18. package/source/class/qx/io/jsonrpc/Client.js +64 -8
  19. package/source/class/qx/io/jsonrpc/protocol/Request.js +10 -6
  20. package/source/class/qx/lang/Function.js +5 -1
  21. package/source/class/qx/test/Mixin.js +260 -0
  22. package/source/class/qx/test/bom/client/Pdfjs.js +4 -0
  23. package/source/class/qx/test/bom/element/AnimationJs.js +3 -0
  24. package/source/class/qx/test/bom/element/Style.js +1 -0
  25. package/source/class/qx/test/bom/media/MediaTestCase.js +6 -0
  26. package/source/class/qx/test/core/Environment.js +44 -0
  27. package/source/class/qx/test/io/MAssert.js +94 -0
  28. package/source/class/qx/test/io/TestMAssert.js +47 -0
  29. package/source/class/qx/test/io/jsonrpc/Client.js +79 -19
  30. package/source/class/qx/test/io/jsonrpc/PostMessageClient.js +152 -0
  31. package/source/class/qx/test/io/jsonrpc/Protocol.js +1 -5
  32. package/source/class/qx/test/ui/embed/Iframe.js +1 -1
  33. package/source/class/qx/test/ui/form/TextArea.js +4 -0
  34. package/source/class/qx/test/util/DeferredCall.js +6 -0
  35. package/source/class/qx/test/util/Function.js +2 -2
  36. package/source/class/qx/tool/cli/api/Test.js +22 -0
  37. package/source/class/qx/tool/cli/commands/Compile.js +17 -7
  38. package/source/class/qx/tool/cli/commands/Test.js +7 -1
  39. package/source/class/qx/tool/compiler/Analyser.js +7 -0
  40. package/source/class/qx/tool/compiler/ClassFile.js +12 -6
  41. package/source/class/qx/tool/compiler/MetaExtraction.js +0 -5
  42. package/source/class/qx/tool/compiler/targets/Target.js +2 -2
  43. package/source/class/qx/tool/compiler/targets/meta/Browserify.js +8 -2
  44. package/source/class/qx/ui/basic/Image.js +72 -8
  45. package/source/class/qx/ui/basic/Label.js +4 -6
  46. package/source/class/qx/ui/control/DateChooser.js +4 -6
  47. package/source/class/qx/ui/core/Blocker.js +4 -6
  48. package/source/class/qx/ui/core/LayoutItem.js +4 -6
  49. package/source/class/qx/ui/core/MPlacement.js +11 -0
  50. package/source/class/qx/ui/core/Widget.js +7 -5
  51. package/source/class/qx/ui/form/AbstractField.js +14 -16
  52. package/source/class/qx/ui/form/IListItem.js +48 -1
  53. package/source/class/qx/ui/form/List.js +11 -6
  54. package/source/class/qx/ui/form/MForm.js +4 -6
  55. package/source/class/qx/ui/form/Spinner.js +4 -6
  56. package/source/class/qx/ui/form/renderer/AbstractRenderer.js +4 -6
  57. package/source/class/qx/ui/menu/AbstractButton.js +7 -11
  58. package/source/class/qx/ui/mobile/basic/Label.js +4 -6
  59. package/source/class/qx/ui/mobile/form/Label.js +4 -6
  60. package/source/class/qx/ui/mobile/list/List.js +4 -6
  61. package/source/class/qx/ui/progressive/renderer/table/Row.js +35 -7
  62. package/source/class/qx/ui/progressive/renderer/table/cell/Boolean.js +4 -6
  63. package/source/class/qx/ui/table/Table.js +12 -6
  64. package/source/class/qx/ui/table/cellrenderer/Abstract.js +4 -6
  65. package/source/class/qx/ui/table/cellrenderer/Boolean.js +4 -6
  66. package/source/class/qx/ui/table/pane/Scroller.js +35 -0
  67. package/source/class/qx/ui/table/rowrenderer/Default.js +4 -6
  68. package/source/resource/qx/tool/schema/compile-1-0-0.json +13 -0
@@ -60,21 +60,33 @@ qx.Class.define("qx.test.io.jsonrpc.Client", {
60
60
  },
61
61
 
62
62
  /**
63
- * Sets up the fake server and instructs it to send the given response(s)
64
- * @param {String} response The server response to the first request
63
+ * Sets up the fake server and instructs it to send the given response
64
+ * @param {String} response Optional server response
65
65
  */
66
66
  setUpFakeServer(response) {
67
67
  // Not fake transport
68
68
  this.getSandbox().restore();
69
69
  this.useFakeServer();
70
70
  this.setUpRequest();
71
+ if (response) {
72
+ this.setServerResponse(response);
73
+ }
74
+ this.getServer().autoRespond = true;
75
+ },
76
+
77
+ /**
78
+ * Set the fake server's response
79
+ * @param {{String|Object} } response Server response. Will be stringified to a JSON string if not a string
80
+ */
81
+ setServerResponse(response) {
82
+ if (typeof response != "string") {
83
+ response = JSON.stringify(response);
84
+ }
71
85
  this.getServer().respondWith("POST", /.*/, [
72
86
  200,
73
87
  { "Content-Type": "application/json; charset=utf-8" },
74
88
  response
75
89
  ]);
76
-
77
- this.getServer().autoRespond = true;
78
90
  },
79
91
 
80
92
  /**
@@ -103,7 +115,7 @@ qx.Class.define("qx.test.io.jsonrpc.Client", {
103
115
  if (!(err instanceof qx.io.exception.Exception)) {
104
116
  throw err;
105
117
  }
106
- this.assertEquals(exception, err.code, `Error code does not match`);
118
+ this.assertEquals(exception, err.code, `Error code does not match. Expected ${exception}, got ${err.code}.`);
107
119
  } else {
108
120
  this.assertInstance(
109
121
  err,
@@ -119,16 +131,20 @@ qx.Class.define("qx.test.io.jsonrpc.Client", {
119
131
  // check transport promise
120
132
  client.send(message_out).catch(errorCallback);
121
133
  this.wait(100, () => {
134
+ // in case of a transport error, ...
135
+ const n = qx.core.Environment.select("qx.io.jsonrpc.forwardTransportPromiseRejectionToRequest", {
136
+ true: 1, // ... we are rejecting the request promise only, v8 default
137
+ false: 2 // ... both request promise and transport promise are rejected, v7 default
138
+ })
122
139
  if (
123
- // the request promise will not be called since the promise is already rejected
140
+ // the request promise will not be rejected because it already is rejected in this special case
124
141
  exception === qx.io.exception.Transport.DUPLICATE_ID ||
125
- // or the send promise will not be rejected because we have a server-side error
126
- exception === qx.io.exception.Protocol
142
+ // or the send promise will not be rejected because we have a server-side error, v7 default only
143
+ (exception === qx.io.exception.Protocol && !qx.core.Environment.get("qx.io.jsonrpc.forwardTransportPromiseRejectionToRequest"))
127
144
  ) {
128
- this.assertCalledTwice(errorCallback);
145
+ this.assertCallCount(errorCallback, n);
129
146
  } else {
130
- // the error handler will be called three times
131
- this.assertCalledThrice(errorCallback);
147
+ this.assertCallCount(errorCallback, n+1);
132
148
  }
133
149
  });
134
150
  },
@@ -208,10 +224,9 @@ qx.Class.define("qx.test.io.jsonrpc.Client", {
208
224
 
209
225
  this.setUpFakeServer(message_in.toString());
210
226
  const client = new qx.io.jsonrpc.Client("http://jsonrpc");
211
- let spy = this.spy(value => this.assertEquals(result, value));
212
- message_out.getPromise().then(spy);
213
227
  await client.send(message_out);
214
- this.assertCalled(spy);
228
+ const value = await message_out.getPromise();
229
+ this.assertEquals(result, value);
215
230
  },
216
231
 
217
232
  async "test: call jsonrpc method and receive batched response"() {
@@ -225,10 +240,9 @@ qx.Class.define("qx.test.io.jsonrpc.Client", {
225
240
  .toString();
226
241
  this.setUpFakeServer(response);
227
242
  const client = new qx.io.jsonrpc.Client("http://jsonrpc");
228
- let spy = this.spy(value => this.assertEquals(result, value));
229
- message_out.getPromise().then(spy);
230
243
  await client.send(message_out);
231
- this.assertCalled(spy);
244
+ const value = await message_out.getPromise();
245
+ this.assertEquals(result, value);
232
246
  },
233
247
 
234
248
  "test: call jsonrpc method and expect error on invalid reponse "() {
@@ -321,10 +335,56 @@ qx.Class.define("qx.test.io.jsonrpc.Client", {
321
335
  spy(message);
322
336
  });
323
337
  client.sendNotification("ping");
338
+ this.wait(100, () => this.assertCalledTwice(spy) );
339
+ },
340
+
341
+ /**
342
+ * Issue #10739
343
+ */
344
+ testIssue10739() {
345
+ this.resetId();
346
+ this.setUpFakeServer();
347
+ const successCallback = this.spy(result =>
348
+ this.debug(`Server response is "${result}"`)
349
+ );
350
+ const errorCallback = this.spy(error => console.error(error.message));
351
+ const client = new qx.io.jsonrpc.Client("http://test.local");
352
+ const auth = new qx.io.request.authentication.Bearer("TOKEN");
353
+ client.getTransport().getTransportImpl().setAuthentication(auth);
354
+ this.setServerResponse({
355
+ jsonrpc: "2.0",
356
+ error: {
357
+ code: 8,
358
+ message: "stale or uninitialized auth token/rune"
359
+ },
360
+ id: 1
361
+ });
362
+ const sendRequest = this.spy(() => {
363
+ this.debug("sendRequest() was called");
364
+ client
365
+ .sendRequest("service.method", ["foo"])
366
+ .then(successCallback)
367
+ .catch(err => {
368
+ if (err.code == 8) {
369
+ this.debug(`Received expected error message.`);
370
+ this.setServerResponse({
371
+ jsonrpc: "2.0",
372
+ result: "OK",
373
+ id: 2
374
+ });
375
+ sendRequest(); // second request
376
+ } else {
377
+ errorCallback(err);
378
+ }
379
+ });
380
+ });
381
+ sendRequest();
324
382
  this.wait(
325
- 100,
383
+ 250,
326
384
  function () {
327
- this.assertCalledTwice(spy);
385
+ this.assertCalledTwice(sendRequest);
386
+ this.assertCalledOnce(successCallback);
387
+ this.assertNotCalled(errorCallback);
328
388
  },
329
389
  this
330
390
  );
@@ -0,0 +1,152 @@
1
+ /* ************************************************************************
2
+
3
+ qooxdoo - the javascript framework for coders
4
+
5
+ http://qooxdoo.org
6
+
7
+ Copyright:
8
+ 2025 qooxdoo contributors
9
+
10
+ License:
11
+ MIT: https://opensource.org/licenses/MIT
12
+ See the LICENSE file in the project's top-level directory for details.
13
+
14
+ Authors:
15
+ * Christian Boulanger (cboulanger)
16
+
17
+ ************************************************************************ */
18
+
19
+ /**
20
+ * Tests for qx.io.jsonrpc.Client with qx.test.io.request.PostMessage transport
21
+ * @ignore(Worker)
22
+ * @ignore(self)
23
+ */
24
+ qx.Class.define("qx.test.io.jsonrpc.PostMessageClient", {
25
+ extend: qx.dev.unit.TestCase,
26
+
27
+ include: [qx.dev.unit.MMock, qx.test.io.MAssert],
28
+
29
+ members: {
30
+ setUp() {
31
+ qx.io.jsonrpc.protocol.Request.resetId();
32
+ },
33
+
34
+ tearDown() {
35
+ this.getSandbox().restore();
36
+ },
37
+
38
+ /**
39
+ * Given a function, return a `Worker` object which calls the function
40
+ * with the function whenever the Worker receives a message from the main
41
+ * thread.
42
+ * @param {Function} fn
43
+ * @returns {Worker}
44
+ */
45
+ createOnMessageWorker(fn) {
46
+ let blob = new Blob(["self.onmessage = ", fn.toString()], {
47
+ type: "text/javascript"
48
+ });
49
+ return new Worker(URL.createObjectURL(blob));
50
+ },
51
+
52
+ async "test: receive 10 out-of-order jsonrpc responses from server, all successful"() {
53
+ qx.io.jsonrpc.protocol.Request.resetId();
54
+
55
+ // create server worker which sends a response with random delay
56
+ const server = this.createOnMessageWorker(evt => {
57
+ let request = JSON.parse(evt.data);
58
+ let id = request.id;
59
+ let response = JSON.stringify({
60
+ jsonrpc: "2.0",
61
+ result: `Result for #${id}`,
62
+ id
63
+ });
64
+ setTimeout(() => {
65
+ console.log(`Sending response for request #${id}`);
66
+ self.postMessage(response);
67
+ }, Math.random() * 1000);
68
+ });
69
+ const transport = new qx.io.transport.PostMessage(server);
70
+ const client = new qx.io.jsonrpc.Client(transport);
71
+ const promises = [];
72
+ // send 10 requests without waiting for the response
73
+ for (let i = 0; i < 10; i++) {
74
+ let request = new qx.io.jsonrpc.protocol.Request("someMethod", ["foo"]);
75
+ await client.send(request);
76
+ promises.push(request.getPromise());
77
+ }
78
+ // Make sure that alle requests have been responded to, i.e. that their promises have
79
+ // been settled.
80
+ const allSettledPromise = Promise.allSettled(promises);
81
+ this.observePromise(allSettledPromise);
82
+ this.assertPromisePending(allSettledPromise);
83
+ this.wait(2000, () => {
84
+ this.assertPromiseFulfilled(
85
+ allSettledPromise,
86
+ "Some request promises were not fulfilled"
87
+ );
88
+ });
89
+ },
90
+
91
+ async "test: receive 100 out-of-order jsonrpc responses from server, with jsonrpc errors"() {
92
+ qx.io.jsonrpc.protocol.Request.resetId();
93
+
94
+ // create server worker which sends a response with random delay
95
+ // some requests fail
96
+ const server = this.createOnMessageWorker(evt => {
97
+ let request = JSON.parse(evt.data);
98
+ let id = request.id;
99
+ let response = JSON.stringify(
100
+ Math.random() > 0.5
101
+ ? {
102
+ jsonrpc: "2.0",
103
+ result: "OK",
104
+ id
105
+ }
106
+ : {
107
+ jsonrpc: "2.0",
108
+ error: {
109
+ code: -1,
110
+ message: "error",
111
+ },
112
+ id
113
+ }
114
+ );
115
+ setTimeout(() => {
116
+ self.postMessage(response);
117
+ }, Math.random() * 1000);
118
+ });
119
+ const transport = new qx.io.transport.PostMessage(server);
120
+ const client = new qx.io.jsonrpc.Client(transport);
121
+ const promises = [];
122
+ const spies = [];
123
+ // send 100 requests without waiting for the response
124
+ for (let i = 0; i < 100; i++) {
125
+ let request = new qx.io.jsonrpc.protocol.Request("someMethod", ["foo"]);
126
+ await client.send(request);
127
+ // create a spy to receive error objects
128
+ let spy = this.spy()
129
+ spies.push(spy)
130
+ promises.push(request.getPromise().then(spy,spy));
131
+ }
132
+ // Make sure that alle requests have been responded to, i.e. that their promises have
133
+ // been settled.
134
+ const allSettledPromise = Promise.allSettled(promises);
135
+ this.observePromise(allSettledPromise);
136
+ this.assertPromisePending(allSettledPromise);
137
+ this.wait(2000, () => {
138
+ this.assertPromiseSettled(
139
+ allSettledPromise,
140
+ "Some request promises were not settled"
141
+ );
142
+ for(let spy of spies){
143
+ const firstCallArg = spy.getCall(0)?.args[0];
144
+ this.assert(
145
+ (firstCallArg instanceof qx.io.exception.Protocol || typeof firstCallArg == "string"),
146
+ `An unexpected error occurred: "${firstCallArg}"`
147
+ );
148
+ }
149
+ });
150
+ }
151
+ }
152
+ });
@@ -25,11 +25,7 @@ qx.Class.define("qx.test.io.jsonrpc.Protocol", {
25
25
  },
26
26
  members: {
27
27
  "test: JSON-RPC request message object"() {
28
- let message = new qx.io.jsonrpc.protocol.Request("foo", [
29
- "bar",
30
- 1,
31
- false
32
- ]);
28
+ let message = new qx.io.jsonrpc.protocol.Request("foo", ["bar",1,false], 1);
33
29
 
34
30
  let expected = {
35
31
  id: 1,
@@ -145,7 +145,7 @@ qx.Class.define("qx.test.ui.embed.Iframe", {
145
145
  this.assertEquals("Hello World!", innerText);
146
146
  });
147
147
  }.bind(this),
148
- 4000
148
+ 5000
149
149
  );
150
150
 
151
151
  this.wait(10000);
@@ -294,6 +294,10 @@ qx.Class.define("qx.test.ui.form.TextArea", {
294
294
  this.skip();
295
295
  }
296
296
 
297
+ if ((qx.core.Environment.get("engine.name") === "gecko") && (navigator.plugins.length == 0)) {
298
+ this.skip("test disabled on headless firefox browser");
299
+ }
300
+
297
301
  var textArea = this.__textArea;
298
302
  textArea.set({
299
303
  autoSize: true,
@@ -24,6 +24,12 @@ qx.Class.define("qx.test.util.DeferredCall", {
24
24
  if (navigator.plugins.length == 0) {
25
25
  this.skip("test disabled on headless browsers");
26
26
  }
27
+
28
+ if (qx.core.Environment.get("browser.name") == "safari") {
29
+ this.skip(
30
+ "we can not detect headless mode in safari"
31
+ );
32
+ }
27
33
 
28
34
  var fail = function () {
29
35
  throw new Error("fail");
@@ -30,7 +30,7 @@ qx.Class.define("qx.test.util.Function", {
30
30
  this.assertNotCalled(test);
31
31
  debouncedTest(false);
32
32
  this.wait(
33
- 100,
33
+ 250,
34
34
  function () {
35
35
  this.assertCalledOnce(test);
36
36
  this.assertCalledWith(test, false);
@@ -52,7 +52,7 @@ qx.Class.define("qx.test.util.Function", {
52
52
  debouncedTest(true);
53
53
  debouncedTest(false);
54
54
  this.wait(
55
- 100,
55
+ 250,
56
56
  function () {
57
57
  this.assertCalledTwice(test);
58
58
  this.assertCalledWith(test, false);
@@ -1,3 +1,16 @@
1
+ /* ************************************************************************
2
+
3
+ qooxdoo - the new era of web development
4
+
5
+ http://qooxdoo.org
6
+
7
+ Copyright:
8
+ 2020 Henner Kollmann
9
+
10
+ License:
11
+ MIT: https://opensource.org/licenses/MIT
12
+ See the LICENSE file in the project"s top-level directory for details.
13
+ ************************************************************************ */
1
14
  /**
2
15
  * This is used to add an test case for qx test
3
16
  */
@@ -47,6 +60,15 @@ qx.Class.define("qx.tool.cli.api.Test", {
47
60
  init: true
48
61
  },
49
62
 
63
+ /**
64
+ * If this special test fails exit process
65
+ */
66
+ failFast: {
67
+ check: "Boolean",
68
+ nullable: false,
69
+ init: false
70
+ },
71
+
50
72
  /**
51
73
  * The test function called by qx test
52
74
  *
@@ -789,9 +789,6 @@ Framework: v${await this.getQxVersion()} in ${await this.getQxPath()}`);
789
789
  }
790
790
 
791
791
  for (let filename of classFiles) {
792
- if (this.argv.verbose) {
793
- qx.tool.compiler.Console.info(`Processing ${filename} ...`);
794
- }
795
792
  await metaDb.addFile(filename, !!this.argv.clean);
796
793
  }
797
794
  await metaDb.reparseAll();
@@ -1348,6 +1345,15 @@ Framework: v${await this.getQxVersion()} in ${await this.getQxPath()}`);
1348
1345
  if (data.environment) {
1349
1346
  maker.setEnvironment(data.environment);
1350
1347
  }
1348
+
1349
+ /*
1350
+ Libraries have to be added first because there is qx library
1351
+ which includes a framework version
1352
+ */
1353
+ for (let library of librariesArray) {
1354
+ maker.getAnalyser().addLibrary(library);
1355
+ }
1356
+
1351
1357
  let targetEnvironment = {
1352
1358
  "qx.version": maker.getAnalyser().getQooxdooVersion(),
1353
1359
  "qx.compiler.targetType": target.getType(),
@@ -1398,6 +1404,14 @@ Framework: v${await this.getQxVersion()} in ${await this.getQxPath()}`);
1398
1404
 
1399
1405
  maker.getAnalyser().setBabelConfig(babelConfig);
1400
1406
 
1407
+ let browserifyConfig = qx.lang.Object.clone(data.browserify || {}, true);
1408
+ browserifyConfig.options = browserifyConfig.options || {};
1409
+ qx.lang.Object.mergeWith(
1410
+ browserifyConfig.options,
1411
+ targetConfig.browserifyOptions || {}
1412
+ );
1413
+ maker.getAnalyser().setBrowserifyConfig(browserifyConfig);
1414
+
1401
1415
  var addCreatedAt =
1402
1416
  targetConfig["addCreatedAt"] || t.argv["addCreatedAt"];
1403
1417
  if (addCreatedAt) {
@@ -1409,10 +1423,6 @@ Framework: v${await this.getQxVersion()} in ${await this.getQxPath()}`);
1409
1423
  maker.getAnalyser().setVerboseCreatedAt(true);
1410
1424
  }
1411
1425
 
1412
- for (let library of librariesArray) {
1413
- maker.getAnalyser().addLibrary(library);
1414
- }
1415
-
1416
1426
  let allApplicationTypes = {};
1417
1427
  appConfigs.forEach(appConfig => {
1418
1428
  var app = (appConfig.app = new qx.tool.compiler.app.Application(
@@ -99,7 +99,7 @@ qx.Class.define("qx.tool.cli.commands.Test", {
99
99
  let exitCode = evt.getData();
100
100
  // overwrite error code only in case of errors
101
101
  if (exitCode !== 0 && argv.failFast) {
102
- process.exit(exitCode);
102
+ process.exit(Math.min(255, exitCode));
103
103
  }
104
104
  });
105
105
  },
@@ -152,6 +152,9 @@ qx.Class.define("qx.tool.cli.commands.Test", {
152
152
  }
153
153
  // overwrite error code only in case of errors
154
154
  if (exitCode !== 0) {
155
+ if (test.getFailFast()) {
156
+ this.argv.failFast = true;
157
+ }
155
158
  this.setExitCode(exitCode);
156
159
  }
157
160
  });
@@ -194,6 +197,9 @@ qx.Class.define("qx.tool.cli.commands.Test", {
194
197
 
195
198
  this.addListener("afterStart", async () => {
196
199
  qx.tool.compiler.Console.info(`Running unit tests`);
200
+ if (this.argv.verbose) {
201
+ console.log(this.argv);
202
+ }
197
203
  await this.fireDataEventAsync("runTests", this);
198
204
  if (
199
205
  this.getCompilerApi() &&
@@ -126,6 +126,13 @@ qx.Class.define("qx.tool.compiler.Analyser", {
126
126
  check: "Object"
127
127
  },
128
128
 
129
+ /** configuration of browserify */
130
+ browserifyConfig: {
131
+ init: null,
132
+ nullable: true,
133
+ check: "Object"
134
+ },
135
+
129
136
  /** list of global ignores */
130
137
  ignores: {
131
138
  init: [],
@@ -962,14 +962,15 @@ qx.Class.define("qx.tool.compiler.ClassFile", {
962
962
 
963
963
  CallExpression(path) {
964
964
  const name = collapseMemberExpression(path.node.callee);
965
+ const env = t.__analyser.getEnvironment();
965
966
 
966
967
  if (
968
+ env["qx.environment.allowRuntimeMutations"] !== true &&
967
969
  (name === "qx.core.Environment.select" ||
968
970
  name === "qx.core.Environment.get") &&
969
- types.isLiteral(path.node.arguments[0])
971
+ types.isLiteral(path.node.arguments[0])
970
972
  ) {
971
973
  const arg = path.node.arguments[0];
972
- const env = t.__analyser.getEnvironment();
973
974
  const envValue = env[arg.value];
974
975
 
975
976
  if (envValue !== undefined) {
@@ -1349,8 +1350,12 @@ qx.Class.define("qx.tool.compiler.ClassFile", {
1349
1350
  t.__hasDefer = true;
1350
1351
  t.__inDefer = true;
1351
1352
  }
1352
- t.__classMeta.functionName = FUNCTION_NAMES[keyName] || keyName;
1353
- if (FUNCTION_NAMES[keyName] !== undefined) {
1353
+ var isSpecialFunctionName =
1354
+ Object.keys(FUNCTION_NAMES).includes(keyName);
1355
+ t.__classMeta.functionName = isSpecialFunctionName
1356
+ ? FUNCTION_NAMES[keyName]
1357
+ : keyName;
1358
+ if (isSpecialFunctionName) {
1354
1359
  makeMeta(keyName, null, functionNode);
1355
1360
  }
1356
1361
  enterFunction(path, functionNode);
@@ -1393,8 +1398,9 @@ qx.Class.define("qx.tool.compiler.ClassFile", {
1393
1398
  }
1394
1399
  var keyName = getKeyName(prop.key);
1395
1400
  checkValidTopLevel(path);
1396
-
1397
- if (FUNCTION_NAMES[keyName] !== undefined) {
1401
+ var isSpecialFunctionName =
1402
+ Object.keys(FUNCTION_NAMES).includes(keyName);
1403
+ if (isSpecialFunctionName) {
1398
1404
  let val = path.node.value;
1399
1405
  val.leadingComments = (path.node.leadingComments || []).concat(
1400
1406
  val.leadingComments || []
@@ -109,11 +109,6 @@ qx.Class.define("qx.tool.compiler.MetaExtraction", {
109
109
 
110
110
  const babelCore = require("@babel/core");
111
111
  let src = await fs.promises.readFile(classFilename, "utf8");
112
- let babelConfig = {
113
- options: {
114
- modules: false
115
- }
116
- };
117
112
 
118
113
  let plugins = [require("@babel/plugin-syntax-jsx"), this.__plugin()];
119
114
 
@@ -373,10 +373,10 @@ qx.Class.define("qx.tool.compiler.targets.Target", {
373
373
 
374
374
  appMeta.setSourceUri(mapTo ? mapTo : targetUri + "transpiled/");
375
375
  mapTo = this.getPathMapping(
376
- path.join(appRootDir, this.getOutputDir(), "resource/")
376
+ path.join(appRootDir, this.getOutputDir(), "resource")
377
377
  );
378
378
 
379
- appMeta.setResourceUri(mapTo ? mapTo : targetUri + "resource/");
379
+ appMeta.setResourceUri(mapTo ? mapTo : targetUri + "resource");
380
380
 
381
381
  const requiredLibs = application.getRequiredLibraries();
382
382
 
@@ -158,12 +158,18 @@ qx.Class.define("qx.tool.compiler.targets.meta.Browserify", {
158
158
  builtins.process = builtins._process;
159
159
 
160
160
  return new Promise((resolve, reject) => {
161
- let b = browserify([], {
161
+ const options = {
162
162
  builtins: builtins,
163
163
  ignoreMissing: true,
164
164
  insertGlobals: true,
165
165
  detectGlobals: true
166
- });
166
+ };
167
+ qx.lang.Object.mergeWith(
168
+ options,
169
+ this.getAppMeta().getAnalyser().getBrowserifyConfig()?.options || {},
170
+ false
171
+ );
172
+ let b = browserify([], options);
167
173
 
168
174
  b._mdeps.on("missing", (id, parent) => {
169
175
  let message = [];