@slim-lang/core 1.2.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 +666 -0
- package/package.json +55 -0
- package/packages/slim/.spm +7 -0
- package/packages/slim/converters/main.slim +106 -0
- package/packages/slim/helpers/array.slim +25 -0
- package/packages/slim/helpers/path.slim +3 -0
- package/packages/slim/helpers/request.slim +102 -0
- package/packages/slim/helpers/string.slim +27 -0
- package/packages/slim/main.slim +42 -0
- package/packages/slim/parse/main.slim +25 -0
- package/packages/slim/server/main.slim +423 -0
- package/packages/slim/time/main.slim +66 -0
- package/packages/slim/types/common.slim +6 -0
- package/packages/slim/types/formats.slim +23 -0
- package/packages/slim/types/hash.slim +6 -0
- package/packages/slim/types/mails.slim +3 -0
- package/packages/slim/types/numerical.slim +9 -0
- package/packages/slim/types/time.slim +3 -0
- package/run-dev-slim.js +133 -0
- package/run-slim.js +20 -0
- package/src/bin/api/github_auth.js +89 -0
- package/src/bin/api/github_get.js +139 -0
- package/src/bin/api/github_req.js +455 -0
- package/src/bin/api/lock.js +37 -0
- package/src/bin/api/spm.js +103 -0
- package/src/bin/api/storage.js +30 -0
- package/src/bin/cli.js +404 -0
- package/src/bin/config.default.json +5 -0
- package/src/bin/helpers.js +147 -0
- package/src/bin/parsers/spm.js +174 -0
- package/src/bin/spm.js +519 -0
- package/src/checker.js +926 -0
- package/src/compile.js +230 -0
- package/src/external/classErrors.js +202 -0
- package/src/external/client.js +38 -0
- package/src/external/core.js +861 -0
- package/src/external/defaults.js +25 -0
- package/src/external/helpers.js +541 -0
- package/src/external/slim-globals.d.ts +65 -0
- package/src/external/types.js +38 -0
- package/src/format.js +81 -0
- package/src/handlers/errorHandler.js +43 -0
- package/src/handlers/parser/components.js +250 -0
- package/src/handlers/parserHandler.js +793 -0
- package/src/jsdoc.js +273 -0
- package/src/lexer.js +174 -0
- package/src/modulePaths.js +74 -0
- package/src/parser.js +818 -0
- package/src/repl.js +32 -0
- package/src/sourcemap.js +0 -0
- package/src/test-runner.js +62 -0
- package/src/transform.js +765 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Full runtime for components and Node error frames; pure code uses core.js.
|
|
2
|
+
|
|
3
|
+
import fs from "fs"
|
|
4
|
+
import { setSourceReader } from "./classErrors.js"
|
|
5
|
+
import {
|
|
6
|
+
htmlToVdom,
|
|
7
|
+
HTMLElement,
|
|
8
|
+
__html__,
|
|
9
|
+
__flush_events__,
|
|
10
|
+
__bind_events__,
|
|
11
|
+
__lifecycle__,
|
|
12
|
+
__create_host__,
|
|
13
|
+
__adopt_into__,
|
|
14
|
+
__define_element__
|
|
15
|
+
} from "./helpers.js"
|
|
16
|
+
|
|
17
|
+
export * from "./core.js"
|
|
18
|
+
|
|
19
|
+
// The bare fs import lets client bundles remove source-frame support.
|
|
20
|
+
setSourceReader(file => fs.readFileSync(file, "utf8"))
|
|
21
|
+
|
|
22
|
+
Object.assign(globalThis, {
|
|
23
|
+
htmlToVdom, HTMLElement, __html__, __flush_events__, __bind_events__, __lifecycle__,
|
|
24
|
+
__create_host__, __adopt_into__, __define_element__
|
|
25
|
+
})
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import { parseHTML } from "linkedom";
|
|
2
|
+
import { CLIENT_RUNTIME } from "./client.js";
|
|
3
|
+
|
|
4
|
+
export function getFunctionBody(fn) {
|
|
5
|
+
const str = fn.toString();
|
|
6
|
+
return str.substring(str.indexOf('{') + 1, str.lastIndexOf('}')).trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const idify = (text) => {
|
|
10
|
+
if (!text) return '';
|
|
11
|
+
|
|
12
|
+
return btoa(text + Math.random(100 * 9999) * 100)
|
|
13
|
+
.toString()
|
|
14
|
+
.trim()
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.normalize('NFD')
|
|
17
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
18
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
19
|
+
.replace(/[\s_-]+/g, '-')
|
|
20
|
+
.replace(/^-+|-+$/g, '');
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const VOID_ELEMENTS = new Set([
|
|
25
|
+
"area", "base", "br", "col", "embed", "hr", "img", "input",
|
|
26
|
+
"link", "meta", "param", "source", "track", "wbr"
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const RAW_TEXT_ELEMENTS = new Set(["script", "style", "textarea", "title"]);
|
|
30
|
+
|
|
31
|
+
// Use the platform DOM or detached linkedom nodes; static import supports bundler stubs.
|
|
32
|
+
const hasNativeDom =
|
|
33
|
+
typeof globalThis.document !== "undefined" &&
|
|
34
|
+
typeof globalThis.HTMLElement !== "undefined";
|
|
35
|
+
|
|
36
|
+
const { document: slimDocument, HTMLElement } = hasNativeDom
|
|
37
|
+
? globalThis
|
|
38
|
+
: parseHTML("<!doctype html><html><head></head><body></body></html>");
|
|
39
|
+
|
|
40
|
+
export { slimDocument, HTMLElement };
|
|
41
|
+
|
|
42
|
+
export class VNode {
|
|
43
|
+
constructor(fields) {
|
|
44
|
+
Object.assign(this, fields);
|
|
45
|
+
}
|
|
46
|
+
render() {
|
|
47
|
+
return renderVNode(this);
|
|
48
|
+
}
|
|
49
|
+
toString() {
|
|
50
|
+
return renderVNode(this);
|
|
51
|
+
}
|
|
52
|
+
toElement(doc = slimDocument) {
|
|
53
|
+
return vnodeToElement(this, doc);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function element(tag, attrs, children) {
|
|
58
|
+
return new VNode({ type: "element", tag, attrs, children });
|
|
59
|
+
}
|
|
60
|
+
function textNode(value) {
|
|
61
|
+
return new VNode({ type: "text", value });
|
|
62
|
+
}
|
|
63
|
+
function commentNode(value) {
|
|
64
|
+
return new VNode({ type: "comment", value });
|
|
65
|
+
}
|
|
66
|
+
function fragmentNode(children) {
|
|
67
|
+
return new VNode({ type: "fragment", children });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isSpace = ch => ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f";
|
|
71
|
+
const isNameStart = ch => (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z");
|
|
72
|
+
const isNameChar = ch => isNameStart(ch) || (ch >= "0" && ch <= "9") || ch === "-" || ch === "_" || ch === ":";
|
|
73
|
+
|
|
74
|
+
function parseAttributes(html, i) {
|
|
75
|
+
const attrs = {};
|
|
76
|
+
const len = html.length;
|
|
77
|
+
let selfClosing = false;
|
|
78
|
+
|
|
79
|
+
while (i < len) {
|
|
80
|
+
while (i < len && isSpace(html[i])) i++;
|
|
81
|
+
if (i >= len) break;
|
|
82
|
+
|
|
83
|
+
if (html[i] === ">") { i++; break; }
|
|
84
|
+
if (html[i] === "/" && html[i + 1] === ">") { selfClosing = true; i += 2; break; }
|
|
85
|
+
|
|
86
|
+
const nameStart = i;
|
|
87
|
+
while (i < len && !isSpace(html[i]) && html[i] !== "=" && html[i] !== ">" && html[i] !== "/") i++;
|
|
88
|
+
const name = html.slice(nameStart, i);
|
|
89
|
+
|
|
90
|
+
if (!name) { i++; continue; }
|
|
91
|
+
|
|
92
|
+
while (i < len && isSpace(html[i])) i++;
|
|
93
|
+
|
|
94
|
+
if (html[i] === "=") {
|
|
95
|
+
i++;
|
|
96
|
+
while (i < len && isSpace(html[i])) i++;
|
|
97
|
+
|
|
98
|
+
const quote = html[i];
|
|
99
|
+
let value;
|
|
100
|
+
if (quote === '"' || quote === "'") {
|
|
101
|
+
i++;
|
|
102
|
+
const start = i;
|
|
103
|
+
while (i < len && html[i] !== quote) i++;
|
|
104
|
+
value = html.slice(start, i);
|
|
105
|
+
if (i < len) i++;
|
|
106
|
+
} else {
|
|
107
|
+
const start = i;
|
|
108
|
+
while (i < len && !isSpace(html[i]) && html[i] !== ">" && !(html[i] === "/" && html[i + 1] === ">")) i++;
|
|
109
|
+
value = html.slice(start, i);
|
|
110
|
+
}
|
|
111
|
+
attrs[name] = value;
|
|
112
|
+
} else {
|
|
113
|
+
attrs[name] = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { attrs, selfClosing, end: i };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function findRawClose(html, lower, from, tag) {
|
|
121
|
+
const needle = "</" + tag;
|
|
122
|
+
let idx = from;
|
|
123
|
+
|
|
124
|
+
while (true) {
|
|
125
|
+
idx = lower.indexOf(needle, idx);
|
|
126
|
+
if (idx === -1) return { contentEnd: html.length, next: html.length };
|
|
127
|
+
|
|
128
|
+
let k = idx + needle.length;
|
|
129
|
+
while (k < html.length && isSpace(html[k])) k++;
|
|
130
|
+
if (html[k] === ">") return { contentEnd: idx, next: k + 1 };
|
|
131
|
+
|
|
132
|
+
idx += needle.length;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function parseNodes(html) {
|
|
137
|
+
const lower = html.toLowerCase();
|
|
138
|
+
const root = fragmentNode([]);
|
|
139
|
+
const stack = [root];
|
|
140
|
+
const top = () => stack[stack.length - 1];
|
|
141
|
+
const len = html.length;
|
|
142
|
+
let i = 0;
|
|
143
|
+
|
|
144
|
+
while (i < len) {
|
|
145
|
+
if (html[i] === "<") {
|
|
146
|
+
if (html.startsWith("<!--", i)) {
|
|
147
|
+
const close = html.indexOf("-->", i + 4);
|
|
148
|
+
const end = close === -1 ? len : close;
|
|
149
|
+
top().children.push(commentNode(html.slice(i + 4, end)));
|
|
150
|
+
i = close === -1 ? len : close + 3;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (html[i + 1] === "!" || html[i + 1] === "?") {
|
|
154
|
+
const close = html.indexOf(">", i);
|
|
155
|
+
i = close === -1 ? len : close + 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (html[i + 1] === "/") {
|
|
159
|
+
const close = html.indexOf(">", i);
|
|
160
|
+
const end = close === -1 ? len : close;
|
|
161
|
+
const name = html.slice(i + 2, end).trim().toLowerCase();
|
|
162
|
+
|
|
163
|
+
for (let s = stack.length - 1; s >= 1; s--) {
|
|
164
|
+
if (stack[s].type === "element" && stack[s].tag === name) {
|
|
165
|
+
stack.length = s;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
i = close === -1 ? len : close + 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (isNameStart(html[i + 1])) {
|
|
173
|
+
let j = i + 1;
|
|
174
|
+
while (j < len && isNameChar(html[j])) j++;
|
|
175
|
+
const tag = html.slice(i + 1, j).toLowerCase();
|
|
176
|
+
|
|
177
|
+
const { attrs, selfClosing, end } = parseAttributes(html, j);
|
|
178
|
+
const el = element(tag, attrs, []);
|
|
179
|
+
top().children.push(el);
|
|
180
|
+
i = end;
|
|
181
|
+
|
|
182
|
+
if (!selfClosing && !VOID_ELEMENTS.has(tag)) {
|
|
183
|
+
if (RAW_TEXT_ELEMENTS.has(tag)) {
|
|
184
|
+
const { contentEnd, next } = findRawClose(html, lower, i, tag);
|
|
185
|
+
const raw = html.slice(i, contentEnd);
|
|
186
|
+
if (raw) el.children.push(textNode(raw));
|
|
187
|
+
i = next;
|
|
188
|
+
} else {
|
|
189
|
+
stack.push(el);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
top().children.push(textNode("<"));
|
|
196
|
+
i++;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const nextLt = html.indexOf("<", i);
|
|
201
|
+
const end = nextLt === -1 ? len : nextLt;
|
|
202
|
+
top().children.push(textNode(html.slice(i, end)));
|
|
203
|
+
i = end;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return root.children;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function escapeAttribute(value) {
|
|
210
|
+
return String(value).replace(/"/g, """);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function renderAttributes(attrs) {
|
|
214
|
+
let out = "";
|
|
215
|
+
for (const key in attrs) {
|
|
216
|
+
const value = attrs[key];
|
|
217
|
+
if (value === true) out += ` ${key}`;
|
|
218
|
+
else if (value === false || value == null) continue;
|
|
219
|
+
else out += ` ${key}="${escapeAttribute(value)}"`;
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function renderVNode(node) {
|
|
225
|
+
if (node == null) return "";
|
|
226
|
+
if (typeof node === "string") return node;
|
|
227
|
+
if (Array.isArray(node)) return node.map(renderVNode).join("");
|
|
228
|
+
if (isDomNode(node)) return String(node);
|
|
229
|
+
|
|
230
|
+
switch (node.type) {
|
|
231
|
+
case "node":
|
|
232
|
+
return String(node.node);
|
|
233
|
+
case "text":
|
|
234
|
+
return node.value ?? "";
|
|
235
|
+
case "comment":
|
|
236
|
+
return `<!--${node.value ?? ""}-->`;
|
|
237
|
+
case "fragment":
|
|
238
|
+
return (node.children || []).map(renderVNode).join("");
|
|
239
|
+
case "element": {
|
|
240
|
+
const open = `<${node.tag}${renderAttributes(node.attrs)}>`;
|
|
241
|
+
if (VOID_ELEMENTS.has(node.tag)) return open;
|
|
242
|
+
const inner = (node.children || []).map(renderVNode).join("");
|
|
243
|
+
return `${open}${inner}</${node.tag}>`;
|
|
244
|
+
}
|
|
245
|
+
default:
|
|
246
|
+
return "";
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function vnodeToElement(node, doc = slimDocument) {
|
|
251
|
+
if (node == null) return doc.createTextNode("");
|
|
252
|
+
if (typeof node === "string") return doc.createTextNode(node);
|
|
253
|
+
if (typeof node.nodeType === "number") return node;
|
|
254
|
+
|
|
255
|
+
switch (node.type) {
|
|
256
|
+
case "node":
|
|
257
|
+
return node.node;
|
|
258
|
+
case "text":
|
|
259
|
+
return doc.createTextNode(node.value ?? "");
|
|
260
|
+
case "comment":
|
|
261
|
+
return doc.createComment(node.value ?? "");
|
|
262
|
+
case "fragment": {
|
|
263
|
+
const frag = doc.createDocumentFragment();
|
|
264
|
+
for (const child of node.children || []) frag.appendChild(vnodeToElement(child, doc));
|
|
265
|
+
frag.toString = fragmentToHtml;
|
|
266
|
+
return frag;
|
|
267
|
+
}
|
|
268
|
+
case "element": {
|
|
269
|
+
const el = doc.createElement(node.tag);
|
|
270
|
+
const attrs = node.attrs || {};
|
|
271
|
+
for (const key in attrs) {
|
|
272
|
+
const value = attrs[key];
|
|
273
|
+
if (value === true) el.setAttribute(key, "");
|
|
274
|
+
else if (value === false || value == null) continue;
|
|
275
|
+
else el.setAttribute(key, String(value));
|
|
276
|
+
}
|
|
277
|
+
for (const child of node.children || []) el.appendChild(vnodeToElement(child, doc));
|
|
278
|
+
return el;
|
|
279
|
+
}
|
|
280
|
+
default:
|
|
281
|
+
return doc.createTextNode("");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function fragmentToHtml() {
|
|
286
|
+
let out = "";
|
|
287
|
+
for (const child of this.childNodes) out += child.toString();
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Park live nodes while parsing templates to retain their listeners and methods.
|
|
292
|
+
const NODE_MARKER = "slim-node:";
|
|
293
|
+
let parkedNodes = [];
|
|
294
|
+
|
|
295
|
+
function isDomNode(value) {
|
|
296
|
+
return !!value && typeof value === "object" && typeof value.nodeType === "number";
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function stringifyChild(value) {
|
|
300
|
+
if (value == null || value === false) return "";
|
|
301
|
+
if (Array.isArray(value)) return value.map(stringifyChild).join("");
|
|
302
|
+
if (isDomNode(value)) return `<!--${NODE_MARKER}${parkedNodes.push(value) - 1}-->`;
|
|
303
|
+
return String(value);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function reviveParked(node) {
|
|
307
|
+
if (!node || typeof node !== "object" || isDomNode(node)) return node;
|
|
308
|
+
|
|
309
|
+
if (node.type === "comment" && typeof node.value === "string" && node.value.startsWith(NODE_MARKER)) {
|
|
310
|
+
return parkedNodes[Number(node.value.slice(NODE_MARKER.length))] ?? node;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (node.children) node.children = node.children.map(reviveParked);
|
|
314
|
+
return node;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Delegated handlers serialize for SSR and retain live closures on the client.
|
|
318
|
+
|
|
319
|
+
let eventHandlers = [];
|
|
320
|
+
|
|
321
|
+
function registerEventHandler(event, fn) {
|
|
322
|
+
const id = eventHandlers.length;
|
|
323
|
+
eventHandlers.push({ event, source: fn.toString(), fn });
|
|
324
|
+
return id;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const boundEvents = new Set();
|
|
328
|
+
|
|
329
|
+
// Client dispatch keeps closures and binds once per event type.
|
|
330
|
+
export function __bind_events__() {
|
|
331
|
+
for (const { event } of eventHandlers) {
|
|
332
|
+
if (boundEvents.has(event)) continue;
|
|
333
|
+
boundEvents.add(event);
|
|
334
|
+
document.addEventListener(event, e => {
|
|
335
|
+
const el = e.target.closest(`[data-slim-on-${event}]`);
|
|
336
|
+
if (!el) return;
|
|
337
|
+
const entry = eventHandlers[el.getAttribute(`data-slim-on-${event}`)];
|
|
338
|
+
if (entry && entry.fn) entry.fn.call(el, e);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function __flush_events__() {
|
|
344
|
+
if (eventHandlers.length === 0) return "";
|
|
345
|
+
|
|
346
|
+
const handlers = eventHandlers;
|
|
347
|
+
eventHandlers = [];
|
|
348
|
+
|
|
349
|
+
const table = handlers.map((h, i) => `${i}:${h.source}`).join(",");
|
|
350
|
+
const events = [...new Set(handlers.map(h => h.event))];
|
|
351
|
+
|
|
352
|
+
const runtime = CLIENT_RUNTIME.map(fn => fn.toString()).join("\n");
|
|
353
|
+
|
|
354
|
+
const script =
|
|
355
|
+
`(function(){` +
|
|
356
|
+
`${runtime}\n` +
|
|
357
|
+
`var H={${table}};` +
|
|
358
|
+
`[${events.map(e => JSON.stringify(e)).join(",")}].forEach(function(evt){` +
|
|
359
|
+
`document.addEventListener(evt,function(e){` +
|
|
360
|
+
`var el=e.target.closest("[data-slim-on-"+evt+"]");` +
|
|
361
|
+
`if(el){var id=el.getAttribute("data-slim-on-"+evt);` +
|
|
362
|
+
`if(H[id])H[id].call(el,e);}` +
|
|
363
|
+
`});` +
|
|
364
|
+
`});` +
|
|
365
|
+
`})();`;
|
|
366
|
+
|
|
367
|
+
return script.replace(/<\/script/gi, "<\\/script");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Run lifecycle hooks once; non-browser environments connect eagerly.
|
|
371
|
+
const __lifecycleRecords__ = new Set();
|
|
372
|
+
let __lifecycleObserver__ = null;
|
|
373
|
+
|
|
374
|
+
function __ensureLifecycleObserver__() {
|
|
375
|
+
if (__lifecycleObserver__) return true;
|
|
376
|
+
if (typeof MutationObserver === "undefined" || typeof document === "undefined") return false;
|
|
377
|
+
|
|
378
|
+
__lifecycleObserver__ = new MutationObserver(() => {
|
|
379
|
+
for (const r of [...__lifecycleRecords__]) {
|
|
380
|
+
const connected = r.el.isConnected;
|
|
381
|
+
if (connected && !r.connected) {
|
|
382
|
+
r.connected = true;
|
|
383
|
+
for (const fn of r.connectFns) fn(r.el);
|
|
384
|
+
if (!r.unmountFns.length) __lifecycleRecords__.delete(r);
|
|
385
|
+
} else if (!connected && r.connected) {
|
|
386
|
+
__lifecycleRecords__.delete(r);
|
|
387
|
+
for (const fn of r.unmountFns) fn(r.el);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
__lifecycleObserver__.observe(document.documentElement, { childList: true, subtree: true });
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export function __lifecycle__(el, connectFns, unmountFns) {
|
|
396
|
+
if ((!connectFns || !connectFns.length) && (!unmountFns || !unmountFns.length)) return;
|
|
397
|
+
|
|
398
|
+
connectFns = connectFns || [];
|
|
399
|
+
unmountFns = unmountFns || [];
|
|
400
|
+
|
|
401
|
+
const r = { el, connectFns, unmountFns, connected: false };
|
|
402
|
+
|
|
403
|
+
if (el.isConnected) {
|
|
404
|
+
r.connected = true;
|
|
405
|
+
for (const fn of connectFns) fn(el);
|
|
406
|
+
if (!unmountFns.length) return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!__ensureLifecycleObserver__()) {
|
|
410
|
+
if (!r.connected) for (const fn of connectFns) fn(el);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
__lifecycleRecords__.add(r);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Custom elements render into their host and use platform lifecycle callbacks.
|
|
418
|
+
|
|
419
|
+
export function __create_host__(tag) {
|
|
420
|
+
const doc = typeof document !== "undefined" ? document : slimDocument;
|
|
421
|
+
return doc.createElement(tag);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export function __adopt_into__(host, el) {
|
|
425
|
+
if (el.nodeType === 11) {
|
|
426
|
+
for (const child of [...el.childNodes]) host.appendChild(child);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
host.appendChild(el);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function __define_element__(tag, factory) {
|
|
434
|
+
// linkedom cannot define custom elements.
|
|
435
|
+
if (typeof customElements === "undefined" || !hasNativeDom) return;
|
|
436
|
+
if (customElements.get(tag)) return;
|
|
437
|
+
|
|
438
|
+
customElements.define(tag, class extends HTMLElement {
|
|
439
|
+
constructor() {
|
|
440
|
+
super();
|
|
441
|
+
this.__slim__ = null;
|
|
442
|
+
this.__props__ = null;
|
|
443
|
+
|
|
444
|
+
// Restore properties assigned before custom-element upgrade.
|
|
445
|
+
if (Object.prototype.hasOwnProperty.call(this, "props")) {
|
|
446
|
+
const pending = this.props;
|
|
447
|
+
delete this.props;
|
|
448
|
+
this.props = pending;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
get props() {
|
|
453
|
+
return this.__props__ ?? {};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
set props(value) {
|
|
457
|
+
this.__props__ = value ?? {};
|
|
458
|
+
if (this.__slim__) this.__slim_render__();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Convert attributes to component props, including camel-cased data attributes.
|
|
462
|
+
__slim_props__() {
|
|
463
|
+
const fromAttributes = {};
|
|
464
|
+
|
|
465
|
+
for (const attribute of this.attributes) {
|
|
466
|
+
fromAttributes[attribute.name] = attribute.value;
|
|
467
|
+
|
|
468
|
+
if (!attribute.name.startsWith("data-")) continue;
|
|
469
|
+
const key = attribute.name
|
|
470
|
+
.slice("data-".length)
|
|
471
|
+
.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
472
|
+
if (key) fromAttributes[key] = attribute.value;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return { ...fromAttributes, ...this.__props__ };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
__slim_render__() {
|
|
479
|
+
for (const fn of this.__slim__?.unmounts ?? []) fn(this);
|
|
480
|
+
this.replaceChildren();
|
|
481
|
+
this.__slim__ = factory(this.__slim_props__(), this);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
connectedCallback() {
|
|
485
|
+
if (!this.__slim__) this.__slim_render__();
|
|
486
|
+
for (const fn of this.__slim__?.connects ?? []) fn(this);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
disconnectedCallback() {
|
|
490
|
+
for (const fn of this.__slim__?.unmounts ?? []) fn(this);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export function __html__(strings, ...values) {
|
|
496
|
+
parkedNodes = [];
|
|
497
|
+
|
|
498
|
+
let out = strings[0];
|
|
499
|
+
|
|
500
|
+
for (let i = 0; i < values.length; i++) {
|
|
501
|
+
const value = values[i];
|
|
502
|
+
const eventMatch = out.match(/\son([A-Z][A-Za-z]*)=\s*$/);
|
|
503
|
+
|
|
504
|
+
if (eventMatch) {
|
|
505
|
+
out = out.slice(0, eventMatch.index);
|
|
506
|
+
if (typeof value === "function") {
|
|
507
|
+
const event = eventMatch[1].toLowerCase();
|
|
508
|
+
const id = registerEventHandler(event, value);
|
|
509
|
+
out += ` data-slim-on-${event}="${id}"`;
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
out += stringifyChild(value);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
out += strings[i + 1];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return out;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function htmlToElement(input, doc = slimDocument) {
|
|
522
|
+
return vnodeToElement(htmlToVdom(input), doc);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export function htmlToVdom(input) {
|
|
526
|
+
if (input instanceof VNode) return input;
|
|
527
|
+
|
|
528
|
+
const html = input == null ? "" : String(input);
|
|
529
|
+
const nodes = parseNodes(html).map(reviveParked);
|
|
530
|
+
parkedNodes = [];
|
|
531
|
+
|
|
532
|
+
const meaningful = nodes.filter(n => !(n.type === "text" && n.value.trim() === ""));
|
|
533
|
+
|
|
534
|
+
if (meaningful.length === 0) return fragmentNode([]);
|
|
535
|
+
if (meaningful.length === 1) {
|
|
536
|
+
const only = meaningful[0];
|
|
537
|
+
// Wrap revived DOM nodes as VNodes.
|
|
538
|
+
return isDomNode(only) ? new VNode({ type: "node", node: only }) : only;
|
|
539
|
+
}
|
|
540
|
+
return fragmentNode(meaningful);
|
|
541
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Runtime globals injected into compiled Slim output.
|
|
2
|
+
|
|
3
|
+
declare function log(...args: any[]): void
|
|
4
|
+
declare function warn(...args: any[]): void
|
|
5
|
+
declare function error(...args: any[]): void
|
|
6
|
+
declare function info(...args: any[]): void
|
|
7
|
+
declare function debug(...args: any[]): void
|
|
8
|
+
|
|
9
|
+
declare function type(value: any, properties?: any): string
|
|
10
|
+
|
|
11
|
+
type email = string
|
|
12
|
+
type url = string
|
|
13
|
+
type uuid = string
|
|
14
|
+
type positive = number
|
|
15
|
+
type negative = number
|
|
16
|
+
type natural = number
|
|
17
|
+
type nonempty = string | any[]
|
|
18
|
+
declare function onError(handler: (error: any) => void): void
|
|
19
|
+
declare function test(name: string, fn: () => void): void
|
|
20
|
+
declare function assert(condition: any, message?: string): void
|
|
21
|
+
declare function assertEqual(actual: any, expected: any, message?: string): void
|
|
22
|
+
|
|
23
|
+
declare const PI: number
|
|
24
|
+
|
|
25
|
+
declare class Struct {}
|
|
26
|
+
declare class Component {}
|
|
27
|
+
declare class Type {}
|
|
28
|
+
declare class HTMLElement {}
|
|
29
|
+
|
|
30
|
+
declare function __def_struct__(name: string, schema: any, specifications?: any, defaults?: any, extendsName?: any, methods?: any): any
|
|
31
|
+
declare function __def_enum__(name: string, schema: any): any
|
|
32
|
+
declare function __typed__(value: any, structName: any, returnMethod?: string): any
|
|
33
|
+
|
|
34
|
+
declare function __typed_variable__<T>(value: T, specification?: any, bindingId?: any, varName?: any): T
|
|
35
|
+
declare function __typed_variable_check__<T>(bindingId: any, value: T, varName?: any): T
|
|
36
|
+
declare function __typed_pattern__<T>(value: T, typeLabel: any): T
|
|
37
|
+
declare function __typed_parameter__(value: any, specification: any, bindingId: any, varName?: any, optional?: any, message?: any): any
|
|
38
|
+
declare function __typed_static_field__<T>(owner: any, field: any, value: T, specification?: any, displayName?: any): T
|
|
39
|
+
declare function __typed_static_field_check__<T>(owner: any, field: any, value: T, displayName?: any): T
|
|
40
|
+
|
|
41
|
+
declare function __type_def__(name: string, definition: any, properties?: any): any
|
|
42
|
+
declare function __type_ref__(label: string, resolve?: any): any
|
|
43
|
+
declare function __type_spec__(...references: any[]): any
|
|
44
|
+
declare function __type_spec_all__(...references: any[]): any
|
|
45
|
+
declare function __type_matches__(specification: any, value: any): boolean
|
|
46
|
+
declare function __type_label__(specification: any): string
|
|
47
|
+
declare function __argument_typed__(value: any, expectedType: any): boolean
|
|
48
|
+
|
|
49
|
+
declare function __sizeof__(value: any): number
|
|
50
|
+
declare function __is_empty__(value: any): boolean
|
|
51
|
+
declare function __copyof__(value: any): any
|
|
52
|
+
declare function __intdiv__(a: number, b: number): number
|
|
53
|
+
declare function __match_eq__(subject: any, pattern: any): boolean
|
|
54
|
+
declare function __lock_object__<T>(value: T): T
|
|
55
|
+
|
|
56
|
+
declare function __handle_sync_error__(error: any): void
|
|
57
|
+
declare function __handle_async_error__(error: any): void
|
|
58
|
+
declare function __html__(...args: any[]): any
|
|
59
|
+
declare function __flush_events__(...args: any[]): any
|
|
60
|
+
declare function htmlToVdom(...args: any[]): any
|
|
61
|
+
|
|
62
|
+
declare class StructError extends Error {}
|
|
63
|
+
declare class StructPassedError extends Error {}
|
|
64
|
+
declare class StructExpectError extends Error {}
|
|
65
|
+
declare class ArgumentDeclarationTypeError extends Error {}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Type, type } from "./core.js";
|
|
2
|
+
|
|
3
|
+
export const SlimVariableTypes = Object.create(null)
|
|
4
|
+
|
|
5
|
+
export class Struct {}
|
|
6
|
+
export class Component {}
|
|
7
|
+
export class Enum {
|
|
8
|
+
constructor(name) {
|
|
9
|
+
this.name = name;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class SlimVariableType {
|
|
13
|
+
constructor(value, expected = null) {
|
|
14
|
+
this.value = value;
|
|
15
|
+
this.kind = type(value)
|
|
16
|
+
this.expected = expected ?? this.kind
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[Symbol.toPrimitive](hint) {
|
|
20
|
+
if (typeof this.value === "function") {
|
|
21
|
+
return this.value.toString();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return this.value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class EnumValue {
|
|
28
|
+
constructor(value) {
|
|
29
|
+
this.value = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isSameType(a, b) {
|
|
34
|
+
if (typeof a === "function" && typeof b === "object") return b instanceof a
|
|
35
|
+
if (typeof b === "function" && typeof a === "object") return a instanceof b
|
|
36
|
+
|
|
37
|
+
return a === b;
|
|
38
|
+
}
|