neo.mjs 3.2.8 → 4.0.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.
package/README.md CHANGED
@@ -54,7 +54,7 @@ neo.mjs offers two different setups which follow the exact same API.
54
54
  You can switch between <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker">dedicated</a> and
55
55
  <a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker">shared</a> workers at any point.
56
56
 
57
- <img src="https://raw.githubusercontent.com/neomjs/pages/master/resources/images/workers-setup-canvas.png">
57
+ <img src="https://raw.githubusercontent.com/neomjs/pages/master/resources/images/workers-setup-v4.png">
58
58
 
59
59
  The dedicated workers setup uses 4 threads (CPUs).
60
60
  Most parts of the frameworks as well as your apps and components live within the app worker.
@@ -8,6 +8,12 @@ import ServiceBase from '../src/worker/ServiceBase.mjs';
8
8
  * @singleton
9
9
  */
10
10
  class ServiceWorker extends ServiceBase {
11
+ /**
12
+ * @member {String} workerId='service'
13
+ * @protected
14
+ */
15
+ workerId = 'service'
16
+
11
17
  static getConfig() {return {
12
18
  /**
13
19
  * @member {String} className='Neo.ServiceWorker'
@@ -18,12 +24,7 @@ class ServiceWorker extends ServiceBase {
18
24
  * @member {Boolean} singleton=true
19
25
  * @protected
20
26
  */
21
- singleton: true,
22
- /**
23
- * @member {String} workerId='service'
24
- * @protected
25
- */
26
- workerId: 'service'
27
+ singleton: true
27
28
  }}
28
29
  }
29
30
 
@@ -32,6 +32,7 @@ program
32
32
  .option('-i, --info', 'print environment debug info')
33
33
  .option('-a, --appName <value>')
34
34
  .option('-m, --mainThreadAddons <value>', `Comma separated list of:\n${addonChoices.join(', ')}\nDefaults to DragDrop, Stylesheet`)
35
+ .option('-s, --useServiceWorker <value>', '"yes", "no"')
35
36
  .option('-t, --themes <value>', ['all', ...themeFolders, 'none'].join(", "))
36
37
  .option('-u, --useSharedWorkers <value>', '"yes", "no"')
37
38
  .allowUnknownOption()
@@ -105,11 +106,22 @@ if (programOpts.info) {
105
106
  });
106
107
  }
107
108
 
