kire 0.1.3 → 0.1.4
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/dist/cjs/index.js +480 -35
- package/dist/esm/index.js +480 -35
- package/dist/types/compiler.d.ts +4 -7
- package/dist/types/kire.d.ts +8 -3
- package/dist/types/types.d.ts +10 -5
- package/dist/types/utils/error.d.ts +8 -0
- package/dist/types/utils/source-map.d.ts +26 -0
- package/kire-schema.json +20 -1
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -230,20 +230,184 @@ function parseParamDefinition(def) {
|
|
|
230
230
|
};
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// core/src/utils/source-map.ts
|
|
234
|
+
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
235
|
+
function encodeVLQ(value) {
|
|
236
|
+
let res = "";
|
|
237
|
+
let vlq = value < 0 ? -value << 1 | 1 : value << 1;
|
|
238
|
+
do {
|
|
239
|
+
let digit = vlq & 31;
|
|
240
|
+
vlq >>>= 5;
|
|
241
|
+
if (vlq > 0)
|
|
242
|
+
digit |= 32;
|
|
243
|
+
res += BASE64_CHARS[digit];
|
|
244
|
+
} while (vlq > 0);
|
|
245
|
+
return res;
|
|
246
|
+
}
|
|
247
|
+
function decodeVLQ(str, index) {
|
|
248
|
+
let result = 0;
|
|
249
|
+
let shift = 0;
|
|
250
|
+
let continuation = true;
|
|
251
|
+
let i = index;
|
|
252
|
+
while (continuation) {
|
|
253
|
+
const char = str[i++];
|
|
254
|
+
if (!char)
|
|
255
|
+
throw new Error("Invalid VLQ");
|
|
256
|
+
const digit = BASE64_CHARS.indexOf(char);
|
|
257
|
+
if (digit === -1)
|
|
258
|
+
throw new Error(`Invalid Base64 char: ${char}`);
|
|
259
|
+
continuation = (digit & 32) !== 0;
|
|
260
|
+
result += (digit & 31) << shift;
|
|
261
|
+
shift += 5;
|
|
262
|
+
}
|
|
263
|
+
const value = result & 1 ? -(result >>> 1) : result >>> 1;
|
|
264
|
+
return [value, i];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
class SourceMapGenerator {
|
|
268
|
+
file;
|
|
269
|
+
mappings = [];
|
|
270
|
+
sources = [];
|
|
271
|
+
constructor(file) {
|
|
272
|
+
this.file = file;
|
|
273
|
+
}
|
|
274
|
+
addSource(source) {
|
|
275
|
+
const index = this.sources.indexOf(source);
|
|
276
|
+
if (index === -1) {
|
|
277
|
+
this.sources.push(source);
|
|
278
|
+
return this.sources.length - 1;
|
|
279
|
+
}
|
|
280
|
+
return index;
|
|
281
|
+
}
|
|
282
|
+
addMapping(mapping) {
|
|
283
|
+
this.mappings.push(mapping);
|
|
284
|
+
}
|
|
285
|
+
toString() {
|
|
286
|
+
let lastGenLine = 1;
|
|
287
|
+
let lastGenCol = 0;
|
|
288
|
+
let lastSourceIndex = 0;
|
|
289
|
+
let lastSourceLine = 0;
|
|
290
|
+
let lastSourceCol = 0;
|
|
291
|
+
const encodedMappings = [];
|
|
292
|
+
let lineMappings = [];
|
|
293
|
+
this.mappings.sort((a, b) => {
|
|
294
|
+
if (a.genLine !== b.genLine)
|
|
295
|
+
return a.genLine - b.genLine;
|
|
296
|
+
return a.genCol - b.genCol;
|
|
297
|
+
});
|
|
298
|
+
for (const m of this.mappings) {
|
|
299
|
+
while (m.genLine > lastGenLine) {
|
|
300
|
+
encodedMappings.push(lineMappings.join(","));
|
|
301
|
+
lineMappings = [];
|
|
302
|
+
lastGenLine++;
|
|
303
|
+
lastGenCol = 0;
|
|
304
|
+
}
|
|
305
|
+
let segment = "";
|
|
306
|
+
segment += encodeVLQ(m.genCol - lastGenCol);
|
|
307
|
+
lastGenCol = m.genCol;
|
|
308
|
+
if (m.sourceLine !== undefined) {
|
|
309
|
+
const sourceIndex = m.sourceIndex ?? 0;
|
|
310
|
+
segment += encodeVLQ(sourceIndex - lastSourceIndex);
|
|
311
|
+
lastSourceIndex = sourceIndex;
|
|
312
|
+
segment += encodeVLQ(m.sourceLine - 1 - lastSourceLine);
|
|
313
|
+
lastSourceLine = m.sourceLine - 1;
|
|
314
|
+
segment += encodeVLQ(m.sourceCol - 1 - lastSourceCol);
|
|
315
|
+
lastSourceCol = m.sourceCol - 1;
|
|
316
|
+
}
|
|
317
|
+
lineMappings.push(segment);
|
|
318
|
+
}
|
|
319
|
+
encodedMappings.push(lineMappings.join(","));
|
|
320
|
+
const map = {
|
|
321
|
+
version: 3,
|
|
322
|
+
file: this.file,
|
|
323
|
+
sources: this.sources,
|
|
324
|
+
names: [],
|
|
325
|
+
mappings: encodedMappings.join(";")
|
|
326
|
+
};
|
|
327
|
+
return JSON.stringify(map);
|
|
328
|
+
}
|
|
329
|
+
toDataUri() {
|
|
330
|
+
const base64 = Buffer.from(this.toString()).toString("base64");
|
|
331
|
+
return `data:application/json;charset=utf-8;base64,${base64}`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function resolveSourceLocation(map, genLine, genCol) {
|
|
335
|
+
if (!map || !map.mappings)
|
|
336
|
+
return null;
|
|
337
|
+
const lines = map.mappings.split(";");
|
|
338
|
+
if (genLine > lines.length || genLine < 1)
|
|
339
|
+
return null;
|
|
340
|
+
let stateGenCol = 0;
|
|
341
|
+
let stateSourceIndex = 0;
|
|
342
|
+
let stateSourceLine = 0;
|
|
343
|
+
let stateSourceCol = 0;
|
|
344
|
+
let bestMatch = null;
|
|
345
|
+
for (let l = 0;l < genLine; l++) {
|
|
346
|
+
const line = lines[l];
|
|
347
|
+
stateGenCol = 0;
|
|
348
|
+
if (!line)
|
|
349
|
+
continue;
|
|
350
|
+
let i = 0;
|
|
351
|
+
while (i < line.length) {
|
|
352
|
+
const [dCol, nextI1] = decodeVLQ(line, i);
|
|
353
|
+
i = nextI1;
|
|
354
|
+
stateGenCol += dCol;
|
|
355
|
+
if (i >= line.length || line[i] === ",") {
|
|
356
|
+
if (l === genLine - 1 && stateGenCol <= genCol) {}
|
|
357
|
+
} else {
|
|
358
|
+
const [dSrcIdx, nextI2] = decodeVLQ(line, i);
|
|
359
|
+
i = nextI2;
|
|
360
|
+
stateSourceIndex += dSrcIdx;
|
|
361
|
+
const [dSrcLine, nextI3] = decodeVLQ(line, i);
|
|
362
|
+
i = nextI3;
|
|
363
|
+
stateSourceLine += dSrcLine;
|
|
364
|
+
const [dSrcCol, nextI4] = decodeVLQ(line, i);
|
|
365
|
+
i = nextI4;
|
|
366
|
+
stateSourceCol += dSrcCol;
|
|
367
|
+
if (i < line.length && line[i] !== ",") {
|
|
368
|
+
const [_, nextI5] = decodeVLQ(line, i);
|
|
369
|
+
i = nextI5;
|
|
370
|
+
}
|
|
371
|
+
if (l === genLine - 1) {
|
|
372
|
+
if (stateGenCol <= genCol) {
|
|
373
|
+
bestMatch = {
|
|
374
|
+
line: stateSourceLine + 1,
|
|
375
|
+
column: stateSourceCol + 1,
|
|
376
|
+
source: map.sources[stateSourceIndex] || ""
|
|
377
|
+
};
|
|
378
|
+
} else {
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (i < line.length && line[i] === ",")
|
|
384
|
+
i++;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return bestMatch;
|
|
388
|
+
}
|
|
389
|
+
|
|
233
390
|
// core/src/compiler.ts
|
|
234
391
|
class Compiler {
|
|
235
392
|
kire;
|
|
393
|
+
filename;
|
|
236
394
|
preBuffer = [];
|
|
237
395
|
resBuffer = [];
|
|
238
396
|
posBuffer = [];
|
|
239
397
|
usedDirectives = new Set;
|
|
240
|
-
|
|
398
|
+
generator;
|
|
399
|
+
resMappings = [];
|
|
400
|
+
constructor(kire, filename = "template.kire") {
|
|
241
401
|
this.kire = kire;
|
|
402
|
+
this.filename = filename;
|
|
403
|
+
this.generator = new SourceMapGenerator(filename);
|
|
404
|
+
this.generator.addSource(filename);
|
|
242
405
|
}
|
|
243
406
|
async compile(nodes) {
|
|
244
407
|
this.preBuffer = [];
|
|
245
408
|
this.resBuffer = [];
|
|
246
409
|
this.posBuffer = [];
|
|
410
|
+
this.resMappings = [];
|
|
247
411
|
this.usedDirectives.clear();
|
|
248
412
|
const varLocals = this.kire.$var_locals || "it";
|
|
249
413
|
await this.compileNodes(nodes);
|
|
@@ -253,22 +417,53 @@ class Compiler {
|
|
|
253
417
|
`);
|
|
254
418
|
const pos = this.posBuffer.join(`
|
|
255
419
|
`);
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
420
|
+
const sanitizedKeys = Array.from(this.kire.$globals.keys()).filter((key) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key));
|
|
421
|
+
const destructuring = sanitizedKeys.length > 0 ? `var { ${sanitizedKeys.join(", ")} } = $ctx.$globals;` : "";
|
|
422
|
+
const startCode = `
|
|
423
|
+
${destructuring}
|
|
424
|
+
let ${varLocals} = $ctx.$props;
|
|
425
|
+
${pre}
|
|
426
|
+
`;
|
|
427
|
+
const startCodeLines = startCode.split(`
|
|
428
|
+
`);
|
|
429
|
+
let currentGenLine = startCodeLines.length;
|
|
430
|
+
if (!this.kire.production) {
|
|
431
|
+
const bufferLineOffsets = new Map;
|
|
432
|
+
for (let i = 0;i < this.resBuffer.length; i++) {
|
|
433
|
+
bufferLineOffsets.set(i, currentGenLine);
|
|
434
|
+
const entry = this.resBuffer[i];
|
|
435
|
+
currentGenLine += (entry.match(/\n/g) || []).length + 1;
|
|
436
|
+
}
|
|
437
|
+
for (const mapping of this.resMappings) {
|
|
438
|
+
const genLine = bufferLineOffsets.get(mapping.index);
|
|
439
|
+
if (genLine !== undefined) {
|
|
440
|
+
this.generator.addMapping({
|
|
441
|
+
genLine,
|
|
442
|
+
genCol: mapping.col,
|
|
443
|
+
sourceLine: mapping.node.loc.start.line,
|
|
444
|
+
sourceCol: mapping.node.loc.start.column
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
let code = `
|
|
450
|
+
${destructuring}
|
|
451
|
+
let ${varLocals} = $ctx.$props;
|
|
259
452
|
${pre}
|
|
260
453
|
${res}
|
|
261
454
|
${pos}
|
|
262
|
-
}
|
|
263
455
|
return $ctx;
|
|
264
|
-
//# sourceURL
|
|
456
|
+
//# sourceURL=${this.filename}`;
|
|
457
|
+
if (!this.kire.production) {
|
|
458
|
+
code += `
|
|
459
|
+
//# sourceMappingURL=${this.generator.toDataUri()}`;
|
|
460
|
+
}
|
|
265
461
|
return code;
|
|
266
462
|
}
|
|
267
463
|
async compileNodes(nodes) {
|
|
268
464
|
let i = 0;
|
|
269
465
|
while (i < nodes.length) {
|
|
270
466
|
const node = nodes[i];
|
|
271
|
-
this.addSourceLine(node);
|
|
272
467
|
switch (node.type) {
|
|
273
468
|
case "text":
|
|
274
469
|
this.compileText(node);
|
|
@@ -286,18 +481,31 @@ return $ctx;
|
|
|
286
481
|
i++;
|
|
287
482
|
}
|
|
288
483
|
}
|
|
289
|
-
addSourceLine(node) {
|
|
290
|
-
if (node.loc) {
|
|
291
|
-
this.resBuffer.push(`// kire-line: ${node.loc.start.line}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
484
|
compileText(node) {
|
|
295
485
|
if (node.content) {
|
|
486
|
+
if (node.loc) {
|
|
487
|
+
const currentLine = node.loc.start.line;
|
|
488
|
+
this.resMappings.push({
|
|
489
|
+
index: this.resBuffer.length + 1,
|
|
490
|
+
node,
|
|
491
|
+
col: this.resBuffer[this.resBuffer.length - 1]?.length || 0
|
|
492
|
+
});
|
|
493
|
+
this.resBuffer.push(`// kire-line: ${currentLine}`);
|
|
494
|
+
}
|
|
296
495
|
this.resBuffer.push(`$ctx.$add(${JSON.stringify(node.content)});`);
|
|
297
496
|
}
|
|
298
497
|
}
|
|
299
498
|
compileVariable(node) {
|
|
300
499
|
if (node.content) {
|
|
500
|
+
if (node.loc) {
|
|
501
|
+
const currentLine = node.loc.start.line;
|
|
502
|
+
this.resMappings.push({
|
|
503
|
+
index: this.resBuffer.length + 1,
|
|
504
|
+
node,
|
|
505
|
+
col: this.resBuffer[this.resBuffer.length - 1]?.length || 0
|
|
506
|
+
});
|
|
507
|
+
this.resBuffer.push(`// kire-line: ${currentLine}`);
|
|
508
|
+
}
|
|
301
509
|
if (node.raw) {
|
|
302
510
|
this.resBuffer.push(`$ctx.$add(${node.content});`);
|
|
303
511
|
} else {
|
|
@@ -307,7 +515,30 @@ return $ctx;
|
|
|
307
515
|
}
|
|
308
516
|
compileJavascript(node) {
|
|
309
517
|
if (node.content) {
|
|
310
|
-
|
|
518
|
+
const lines = node.content.split(`
|
|
519
|
+
`);
|
|
520
|
+
for (let i = 0;i < lines.length; i++) {
|
|
521
|
+
const lineContent = lines[i];
|
|
522
|
+
if (node.loc) {
|
|
523
|
+
const currentLine = node.loc.start.line + i;
|
|
524
|
+
this.resMappings.push({
|
|
525
|
+
index: this.resBuffer.length + 1,
|
|
526
|
+
node: {
|
|
527
|
+
...node,
|
|
528
|
+
loc: {
|
|
529
|
+
start: {
|
|
530
|
+
line: currentLine,
|
|
531
|
+
column: i === 0 ? node.loc.start.column + 4 : 1
|
|
532
|
+
},
|
|
533
|
+
end: node.loc.end
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
col: 0
|
|
537
|
+
});
|
|
538
|
+
this.resBuffer.push(`// kire-line: ${currentLine}`);
|
|
539
|
+
}
|
|
540
|
+
this.resBuffer.push(lineContent);
|
|
541
|
+
}
|
|
311
542
|
}
|
|
312
543
|
}
|
|
313
544
|
async processDirective(node) {
|
|
@@ -319,6 +550,8 @@ return $ctx;
|
|
|
319
550
|
console.warn(`Directive @${name} not found.`);
|
|
320
551
|
return;
|
|
321
552
|
}
|
|
553
|
+
if (node.loc)
|
|
554
|
+
this.resMappings.push({ index: this.resBuffer.length, node, col: this.resBuffer[this.resBuffer.length - 1]?.length || 0 });
|
|
322
555
|
const compiler = this.createCompilerContext(node, directive);
|
|
323
556
|
await directive.onCall(compiler);
|
|
324
557
|
}
|
|
@@ -1008,6 +1241,75 @@ var natives_default = (kire) => {
|
|
|
1008
1241
|
compiler.raw(`}`);
|
|
1009
1242
|
}
|
|
1010
1243
|
});
|
|
1244
|
+
kire.directive({
|
|
1245
|
+
name: "csrf",
|
|
1246
|
+
type: "html",
|
|
1247
|
+
description: "Renders a CSRF token input field.",
|
|
1248
|
+
onCall(compiler) {
|
|
1249
|
+
compiler.raw(`
|
|
1250
|
+
if (typeof $ctx.$globals.csrf === 'undefined') {
|
|
1251
|
+
throw new Error("CSRF token not defined. Please define it using kire.$global('csrf', 'token')");
|
|
1252
|
+
}
|
|
1253
|
+
$ctx.$add(\`<input type="hidden" name="_token" value="\${$ctx.$globals.csrf}">\`);
|
|
1254
|
+
`);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
kire.directive({
|
|
1258
|
+
name: "method",
|
|
1259
|
+
params: ["method:string"],
|
|
1260
|
+
type: "html",
|
|
1261
|
+
description: "Spoofs an HTTP method using a hidden input.",
|
|
1262
|
+
onCall(compiler) {
|
|
1263
|
+
const method = compiler.param("method");
|
|
1264
|
+
compiler.res(`<input type="hidden" name="_method" value="${method}">`);
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
kire.directive({
|
|
1268
|
+
name: "defer",
|
|
1269
|
+
children: true,
|
|
1270
|
+
type: "html",
|
|
1271
|
+
description: "Defers rendering of a block until the main content is loaded (Out-of-Order Streaming). Falls back to immediate rendering if streaming is disabled.",
|
|
1272
|
+
async onCall(compiler) {
|
|
1273
|
+
compiler.raw(`{`);
|
|
1274
|
+
compiler.raw(`const deferredRender = async ($ctx) => {`);
|
|
1275
|
+
if (compiler.children)
|
|
1276
|
+
await compiler.set(compiler.children);
|
|
1277
|
+
compiler.raw(`};`);
|
|
1278
|
+
compiler.raw(`if ($ctx.$kire.stream) {`);
|
|
1279
|
+
compiler.raw(` const deferId = 'defer-' + Math.random().toString(36).substr(2, 9);`);
|
|
1280
|
+
compiler.raw(` $ctx.$add(\`<div id="\${deferId}"></div>\`);`);
|
|
1281
|
+
compiler.raw(` if (!$ctx.$deferred) $ctx.$deferred = [];`);
|
|
1282
|
+
compiler.raw(` $ctx.$deferred.push(async () => {`);
|
|
1283
|
+
compiler.raw(` const $parentCtx = $ctx;`);
|
|
1284
|
+
compiler.raw(` {`);
|
|
1285
|
+
compiler.raw(` const $ctx = $parentCtx.$fork();`);
|
|
1286
|
+
compiler.raw(` let swapScript = '';`);
|
|
1287
|
+
compiler.raw(` await $ctx.$merge(deferredRender);`);
|
|
1288
|
+
compiler.raw(` const content = $ctx.$response;`);
|
|
1289
|
+
compiler.raw(` $ctx.$response = '';`);
|
|
1290
|
+
compiler.raw(` const templateId = 'tpl-' + deferId;`);
|
|
1291
|
+
compiler.raw(` swapScript = \`
|
|
1292
|
+
<template id="\${templateId}">\${content}</template>
|
|
1293
|
+
<script>
|
|
1294
|
+
(function() {
|
|
1295
|
+
var src = document.getElementById('\${templateId}');
|
|
1296
|
+
var dest = document.getElementById('\${deferId}');
|
|
1297
|
+
if (src && dest) {
|
|
1298
|
+
dest.replaceWith(src.content);
|
|
1299
|
+
src.remove();
|
|
1300
|
+
}
|
|
1301
|
+
})();
|
|
1302
|
+
</script>
|
|
1303
|
+
\`;`);
|
|
1304
|
+
compiler.raw(` $ctx.$add(swapScript);`);
|
|
1305
|
+
compiler.raw(` }`);
|
|
1306
|
+
compiler.raw(` });`);
|
|
1307
|
+
compiler.raw(`} else {`);
|
|
1308
|
+
compiler.raw(` await deferredRender($ctx);`);
|
|
1309
|
+
compiler.raw(`}`);
|
|
1310
|
+
compiler.raw(`}`);
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1011
1313
|
};
|
|
1012
1314
|
|
|
1013
1315
|
// core/src/directives/index.ts
|
|
@@ -1022,6 +1324,7 @@ var KireDirectives = {
|
|
|
1022
1324
|
const isProd = kire.production;
|
|
1023
1325
|
const cachedHash = cached.get(`md5:${resolvedPath}`);
|
|
1024
1326
|
let compiledFn = cached.get(`js:${resolvedPath}`);
|
|
1327
|
+
let sourceMap = cached.get(`map:${resolvedPath}`);
|
|
1025
1328
|
let content = "";
|
|
1026
1329
|
if (!cachedHash || !compiledFn || !isProd) {
|
|
1027
1330
|
try {
|
|
@@ -1037,13 +1340,20 @@ var KireDirectives = {
|
|
|
1037
1340
|
}
|
|
1038
1341
|
const newHash = md5(content);
|
|
1039
1342
|
if (cachedHash === newHash && compiledFn) {} else {
|
|
1040
|
-
compiledFn = await kire.compileFn(content);
|
|
1343
|
+
compiledFn = await kire.compileFn(content, resolvedPath);
|
|
1344
|
+
sourceMap = compiledFn._map;
|
|
1041
1345
|
cached.set(`md5:${resolvedPath}`, newHash);
|
|
1042
1346
|
cached.set(`js:${resolvedPath}`, compiledFn);
|
|
1347
|
+
if (sourceMap) {
|
|
1348
|
+
cached.set(`map:${resolvedPath}`, sourceMap);
|
|
1349
|
+
}
|
|
1043
1350
|
}
|
|
1044
1351
|
}
|
|
1045
1352
|
if (!compiledFn)
|
|
1046
1353
|
return null;
|
|
1354
|
+
if (sourceMap && !compiledFn._map) {
|
|
1355
|
+
compiledFn._map = sourceMap;
|
|
1356
|
+
}
|
|
1047
1357
|
return kire.run(compiledFn, locals, true, controller);
|
|
1048
1358
|
});
|
|
1049
1359
|
layout_default(kire);
|
|
@@ -1114,7 +1424,7 @@ class Parser {
|
|
|
1114
1424
|
const inner = this.template.slice(this.cursor + 4, end);
|
|
1115
1425
|
this.addNode({
|
|
1116
1426
|
type: "javascript",
|
|
1117
|
-
content: inner
|
|
1427
|
+
content: inner,
|
|
1118
1428
|
start: this.cursor,
|
|
1119
1429
|
end: end + 2,
|
|
1120
1430
|
loc: this.getLoc(content)
|
|
@@ -1561,6 +1871,78 @@ class Parser {
|
|
|
1561
1871
|
}
|
|
1562
1872
|
}
|
|
1563
1873
|
|
|
1874
|
+
// core/src/utils/error.ts
|
|
1875
|
+
class KireError extends Error {
|
|
1876
|
+
originalError;
|
|
1877
|
+
fileMeta;
|
|
1878
|
+
constructor(message, fileMeta) {
|
|
1879
|
+
const originalError = message instanceof Error ? message : new Error(message);
|
|
1880
|
+
super(originalError.message);
|
|
1881
|
+
this.name = "KireError";
|
|
1882
|
+
this.originalError = originalError;
|
|
1883
|
+
this.fileMeta = fileMeta;
|
|
1884
|
+
const originalStack = originalError.stack || "";
|
|
1885
|
+
this.stack = this.formatStack(originalStack);
|
|
1886
|
+
}
|
|
1887
|
+
formatStack(stack) {
|
|
1888
|
+
const lines = stack.split(`
|
|
1889
|
+
`);
|
|
1890
|
+
const messageLine = lines[0] || `${this.name}: ${this.message}`;
|
|
1891
|
+
const mappedLines = [];
|
|
1892
|
+
for (let i = 1;i < lines.length; i++) {
|
|
1893
|
+
const line = lines[i];
|
|
1894
|
+
const mappedLine = this.mapStackLine(line);
|
|
1895
|
+
mappedLines.push(mappedLine);
|
|
1896
|
+
}
|
|
1897
|
+
let finalMessage = messageLine;
|
|
1898
|
+
if (finalMessage.startsWith("Error:")) {
|
|
1899
|
+
finalMessage = `KireError:${finalMessage.slice(6)}`;
|
|
1900
|
+
} else if (!finalMessage.includes("KireError")) {
|
|
1901
|
+
finalMessage = `KireError: ${finalMessage}`;
|
|
1902
|
+
}
|
|
1903
|
+
return `${finalMessage}
|
|
1904
|
+
${mappedLines.join(`
|
|
1905
|
+
`)}`;
|
|
1906
|
+
}
|
|
1907
|
+
mapStackLine(line) {
|
|
1908
|
+
const match = line.match(/^\s*at\s+(?:(.*?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
|
|
1909
|
+
if (match) {
|
|
1910
|
+
const functionName = match[1];
|
|
1911
|
+
const filename = match[2];
|
|
1912
|
+
const genLine = parseInt(match[3]);
|
|
1913
|
+
const genCol = parseInt(match[4]);
|
|
1914
|
+
if (this.fileMeta) {
|
|
1915
|
+
const isTemplateFile = filename.includes(this.fileMeta.path) || filename.includes("template.kire") || filename.includes("<anonymous>") || filename.includes("eval") || filename.includes("AsyncFunction");
|
|
1916
|
+
if (isTemplateFile) {
|
|
1917
|
+
if (this.fileMeta.code) {
|
|
1918
|
+
const generatedLines = this.fileMeta.code.split(`
|
|
1919
|
+
`);
|
|
1920
|
+
const codeGenLine = genLine - 2;
|
|
1921
|
+
for (let offset = 0;offset >= -10; offset--) {
|
|
1922
|
+
const idx = codeGenLine + offset - 1;
|
|
1923
|
+
if (idx >= 0 && idx < generatedLines.length) {
|
|
1924
|
+
const gl = generatedLines[idx];
|
|
1925
|
+
if (gl && gl.trim().startsWith("// kire-line:")) {
|
|
1926
|
+
const sourceLine = parseInt(gl.split(":")[1].trim());
|
|
1927
|
+
const finalLine = sourceLine;
|
|
1928
|
+
return ` at ${functionName ? functionName + " " : ""}(${this.fileMeta.path}:${finalLine}:${genCol})`;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
if (this.fileMeta.map) {
|
|
1934
|
+
const resolved = resolveSourceLocation(this.fileMeta.map, genLine, genCol);
|
|
1935
|
+
if (resolved) {
|
|
1936
|
+
return ` at ${functionName ? functionName + " " : ""}(${resolved.source}:${resolved.line}:${resolved.column})`;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
return line;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1564
1946
|
// core/src/utils/elements-runner.ts
|
|
1565
1947
|
var processElements = async ($ctx) => {
|
|
1566
1948
|
let resultHtml = $ctx.$response;
|
|
@@ -1635,7 +2017,7 @@ function escapeHtml(unsafe) {
|
|
|
1635
2017
|
if (unsafe === null || unsafe === undefined)
|
|
1636
2018
|
return "";
|
|
1637
2019
|
const str = String(unsafe);
|
|
1638
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2020
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/`/g, "`");
|
|
1639
2021
|
}
|
|
1640
2022
|
|
|
1641
2023
|
// core/src/runtime.ts
|
|
@@ -1656,6 +2038,7 @@ function runtime_default($kire, locals, meta) {
|
|
|
1656
2038
|
$ctx.$hooks.set("before", []);
|
|
1657
2039
|
$ctx.$hooks.set("after", []);
|
|
1658
2040
|
$ctx.$hooks.set("end", []);
|
|
2041
|
+
$ctx.$deferred = [];
|
|
1659
2042
|
$ctx.$on = (ev, cb) => {
|
|
1660
2043
|
const hooks = $ctx.$hooks.get(ev);
|
|
1661
2044
|
if (hooks) {
|
|
@@ -1692,6 +2075,24 @@ function runtime_default($kire, locals, meta) {
|
|
|
1692
2075
|
await fn($ctx);
|
|
1693
2076
|
$ctx.$response = $pres + $ctx.$response;
|
|
1694
2077
|
};
|
|
2078
|
+
$ctx.$fork = () => {
|
|
2079
|
+
const newCtx = { ...$ctx };
|
|
2080
|
+
newCtx.$merge = async (fn) => {
|
|
2081
|
+
const originalAdd = newCtx.$add;
|
|
2082
|
+
let buffer = "";
|
|
2083
|
+
newCtx.$add = (str) => {
|
|
2084
|
+
buffer += str;
|
|
2085
|
+
newCtx.$response = (newCtx.$response || "") + str;
|
|
2086
|
+
};
|
|
2087
|
+
const prevRes = newCtx.$response;
|
|
2088
|
+
newCtx.$response = "";
|
|
2089
|
+
await fn(newCtx);
|
|
2090
|
+
newCtx.$add = originalAdd;
|
|
2091
|
+
newCtx.$response = prevRes + buffer;
|
|
2092
|
+
};
|
|
2093
|
+
newCtx.$fork = $ctx.$fork;
|
|
2094
|
+
return newCtx;
|
|
2095
|
+
};
|
|
1695
2096
|
const execute = async () => {
|
|
1696
2097
|
if ($ctx.$kire.$directives.size > 0) {
|
|
1697
2098
|
for (const directive of $ctx.$kire.$directives.values()) {
|
|
@@ -1700,13 +2101,14 @@ function runtime_default($kire, locals, meta) {
|
|
|
1700
2101
|
}
|
|
1701
2102
|
}
|
|
1702
2103
|
}
|
|
2104
|
+
await $ctx.$emit("before");
|
|
1703
2105
|
try {
|
|
1704
2106
|
await $ctx.$file.execute.call($ctx.$props, $ctx);
|
|
1705
2107
|
} catch (e) {
|
|
1706
|
-
|
|
1707
|
-
|
|
2108
|
+
const kireError = new KireError(e, $ctx.$file);
|
|
2109
|
+
console.error(kireError.stack);
|
|
2110
|
+
return $kire.renderError(kireError, $ctx);
|
|
1708
2111
|
}
|
|
1709
|
-
await $ctx.$emit("before");
|
|
1710
2112
|
await $ctx.$emit("after");
|
|
1711
2113
|
await $ctx.$emit("end");
|
|
1712
2114
|
let resultHtml = $ctx.$response;
|
|
@@ -1740,6 +2142,9 @@ function runtime_default($kire, locals, meta) {
|
|
|
1740
2142
|
if (typeof result === "string" && result.includes("Kire Runtime Error")) {
|
|
1741
2143
|
controller.enqueue(encoder.encode(result));
|
|
1742
2144
|
}
|
|
2145
|
+
if ($ctx.$deferred && $ctx.$deferred.length > 0) {
|
|
2146
|
+
await Promise.all($ctx.$deferred.map((t) => t()));
|
|
2147
|
+
}
|
|
1743
2148
|
controller.close();
|
|
1744
2149
|
} catch (e) {
|
|
1745
2150
|
controller.error(e);
|
|
@@ -1854,14 +2259,19 @@ function resolvePath(filepath, namespaces, locals = {}, extension = "kire") {
|
|
|
1854
2259
|
let normalized = filepath.replace(/\\/g, "/");
|
|
1855
2260
|
const sortedNamespaces = Array.from(namespaces.keys()).sort((a, b) => b.length - a.length);
|
|
1856
2261
|
for (const ns of sortedNamespaces) {
|
|
1857
|
-
if (normalized.startsWith(ns)) {
|
|
2262
|
+
if (normalized.startsWith(ns) || ns === "@") {
|
|
1858
2263
|
let template = namespaces.get(ns);
|
|
1859
2264
|
const data = { ...locals };
|
|
1860
2265
|
template = template.replace(/\{(\w+)\}/g, (_, key) => {
|
|
1861
2266
|
return data[key] !== undefined ? String(data[key]) : `{${key}}`;
|
|
1862
2267
|
});
|
|
1863
2268
|
template = template.replace(/\\/g, "/");
|
|
1864
|
-
let suffix =
|
|
2269
|
+
let suffix = "";
|
|
2270
|
+
if (ns === "@") {
|
|
2271
|
+
suffix = normalized;
|
|
2272
|
+
} else {
|
|
2273
|
+
suffix = normalized.slice(ns.length);
|
|
2274
|
+
}
|
|
1865
2275
|
if (suffix.startsWith(".") || suffix.startsWith("/")) {
|
|
1866
2276
|
suffix = suffix.slice(1);
|
|
1867
2277
|
}
|
|
@@ -1905,6 +2315,7 @@ return (${callbackSource}).apply(this, arguments);`);
|
|
|
1905
2315
|
// core/src/kire.ts
|
|
1906
2316
|
class Kire {
|
|
1907
2317
|
$scoped = scoped;
|
|
2318
|
+
$error = KireError;
|
|
1908
2319
|
$directives = new Map;
|
|
1909
2320
|
$elements = new Set;
|
|
1910
2321
|
$globals = new LayeredMap;
|
|
@@ -1989,6 +2400,7 @@ class Kire {
|
|
|
1989
2400
|
for (const item of pluginsToLoad) {
|
|
1990
2401
|
this.plugin(item.p, item.o);
|
|
1991
2402
|
}
|
|
2403
|
+
this.$error.html = (e, ctx) => this.renderError(e, ctx);
|
|
1992
2404
|
}
|
|
1993
2405
|
namespace(name, path) {
|
|
1994
2406
|
const unixPath = path.replace(/\\/g, "/");
|
|
@@ -1996,6 +2408,9 @@ class Kire {
|
|
|
1996
2408
|
return this;
|
|
1997
2409
|
}
|
|
1998
2410
|
$prop(keyOrObj, value) {
|
|
2411
|
+
if (!this.$parent) {
|
|
2412
|
+
console.warn("Kire Warning: You are setting props on the global instance. This data will be shared across all requests. Use kire.fork() for per-request isolation.");
|
|
2413
|
+
}
|
|
1999
2414
|
if (typeof keyOrObj === "string") {
|
|
2000
2415
|
this.$props.set(keyOrObj, value);
|
|
2001
2416
|
} else if (typeof keyOrObj === "object") {
|
|
@@ -2115,45 +2530,71 @@ class Kire {
|
|
|
2115
2530
|
const parser = new this.$parser(template, this);
|
|
2116
2531
|
return parser.parse();
|
|
2117
2532
|
}
|
|
2118
|
-
async compile(template) {
|
|
2533
|
+
async compile(template, filename) {
|
|
2119
2534
|
const parser = new this.$parser(template, this);
|
|
2120
2535
|
const nodes = parser.parse();
|
|
2121
|
-
const compiler = new this.$compiler(this);
|
|
2536
|
+
const compiler = new this.$compiler(this, filename);
|
|
2122
2537
|
return compiler.compile(nodes);
|
|
2123
2538
|
}
|
|
2124
|
-
async compileFn(content) {
|
|
2125
|
-
const code = `${await this.compile(content)}`;
|
|
2539
|
+
async compileFn(content, filename = "template.kire") {
|
|
2540
|
+
const code = `${await this.compile(content, filename)}`;
|
|
2126
2541
|
try {
|
|
2127
2542
|
const mainFn = this.$executor(code, ["$ctx"]);
|
|
2128
2543
|
mainFn._code = code;
|
|
2129
2544
|
mainFn._source = content;
|
|
2545
|
+
mainFn._path = filename;
|
|
2546
|
+
const mapMatch = code.match(/\/\/# sourceMappingURL=data:application\/json;charset=utf-8;base64,(.*)/);
|
|
2547
|
+
if (mapMatch) {
|
|
2548
|
+
try {
|
|
2549
|
+
const json = Buffer.from(mapMatch[1], "base64").toString("utf8");
|
|
2550
|
+
mainFn._map = JSON.parse(json);
|
|
2551
|
+
} catch (e) {}
|
|
2552
|
+
}
|
|
2130
2553
|
return mainFn;
|
|
2131
2554
|
} catch (e) {
|
|
2132
2555
|
console.error("Error creating function from code:", code);
|
|
2133
2556
|
throw e;
|
|
2134
2557
|
}
|
|
2135
2558
|
}
|
|
2136
|
-
async render(template, locals = {}, controller) {
|
|
2137
|
-
|
|
2559
|
+
async render(template, locals = {}, controller, filename = "template.kire") {
|
|
2560
|
+
if (!this.$parent) {
|
|
2561
|
+
console.warn("Kire Warning: You are rendering on the global instance. Use kire.fork() for per-request isolation.");
|
|
2562
|
+
}
|
|
2563
|
+
const fn = await this.compileFn(template, filename);
|
|
2138
2564
|
return this.run(fn, locals, false, controller);
|
|
2139
2565
|
}
|
|
2140
2566
|
renderError(e, ctx) {
|
|
2141
2567
|
let snippet = "";
|
|
2142
2568
|
let location = "";
|
|
2143
2569
|
if (ctx && ctx.$file && ctx.$file.code && e.stack) {
|
|
2144
|
-
const
|
|
2570
|
+
const safePath = ctx.$file.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2571
|
+
const match = e.stack.match(new RegExp(`${safePath}:(\\d+):(\\d+)`)) || e.stack.match(/kire-generated\.js:(\d+):(\d+)/) || e.stack.match(/eval:(\d+):(\d+)/) || e.stack.match(/<anonymous>:(\d+):(\d+)/);
|
|
2145
2572
|
if (match) {
|
|
2146
2573
|
const genLine = parseInt(match[1]) - 1;
|
|
2574
|
+
const genCol = parseInt(match[2]);
|
|
2147
2575
|
const lines = ctx.$file.code.split(`
|
|
2148
2576
|
`);
|
|
2149
2577
|
let sourceLine = -1;
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2578
|
+
if (ctx.$file.map) {
|
|
2579
|
+
console.log("Resolving map for", genLine + 1, genCol);
|
|
2580
|
+
const resolved = resolveSourceLocation(ctx.$file.map, genLine + 1, genCol);
|
|
2581
|
+
console.log("Resolved:", resolved);
|
|
2582
|
+
if (resolved) {
|
|
2583
|
+
sourceLine = resolved.line - 1;
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
if (sourceLine === -1) {
|
|
2587
|
+
for (let i = genLine;i >= 0; i--) {
|
|
2588
|
+
const line = lines[i];
|
|
2589
|
+
if (line && line.trim().startsWith("// kire-line:")) {
|
|
2590
|
+
sourceLine = parseInt(line.split(":")[1].trim()) - 1;
|
|
2591
|
+
break;
|
|
2592
|
+
}
|
|
2155
2593
|
}
|
|
2156
2594
|
}
|
|
2595
|
+
if (sourceLine === -1 && match[0].includes(ctx.$file.path)) {
|
|
2596
|
+
sourceLine = genLine;
|
|
2597
|
+
}
|
|
2157
2598
|
if (sourceLine !== -1 && ctx.$file.source) {
|
|
2158
2599
|
location = ` at ${ctx.$file.path}:${sourceLine + 1}`;
|
|
2159
2600
|
const sourceLines = ctx.$file.source.split(`
|
|
@@ -2202,6 +2643,9 @@ class Kire {
|
|
|
2202
2643
|
</html>`;
|
|
2203
2644
|
}
|
|
2204
2645
|
async view(path, locals = {}, controller) {
|
|
2646
|
+
if (!this.$parent) {
|
|
2647
|
+
console.warn("Kire Warning: You are rendering a view on the global instance. Use kire.fork() for per-request isolation.");
|
|
2648
|
+
}
|
|
2205
2649
|
let ext = this.extension;
|
|
2206
2650
|
if (path.endsWith(".md") || path.endsWith(".markdown")) {
|
|
2207
2651
|
ext = null;
|
|
@@ -2215,7 +2659,7 @@ class Kire {
|
|
|
2215
2659
|
compiled = this.$files.get(resolvedPath);
|
|
2216
2660
|
} else {
|
|
2217
2661
|
const content = await this.$resolver(resolvedPath);
|
|
2218
|
-
compiled = await this.compileFn(content);
|
|
2662
|
+
compiled = await this.compileFn(content, resolvedPath);
|
|
2219
2663
|
if (this.production) {
|
|
2220
2664
|
this.$files.set(resolvedPath, compiled);
|
|
2221
2665
|
}
|
|
@@ -2225,7 +2669,7 @@ class Kire {
|
|
|
2225
2669
|
return this.run(compiled, locals, false, controller);
|
|
2226
2670
|
}
|
|
2227
2671
|
resolvePath(filepath, locals = {}, extension = this.extension) {
|
|
2228
|
-
return resolvePath(filepath, this.$namespaces, { ...this.$props.toObject(), ...locals }, extension === null ? "" : extension);
|
|
2672
|
+
return resolvePath(filepath, this.$namespaces, { ...this.$globals.toObject(), ...this.$props.toObject(), ...locals }, extension === null ? "" : extension);
|
|
2229
2673
|
}
|
|
2230
2674
|
async run(mainFn, locals, children = false, controller) {
|
|
2231
2675
|
return runtime_default(this, locals, {
|
|
@@ -2235,6 +2679,7 @@ class Kire {
|
|
|
2235
2679
|
name: mainFn.name,
|
|
2236
2680
|
source: mainFn._source,
|
|
2237
2681
|
path: mainFn._path,
|
|
2682
|
+
map: mainFn._map,
|
|
2238
2683
|
controller
|
|
2239
2684
|
});
|
|
2240
2685
|
}
|