tw-framework 1.0.6 → 1.0.7
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/hydration-runtime.js +67 -5
- package/dist/testing.cjs +6878 -431
- package/dist/tw.mjs +811 -487
- package/package.json +1 -1
package/dist/tw.mjs
CHANGED
|
@@ -3,7 +3,9 @@ globalThis.__TW_BUNDLE_DIR = new URL(".", import.meta.url).pathname;
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
7
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
9
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
10
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
11
|
}) : x)(function(x) {
|
|
@@ -17,6 +19,15 @@ var __export = (target, all) => {
|
|
|
17
19
|
for (var name in all)
|
|
18
20
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
21
|
};
|
|
22
|
+
var __copyProps = (to, from, except, desc) => {
|
|
23
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
+
for (let key of __getOwnPropNames(from))
|
|
25
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
26
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
27
|
+
}
|
|
28
|
+
return to;
|
|
29
|
+
};
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
31
|
|
|
21
32
|
// ../../packages/shared/tw/hashing/md5.ts
|
|
22
33
|
function bytesToWords(bytes) {
|
|
@@ -2555,6 +2566,97 @@ var init_config = __esm({
|
|
|
2555
2566
|
}
|
|
2556
2567
|
});
|
|
2557
2568
|
|
|
2569
|
+
// ../../packages/shared/tw/source-mask.ts
|
|
2570
|
+
function blank(s) {
|
|
2571
|
+
return s.replace(/[^\n]/g, " ");
|
|
2572
|
+
}
|
|
2573
|
+
function maskSourceStringsAndComments(src) {
|
|
2574
|
+
let out = "";
|
|
2575
|
+
let i = 0;
|
|
2576
|
+
const n = src.length;
|
|
2577
|
+
while (i < n) {
|
|
2578
|
+
const ch = src[i];
|
|
2579
|
+
const next = src[i + 1];
|
|
2580
|
+
if (ch === "/" && next === "/") {
|
|
2581
|
+
let j = i;
|
|
2582
|
+
while (j < n && src[j] !== "\n") j++;
|
|
2583
|
+
out += blank(src.slice(i, j));
|
|
2584
|
+
i = j;
|
|
2585
|
+
continue;
|
|
2586
|
+
}
|
|
2587
|
+
if (ch === "/" && next === "*") {
|
|
2588
|
+
let j = i + 2;
|
|
2589
|
+
while (j < n && !(src[j] === "*" && src[j + 1] === "/")) j++;
|
|
2590
|
+
j = Math.min(j + 2, n);
|
|
2591
|
+
out += blank(src.slice(i, j));
|
|
2592
|
+
i = j;
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
2596
|
+
let j = i + 1;
|
|
2597
|
+
while (j < n) {
|
|
2598
|
+
const c = src[j];
|
|
2599
|
+
if (c === "\\") {
|
|
2600
|
+
j += 2;
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
if (c === ch) {
|
|
2604
|
+
j++;
|
|
2605
|
+
break;
|
|
2606
|
+
}
|
|
2607
|
+
j++;
|
|
2608
|
+
}
|
|
2609
|
+
out += blank(src.slice(i, j));
|
|
2610
|
+
i = j;
|
|
2611
|
+
continue;
|
|
2612
|
+
}
|
|
2613
|
+
out += ch;
|
|
2614
|
+
i++;
|
|
2615
|
+
}
|
|
2616
|
+
return out;
|
|
2617
|
+
}
|
|
2618
|
+
function stripCommentsStringAware(src) {
|
|
2619
|
+
let out = "";
|
|
2620
|
+
let i = 0;
|
|
2621
|
+
const n = src.length;
|
|
2622
|
+
while (i < n) {
|
|
2623
|
+
const c = src[i];
|
|
2624
|
+
if (c === '"' || c === "'") {
|
|
2625
|
+
const q = c;
|
|
2626
|
+
out += c;
|
|
2627
|
+
i++;
|
|
2628
|
+
while (i < n && src[i] !== q) {
|
|
2629
|
+
if (src[i] === "\\") {
|
|
2630
|
+
out += src[i++];
|
|
2631
|
+
if (i < n) out += src[i++];
|
|
2632
|
+
continue;
|
|
2633
|
+
}
|
|
2634
|
+
out += src[i++];
|
|
2635
|
+
}
|
|
2636
|
+
out += src[i] ?? "";
|
|
2637
|
+
i++;
|
|
2638
|
+
continue;
|
|
2639
|
+
}
|
|
2640
|
+
if (c === "/" && src[i + 1] === "/") {
|
|
2641
|
+
while (i < n && src[i] !== "\n") i++;
|
|
2642
|
+
continue;
|
|
2643
|
+
}
|
|
2644
|
+
if (c === "/" && src[i + 1] === "*") {
|
|
2645
|
+
const end = src.indexOf("*/", i + 2);
|
|
2646
|
+
i = end === -1 ? n : end + 2;
|
|
2647
|
+
out += " ";
|
|
2648
|
+
continue;
|
|
2649
|
+
}
|
|
2650
|
+
out += c;
|
|
2651
|
+
i++;
|
|
2652
|
+
}
|
|
2653
|
+
return out;
|
|
2654
|
+
}
|
|
2655
|
+
var init_source_mask = __esm({
|
|
2656
|
+
"../../packages/shared/tw/source-mask.ts"() {
|
|
2657
|
+
}
|
|
2658
|
+
});
|
|
2659
|
+
|
|
2558
2660
|
// ../../packages/shared/tw/cache.ts
|
|
2559
2661
|
function parseCacheBody(body) {
|
|
2560
2662
|
const meta = {};
|
|
@@ -2577,9 +2679,12 @@ function parseCacheBody(body) {
|
|
|
2577
2679
|
return meta;
|
|
2578
2680
|
}
|
|
2579
2681
|
function extractCacheDirective(source) {
|
|
2580
|
-
const
|
|
2682
|
+
const masked = maskSourceStringsAndComments(source);
|
|
2683
|
+
const m = /page\s*\{[^}]*?\bcache\s*\{([^}]*)\}/.exec(masked);
|
|
2581
2684
|
if (!m) return null;
|
|
2582
|
-
|
|
2685
|
+
const bodyStart = m.index + m[0].length - m[1].length - 1;
|
|
2686
|
+
const bodyEnd = bodyStart + m[1].length;
|
|
2687
|
+
return parseCacheBody(source.slice(bodyStart, bodyEnd));
|
|
2583
2688
|
}
|
|
2584
2689
|
function cacheMetaIsUsable(meta) {
|
|
2585
2690
|
if (!meta) return false;
|
|
@@ -2711,6 +2816,7 @@ function parseCacheProfilesSource(src) {
|
|
|
2711
2816
|
var BUILTIN_CACHE_PROFILES;
|
|
2712
2817
|
var init_cache = __esm({
|
|
2713
2818
|
"../../packages/shared/tw/cache.ts"() {
|
|
2819
|
+
init_source_mask();
|
|
2714
2820
|
BUILTIN_CACHE_PROFILES = {
|
|
2715
2821
|
seconds: { stale: 1, revalidate: 1, expire: 2 },
|
|
2716
2822
|
minutes: { stale: 60, revalidate: 300, expire: 3600 },
|
|
@@ -6653,6 +6759,7 @@ __export(tw_exports, {
|
|
|
6653
6759
|
maskPhone: () => maskPhone,
|
|
6654
6760
|
maskSSN: () => maskSSN,
|
|
6655
6761
|
maskSensitiveData: () => maskSensitiveData,
|
|
6762
|
+
maskSourceStringsAndComments: () => maskSourceStringsAndComments,
|
|
6656
6763
|
md5: () => md5,
|
|
6657
6764
|
md5Async: () => md52,
|
|
6658
6765
|
mergeConfig: () => mergeConfig,
|
|
@@ -6992,6 +7099,7 @@ __export(tw_exports, {
|
|
|
6992
7099
|
shortHash: () => shortHash,
|
|
6993
7100
|
signData: () => signData,
|
|
6994
7101
|
statSync: () => statSync,
|
|
7102
|
+
stripCommentsStringAware: () => stripCommentsStringAware,
|
|
6995
7103
|
supportsColor: () => supportsColor,
|
|
6996
7104
|
supportsFileSystem: () => supportsFileSystem,
|
|
6997
7105
|
supportsWebSocket: () => supportsWebSocket,
|
|
@@ -7072,6 +7180,7 @@ var init_tw = __esm({
|
|
|
7072
7180
|
init_utils();
|
|
7073
7181
|
init_config();
|
|
7074
7182
|
init_cache();
|
|
7183
|
+
init_source_mask();
|
|
7075
7184
|
init_constants();
|
|
7076
7185
|
init_crypto();
|
|
7077
7186
|
init_env();
|
|
@@ -9518,6 +9627,9 @@ function readEscape(source, start) {
|
|
|
9518
9627
|
if (LINE_TERMINATORS.has(ch)) {
|
|
9519
9628
|
return { value: "", end: start + 2 };
|
|
9520
9629
|
}
|
|
9630
|
+
if (ch === "{" || ch === "}") {
|
|
9631
|
+
return { value: "\\" + ch, end: start + 2 };
|
|
9632
|
+
}
|
|
9521
9633
|
return { value: ch, end: start + 2 };
|
|
9522
9634
|
}
|
|
9523
9635
|
function validateString(str) {
|
|
@@ -12848,6 +12960,7 @@ function parsePageDirective(cursor, token) {
|
|
|
12848
12960
|
if (valToken && (valToken.type === "STRING" || valToken.type === "NUMBER")) {
|
|
12849
12961
|
cursor.advance();
|
|
12850
12962
|
value = valToken.value;
|
|
12963
|
+
if (valToken.type === "STRING") value = String(value).replace(/\\([{}"])/g, "$1");
|
|
12851
12964
|
if (valToken.type === "NUMBER") value = parseFloat(valToken.value);
|
|
12852
12965
|
} else if (cursor.match("EQUALS") || cursor.match("COLON")) {
|
|
12853
12966
|
cursor.skipWhitespace();
|
|
@@ -12855,6 +12968,7 @@ function parsePageDirective(cursor, token) {
|
|
|
12855
12968
|
if (vToken) {
|
|
12856
12969
|
cursor.advance();
|
|
12857
12970
|
value = vToken.value;
|
|
12971
|
+
if (vToken.type === "STRING") value = String(value).replace(/\\([{}"])/g, "$1");
|
|
12858
12972
|
if (vToken.type === "NUMBER") value = parseFloat(vToken.value);
|
|
12859
12973
|
if (vToken.type === "BOOLEAN") value = vToken.value === "true";
|
|
12860
12974
|
}
|
|
@@ -12878,7 +12992,7 @@ function parsePageDirective(cursor, token) {
|
|
|
12878
12992
|
const nextToken = cursor.peek();
|
|
12879
12993
|
if (nextToken && (nextToken.type === "STRING" || nextToken.type === "NUMBER" || nextToken.type === "IDENT" || nextToken.type === "KEYWORD")) {
|
|
12880
12994
|
cursor.advance();
|
|
12881
|
-
options[optName] = nextToken.value;
|
|
12995
|
+
options[optName] = nextToken.type === "STRING" ? String(nextToken.value).replace(/\\([{}"])/g, "$1") : nextToken.value;
|
|
12882
12996
|
} else if (cursor.match("EQUALS")) {
|
|
12883
12997
|
cursor.skipWhitespace();
|
|
12884
12998
|
const optVal = cursor.consumeValue();
|
|
@@ -13260,6 +13374,7 @@ function consumeStateValue(cursor) {
|
|
|
13260
13374
|
if (!t2) return "";
|
|
13261
13375
|
if (t2.type === "STRING" || t2.type === "NUMBER" || t2.type === "IDENT" || t2.type === "KEYWORD") {
|
|
13262
13376
|
cursor.advance();
|
|
13377
|
+
if (t2.type === "STRING") return String(t2.value).replace(/\\([{}"])/g, "$1");
|
|
13263
13378
|
return t2.value;
|
|
13264
13379
|
}
|
|
13265
13380
|
if (t2.type === "LBRACKET" || t2.type === "LBRACE" || t2.type === "LBRACK") {
|
|
@@ -13272,7 +13387,7 @@ function consumeStateValue(cursor) {
|
|
|
13272
13387
|
else if (tk.type === "RBRACKET" || tk.type === "RBRACE" || tk.type === "RBRACK") depth--;
|
|
13273
13388
|
const after = cursor.peek(1);
|
|
13274
13389
|
if (tk.type === "STRING") {
|
|
13275
|
-
parts.push(`"${tk.value}"`);
|
|
13390
|
+
parts.push(`"${String(tk.value).replace(/\\([{}"])/g, "$1")}"`);
|
|
13276
13391
|
} else if (tk.type === "IDENT" && after && after.type === "COLON" && depth > 0) {
|
|
13277
13392
|
parts.push(`"${tk.value}"`);
|
|
13278
13393
|
} else {
|
|
@@ -21274,6 +21389,15 @@ function splitArgs2(str) {
|
|
|
21274
21389
|
function findMatchingBrace3(str, start) {
|
|
21275
21390
|
let depth = 1;
|
|
21276
21391
|
for (let i = start + 1; i < str.length; i++) {
|
|
21392
|
+
if (str[i] === '"' || str[i] === "'") {
|
|
21393
|
+
const q = str[i];
|
|
21394
|
+
i++;
|
|
21395
|
+
while (i < str.length && str[i] !== q) {
|
|
21396
|
+
if (str[i] === "\\") i++;
|
|
21397
|
+
i++;
|
|
21398
|
+
}
|
|
21399
|
+
continue;
|
|
21400
|
+
}
|
|
21277
21401
|
if (str[i] === "{") depth++;
|
|
21278
21402
|
else if (str[i] === "}") {
|
|
21279
21403
|
depth--;
|
|
@@ -22826,7 +22950,7 @@ var init_types4 = __esm({
|
|
|
22826
22950
|
var TW_VERSION, TW_GENERATOR_META;
|
|
22827
22951
|
var init_version = __esm({
|
|
22828
22952
|
"../../packages/compiler/tw/codegen/version.ts"() {
|
|
22829
|
-
TW_VERSION = "1.0.
|
|
22953
|
+
TW_VERSION = "1.0.7";
|
|
22830
22954
|
TW_GENERATOR_META = `<meta name="generator" content="TW Framework ${TW_VERSION}">`;
|
|
22831
22955
|
}
|
|
22832
22956
|
});
|
|
@@ -23386,6 +23510,14 @@ function interpolate(template, vars) {
|
|
|
23386
23510
|
let i = 0;
|
|
23387
23511
|
while (i < template.length) {
|
|
23388
23512
|
const ch = template[i];
|
|
23513
|
+
if (ch === "\\") {
|
|
23514
|
+
const nxt = template[i + 1];
|
|
23515
|
+
if (nxt === "{" || nxt === "}") {
|
|
23516
|
+
result += nxt;
|
|
23517
|
+
i += 2;
|
|
23518
|
+
continue;
|
|
23519
|
+
}
|
|
23520
|
+
}
|
|
23389
23521
|
if (ch === "{" && template[i + 1] !== "{") {
|
|
23390
23522
|
let depth = 1;
|
|
23391
23523
|
let j = i + 1;
|
|
@@ -23874,7 +24006,8 @@ function validatePlainCss(src, filePath) {
|
|
|
23874
24006
|
function compileDecl(decl) {
|
|
23875
24007
|
const trimmed = decl.trim();
|
|
23876
24008
|
if (!trimmed) return "";
|
|
23877
|
-
|
|
24009
|
+
const cssForm = /^([-\w]+)\s*:\s*/.exec(trimmed);
|
|
24010
|
+
if (cssForm) return trimmed.replace(/^([-\w]+)\s*:\s*/, "$1: ");
|
|
23878
24011
|
const spaceIdx = trimmed.search(/[\s]/);
|
|
23879
24012
|
if (spaceIdx === -1) {
|
|
23880
24013
|
return TSS_SHORTHANDS2[trimmed] ?? trimmed;
|
|
@@ -23888,9 +24021,72 @@ function combineSelector(parent, child) {
|
|
|
23888
24021
|
if (child.includes("&")) return child.replace(/&/g, parent);
|
|
23889
24022
|
return `${parent} ${child}`;
|
|
23890
24023
|
}
|
|
24024
|
+
function stripTssComments(src) {
|
|
24025
|
+
let out = "";
|
|
24026
|
+
let i = 0;
|
|
24027
|
+
const n = src.length;
|
|
24028
|
+
while (i < n) {
|
|
24029
|
+
const c = src[i];
|
|
24030
|
+
if (c === '"' || c === "'") {
|
|
24031
|
+
const q = c;
|
|
24032
|
+
out += c;
|
|
24033
|
+
i++;
|
|
24034
|
+
while (i < n && src[i] !== q) {
|
|
24035
|
+
if (src[i] === "\\") {
|
|
24036
|
+
out += src[i++];
|
|
24037
|
+
if (i < n) out += src[i++];
|
|
24038
|
+
continue;
|
|
24039
|
+
}
|
|
24040
|
+
out += src[i++];
|
|
24041
|
+
}
|
|
24042
|
+
out += src[i] ?? "";
|
|
24043
|
+
i++;
|
|
24044
|
+
continue;
|
|
24045
|
+
}
|
|
24046
|
+
if (c === "u" && /^url\s*\(/i.test(src.slice(i, i + 6))) {
|
|
24047
|
+
let depth = 0;
|
|
24048
|
+
let j = i;
|
|
24049
|
+
for (; j < n; j++) {
|
|
24050
|
+
if (src[j] === "(") depth++;
|
|
24051
|
+
else if (src[j] === ")") {
|
|
24052
|
+
depth--;
|
|
24053
|
+
if (depth === 0) {
|
|
24054
|
+
j++;
|
|
24055
|
+
break;
|
|
24056
|
+
}
|
|
24057
|
+
}
|
|
24058
|
+
}
|
|
24059
|
+
out += src.slice(i, j);
|
|
24060
|
+
i = j;
|
|
24061
|
+
continue;
|
|
24062
|
+
}
|
|
24063
|
+
if (c === "/" && src[i + 1] === "/") {
|
|
24064
|
+
while (i < n && src[i] !== "\n") i++;
|
|
24065
|
+
continue;
|
|
24066
|
+
}
|
|
24067
|
+
if (c === "/" && src[i + 1] === "*") {
|
|
24068
|
+
const end = src.indexOf("*/", i + 2);
|
|
24069
|
+
i = end === -1 ? n : end + 2;
|
|
24070
|
+
out += " ";
|
|
24071
|
+
continue;
|
|
24072
|
+
}
|
|
24073
|
+
out += c;
|
|
24074
|
+
i++;
|
|
24075
|
+
}
|
|
24076
|
+
return out;
|
|
24077
|
+
}
|
|
23891
24078
|
function compileTSS(source) {
|
|
24079
|
+
const __twOut = __twCompileTSSInner(source);
|
|
24080
|
+
const leftover = __twOut.match(/\$[A-Za-z_][\w-]*/g);
|
|
24081
|
+
if (leftover && !/\$\{/.test(__twOut)) {
|
|
24082
|
+
const seen = Array.from(new Set(leftover));
|
|
24083
|
+
console.warn("[tss] unsubstituted $var(s) in output: " + seen.join(", ") + " -- define them as state vars or fix the spelling");
|
|
24084
|
+
}
|
|
24085
|
+
return __twOut;
|
|
24086
|
+
}
|
|
24087
|
+
function __twCompileTSSInner(source) {
|
|
23892
24088
|
if (!source || !source.trim()) return "";
|
|
23893
|
-
let src = source
|
|
24089
|
+
let src = stripTssComments(source);
|
|
23894
24090
|
const vars = {};
|
|
23895
24091
|
src = src.replace(/\$([A-Za-z_][\w-]*)\s*:\s*([^;{}\n]+);?/g, (_m, name, val) => {
|
|
23896
24092
|
vars[name] = String(val).trim();
|
|
@@ -24100,6 +24296,11 @@ function stripComments(src) {
|
|
|
24100
24296
|
out += c;
|
|
24101
24297
|
i++;
|
|
24102
24298
|
while (i < n && src[i] !== q) {
|
|
24299
|
+
if (src[i] === "\\") {
|
|
24300
|
+
out += src[i++];
|
|
24301
|
+
if (i < n) out += src[i++];
|
|
24302
|
+
continue;
|
|
24303
|
+
}
|
|
24103
24304
|
out += src[i++];
|
|
24104
24305
|
}
|
|
24105
24306
|
out += src[i] ?? "";
|
|
@@ -24418,9 +24619,19 @@ function emit(items, parent, env, mixins, out) {
|
|
|
24418
24619
|
out.push(...childCss);
|
|
24419
24620
|
}
|
|
24420
24621
|
function compileSCSS(source) {
|
|
24622
|
+
const cleaned0 = stripComments(source);
|
|
24623
|
+
const UNSUPPORTED = /@(import|mixin|include|extend|use|forward|function|each|if)\b/g;
|
|
24624
|
+
const m = cleaned0.match(UNSUPPORTED);
|
|
24625
|
+
if (m) {
|
|
24626
|
+
const seen = Array.from(new Set(m.map((x) => x.trim())));
|
|
24627
|
+
console.warn("[scss] unsupported at-rule(s) ignored: " + seen.join(", ") + " -- TW's SCSS subset supports nesting, $vars and & references only");
|
|
24628
|
+
}
|
|
24421
24629
|
const items = parseItems(stripComments(source));
|
|
24422
24630
|
const out = [];
|
|
24423
24631
|
emit(items, "", [/* @__PURE__ */ new Map()], /* @__PURE__ */ new Map(), out);
|
|
24632
|
+
if (out.length === 0 && cleaned0.replace(/[\s;]/g, "").length > 0) {
|
|
24633
|
+
console.warn("[scss] compileSCSS produced no output for a non-empty source -- check for unsupported syntax (TW uses `color: #fff` declarations, not `color #fff`)");
|
|
24634
|
+
}
|
|
24424
24635
|
return out.join("\n");
|
|
24425
24636
|
}
|
|
24426
24637
|
var init_scss = __esm({
|
|
@@ -24460,7 +24671,8 @@ function addScope(css, scopeId) {
|
|
|
24460
24671
|
return css.replace(/([.#]?[\w-]+)\s*\{/g, `$1[data-tw-scope="${scopeId}"] {`);
|
|
24461
24672
|
}
|
|
24462
24673
|
function interpolate2(expr, vars) {
|
|
24463
|
-
|
|
24674
|
+
const masked = expr.replace(/\\\{/g, "O").replace(/\\\}/g, "C");
|
|
24675
|
+
const out = masked.replace(/\{([^}]+)\}/g, (_, name) => {
|
|
24464
24676
|
const key = name.trim();
|
|
24465
24677
|
if (/^[a-zA-Z_$][\w$]*$/.test(key)) {
|
|
24466
24678
|
return vars[key] ?? "";
|
|
@@ -24468,6 +24680,7 @@ function interpolate2(expr, vars) {
|
|
|
24468
24680
|
const val = evaluate(key, vars);
|
|
24469
24681
|
return val === null ? "" : val;
|
|
24470
24682
|
});
|
|
24683
|
+
return out.replace(/\u0001O/g, "{").replace(/\u0001C/g, "}");
|
|
24471
24684
|
}
|
|
24472
24685
|
function isTruthy2(val) {
|
|
24473
24686
|
return isTruthy(val);
|
|
@@ -24869,6 +25082,8 @@ function renderAttrs(el, ctx) {
|
|
|
24869
25082
|
} else if (typeof attr.value === "string" && !attr.name.startsWith("data-tw-event") && /\{[^}]+\}/.test(attr.value) && Object.keys(ctx.stateVars).length > 0) {
|
|
24870
25083
|
const val = interpolate2(attr.value, ctx.stateVars);
|
|
24871
25084
|
parts.push(`${attr.name}="${escapeAttr2(sanitizeUrlAttr(attr.name, val))}"`);
|
|
25085
|
+
} else if (typeof attr.value === "string" && /\\[{}]/.test(attr.value)) {
|
|
25086
|
+
parts.push(`${attr.name}="${escapeAttr2(sanitizeUrlAttr(attr.name, String(attr.value).replace(/\\([{}])/g, "$1")))}"`);
|
|
24872
25087
|
} else {
|
|
24873
25088
|
parts.push(`${attr.name}="${escapeAttr2(sanitizeUrlAttr(attr.name, attr.value))}"`);
|
|
24874
25089
|
}
|
|
@@ -24975,6 +25190,9 @@ function generateText(text, ctx) {
|
|
|
24975
25190
|
}
|
|
24976
25191
|
return escapeHTML(interpolate2(text.value, ctx.stateVars));
|
|
24977
25192
|
}
|
|
25193
|
+
if (text.value && /\\[{}]/.test(text.value)) {
|
|
25194
|
+
return escapeHTML(text.value.replace(/\\([{}])/g, "$1"));
|
|
25195
|
+
}
|
|
24978
25196
|
return escapeHTML(text.value);
|
|
24979
25197
|
}
|
|
24980
25198
|
function registerComponentTemplate(name, program) {
|
|
@@ -26458,7 +26676,7 @@ function emitElement(gen, el, ctx) {
|
|
|
26458
26676
|
attrStr = el.attrs.map((a) => {
|
|
26459
26677
|
if (a.value === true) return a.name;
|
|
26460
26678
|
const val = a.isInterpolated ? interpolate3(a.value, ctx.stateVars) : a.value;
|
|
26461
|
-
return `${a.name}="${escapeAttr3(val)}"`;
|
|
26679
|
+
return `${a.name}="${escapeAttr3(sanitizeUrlAttr2(a.name, val))}"`;
|
|
26462
26680
|
}).join(" ");
|
|
26463
26681
|
}
|
|
26464
26682
|
if (el.styles && el.styles.length > 0) {
|
|
@@ -26574,14 +26792,25 @@ function getDirectiveValue2(program, key) {
|
|
|
26574
26792
|
function escapeHTML2(str) {
|
|
26575
26793
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
26576
26794
|
}
|
|
26795
|
+
function sanitizeUrlAttr2(name, value) {
|
|
26796
|
+
const URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href", "poster", "background"]);
|
|
26797
|
+
if (!URL_ATTRS.has(name.toLowerCase())) return value;
|
|
26798
|
+
const trimmed = String(value).trim().toLowerCase().replace(/[\s\0]/g, "");
|
|
26799
|
+
if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:text/html") || trimmed.startsWith("vbscript:")) {
|
|
26800
|
+
return "#";
|
|
26801
|
+
}
|
|
26802
|
+
return value;
|
|
26803
|
+
}
|
|
26577
26804
|
function escapeAttr3(str) {
|
|
26578
26805
|
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'");
|
|
26579
26806
|
}
|
|
26580
26807
|
function interpolate3(expr, vars) {
|
|
26581
|
-
|
|
26808
|
+
const masked = expr.replace(/\\\{/g, "O").replace(/\\\}/g, "C");
|
|
26809
|
+
const out = masked.replace(/\{([^}]+)\}/g, (_, name) => {
|
|
26582
26810
|
const key = name.trim();
|
|
26583
26811
|
return vars[key] ?? "";
|
|
26584
26812
|
});
|
|
26813
|
+
return out.replace(/\u0001O/g, "{").replace(/\u0001C/g, "}");
|
|
26585
26814
|
}
|
|
26586
26815
|
function isTruthy3(val) {
|
|
26587
26816
|
if (!val) return false;
|
|
@@ -26785,6 +27014,15 @@ var init_streaming = __esm({
|
|
|
26785
27014
|
});
|
|
26786
27015
|
|
|
26787
27016
|
// ../../packages/compiler/tw/codegen/vdom-gen.ts
|
|
27017
|
+
function sanitizeUrlAttr3(name, value) {
|
|
27018
|
+
const URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href", "poster", "background"]);
|
|
27019
|
+
if (!URL_ATTRS.has(name.toLowerCase())) return value;
|
|
27020
|
+
const trimmed = String(value).trim().toLowerCase().replace(/[\s\0]/g, "");
|
|
27021
|
+
if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:text/html") || trimmed.startsWith("vbscript:")) {
|
|
27022
|
+
return "#";
|
|
27023
|
+
}
|
|
27024
|
+
return value;
|
|
27025
|
+
}
|
|
26788
27026
|
function generateVDOMCode(program, ctx) {
|
|
26789
27027
|
const builder2 = new VDOMCodeBuilder(ctx);
|
|
26790
27028
|
return builder2.generateModule(program);
|
|
@@ -26798,10 +27036,12 @@ function generateNodeVDOM(node, ctx) {
|
|
|
26798
27036
|
return builder2.generateFromAST(node);
|
|
26799
27037
|
}
|
|
26800
27038
|
function interpolate4(expr, vars) {
|
|
26801
|
-
|
|
27039
|
+
const masked = expr.replace(/\\\{/g, "O").replace(/\\\}/g, "C");
|
|
27040
|
+
const out = masked.replace(/\{([^}]+)\}/g, (_, name) => {
|
|
26802
27041
|
const key = name.trim();
|
|
26803
27042
|
return vars[key] ?? "";
|
|
26804
27043
|
});
|
|
27044
|
+
return out.replace(/\u0001O/g, "{").replace(/\u0001C/g, "}");
|
|
26805
27045
|
}
|
|
26806
27046
|
function isTruthy4(val) {
|
|
26807
27047
|
if (!val) return false;
|
|
@@ -26935,7 +27175,7 @@ var init_vdom_gen = __esm({
|
|
|
26935
27175
|
const expr = this.interpolateToExpr(attr.value);
|
|
26936
27176
|
attrs.push(`${JSON.stringify(attr.name)}: ${expr}`);
|
|
26937
27177
|
} else {
|
|
26938
|
-
attrs.push(`${JSON.stringify(attr.name)}: ${JSON.stringify(attr.value)}`);
|
|
27178
|
+
attrs.push(`${JSON.stringify(attr.name)}: ${JSON.stringify(sanitizeUrlAttr3(attr.name, attr.value))}`);
|
|
26939
27179
|
}
|
|
26940
27180
|
}
|
|
26941
27181
|
}
|
|
@@ -28952,7 +29192,7 @@ function generateVDOM(program, ctx) {
|
|
|
28952
29192
|
return {
|
|
28953
29193
|
t: el.tag,
|
|
28954
29194
|
a: el.attrs.reduce((acc, a) => {
|
|
28955
|
-
acc[a.name] = a.value;
|
|
29195
|
+
acc[a.name] = sanitizeUrlAttr4(a.name, String(a.value));
|
|
28956
29196
|
return acc;
|
|
28957
29197
|
}, {}),
|
|
28958
29198
|
c: el.children.map(toVNode)
|
|
@@ -28990,16 +29230,27 @@ function processDirectives(program, ctx) {
|
|
|
28990
29230
|
}
|
|
28991
29231
|
}
|
|
28992
29232
|
function interpolate5(expr, vars) {
|
|
28993
|
-
|
|
29233
|
+
const masked = expr.replace(/\\\{/g, "O").replace(/\\\}/g, "C");
|
|
29234
|
+
const out = masked.replace(/\{([^}]+)\}/g, (_, name) => {
|
|
28994
29235
|
const key = name.trim();
|
|
28995
29236
|
return vars[key] ?? "";
|
|
28996
29237
|
});
|
|
29238
|
+
return out.replace(/\u0001O/g, "{").replace(/\u0001C/g, "}");
|
|
28997
29239
|
}
|
|
28998
29240
|
function isTruthy5(val) {
|
|
28999
29241
|
if (!val) return false;
|
|
29000
29242
|
const lower = val.toLowerCase().trim();
|
|
29001
29243
|
return lower !== "false" && lower !== "0" && lower !== "" && lower !== "null" && lower !== "undefined";
|
|
29002
29244
|
}
|
|
29245
|
+
function sanitizeUrlAttr4(name, value) {
|
|
29246
|
+
const URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href", "poster", "background"]);
|
|
29247
|
+
if (!URL_ATTRS.has(name.toLowerCase())) return value;
|
|
29248
|
+
const trimmed = String(value).trim().toLowerCase().replace(/[\s\0]/g, "");
|
|
29249
|
+
if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:text/html") || trimmed.startsWith("vbscript:")) {
|
|
29250
|
+
return "#";
|
|
29251
|
+
}
|
|
29252
|
+
return value;
|
|
29253
|
+
}
|
|
29003
29254
|
function generate(program, stateVars) {
|
|
29004
29255
|
const startTime = performance.now();
|
|
29005
29256
|
const ctx = createContext("ssr");
|
|
@@ -33080,7 +33331,12 @@ async function renderIslandContent(component, props, ctx) {
|
|
|
33080
33331
|
return { html: result.html, css: result.css, js: result.js };
|
|
33081
33332
|
}
|
|
33082
33333
|
function countServerComponents(source) {
|
|
33083
|
-
|
|
33334
|
+
let cleaned = source;
|
|
33335
|
+
try {
|
|
33336
|
+
cleaned = stripCommentsStringAware(source);
|
|
33337
|
+
} catch {
|
|
33338
|
+
}
|
|
33339
|
+
const matches = cleaned.match(/@render\s+mode:\s*['"]server['"]/g);
|
|
33084
33340
|
return matches ? matches.length : 0;
|
|
33085
33341
|
}
|
|
33086
33342
|
function determineHydrationStrategy(component, position) {
|
|
@@ -33118,6 +33374,7 @@ function parseClientDirective(source) {
|
|
|
33118
33374
|
var componentRegistry2;
|
|
33119
33375
|
var init_server_components = __esm({
|
|
33120
33376
|
"../../packages/compiler/tw/server-components.ts"() {
|
|
33377
|
+
init_tw();
|
|
33121
33378
|
init_tw4();
|
|
33122
33379
|
componentRegistry2 = /* @__PURE__ */ new Map();
|
|
33123
33380
|
}
|
|
@@ -40041,385 +40298,6 @@ var init_tw4 = __esm({
|
|
|
40041
40298
|
}
|
|
40042
40299
|
});
|
|
40043
40300
|
|
|
40044
|
-
// ../../packages/shared/tw/node-import.ts
|
|
40045
|
-
var node_import_exports = {};
|
|
40046
|
-
__export(node_import_exports, {
|
|
40047
|
-
twImportTs: () => twImportTs
|
|
40048
|
-
});
|
|
40049
|
-
import { join as join10 } from "node:path";
|
|
40050
|
-
import { tmpdir } from "node:os";
|
|
40051
|
-
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
40052
|
-
async function twImportTs(tsPath) {
|
|
40053
|
-
if (!globalThis.__TW_NODE) {
|
|
40054
|
-
return import(tsPath);
|
|
40055
|
-
}
|
|
40056
|
-
const esbuildMain = globalThis.__TW_ESBUILD_MODULE;
|
|
40057
|
-
const esbuild = await (esbuildMain ? import(esbuildMain) : import("esbuild"));
|
|
40058
|
-
const safe = tsPath.replace(/[^a-zA-Z0-9]/g, "_").slice(-120);
|
|
40059
|
-
const cachePath = join10(tmpdir(), "tw-" + safe + ".mjs");
|
|
40060
|
-
await esbuild.build({
|
|
40061
|
-
entryPoints: [tsPath],
|
|
40062
|
-
bundle: true,
|
|
40063
|
-
format: "esm",
|
|
40064
|
-
platform: "node",
|
|
40065
|
-
target: "node18",
|
|
40066
|
-
outfile: cachePath,
|
|
40067
|
-
logLevel: "silent"
|
|
40068
|
-
});
|
|
40069
|
-
return import(pathToFileURL2(cachePath).href);
|
|
40070
|
-
}
|
|
40071
|
-
var init_node_import = __esm({
|
|
40072
|
-
"../../packages/shared/tw/node-import.ts"() {
|
|
40073
|
-
}
|
|
40074
|
-
});
|
|
40075
|
-
|
|
40076
|
-
// tw/commands/client-bundle.ts
|
|
40077
|
-
var client_bundle_exports = {};
|
|
40078
|
-
__export(client_bundle_exports, {
|
|
40079
|
-
BUILTIN_CLIENT_OK_SPECIFIERS: () => BUILTIN_CLIENT_OK_SPECIFIERS,
|
|
40080
|
-
RUNTIME_CLIENT_OK_SPECIFIERS: () => RUNTIME_CLIENT_OK_SPECIFIERS,
|
|
40081
|
-
buildClientChunk: () => buildClientChunk,
|
|
40082
|
-
buildPageScope: () => buildPageScope,
|
|
40083
|
-
collectClientImportsFromSource: () => collectClientImportsFromSource,
|
|
40084
|
-
findServerOnlyViolations: () => findServerOnlyViolations,
|
|
40085
|
-
importNames: () => importNames,
|
|
40086
|
-
isClientImportSpecifier: () => isClientImportSpecifier
|
|
40087
|
-
});
|
|
40088
|
-
import { join as join11, resolve as resolve3 } from "node:path";
|
|
40089
|
-
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
40090
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
40091
|
-
function isClientImportSpecifier(spec) {
|
|
40092
|
-
if (spec.startsWith("@./") || spec.startsWith("./") || spec.startsWith("../")) return false;
|
|
40093
|
-
if (/\.(tw|tss|twm|ts|js)$/.test(spec)) return false;
|
|
40094
|
-
if (spec.startsWith("@tw/")) {
|
|
40095
|
-
return RUNTIME_CLIENT_OK_SPECIFIERS.includes(spec);
|
|
40096
|
-
}
|
|
40097
|
-
return true;
|
|
40098
|
-
}
|
|
40099
|
-
function collectClientImportsFromSource(src) {
|
|
40100
|
-
const lines = [];
|
|
40101
|
-
let m;
|
|
40102
|
-
IMPORT_RE.lastIndex = 0;
|
|
40103
|
-
while (m = IMPORT_RE.exec(src)) {
|
|
40104
|
-
if (isClientImportSpecifier(m[2])) lines.push(m[0].trim().replace(/;$/, ""));
|
|
40105
|
-
}
|
|
40106
|
-
return lines;
|
|
40107
|
-
}
|
|
40108
|
-
function importNames(line) {
|
|
40109
|
-
const clause = line.match(/^import\s+([\s\S]+?)\s+from/);
|
|
40110
|
-
if (!clause) return [];
|
|
40111
|
-
const c = clause[1].trim();
|
|
40112
|
-
const names = [];
|
|
40113
|
-
if (c.startsWith("*")) {
|
|
40114
|
-
const as = c.split(/\s+as\s+/);
|
|
40115
|
-
if (as[1]) names.push(as[1].trim());
|
|
40116
|
-
} else if (c.startsWith("{")) {
|
|
40117
|
-
const braces = c.match(/\{([\s\S]*)\}/);
|
|
40118
|
-
if (braces) {
|
|
40119
|
-
for (const part of braces[1].split(",")) {
|
|
40120
|
-
const p = part.trim();
|
|
40121
|
-
if (!p) continue;
|
|
40122
|
-
const seg = p.split(/\s+as\s+/);
|
|
40123
|
-
names.push(seg[seg.length - 1].trim());
|
|
40124
|
-
}
|
|
40125
|
-
}
|
|
40126
|
-
} else {
|
|
40127
|
-
const def = c.match(/^[A-Za-z_$][\w$]*/);
|
|
40128
|
-
if (def) names.push(def[0]);
|
|
40129
|
-
}
|
|
40130
|
-
return names;
|
|
40131
|
-
}
|
|
40132
|
-
function findEsbuildBin(rootDir) {
|
|
40133
|
-
const candidates = [
|
|
40134
|
-
join11(rootDir, "node_modules", ".bin", "esbuild"),
|
|
40135
|
-
resolve3(rootDir, "..", "node_modules", ".bin", "esbuild"),
|
|
40136
|
-
// CLI repo layout (dev / source runs)
|
|
40137
|
-
resolve3(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "node_modules", ".bin", "esbuild")
|
|
40138
|
-
];
|
|
40139
|
-
for (const c of candidates) {
|
|
40140
|
-
try {
|
|
40141
|
-
if (existsSync7(c)) return c;
|
|
40142
|
-
} catch {
|
|
40143
|
-
}
|
|
40144
|
-
}
|
|
40145
|
-
const bd = globalThis.__TW_BUNDLE_DIR;
|
|
40146
|
-
if (bd) {
|
|
40147
|
-
const b = resolve3(bd, "..", "..", "..", "node_modules", ".bin", "esbuild");
|
|
40148
|
-
try {
|
|
40149
|
-
if (existsSync7(b)) return b;
|
|
40150
|
-
} catch {
|
|
40151
|
-
}
|
|
40152
|
-
}
|
|
40153
|
-
return "esbuild";
|
|
40154
|
-
}
|
|
40155
|
-
function findServerOnlyViolations(src) {
|
|
40156
|
-
const out = [];
|
|
40157
|
-
let m;
|
|
40158
|
-
IMPORT_RE.lastIndex = 0;
|
|
40159
|
-
while (m = IMPORT_RE.exec(src)) {
|
|
40160
|
-
const spec = m[2];
|
|
40161
|
-
if (spec === "lib" || spec.startsWith("lib/")) {
|
|
40162
|
-
out.push(spec);
|
|
40163
|
-
continue;
|
|
40164
|
-
}
|
|
40165
|
-
if (isClientImportSpecifier(spec)) continue;
|
|
40166
|
-
if (spec.startsWith("@tw/")) {
|
|
40167
|
-
if (BUILTIN_CLIENT_OK_SPECIFIERS.includes(spec)) continue;
|
|
40168
|
-
if (RUNTIME_CLIENT_OK_SPECIFIERS.includes(spec)) continue;
|
|
40169
|
-
out.push(spec);
|
|
40170
|
-
continue;
|
|
40171
|
-
}
|
|
40172
|
-
if (spec.startsWith("@./") || spec.startsWith("./") || spec.startsWith("../")) {
|
|
40173
|
-
if (/\.(tw|tss)$/.test(spec)) continue;
|
|
40174
|
-
out.push(spec);
|
|
40175
|
-
continue;
|
|
40176
|
-
}
|
|
40177
|
-
if (spec.startsWith("lib/")) out.push(spec);
|
|
40178
|
-
}
|
|
40179
|
-
return out;
|
|
40180
|
-
}
|
|
40181
|
-
function contentHash2(s) {
|
|
40182
|
-
let h = 5381;
|
|
40183
|
-
for (let i = 0; i < s.length; i++) h = (h * 33 ^ s.charCodeAt(i)) >>> 0;
|
|
40184
|
-
return h.toString(36);
|
|
40185
|
-
}
|
|
40186
|
-
function vendoredRuntimeClient() {
|
|
40187
|
-
const fromSrc = new URL("../../dist/tw-runtime-client.mjs", import.meta.url).pathname;
|
|
40188
|
-
if (existsSync7(fromSrc)) return fromSrc;
|
|
40189
|
-
const fromBundle = new URL("./tw-runtime-client.mjs", import.meta.url).pathname;
|
|
40190
|
-
if (existsSync7(fromBundle)) return fromBundle;
|
|
40191
|
-
return null;
|
|
40192
|
-
}
|
|
40193
|
-
function rewriteRuntimeSpecifier(importLine) {
|
|
40194
|
-
const vendored = vendoredRuntimeClient();
|
|
40195
|
-
if (!vendored) return importLine;
|
|
40196
|
-
const m = importLine.match(/from\s+["'](@tw\/runtime)["']/);
|
|
40197
|
-
if (!m) return importLine;
|
|
40198
|
-
return importLine.replace(m[0], `from ${JSON.stringify(vendored)}`);
|
|
40199
|
-
}
|
|
40200
|
-
function buildClientChunk(rootDir, importLine, minify) {
|
|
40201
|
-
if (!importLine) return "";
|
|
40202
|
-
const cached = chunkCache.get(importLine);
|
|
40203
|
-
if (cached && existsSync7(join11(rootDir, ".tw", cached))) return cached;
|
|
40204
|
-
const jsDir = join11(rootDir, ".tw", "js");
|
|
40205
|
-
mkdirSync4(jsDir, { recursive: true });
|
|
40206
|
-
const names = importNames(importLine);
|
|
40207
|
-
const entryLine = rewriteRuntimeSpecifier(importLine);
|
|
40208
|
-
const entry = [
|
|
40209
|
-
entryLine + ";",
|
|
40210
|
-
"globalThis.__twClient = globalThis.__twClient || {};",
|
|
40211
|
-
...names.map((n) => "globalThis.__twClient." + n + " = " + n + ";")
|
|
40212
|
-
].join("\n");
|
|
40213
|
-
const stamp = contentHash2(importLine);
|
|
40214
|
-
const entryPath = join11(rootDir, ".tw", ".chunk-entry-" + stamp + ".js");
|
|
40215
|
-
const tmpOut = join11(rootDir, ".tw", ".chunk-tmp-" + stamp + ".js");
|
|
40216
|
-
writeFileSync4(entryPath, entry);
|
|
40217
|
-
const bin = findEsbuildBin(rootDir);
|
|
40218
|
-
const args = [entryPath, "--bundle", "--format=iife", "--target=es2019", "--outfile=" + tmpOut];
|
|
40219
|
-
if (minify) args.push("--minify");
|
|
40220
|
-
const r = spawnSync2(bin, args, { cwd: rootDir });
|
|
40221
|
-
try {
|
|
40222
|
-
unlinkSync2(entryPath);
|
|
40223
|
-
} catch {
|
|
40224
|
-
}
|
|
40225
|
-
if (r.status !== 0) {
|
|
40226
|
-
const errText = (r.stderr && r.stderr.toString ? r.stderr.toString() : "") || String(r.error || "unknown");
|
|
40227
|
-
console.error(" [tw] client chunk failed (" + importLine + "): " + errText.split("\n")[0]);
|
|
40228
|
-
try {
|
|
40229
|
-
unlinkSync2(tmpOut);
|
|
40230
|
-
} catch {
|
|
40231
|
-
}
|
|
40232
|
-
return "";
|
|
40233
|
-
}
|
|
40234
|
-
const code = readFileSync6(tmpOut, "utf-8");
|
|
40235
|
-
const url = "js/c-" + contentHash2(code) + ".js";
|
|
40236
|
-
writeFileSync4(join11(rootDir, ".tw", url), code);
|
|
40237
|
-
try {
|
|
40238
|
-
unlinkSync2(tmpOut);
|
|
40239
|
-
} catch {
|
|
40240
|
-
}
|
|
40241
|
-
chunkCache.set(importLine, url);
|
|
40242
|
-
return url;
|
|
40243
|
-
}
|
|
40244
|
-
function buildPageScope(rootDir, ownImportLines) {
|
|
40245
|
-
if (!ownImportLines || ownImportLines.length === 0) return "";
|
|
40246
|
-
const names = ownImportLines.flatMap((l) => importNames(l));
|
|
40247
|
-
if (names.length === 0) return "";
|
|
40248
|
-
mkdirSync4(join11(rootDir, ".tw", "js"), { recursive: true });
|
|
40249
|
-
const body = names.map((n) => "p." + n + " = g.__twClient." + n + ";").join("\n");
|
|
40250
|
-
const code = "(function(g){g.__twClient=g.__twClient||{};var p={};\n" + body + "\ng.__twClient.$page=p;})(globalThis);\n";
|
|
40251
|
-
const url = "js/p-" + contentHash2(names.slice().sort().join("|")) + ".js";
|
|
40252
|
-
writeFileSync4(join11(rootDir, ".tw", url), code);
|
|
40253
|
-
return url;
|
|
40254
|
-
}
|
|
40255
|
-
var IMPORT_RE, BUILTIN_CLIENT_OK_SPECIFIERS, RUNTIME_CLIENT_OK_SPECIFIERS, chunkCache;
|
|
40256
|
-
var init_client_bundle = __esm({
|
|
40257
|
-
"tw/commands/client-bundle.ts"() {
|
|
40258
|
-
IMPORT_RE = /^[ \t]*import\s+([^\n]+?)\s+from\s+["'']([^"'']+)["''][; \t]*$/gm;
|
|
40259
|
-
BUILTIN_CLIENT_OK_SPECIFIERS = ["@tw/optImage", "@tw/RouterLink"];
|
|
40260
|
-
RUNTIME_CLIENT_OK_SPECIFIERS = ["@tw/runtime"];
|
|
40261
|
-
chunkCache = /* @__PURE__ */ new Map();
|
|
40262
|
-
}
|
|
40263
|
-
});
|
|
40264
|
-
|
|
40265
|
-
// ../../packages/server/tw/routing/revalidate.ts
|
|
40266
|
-
function setActivePipeline(p) {
|
|
40267
|
-
activePipeline = p;
|
|
40268
|
-
}
|
|
40269
|
-
function getActivePipeline() {
|
|
40270
|
-
return activePipeline;
|
|
40271
|
-
}
|
|
40272
|
-
function revalidatePath(pathname, opts) {
|
|
40273
|
-
return activePipeline ? activePipeline.revalidatePath(pathname, opts) : 0;
|
|
40274
|
-
}
|
|
40275
|
-
function registerTwmTagInvalidator(fn) {
|
|
40276
|
-
twmTagInvalidator = fn;
|
|
40277
|
-
}
|
|
40278
|
-
function revalidateTag(tag) {
|
|
40279
|
-
let dropped = 0;
|
|
40280
|
-
if (activePipeline) dropped += activePipeline.revalidateTag(tag);
|
|
40281
|
-
if (twmTagInvalidator) dropped += twmTagInvalidator(tag);
|
|
40282
|
-
return dropped;
|
|
40283
|
-
}
|
|
40284
|
-
var activePipeline, twmTagInvalidator;
|
|
40285
|
-
var init_revalidate = __esm({
|
|
40286
|
-
"../../packages/server/tw/routing/revalidate.ts"() {
|
|
40287
|
-
activePipeline = null;
|
|
40288
|
-
twmTagInvalidator = null;
|
|
40289
|
-
}
|
|
40290
|
-
});
|
|
40291
|
-
|
|
40292
|
-
// ../../packages/server/tw/routing/signal-stream.ts
|
|
40293
|
-
function getSignalHub() {
|
|
40294
|
-
if (!hub) hub = new SignalHub();
|
|
40295
|
-
return hub;
|
|
40296
|
-
}
|
|
40297
|
-
function setSignal(name, value, opts) {
|
|
40298
|
-
let session = opts?.session;
|
|
40299
|
-
if (!session && opts?.request) {
|
|
40300
|
-
try {
|
|
40301
|
-
const cookies = opts.request.cookies;
|
|
40302
|
-
if (cookies && typeof cookies.get === "function") {
|
|
40303
|
-
session = cookies.get("tw_session") ?? void 0;
|
|
40304
|
-
} else {
|
|
40305
|
-
const header = opts.request.headers?.get?.("cookie") ?? "";
|
|
40306
|
-
const m = /(?:^|;\s*)tw_session=([^;]+)/.exec(header);
|
|
40307
|
-
if (m) session = decodeURIComponent(m[1].trim().replace(/^"|"$/g, ""));
|
|
40308
|
-
}
|
|
40309
|
-
} catch {
|
|
40310
|
-
}
|
|
40311
|
-
}
|
|
40312
|
-
return getSignalHub().setSignal(name, value, session ? { session } : void 0);
|
|
40313
|
-
}
|
|
40314
|
-
var BATCH_MS, HISTORY_LIMIT, SignalHub, hub;
|
|
40315
|
-
var init_signal_stream = __esm({
|
|
40316
|
-
"../../packages/server/tw/routing/signal-stream.ts"() {
|
|
40317
|
-
BATCH_MS = 100;
|
|
40318
|
-
HISTORY_LIMIT = 1e3;
|
|
40319
|
-
SignalHub = class {
|
|
40320
|
-
clients = /* @__PURE__ */ new Map();
|
|
40321
|
-
nextClientId = 1;
|
|
40322
|
-
seq = 0;
|
|
40323
|
-
values = /* @__PURE__ */ new Map();
|
|
40324
|
-
kinds = /* @__PURE__ */ new Map();
|
|
40325
|
-
history = [];
|
|
40326
|
-
pending = /* @__PURE__ */ new Map();
|
|
40327
|
-
pendingSession;
|
|
40328
|
-
timer = null;
|
|
40329
|
-
/**
|
|
40330
|
-
* Server-side mutation (route handlers): queue an update, batch-flush.
|
|
40331
|
-
* With `session` (derived from the request's tw_session cookie) private
|
|
40332
|
-
* signals are delivered only to clients authenticated as that session.
|
|
40333
|
-
*/
|
|
40334
|
-
setSignal(name, value, opts) {
|
|
40335
|
-
const kind = this.kinds.get(name) ?? "public";
|
|
40336
|
-
this.values.set(name, value);
|
|
40337
|
-
this.kinds.set(name, kind);
|
|
40338
|
-
this.pending.set(name, value);
|
|
40339
|
-
if (opts?.session) this.pendingSession = opts.session;
|
|
40340
|
-
if (this.timer == null) {
|
|
40341
|
-
this.timer = setTimeout(() => this.flush(), BATCH_MS);
|
|
40342
|
-
}
|
|
40343
|
-
return { queued: true, kind };
|
|
40344
|
-
}
|
|
40345
|
-
/** Latest known values (also used for fresh snapshots). */
|
|
40346
|
-
snapshot() {
|
|
40347
|
-
return { seq: this.seq, snapshot: Object.fromEntries(this.values) };
|
|
40348
|
-
}
|
|
40349
|
-
register(declared, session) {
|
|
40350
|
-
const id = this.nextClientId++;
|
|
40351
|
-
for (const [name, kind] of declared) {
|
|
40352
|
-
if (!this.kinds.has(name)) this.kinds.set(name, kind);
|
|
40353
|
-
}
|
|
40354
|
-
const client = {
|
|
40355
|
-
id,
|
|
40356
|
-
declared,
|
|
40357
|
-
session,
|
|
40358
|
-
send: () => {
|
|
40359
|
-
},
|
|
40360
|
-
close: () => this.clients.delete(id)
|
|
40361
|
-
};
|
|
40362
|
-
this.clients.set(id, client);
|
|
40363
|
-
return client;
|
|
40364
|
-
}
|
|
40365
|
-
/** Build the replay/snapshot preamble for a connecting client. */
|
|
40366
|
-
connectPayloads(since, declared, session) {
|
|
40367
|
-
const out = [];
|
|
40368
|
-
const hasCoverage = since <= this.seq && this.history.length > 0 && since >= this.history[0].seq - 1;
|
|
40369
|
-
if (hasCoverage) {
|
|
40370
|
-
for (const entry of this.history) {
|
|
40371
|
-
if (entry.seq > since && this.sessionMatches(session, entry) && this.deliversTo(declared, entry.names)) {
|
|
40372
|
-
out.push(entry.payload);
|
|
40373
|
-
}
|
|
40374
|
-
}
|
|
40375
|
-
} else if (this.values.size > 0) {
|
|
40376
|
-
const snap = {};
|
|
40377
|
-
for (const [name, value] of this.values) {
|
|
40378
|
-
if (this.sessionMatches(session, { names: [name], session: this.valueSessions.get(name) }) && this.deliversTo(declared, [name])) snap[name] = value;
|
|
40379
|
-
}
|
|
40380
|
-
if (Object.keys(snap).length > 0) {
|
|
40381
|
-
out.push(this.frame({ v: 1, seq: this.seq, snapshot: snap }));
|
|
40382
|
-
}
|
|
40383
|
-
}
|
|
40384
|
-
return out;
|
|
40385
|
-
}
|
|
40386
|
-
/** latest-known session per signal (for snapshot filtering) */
|
|
40387
|
-
valueSessions = /* @__PURE__ */ new Map();
|
|
40388
|
-
sessionMatches(clientSession, entry) {
|
|
40389
|
-
if (!entry.session) return true;
|
|
40390
|
-
return entry.session === clientSession;
|
|
40391
|
-
}
|
|
40392
|
-
deliversTo(declared, names) {
|
|
40393
|
-
return names.some((n) => {
|
|
40394
|
-
const kind = this.kinds.get(n) ?? "public";
|
|
40395
|
-
return kind === "public" || declared.has(n);
|
|
40396
|
-
});
|
|
40397
|
-
}
|
|
40398
|
-
flush() {
|
|
40399
|
-
this.timer = null;
|
|
40400
|
-
if (this.pending.size === 0) return;
|
|
40401
|
-
const updates = [...this.pending.entries()];
|
|
40402
|
-
const session = this.pendingSession;
|
|
40403
|
-
this.pending.clear();
|
|
40404
|
-
this.pendingSession = void 0;
|
|
40405
|
-
this.seq++;
|
|
40406
|
-
const names = updates.map(([n]) => n);
|
|
40407
|
-
for (const [n] of updates) this.valueSessions.set(n, session);
|
|
40408
|
-
const payload = this.frame({ v: 1, seq: this.seq, updates });
|
|
40409
|
-
this.history.push({ seq: this.seq, payload, names, session });
|
|
40410
|
-
if (this.history.length > HISTORY_LIMIT) this.history.shift();
|
|
40411
|
-
for (const client of this.clients.values()) {
|
|
40412
|
-
if (this.sessionMatches(client.session, { names, session }) && this.deliversTo(client.declared, names)) client.send(payload);
|
|
40413
|
-
}
|
|
40414
|
-
}
|
|
40415
|
-
frame(obj) {
|
|
40416
|
-
return JSON.stringify(obj).replace(/</g, "\\u003c");
|
|
40417
|
-
}
|
|
40418
|
-
};
|
|
40419
|
-
hub = null;
|
|
40420
|
-
}
|
|
40421
|
-
});
|
|
40422
|
-
|
|
40423
40301
|
// ../../packages/server/tw/routing/twm-rules.ts
|
|
40424
40302
|
var twm_rules_exports = {};
|
|
40425
40303
|
__export(twm_rules_exports, {
|
|
@@ -40538,16 +40416,30 @@ function verifyJwt_hs256(token, secret) {
|
|
|
40538
40416
|
return true;
|
|
40539
40417
|
}
|
|
40540
40418
|
function matchPath(pattern, pathname) {
|
|
40541
|
-
|
|
40542
|
-
|
|
40543
|
-
|
|
40544
|
-
|
|
40419
|
+
const norm = (p) => {
|
|
40420
|
+
let x = p;
|
|
40421
|
+
try {
|
|
40422
|
+
x = decodeURIComponent(x);
|
|
40423
|
+
} catch {
|
|
40424
|
+
}
|
|
40425
|
+
if (x.length > 1 && x.endsWith("/")) x = x.slice(0, -1);
|
|
40426
|
+
return x;
|
|
40427
|
+
};
|
|
40428
|
+
const pat = norm(pattern);
|
|
40429
|
+
const path = norm(pathname);
|
|
40430
|
+
if (pat === "/**") return true;
|
|
40431
|
+
if (pat === "/*") return path === "" || path === "/";
|
|
40432
|
+
if (pat.endsWith("/**")) {
|
|
40433
|
+
const prefix = pat.slice(0, -3);
|
|
40434
|
+
return path === prefix || path.startsWith(prefix + "/");
|
|
40545
40435
|
}
|
|
40546
|
-
if (
|
|
40547
|
-
const prefix =
|
|
40548
|
-
|
|
40436
|
+
if (pat.endsWith("/*")) {
|
|
40437
|
+
const prefix = pat.slice(0, -2);
|
|
40438
|
+
if (!path.startsWith(prefix + "/")) return path === prefix;
|
|
40439
|
+
const rest = path.slice(prefix.length + 1);
|
|
40440
|
+
return !rest.includes("/");
|
|
40549
40441
|
}
|
|
40550
|
-
return
|
|
40442
|
+
return path === pat;
|
|
40551
40443
|
}
|
|
40552
40444
|
function ruleBlocked(rule, request, pathname) {
|
|
40553
40445
|
const headers = request.headers;
|
|
@@ -40870,6 +40762,385 @@ var init_twm_rules = __esm({
|
|
|
40870
40762
|
}
|
|
40871
40763
|
});
|
|
40872
40764
|
|
|
40765
|
+
// ../../packages/shared/tw/node-import.ts
|
|
40766
|
+
var node_import_exports = {};
|
|
40767
|
+
__export(node_import_exports, {
|
|
40768
|
+
twImportTs: () => twImportTs
|
|
40769
|
+
});
|
|
40770
|
+
import { join as join10 } from "node:path";
|
|
40771
|
+
import { tmpdir } from "node:os";
|
|
40772
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
40773
|
+
async function twImportTs(tsPath) {
|
|
40774
|
+
if (!globalThis.__TW_NODE) {
|
|
40775
|
+
return import(tsPath);
|
|
40776
|
+
}
|
|
40777
|
+
const esbuildMain = globalThis.__TW_ESBUILD_MODULE;
|
|
40778
|
+
const esbuild = await (esbuildMain ? import(esbuildMain) : import("esbuild"));
|
|
40779
|
+
const safe = tsPath.replace(/[^a-zA-Z0-9]/g, "_").slice(-120);
|
|
40780
|
+
const cachePath = join10(tmpdir(), "tw-" + safe + ".mjs");
|
|
40781
|
+
await esbuild.build({
|
|
40782
|
+
entryPoints: [tsPath],
|
|
40783
|
+
bundle: true,
|
|
40784
|
+
format: "esm",
|
|
40785
|
+
platform: "node",
|
|
40786
|
+
target: "node18",
|
|
40787
|
+
outfile: cachePath,
|
|
40788
|
+
logLevel: "silent"
|
|
40789
|
+
});
|
|
40790
|
+
return import(pathToFileURL2(cachePath).href);
|
|
40791
|
+
}
|
|
40792
|
+
var init_node_import = __esm({
|
|
40793
|
+
"../../packages/shared/tw/node-import.ts"() {
|
|
40794
|
+
}
|
|
40795
|
+
});
|
|
40796
|
+
|
|
40797
|
+
// tw/commands/client-bundle.ts
|
|
40798
|
+
var client_bundle_exports = {};
|
|
40799
|
+
__export(client_bundle_exports, {
|
|
40800
|
+
BUILTIN_CLIENT_OK_SPECIFIERS: () => BUILTIN_CLIENT_OK_SPECIFIERS,
|
|
40801
|
+
RUNTIME_CLIENT_OK_SPECIFIERS: () => RUNTIME_CLIENT_OK_SPECIFIERS,
|
|
40802
|
+
buildClientChunk: () => buildClientChunk,
|
|
40803
|
+
buildPageScope: () => buildPageScope,
|
|
40804
|
+
collectClientImportsFromSource: () => collectClientImportsFromSource,
|
|
40805
|
+
findServerOnlyViolations: () => findServerOnlyViolations,
|
|
40806
|
+
importNames: () => importNames,
|
|
40807
|
+
isClientImportSpecifier: () => isClientImportSpecifier
|
|
40808
|
+
});
|
|
40809
|
+
import { join as join11, resolve as resolve3 } from "node:path";
|
|
40810
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
40811
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
40812
|
+
function isClientImportSpecifier(spec) {
|
|
40813
|
+
if (spec.startsWith("@./") || spec.startsWith("./") || spec.startsWith("../")) return false;
|
|
40814
|
+
if (/\.(tw|tss|twm|ts|js)$/.test(spec)) return false;
|
|
40815
|
+
if (spec.startsWith("@tw/")) {
|
|
40816
|
+
return RUNTIME_CLIENT_OK_SPECIFIERS.includes(spec);
|
|
40817
|
+
}
|
|
40818
|
+
return true;
|
|
40819
|
+
}
|
|
40820
|
+
function collectClientImportsFromSource(src) {
|
|
40821
|
+
const lines = [];
|
|
40822
|
+
let m;
|
|
40823
|
+
IMPORT_RE.lastIndex = 0;
|
|
40824
|
+
while (m = IMPORT_RE.exec(src)) {
|
|
40825
|
+
if (isClientImportSpecifier(m[2])) lines.push(m[0].trim().replace(/;$/, ""));
|
|
40826
|
+
}
|
|
40827
|
+
return lines;
|
|
40828
|
+
}
|
|
40829
|
+
function importNames(line) {
|
|
40830
|
+
const clause = line.match(/^import\s+([\s\S]+?)\s+from/);
|
|
40831
|
+
if (!clause) return [];
|
|
40832
|
+
const c = clause[1].trim();
|
|
40833
|
+
const names = [];
|
|
40834
|
+
if (c.startsWith("*")) {
|
|
40835
|
+
const as = c.split(/\s+as\s+/);
|
|
40836
|
+
if (as[1]) names.push(as[1].trim());
|
|
40837
|
+
} else if (c.startsWith("{")) {
|
|
40838
|
+
const braces = c.match(/\{([\s\S]*)\}/);
|
|
40839
|
+
if (braces) {
|
|
40840
|
+
for (const part of braces[1].split(",")) {
|
|
40841
|
+
const p = part.trim();
|
|
40842
|
+
if (!p) continue;
|
|
40843
|
+
const seg = p.split(/\s+as\s+/);
|
|
40844
|
+
names.push(seg[seg.length - 1].trim());
|
|
40845
|
+
}
|
|
40846
|
+
}
|
|
40847
|
+
} else {
|
|
40848
|
+
const def = c.match(/^[A-Za-z_$][\w$]*/);
|
|
40849
|
+
if (def) names.push(def[0]);
|
|
40850
|
+
}
|
|
40851
|
+
return names;
|
|
40852
|
+
}
|
|
40853
|
+
function findEsbuildBin(rootDir) {
|
|
40854
|
+
const candidates = [
|
|
40855
|
+
join11(rootDir, "node_modules", ".bin", "esbuild"),
|
|
40856
|
+
resolve3(rootDir, "..", "node_modules", ".bin", "esbuild"),
|
|
40857
|
+
// CLI repo layout (dev / source runs)
|
|
40858
|
+
resolve3(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "node_modules", ".bin", "esbuild")
|
|
40859
|
+
];
|
|
40860
|
+
for (const c of candidates) {
|
|
40861
|
+
try {
|
|
40862
|
+
if (existsSync7(c)) return c;
|
|
40863
|
+
} catch {
|
|
40864
|
+
}
|
|
40865
|
+
}
|
|
40866
|
+
const bd = globalThis.__TW_BUNDLE_DIR;
|
|
40867
|
+
if (bd) {
|
|
40868
|
+
const b = resolve3(bd, "..", "..", "..", "node_modules", ".bin", "esbuild");
|
|
40869
|
+
try {
|
|
40870
|
+
if (existsSync7(b)) return b;
|
|
40871
|
+
} catch {
|
|
40872
|
+
}
|
|
40873
|
+
}
|
|
40874
|
+
return "esbuild";
|
|
40875
|
+
}
|
|
40876
|
+
function findServerOnlyViolations(src) {
|
|
40877
|
+
const out = [];
|
|
40878
|
+
let m;
|
|
40879
|
+
IMPORT_RE.lastIndex = 0;
|
|
40880
|
+
while (m = IMPORT_RE.exec(src)) {
|
|
40881
|
+
const spec = m[2];
|
|
40882
|
+
if (spec === "lib" || spec.startsWith("lib/")) {
|
|
40883
|
+
out.push(spec);
|
|
40884
|
+
continue;
|
|
40885
|
+
}
|
|
40886
|
+
if (isClientImportSpecifier(spec)) continue;
|
|
40887
|
+
if (spec.startsWith("@tw/")) {
|
|
40888
|
+
if (BUILTIN_CLIENT_OK_SPECIFIERS.includes(spec)) continue;
|
|
40889
|
+
if (RUNTIME_CLIENT_OK_SPECIFIERS.includes(spec)) continue;
|
|
40890
|
+
out.push(spec);
|
|
40891
|
+
continue;
|
|
40892
|
+
}
|
|
40893
|
+
if (spec.startsWith("@./") || spec.startsWith("./") || spec.startsWith("../")) {
|
|
40894
|
+
if (/\.(tw|tss)$/.test(spec)) continue;
|
|
40895
|
+
out.push(spec);
|
|
40896
|
+
continue;
|
|
40897
|
+
}
|
|
40898
|
+
if (spec.startsWith("lib/")) out.push(spec);
|
|
40899
|
+
}
|
|
40900
|
+
return out;
|
|
40901
|
+
}
|
|
40902
|
+
function contentHash2(s) {
|
|
40903
|
+
let h = 5381;
|
|
40904
|
+
for (let i = 0; i < s.length; i++) h = (h * 33 ^ s.charCodeAt(i)) >>> 0;
|
|
40905
|
+
return h.toString(36);
|
|
40906
|
+
}
|
|
40907
|
+
function vendoredRuntimeClient() {
|
|
40908
|
+
const fromSrc = new URL("../../dist/tw-runtime-client.mjs", import.meta.url).pathname;
|
|
40909
|
+
if (existsSync7(fromSrc)) return fromSrc;
|
|
40910
|
+
const fromBundle = new URL("./tw-runtime-client.mjs", import.meta.url).pathname;
|
|
40911
|
+
if (existsSync7(fromBundle)) return fromBundle;
|
|
40912
|
+
return null;
|
|
40913
|
+
}
|
|
40914
|
+
function rewriteRuntimeSpecifier(importLine) {
|
|
40915
|
+
const vendored = vendoredRuntimeClient();
|
|
40916
|
+
if (!vendored) return importLine;
|
|
40917
|
+
const m = importLine.match(/from\s+["'](@tw\/runtime)["']/);
|
|
40918
|
+
if (!m) return importLine;
|
|
40919
|
+
return importLine.replace(m[0], `from ${JSON.stringify(vendored)}`);
|
|
40920
|
+
}
|
|
40921
|
+
function buildClientChunk(rootDir, importLine, minify) {
|
|
40922
|
+
if (!importLine) return "";
|
|
40923
|
+
const cached = chunkCache.get(importLine);
|
|
40924
|
+
if (cached && existsSync7(join11(rootDir, ".tw", cached))) return cached;
|
|
40925
|
+
const jsDir = join11(rootDir, ".tw", "js");
|
|
40926
|
+
mkdirSync4(jsDir, { recursive: true });
|
|
40927
|
+
const names = importNames(importLine);
|
|
40928
|
+
const entryLine = rewriteRuntimeSpecifier(importLine);
|
|
40929
|
+
const entry = [
|
|
40930
|
+
entryLine + ";",
|
|
40931
|
+
"globalThis.__twClient = globalThis.__twClient || {};",
|
|
40932
|
+
...names.map((n) => "globalThis.__twClient." + n + " = " + n + ";")
|
|
40933
|
+
].join("\n");
|
|
40934
|
+
const stamp = contentHash2(importLine);
|
|
40935
|
+
const entryPath = join11(rootDir, ".tw", ".chunk-entry-" + stamp + ".js");
|
|
40936
|
+
const tmpOut = join11(rootDir, ".tw", ".chunk-tmp-" + stamp + ".js");
|
|
40937
|
+
writeFileSync4(entryPath, entry);
|
|
40938
|
+
const bin = findEsbuildBin(rootDir);
|
|
40939
|
+
const args = [entryPath, "--bundle", "--format=iife", "--target=es2019", "--outfile=" + tmpOut];
|
|
40940
|
+
if (minify) args.push("--minify");
|
|
40941
|
+
const r = spawnSync2(bin, args, { cwd: rootDir });
|
|
40942
|
+
try {
|
|
40943
|
+
unlinkSync2(entryPath);
|
|
40944
|
+
} catch {
|
|
40945
|
+
}
|
|
40946
|
+
if (r.status !== 0) {
|
|
40947
|
+
const errText = (r.stderr && r.stderr.toString ? r.stderr.toString() : "") || String(r.error || "unknown");
|
|
40948
|
+
console.error(" [tw] client chunk failed (" + importLine + "): " + errText.split("\n")[0]);
|
|
40949
|
+
try {
|
|
40950
|
+
unlinkSync2(tmpOut);
|
|
40951
|
+
} catch {
|
|
40952
|
+
}
|
|
40953
|
+
return "";
|
|
40954
|
+
}
|
|
40955
|
+
const code = readFileSync6(tmpOut, "utf-8");
|
|
40956
|
+
const url = "js/c-" + contentHash2(code) + ".js";
|
|
40957
|
+
writeFileSync4(join11(rootDir, ".tw", url), code);
|
|
40958
|
+
try {
|
|
40959
|
+
unlinkSync2(tmpOut);
|
|
40960
|
+
} catch {
|
|
40961
|
+
}
|
|
40962
|
+
chunkCache.set(importLine, url);
|
|
40963
|
+
return url;
|
|
40964
|
+
}
|
|
40965
|
+
function buildPageScope(rootDir, ownImportLines) {
|
|
40966
|
+
if (!ownImportLines || ownImportLines.length === 0) return "";
|
|
40967
|
+
const names = ownImportLines.flatMap((l) => importNames(l));
|
|
40968
|
+
if (names.length === 0) return "";
|
|
40969
|
+
mkdirSync4(join11(rootDir, ".tw", "js"), { recursive: true });
|
|
40970
|
+
const body = names.map((n) => "p." + n + " = g.__twClient." + n + ";").join("\n");
|
|
40971
|
+
const code = "(function(g){g.__twClient=g.__twClient||{};var p={};\n" + body + "\ng.__twClient.$page=p;})(globalThis);\n";
|
|
40972
|
+
const url = "js/p-" + contentHash2(names.slice().sort().join("|")) + ".js";
|
|
40973
|
+
writeFileSync4(join11(rootDir, ".tw", url), code);
|
|
40974
|
+
return url;
|
|
40975
|
+
}
|
|
40976
|
+
var IMPORT_RE, BUILTIN_CLIENT_OK_SPECIFIERS, RUNTIME_CLIENT_OK_SPECIFIERS, chunkCache;
|
|
40977
|
+
var init_client_bundle = __esm({
|
|
40978
|
+
"tw/commands/client-bundle.ts"() {
|
|
40979
|
+
IMPORT_RE = /^[ \t]*import\s+([^\n]+?)\s+from\s+["'']([^"'']+)["''][; \t]*$/gm;
|
|
40980
|
+
BUILTIN_CLIENT_OK_SPECIFIERS = ["@tw/optImage", "@tw/RouterLink"];
|
|
40981
|
+
RUNTIME_CLIENT_OK_SPECIFIERS = ["@tw/runtime"];
|
|
40982
|
+
chunkCache = /* @__PURE__ */ new Map();
|
|
40983
|
+
}
|
|
40984
|
+
});
|
|
40985
|
+
|
|
40986
|
+
// ../../packages/server/tw/routing/revalidate.ts
|
|
40987
|
+
function setActivePipeline(p) {
|
|
40988
|
+
activePipeline = p;
|
|
40989
|
+
}
|
|
40990
|
+
function getActivePipeline() {
|
|
40991
|
+
return activePipeline;
|
|
40992
|
+
}
|
|
40993
|
+
function revalidatePath(pathname, opts) {
|
|
40994
|
+
return activePipeline ? activePipeline.revalidatePath(pathname, opts) : 0;
|
|
40995
|
+
}
|
|
40996
|
+
function registerTwmTagInvalidator(fn) {
|
|
40997
|
+
twmTagInvalidator = fn;
|
|
40998
|
+
}
|
|
40999
|
+
function revalidateTag(tag) {
|
|
41000
|
+
let dropped = 0;
|
|
41001
|
+
if (activePipeline) dropped += activePipeline.revalidateTag(tag);
|
|
41002
|
+
if (twmTagInvalidator) dropped += twmTagInvalidator(tag);
|
|
41003
|
+
return dropped;
|
|
41004
|
+
}
|
|
41005
|
+
var activePipeline, twmTagInvalidator;
|
|
41006
|
+
var init_revalidate = __esm({
|
|
41007
|
+
"../../packages/server/tw/routing/revalidate.ts"() {
|
|
41008
|
+
activePipeline = null;
|
|
41009
|
+
twmTagInvalidator = null;
|
|
41010
|
+
}
|
|
41011
|
+
});
|
|
41012
|
+
|
|
41013
|
+
// ../../packages/server/tw/routing/signal-stream.ts
|
|
41014
|
+
function getSignalHub() {
|
|
41015
|
+
if (!hub) hub = new SignalHub();
|
|
41016
|
+
return hub;
|
|
41017
|
+
}
|
|
41018
|
+
function setSignal(name, value, opts) {
|
|
41019
|
+
let session = opts?.session;
|
|
41020
|
+
if (!session && opts?.request) {
|
|
41021
|
+
try {
|
|
41022
|
+
const cookies = opts.request.cookies;
|
|
41023
|
+
if (cookies && typeof cookies.get === "function") {
|
|
41024
|
+
session = cookies.get("tw_session") ?? void 0;
|
|
41025
|
+
} else {
|
|
41026
|
+
const header = opts.request.headers?.get?.("cookie") ?? "";
|
|
41027
|
+
const m = /(?:^|;\s*)tw_session=([^;]+)/.exec(header);
|
|
41028
|
+
if (m) session = decodeURIComponent(m[1].trim().replace(/^"|"$/g, ""));
|
|
41029
|
+
}
|
|
41030
|
+
} catch {
|
|
41031
|
+
}
|
|
41032
|
+
}
|
|
41033
|
+
return getSignalHub().setSignal(name, value, session ? { session } : void 0);
|
|
41034
|
+
}
|
|
41035
|
+
var BATCH_MS, HISTORY_LIMIT, SignalHub, hub;
|
|
41036
|
+
var init_signal_stream = __esm({
|
|
41037
|
+
"../../packages/server/tw/routing/signal-stream.ts"() {
|
|
41038
|
+
BATCH_MS = 100;
|
|
41039
|
+
HISTORY_LIMIT = 1e3;
|
|
41040
|
+
SignalHub = class {
|
|
41041
|
+
clients = /* @__PURE__ */ new Map();
|
|
41042
|
+
nextClientId = 1;
|
|
41043
|
+
seq = 0;
|
|
41044
|
+
values = /* @__PURE__ */ new Map();
|
|
41045
|
+
kinds = /* @__PURE__ */ new Map();
|
|
41046
|
+
history = [];
|
|
41047
|
+
pending = /* @__PURE__ */ new Map();
|
|
41048
|
+
pendingSession;
|
|
41049
|
+
timer = null;
|
|
41050
|
+
/**
|
|
41051
|
+
* Server-side mutation (route handlers): queue an update, batch-flush.
|
|
41052
|
+
* With `session` (derived from the request's tw_session cookie) private
|
|
41053
|
+
* signals are delivered only to clients authenticated as that session.
|
|
41054
|
+
*/
|
|
41055
|
+
setSignal(name, value, opts) {
|
|
41056
|
+
const kind = this.kinds.get(name) ?? "public";
|
|
41057
|
+
this.values.set(name, value);
|
|
41058
|
+
this.kinds.set(name, kind);
|
|
41059
|
+
this.pending.set(name, value);
|
|
41060
|
+
if (opts?.session) this.pendingSession = opts.session;
|
|
41061
|
+
if (this.timer == null) {
|
|
41062
|
+
this.timer = setTimeout(() => this.flush(), BATCH_MS);
|
|
41063
|
+
}
|
|
41064
|
+
return { queued: true, kind };
|
|
41065
|
+
}
|
|
41066
|
+
/** Latest known values (also used for fresh snapshots). */
|
|
41067
|
+
snapshot() {
|
|
41068
|
+
return { seq: this.seq, snapshot: Object.fromEntries(this.values) };
|
|
41069
|
+
}
|
|
41070
|
+
register(declared, session) {
|
|
41071
|
+
const id = this.nextClientId++;
|
|
41072
|
+
for (const [name, kind] of declared) {
|
|
41073
|
+
if (!this.kinds.has(name)) this.kinds.set(name, kind);
|
|
41074
|
+
}
|
|
41075
|
+
const client = {
|
|
41076
|
+
id,
|
|
41077
|
+
declared,
|
|
41078
|
+
session,
|
|
41079
|
+
send: () => {
|
|
41080
|
+
},
|
|
41081
|
+
close: () => this.clients.delete(id)
|
|
41082
|
+
};
|
|
41083
|
+
this.clients.set(id, client);
|
|
41084
|
+
return client;
|
|
41085
|
+
}
|
|
41086
|
+
/** Build the replay/snapshot preamble for a connecting client. */
|
|
41087
|
+
connectPayloads(since, declared, session) {
|
|
41088
|
+
const out = [];
|
|
41089
|
+
const hasCoverage = since <= this.seq && this.history.length > 0 && since >= this.history[0].seq - 1;
|
|
41090
|
+
if (hasCoverage) {
|
|
41091
|
+
for (const entry of this.history) {
|
|
41092
|
+
if (entry.seq > since && this.sessionMatches(session, entry) && this.deliversTo(declared, entry.names)) {
|
|
41093
|
+
out.push(entry.payload);
|
|
41094
|
+
}
|
|
41095
|
+
}
|
|
41096
|
+
} else if (this.values.size > 0) {
|
|
41097
|
+
const snap = {};
|
|
41098
|
+
for (const [name, value] of this.values) {
|
|
41099
|
+
if (this.sessionMatches(session, { names: [name], session: this.valueSessions.get(name) }) && this.deliversTo(declared, [name])) snap[name] = value;
|
|
41100
|
+
}
|
|
41101
|
+
if (Object.keys(snap).length > 0) {
|
|
41102
|
+
out.push(this.frame({ v: 1, seq: this.seq, snapshot: snap }));
|
|
41103
|
+
}
|
|
41104
|
+
}
|
|
41105
|
+
return out;
|
|
41106
|
+
}
|
|
41107
|
+
/** latest-known session per signal (for snapshot filtering) */
|
|
41108
|
+
valueSessions = /* @__PURE__ */ new Map();
|
|
41109
|
+
sessionMatches(clientSession, entry) {
|
|
41110
|
+
if (!entry.session) return true;
|
|
41111
|
+
return entry.session === clientSession;
|
|
41112
|
+
}
|
|
41113
|
+
deliversTo(declared, names) {
|
|
41114
|
+
return names.some((n) => {
|
|
41115
|
+
const kind = this.kinds.get(n) ?? "public";
|
|
41116
|
+
return kind === "public" || declared.has(n);
|
|
41117
|
+
});
|
|
41118
|
+
}
|
|
41119
|
+
flush() {
|
|
41120
|
+
this.timer = null;
|
|
41121
|
+
if (this.pending.size === 0) return;
|
|
41122
|
+
const updates = [...this.pending.entries()];
|
|
41123
|
+
const session = this.pendingSession;
|
|
41124
|
+
this.pending.clear();
|
|
41125
|
+
this.pendingSession = void 0;
|
|
41126
|
+
this.seq++;
|
|
41127
|
+
const names = updates.map(([n]) => n);
|
|
41128
|
+
for (const [n] of updates) this.valueSessions.set(n, session);
|
|
41129
|
+
const payload = this.frame({ v: 1, seq: this.seq, updates });
|
|
41130
|
+
this.history.push({ seq: this.seq, payload, names, session });
|
|
41131
|
+
if (this.history.length > HISTORY_LIMIT) this.history.shift();
|
|
41132
|
+
for (const client of this.clients.values()) {
|
|
41133
|
+
if (this.sessionMatches(client.session, { names, session }) && this.deliversTo(client.declared, names)) client.send(payload);
|
|
41134
|
+
}
|
|
41135
|
+
}
|
|
41136
|
+
frame(obj) {
|
|
41137
|
+
return JSON.stringify(obj).replace(/</g, "\\u003c");
|
|
41138
|
+
}
|
|
41139
|
+
};
|
|
41140
|
+
hub = null;
|
|
41141
|
+
}
|
|
41142
|
+
});
|
|
41143
|
+
|
|
40873
41144
|
// ../../packages/server/tw/websocket-manager.ts
|
|
40874
41145
|
var DEFAULT_MAX_ROOMS, DEFAULT_MAX_ROOM_SIZE, WebSocketManager;
|
|
40875
41146
|
var init_websocket_manager = __esm({
|
|
@@ -41096,7 +41367,8 @@ function extractRenderMode(page) {
|
|
|
41096
41367
|
try {
|
|
41097
41368
|
const fs = __require("node:fs");
|
|
41098
41369
|
const source = fs.readFileSync(page.absolutePath, "utf-8");
|
|
41099
|
-
const
|
|
41370
|
+
const { maskSourceStringsAndComments: maskSourceStringsAndComments2 } = (init_tw(), __toCommonJS(tw_exports));
|
|
41371
|
+
const m = maskSourceStringsAndComments2(source).match(/render\s+(static|ssr|island|edge)/);
|
|
41100
41372
|
if (m) {
|
|
41101
41373
|
return m[1];
|
|
41102
41374
|
}
|
|
@@ -41744,7 +42016,7 @@ var init_render_pipeline = __esm({
|
|
|
41744
42016
|
for (const headMatch of bodyHTML.matchAll(/<head[^>]*>([\s\S]*?)<\/head>/gi)) {
|
|
41745
42017
|
const headInner = headMatch[1];
|
|
41746
42018
|
const tMatch = headInner.match(/<title>([^<]*)<\/title>/i);
|
|
41747
|
-
if (tMatch) layoutTitle = tMatch[1].trim();
|
|
42019
|
+
if (tMatch) layoutTitle = tMatch[1].trim().replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
41748
42020
|
const kids = headInner.replace(/<title>[^<]*<\/title>/gi, "").replace(/<meta[^>]+charset[^>]*>/gi, "").replace(/<meta[^>]+viewport[^>]*>/gi, "").trim();
|
|
41749
42021
|
if (kids) liftedParts.push(kids);
|
|
41750
42022
|
}
|
|
@@ -41784,7 +42056,7 @@ ${bodyHTML}`;
|
|
|
41784
42056
|
for (const hp of headCandidates) {
|
|
41785
42057
|
const headCompiled = this.compileFile({ absolutePath: hp, type: "head" });
|
|
41786
42058
|
const m = headCompiled.html.match(/<body>([\s\S]*)<\/body>/);
|
|
41787
|
-
if (m && m[1].trim()) headExtra += m[1].trim() + "\n";
|
|
42059
|
+
if (m && m[1].trim()) headExtra += m[1].trim().replace(/\\([{}])/g, "$1") + "\n";
|
|
41788
42060
|
}
|
|
41789
42061
|
if (liftedHead) headExtra += liftedHead + "\n";
|
|
41790
42062
|
} catch {
|
|
@@ -41982,8 +42254,13 @@ ${bodyHTML}`;
|
|
|
41982
42254
|
extractTitle(file) {
|
|
41983
42255
|
try {
|
|
41984
42256
|
const source = readFileSync7(file.absolutePath, "utf-8");
|
|
41985
|
-
const
|
|
41986
|
-
|
|
42257
|
+
const masked = maskSourceStringsAndComments(source);
|
|
42258
|
+
const m = masked.match(/page\s*\{[^}]*title\s+"/);
|
|
42259
|
+
if (!m || m.index === void 0) return null;
|
|
42260
|
+
const start = m.index + m[0].length;
|
|
42261
|
+
const end = source.indexOf('"', start);
|
|
42262
|
+
if (end === -1 || end === start) return null;
|
|
42263
|
+
return source.slice(start, end).replace(/\\([{}"])/g, "$1");
|
|
41987
42264
|
} catch {
|
|
41988
42265
|
return null;
|
|
41989
42266
|
}
|
|
@@ -41997,7 +42274,9 @@ ${bodyHTML}`;
|
|
|
41997
42274
|
extractRenderMode(file) {
|
|
41998
42275
|
try {
|
|
41999
42276
|
const src = readFileSync7(file.absolutePath, "utf8");
|
|
42000
|
-
const m = /render\s+(static|ssr|island|edge|csr|stream|ppr)\b/.exec(
|
|
42277
|
+
const m = /render\s+(static|ssr|island|edge|csr|stream|ppr)\b/.exec(
|
|
42278
|
+
maskSourceStringsAndComments(src)
|
|
42279
|
+
);
|
|
42001
42280
|
return m ? m[1] : "static";
|
|
42002
42281
|
} catch {
|
|
42003
42282
|
return "static";
|
|
@@ -42045,7 +42324,7 @@ ${bodyHTML}`;
|
|
|
42045
42324
|
extractRevalidate(file) {
|
|
42046
42325
|
try {
|
|
42047
42326
|
const source = readFileSync7(file.absolutePath, "utf-8");
|
|
42048
|
-
const m = source.match(/page\s*\{[^}]*revalidate\s+(\d+)/);
|
|
42327
|
+
const m = maskSourceStringsAndComments(source).match(/page\s*\{[^}]*revalidate\s+(\d+)/);
|
|
42049
42328
|
if (m) {
|
|
42050
42329
|
const n = Number(m[1]);
|
|
42051
42330
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
@@ -42091,14 +42370,32 @@ ${bodyHTML}`;
|
|
|
42091
42370
|
buildMetaTags(file) {
|
|
42092
42371
|
try {
|
|
42093
42372
|
const source = readFileSync7(file.absolutePath, "utf-8");
|
|
42094
|
-
const
|
|
42095
|
-
|
|
42096
|
-
|
|
42373
|
+
const masked = maskSourceStringsAndComments(source);
|
|
42374
|
+
const fm = masked.match(/page\s*\{/);
|
|
42375
|
+
if (!fm || fm.index === void 0) return "";
|
|
42376
|
+
const bodyStart = fm.index + fm[0].length;
|
|
42377
|
+
const bodyEnd = masked.indexOf("}", bodyStart);
|
|
42378
|
+
if (bodyEnd === -1) return "";
|
|
42379
|
+
const body = source.slice(bodyStart, bodyEnd);
|
|
42097
42380
|
const AMP = String.fromCharCode(38);
|
|
42098
42381
|
const esc = (v) => v.split(AMP).join(AMP + "amp;").split(String.fromCharCode(34)).join(AMP + "quot;").split("<").join(AMP + "lt;");
|
|
42099
42382
|
const pick = (key) => {
|
|
42100
|
-
const
|
|
42101
|
-
|
|
42383
|
+
const i = body.indexOf(key + ' "');
|
|
42384
|
+
if (i === -1) return null;
|
|
42385
|
+
const rest = body.slice(i + key.length + 2);
|
|
42386
|
+
let end = -1;
|
|
42387
|
+
for (let j = 0; j < rest.length; j++) {
|
|
42388
|
+
if (rest[j] === "\\") {
|
|
42389
|
+
j++;
|
|
42390
|
+
continue;
|
|
42391
|
+
}
|
|
42392
|
+
if (rest[j] === '"') {
|
|
42393
|
+
end = j;
|
|
42394
|
+
break;
|
|
42395
|
+
}
|
|
42396
|
+
}
|
|
42397
|
+
if (end === -1) return null;
|
|
42398
|
+
return rest.slice(0, end).replace(/\\([{}"])/g, "$1");
|
|
42102
42399
|
};
|
|
42103
42400
|
let tags = "";
|
|
42104
42401
|
const description = pick("description");
|
|
@@ -53507,6 +53804,13 @@ function generateSecurityHeaders(options = {}) {
|
|
|
53507
53804
|
const headers = {};
|
|
53508
53805
|
if (opts.csp) {
|
|
53509
53806
|
headers["Content-Security-Policy"] = buildCSPHeader(opts.csp);
|
|
53807
|
+
try {
|
|
53808
|
+
const scriptSrc = Array.isArray(opts.csp["script-src"]) ? opts.csp["script-src"].join(" ") : String(opts.csp["script-src"] ?? "");
|
|
53809
|
+
if (scriptSrc && !scriptSrc.includes("'unsafe-eval'") && !scriptSrc.includes("'strict-dynamic'")) {
|
|
53810
|
+
console.warn("[tw] security: configured CSP has no 'unsafe-eval' in script-src -- the island hydration runtime needs it (add script-src: ['self', ''unsafe-eval''] or migrate handlers).");
|
|
53811
|
+
}
|
|
53812
|
+
} catch {
|
|
53813
|
+
}
|
|
53510
53814
|
}
|
|
53511
53815
|
if (opts.hsts) {
|
|
53512
53816
|
const hstsOpts = typeof opts.hsts === "object" ? opts.hsts : { maxAge: 31536e3, includeSubDomains: true };
|
|
@@ -54931,7 +55235,11 @@ async function executeRouteHandler(filePath, method, request, rootDir) {
|
|
|
54931
55235
|
}
|
|
54932
55236
|
} else if (typeof request?.json === "function") {
|
|
54933
55237
|
try {
|
|
54934
|
-
|
|
55238
|
+
const rawBody2 = await request.text();
|
|
55239
|
+
if (rawBody2.length > MAX_BODY_BYTES) {
|
|
55240
|
+
return { status: 413, json: { ok: false, error: "Payload too large" } };
|
|
55241
|
+
}
|
|
55242
|
+
parsedBody = rawBody2 ? JSON.parse(rawBody2) : {};
|
|
54935
55243
|
} catch {
|
|
54936
55244
|
parsedBody = {};
|
|
54937
55245
|
}
|
|
@@ -55211,7 +55519,9 @@ function findCompilerPath2() {
|
|
|
55211
55519
|
return resolve6(process.cwd(), "packages/compiler/tw/index.ts");
|
|
55212
55520
|
}
|
|
55213
55521
|
function extractRenderMode2(src) {
|
|
55214
|
-
const m = /render\s+(static|ssr|island|edge|csr|stream|ppr|signalStream)\b/.exec(
|
|
55522
|
+
const m = /render\s+(static|ssr|island|edge|csr|stream|ppr|signalStream)\b/.exec(
|
|
55523
|
+
maskSourceStringsAndComments(src)
|
|
55524
|
+
);
|
|
55215
55525
|
return m ? m[1] : "static";
|
|
55216
55526
|
}
|
|
55217
55527
|
function csrifyHtml(html) {
|
|
@@ -55325,18 +55635,32 @@ function scanComponents2(rootDir) {
|
|
|
55325
55635
|
for (const d of privDirs) registerComponentsFrom2(d);
|
|
55326
55636
|
}
|
|
55327
55637
|
function buildMetaTagsFromSource(source) {
|
|
55328
|
-
const
|
|
55329
|
-
|
|
55330
|
-
|
|
55638
|
+
const masked = maskSourceStringsAndComments(source);
|
|
55639
|
+
const fm = masked.match(/page\s*\{/);
|
|
55640
|
+
if (!fm || fm.index === void 0) return "";
|
|
55641
|
+
const bodyStart = fm.index + fm[0].length;
|
|
55642
|
+
const bodyEnd = masked.indexOf("}", bodyStart);
|
|
55643
|
+
if (bodyEnd === -1) return "";
|
|
55644
|
+
const body = source.slice(bodyStart, bodyEnd);
|
|
55331
55645
|
const AMP = String.fromCharCode(38);
|
|
55332
55646
|
const esc = (v) => v.split(AMP).join(AMP + "amp;").split(String.fromCharCode(34)).join(AMP + "quot;").split("<").join(AMP + "lt;");
|
|
55333
55647
|
const pick = (key) => {
|
|
55334
55648
|
const i = body.indexOf(key + ' "');
|
|
55335
55649
|
if (i === -1) return null;
|
|
55336
55650
|
const rest = body.slice(i + key.length + 2);
|
|
55337
|
-
|
|
55651
|
+
let end = -1;
|
|
55652
|
+
for (let j = 0; j < rest.length; j++) {
|
|
55653
|
+
if (rest[j] === "\\") {
|
|
55654
|
+
j++;
|
|
55655
|
+
continue;
|
|
55656
|
+
}
|
|
55657
|
+
if (rest[j] === '"') {
|
|
55658
|
+
end = j;
|
|
55659
|
+
break;
|
|
55660
|
+
}
|
|
55661
|
+
}
|
|
55338
55662
|
if (end === -1) return null;
|
|
55339
|
-
return rest.slice(0, end);
|
|
55663
|
+
return rest.slice(0, end).replace(/\\([{}"])/g, "$1");
|
|
55340
55664
|
};
|
|
55341
55665
|
let tags = "";
|
|
55342
55666
|
const description = pick("description");
|
|
@@ -55466,7 +55790,7 @@ async function buildCommand() {
|
|
|
55466
55790
|
errorCount++;
|
|
55467
55791
|
}
|
|
55468
55792
|
}
|
|
55469
|
-
const revM = resolved ? null : /page\s*\{[^}]*revalidate\s+(\d+)/.exec(pageSrc);
|
|
55793
|
+
const revM = resolved ? null : /page\s*\{[^}]*revalidate\s+(\d+)/.exec(maskSourceStringsAndComments(pageSrc));
|
|
55470
55794
|
if (resolved || revM) {
|
|
55471
55795
|
for (const ps of paramSets) {
|
|
55472
55796
|
const rp = routePath.split("/").map((seg) => {
|
|
@@ -55550,7 +55874,10 @@ async function buildCommand() {
|
|
|
55550
55874
|
const headResult = _compileSync(headSource, { filePath: headSrc });
|
|
55551
55875
|
const headBody = headResult.html.match(/<body>([\s\S]*)<\/body>/);
|
|
55552
55876
|
if (headBody && headBody[1].trim()) {
|
|
55553
|
-
html = html.replace(
|
|
55877
|
+
html = html.replace(
|
|
55878
|
+
"</head>",
|
|
55879
|
+
" " + headBody[1].trim().replace(/\\([{}])/g, "$1") + "\n</head>"
|
|
55880
|
+
);
|
|
55554
55881
|
}
|
|
55555
55882
|
} catch (e) {
|
|
55556
55883
|
console.log(" Warning: head.tw failed: " + e.message);
|
|
@@ -55761,6 +56088,7 @@ async function buildCommand() {
|
|
|
55761
56088
|
}
|
|
55762
56089
|
console.log("\n \x1B[32mBuild complete!\x1B[0m");
|
|
55763
56090
|
console.log(" Pages: " + pageCount + " compiled");
|
|
56091
|
+
if (pageCount === 0) console.warn(" [!] No static pages were compiled -- every page is SSR/API. Static hosts (Vercel/Cloudflare Pages output) will serve an empty site. Use `render static` or check your render modes.");
|
|
55764
56092
|
console.log(" APIs: " + apiCount + " routes");
|
|
55765
56093
|
if (isrRoutes.size > 0) {
|
|
55766
56094
|
const manifest = {};
|
|
@@ -57709,9 +58037,11 @@ var init_provider2 = __esm({
|
|
|
57709
58037
|
getExpressionCompletions(doc) {
|
|
57710
58038
|
const items = [];
|
|
57711
58039
|
const definedVars = /* @__PURE__ */ new Set();
|
|
57712
|
-
const
|
|
58040
|
+
const scriptMatches = [...doc.content.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/gi)];
|
|
58041
|
+
const scriptMatch = scriptMatches.length ? [scriptMatches.map((m) => m[1]).join("\n")] : null;
|
|
58042
|
+
const scriptContent = scriptMatch ? scriptMatch[0] : "";
|
|
57713
58043
|
if (scriptMatch) {
|
|
57714
|
-
const sc =
|
|
58044
|
+
const sc = scriptContent;
|
|
57715
58045
|
const varRegex = /(?:const|let|var)\s+(\w+)/g;
|
|
57716
58046
|
let m;
|
|
57717
58047
|
while ((m = varRegex.exec(sc)) !== null) {
|
|
@@ -58286,8 +58616,10 @@ var init_symbols = __esm({
|
|
|
58286
58616
|
const doc = this.docManager.getDocument(uri);
|
|
58287
58617
|
if (!doc) return [];
|
|
58288
58618
|
const symbols = [];
|
|
58289
|
-
const
|
|
58290
|
-
|
|
58619
|
+
const scriptMatches = [...doc.content.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/gi)];
|
|
58620
|
+
const scriptMatch = scriptMatches.length ? [scriptMatches.map((m) => m[1]).join("\n")] : null;
|
|
58621
|
+
const scriptContent = scriptMatch ? scriptMatch[0] : "";
|
|
58622
|
+
if (scriptMatch) symbols.push(...this.parseScriptSymbols(scriptContent, this.findScriptStart(doc)));
|
|
58291
58623
|
symbols.push(...this.parseTemplateSymbols(doc));
|
|
58292
58624
|
return symbols;
|
|
58293
58625
|
}
|
|
@@ -58600,6 +58932,11 @@ var init_provider14 = __esm({
|
|
|
58600
58932
|
});
|
|
58601
58933
|
|
|
58602
58934
|
// ../../packages/lsp/tw/code-lens/provider.ts
|
|
58935
|
+
function stripLineComments(src) {
|
|
58936
|
+
return src.split("\n").map(function(l) {
|
|
58937
|
+
return l.replace(/(^|\s)\/\/.*$/, "");
|
|
58938
|
+
}).join("\n");
|
|
58939
|
+
}
|
|
58603
58940
|
var CodeLensProvider;
|
|
58604
58941
|
var init_provider15 = __esm({
|
|
58605
58942
|
"../../packages/lsp/tw/code-lens/provider.ts"() {
|
|
@@ -58632,7 +58969,7 @@ var init_provider15 = __esm({
|
|
|
58632
58969
|
const esc = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
58633
58970
|
const regex = new RegExp(`\\b${esc}\\b`, "g");
|
|
58634
58971
|
for (const d of this.docManager.getAllDocuments()) {
|
|
58635
|
-
const m = d.content.match(regex);
|
|
58972
|
+
const m = stripLineComments(d.content).match(regex);
|
|
58636
58973
|
if (m) count += m.length - 1;
|
|
58637
58974
|
}
|
|
58638
58975
|
return Math.max(0, count);
|
|
@@ -58642,7 +58979,7 @@ var init_provider15 = __esm({
|
|
|
58642
58979
|
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
58643
58980
|
const regex = new RegExp(`<${esc}\\b`, "g");
|
|
58644
58981
|
for (const d of this.docManager.getAllDocuments()) {
|
|
58645
|
-
const m = d.content.match(regex);
|
|
58982
|
+
const m = stripLineComments(d.content).match(regex);
|
|
58646
58983
|
if (m) count += m.length;
|
|
58647
58984
|
}
|
|
58648
58985
|
return count;
|
|
@@ -60983,38 +61320,15 @@ function destroySession(req) {
|
|
|
60983
61320
|
const match = cookie.match(new RegExp(`${SESSION_COOKIE}=([^;]+)`));
|
|
60984
61321
|
if (match) sessions.delete(match[1]);
|
|
60985
61322
|
}
|
|
60986
|
-
function
|
|
60987
|
-
|
|
60988
|
-
|
|
60989
|
-
|
|
60990
|
-
|
|
60991
|
-
|
|
60992
|
-
|
|
60993
|
-
|
|
60994
|
-
const name = match[1];
|
|
60995
|
-
const body = match[2];
|
|
60996
|
-
const pathMatch = body.match(/match\s+"([^"]+)"/);
|
|
60997
|
-
const authMatch = body.match(/auth\s+(true|false)/);
|
|
60998
|
-
const redirectMatch = body.match(/redirect\s+"([^"]+)"/);
|
|
60999
|
-
if (pathMatch) {
|
|
61000
|
-
rules.push({
|
|
61001
|
-
name,
|
|
61002
|
-
match: pathMatch[1],
|
|
61003
|
-
auth: authMatch ? authMatch[1] === "true" : false,
|
|
61004
|
-
redirect: redirectMatch ? redirectMatch[1] : "/login"
|
|
61005
|
-
});
|
|
61006
|
-
}
|
|
61007
|
-
}
|
|
61008
|
-
return rules;
|
|
61009
|
-
}
|
|
61010
|
-
function matchGlob(path, pattern) {
|
|
61011
|
-
if (pattern === "/**") return true;
|
|
61012
|
-
if (pattern === path) return true;
|
|
61013
|
-
if (pattern.endsWith("/**")) {
|
|
61014
|
-
const prefix = pattern.slice(0, -3);
|
|
61015
|
-
return path === prefix || path.startsWith(prefix + "/");
|
|
61323
|
+
async function loadMiddlewareRules(mwPath) {
|
|
61324
|
+
try {
|
|
61325
|
+
const source = readFileSync11(mwPath, "utf-8");
|
|
61326
|
+
const { parseRules: parseRules2 } = await Promise.resolve().then(() => (init_twm_rules(), twm_rules_exports));
|
|
61327
|
+
return parseRules2(source);
|
|
61328
|
+
} catch (e) {
|
|
61329
|
+
console.log(" Middleware error: " + e.message);
|
|
61330
|
+
return [];
|
|
61016
61331
|
}
|
|
61017
|
-
return false;
|
|
61018
61332
|
}
|
|
61019
61333
|
function get404() {
|
|
61020
61334
|
return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>404 - Page Not Found</title><style>${DEFAULT_CSS}</style></head><body><div class="wrapper"><section class="hero"><div class="container"><h1>404</h1><p>Page not found</p><a class="btn" href="/">Go Home</a></div></section></div></body></html>`;
|
|
@@ -61093,7 +61407,8 @@ async function devCommand() {
|
|
|
61093
61407
|
if (existsSync15(stylePath)) parts.push(readFileSync11(stylePath, "utf-8"));
|
|
61094
61408
|
if (parts.length) css = parts.join("\n");
|
|
61095
61409
|
}
|
|
61096
|
-
const
|
|
61410
|
+
const mwPath = join19(rootDir, "middleware.twm");
|
|
61411
|
+
const middlewareRules = existsSync15(mwPath) ? await loadMiddlewareRules(mwPath) : [];
|
|
61097
61412
|
scanComponents(rootDir);
|
|
61098
61413
|
const compDir = join19(rootDir, "components");
|
|
61099
61414
|
if (existsSync15(compDir)) {
|
|
@@ -61168,13 +61483,13 @@ async function devCommand() {
|
|
|
61168
61483
|
}
|
|
61169
61484
|
async function runUserMiddleware(req) {
|
|
61170
61485
|
try {
|
|
61171
|
-
const
|
|
61172
|
-
if (!existsSync15(
|
|
61486
|
+
const mwPath2 = join19(rootDir, "middleware.twm");
|
|
61487
|
+
if (!existsSync15(mwPath2)) return null;
|
|
61173
61488
|
const { executeMiddleware: executeMiddleware2, loadTWMModule: loadTWMModule2, shouldMatchMiddleware: shouldMatchMiddleware2 } = await Promise.resolve().then(() => (init_twm_loader(), twm_loader_exports));
|
|
61174
|
-
const mod = await loadTWMModule2(
|
|
61489
|
+
const mod = await loadTWMModule2(mwPath2, rootDir);
|
|
61175
61490
|
const mwPathname = new URL(req.url).pathname;
|
|
61176
61491
|
if (!mod.config?.matcher || shouldMatchMiddleware2(mod.config.matcher, mwPathname)) {
|
|
61177
|
-
const mwResult = await executeMiddleware2(
|
|
61492
|
+
const mwResult = await executeMiddleware2(mwPath2, req, rootDir);
|
|
61178
61493
|
if (mwResult instanceof Response) return mwResult;
|
|
61179
61494
|
if (mwResult && typeof mwResult === "object" && mwResult.headers && !mwResult.status && !mwResult.json) {
|
|
61180
61495
|
return mwResult.headers;
|
|
@@ -61230,16 +61545,15 @@ async function devCommand() {
|
|
|
61230
61545
|
rescanComponentsIfNeeded(rootDir);
|
|
61231
61546
|
console.log(" [" + time + "] " + req.method + " " + path);
|
|
61232
61547
|
const session = getSession(req);
|
|
61233
|
-
|
|
61234
|
-
|
|
61235
|
-
|
|
61236
|
-
|
|
61237
|
-
|
|
61238
|
-
|
|
61239
|
-
headers: { Location: rule.redirect }
|
|
61240
|
-
});
|
|
61241
|
-
}
|
|
61548
|
+
try {
|
|
61549
|
+
const { evaluateRules: evaluateRules2 } = await Promise.resolve().then(() => (init_twm_rules(), twm_rules_exports));
|
|
61550
|
+
const mwRes = evaluateRules2(middlewareRules, req);
|
|
61551
|
+
if (mwRes) {
|
|
61552
|
+
console.log(" -> middleware response " + mwRes.status + " for " + path);
|
|
61553
|
+
return mwRes;
|
|
61242
61554
|
}
|
|
61555
|
+
} catch (e) {
|
|
61556
|
+
console.log(" Middleware error: " + e.message);
|
|
61243
61557
|
}
|
|
61244
61558
|
if (path === "/__tw_runtime.js") {
|
|
61245
61559
|
const _fu = import.meta.url.replace("file://", "");
|
|
@@ -61449,13 +61763,20 @@ async function devCommand() {
|
|
|
61449
61763
|
if (/<\/body>/.test(html)) {
|
|
61450
61764
|
html = html.replace("</body>", devClientTag + ' <script src="/__tw_runtime.js"></script>\n</body>');
|
|
61451
61765
|
}
|
|
61766
|
+
const devGlobalHead = join19(homeDir, "head.tw");
|
|
61767
|
+
const devHeadSources = [];
|
|
61768
|
+
if (existsSync15(devGlobalHead)) devHeadSources.push(devGlobalHead);
|
|
61452
61769
|
const headTw = join19(dirname5(resolved.file), "head.tw");
|
|
61453
|
-
if (existsSync15(headTw))
|
|
61770
|
+
if (headTw !== devGlobalHead && existsSync15(headTw)) devHeadSources.push(headTw);
|
|
61771
|
+
for (const devHeadTw of devHeadSources) {
|
|
61454
61772
|
try {
|
|
61455
|
-
const headRes = compileSync3(readFileSync11(
|
|
61773
|
+
const headRes = compileSync3(readFileSync11(devHeadTw, "utf-8"), { filePath: devHeadTw });
|
|
61456
61774
|
const headBody = headRes.html.match(/<body>([\s\S]*)<\/body>/);
|
|
61457
61775
|
if (headBody && headBody[1].trim()) {
|
|
61458
|
-
html = html.replace(
|
|
61776
|
+
html = html.replace(
|
|
61777
|
+
"</head>",
|
|
61778
|
+
" " + headBody[1].trim().replace(/\\([{}])/g, "$1") + "\n</head>"
|
|
61779
|
+
);
|
|
61459
61780
|
}
|
|
61460
61781
|
} catch (e) {
|
|
61461
61782
|
console.log(" Warning: head.tw failed: " + e.message);
|
|
@@ -61987,6 +62308,7 @@ async function testCommand() {
|
|
|
61987
62308
|
}
|
|
61988
62309
|
|
|
61989
62310
|
// tw/commands/check.ts
|
|
62311
|
+
init_tw();
|
|
61990
62312
|
import { join as join28 } from "node:path";
|
|
61991
62313
|
import { existsSync as existsSync23, readFileSync as readFileSync15, readdirSync as readdirSync10, statSync as statSync10 } from "node:fs";
|
|
61992
62314
|
async function checkCommand() {
|
|
@@ -62044,23 +62366,24 @@ async function checkCommand() {
|
|
|
62044
62366
|
for (const filePath of twFiles) {
|
|
62045
62367
|
const relativePath = filePath.replace(rootDir + "/", "");
|
|
62046
62368
|
const source = readFileSync15(filePath, "utf-8");
|
|
62047
|
-
const
|
|
62048
|
-
const
|
|
62369
|
+
const maskedSource = maskSourceStringsAndComments(source);
|
|
62370
|
+
const openBraces = (maskedSource.match(/\{/g) || []).length;
|
|
62371
|
+
const closeBraces = (maskedSource.match(/\}/g) || []).length;
|
|
62049
62372
|
if (openBraces !== closeBraces) {
|
|
62050
62373
|
totalErrors++;
|
|
62051
|
-
console.
|
|
62374
|
+
console.log(` X ${relativePath}: unbalanced braces (${openBraces} open, ${closeBraces} close)`);
|
|
62052
62375
|
} else {
|
|
62053
|
-
console.
|
|
62376
|
+
console.log(` OK ${relativePath} -- syntax OK`);
|
|
62054
62377
|
}
|
|
62055
62378
|
}
|
|
62056
62379
|
}
|
|
62057
|
-
console.
|
|
62380
|
+
console.log(`
|
|
62058
62381
|
Summary: ${totalErrors} errors, ${totalWarnings} warnings, ${totalInfo} info`);
|
|
62059
62382
|
if (totalErrors > 0) {
|
|
62060
|
-
console.
|
|
62383
|
+
console.log(" Status: FAILED\n");
|
|
62061
62384
|
process.exit(1);
|
|
62062
62385
|
} else {
|
|
62063
|
-
console.
|
|
62386
|
+
console.log(" Status: PASSED\n");
|
|
62064
62387
|
}
|
|
62065
62388
|
}
|
|
62066
62389
|
function collectFiles2(dir, ext, results) {
|
|
@@ -62077,11 +62400,12 @@ function collectFiles2(dir, ext, results) {
|
|
|
62077
62400
|
}
|
|
62078
62401
|
|
|
62079
62402
|
// tw/commands/plugin.ts
|
|
62403
|
+
init_tw();
|
|
62080
62404
|
import { join as join29 } from "node:path";
|
|
62081
62405
|
import { existsSync as existsSync24, readFileSync as readFileSync16, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, readdirSync as readdirSync11 } from "node:fs";
|
|
62082
62406
|
function readConfigPlugins(cfgPath) {
|
|
62083
62407
|
if (!existsSync24(cfgPath)) return [];
|
|
62084
|
-
const src = readFileSync16(cfgPath, "utf-8");
|
|
62408
|
+
const src = stripCommentsStringAware(readFileSync16(cfgPath, "utf-8"));
|
|
62085
62409
|
const m = /plugins\s*:\s*\[([^\]]*)\]/s.exec(src);
|
|
62086
62410
|
if (!m) return [];
|
|
62087
62411
|
const out = [];
|
|
@@ -62100,7 +62424,7 @@ function addConfigPlugin(cfgPath, name) {
|
|
|
62100
62424
|
`);
|
|
62101
62425
|
return true;
|
|
62102
62426
|
}
|
|
62103
|
-
const src = readFileSync16(cfgPath, "utf-8");
|
|
62427
|
+
const src = stripCommentsStringAware(readFileSync16(cfgPath, "utf-8"));
|
|
62104
62428
|
if (new RegExp(`["'\`]${name}["'\`]`).test(src)) return false;
|
|
62105
62429
|
const m = /plugins\s*:\s*\[([^\]]*)\]/s.exec(src);
|
|
62106
62430
|
if (m) {
|
|
@@ -62117,7 +62441,7 @@ function addConfigPlugin(cfgPath, name) {
|
|
|
62117
62441
|
}
|
|
62118
62442
|
function removeConfigPlugin(cfgPath, name) {
|
|
62119
62443
|
if (!existsSync24(cfgPath)) return false;
|
|
62120
|
-
const src = readFileSync16(cfgPath, "utf-8");
|
|
62444
|
+
const src = stripCommentsStringAware(readFileSync16(cfgPath, "utf-8"));
|
|
62121
62445
|
const m = /plugins\s*:\s*\[([^\]]*)\]/s.exec(src);
|
|
62122
62446
|
if (!m) return false;
|
|
62123
62447
|
const kept = m[1].split(",").map((e) => e.trim()).filter((e) => e && e !== `"${name}"` && e !== `'${name}'` && e !== "`" + name + "`");
|