jsgui3-server 0.0.143 → 0.0.145

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 (67) hide show
  1. package/docs/comprehensive-documentation.md +25 -6
  2. package/docs/configuration-reference.md +46 -11
  3. package/docs/controls-development.md +54 -26
  4. package/docs/jsgui3-html-improvement-ideas.md +162 -0
  5. package/docs/jsgui3-html-improvement-ideas.svg +151 -0
  6. package/docs/troubleshooting.md +9 -8
  7. package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +19 -14
  8. package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +5 -5
  9. package/examples/jsgui3-html/01) mvvm-counter/client.js +648 -0
  10. package/examples/jsgui3-html/01) mvvm-counter/server.js +21 -0
  11. package/examples/jsgui3-html/02) date-transform/client.js +764 -0
  12. package/examples/jsgui3-html/02) date-transform/server.js +21 -0
  13. package/examples/jsgui3-html/03) form-validation/client.js +1045 -0
  14. package/examples/jsgui3-html/03) form-validation/server.js +21 -0
  15. package/examples/jsgui3-html/04) data-grid/client.js +738 -0
  16. package/examples/jsgui3-html/04) data-grid/server.js +21 -0
  17. package/examples/jsgui3-html/05) master-detail/client.js +649 -0
  18. package/examples/jsgui3-html/05) master-detail/server.js +21 -0
  19. package/examples/jsgui3-html/06) theming/client.js +514 -0
  20. package/examples/jsgui3-html/06) theming/server.js +21 -0
  21. package/examples/jsgui3-html/07) mixins/client.js +465 -0
  22. package/examples/jsgui3-html/07) mixins/server.js +21 -0
  23. package/examples/jsgui3-html/08) router/client.js +372 -0
  24. package/examples/jsgui3-html/08) router/server.js +21 -0
  25. package/examples/jsgui3-html/09) resource-transform/client.js +692 -0
  26. package/examples/jsgui3-html/09) resource-transform/server.js +21 -0
  27. package/examples/jsgui3-html/10) binding-debugger/client.js +810 -0
  28. package/examples/jsgui3-html/10) binding-debugger/server.js +21 -0
  29. package/examples/jsgui3-html/README.md +48 -0
  30. package/http/responders/static/Static_Route_HTTP_Responder.js +25 -20
  31. package/lab/README.md +19 -0
  32. package/lab/experiments/window_examples_dom_audit.js +241 -0
  33. package/lab/results/window_examples_dom_audit.json +131 -0
  34. package/lab/results/window_examples_dom_audit.md +46 -0
  35. package/package.json +8 -3
  36. package/publishers/http-webpageorsite-publisher.js +8 -4
  37. package/resources/processors/bundlers/css-bundler.js +28 -173
  38. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +32 -20
  39. package/resources/processors/bundlers/style-bundler.js +288 -0
  40. package/resources/processors/compilers/SASS_Compiler.js +88 -0
  41. package/resources/processors/extractors/js/css_and_js/AST_Node/CSS_And_JS_From_JS_String_Using_AST_Node_Extractor.js +64 -68
  42. package/resources/website-css-resource.js +24 -20
  43. package/resources/website-javascript-resource-processor.js +17 -57
  44. package/resources/website-javascript-resource.js +17 -57
  45. package/serve-factory.js +38 -24
  46. package/server.js +116 -92
  47. package/tests/README.md +38 -3
  48. package/tests/bundlers.test.js +41 -32
  49. package/tests/content-analysis.test.js +19 -18
  50. package/tests/end-to-end.test.js +336 -365
  51. package/tests/error-handling.test.js +13 -11
  52. package/tests/examples-controls.e2e.test.js +13 -1
  53. package/tests/fixtures/end-to-end-client.js +54 -0
  54. package/tests/fixtures/jsgui3-html/binding_debugger_expectations.json +15 -0
  55. package/tests/fixtures/jsgui3-html/counter_expectations.json +31 -0
  56. package/tests/fixtures/jsgui3-html/data_grid_expectations.json +26 -0
  57. package/tests/fixtures/jsgui3-html/date_transform_expectations.json +26 -0
  58. package/tests/fixtures/jsgui3-html/form_validation_expectations.json +27 -0
  59. package/tests/fixtures/jsgui3-html/master_detail_expectations.json +15 -0
  60. package/tests/fixtures/jsgui3-html/mixins_expectations.json +10 -0
  61. package/tests/fixtures/jsgui3-html/resource_transform_expectations.json +11 -0
  62. package/tests/fixtures/jsgui3-html/router_expectations.json +10 -0
  63. package/tests/fixtures/jsgui3-html/theming_expectations.json +10 -0
  64. package/tests/jsgui3-html-examples.puppeteer.test.js +537 -0
  65. package/tests/sass-controls.e2e.test.js +327 -0
  66. package/tests/test-runner.js +4 -1
  67. package/tests/window-examples.puppeteer.test.js +455 -0