109
+ if (!programOpts.useServiceWorker) {
110
+ questions.push({
111
+ type : 'list',
112
+ name : 'useServiceWorker',
113
+ message: 'Do you want to use a ServiceWorker for caching assets?',
114
+ choices: ['yes', 'no'],
115
+ default: 'no'
116
+ });
117
+ }
118
+
108
119
  inquirer.prompt(questions).then(answers => {
109
120
  let appName = programOpts.appName || answers.appName,
110
121
  mainThreadAddons = programOpts.mainThreadAddons || answers.mainThreadAddons,
111
122
  themes = programOpts.themes || answers.themes,
112
123
  useSharedWorkers = programOpts.useSharedWorkers || answers.useSharedWorkers,
124
+ useServiceWorker = programOpts.useServiceWorker || answers.useServiceWorker,
113
125
  lAppName = appName.toLowerCase(),
114
126
  appPath = 'apps/' + lAppName + '/',
115
127
  dir = 'apps/' + lAppName,
@@ -181,6 +193,10 @@ if (programOpts.info) {
181
193
  neoConfig.useSharedWorkers = true;
182
194
  }
183
195
 
196
+ if (useServiceWorker !== 'no') {
197
+ neoConfig.useServiceWorker = true;
198
+ }
199
+
184
200
  if (!insideNeo) {
185
201
  neoConfig.workerBasePath = '../../node_modules/neo.mjs/src/worker/';
186
202
  }
@@ -15,8 +15,10 @@ export default env => {
15
15
 
16
16
  if (filenameConfig.workers) {
17
17
  Object.entries(filenameConfig.workers).forEach(([key, value]) => {
18
+ // Inside a neo workspace, we want to use the SW inside the top level apps folder,
19
+ // to allow overriding its logic
18
20
  if (key === env.worker) {
19
- entry[key] = path.resolve(neoPath, value.input);
21
+ entry[key] = path.resolve(key === 'service' && !insideNeo ? cwd : neoPath, value.input);
20
22
  }
21
23
  });
22
24
  }
@@ -15,8 +15,10 @@ export default env => {
15
15
 
16
16
  if (filenameConfig.workers) {
17
17
  Object.entries(filenameConfig.workers).forEach(([key, value]) => {
18
+ // Inside a neo workspace, we want to use the SW inside the top level apps folder,
19
+ // to allow overriding its logic
18
20
  if (key === env.worker) {
19
- entry[key] = path.resolve(neoPath, value.input);
21
+ entry[key] = path.resolve(key === 'service' && !insideNeo ? cwd : neoPath, value.input);
20
22
  }
21
23
  });
22
24
  }
@@ -8,6 +8,12 @@ import ServiceBase from '../src/worker/ServiceBase.mjs';
8
8
  * @singleton
9
9
  */
10
10
  class ServiceWorker extends ServiceBase {
11
+ /**
12
+ * @member {String} workerId='service'
13
+ * @protected
14
+ */
15
+ workerId = 'service'
16
+
11
17
  static getConfig() {return {
12
18
  /**
13
19
  * @member {String} className='Neo.ServiceWorker'
@@ -18,12 +24,7 @@ class ServiceWorker extends ServiceBase {
18
24
  * @member {Boolean} singleton=true
19
25
  * @protected
20
26
  */
21
- singleton: true,
22
- /**
23
- * @member {String} workerId='service'
24
- * @protected
25
- */
26
- workerId: 'service'
27
+ singleton: true
27
28
  }}
28
29
  }
29
30
 
@@ -9,11 +9,26 @@ import Viewport from '../../../src/container/Viewport.mjs';
9
9
  */
10
10
  class MainContainer extends Viewport {
11
11
  static getConfig() {return {
12
+ /**
13
+ * @member {String} className='Neo.examples.preloadingAssets.view.MainContainer'
14
+ * @protected
15
+ */
12
16
  className: 'Neo.examples.preloadingAssets.view.MainContainer',
17
+ /**
18
+ * @member {Boolean} autoMount=true
19
+ */
13
20
  autoMount: true,
21
+ /**
22
+ * @member {Neo.controller.Component} controller=MainContainerController
23
+ */
14
24
  controller: MainContainerController,
15
- layout : {ntype: 'fit'},
16
-
25
+ /**
26
+ * @member {Object} layout={ntype:'fit'}
27
+ */
28
+ layout: {ntype: 'fit'},
29
+ /**
30
+ * @member {Object[]} items
31
+ */
17
32
  items: [{
18
33
  module: TabContainer,
19
34
  height: 300,
@@ -33,7 +48,7 @@ class MainContainer extends Viewport {
33
48
 
34
49
  vdom: {
35
50
  tag: 'img',
36
- src: '../../resources/examples/ai_images/000150.jpg'
51
+ src: 'https://raw.githubusercontent.com/neomjs/pages/master/resources/examples/ai_images/000150.jpg'
37
52
  }
38
53
  }, {
39
54
  tabButtonConfig: {
@@ -43,10 +58,12 @@ class MainContainer extends Viewport {
43
58
 
44
59
  vdom: {
45
60
  tag: 'img',
46
- src: '../../resources/examples/ai_images/000074.jpg'
61
+ src: 'https://raw.githubusercontent.com/neomjs/pages/master/resources/examples/ai_images/000074.jpg'
47
62
  }
48
63
  }],
49
-
64
+ /**
65
+ * @member {Object} listeners
66
+ */
50
67
  listeners: {
51
68
  activeIndexChange: 'onActiveIndexChange',
52
69
  mounted : 'onMounted'
@@ -28,7 +28,7 @@ class MainContainerController extends Component {
28
28
 
29
29
  setTimeout(() => {
30
30
  Neo.ServiceWorker.preloadAssets({
31
- files: ['../resources/examples/ai_images/000074.jpg']
31
+ files: ['https://raw.githubusercontent.com/neomjs/pages/master/resources/examples/ai_images/000074.jpg']
32
32
  }).then(data => {
33
33
  console.log(data);
34
34
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "3.2.8",
3
+ "version": "4.0.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -34,21 +34,21 @@
34
34
  },
35
35
  "homepage": "https://neomjs.github.io/pages/",
36
36
  "dependencies": {
37
- "@fortawesome/fontawesome-free": "^6.0.0",
37
+ "@fortawesome/fontawesome-free": "^6.1.1",
38
38
  "@material/mwc-button": "^0.25.3",
39
39
  "@material/mwc-textfield": "^0.25.3",
40
- "autoprefixer": "^10.4.2",
41
- "chalk": "^5.0.0",
40
+ "autoprefixer": "^10.4.4",
41
+ "chalk": "^5.0.1",
42
42
  "clean-webpack-plugin": "^4.0.0",
43
- "commander": "^9.0.0",
44
- "cssnano": "^5.1.0",
43
+ "commander": "^9.1.0",
44
+ "cssnano": "^5.1.5",
45
45
  "envinfo": "^7.8.1",
46
46
  "fs-extra": "^10.0.1",
47
47
  "highlightjs-line-numbers.js": "^2.8.0",
48
- "inquirer": "^8.1.5",
48
+ "inquirer": "^8.2.2",
49
49
  "neo-jsdoc": "^1.0.1",
50
50
  "neo-jsdoc-x": "^1.0.4",
51
- "postcss": "^8.4.7",
51
+ "postcss": "^8.4.12",
52
52
  "sass": "^1.49.9",
53
53
  "webpack": "^5.70.0",
54
54
  "webpack-cli": "^4.9.2",
@@ -1,6 +1,5 @@
1
- import Base from '../../core/Base.mjs';
2
- import RemoteMethodAccess from '../../worker/mixin/RemoteMethodAccess.mjs';
3
- import WorkerManager from '../../worker/Manager.mjs';
1
+ import Base from '../../core/Base.mjs';
2
+ import WorkerManager from '../../worker/Manager.mjs';
4
3
 
5
4
  /**
6
5
  * Creates a ServiceWorker instance, in case Neo.config.useServiceWorker is set to true
@@ -15,15 +14,6 @@ class ServiceWorker extends Base {
15
14
  * @protected
16
15
  */
17
16
  className: 'Neo.main.addon.ServiceWorker',
18
- /**
19
- * @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
20
- */
21
- mixins: [RemoteMethodAccess],
22
- /**
23
- * @member {ServiceWorkerRegistration|null} registration=null
24
- * @protected
25
- */
26
- registration: null,
27
17
  /**
28
18
  * @member {Boolean} singleton=true
29
19
  * @protected
@@ -42,24 +32,33 @@ class ServiceWorker extends Base {
42
32
  fileName = devMode ? 'ServiceWorker.mjs' : 'serviceworker.js',
43
33
  folder = window.location.pathname.includes('/examples/') ? 'examples/' : 'apps/',
44
34
  opts = devMode ? {type: 'module'} : {},
35
+ path = (devMode ? config.basePath : config.workerBasePath) + (devMode ? folder : '') + fileName,
45
36
  serviceWorker = navigator.serviceWorker;
46
37
 
47
- serviceWorker.register(config.basePath + folder + fileName, opts)
48
- .then(registration => {
49
- me.registration = registration;
38
+ window.addEventListener('beforeunload', me.onBeforeUnload.bind(me));
50
39
 
40
+ serviceWorker.register(path, opts)
41
+ .then(registration => {
51
42
  serviceWorker.ready.then(() => {
52
43
  serviceWorker.onmessage = WorkerManager.onWorkerMessage.bind(WorkerManager);
53
44
 
54
45
  WorkerManager.sendMessage('service', {
55
- action : 'registerNeoConfig',
56
- channelPort: registration.active,
57
- data : config
46
+ action: 'registerNeoConfig',
47
+ data : config
58
48
  });
59
49
  });
60
50
  })
61
51
  }
62
52
  }
53
+
54
+ /**
55
+ *
56
+ */
57
+ onBeforeUnload() {
58
+ WorkerManager.sendMessage('service', {
59
+ action: 'unregisterPort'
60
+ });
61
+ }
63
62
  }
64
63
 
65
64
  Neo.applyClassConfig(ServiceWorker);
@@ -8,6 +8,19 @@ import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
8
8
  * @abstract
9
9
  */
10
10
  class ServiceBase extends Base {
11
+ /**
12
+ * @member {String} cacheName='neo-runtime'
13
+ */
14
+ cacheName = 'neo-runtime'
15
+ /**
16
+ * @member {String[]} cachePaths
17
+ */
18
+ cachePaths = [
19
+ 'raw.githubusercontent.com/',
20
+ '/dist/production/',
21
+ '/fontawesome',
22
+ '/resources/'
23
+ ]
11
24
  /**
12
25
  * @member {Object[]|null} channelPorts=null
13
26
  * @protected
@@ -28,6 +41,11 @@ class ServiceBase extends Base {
28
41
  * @protected
29
42
  */
30
43
  remotes = []
44
+ /**
45
+ * @member {String|null} workerId=null
46
+ * @protected
47
+ */
48
+ workerId = null
31
49
 
32
50
  static getConfig() {return {
33
51
  /**
@@ -35,19 +53,6 @@ class ServiceBase extends Base {
35
53
  * @protected
36
54
  */
37
55
  className: 'Neo.worker.ServiceBase',
38
- /**
39
- * @member {String} cacheName='neo-runtime'
40
- */
41
- cacheName: 'neo-runtime',
42
- /**
43
- * @member {String[]|null} cachePaths
44
- */
45
- cachePaths: [
46
- 'raw.githubusercontent.com/',
47
- '/dist/production/',
48
- '/fontawesome',
49
- '/resources/'
50
- ],
51
56
  /**
52
57
  * @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
53
58
  */
@@ -61,14 +66,10 @@ class ServiceBase extends Base {
61
66
  app: [
62
67
  'clearCache',
63
68
  'clearCaches',
64
- 'preloadAssets'
69
+ 'preloadAssets',
70
+ 'removeAssets'
65
71
  ]
66
- },
67
- /**
68
- * @member {String|null} workerId=null
69
- * @protected
70
- */
71
- workerId: null
72
+ }
72
73
  }}
73
74
 
74
75
  /**
@@ -95,19 +96,20 @@ class ServiceBase extends Base {
95
96
 
96
97
  /**
97
98
  * @param {String} name=this.cacheName
99
+ * @returns {Object}
98
100
  */
99
- clearCache(name=this.cacheName) {
100
- caches.keys()
101
- .then(cacheNames => cacheNames.filter(cacheName => cacheName === name))
102
- .then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
101
+ async clearCache(name=this.cacheName) {
102
+ await caches.delete(name);
103
+ return {success: true}
103
104
  }
104
105
 
105
106
  /**
106
- *
107
+ * @returns {Object}
107
108
  */
108
- clearCaches() {
109
- caches.keys()
110
- .then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
109
+ async clearCaches() {
110
+ let keys = await caches.keys();
111
+ await Promise.all(keys.map(name => caches.delete(name)));
112
+ return {success: true}
111
113
  }
112
114
 
113
115
  /**
@@ -255,6 +257,19 @@ class ServiceBase extends Base {
255
257
  this.onConnect(event.source);
256
258
  }
257
259
 
260
+ /**
261
+ * @param {Object} msg
262
+ * @param {ExtendableMessageEvent} event
263
+ */
264
+ onUnregisterPort(msg, event) {
265
+ for (let [index, value] of this.channelPorts.entries()) {
266
+ if (value.clientId === event.source.id) {
267
+ this.channelPorts.splice(index, 1);
268
+ break;
269
+ }
270
+ }
271
+ }
272
+
258
273
  /**
259
274
  * @param {Object} data
260
275
  * @param {String} [data.cacheName=this.cacheName]
@@ -280,9 +295,7 @@ class ServiceBase extends Base {
280
295
  hasMatch = !!asset;
281
296
  }
282
297
 
283
- if (!hasMatch) {
284
- items.push(item);
285
- }
298
+ !hasMatch && items.push(item);
286
299
  }
287
300
 
288
301
  if (items.length > 0) {
@@ -311,6 +324,44 @@ class ServiceBase extends Base {
311
324
  });
312
325
  }
313
326
 
327
+ /**
328
+ * You can either pass an url, an array of urls or an object with additional options
329
+ * See: https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete
330
+ * @param {String|String[]|Object} data
331
+ * @param {String|String[]} data.assets
332
+ * @param {String} data.cacheName=this.cacheName
333
+ * @param {Object} data.options
334
+ * @param {Boolean} data.options.ignoreMethod=false
335
+ * @param {Boolean} data.options.ignoreSearch=false
336
+ * @param {Boolean} data.options.ignoreVary=false
337
+ * @returns {Object}
338
+ */
339
+ async removeAssets(data) {
340
+ if (!Neo.isObject(data)) {
341
+ data = {
342
+ assets: data
343
+ };
344
+ }
345
+
346
+ let assets = data.assets,
347
+ cacheName = data.cacheName || this.cacheName,
348
+ options = data.options || {},
349
+ cache = await caches.open(cacheName),
350
+ promises = [];
351
+
352
+ if (!Array.isArray(assets)) {
353
+ assets = [assets];
354
+ }
355
+
356
+ assets.forEach(asset => {
357
+ promises.push(cache.delete(asset, options));
358
+ });
359
+
360
+ await Promise.all(promises);
361
+
362
+ return {success: true};
363
+ }
364
+
314
365
  /**
315
366
  * @param {String} dest app, data, main or vdom (excluding the current worker)
316
367
  * @param {Object} opts configs for Neo.worker.Message