apprun 3.36.1 → 3.37.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 +37 -32
- package/WHATSNEW.md +12 -3
- package/apprun.d.ts +134 -47
- package/dist/apprun-code.js +2 -0
- package/dist/apprun-code.js.map +1 -0
- package/dist/apprun-dev-tools.js +1 -1
- package/dist/apprun-dev-tools.js.map +1 -1
- package/dist/apprun-html.esm.js +7 -7
- package/dist/apprun-html.esm.js.map +1 -1
- package/dist/apprun-html.js +1 -1
- package/dist/apprun-html.js.map +1 -1
- package/dist/apprun-play-html.esm.js +7 -7
- package/dist/apprun-play-html.esm.js.map +1 -1
- package/dist/apprun-play.js +1 -1
- package/dist/apprun-play.js.map +1 -1
- package/dist/apprun.esm.js +1 -1
- package/dist/apprun.esm.js.map +1 -1
- package/dist/apprun.js +1 -1
- package/dist/apprun.js.map +1 -1
- package/dist/createState.js +2 -0
- package/dist/createState.js.map +1 -0
- package/esm/add-components.js +90 -0
- package/esm/add-components.js.map +1 -0
- package/esm/app.js +5 -2
- package/esm/app.js.map +1 -1
- package/esm/apprun-code.js +51 -16
- package/esm/apprun-code.js.map +1 -1
- package/esm/apprun-dev-tools.js +31 -25
- package/esm/apprun-dev-tools.js.map +1 -1
- package/esm/apprun-play.js +50 -17
- package/esm/apprun-play.js.map +1 -1
- package/esm/apprun.js +24 -7
- package/esm/apprun.js.map +1 -1
- package/esm/component.js +13 -20
- package/esm/component.js.map +1 -1
- package/esm/createState.js +9 -0
- package/esm/createState.js.map +1 -0
- package/esm/decorator.js.map +1 -1
- package/esm/directive.js +1 -1
- package/esm/directive.js.map +1 -1
- package/esm/router.js.map +1 -1
- package/esm/shadow.js +1 -1
- package/esm/shadow.js.map +1 -1
- package/esm/type-utils.js +2 -3
- package/esm/type-utils.js.map +1 -1
- package/esm/vdom-my-new.js +8 -10
- package/esm/vdom-my-new.js.map +1 -1
- package/esm/vdom-my.js +7 -9
- package/esm/vdom-my.js.map +1 -1
- package/esm/vdom-patch.js +1 -1
- package/esm/vdom-patch.js.map +1 -1
- package/esm/version.js +1 -1
- package/esm/web-component.js +3 -4
- package/esm/web-component.js.map +1 -1
- package/index.html +5 -2
- package/jest.config.js +0 -1
- package/jsx-runtime.js +1 -1
- package/jsx-runtime.js.map +1 -1
- package/package.json +10 -10
- package/src/add-components.ts +103 -0
- package/src/app.ts +0 -14
- package/src/apprun-code.tsx +48 -10
- package/src/apprun-dev-tools.tsx +22 -20
- package/src/apprun-play.tsx +46 -9
- package/src/apprun.ts +28 -37
- package/src/component.ts +11 -8
- package/src/createState.ts +11 -0
- package/src/decorator.ts +2 -1
- package/src/router.ts +2 -2
- package/src/shadow.tsx +1 -1
- package/src/tsconfig.json +2 -2
- package/src/types.ts +62 -2
- package/src/version.ts +1 -1
- package/src/web-component.ts +4 -10
- package/tsconfig.jest.json +15 -6
- package/tsconfig.json +3 -3
- package/webpack.config.cjs +3 -1
- 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.
|
|
3
|
+
"version": "3.37.0",
|
|
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,16 +44,17 @@
|
|
|
45
44
|
"jest": "^29.7.0",
|
|
46
45
|
"jest-environment-jsdom": "^29.7.0",
|
|
47
46
|
"json-loader": "^0.5.7",
|
|
48
|
-
"
|
|
47
|
+
"immer": "^10.1.1",
|
|
48
|
+
"lit": "^3.3.1",
|
|
49
49
|
"marked": "^16.0.0",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"ts-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
50
|
+
"property-information": "^7.1.0",
|
|
51
|
+
"rollup": "^4.45.1",
|
|
52
|
+
"ts-jest": "^29.4.0",
|
|
53
|
+
"ts-loader": "^9.5.2",
|
|
54
|
+
"typescript": "^5.8.3",
|
|
55
|
+
"webpack": "^5.100.2",
|
|
55
56
|
"webpack-cli": "^6.0.1",
|
|
56
|
-
"webpack-dev-server": "^5.2.
|
|
57
|
-
"property-information": "^7.1.0"
|
|
57
|
+
"webpack-dev-server": "^5.2.2"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"commander": "^14.0.0"
|
|
@@ -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
|
}
|
package/src/apprun-code.tsx
CHANGED
|
@@ -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, '&')
|
|
29
|
+
.replace(/</g, '<')
|
|
30
|
+
.replace(/>/g, '>')
|
|
31
|
+
.replace(/"/g, '"')
|
|
32
|
+
.replace(/'/g, ''');
|
|
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://
|
|
41
|
-
<script src="
|
|
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
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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>`;
|
package/src/apprun-dev-tools.tsx
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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) {
|
package/src/apprun-play.tsx
CHANGED
|
@@ -92,13 +92,20 @@ a.button:hover {
|
|
|
92
92
|
</div>
|
|
93
93
|
</div>`;
|
|
94
94
|
|
|
95
|
+
const encodeHTML = code => {
|
|
96
|
+
return code.replace(/&/g, '&')
|
|
97
|
+
.replace(/</g, '<')
|
|
98
|
+
.replace(/>/g, '>')
|
|
99
|
+
.replace(/"/g, '"')
|
|
100
|
+
.replace(/'/g, ''');
|
|
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://
|
|
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
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
|
58
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
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
|
-
|
|
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:
|
|
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