package/server.js CHANGED
@@ -45,9 +45,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
45
45
  };
46
46
 
47
47
 
48
- if (spec.debug !== undefined) {
49
- this.debug = spec.debug;
50
- }
48
+ if (spec.debug !== undefined) {
49
+ this.debug = spec.debug;
50
+ }
51
+
52
+ const style_config = spec.style;
53
+ const bundler_config = spec.bundler;
51
54
 
52
55
  // or src_path_client_js as well...
53
56
 
@@ -91,16 +94,18 @@ class JSGUI_Single_Process_Server extends Evented_Class {
91
94
 
92
95
  const wp_app = new Webpage({content: Ctrl});
93
96
 
94
- const opts_wp_publisher = {
95
- 'webpage': wp_app
96
-
97
- };
97
+ const opts_wp_publisher = {
98
+ 'webpage': wp_app
99
+
100
+ };
101
+ if (bundler_config !== undefined) opts_wp_publisher.bundler = bundler_config;
102
+
103
+ if (this.debug) {
104
+ opts_wp_publisher.debug = this.debug;
105
+ }
98
106
 
99
- if (this.debug) {
100
- opts_wp_publisher.debug = this.debug;
101
- }
102
-
103
- if (disk_path_client_js) opts_wp_publisher.src_path_client_js = disk_path_client_js;
107
+ if (disk_path_client_js) opts_wp_publisher.src_path_client_js = disk_path_client_js;
108
+ if (style_config !== undefined) opts_wp_publisher.style = style_config;
104
109
 
105
110
 
106
111
 
@@ -160,12 +165,18 @@ class JSGUI_Single_Process_Server extends Evented_Class {
160
165
  const ws_app = this.app = this.website = new Website(opts_website);
161
166
  // Be able to treat Webpage as an app?
162
167
 
163
- const opts_ws_publisher = {
164
- 'website': ws_app
165
- };
166
- if (disk_path_client_js) {
167
- opts_ws_publisher.disk_path_client_js = disk_path_client_js;
168
- }
168
+ const opts_ws_publisher = {
169
+ 'website': ws_app
170
+ };
171
+ if (disk_path_client_js) {
172
+ opts_ws_publisher.disk_path_client_js = disk_path_client_js;
173
+ }
174
+ if (bundler_config !== undefined) {
175
+ opts_ws_publisher.bundler = bundler_config;
176
+ }
177
+ if (style_config !== undefined) {
178
+ opts_ws_publisher.style = style_config;
179
+ }
169
180
  const ws_publisher = new HTTP_Website_Publisher(opts_ws_publisher);
170
181
  this._ws_publisher = ws_publisher;
171
182
  ws_publisher.on('ready', () => {
@@ -234,16 +245,33 @@ class JSGUI_Single_Process_Server extends Evented_Class {
234
245
  if (this.allowed_addresses && this.allowed_addresses.length) {
235
246
  arr_ipv4_addresses = arr_ipv4_addresses.filter(a => this.allowed_addresses.indexOf(a) > -1);
236
247
  }
237
- arr_ipv4_addresses = [...new Set(arr_ipv4_addresses)];
238
- console.log('IPv4 addresses to bind:', arr_ipv4_addresses);
239
- let num_to_start = arr_ipv4_addresses.length;
240
- if (num_to_start === 0) {
241
- callback('No allowed network interfaces found.');
242
- return;
243
- }
244
- const respond_not_found = (res) => {
245
- if (!res.headersSent) {
246
- const body = 'Not Found';
248
+ arr_ipv4_addresses = [...new Set(arr_ipv4_addresses)];
249
+ console.log('IPv4 addresses to bind:', arr_ipv4_addresses);
250
+ let num_to_start = arr_ipv4_addresses.length;
251
+ let started_count = 0;
252
+ let last_error = null;
253
+ let ready_raised = false;
254
+ if (num_to_start === 0) {
255
+ callback('No allowed network interfaces found.');
256
+ return;
257
+ }
258
+ const finalize_start = (err) => {
259
+ if (num_to_start !== 0) return;
260
+ if (started_count > 0) {
261
+ if (!ready_raised) {
262
+ console.log('Server ready');
263
+ this.raise('ready');
264
+ ready_raised = true;
265
+ }
266
+ if (callback) callback(null, true);
267
+ return;
268
+ }
269
+ const final_error = err || last_error || new Error('No servers started.');
270
+ if (callback) callback(final_error);
271
+ };
272
+ const respond_not_found = (res) => {
273
+ if (!res.headersSent) {
274
+ const body = 'Not Found';
247
275
  res.statusCode = 404;
248
276
  res.setHeader('Content-Type', 'text/plain; charset=utf-8');
249
277
  res.setHeader('Content-Length', Buffer.byteLength(body));
@@ -302,70 +330,66 @@ class JSGUI_Single_Process_Server extends Evented_Class {
302
330
  process_request(req, res);
303
331
  });
304
332
  this.http_servers.push(https_server);
305
- https_server.on('error', (err) => {
306
- if (err.code === 'EACCES') {
307
- console.error('Permission denied:', err.message);
308
- } else if (err.code === 'EADDRINUSE') {
309
- console.error(`Address ${ipv4_address}:${port} already in use; skipping.`);
310
- } else {
311
- console.error('https_server error:', err);
312
- }
313
- num_to_start--;
314
- if (num_to_start === 0 && callback) callback(null, true);
315
- });
316
- https_server.timeout = 10800000;
317
- https_server.listen(port, ipv4_address, () => {
318
- console.log('* Server running at https://' + ipv4_address + ':' + port + '/');
319
- num_to_start--;
320
- if (num_to_start === 0) {
321
- console.log('Server ready');
322
- this.raise('ready');
323
- if (callback) callback(null, true);
324
- }
325
- });
326
- } catch (err) {
327
- console.log('https_server err', err);
328
- num_to_start--;
329
- if (num_to_start === 0 && callback) callback(null, true);
330
- }
331
- });
332
- } else {
333
- each(arr_ipv4_addresses, (ipv4_address) => {
334
- try {
335
- var http_server = http.createServer(function(req, res) {
336
- process_request(req, res);
337
- });
338
- this.http_servers.push(http_server);
339
- http_server.on('error', (err) => {
340
- if (err.code === 'EACCES') {
341
- console.error('Permission denied:', err.message);
342
- } else if (err.code === 'EADDRINUSE') {
343
- console.error(`Address ${ipv4_address}:${port} already in use; skipping.`);
344
- } else {
345
- console.error('http_server error:', err);
346
- }
347
- num_to_start--;
348
- if (num_to_start === 0 && callback) callback(null, true);
349
- });
350
- http_server.timeout = 10800000;
351
- http_server.listen(port, ipv4_address, () => {
352
- console.log('* Server running at http://' + ipv4_address + ':' + port + '/');
353
- num_to_start--;
354
- if (num_to_start === 0) {
355
- console.log('Server ready');
356
- this.raise('ready');
357
- if (callback) callback(null, true);
358
- }
359
- });
360
- } catch (err) {
361
- console.log('http_server err', err);
362
- num_to_start--;
363
- if (num_to_start === 0 && callback) callback(null, true);
364
- }
365
- });
366
- }
367
- }
368
- });
333
+ https_server.on('error', (err) => {
334
+ last_error = err;
335
+ if (err.code === 'EACCES') {
336
+ console.error('Permission denied:', err.message);
337
+ } else if (err.code === 'EADDRINUSE') {
338
+ console.error(`Address ${ipv4_address}:${port} already in use; skipping.`);
339
+ } else {
340
+ console.error('https_server error:', err);
341
+ }
342
+ num_to_start--;
343
+ finalize_start(err);
344
+ });
345
+ https_server.timeout = 10800000;
346
+ https_server.listen(port, ipv4_address, () => {
347
+ console.log('* Server running at https://' + ipv4_address + ':' + port + '/');
348
+ started_count++;
349
+ num_to_start--;
350
+ finalize_start(null);
351
+ });
352
+ } catch (err) {
353
+ console.log('https_server err', err);
354
+ num_to_start--;
355
+ finalize_start(err);
356
+ }
357
+ });
358
+ } else {
359
+ each(arr_ipv4_addresses, (ipv4_address) => {
360
+ try {
361
+ var http_server = http.createServer(function(req, res) {
362
+ process_request(req, res);
363
+ });
364
+ this.http_servers.push(http_server);
365
+ http_server.on('error', (err) => {
366
+ last_error = err;
367
+ if (err.code === 'EACCES') {
368
+ console.error('Permission denied:', err.message);
369
+ } else if (err.code === 'EADDRINUSE') {
370
+ console.error(`Address ${ipv4_address}:${port} already in use; skipping.`);
371
+ } else {
372
+ console.error('http_server error:', err);
373
+ }
374
+ num_to_start--;
375
+ finalize_start(err);
376
+ });
377
+ http_server.timeout = 10800000;
378
+ http_server.listen(port, ipv4_address, () => {
379
+ console.log('* Server running at http://' + ipv4_address + ':' + port + '/');
380
+ started_count++;
381
+ num_to_start--;
382
+ finalize_start(null);
383
+ });
384
+ } catch (err) {
385
+ console.log('http_server err', err);
386
+ num_to_start--;
387
+ finalize_start(err);
388
+ }
389
+ });
390
+ }
391
+ }
392
+ });
369
393
  }
