apprun 3.36.1 → 3.37.1

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 (79) hide show
  1. package/CHANGELOG.md +14 -4
  2. package/README.md +37 -32
  3. package/WHATSNEW.md +12 -3
  4. package/apprun.d.ts +134 -47
  5. package/dist/apprun-code.js +2 -0
  6. package/dist/apprun-code.js.map +1 -0
  7. package/dist/apprun-dev-tools.js +1 -1
  8. package/dist/apprun-dev-tools.js.map +1 -1
  9. package/dist/apprun-html.esm.js +7 -7
  10. package/dist/apprun-html.esm.js.map +1 -1
  11. package/dist/apprun-html.js +1 -1
  12. package/dist/apprun-html.js.map +1 -1
  13. package/dist/apprun-play-html.esm.js +7 -7
  14. package/dist/apprun-play-html.esm.js.map +1 -1
  15. package/dist/apprun-play.js +1 -1
  16. package/dist/apprun-play.js.map +1 -1
  17. package/dist/apprun.esm.js +1 -1
  18. package/dist/apprun.esm.js.map +1 -1
  19. package/dist/apprun.js +1 -1
  20. package/dist/apprun.js.map +1 -1
  21. package/dist/createState.js +2 -0
  22. package/dist/createState.js.map +1 -0
  23. package/esm/add-components.js +90 -0
  24. package/esm/add-components.js.map +1 -0
  25. package/esm/app.js +5 -2
  26. package/esm/app.js.map +1 -1
  27. package/esm/apprun-code.js +51 -16
  28. package/esm/apprun-code.js.map +1 -1
  29. package/esm/apprun-dev-tools.js +31 -25
  30. package/esm/apprun-dev-tools.js.map +1 -1
  31. package/esm/apprun-play.js +50 -17
  32. package/esm/apprun-play.js.map +1 -1
  33. package/esm/apprun.js +24 -7
  34. package/esm/apprun.js.map +1 -1
  35. package/esm/component.js +13 -20
  36. package/esm/component.js.map +1 -1
  37. package/esm/createState.js +9 -0
  38. package/esm/createState.js.map +1 -0
  39. package/esm/decorator.js.map +1 -1
  40. package/esm/directive.js +1 -1
  41. package/esm/directive.js.map +1 -1
  42. package/esm/router.js.map +1 -1
  43. package/esm/shadow.js +1 -1
  44. package/esm/shadow.js.map +1 -1
  45. package/esm/type-utils.js +2 -3
  46. package/esm/type-utils.js.map +1 -1
  47. package/esm/vdom-my-new.js +8 -10
  48. package/esm/vdom-my-new.js.map +1 -1
  49. package/esm/vdom-my.js +7 -9
  50. package/esm/vdom-my.js.map +1 -1
  51. package/esm/vdom-patch.js +1 -1
  52. package/esm/vdom-patch.js.map +1 -1
  53. package/esm/version.js +1 -1
  54. package/esm/web-component.js +3 -4
  55. package/esm/web-component.js.map +1 -1
  56. package/index.html +5 -2
  57. package/jest.config.js +0 -1
  58. package/jsx-runtime.js +1 -1
  59. package/jsx-runtime.js.map +1 -1
  60. package/package.json +11 -11
  61. package/src/add-components.ts +103 -0
  62. package/src/app.ts +0 -14
  63. package/src/apprun-code.tsx +48 -10
  64. package/src/apprun-dev-tools.tsx +22 -20
  65. package/src/apprun-play.tsx +46 -9
  66. package/src/apprun.ts +28 -37
  67. package/src/component.ts +11 -8
  68. package/src/createState.ts +11 -0
  69. package/src/decorator.ts +2 -1
  70. package/src/router.ts +2 -2
  71. package/src/shadow.tsx +1 -1
  72. package/src/tsconfig.json +2 -2
  73. package/src/types.ts +62 -2
  74. package/src/version.ts +1 -1
  75. package/src/web-component.ts +4 -10
  76. package/tsconfig.jest.json +15 -6
  77. package/tsconfig.json +3 -3
  78. package/webpack.config.cjs +3 -1
  79. package/src/types/apprun.d.ts +0 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apprun",
