rip-lang 3.13.55 → 3.13.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/bin/rip +26 -15
- package/docs/RIP-LANG.md +38 -10
- package/docs/dist/rip.js +292 -278
- package/docs/dist/rip.min.js +103 -103
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/src/app.rip +1 -1
- package/src/browser.js +13 -4
- package/src/compiler.js +29 -14
- package/src/components.js +25 -15
package/docs/dist/rip.js
CHANGED
|
@@ -3638,6 +3638,11 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3638
3638
|
tokens.splice(i + 1, 1, colonToken, valueToken);
|
|
3639
3639
|
return 1;
|
|
3640
3640
|
}
|
|
3641
|
+
if (tag === "PROPERTY" && token[1][0] === "$" && token[1].length > 1) {
|
|
3642
|
+
token[0] = "STRING";
|
|
3643
|
+
token[1] = `"data-${token[1].slice(1)}"`;
|
|
3644
|
+
return 1;
|
|
3645
|
+
}
|
|
3641
3646
|
if (tag === "IDENTIFIER" && !token.spaced) {
|
|
3642
3647
|
let parts = [token[1]];
|
|
3643
3648
|
let j = i + 1;
|
|
@@ -3654,8 +3659,11 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3654
3659
|
}
|
|
3655
3660
|
}
|
|
3656
3661
|
if (parts.length > 1 && j > i + 1 && tokens[j - 1][0] === "PROPERTY") {
|
|
3662
|
+
let joined = parts.join("-");
|
|
3663
|
+
if (joined[0] === "$")
|
|
3664
|
+
joined = "data-" + joined.slice(1);
|
|
3657
3665
|
token[0] = "STRING";
|
|
3658
|
-
token[1] = `"${
|
|
3666
|
+
token[1] = `"${joined}"`;
|
|
3659
3667
|
tokens.splice(i + 1, j - i - 1);
|
|
3660
3668
|
return 1;
|
|
3661
3669
|
}
|
|
@@ -4494,16 +4502,6 @@ ${blockFactoriesCode}return ${lines.join(`
|
|
|
4494
4502
|
const valueCode = this.generateInComponent(value, "value");
|
|
4495
4503
|
if ((key === "value" || key === "checked") && this.hasReactiveDeps(value)) {
|
|
4496
4504
|
this._pushEffect(`${elVar}.${key} = ${valueCode};`);
|
|
4497
|
-
const rootMemberImplicit = !this.isSimpleAssignable(value) && this.findRootReactiveMember(value);
|
|
4498
|
-
if (this.isSimpleAssignable(value) || rootMemberImplicit) {
|
|
4499
|
-
const event = key === "checked" ? "change" : "input";
|
|
4500
|
-
const accessor = key === "checked" ? "e.target.checked" : inputType === "number" || inputType === "range" ? "e.target.valueAsNumber" : "e.target.value";
|
|
4501
|
-
let assignCode = `${valueCode} = ${accessor}`;
|
|
4502
|
-
if (rootMemberImplicit) {
|
|
4503
|
-
assignCode += `; ${this._self}.${rootMemberImplicit}.touch?.()`;
|
|
4504
|
-
}
|
|
4505
|
-
this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${assignCode}; });`);
|
|
4506
|
-
}
|
|
4507
4505
|
continue;
|
|
4508
4506
|
}
|
|
4509
4507
|
if (key === "innerHTML" || key === "textContent" || key === "innerText") {
|
|
@@ -4692,7 +4690,18 @@ ${blockFactoriesCode}return ${lines.join(`
|
|
|
4692
4690
|
this._createLines.push(`${anchorVar} = document.createComment('for');`);
|
|
4693
4691
|
const varNames = Array.isArray(vars) ? vars : [vars];
|
|
4694
4692
|
const itemVar = varNames[0];
|
|
4695
|
-
|
|
4693
|
+
let indexVar = varNames[1] || null;
|
|
4694
|
+
if (!indexVar) {
|
|
4695
|
+
const usedNames = new Set(this._loopVarStack.flatMap((v) => [v.itemVar, v.indexVar]));
|
|
4696
|
+
usedNames.add(itemVar);
|
|
4697
|
+
for (const candidate of ["i", "j", "k", "l", "m", "n"]) {
|
|
4698
|
+
if (!usedNames.has(candidate)) {
|
|
4699
|
+
indexVar = candidate;
|
|
4700
|
+
break;
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
indexVar = indexVar || `_i${this._loopVarStack.length}`;
|
|
4704
|
+
}
|
|
4696
4705
|
const collectionCode = this.generateInComponent(collection, "value");
|
|
4697
4706
|
let keyExpr = itemVar;
|
|
4698
4707
|
if (this.is(body, "block") && body.length > 1) {
|
|
@@ -5823,29 +5832,27 @@ if (typeof globalThis !== 'undefined') {
|
|
|
5823
5832
|
}
|
|
5824
5833
|
generateProgram(head, statements, context, sexpr) {
|
|
5825
5834
|
let code = "";
|
|
5826
|
-
let imports = [],
|
|
5835
|
+
let imports = [], body = [];
|
|
5827
5836
|
for (let stmt of statements) {
|
|
5828
5837
|
if (!Array.isArray(stmt)) {
|
|
5829
|
-
|
|
5838
|
+
body.push(stmt);
|
|
5830
5839
|
continue;
|
|
5831
5840
|
}
|
|
5832
5841
|
let h = stmt[0];
|
|
5833
5842
|
if (h === "import")
|
|
5834
5843
|
imports.push(stmt);
|
|
5835
|
-
else if (h === "export" || h === "export-default" || h === "export-all" || h === "export-from")
|
|
5836
|
-
exports.push(stmt);
|
|
5837
5844
|
else
|
|
5838
|
-
|
|
5845
|
+
body.push(stmt);
|
|
5839
5846
|
}
|
|
5840
5847
|
let blockStmts = ["def", "class", "if", "for-in", "for-of", "for-as", "while", "loop", "switch", "try"];
|
|
5841
|
-
let stmtEntries =
|
|
5842
|
-
let isSingle =
|
|
5848
|
+
let stmtEntries = body.map((stmt, index) => {
|
|
5849
|
+
let isSingle = body.length === 1 && imports.length === 0;
|
|
5843
5850
|
let isObj = this.is(stmt, "object");
|
|
5844
5851
|
let isObjComp = isObj && stmt.length === 2 && Array.isArray(stmt[1]) && Array.isArray(stmt[1][1]) && stmt[1][1][0] === "comprehension";
|
|
5845
5852
|
let isAlreadyExpr = this.is(stmt, "comprehension") || this.is(stmt, "object-comprehension") || this.is(stmt, "do-iife");
|
|
5846
5853
|
let hasNoVars = this.programVars.size === 0;
|
|
5847
5854
|
let needsParens = isSingle && isObj && hasNoVars && !isAlreadyExpr && !isObjComp;
|
|
5848
|
-
let isLast = index ===
|
|
5855
|
+
let isLast = index === body.length - 1;
|
|
5849
5856
|
let isLastComp = isLast && isAlreadyExpr;
|
|
5850
5857
|
let generated;
|
|
5851
5858
|
if (needsParens)
|
|
@@ -5930,12 +5937,6 @@ if (typeof globalThis !== 'undefined') {
|
|
|
5930
5937
|
needsBlank = true;
|
|
5931
5938
|
}
|
|
5932
5939
|
}
|
|
5933
|
-
let exportsCode = "";
|
|
5934
|
-
if (exports.length > 0) {
|
|
5935
|
-
exportsCode = `
|
|
5936
|
-
` + exports.map((s) => this.addSemicolon(s, this.generate(s, "statement"))).join(`
|
|
5937
|
-
`);
|
|
5938
|
-
}
|
|
5939
5940
|
if (this.usesReactivity && !skip) {
|
|
5940
5941
|
if (skipRT) {
|
|
5941
5942
|
code += `var { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;
|
|
@@ -5973,7 +5974,6 @@ _setDataSection();
|
|
|
5973
5974
|
this._preambleLines = code.length === 0 ? 0 : code.split(`
|
|
5974
5975
|
`).length - 1;
|
|
5975
5976
|
code += statementsCode;
|
|
5976
|
-
code += exportsCode;
|
|
5977
5977
|
if (this.dataSection !== null && this.dataSection !== undefined) {
|
|
5978
5978
|
code += `
|
|
5979
5979
|
|
|
@@ -8680,7 +8680,7 @@ globalThis.zip ??= (...a) => a[0].map((_, i) => a.map(b => b[i]));
|
|
|
8680
8680
|
}
|
|
8681
8681
|
// src/browser.js
|
|
8682
8682
|
var VERSION = "3.13.55";
|
|
8683
|
-
var BUILD_DATE = "2026-02-
|
|
8683
|
+
var BUILD_DATE = "2026-02-28@19:43:03GMT";
|
|
8684
8684
|
if (typeof globalThis !== "undefined") {
|
|
8685
8685
|
if (!globalThis.__rip)
|
|
8686
8686
|
new Function(getReactiveRuntime())();
|
|
@@ -8732,13 +8732,14 @@ globalThis.zip ??= (...a) => a[0].map((_, i) => a.map(b => b[i]));
|
|
|
8732
8732
|
if (!s.code)
|
|
8733
8733
|
continue;
|
|
8734
8734
|
try {
|
|
8735
|
-
|
|
8735
|
+
const js = compileToJS(s.code, opts);
|
|
8736
|
+
compiled.push({ js, url: s.url || "inline" });
|
|
8736
8737
|
} catch (e) {
|
|
8737
|
-
console.error(
|
|
8738
|
+
console.error(`Rip compile error in ${s.url || "inline"}:`, e.message);
|
|
8738
8739
|
}
|
|
8739
8740
|
}
|
|
8740
8741
|
if (compiled.length > 0) {
|
|
8741
|
-
let js = compiled.join(`
|
|
8742
|
+
let js = compiled.map((c) => c.js).join(`
|
|
8742
8743
|
`);
|
|
8743
8744
|
const mount = runtimeTag?.getAttribute("data-mount");
|
|
8744
8745
|
if (mount) {
|
|
@@ -8751,7 +8752,20 @@ ${mount}.mount(${JSON.stringify(target)});`;
|
|
|
8751
8752
|
${js}
|
|
8752
8753
|
})()`);
|
|
8753
8754
|
} catch (e) {
|
|
8754
|
-
|
|
8755
|
+
if (e instanceof SyntaxError) {
|
|
8756
|
+
console.error(`Rip syntax error in combined output: ${e.message}`);
|
|
8757
|
+
for (const c of compiled) {
|
|
8758
|
+
try {
|
|
8759
|
+
new Function(`(async()=>{
|
|
8760
|
+
${c.js}
|
|
8761
|
+
})()`);
|
|
8762
|
+
} catch (e2) {
|
|
8763
|
+
console.error(` → source: ${c.url}`, e2.message);
|
|
8764
|
+
}
|
|
8765
|
+
}
|
|
8766
|
+
} else {
|
|
8767
|
+
console.error("Rip runtime error:", e);
|
|
8768
|
+
}
|
|
8755
8769
|
}
|
|
8756
8770
|
}
|
|
8757
8771
|
}
|
|
@@ -9053,252 +9067,6 @@ ${indented}`);
|
|
|
9053
9067
|
}
|
|
9054
9068
|
return value;
|
|
9055
9069
|
};
|
|
9056
|
-
_toFn = function(source) {
|
|
9057
|
-
return typeof source === "function" ? source : function() {
|
|
9058
|
-
return source.value;
|
|
9059
|
-
};
|
|
9060
|
-
};
|
|
9061
|
-
_proxy = function(out, source) {
|
|
9062
|
-
let obj;
|
|
9063
|
-
obj = { read: function() {
|
|
9064
|
-
return out.read();
|
|
9065
|
-
} };
|
|
9066
|
-
Object.defineProperty(obj, "value", { get: function() {
|
|
9067
|
-
return out.value;
|
|
9068
|
-
}, set: function(v) {
|
|
9069
|
-
return source.value = v;
|
|
9070
|
-
} });
|
|
9071
|
-
return obj;
|
|
9072
|
-
};
|
|
9073
|
-
fileToPattern = function(rel) {
|
|
9074
|
-
let pattern;
|
|
9075
|
-
pattern = rel.replace(/\.rip$/, "");
|
|
9076
|
-
pattern = pattern.replace(/\[\.\.\.(\w+)\]/g, "*$1");
|
|
9077
|
-
pattern = pattern.replace(/\[(\w+)\]/g, ":$1");
|
|
9078
|
-
if (pattern === "index")
|
|
9079
|
-
return "/";
|
|
9080
|
-
pattern = pattern.replace(/\/index$/, "");
|
|
9081
|
-
return "/" + pattern;
|
|
9082
|
-
};
|
|
9083
|
-
patternToRegex = function(pattern) {
|
|
9084
|
-
let names, str2;
|
|
9085
|
-
names = [];
|
|
9086
|
-
str2 = pattern.replace(/\*(\w+)/g, function(_, name) {
|
|
9087
|
-
names.push(name);
|
|
9088
|
-
return "(.+)";
|
|
9089
|
-
}).replace(/:(\w+)/g, function(_, name) {
|
|
9090
|
-
names.push(name);
|
|
9091
|
-
return "([^/]+)";
|
|
9092
|
-
});
|
|
9093
|
-
return { regex: new RegExp("^" + str2 + "$"), names };
|
|
9094
|
-
};
|
|
9095
|
-
matchRoute = function(path, routes) {
|
|
9096
|
-
let match, params;
|
|
9097
|
-
for (const route of routes) {
|
|
9098
|
-
match = path.match(route.regex.regex);
|
|
9099
|
-
if (match) {
|
|
9100
|
-
params = {};
|
|
9101
|
-
for (let i = 0;i < route.regex.names.length; i++) {
|
|
9102
|
-
const name = route.regex.names[i];
|
|
9103
|
-
params[name] = decodeURIComponent(match[i + 1]);
|
|
9104
|
-
}
|
|
9105
|
-
return { route, params };
|
|
9106
|
-
}
|
|
9107
|
-
}
|
|
9108
|
-
return null;
|
|
9109
|
-
};
|
|
9110
|
-
buildRoutes = function(components, root = "components") {
|
|
9111
|
-
let allFiles, dir, layouts, name, regex, rel, routes, segs, urlPattern;
|
|
9112
|
-
routes = [];
|
|
9113
|
-
layouts = new Map;
|
|
9114
|
-
allFiles = components.listAll(root);
|
|
9115
|
-
for (const filePath of allFiles) {
|
|
9116
|
-
rel = filePath.slice(root.length + 1);
|
|
9117
|
-
if (!rel.endsWith(".rip"))
|
|
9118
|
-
continue;
|
|
9119
|
-
name = rel.split("/").pop();
|
|
9120
|
-
if (name === "_layout.rip") {
|
|
9121
|
-
dir = rel === "_layout.rip" ? "" : rel.slice(0, -"/_layout.rip".length);
|
|
9122
|
-
layouts.set(dir, filePath);
|
|
9123
|
-
continue;
|
|
9124
|
-
}
|
|
9125
|
-
if (name.startsWith("_"))
|
|
9126
|
-
continue;
|
|
9127
|
-
segs = rel.split("/");
|
|
9128
|
-
if (segs.length > 1 && segs.some(function(s, i) {
|
|
9129
|
-
return i < segs.length - 1 && s.startsWith("_");
|
|
9130
|
-
}))
|
|
9131
|
-
continue;
|
|
9132
|
-
urlPattern = fileToPattern(rel);
|
|
9133
|
-
regex = patternToRegex(urlPattern);
|
|
9134
|
-
routes.push({ pattern: urlPattern, regex, file: filePath, rel });
|
|
9135
|
-
}
|
|
9136
|
-
routes.sort(function(a, b) {
|
|
9137
|
-
let aCatch, aDyn, bCatch, bDyn;
|
|
9138
|
-
aDyn = (a.pattern.match(/:/g) || []).length;
|
|
9139
|
-
bDyn = (b.pattern.match(/:/g) || []).length;
|
|
9140
|
-
aCatch = a.pattern.includes("*") ? 1 : 0;
|
|
9141
|
-
bCatch = b.pattern.includes("*") ? 1 : 0;
|
|
9142
|
-
if (aCatch !== bCatch)
|
|
9143
|
-
return aCatch - bCatch;
|
|
9144
|
-
if (aDyn !== bDyn)
|
|
9145
|
-
return aDyn - bDyn;
|
|
9146
|
-
return a.pattern.localeCompare(b.pattern);
|
|
9147
|
-
});
|
|
9148
|
-
return { routes, layouts };
|
|
9149
|
-
};
|
|
9150
|
-
getLayoutChain = function(routeFile, root, layouts) {
|
|
9151
|
-
let chain, dir, rel, segments;
|
|
9152
|
-
chain = [];
|
|
9153
|
-
rel = routeFile.slice(root.length + 1);
|
|
9154
|
-
segments = rel.split("/");
|
|
9155
|
-
dir = "";
|
|
9156
|
-
if (layouts.has(""))
|
|
9157
|
-
chain.push(layouts.get(""));
|
|
9158
|
-
for (let i = 0;i < segments.length; i++) {
|
|
9159
|
-
const seg = segments[i];
|
|
9160
|
-
if (i === segments.length - 1)
|
|
9161
|
-
break;
|
|
9162
|
-
dir = dir ? dir + "/" + seg : seg;
|
|
9163
|
-
if (layouts.has(dir))
|
|
9164
|
-
chain.push(layouts.get(dir));
|
|
9165
|
-
}
|
|
9166
|
-
return chain;
|
|
9167
|
-
};
|
|
9168
|
-
arraysEqual = function(a, b) {
|
|
9169
|
-
if (a.length !== b.length)
|
|
9170
|
-
return false;
|
|
9171
|
-
for (let i = 0;i < a.length; i++) {
|
|
9172
|
-
const item = a[i];
|
|
9173
|
-
if (item !== b[i])
|
|
9174
|
-
return false;
|
|
9175
|
-
}
|
|
9176
|
-
return true;
|
|
9177
|
-
};
|
|
9178
|
-
findComponent = function(mod) {
|
|
9179
|
-
for (const key in mod) {
|
|
9180
|
-
const val = mod[key];
|
|
9181
|
-
if (typeof val === "function" && (val.prototype?.mount || val.prototype?._create))
|
|
9182
|
-
return val;
|
|
9183
|
-
}
|
|
9184
|
-
return typeof mod.default === "function" ? mod.default : undefined;
|
|
9185
|
-
};
|
|
9186
|
-
findAllComponents = function(mod) {
|
|
9187
|
-
let result;
|
|
9188
|
-
result = {};
|
|
9189
|
-
for (const key in mod) {
|
|
9190
|
-
const val = mod[key];
|
|
9191
|
-
if (typeof val === "function" && (val.prototype?.mount || val.prototype?._create)) {
|
|
9192
|
-
result[key] = val;
|
|
9193
|
-
}
|
|
9194
|
-
}
|
|
9195
|
-
return result;
|
|
9196
|
-
};
|
|
9197
|
-
fileToComponentName = function(filePath) {
|
|
9198
|
-
let name;
|
|
9199
|
-
name = filePath.split("/").pop().replace(/\.rip$/, "");
|
|
9200
|
-
return name.replace(/(^|[-_])([a-z])/g, function(_, sep, ch) {
|
|
9201
|
-
return ch.toUpperCase();
|
|
9202
|
-
});
|
|
9203
|
-
};
|
|
9204
|
-
buildComponentMap = function(components, root = "components") {
|
|
9205
|
-
let fileName, map, name;
|
|
9206
|
-
map = {};
|
|
9207
|
-
for (const path of components.listAll(root)) {
|
|
9208
|
-
if (!path.endsWith(".rip"))
|
|
9209
|
-
continue;
|
|
9210
|
-
fileName = path.split("/").pop();
|
|
9211
|
-
if (fileName.startsWith("_"))
|
|
9212
|
-
continue;
|
|
9213
|
-
name = fileToComponentName(path);
|
|
9214
|
-
if (map[name]) {
|
|
9215
|
-
console.warn(`[Rip] Component name collision: ${name} (${map[name]} vs ${path})`);
|
|
9216
|
-
}
|
|
9217
|
-
map[name] = path;
|
|
9218
|
-
}
|
|
9219
|
-
return map;
|
|
9220
|
-
};
|
|
9221
|
-
compileAndImport = async function(source, compile2, components = null, path = null, resolver = null) {
|
|
9222
|
-
let blob, cached, depMod, depSource, found, header, js, mod, names, needed, preamble, url;
|
|
9223
|
-
if (components && path) {
|
|
9224
|
-
cached = components.getCompiled(path);
|
|
9225
|
-
if (cached)
|
|
9226
|
-
return cached;
|
|
9227
|
-
}
|
|
9228
|
-
js = compile2(source);
|
|
9229
|
-
if (resolver) {
|
|
9230
|
-
needed = {};
|
|
9231
|
-
for (const name in resolver.map) {
|
|
9232
|
-
const depPath = resolver.map[name];
|
|
9233
|
-
if (depPath !== path && js.includes(`new ${name}(`)) {
|
|
9234
|
-
if (!resolver.classes[name]) {
|
|
9235
|
-
depSource = components.read(depPath);
|
|
9236
|
-
if (depSource) {
|
|
9237
|
-
depMod = await compileAndImport(depSource, compile2, components, depPath, resolver);
|
|
9238
|
-
found = findAllComponents(depMod);
|
|
9239
|
-
for (const k in found) {
|
|
9240
|
-
const v = found[k];
|
|
9241
|
-
resolver.classes[k] = v;
|
|
9242
|
-
}
|
|
9243
|
-
}
|
|
9244
|
-
}
|
|
9245
|
-
if (resolver.classes[name])
|
|
9246
|
-
needed[name] = true;
|
|
9247
|
-
}
|
|
9248
|
-
}
|
|
9249
|
-
names = Object.keys(needed);
|
|
9250
|
-
if (names.length > 0) {
|
|
9251
|
-
preamble = `const {${names.join(", ")}} = globalThis['${resolver.key}'];
|
|
9252
|
-
`;
|
|
9253
|
-
js = preamble + js;
|
|
9254
|
-
}
|
|
9255
|
-
}
|
|
9256
|
-
header = path ? `// ${path}
|
|
9257
|
-
` : "";
|
|
9258
|
-
blob = new Blob([header + js], { type: "application/javascript" });
|
|
9259
|
-
url = URL.createObjectURL(blob);
|
|
9260
|
-
mod = await import(url);
|
|
9261
|
-
if (resolver) {
|
|
9262
|
-
found = findAllComponents(mod);
|
|
9263
|
-
for (const k in found) {
|
|
9264
|
-
const v = found[k];
|
|
9265
|
-
resolver.classes[k] = v;
|
|
9266
|
-
}
|
|
9267
|
-
}
|
|
9268
|
-
if (components && path)
|
|
9269
|
-
components.setCompiled(path, mod);
|
|
9270
|
-
return mod;
|
|
9271
|
-
};
|
|
9272
|
-
connectWatch = function(url) {
|
|
9273
|
-
let connect, maxDelay, retryDelay;
|
|
9274
|
-
retryDelay = 1000;
|
|
9275
|
-
maxDelay = 30000;
|
|
9276
|
-
connect = function() {
|
|
9277
|
-
let es;
|
|
9278
|
-
es = new EventSource(url);
|
|
9279
|
-
es.addEventListener("connected", function() {
|
|
9280
|
-
retryDelay = 1000;
|
|
9281
|
-
return console.log("[Rip] Hot reload connected");
|
|
9282
|
-
});
|
|
9283
|
-
es.addEventListener("reload", function() {
|
|
9284
|
-
console.log("[Rip] Reloading...");
|
|
9285
|
-
return location.reload();
|
|
9286
|
-
});
|
|
9287
|
-
es.addEventListener("css", function() {
|
|
9288
|
-
for (const link of document.querySelectorAll('link[rel="stylesheet"]')) {
|
|
9289
|
-
url = new URL(link.href);
|
|
9290
|
-
url.searchParams.set("_t", Date.now());
|
|
9291
|
-
link.href = url.toString();
|
|
9292
|
-
}
|
|
9293
|
-
});
|
|
9294
|
-
return es.onerror = function() {
|
|
9295
|
-
es.close();
|
|
9296
|
-
setTimeout(connect, retryDelay);
|
|
9297
|
-
return retryDelay = Math.min(retryDelay * 2, maxDelay);
|
|
9298
|
-
};
|
|
9299
|
-
};
|
|
9300
|
-
return connect();
|
|
9301
|
-
};
|
|
9302
9070
|
var stash = function(data = {}) {
|
|
9303
9071
|
return makeProxy(data);
|
|
9304
9072
|
};
|
|
@@ -9342,6 +9110,23 @@ ${indented}`);
|
|
|
9342
9110
|
load();
|
|
9343
9111
|
return resource;
|
|
9344
9112
|
};
|
|
9113
|
+
_toFn = function(source) {
|
|
9114
|
+
return typeof source === "function" ? source : function() {
|
|
9115
|
+
return source.value;
|
|
9116
|
+
};
|
|
9117
|
+
};
|
|
9118
|
+
_proxy = function(out, source) {
|
|
9119
|
+
let obj;
|
|
9120
|
+
obj = { read: function() {
|
|
9121
|
+
return out.read();
|
|
9122
|
+
} };
|
|
9123
|
+
Object.defineProperty(obj, "value", { get: function() {
|
|
9124
|
+
return out.value;
|
|
9125
|
+
}, set: function(v) {
|
|
9126
|
+
return source.value = v;
|
|
9127
|
+
} });
|
|
9128
|
+
return obj;
|
|
9129
|
+
};
|
|
9345
9130
|
var delay = function(ms, source) {
|
|
9346
9131
|
let fn, out;
|
|
9347
9132
|
fn = _toFn(source);
|
|
@@ -9485,6 +9270,101 @@ ${indented}`);
|
|
|
9485
9270
|
return compiled.set(path, result);
|
|
9486
9271
|
} };
|
|
9487
9272
|
};
|
|
9273
|
+
fileToPattern = function(rel) {
|
|
9274
|
+
let pattern;
|
|
9275
|
+
pattern = rel.replace(/\.rip$/, "");
|
|
9276
|
+
pattern = pattern.replace(/\[\.\.\.(\w+)\]/g, "*$1");
|
|
9277
|
+
pattern = pattern.replace(/\[(\w+)\]/g, ":$1");
|
|
9278
|
+
if (pattern === "index")
|
|
9279
|
+
return "/";
|
|
9280
|
+
pattern = pattern.replace(/\/index$/, "");
|
|
9281
|
+
return "/" + pattern;
|
|
9282
|
+
};
|
|
9283
|
+
patternToRegex = function(pattern) {
|
|
9284
|
+
let names, str2;
|
|
9285
|
+
names = [];
|
|
9286
|
+
str2 = pattern.replace(/\*(\w+)/g, function(_, name) {
|
|
9287
|
+
names.push(name);
|
|
9288
|
+
return "(.+)";
|
|
9289
|
+
}).replace(/:(\w+)/g, function(_, name) {
|
|
9290
|
+
names.push(name);
|
|
9291
|
+
return "([^/]+)";
|
|
9292
|
+
});
|
|
9293
|
+
return { regex: new RegExp("^" + str2 + "$"), names };
|
|
9294
|
+
};
|
|
9295
|
+
matchRoute = function(path, routes) {
|
|
9296
|
+
let match, params;
|
|
9297
|
+
for (const route of routes) {
|
|
9298
|
+
match = path.match(route.regex.regex);
|
|
9299
|
+
if (match) {
|
|
9300
|
+
params = {};
|
|
9301
|
+
for (let i = 0;i < route.regex.names.length; i++) {
|
|
9302
|
+
const name = route.regex.names[i];
|
|
9303
|
+
params[name] = decodeURIComponent(match[i + 1]);
|
|
9304
|
+
}
|
|
9305
|
+
return { route, params };
|
|
9306
|
+
}
|
|
9307
|
+
}
|
|
9308
|
+
return null;
|
|
9309
|
+
};
|
|
9310
|
+
buildRoutes = function(components, root = "components") {
|
|
9311
|
+
let allFiles, dir, layouts, name, regex, rel, routes, segs, urlPattern;
|
|
9312
|
+
routes = [];
|
|
9313
|
+
layouts = new Map;
|
|
9314
|
+
allFiles = components.listAll(root);
|
|
9315
|
+
for (const filePath of allFiles) {
|
|
9316
|
+
rel = filePath.slice(root.length + 1);
|
|
9317
|
+
if (!rel.endsWith(".rip"))
|
|
9318
|
+
continue;
|
|
9319
|
+
name = rel.split("/").pop();
|
|
9320
|
+
if (name === "_layout.rip") {
|
|
9321
|
+
dir = rel === "_layout.rip" ? "" : rel.slice(0, -"/_layout.rip".length);
|
|
9322
|
+
layouts.set(dir, filePath);
|
|
9323
|
+
continue;
|
|
9324
|
+
}
|
|
9325
|
+
if (name.startsWith("_"))
|
|
9326
|
+
continue;
|
|
9327
|
+
segs = rel.split("/");
|
|
9328
|
+
if (segs.length > 1 && segs.some(function(s, i) {
|
|
9329
|
+
return i < segs.length - 1 && s.startsWith("_");
|
|
9330
|
+
}))
|
|
9331
|
+
continue;
|
|
9332
|
+
urlPattern = fileToPattern(rel);
|
|
9333
|
+
regex = patternToRegex(urlPattern);
|
|
9334
|
+
routes.push({ pattern: urlPattern, regex, file: filePath, rel });
|
|
9335
|
+
}
|
|
9336
|
+
routes.sort(function(a, b) {
|
|
9337
|
+
let aCatch, aDyn, bCatch, bDyn;
|
|
9338
|
+
aDyn = (a.pattern.match(/:/g) || []).length;
|
|
9339
|
+
bDyn = (b.pattern.match(/:/g) || []).length;
|
|
9340
|
+
aCatch = a.pattern.includes("*") ? 1 : 0;
|
|
9341
|
+
bCatch = b.pattern.includes("*") ? 1 : 0;
|
|
9342
|
+
if (aCatch !== bCatch)
|
|
9343
|
+
return aCatch - bCatch;
|
|
9344
|
+
if (aDyn !== bDyn)
|
|
9345
|
+
return aDyn - bDyn;
|
|
9346
|
+
return a.pattern.localeCompare(b.pattern);
|
|
9347
|
+
});
|
|
9348
|
+
return { routes, layouts };
|
|
9349
|
+
};
|
|
9350
|
+
getLayoutChain = function(routeFile, root, layouts) {
|
|
9351
|
+
let chain, dir, rel, segments;
|
|
9352
|
+
chain = [];
|
|
9353
|
+
rel = routeFile.slice(root.length + 1);
|
|
9354
|
+
segments = rel.split("/");
|
|
9355
|
+
dir = "";
|
|
9356
|
+
if (layouts.has(""))
|
|
9357
|
+
chain.push(layouts.get(""));
|
|
9358
|
+
for (let i = 0;i < segments.length; i++) {
|
|
9359
|
+
const seg = segments[i];
|
|
9360
|
+
if (i === segments.length - 1)
|
|
9361
|
+
break;
|
|
9362
|
+
dir = dir ? dir + "/" + seg : seg;
|
|
9363
|
+
if (layouts.has(dir))
|
|
9364
|
+
chain.push(layouts.get(dir));
|
|
9365
|
+
}
|
|
9366
|
+
return chain;
|
|
9367
|
+
};
|
|
9488
9368
|
var createRouter = function(components, opts = {}) {
|
|
9489
9369
|
let _hash, _layouts, _navigating, _params, _path, _query, _route, addBase, base, hashMode, navCallbacks, onClick, onError, onPopState, readUrl, resolve, root, router, stripBase, tree, writeUrl;
|
|
9490
9370
|
root = opts.root || "components";
|
|
@@ -9633,6 +9513,110 @@ ${indented}`);
|
|
|
9633
9513
|
} });
|
|
9634
9514
|
return router;
|
|
9635
9515
|
};
|
|
9516
|
+
arraysEqual = function(a, b) {
|
|
9517
|
+
if (a.length !== b.length)
|
|
9518
|
+
return false;
|
|
9519
|
+
for (let i = 0;i < a.length; i++) {
|
|
9520
|
+
const item = a[i];
|
|
9521
|
+
if (item !== b[i])
|
|
9522
|
+
return false;
|
|
9523
|
+
}
|
|
9524
|
+
return true;
|
|
9525
|
+
};
|
|
9526
|
+
findComponent = function(mod) {
|
|
9527
|
+
for (const key in mod) {
|
|
9528
|
+
const val = mod[key];
|
|
9529
|
+
if (typeof val === "function" && (val.prototype?.mount || val.prototype?._create))
|
|
9530
|
+
return val;
|
|
9531
|
+
}
|
|
9532
|
+
return typeof mod.default === "function" ? mod.default : undefined;
|
|
9533
|
+
};
|
|
9534
|
+
findAllComponents = function(mod) {
|
|
9535
|
+
let result;
|
|
9536
|
+
result = {};
|
|
9537
|
+
for (const key in mod) {
|
|
9538
|
+
const val = mod[key];
|
|
9539
|
+
if (typeof val === "function" && (val.prototype?.mount || val.prototype?._create)) {
|
|
9540
|
+
result[key] = val;
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
return result;
|
|
9544
|
+
};
|
|
9545
|
+
fileToComponentName = function(filePath) {
|
|
9546
|
+
let name;
|
|
9547
|
+
name = filePath.split("/").pop().replace(/\.rip$/, "");
|
|
9548
|
+
return name.replace(/(^|[-_])([a-z])/g, function(_, sep, ch) {
|
|
9549
|
+
return ch.toUpperCase();
|
|
9550
|
+
});
|
|
9551
|
+
};
|
|
9552
|
+
buildComponentMap = function(components, root = "components") {
|
|
9553
|
+
let fileName, map, name;
|
|
9554
|
+
map = {};
|
|
9555
|
+
for (const path of components.listAll(root)) {
|
|
9556
|
+
if (!path.endsWith(".rip"))
|
|
9557
|
+
continue;
|
|
9558
|
+
fileName = path.split("/").pop();
|
|
9559
|
+
if (fileName.startsWith("_"))
|
|
9560
|
+
continue;
|
|
9561
|
+
name = fileToComponentName(path);
|
|
9562
|
+
if (map[name]) {
|
|
9563
|
+
console.warn(`[Rip] Component name collision: ${name} (${map[name]} vs ${path})`);
|
|
9564
|
+
}
|
|
9565
|
+
map[name] = path;
|
|
9566
|
+
}
|
|
9567
|
+
return map;
|
|
9568
|
+
};
|
|
9569
|
+
compileAndImport = async function(source, compile2, components = null, path = null, resolver = null) {
|
|
9570
|
+
let blob, cached, depMod, depSource, found, header, js, mod, names, needed, preamble, url;
|
|
9571
|
+
if (components && path) {
|
|
9572
|
+
cached = components.getCompiled(path);
|
|
9573
|
+
if (cached)
|
|
9574
|
+
return cached;
|
|
9575
|
+
}
|
|
9576
|
+
js = compile2(source);
|
|
9577
|
+
if (resolver) {
|
|
9578
|
+
needed = {};
|
|
9579
|
+
for (const name in resolver.map) {
|
|
9580
|
+
const depPath = resolver.map[name];
|
|
9581
|
+
if (depPath !== path && js.includes(`new ${name}(`)) {
|
|
9582
|
+
if (!resolver.classes[name]) {
|
|
9583
|
+
depSource = components.read(depPath);
|
|
9584
|
+
if (depSource) {
|
|
9585
|
+
depMod = await compileAndImport(depSource, compile2, components, depPath, resolver);
|
|
9586
|
+
found = findAllComponents(depMod);
|
|
9587
|
+
for (const k in found) {
|
|
9588
|
+
const v = found[k];
|
|
9589
|
+
resolver.classes[k] = v;
|
|
9590
|
+
}
|
|
9591
|
+
}
|
|
9592
|
+
}
|
|
9593
|
+
if (resolver.classes[name])
|
|
9594
|
+
needed[name] = true;
|
|
9595
|
+
}
|
|
9596
|
+
}
|
|
9597
|
+
names = Object.keys(needed);
|
|
9598
|
+
if (names.length > 0) {
|
|
9599
|
+
preamble = `const {${names.join(", ")}} = globalThis['${resolver.key}'];
|
|
9600
|
+
`;
|
|
9601
|
+
js = preamble + js;
|
|
9602
|
+
}
|
|
9603
|
+
}
|
|
9604
|
+
header = path ? `// ${path}
|
|
9605
|
+
` : "";
|
|
9606
|
+
blob = new Blob([header + js], { type: "application/javascript" });
|
|
9607
|
+
url = URL.createObjectURL(blob);
|
|
9608
|
+
mod = await import(url);
|
|
9609
|
+
if (resolver) {
|
|
9610
|
+
found = findAllComponents(mod);
|
|
9611
|
+
for (const k in found) {
|
|
9612
|
+
const v = found[k];
|
|
9613
|
+
resolver.classes[k] = v;
|
|
9614
|
+
}
|
|
9615
|
+
}
|
|
9616
|
+
if (components && path)
|
|
9617
|
+
components.setCompiled(path, mod);
|
|
9618
|
+
return mod;
|
|
9619
|
+
};
|
|
9636
9620
|
var createRenderer = function(opts = {}) {
|
|
9637
9621
|
let app, cacheComponent, compile2, componentCache, components, container, currentComponent, currentLayouts, currentParams, currentRoute, disposeEffect, generation, layoutInstances, maxCacheSize, mountPoint, mountRoute, onError, renderer, resolver, router, target, unmount;
|
|
9638
9622
|
({ router, app, components, resolver, compile: compile2, target, onError } = opts);
|
|
@@ -9845,6 +9829,36 @@ ${indented}`);
|
|
|
9845
9829
|
}, cache: componentCache };
|
|
9846
9830
|
return renderer;
|
|
9847
9831
|
};
|
|
9832
|
+
connectWatch = function(url) {
|
|
9833
|
+
let connect, maxDelay, retryDelay;
|
|
9834
|
+
retryDelay = 1000;
|
|
9835
|
+
maxDelay = 30000;
|
|
9836
|
+
connect = function() {
|
|
9837
|
+
let es;
|
|
9838
|
+
es = new EventSource(url);
|
|
9839
|
+
es.addEventListener("connected", function() {
|
|
9840
|
+
retryDelay = 1000;
|
|
9841
|
+
return console.log("[Rip] Hot reload connected");
|
|
9842
|
+
});
|
|
9843
|
+
es.addEventListener("reload", function() {
|
|
9844
|
+
console.log("[Rip] Reloading...");
|
|
9845
|
+
return location.reload();
|
|
9846
|
+
});
|
|
9847
|
+
es.addEventListener("css", function() {
|
|
9848
|
+
for (const link of document.querySelectorAll('link[rel="stylesheet"]')) {
|
|
9849
|
+
url = new URL(link.href);
|
|
9850
|
+
url.searchParams.set("_t", Date.now());
|
|
9851
|
+
link.href = url.toString();
|
|
9852
|
+
}
|
|
9853
|
+
});
|
|
9854
|
+
return es.onerror = function() {
|
|
9855
|
+
es.close();
|
|
9856
|
+
setTimeout(connect, retryDelay);
|
|
9857
|
+
return retryDelay = Math.min(retryDelay * 2, maxDelay);
|
|
9858
|
+
};
|
|
9859
|
+
};
|
|
9860
|
+
return connect();
|
|
9861
|
+
};
|
|
9848
9862
|
var launch = async function(appBase = "", opts = {}) {
|
|
9849
9863
|
let _save, _storage, _storageKey, app, appComponents, bundle, cached, classesKey, compile2, el, etag, etagKey, hash, headers, persist, renderer, res, resolver, router, saved, savedData, target;
|
|
9850
9864
|
globalThis.__ripLaunched = true;
|