370
394
  });
371
395
  }
package/tests/README.md CHANGED
@@ -26,6 +26,9 @@ tests/
26
26
  ├── performance.test.js # Performance benchmarks
27
27
  ├── error-handling.test.js # Error handling and edge cases
28
28
  ├── examples-controls.e2e.test.js # Example apps regression (controls)
29
+ ├── sass-controls.e2e.test.js # Sass/CSS controls E2E coverage
30
+ ├── jsgui3-html-examples.puppeteer.test.js # Puppeteer interaction tests (jsgui3-html examples)
31
+ ├── window-examples.puppeteer.test.js # Puppeteer interaction tests (window examples)
29
32
  ├── test-runner.js # Custom test runner with reporting
30
33
  └── README.md # This file
31
34
  ```
@@ -51,6 +54,11 @@ npx mocha tests/bundlers.test.js
51
54
  npm run test:examples:controls
52
55
  ```
53
56
 
57
+ ### Run Puppeteer Window Example Tests
58
+ ```bash
59
+ npm run test:puppeteer:windows
60
+ ```
61
+
54
62
  ### Run Tests with Options
55
63
  ```bash
56
64
  # Debug mode (enables sourcemaps)
@@ -132,6 +140,32 @@ Regression coverage for a representative set of `examples/controls/*` apps:
132
140
  - Verifies `/`, `/js/js.js`, and `/css/css.css` routes
133
141
  - Ensures HTML rendering works without `Accept-Encoding`
134
142
 
143
+ ### 8. Puppeteer Window Example Tests (`window-examples.puppeteer.test.js`)
144
+
145
+ Browser-level interaction checks for selected window examples:
146
+
147
+ - Minimize/restore window state (class toggling)
148
+ - Tabbed panel tab switching visibility
149
+ - Checkbox label toggling checked state
150
+ - Date picker month header rendering
151
+
152
+ ### 9. Sass/CSS Controls E2E Tests (`sass-controls.e2e.test.js`)
153
+
154
+ Server-level integration tests for controls that define `.scss` or `.sass` styles:
155
+
156
+ - Verifies CSS + SCSS mixing order in bundled CSS output
157
+ - Confirms indented Sass compiles and is removed from JS bundles
158
+ - Checks inline CSS sourcemaps include original Sass/SCSS sources
159
+ - Ensures mixed CSS + Sass output preserves order without emitting inaccurate sourcemaps
160
+
161
+ Note: These tests are skipped if the `sass` dependency is not installed.
162
+
163
+ ### 10. JSGUI3-HTML Example Puppeteer Tests (`jsgui3-html-examples.puppeteer.test.js`)
164
+
165
+ Browser-level interaction checks for jsgui3-html examples:
166
+
167
+ - MVVM counter interactions, bindings, and validation state
168
+
135
169
  ## Configuration Examples
136
170
 
137
171
  ### Basic Minification
@@ -205,12 +239,13 @@ Total Test Suites: 8
205
239
  🎉 All tests passed! The minification, compression, and sourcemap features are working correctly.
206
240
  ```
207
241
 
208
- ## Environment Requirements
242
+ ## Environment Requirements
209
243
 
210
244
  - Node.js >= 15.0.0
211
245
  - Mocha test framework (included in package.json)
212
- - ESBuild (included in package.json)
213
- - zlib (built-in Node.js module)
246
+ - ESBuild (included in package.json)
247
+ - zlib (built-in Node.js module)
248
+ - Puppeteer (dev dependency; set `PUPPETEER_EXECUTABLE_PATH` to a local Chrome/Chromium if downloads are disabled)
214
249
 
215
250
  ## Troubleshooting
216
251
 
@@ -16,29 +16,34 @@ this.timeout(30000); // Increase timeout for bundling operations
16
16
 
17
17
  beforeEach(async function() {
18
18
  // Create a temporary test JS file
19
- testJsContent = `
20
- // Test JavaScript with CSS
21
- const css = \`
22
- .test-class {
23
- color: red;
24
- font-size: 14px;
25
- }
26
- \`;
27
-
28
- // Add CSS to document
29
- const style = document.createElement('style');
30
- style.textContent = css;
31
- document.head.appendChild(style);
32
-
33
- // Test function
34
- function testFunction() {
35
- console.log('Hello from test function');
36
- return 'test result';
37
- }
38
-
39
- // Export for testing
40
- module.exports = { testFunction };
41
- `;
19
+ testJsContent = `
20
+ class Test_Class {
21
+ constructor() {}
22
+ }
23
+
24
+ Test_Class.css = \`
25
+ .test-class {
26
+ color: red;
27
+ font-size: 14px;
28
+ }
29
+ \`;
30
+
31
+ Test_Class.scss = \`
32
+ $accent-color: #33aacc;
33
+ .sass-class {
34
+ color: $accent-color;
35
+ }
36
+ \`;
37
+
38
+ // Test function
39
+ function testFunction() {
40
+ console.log('Hello from test function');
41
+ return 'test result';
42
+ }
43
+
44
+ // Export for testing
45
+ module.exports = { testFunction, Test_Class };
46
+ `;
42
47
 
43
48
  testJsFile = path.join(__dirname, 'temp_test.js');
44
49
  await fs.writeFile(testJsFile, testJsContent);
@@ -242,14 +247,18 @@ this.timeout(30000); // Increase timeout for bundling operations
242
247
  assert.strictEqual(jsItem.extension, 'js');
243
248
  assert.strictEqual(cssItem.extension, 'css');
244
249
 
245
- // Verify CSS was extracted
246
- assert(cssItem.text.includes('.test-class'), 'CSS should contain the test class');
247
- assert(cssItem.text.includes('color: red'), 'CSS should contain the color property');
248
-
249
- // Verify JS no longer contains CSS
250
- assert(!jsItem.text.includes('.test-class'), 'JS should not contain CSS after extraction');
251
- assert(jsItem.text.includes('testFunction'), 'JS should still contain the test function');
252
- });
250
+ // Verify CSS was extracted
251
+ assert(cssItem.text.includes('.test-class'), 'CSS should contain the test class');
252
+ assert(cssItem.text.includes('color: red'), 'CSS should contain the color property');
253
+ assert(cssItem.text.includes('.sass-class'), 'CSS should contain the SCSS class');
254
+ assert(cssItem.text.includes('#33aacc'), 'CSS should contain compiled SCSS color value');
255
+
256
+ // Verify JS no longer contains CSS
257
+ assert(!jsItem.text.includes('.test-class'), 'JS should not contain CSS after extraction');
258
+ assert(!jsItem.text.includes('.sass-class'), 'JS should not contain SCSS after extraction');
259
+ assert(!jsItem.text.includes('$accent-color'), 'JS should not contain SCSS variables');
260
+ assert(jsItem.text.includes('testFunction'), 'JS should still contain the test function');
261
+ });
253
262
 
254
263
  it('should handle debug mode with sourcemaps', async function() {
255
264
  const bundler = new Advanced_JS_Bundler_Using_ESBuild({
@@ -326,4 +335,4 @@ this.timeout(30000); // Increase timeout for bundling operations
326
335
  }
327
336
  });
328
337
  });
329
- });
338
+ });
@@ -46,23 +46,24 @@ describe('Content Analysis Tests', function() {
46
46
  }
47
47
  }
48
48
 
49
- // Some CSS embedded in JS
50
- const css = \`
51
- .test-class {
52
- background-color: #ffffff;
53
- border: 1px solid #cccccc;
54
- padding: 10px;
55
- margin: 5px;
56
- }
57
- .test-class:hover {
58
- background-color: #f0f0f0;
59
- }
60
- \`;
61
-
62
- // Add CSS to document
63
- const style = document.createElement('style');
64
- style.textContent = css;
65
- document.head.appendChild(style);
49
+ TestClass.css = \`
50
+ .test-class {
51
+ background-color: #ffffff;
52
+ border: 1px solid #cccccc;
53
+ padding: 10px;
54
+ margin: 5px;
55
+ }
56
+ .test-class:hover {
57
+ background-color: #f0f0f0;
58
+ }
59
+ \`;
60
+
61
+ TestClass.scss = \`
62
+ $accent-color: #33aacc;
63
+ .sass-class {
64
+ color: $accent-color;
65
+ }
66
+ \`;
66
67
 
67
68
  // Export
68
69
  module.exports = { testFunction, testObject, TestClass };
@@ -638,4 +639,4 @@ describe('Content Analysis Tests', function() {
638
639
  assert(brotliSize < originalSize * 0.5, 'Brotli should achieve >50% compression on repetitive content');
639
640
  });
640
641
  });
641
- });
642
+ });