3
- "version": "3.36.1",
3
+ "version": "3.37.1",
4
4
  "description": "JavaScript library that has Elm inspired architecture, event pub-sub and components",
5
5
  "main": "dist/apprun.js",
6
6
  "module": "esm/apprun.js",
@@ -14,7 +14,6 @@
14
14
  "jest": "jest --watch",
15
15
  "jest:debug": "node --inspect node_modules/.bin/jest --runInBand",
16
16
  "start": "webpack serve --mode development",
17
- "build:docs": "marked -i README.md -o demo/home.html & marked -i WHATSNEW.md -o demo/new.html",
18
17
  "rollup": "tsc -p src && rollup -c"
19
18
  },
20
19
  "keywords": [
@@ -45,18 +44,19 @@
45
44
  "jest": "^29.7.0",
46
45
  "jest-environment-jsdom": "^29.7.0",
47
46
  "json-loader": "^0.5.7",
48
- "lit": "^3.3.0",
47
+ "lit": "^3.3.1",
49
48
  "marked": "^16.0.0",
50
- "rollup": "^4.29.2",
51
- "ts-jest": "^29.2.5",
52
- "ts-loader": "^9.5.1",
53
- "typescript": "^5.7.2",
54
- "webpack": "^5.97.1",
49
+ "rollup": "^4.45.1",
50
+ "ts-jest": "^29.4.0",
51
+ "ts-loader": "^9.5.2",
52
+ "typescript": "^5.8.3",
53
+ "webpack": "^5.100.2",
55
54
  "webpack-cli": "^6.0.1",
56
- "webpack-dev-server": "^5.2.0",
57
- "property-information": "^7.1.0"
55
+ "webpack-dev-server": "^5.2.2"
58
56
  },
59
57
  "dependencies": {
60
- "commander": "^14.0.0"
58
+ "commander": "^14.0.0",
59
+ "immer": "^10.1.1",
60
+ "property-information": "^7.1.0"
61
61
  }
62
62
  }
