@hyperspan/framework 0.0.2 → 0.1.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 +3 -81
- package/package.json +29 -29
- package/src/assets.ts +141 -0
- package/src/clientjs/hyperspan-client.ts +7 -175
- package/src/clientjs/idiomorph.esm.js +1278 -0
- package/src/clientjs/preact.ts +1 -0
- package/src/server.ts +298 -146
- package/.prettierrc +0 -7
- package/build.ts +0 -29
- package/bun.lockb +0 -0
- package/dist/index.d.ts +0 -50
- package/dist/index.js +0 -468
- package/dist/server.d.ts +0 -109
- package/dist/server.js +0 -1941
- package/src/app.ts +0 -186
- package/src/clientjs/idomorph.esm.js +0 -854
- package/src/document.ts +0 -10
- package/src/forms.ts +0 -110
- package/src/html.test.ts +0 -69
- package/src/html.ts +0 -342
- package/src/index.ts +0 -14
package/src/document.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { html } from './html';
|
|
2
|
-
import { clientCSSFile, clientJSFile } from './server';
|
|
3
|
-
|
|
4
|
-
export function HyperspanStyles() {
|
|
5
|
-
return html`<link rel="stylesheet" href="/_hs/css/${clientCSSFile}" />`;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function HyperspanScripts() {
|
|
9
|
-
return html`<script src="/_hs/js/${clientJSFile}"></script>`;
|
|
10
|
-
}
|
package/src/forms.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { Idiomorph } from './clientjs/idomorph.esm';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Return JSON data structure for a given FormData object
|
|
5
|
-
* Accounts for array fields (e.g. name="options[]" or <select multiple>)
|
|
6
|
-
*
|
|
7
|
-
* @link https://stackoverflow.com/a/75406413
|
|
8
|
-
*/
|
|
9
|
-
export function formDataToJSON(formData: FormData): Record<string, string | string[]> {
|
|
10
|
-
let object = {};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Parses FormData key xxx`[x][x][x]` fields into array
|
|
14
|
-
*/
|
|
15
|
-
const parseKey = (key: string) => {
|
|
16
|
-
const subKeyIdx = key.indexOf('[');
|
|
17
|
-
|
|
18
|
-
if (subKeyIdx !== -1) {
|
|
19
|
-
const keys = [key.substring(0, subKeyIdx)];
|
|
20
|
-
key = key.substring(subKeyIdx);
|
|
21
|
-
|
|
22
|
-
for (const match of key.matchAll(/\[(?<key>.*?)]/gm)) {
|
|
23
|
-
if (match.groups) {
|
|
24
|
-
keys.push(match.groups.key);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return keys;
|
|
28
|
-
} else {
|
|
29
|
-
return [key];
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Recursively iterates over keys and assigns key/values to object
|
|
35
|
-
*/
|
|
36
|
-
const assign = (keys: string[], value: FormDataEntryValue, object: any): void => {
|
|
37
|
-
const key = keys.shift();
|
|
38
|
-
|
|
39
|
-
// When last key in the iterations
|
|
40
|
-
if (key === '' || key === undefined) {
|
|
41
|
-
return object.push(value);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (Reflect.has(object, key)) {
|
|
45
|
-
// If key has been found, but final pass - convert the value to array
|
|
46
|
-
if (keys.length === 0) {
|
|
47
|
-
if (!Array.isArray(object[key])) {
|
|
48
|
-
object[key] = [object[key], value];
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
// Recurse again with found object
|
|
53
|
-
return assign(keys, value, object[key]);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Create empty object for key, if next key is '' do array instead, otherwise set value
|
|
57
|
-
if (keys.length >= 1) {
|
|
58
|
-
object[key] = keys[0] === '' ? [] : {};
|
|
59
|
-
return assign(keys, value, object[key]);
|
|
60
|
-
} else {
|
|
61
|
-
object[key] = value;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
for (const pair of formData.entries()) {
|
|
66
|
-
assign(parseKey(pair[0]), pair[1], object);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return object;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Submit form data to route and replace contents with response
|
|
74
|
-
*/
|
|
75
|
-
export function formSubmitToRoute(e: Event, form: HTMLFormElement) {
|
|
76
|
-
e.preventDefault();
|
|
77
|
-
|
|
78
|
-
const formUrl = form.getAttribute('action') || '';
|
|
79
|
-
const formData = new FormData(form);
|
|
80
|
-
const method = form.getAttribute('method')?.toUpperCase() || 'POST';
|
|
81
|
-
|
|
82
|
-
let response: Response;
|
|
83
|
-
|
|
84
|
-
fetch(formUrl, { body: formData, method })
|
|
85
|
-
.then((res: Response) => {
|
|
86
|
-
const isRedirect = [301, 302].includes(res.status);
|
|
87
|
-
|
|
88
|
-
// Is response a redirect? If so, let's follow it in the client!
|
|
89
|
-
if (isRedirect) {
|
|
90
|
-
const newUrl = res.headers.get('Location');
|
|
91
|
-
if (newUrl) {
|
|
92
|
-
window.location.assign(newUrl);
|
|
93
|
-
}
|
|
94
|
-
return '';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
response = res;
|
|
98
|
-
return res.text();
|
|
99
|
-
})
|
|
100
|
-
.then((content: string | boolean) => {
|
|
101
|
-
// No content = DO NOTHING (redirect or something else happened)
|
|
102
|
-
if (!content) {
|
|
103
|
-
return '';
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
console.log('Got content =', content, response);
|
|
107
|
-
|
|
108
|
-
Idiomorph.morph(form, content);
|
|
109
|
-
});
|
|
110
|
-
}
|
package/src/html.test.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'bun:test';
|
|
2
|
-
import { compressHTMLString, html, renderToString, renderFunctionToString } from './html';
|
|
3
|
-
|
|
4
|
-
describe('html', () => {
|
|
5
|
-
const htmlPromise1 = () => Promise.resolve('content_promise1');
|
|
6
|
-
const htmlPromise2 = () => Promise.resolve('content_promise2');
|
|
7
|
-
const asyncHtml1 = async () => html`<div>content_asynchtml1</div>`;
|
|
8
|
-
const asyncNestedHtml1 = async () =>
|
|
9
|
-
html`<div>content_asyncnestedhtml1</div>
|
|
10
|
-
${asyncHtml1()}`;
|
|
11
|
-
|
|
12
|
-
it('should escape HTML by default', async () => {
|
|
13
|
-
const testHtml = '<br />';
|
|
14
|
-
const result = html`
|
|
15
|
-
<ul>
|
|
16
|
-
<li>${testHtml}</li>
|
|
17
|
-
<li>${htmlPromise1()}</li>
|
|
18
|
-
</ul>
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
const content = compressHTMLString(await renderToString(result));
|
|
22
|
-
|
|
23
|
-
expect(content).toContain('content_promise1');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should handle pure string templates', async () => {
|
|
27
|
-
const result = html`<p>Hello World!</p>`;
|
|
28
|
-
|
|
29
|
-
expect(await renderToString(result)).toEqual('<p>Hello World!</p>');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should handle arrays of strings', async () => {
|
|
33
|
-
const testHtml = ['<br />', '<br />'];
|
|
34
|
-
const result = html`<div>${testHtml}</div>`;
|
|
35
|
-
|
|
36
|
-
expect(await renderToString(result)).toEqual('<div><br /><br /></div>');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should handle nested async promises returning templates', async () => {
|
|
40
|
-
const result = html`<div>${asyncNestedHtml1()}</div>`;
|
|
41
|
-
const content = compressHTMLString(await renderToString(result));
|
|
42
|
-
|
|
43
|
-
expect(content).toContain('content_asyncnestedhtml1');
|
|
44
|
-
expect(content).toContain('content_asynchtml1');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Streaming
|
|
48
|
-
/*
|
|
49
|
-
describe('streaming response', () => {
|
|
50
|
-
it('should handle nested async promises returning templates', async () => {
|
|
51
|
-
const result = html`<div>${asyncNestedHtml1()}</div>`;
|
|
52
|
-
const stream = renderToStream(result);
|
|
53
|
-
|
|
54
|
-
expect(stream).toEqual(
|
|
55
|
-
'<div><div>content_asyncnestedhtml1</div><div>content_asynchtml1</div></div>'
|
|
56
|
-
);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
describe('renderFunctionToString', () => {
|
|
62
|
-
it('should render fat arrow function to string', async () => {
|
|
63
|
-
const fn = () => 'blah!';
|
|
64
|
-
const fns = renderFunctionToString(fn);
|
|
65
|
-
|
|
66
|
-
expect(fns).toEqual("function () { return 'blah'; }");
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
});
|
package/src/html.ts
DELETED
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
import escapeHTML from 'escape-html';
|
|
2
|
-
import { md5 } from './clientjs/md5';
|
|
3
|
-
|
|
4
|
-
const IS_CLIENT = typeof window !== 'undefined';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Template object - used so it will be possible to (eventually) pass down context
|
|
8
|
-
*/
|
|
9
|
-
export class HSTemplate {
|
|
10
|
-
__hsTemplate = true;
|
|
11
|
-
content: any[];
|
|
12
|
-
constructor(content: any[]) {
|
|
13
|
-
this.content = content;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* HTML template
|
|
19
|
-
*/
|
|
20
|
-
export function html(strings: TemplateStringsArray, ...values: any[]): HSTemplate {
|
|
21
|
-
const content: any[] = [];
|
|
22
|
-
|
|
23
|
-
// String templates only?
|
|
24
|
-
if (values.length === 0) {
|
|
25
|
-
content.push({ kind: 'string_safe', value: strings.join('\n') });
|
|
26
|
-
return new HSTemplate(content);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let i = 0;
|
|
30
|
-
for (i = 0; i < values.length; i++) {
|
|
31
|
-
content.push({ kind: 'string_safe', value: strings[i] });
|
|
32
|
-
|
|
33
|
-
let tValue = values[i] === undefined || values[i] === null || values[i] === '' ? '' : values[i];
|
|
34
|
-
|
|
35
|
-
if (!Array.isArray(tValue)) {
|
|
36
|
-
tValue = [tValue];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
for (let j = 0; j < tValue.length; j++) {
|
|
40
|
-
content.push({ kind: _typeOf(tValue[j]), value: tValue[j] });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
content.push({ kind: 'string_safe', value: strings[i] });
|
|
44
|
-
|
|
45
|
-
return new HSTemplate(content);
|
|
46
|
-
}
|
|
47
|
-
// Allow raw/unescaped HTML
|
|
48
|
-
html.raw = (value: string) => {
|
|
49
|
-
return new HSTemplate([{ kind: 'string_safe', value }]);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type TRenderPromise = {
|
|
53
|
-
id: string;
|
|
54
|
-
pending: boolean;
|
|
55
|
-
value?: any;
|
|
56
|
-
promise: Promise<any>;
|
|
57
|
-
};
|
|
58
|
-
async function* _render(
|
|
59
|
-
obj: any,
|
|
60
|
-
promises: Array<TRenderPromise> = [],
|
|
61
|
-
{ js }: { js: string[] }
|
|
62
|
-
): AsyncGenerator<string> {
|
|
63
|
-
let { kind, value } = obj;
|
|
64
|
-
let id = randomId();
|
|
65
|
-
|
|
66
|
-
if (!kind || !value) {
|
|
67
|
-
kind = _typeOf(obj);
|
|
68
|
-
value = obj;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (value instanceof HSTemplate) {
|
|
72
|
-
yield* renderToStream(value);
|
|
73
|
-
} else if (typeof value.render !== 'undefined') {
|
|
74
|
-
value.id = id;
|
|
75
|
-
yield await value.render();
|
|
76
|
-
} else if (value === undefined || value === null) {
|
|
77
|
-
yield '';
|
|
78
|
-
} else {
|
|
79
|
-
switch (kind) {
|
|
80
|
-
case 'string':
|
|
81
|
-
yield escapeHTML(value);
|
|
82
|
-
break;
|
|
83
|
-
case 'string_safe':
|
|
84
|
-
yield value;
|
|
85
|
-
break;
|
|
86
|
-
case 'array':
|
|
87
|
-
yield* value;
|
|
88
|
-
break;
|
|
89
|
-
case 'promise':
|
|
90
|
-
const promise = value.then((v: unknown) => {
|
|
91
|
-
return _render(v, promises, { js });
|
|
92
|
-
});
|
|
93
|
-
const pid = 'async_' + id;
|
|
94
|
-
promises.push({ id: pid, pending: true, promise });
|
|
95
|
-
yield* renderToStream(html`<div id="${pid}">Loading...</div>`);
|
|
96
|
-
break;
|
|
97
|
-
case 'function':
|
|
98
|
-
const fns = renderFunctionToString(value);
|
|
99
|
-
const fnId = 'fn_' + md5(fns);
|
|
100
|
-
|
|
101
|
-
// @ts-ignore
|
|
102
|
-
if (!IS_CLIENT || !window.hyperspan._fn.has(fnId)) {
|
|
103
|
-
js.push(`hyperspan.fn('${fnId}', ${fns});`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
yield `"hyperspan:${fnId}"`;
|
|
107
|
-
break;
|
|
108
|
-
case 'json':
|
|
109
|
-
yield ''; //JSON.stringify(value);
|
|
110
|
-
break;
|
|
111
|
-
case 'object':
|
|
112
|
-
if (typeof value.render === 'function') {
|
|
113
|
-
yield value.render();
|
|
114
|
-
} else if (typeof value.toString === 'function') {
|
|
115
|
-
yield value.toString();
|
|
116
|
-
} else {
|
|
117
|
-
yield value;
|
|
118
|
-
}
|
|
119
|
-
break;
|
|
120
|
-
case 'generator':
|
|
121
|
-
yield* value;
|
|
122
|
-
break;
|
|
123
|
-
case 'date':
|
|
124
|
-
yield value.toISOString();
|
|
125
|
-
break;
|
|
126
|
-
default:
|
|
127
|
-
yield String(value);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Render HSTemplate to async generator that streams output to a string
|
|
134
|
-
*/
|
|
135
|
-
export async function* renderToStream(template: HSTemplate | string): AsyncGenerator<string> {
|
|
136
|
-
let promises: Array<TRenderPromise> = [];
|
|
137
|
-
let js: string[] = [];
|
|
138
|
-
|
|
139
|
-
if (typeof template === 'string') {
|
|
140
|
-
return template;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
for (let i = 0; i < template.content.length; i++) {
|
|
144
|
-
yield* _render(template.content[i], promises, { js });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
while (promises.length > 0) {
|
|
148
|
-
const promisesToRun = promises.map((p) =>
|
|
149
|
-
p.promise.then((v) => {
|
|
150
|
-
return { id: p.id, pending: false, value: v, promise: null };
|
|
151
|
-
})
|
|
152
|
-
);
|
|
153
|
-
const result = await Promise.race(promisesToRun);
|
|
154
|
-
|
|
155
|
-
yield* renderToStream(html`<template id="${result.id}_content">${result.value}</template>`);
|
|
156
|
-
|
|
157
|
-
promises = promises.filter((p) => {
|
|
158
|
-
return p.id !== result.id;
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (js.length !== 0) {
|
|
163
|
-
yield '<script>' + js.join('\n') + '</script>';
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Render HSTemplate to string (awaits/buffers entire response)
|
|
169
|
-
*/
|
|
170
|
-
export async function renderToString(template: HSTemplate | string): Promise<string> {
|
|
171
|
-
let result = '';
|
|
172
|
-
|
|
173
|
-
for await (const chunk of renderToStream(template)) {
|
|
174
|
-
result += chunk;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return result;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Strip extra spacing between HTML tags (used for tests)
|
|
182
|
-
*/
|
|
183
|
-
export function compressHTMLString(str: string) {
|
|
184
|
-
return str.replace(/(<(pre|script|style|textarea)[^]+?<\/\2)|(^|>)\s+|\s+(?=<|$)/g, '$1$3');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Generate random ID (used for promise/async resolver)
|
|
189
|
-
*/
|
|
190
|
-
function randomId() {
|
|
191
|
-
return Math.random().toString(36).substring(2, 9);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* LOL JavaScript...
|
|
196
|
-
*/
|
|
197
|
-
export function _typeOf(obj: any): string {
|
|
198
|
-
if (obj instanceof Promise) return 'promise';
|
|
199
|
-
if (obj instanceof Date) return 'date';
|
|
200
|
-
if (obj instanceof String) return 'string';
|
|
201
|
-
if (obj instanceof Number) return 'number';
|
|
202
|
-
if (obj instanceof Boolean) return 'boolean';
|
|
203
|
-
if (obj instanceof Function) return 'function';
|
|
204
|
-
if (Array.isArray(obj)) return 'array';
|
|
205
|
-
if (Number.isNaN(obj)) return 'nan';
|
|
206
|
-
if (obj === undefined) return 'undefined';
|
|
207
|
-
if (obj === null) return 'null';
|
|
208
|
-
if (isGenerator(obj)) return 'generator';
|
|
209
|
-
if (isPlainObject(obj)) return 'json';
|
|
210
|
-
return typeof obj;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function isGenerator(obj: any): boolean {
|
|
214
|
-
return obj && 'function' == typeof obj.next && 'function' == typeof obj.throw;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function isPlainObject(val: any) {
|
|
218
|
-
return Object == val.constructor;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Client component
|
|
223
|
-
*/
|
|
224
|
-
export type THSWCState = Record<string, any>;
|
|
225
|
-
export type THSWCSetStateArg = THSWCState | ((state: THSWCState) => THSWCState);
|
|
226
|
-
export type THSWC = {
|
|
227
|
-
this: THSWC;
|
|
228
|
-
state: THSWCState | undefined;
|
|
229
|
-
id: string;
|
|
230
|
-
setState: (fn: THSWCSetStateArg) => THSWCState;
|
|
231
|
-
mergeState: (newState: THSWCState) => THSWCState;
|
|
232
|
-
render: () => any;
|
|
233
|
-
};
|
|
234
|
-
export type THSWCUser = Pick<THSWC, 'render'> & Record<string, any>;
|
|
235
|
-
export function clientComponent(id: string, wc: THSWCUser) {
|
|
236
|
-
const comp = {
|
|
237
|
-
...wc,
|
|
238
|
-
state: wc.state || {},
|
|
239
|
-
id,
|
|
240
|
-
randomId() {
|
|
241
|
-
return Math.random().toString(36).substring(2, 9);
|
|
242
|
-
},
|
|
243
|
-
setState(fn: THSWCSetStateArg): THSWCState {
|
|
244
|
-
try {
|
|
245
|
-
const val = typeof fn === 'function' ? fn(this.state) : fn;
|
|
246
|
-
this.state = val;
|
|
247
|
-
const el = document.getElementById(this.id);
|
|
248
|
-
if (el) {
|
|
249
|
-
el.dataset.state = JSON.stringify(val);
|
|
250
|
-
//this.render();
|
|
251
|
-
}
|
|
252
|
-
} catch (e) {
|
|
253
|
-
console.error(e);
|
|
254
|
-
}
|
|
255
|
-
return this.state;
|
|
256
|
-
},
|
|
257
|
-
mergeState(newState: THSWCState): THSWCState {
|
|
258
|
-
return this.setState(Object.assign(this.state, newState));
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
if (typeof window !== 'undefined') {
|
|
263
|
-
// @ts-ignore
|
|
264
|
-
window.hyperspan.wc.set(id, comp);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return (attrs?: Record<string, string>, state?: Record<string, any>) => {
|
|
268
|
-
const _state = Object.assign({}, comp.state, state || {});
|
|
269
|
-
return html`
|
|
270
|
-
<script>
|
|
271
|
-
${html.raw(renderObjectToLiteralString(comp))};
|
|
272
|
-
</script>
|
|
273
|
-
<hs-wc id="${attrs?.id || id}" data-state="${JSON.stringify(_state)}"></hs-wc>
|
|
274
|
-
`;
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export function renderFunctionToString(fn: Function): string {
|
|
279
|
-
let fns = fn.toString();
|
|
280
|
-
const firstLine = fns.split('\n')[0];
|
|
281
|
-
const isFatArrow = firstLine.includes('=>');
|
|
282
|
-
const isAsync = firstLine.includes('async');
|
|
283
|
-
const hasFunctionWord = firstLine.includes('function');
|
|
284
|
-
|
|
285
|
-
// Ensure word 'function' is present
|
|
286
|
-
if (isFatArrow) {
|
|
287
|
-
fns = 'function (...args) { return (' + fns + ')(..args); }';
|
|
288
|
-
} else {
|
|
289
|
-
// Class methods can omit the 'function' word without being a fat arrow function
|
|
290
|
-
if (!hasFunctionWord) {
|
|
291
|
-
fns = 'function ' + fns;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Ensure 'async' is first word in function declration
|
|
296
|
-
if (isAsync) {
|
|
297
|
-
fns = 'async ' + fns.replace('async ', '');
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return fns;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Render object out to string literal (one level only)
|
|
305
|
-
*/
|
|
306
|
-
function renderObjectToLiteralString(obj: Record<string, any>): string {
|
|
307
|
-
const lines: string[][] = [];
|
|
308
|
-
|
|
309
|
-
let str = 'hyperspan.wc.set("' + obj.id + '", {\n';
|
|
310
|
-
|
|
311
|
-
for (const prop in obj) {
|
|
312
|
-
const kind = _typeOf(obj[prop]);
|
|
313
|
-
let val = obj[prop];
|
|
314
|
-
|
|
315
|
-
switch (kind) {
|
|
316
|
-
case 'string':
|
|
317
|
-
lines.push([prop, ': ', '"' + val + '"']);
|
|
318
|
-
break;
|
|
319
|
-
case 'object':
|
|
320
|
-
case 'json':
|
|
321
|
-
lines.push([prop, ': ', "JSON.parse('" + JSON.stringify(val) + "')"]);
|
|
322
|
-
break;
|
|
323
|
-
case 'function':
|
|
324
|
-
const fn = val.toString();
|
|
325
|
-
const isFatArrow = fn.split('\n')[0].includes('=>');
|
|
326
|
-
|
|
327
|
-
if (isFatArrow) {
|
|
328
|
-
lines.push([prop, ': ', fn]);
|
|
329
|
-
} else {
|
|
330
|
-
lines.push([fn]);
|
|
331
|
-
}
|
|
332
|
-
break;
|
|
333
|
-
default:
|
|
334
|
-
lines.push([prop, ': ', val]);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
str += lines.map((line) => line.join('') + ',').join('\n');
|
|
339
|
-
str += '\n})';
|
|
340
|
-
|
|
341
|
-
return str;
|
|
342
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hyperspan
|
|
3
|
-
* Only export html/client libs by default just incase this gets in a JS bundle
|
|
4
|
-
* Server package must be imported with 'hypserspan/server'
|
|
5
|
-
*/
|
|
6
|
-
export {
|
|
7
|
-
_typeOf,
|
|
8
|
-
clientComponent,
|
|
9
|
-
compressHTMLString,
|
|
10
|
-
html,
|
|
11
|
-
HSTemplate,
|
|
12
|
-
renderToStream,
|
|
13
|
-
renderToString,
|
|
14
|
-
} from './html';
|