@@ -0,0 +1,103 @@
1
+ import { ComponentRoute, IApp } from './types';
2
+ import app from './app'; // ADD: Global app instance access
3
+
4
+ // Type guard functions using the enhanced type system
5
+ function isComponentInstance(obj: any): boolean {
6
+ return obj && typeof obj === 'object' && typeof obj.mount === 'function';
7
+ }
8
+
9
+ function isComponentConstructor(fn: any): boolean {
10
+ return typeof fn === 'function' &&
11
+ fn.prototype &&
12
+ fn.prototype.constructor === fn &&
13
+ (fn.prototype.mount !== undefined ||
14
+ fn.prototype.state !== undefined ||
15
+ fn.prototype.view !== undefined);
16
+ }
17
+
18
+ function isFactoryFunction(fn: any): boolean {
19
+ return typeof fn === 'function' && !isComponentConstructor(fn);
20
+ }
21
+
22
+ // Recursive function resolution with enhanced type checking
23
+ async function resolveComponent(component: any, maxDepth = 3): Promise<any> {
24
+ let resolved = component;
25
+ let depth = 0;
26
+
27
+ while (isFactoryFunction(resolved) && depth < maxDepth) {
28
+ try {
29
+ const result = await resolved();
30
+ if (result === resolved) break; // Prevent infinite loops
31
+ resolved = result;
32
+ depth++;
33
+ } catch (error) {
34
+ console.error(`Error executing component function: ${error}`);
35
+ break;
36
+ }
37
+ }
38
+
39
+ return resolved;
40
+ }
41
+
42
+ export default async (element: HTMLElement | string, components: ComponentRoute) => {
43
+ for (const [route, component] of Object.entries(components)) {
44
+ if (!component || !route) {
45
+ console.error(`Invalid component configuration: component=${component}, route=${route}`);
46
+ continue;
47
+ }
48
+
49
+ // Check if it's a direct component instance
50
+ if (isComponentInstance(component)) {
51
+ const options = { route };
52
+ (component as any).mount(element, options);
53
+ continue;
54
+ }
55
+
56
+ // Check if it's a component class constructor
57
+ if (isComponentConstructor(component)) {
58
+ const instance = new (component as any)();
59
+ const options = { route };
60
+ instance.mount(element, options);
61
+ continue;
62
+ }
63
+
64
+ // At this point it must be a function - resolve it
65
+ if (isFactoryFunction(component)) {
66
+ // Resolve the function to see what it returns
67
+ let resolved = await resolveComponent(component);
68
+
69
+ // Check if resolved result is a component instance
70
+ if (isComponentInstance(resolved)) {
71
+ const options = { route };
72
+ resolved.mount(element, options);
73
+ continue;
74
+ }
75
+
76
+ // Check if resolved result is a component constructor
77
+ if (isComponentConstructor(resolved)) {
78
+ const instance = new resolved();
79
+ const options = { route };
80
+ instance.mount(element, options);
81
+ continue;
82
+ }
83
+
84
+ // If resolved result is still a function or anything else, treat original as event handler with render wrapper
85
+ app.on(route, (...args: any[]) => {
86
+ const result = (component as any)(...args);
87
+ if (typeof element === 'string') {
88
+ element = document.querySelector(element) as HTMLElement;
89
+ if (!element) {
90
+ console.error(`Element not found: ${element}`);
91
+ return;
92
+ }
93
+ }
94
+ return (app as unknown as IApp).render(element, result);
95
+ });
96
+ continue;
97
+ }
98
+
99
+ // If we get here, it's an invalid component type
100
+ console.error(`Invalid component: component must be a class, instance, or function that returns a class/instance`);
101
+ }
102
+ }
103
+
package/src/app.ts CHANGED
@@ -52,20 +52,6 @@ export class App {
52
52
 
53
53
  _events: { [key: string]: Array<{ fn: (...args: any[]) => any, options: EventOptions }> };
54
54
 
55
- public start: any;
56
- public h: any;
57
- public createElement: any;
58
- public render: any;
59
- public Fragment: any;
60
- public webComponent: any;
61
- public safeHTML: any;
62
- public use_render: any;
63
- public use_react: any;
64
- public route: any;
65
- public basePath: string; // Base path for sub-directory deployments
66
-
67
- public version: string;
68
-
69
55
  constructor() {
70
56
  this._events = {} as { [key: string]: Array<{ fn: (...args: any[]) => any, options: EventOptions }> };
71
57
  }
@@ -11,6 +11,7 @@ const styles = `
11
11
  .apprun-play {
12
12
  height: 100%;
13
13
  display: flex;
14
+ font-size: 1.1rem;
14
15
  }
15
16
 
16
17
  .apprun-play .col {
@@ -23,13 +24,20 @@ const styles = `
23
24
  }
24
25
  `;
25
26
 
27
+ const encodeHTML = code => {
28
+ return code.replace(/&/g, '&amp;')
29
+ .replace(/</g, '&lt;')
30
+ .replace(/>/g, '&gt;')
31
+ .replace(/"/g, '&quot;')
32
+ .replace(/'/g, '&#039;');
33
+ }
34
+
26
35
  const code_html = code => `<!DOCTYPE html>
27
36
  <html lang="en">
28
37
  <head>
29
38
  <meta charset="UTF-8">
30
39
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
31
40
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
32
- <script src="https://cdnjs.cloudflare.com/ajax/libs/custom-elements/1.1.2/custom-elements.min.js"></script>
33
41
  <title>AppRun Playground</title>
34
42
  <style>
35
43
  body {
@@ -37,17 +45,47 @@ const code_html = code => `<!DOCTYPE html>
37
45
  margin: 2em;
38
46
  }
39
47
  </style>
40
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
41
- <script src="https://unpkg.com/apprun/dist/apprun-html.js"></script>
48
+ <script src="https://cdn.jsdelivr.net/npm/typescript@latest"></script>
49
+ <script src="dist/apprun-html.js"></script>
42
50
  </head>
43
51
  <body>
44
- <script>
45
- Babel.registerPlugin("d", [Babel.availablePlugins["proposal-decorators"], {legacy: true}]);
46
- Babel.registerPlugin("c", [Babel.availablePlugins["proposal-class-properties"], {loose: true}]);
47
- Babel.registerPlugin("b", [Babel.availablePlugins["proposal-private-methods"], {loose: true}]);
48
- </script>
49
- <script type="text/babel" data-plugins="d, c, b">
50
- ${code}
52
+ <pre id="code" style="display:none">${encodeHTML(code)}</pre>
53
+ <script type="module">
54
+ const code = document.getElementById('code').innerText;
55
+ const compiled = ts.transpileModule(code, {
56
+ compilerOptions: {
57
+ "jsx": "react",
58
+ "jsxFactory": "app.h",
59
+ "jsxFragmentFactory": "app.Fragment",
60
+ "target": "es2020",
61
+ "module": "esnext",
62
+ },
63
+ reportDiagnostics: true,
64
+ });
65
+
66
+ if (compiled.diagnostics && compiled.diagnostics.length) {
67
+ const pre = document.createElement('pre');
68
+ pre.style = 'font-size: 10px;';
69
+ pre.innerText = compiled.diagnostics.map(d => {
70
+ const start = d.start;
71
+ const end = d.start + d.length;
72
+ const line = code.substring(0, end).split('\\n').length;
73
+ const column = code.substring(0, end).split('\\n').pop().length;
74
+ return \`Line: \${line}, Column: \${column}, \${d.messageText}\`;
75
+ }).join('\\n');
76
+ document.body.appendChild(pre);
77
+ } else {
78
+ window.onerror = function () {
79
+ const pre = document.createElement('pre');
80
+ pre.style = 'font-size: 10px;';
81
+ pre.innerText = compiled.outputText;;
82
+ document.body.appendChild(pre);
83
+ };
84
+ const script = document.createElement('script');
85
+ script.type = 'module';
86
+ script.text = compiled.outputText;
87
+ document.body.appendChild(script);
88
+ }
51
89
  </script>
52
90
  </body>
53
91
  </html>`;
@@ -47,7 +47,17 @@ function getVDOM(component) {
47
47
  return view;
48
48
  }
49
49
 
50
+ const componentCache = new Map();
50
51
  app['debug'] = true;
52
+ app.on('debug-create-component', component => {
53
+ const element = component.element;
54
+ if (!element) {
55
+ console.warn('Component created without an element:', component);
56
+ return;
57
+ }
58
+ if (componentCache.get(element)) { componentCache.get(element).push(component) }
59
+ else { componentCache.set(element, [component]) }
60
+ });
51
61
 
52
62
  window['_apprun-help'] = ['', () => {
53
63
  Object.keys(window).forEach(cmd => {
@@ -75,12 +85,6 @@ function newWin(html) {
75
85
  win.document.close();
76
86
  }
77
87
 
78
- const get_components = () => {
79
- const o = { components: {} };
80
- app.run('get-components', o);
81
- const { components } = o;
82
- return components;
83
- }
84
88
  const viewElement = element => <div>
85
89
  {element.tagName.toLowerCase()}{element.id ? '#' + element.id : ''}
86
90
  {' '}
@@ -146,7 +150,7 @@ const viewEvents = state => {
146
150
  const _events = (print?) => {
147
151
  const global_events = app['_events']
148
152
  const events = {};
149
- const cache = get_components();
153
+ const cache = componentCache;
150
154
 
151
155
  const add_component = component => component['_actions'].forEach(event => {
152
156
  events[event.name] = events[event.name] || [];
@@ -157,10 +161,6 @@ const _events = (print?) => {
157
161
  for (let [key, comps] of cache) {
158
162
  comps.forEach(add_component);
159
163
  }
160
- } else {
161
- Object.keys(cache).forEach(el =>
162
- cache[el].forEach(add_component)
163
- );
164
164
  }
165
165
  const data = [];
166
166
  Object.keys(events).forEach(event => {
@@ -183,7 +183,7 @@ const _events = (print?) => {
183
183
  }
184
184
 
185
185
  const _components = (print?) => {
186
- const components = get_components();
186
+ const components = componentCache;
187
187
  const data = [];
188
188
 
189
189
  if (components instanceof Map) {
@@ -191,11 +191,6 @@ const _components = (print?) => {
191
191
  const element = typeof key === 'string' ? document.getElementById(key) || document.querySelector(key) : key;
192
192
  data.push({ element, comps });
193
193
  }
194
- } else {
195
- Object.keys(components).forEach(el => {
196
- const element = typeof el === 'string' ? document.getElementById(el) || document.querySelector(el) : el;
197
- data.push({ element, comps: components[el] });
198
- });
199
194
  }
200
195
  if (print) {
201
196
  const vdom = viewComponents(data);
@@ -211,8 +206,15 @@ app.on('debug', p => {
211
206
  if (debugging & 2 && p.vdom) console.log(p);
212
207
  });
213
208
 
214
- window['_apprun-components'] = ['components [print]', (p) => {
215
- _components(p === 'print');
209
+ window['_apprun-components'] = ['components [print|clear]', (p) => {
210
+ if (p === 'print') {
211
+ _components(true);
212
+ } else if (p === 'clear') {
213
+ componentCache.clear();
214
+ console.log('Component cache cleared');
215
+ } else {
216
+ _components(false);
217
+ }
216
218
  }]
217
219
 
218
220
  window['_apprun-events'] = ['events [print]', (p) => {
@@ -256,7 +258,7 @@ window['_apprun'] = (strings) => {
256
258
  else window['_apprun-help'][1]();
257
259
  }
258
260
 
259
- console.info('AppRun DevTools 2.27: type "_apprun `help`" to list all available commands.');
261
+ console.info('AppRun DevTools 3.36: type "_apprun `help`" to list all available commands.');
260
262
 
261
263
  const reduxExt = window['__REDUX_DEVTOOLS_EXTENSION__'];
262
264
  if (reduxExt) {
@@ -92,13 +92,20 @@ a.button:hover {
92
92
  </div>
93
93
  </div>`;
94
94
 
95
+ const encodeHTML = code => {
96
+ return code.replace(/&/g, '&amp;')
97
+ .replace(/</g, '&lt;')
98
+ .replace(/>/g, '&gt;')
99
+ .replace(/"/g, '&quot;')
100
+ .replace(/'/g, '&#039;');
101
+ }
102
+
95
103
  const code_html = code => `<!DOCTYPE html>
96
104
  <html lang="en">
97
105
  <head>
98
106
  <meta charset="UTF-8">
99
107
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
100
108
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
101
- <script src="https://cdnjs.cloudflare.com/ajax/libs/custom-elements/1.1.2/custom-elements.min.js"></script>
102
109
  <title>AppRun Playground</title>
103
110
  <style>
104
111
  body {
@@ -106,17 +113,47 @@ const code_html = code => `<!DOCTYPE html>
106
113
  margin: 2em;
107
114
  }
108
115
  </style>
109
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
116
+ <script src="https://cdn.jsdelivr.net/npm/typescript@latest"></script>
110
117
  <script src="https://unpkg.com/apprun/dist/apprun-html.js"></script>
111
118
  </head>
112
119
  <body>
113
- <script>
114
- Babel.registerPlugin("d", [Babel.availablePlugins["proposal-decorators"], {legacy: true}]);
115
- Babel.registerPlugin("c", [Babel.availablePlugins["proposal-class-properties"], {loose: true}]);
116
- Babel.registerPlugin("b", [Babel.availablePlugins["proposal-private-methods"], {loose: true}]);
117
- </script>
118
- <script type="text/babel" data-plugins="d, c, b">
119
- ${code}
120
+ <pre id="code" style="display:none">${encodeHTML(code)}</pre>
121
+ <script type="module">
122
+ const code = document.getElementById('code').innerText;
123
+ const compiled = ts.transpileModule(code, {
124
+ compilerOptions: {
125
+ "jsx": "react",
126
+ "jsxFactory": "app.h",
127
+ "jsxFragmentFactory": "app.Fragment",
128
+ "target": "es2020",
129
+ "module": "esnext",
130
+ },
131
+ reportDiagnostics: true,
132
+ });
133
+
134
+ if (compiled.diagnostics && compiled.diagnostics.length) {
135
+ const pre = document.createElement('pre');
136
+ pre.style = 'font-size: 10px;';
137
+ pre.innerText = compiled.diagnostics.map(d => {
138
+ const start = d.start;
139
+ const end = d.start + d.length;
140
+ const line = code.substring(0, end).split('\\n').length;
141
+ const column = code.substring(0, end).split('\\n').pop().length;
142
+ return \`Line: \${line}, Column: \${column}, \${d.messageText}\`;
143
+ }).join('\\n');
144
+ document.body.appendChild(pre);
145
+ } else {
146
+ window.onerror = function () {
147
+ const pre = document.createElement('pre');
148
+ pre.style = 'font-size: 10px;';
149
+ pre.innerText = compiled.outputText;;
150
+ document.body.appendChild(pre);
151
+ };
152
+ const script = document.createElement('script');
153
+ script.type = 'module';
154
+ script.text = compiled.outputText;
155
+ document.body.appendChild(script);
156
+ }
120
157
  </script>
121
158
  </body>
122
159
  </html>`;
package/src/apprun.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  * - Router with improved null safety
11
11
  * - Web component support
12
12
  * - Type-safe React integration
13
+ * - Component batch mounting system
13
14
  *
14
15
  * Key exports:
15
16
  * - app: Global event system instance
@@ -26,6 +27,7 @@
26
27
  * - Web Components integration
27
28
  * - React compatibility layer
28
29
  * - TypeScript support with strong typing
30
+ * - Batch component mounting with addComponents(element, components)
29
31
  *
30
32
  * Type Safety Improvements (v3.35.1):
31
33
  * - Added null checks for DOM event targets
@@ -34,6 +36,10 @@
34
36
  * - Better error handling for invalid event handlers
35
37
  * - Safer element access with proper type assertions
36
38
  *
39
+ * Recent Changes:
40
+ * - Modified addComponents to accept (element, components) where components is a key-value object with routes as keys and components as values
41
+ * - Simplified component mounting API for better usability
42
+ *
37
43
  * Usage:
38
44
  * ```ts
39
45
  * import { app, Component } from 'apprun';
@@ -46,16 +52,23 @@
46
52
  * 'event': (state, ...args) => // Handle events
47
53
  * }
48
54
  * }
55
+ *
56
+ * // Mount multiple components
57
+ * app.addComponents(document.body, {
58
+ * '/home': MyComponent,
59
+ * '/about': AnotherComponent
60
+ * });
49
61
  * ```
50
62
  */
51
63
 
52
- import app, { App } from './app';
64
+ import _app, { App } from './app';
53
65
  import { createElement, render, Fragment, safeHTML } from './vdom';
54
66
  import { Component } from './component';
55
- import { VNode, View, Action, Update, EventOptions, ActionOptions, MountOptions, AppStartOptions } from './types';
67
+ import { IApp, VNode, View, Action, Update, EventOptions, ActionOptions, MountOptions, AppStartOptions, CustomElementOptions } from './types';
56
68
  import { on, update, customElement } from './decorator';
57
- import webComponent, { CustomElementOptions } from './web-component';
58
- import { Route, route, ROUTER_EVENT, ROUTER_404_EVENT } from './router';
69
+ import { route, ROUTER_EVENT, ROUTER_404_EVENT } from './router';
70
+ import webComponent from './web-component';
71
+ import addComponents from './add-components';
59
72
  import { APPRUN_VERSION } from './version';
60
73
 
61
74
  export type StatelessComponent<T = {}> = (args: T) => string | VNode | void;
@@ -64,6 +77,9 @@ type OnDecorator = {
64
77
  <E = string>(events?: E, options?: any): (target: any, key: string) => void;
65
78
  };
66
79
 
80
+ const app: IApp = _app as unknown as IApp;
81
+ export default app as IApp;
82
+
67
83
  export {
68
84
  App,
69
85
  app,
@@ -82,30 +98,6 @@ export {
82
98
  export { update as event };
83
99
  export { ROUTER_EVENT, ROUTER_404_EVENT };
84
100
  export { customElement, CustomElementOptions, AppStartOptions };
85
- export default app as IApp;
86
-
87
- export interface IApp {
88
- start<T, E = any>(element?: Element | string, model?: T, view?: View<T>, update?: Update<T, E>,
89
- options?: AppStartOptions<T>): Component<T, E>;
90
- on(name: string, fn: (...args: any[]) => void, options?: any): void;
91
- once(name: string, fn: (...args: any[]) => void, options?: any): void;
92
- off(name: string, fn: (...args: any[]) => void): void;
93
- run(name: string, ...args: any[]): number;
94
- find(name: string): any | any[];
95
- /** @deprecated Use runAsync() instead. query() will be removed in a future version. */
96
- query(name: string, ...args: any[]): Promise<any[]>;
97
- runAsync(name: string, ...args: any[]): Promise<any[]>;
98
- h(tag: string | Function, props, ...children): VNode | VNode[];
99
- createElement(tag: string | Function, props, ...children): VNode | VNode[];
100
- render(element: Element | string, node: VNode): void;
101
- Fragment(props, ...children): any[];
102
- route?: Route;
103
- webComponent(name: string, componentClass, options?: CustomElementOptions): void;
104
- safeHTML(html: string): any[];
105
- use_render(render, mode?: 0 | 1);
106
- use_react(React, ReactDOM);
107
- version: string;
108
- }
109
101
 
110
102
  if (!app.start) {
111
103
 
@@ -127,10 +119,6 @@ if (!app.start) {
127
119
  return component;
128
120
  };
129
121
 
130
- app.once = app.once || ((name: string, fn: (...args: any[]) => void, options: any = {}) => {
131
- app.on(name, fn, { ...options, once: true });
132
- });
133
-
134
122
  // Deprecated: app.query is deprecated in favor of app.runAsync
135
123
  app.query = app.query || app.runAsync;
136
124
 
@@ -143,6 +131,11 @@ if (!app.start) {
143
131
  app.on('route', url => app['route'] && app['route'](url));
144
132
 
145
133
  if (typeof document === 'object') {
134
+ let basePath = location.pathname;
135
+ if (basePath.endsWith('/')) {
136
+ basePath = basePath.slice(0, -1);
137
+ }
138
+ app.basePath = basePath;
146
139
  document.addEventListener("DOMContentLoaded", () => {
147
140
  const no_init_route = document.body.hasAttribute('apprun-no-init') || app['no-init-route'] || false;
148
141
  const use_hash = app.find('#') || app.find('#/') || false;
@@ -188,13 +181,9 @@ if (!app.start) {
188
181
  });
189
182
  }
190
183
 
191
- type ComponentType = typeof Component & {
192
- <T = any>(options?: any): (constructor: Function) => void;
193
- };
194
-
195
184
  if (typeof window === 'object') {
196
185
  const globalWindow = window as any;
197
- globalWindow['Component'] = Component as ComponentType;
186
+ globalWindow['Component'] = Component;
198
187
  globalWindow['_React'] = globalWindow['React'];
199
188
  globalWindow['React'] = app;
200
189
  globalWindow['on'] = on as OnDecorator;
@@ -249,4 +238,6 @@ if (!app.start) {
249
238
  app.render = (el, vdom) => ReactDOM.render(vdom, el);
250
239
  }
251
240
  }
241
+
242
+ app.addComponents = addComponents;
252
243
  }
package/src/component.ts CHANGED
@@ -51,16 +51,20 @@
51
51
  * ```
52
52
  */
53
53
 
54
- import app, { App } from './app';
54
+ import _app, { App } from './app';
55
+
56
+
55
57
  import { Reflect } from './decorator'
56
- import { View, Update, ActionDef, ActionOptions, MountOptions, EventOptions } from './types';
58
+ import { View, Update, ActionDef, ActionOptions, MountOptions, EventOptions, IApp } from './types';
57
59
  import directive from './directive';
58
60
  import { safeQuerySelector, safeGetElementById } from './type-utils';
59
61
 
60
- const componentCache = new Map();
61
- if (!app.find('get-components')) app.on('get-components', o => o.components = componentCache);
62
+ // const componentCache = new Map();
63
+ // if (!app.find('get-components')) app.on('get-components', o => o.components = componentCache);
64
+
65
+ export const REFRESH = state => state;
62
66
 
63
- const REFRESH = state => state;
67
+ const app = _app as unknown as IApp;
64
68
 
65
69
  export class Component<T = any, E = any> {
66
70
  static __isAppRunComponent = true;
@@ -244,9 +248,8 @@ export class Component<T = any, E = any> {
244
248
 
245
249
  this.setState(this.state, { render: !!options.render, history: true });
246
250
 
247
- if (app['debug']) {
248
- if (componentCache.get(element)) { componentCache.get(element).push(this) }
249
- else { componentCache.set(element, [this]) }
251
+ if (app['debug'] && app.find('debug-create-component')?.length) {
252
+ app.run('debug-create-component', this);
250
253
  }
251
254
  return this;
252
255
  }
@@ -0,0 +1,11 @@
1
+ import { produce, Draft } from 'immer';
2
+
3
+ export function createState<T = any>(
4
+ fn: (draft: Draft<T>, ...args: any[]) => void
5
+ ): (state: T, ...args: any[]) => T {
6
+ return (state: T, ...args: any[]): T => {
7
+ return produce(state, (draft) => {
8
+ fn(draft, ...args);
9
+ });
10
+ };
11
+ }
package/src/decorator.ts CHANGED
@@ -1,4 +1,5 @@
1
- import webComponent, { CustomElementOptions } from './web-component';
1
+ import { CustomElementOptions } from './types';
2
+ import webComponent from './web-component';
2
3
 
3
4
  /**
4
5
  * TypeScript Decorators for AppRun Components
package/src/router.ts CHANGED
@@ -297,11 +297,11 @@ const publishRoute = (name: string, ...args: any[]) => {
297
297
  app.run(ROUTER_EVENT, name, ...args);
298
298
  }
299
299
 
300
- export type Route = (url: string, ...args: any[]) => any;
300
+ import { Router } from './types';
301
301
 
302
302
  export const ROUTER_EVENT: string = '//';
303
303
  export const ROUTER_404_EVENT: string = '///';
304
- export const route: Route = (url: string) => {
304
+ export const route: Router = (url: string) => {
305
305
  if (app['lastUrl'] === url) return; // Prevent duplicate routing
306
306
  app['lastUrl'] = url;
307
307
 
package/src/shadow.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import app from './app';
1
+ import app from './apprun';
2
2
 
3
3
  export default (_, children) => {
4
4
  const el = document.createElement('section');
package/src/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "es2017",
4
- "module": "es2015",
3
+ "target": "es2020",
4
+ "module": "esnext",
5
5
  "moduleResolution": "node",
6
6
  "jsx": "react",
7
7
  "jsxFactory": "app.h",