@lark.js/mvc 0.0.6 → 0.0.8

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/index.js CHANGED
@@ -795,6 +795,9 @@ function attachViewAndPath(loc) {
795
795
  if (!loc.view) {
796
796
  const rawPath = routeMode === "history" ? loc.query["path"] || loc.hash["path"] : loc.hash["path"];
797
797
  let path = rawPath || cachedDefaultPath || "/";
798
+ if (!cachedRoutes[path] && path === "/" && cachedDefaultPath && cachedDefaultPath !== "/") {
799
+ path = cachedDefaultPath;
800
+ }
798
801
  if (cachedRewrite) {
799
802
  path = cachedRewrite(
800
803
  path,
@@ -863,6 +866,10 @@ function getChanged(oldLoc, newLoc) {
863
866
  function updateBrowserUrl(path, replace) {
864
867
  if (routeMode === "history") {
865
868
  const url = path || "/";
869
+ const currentUrl = window.location.pathname + window.location.search;
870
+ if (url === currentUrl) {
871
+ return;
872
+ }
866
873
  if (replace) {
867
874
  window.history.replaceState(null, "", url);
868
875
  } else {
@@ -884,6 +891,9 @@ function updateUrl(path, params, loc, replace, silentFlag, lQuery) {
884
891
  if (path !== currentSrc) {
885
892
  silent = silentFlag ? 1 : 0;
886
893
  updateBrowserUrl(path, replace);
894
+ if (routeMode === "history" && Router.notify) {
895
+ Router.notify();
896
+ }
887
897
  }
888
898
  }
889
899
  var Router = {
@@ -2151,28 +2161,28 @@ var View = class _View {
2151
2161
  // ============================================================
2152
2162
  // Static public methods
2153
2163
  // ============================================================
2154
- /** Collected ctors from mixins */
2155
- static ctors;
2164
+ /** Collected makes from mixins */
2165
+ static makes;
2156
2166
  /**
2157
2167
  * Prepare a View subclass by scanning its prototype for event method patterns.
2158
2168
  * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2159
2169
  *
2160
- * Only runs once per View subclass (guarded by ctors marker).
2170
+ * Only runs once per View subclass (guarded by makes marker).
2161
2171
  * Called from Frame.mountView before creating the view instance.
2162
2172
  */
2163
2173
  static prepare(oView) {
2164
- if (oView.ctors) {
2165
- return oView.ctors;
2174
+ if (oView.makes) {
2175
+ return oView.makes;
2166
2176
  }
2167
- const ctors = [];
2168
- oView.ctors = ctors;
2177
+ const makes = [];
2178
+ oView.makes = makes;
2169
2179
  const proto = oView.prototype;
2170
2180
  const eventsObject = {};
2171
2181
  const eventsList = [];
2172
2182
  const selectorObject = {};
2173
2183
  const mixins = proto["mixins"];
2174
2184
  if (mixins && Array.isArray(mixins)) {
2175
- _View.mergeMixins(mixins, oView, ctors);
2185
+ _View.mergeMixins(mixins, oView, makes);
2176
2186
  }
2177
2187
  for (const p in proto) {
2178
2188
  if (!hasOwnProperty(proto, p)) continue;
@@ -2242,7 +2252,7 @@ var View = class _View {
2242
2252
  proto["$globalEvtList"] = eventsList;
2243
2253
  proto["$selMap"] = selectorObject;
2244
2254
  proto["$assignFn"] = proto["assign"];
2245
- return ctors;
2255
+ return makes;
2246
2256
  }
2247
2257
  /**
2248
2258
  * Bind or unbind event delegation for a view instance.
@@ -2367,7 +2377,7 @@ var View = class _View {
2367
2377
  /**
2368
2378
  * Merge an array of mixin objects into the view prototype.
2369
2379
  */
2370
- static mergeMixins(mixins, viewClass, ctors) {
2380
+ static mergeMixins(mixins, viewClass, makes) {
2371
2381
  const proto = viewClass.prototype;
2372
2382
  const temp = {};
2373
2383
  for (const node of mixins) {
@@ -2378,7 +2388,7 @@ var View = class _View {
2378
2388
  const mixinFn = fn;
2379
2389
  const exist = temp[p];
2380
2390
  if (p === "make") {
2381
- ctors.push(mixinFn);
2391
+ makes.push(mixinFn);
2382
2392
  continue;
2383
2393
  }
2384
2394
  if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
@@ -2429,9 +2439,9 @@ var View = class _View {
2429
2439
  static extend(props, statics) {
2430
2440
  const definedProps = props ?? {};
2431
2441
  const make = definedProps["make"];
2432
- const ctors = [];
2442
+ const makes = [];
2433
2443
  if (typeof make === "function") {
2434
- ctors.push(make);
2444
+ makes.push(make);
2435
2445
  }
2436
2446
  const ParentView = this;
2437
2447
  const ChildView = class extends ParentView {
@@ -2453,7 +2463,7 @@ var View = class _View {
2453
2463
  deep: !this.template
2454
2464
  }
2455
2465
  ];
2456
- const concatCtors = ctors.concat(mixinCtors || []);
2466
+ const concatCtors = makes.concat(mixinCtors || []);
2457
2467
  if (concatCtors.length) {
2458
2468
  funcWithTry(concatCtors, params, this, noop);
2459
2469
  }
@@ -2479,7 +2489,7 @@ var View = class _View {
2479
2489
  * Merge mixins into View prototype.
2480
2490
  */
2481
2491
  static merge(...mixins) {
2482
- const existingCtors = this.ctors || [];
2492
+ const existingCtors = this.makes || [];
2483
2493
  _View.mergeMixins(mixins, this, existingCtors);
2484
2494
  return this;
2485
2495
  }
@@ -3223,13 +3233,13 @@ var Service = class {
3223
3233
  * References per-type static state from the current class.
3224
3234
  */
3225
3235
  get internals() {
3226
- const ctor = this.constructor;
3236
+ const constructor = this.constructor;
3227
3237
  return {
3228
- metaList: ctor._metaList,
3229
- payloadCache: ctor._payloadCache,
3230
- pendingCacheKeys: ctor._pendingCacheKeys,
3231
- syncFn: ctor._syncFn,
3232
- staticEmitter: ctor._staticEmitter
3238
+ metaList: constructor._metaList,
3239
+ payloadCache: constructor._payloadCache,
3240
+ pendingCacheKeys: constructor._pendingCacheKeys,
3241
+ syncFn: constructor._syncFn,
3242
+ staticEmitter: constructor._staticEmitter
3233
3243
  };
3234
3244
  }
3235
3245
  /**
@@ -3564,6 +3574,7 @@ function serviceSend(service, attrs, done, flag, save) {
3564
3574
  const payloadInfo = service.type.get(attrObj, save);
3565
3575
  const payloadEntity = payloadInfo.entity;
3566
3576
  const cacheKey = payloadEntity.cacheInfo?.key || "";
3577
+ doneArr[requestCount + 1] = payloadEntity;
3567
3578
  const complete = remoteComplete.bind(null, requestCount++);
3568
3579
  if (cacheKey && pendingCacheKeys[cacheKey]) {
3569
3580
  pendingCacheKeys[cacheKey].push(complete);
@@ -4223,755 +4234,6 @@ function bindStore(view, store, selector) {
4223
4234
  return off;
4224
4235
  }
4225
4236
  var defineStore = create;
4226
-
4227
- // src/compiler.ts
4228
- import { parse as babelParse } from "@babel/parser";
4229
- var SPLITTER2 = String.fromCharCode(30);
4230
- var VIEW_ID_PLACEHOLDER = String.fromCharCode(31);
4231
- function jsObjectToUrlParams(paramsStr) {
4232
- const trimmed = paramsStr.trim();
4233
- if (!/^[{[]/.test(trimmed) && /=/.test(trimmed)) {
4234
- return trimmed;
4235
- }
4236
- const objMatch = trimmed.match(/^\{(.*)\}$/s);
4237
- if (objMatch) {
4238
- const inner = objMatch[1];
4239
- const pairs = [];
4240
- const pairRegExp = /(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|([^,}]+))/g;
4241
- let m;
4242
- while ((m = pairRegExp.exec(inner)) !== null) {
4243
- const key = m[1];
4244
- const value = m[2] ?? m[3] ?? m[4]?.trim() ?? "";
4245
- pairs.push(`${key}=${value}`);
4246
- }
4247
- return pairs.join("&");
4248
- }
4249
- return trimmed;
4250
- }
4251
- function protectComments(source) {
4252
- const comments = [];
4253
- const protectedSource = source.replace(/<!--[\s\S]*?-->/g, (match) => {
4254
- comments.push(match);
4255
- return `__lark_comment_${comments.length - 1}__`;
4256
- });
4257
- return { protectedSource, comments };
4258
- }
4259
- function restoreComments(source, comments) {
4260
- return source.replace(/__lark_comment_(\d+)__/g, (_, index) => {
4261
- return comments[parseInt(index, 10)];
4262
- });
4263
- }
4264
- function processViewEvents(source) {
4265
- return source.replace(
4266
- /@(\w+)="([^"]+)"/g,
4267
- (fullAttr, eventName, attrValue) => {
4268
- const eventMatch = attrValue.match(/^(\w+)\((.*)\)$/s);
4269
- if (!eventMatch) return fullAttr;
4270
- const handlerName = eventMatch[1];
4271
- const paramsStr = eventMatch[2].trim();
4272
- if (!paramsStr) {
4273
- return `@${eventName}="${VIEW_ID_PLACEHOLDER}${SPLITTER2}${handlerName}()"`;
4274
- }
4275
- const urlParams = jsObjectToUrlParams(paramsStr);
4276
- return `@${eventName}="${VIEW_ID_PLACEHOLDER}${SPLITTER2}${handlerName}(${urlParams})"`;
4277
- }
4278
- );
4279
- }
4280
- function addLineMarkers(source) {
4281
- const lines = source.split(/\r\n?|\n/);
4282
- const result = [];
4283
- let lineNo = 0;
4284
- const openTag = "{{";
4285
- for (const line of lines) {
4286
- const parts = line.split(openTag);
4287
- if (parts.length > 1) {
4288
- const reconstructed = parts.map((part, i) => {
4289
- if (i === 0) return part;
4290
- return openTag + SPLITTER2 + ++lineNo + part;
4291
- }).join("");
4292
- result.push(reconstructed);
4293
- } else {
4294
- result.push(line);
4295
- }
4296
- }
4297
- return result.join("\n");
4298
- }
4299
- function extractArtInfo(art) {
4300
- const m = art.match(new RegExp(`^${SPLITTER2}(\\d+)([\\s\\S]+)`));
4301
- if (m) {
4302
- let code = m[2].trimStart();
4303
- if (code.startsWith("if(")) {
4304
- code = code.substring(0, 2) + " " + code.substring(2);
4305
- } else if (code.startsWith("for(")) {
4306
- code = code.substring(0, 3) + " " + code.substring(3);
4307
- }
4308
- return { line: parseInt(m[1], 10), art: code };
4309
- }
4310
- return null;
4311
- }
4312
- function convertArtSyntax(source, debug) {
4313
- const markedSource = debug ? addLineMarkers(source) : source;
4314
- const openTag = "{{";
4315
- const parts = markedSource.split(openTag);
4316
- const result = [parts[0]];
4317
- const blockStack = [];
4318
- for (let i = 1; i < parts.length; i++) {
4319
- const part = parts[i];
4320
- const closeIdx = findCloseBrace(part);
4321
- if (closeIdx === -1) {
4322
- result.push(openTag + part);
4323
- continue;
4324
- }
4325
- const code = part.substring(0, closeIdx);
4326
- const rest = part.substring(closeIdx + 2);
4327
- let lineNo = -1;
4328
- let cleanCode = code;
4329
- if (debug) {
4330
- const info = extractArtInfo(code);
4331
- if (info) {
4332
- lineNo = info.line;
4333
- cleanCode = info.art;
4334
- }
4335
- } else {
4336
- cleanCode = code.trim();
4337
- }
4338
- const converted = convertArtExpression(
4339
- cleanCode,
4340
- debug,
4341
- lineNo,
4342
- blockStack
4343
- );
4344
- result.push(converted);
4345
- result.push(rest);
4346
- }
4347
- if (blockStack.length > 0) {
4348
- const unclosed = blockStack.map((b) => `"${b.ctrl}" at line ${b.line}`).join(", ");
4349
- throw new Error(`[@lark.js/mvc error] unclosed block(s): ${unclosed}`);
4350
- }
4351
- return result.join("");
4352
- }
4353
- function findCloseBrace(str) {
4354
- let leftCount = 0;
4355
- let rightCount = 0;
4356
- let maybeCount = 0;
4357
- let maybeAt = -1;
4358
- for (let i = 0; i < str.length; i++) {
4359
- const c = str.charAt(i);
4360
- if (c !== "}") {
4361
- if (maybeCount >= 2 && maybeAt === -1) {
4362
- maybeAt = i;
4363
- }
4364
- maybeCount = 0;
4365
- rightCount = 0;
4366
- }
4367
- if (c === "{") {
4368
- leftCount++;
4369
- } else if (c === "}") {
4370
- maybeCount++;
4371
- if (!leftCount) {
4372
- rightCount++;
4373
- if (rightCount === 2) {
4374
- return i - 1;
4375
- }
4376
- } else {
4377
- leftCount--;
4378
- }
4379
- }
4380
- }
4381
- if (!leftCount && maybeCount >= 2 && maybeAt === -1) {
4382
- maybeAt = str.length - 2;
4383
- }
4384
- if (maybeAt > -1) {
4385
- return maybeAt - 2;
4386
- }
4387
- return -1;
4388
- }
4389
- function trimOuterParens(expr) {
4390
- expr = expr.trim();
4391
- while (expr.startsWith("(") && expr.endsWith(")")) {
4392
- let depth = 0;
4393
- let matched = true;
4394
- for (let i = 0; i < expr.length - 1; i++) {
4395
- const c = expr.charAt(i);
4396
- if (c === "(") depth++;
4397
- else if (c === ")") depth--;
4398
- if (depth === 0 && i < expr.length - 1) {
4399
- matched = false;
4400
- break;
4401
- }
4402
- }
4403
- if (!matched) break;
4404
- expr = expr.substring(1, expr.length - 1).trim();
4405
- }
4406
- return expr;
4407
- }
4408
- function convertArtExpression(code, debug, lineNo, blockStack = []) {
4409
- code = code.trim();
4410
- const debugPrefix = debug && lineNo > -1 ? `<%'${lineNo}${code.replace(/\\|'/g, "\\$&").replace(/\r\n?|\n/g, "\\n")}'%>` : "";
4411
- const ifForMatch = code.match(/^\s*(if|for)\s*\(/);
4412
- if (ifForMatch) {
4413
- const keyword2 = ifForMatch[1];
4414
- const expr = code.substring(ifForMatch[0].length);
4415
- if (keyword2 === "if") {
4416
- blockStack.push({ ctrl: "if", line: lineNo });
4417
- const rawExpr = expr.replace(/\)\s*$/, "");
4418
- const cleanExpr = trimOuterParens(rawExpr);
4419
- return `${debugPrefix}<%if(${cleanExpr}){%>`;
4420
- }
4421
- blockStack.push({ ctrl: "for", line: lineNo });
4422
- const forExpr = expr.replace(/\)\s*$/, "");
4423
- return `${debugPrefix}<%for(${forExpr}){%>`;
4424
- }
4425
- const tokens = code.split(/\s+/);
4426
- const keyword = tokens.shift() ?? "";
4427
- switch (keyword) {
4428
- case "if": {
4429
- blockStack.push({ ctrl: "if", line: lineNo });
4430
- const rawExpr = tokens.join(" ").trim();
4431
- const expr = trimOuterParens(rawExpr);
4432
- return `${debugPrefix}<%if(${expr}){%>`;
4433
- }
4434
- case "else": {
4435
- if (tokens[0] === "if") {
4436
- tokens.shift();
4437
- const rawExpr = tokens.join(" ").trim();
4438
- const expr = trimOuterParens(rawExpr);
4439
- return `${debugPrefix}<%}else if(${expr}){%>`;
4440
- }
4441
- return `${debugPrefix}<%}else{%>`;
4442
- }
4443
- case "forOf": {
4444
- blockStack.push({ ctrl: "forOf", line: lineNo });
4445
- const object = tokens[0];
4446
- if (tokens.length > 1 && tokens[1] !== "as") {
4447
- throw new Error(
4448
- `[@lark.js/mvc error] bad forOf syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forOf list as item [index]}}`
4449
- );
4450
- }
4451
- const restTokens = tokens.slice(2);
4452
- const asValue = restTokens.join(" ");
4453
- const asExpr = parseAsExpr(asValue);
4454
- const index = asExpr.key || "_i";
4455
- const refObj = /[.[\]]/.test(object) ? `_art_obj_${object.replace(/[^\w]/g, "_")}` : object;
4456
- const refExpr = /[.[\]]/.test(object) ? `,${refObj}=${object}` : "";
4457
- const refObjCount = "_l";
4458
- const valueDecl = asExpr.vars ? `let ${asExpr.vars}=${refObj}[${index}]` : "";
4459
- let firstAndLast = "";
4460
- let lastCount = "";
4461
- if (asExpr.first) {
4462
- firstAndLast += `let ${asExpr.first}=${index}===0;`;
4463
- }
4464
- if (asExpr.last) {
4465
- lastCount = `,_lc=${refObjCount}-1`;
4466
- firstAndLast += `let ${asExpr.last}=${index}===_lc;`;
4467
- }
4468
- return `${debugPrefix}<%for(let ${index}=0${refExpr},${refObjCount}=${refObj}.length${lastCount};${index}<${refObjCount};${index}++){${firstAndLast}${valueDecl}%>`;
4469
- }
4470
- case "forIn": {
4471
- blockStack.push({ ctrl: "forIn", line: lineNo });
4472
- const object = tokens[0];
4473
- if (tokens.length > 1 && tokens[1] !== "as") {
4474
- throw new Error(
4475
- `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4476
- );
4477
- }
4478
- const restTokens2 = tokens.slice(2);
4479
- const asValue2 = restTokens2.join(" ");
4480
- const asExpr2 = parseAsExpr(asValue2);
4481
- const key1 = asExpr2.key || "_k";
4482
- const refObj2 = /[.[\]]/.test(object) ? `_art_obj_${object.replace(/[^\w]/g, "_")}` : object;
4483
- const refExpr2 = /[.[\]]/.test(object) ? `let ${refObj2}=${object};` : "";
4484
- const valueDecl2 = asExpr2.vars ? `let ${asExpr2.vars}=${refObj2}[${key1}]` : "";
4485
- return `${debugPrefix}<%${refExpr2}for(let ${key1} in ${refObj2}){${valueDecl2}%>`;
4486
- }
4487
- case "for": {
4488
- blockStack.push({ ctrl: "for", line: lineNo });
4489
- const expr = tokens.join(" ").trim();
4490
- return `${debugPrefix}<%for(${expr}){%>`;
4491
- }
4492
- case "set":
4493
- return `${debugPrefix}<%let ${tokens.join(" ")};%>`;
4494
- case "/if":
4495
- case "/forOf":
4496
- case "/forIn":
4497
- case "/for": {
4498
- const expectedCtrl = keyword.substring(1);
4499
- const last = blockStack.pop();
4500
- if (!last) {
4501
- throw new Error(
4502
- `[@lark.js/mvc error] unexpected {{${code}}}: no matching open block`
4503
- );
4504
- }
4505
- if (last.ctrl !== expectedCtrl) {
4506
- throw new Error(
4507
- `[@lark.js/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4508
- );
4509
- }
4510
- return `${debugPrefix}<%}%>`;
4511
- }
4512
- default:
4513
- return `${debugPrefix}<%${code}%>`;
4514
- }
4515
- }
4516
- function parseAsExpr(expr) {
4517
- expr = expr.trim();
4518
- if (!expr) {
4519
- return { vars: "", key: "", last: "", first: "", bad: false };
4520
- }
4521
- if (expr.startsWith("{") || expr.startsWith("[")) {
4522
- const stack = [];
4523
- let vars = "";
4524
- let key = "";
4525
- let last = "";
4526
- let first = "";
4527
- let pos = 0;
4528
- let bad = false;
4529
- for (const c of expr) {
4530
- if (pos === 0) vars += c;
4531
- else if (pos === 1) key += c;
4532
- else if (pos === 2) last += c;
4533
- else if (pos === 3) first += c;
4534
- if (c === "{" || c === "[") stack.push(c);
4535
- else if (c === "}") {
4536
- if (stack[stack.length - 1] === "{") stack.pop();
4537
- else {
4538
- bad = true;
4539
- break;
4540
- }
4541
- } else if (c === "]") {
4542
- if (stack[stack.length - 1] === "[") stack.pop();
4543
- else {
4544
- bad = true;
4545
- break;
4546
- }
4547
- } else if (c === " " && !stack.length) {
4548
- pos++;
4549
- }
4550
- }
4551
- return {
4552
- vars: vars.trim(),
4553
- key: key.trim(),
4554
- last: last.trim(),
4555
- first: first.trim(),
4556
- bad: bad || stack.length > 0
4557
- };
4558
- }
4559
- const parts = expr.split(/\s+/);
4560
- return {
4561
- vars: parts[0] || "",
4562
- key: parts[1] || "",
4563
- last: parts[2] || "",
4564
- first: parts[3] || "",
4565
- bad: false
4566
- };
4567
- }
4568
- function compileToFunction(source, debug, file) {
4569
- const matcher = /<%([@=!:])?([\s\S]*?)%>|$/g;
4570
- let index = 0;
4571
- let funcSource = `$out+='`;
4572
- let hasAtRule = false;
4573
- const escapeSlashRegExp = /\\|'/g;
4574
- const escapeBreakReturnRegExp = /\r|\n/g;
4575
- source.replace(matcher, (match, operate, content, offset) => {
4576
- funcSource += source.substring(index, offset).replace(escapeSlashRegExp, "\\$&").replace(escapeBreakReturnRegExp, "\\n");
4577
- index = offset + match.length;
4578
- if (debug) {
4579
- let expr = source.substring(
4580
- index - match.length + 2 + (operate ? 1 : 0),
4581
- index - 2
4582
- );
4583
- const x11 = String.fromCharCode(17);
4584
- const artRegExp = new RegExp(`^'(\\d+)${x11}([^${x11}]+)${x11}'$`);
4585
- const artM = expr.match(artRegExp);
4586
- let art = "";
4587
- let line = -1;
4588
- if (artM) {
4589
- expr = expr.replace(artRegExp, "");
4590
- art = artM[2];
4591
- line = parseInt(artM[1], 10);
4592
- } else {
4593
- expr = expr.replace(escapeSlashRegExp, "\\$&").replace(escapeBreakReturnRegExp, "\\n");
4594
- }
4595
- if (operate === "@") {
4596
- hasAtRule = true;
4597
- funcSource += `'+($dbgExpr='<%${operate + expr}%>',$refFn($refAlt,${content}))+'`;
4598
- } else if (operate === "=" || operate === ":") {
4599
- funcSource += `'+($dbgExpr='<%${operate + expr}%>',$encHtml(${content}))+'`;
4600
- } else if (operate === "!") {
4601
- if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
4602
- content = `$strSafe(${content})`;
4603
- }
4604
- funcSource += `'+($dbgExpr='<%${operate + expr}%>',${content})+'`;
4605
- } else if (content) {
4606
- if (line > -1) {
4607
- funcSource += `';$dbgLine=${line};$dbgArt='${art}';`;
4608
- content = "";
4609
- } else {
4610
- funcSource += `';`;
4611
- }
4612
- if (funcSource.endsWith(`+'';`)) {
4613
- funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4614
- }
4615
- if (expr) {
4616
- funcSource += `$dbgExpr='<%${expr}%>';`;
4617
- }
4618
- funcSource += content + `;$out+='`;
4619
- }
4620
- } else {
4621
- if (operate === "@") {
4622
- hasAtRule = true;
4623
- funcSource += `'+$refFn($refAlt,${content})+'`;
4624
- } else if (operate === "=" || operate === ":") {
4625
- funcSource += `'+$encHtml(${content})+'`;
4626
- } else if (operate === "!") {
4627
- if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
4628
- content = `$strSafe(${content})`;
4629
- }
4630
- funcSource += `'+${content}+'`;
4631
- } else if (content) {
4632
- funcSource += `';`;
4633
- if (funcSource.endsWith(`+'';`)) {
4634
- funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4635
- }
4636
- funcSource += `${content};$out+='`;
4637
- }
4638
- }
4639
- return match;
4640
- });
4641
- funcSource += `';`;
4642
- funcSource = funcSource.replace(/\$out\+='';/g, "");
4643
- funcSource = funcSource.replace(/\$out\+=''\+/g, "$out+=");
4644
- if (debug) {
4645
- const filePart = file ? `\\r\\n\\tat file:${file}` : "";
4646
- funcSource = `let $dbgExpr,$dbgArt,$dbgLine;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($dbgArt)msg+='\\r\\n\\tsrc art:{{'+$dbgArt+'}}\\r\\n\\tat line:'+$dbgLine;msg+='\\r\\n\\t'+($dbgArt?'translate to:':'expr:');msg+=$dbgExpr+'${filePart}';throw msg;}`;
4647
- }
4648
- const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4649
- funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4650
- void hasAtRule;
4651
- const refFallback = "if(!$refAlt)$refAlt=$data;";
4652
- const fullSource = `${refFallback}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4653
- return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4654
- }
4655
- function compileTemplate(source, options = {}) {
4656
- const { debug = false, globalVars = [], file } = options;
4657
- const { protectedSource, comments } = protectComments(source);
4658
- const converted = convertArtSyntax(protectedSource, debug);
4659
- const viewEventProcessed = processViewEvents(converted);
4660
- const finalSource = restoreComments(viewEventProcessed, comments);
4661
- const funcBody = compileToFunction(finalSource, debug, file);
4662
- const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4663
- const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4664
- return `import { encHtml as __larkEncHtml, strSafe as __larkStrSafe, encUri as __larkEncUri, encQuote as __larkEncQuote, refFn as __larkRefFn } from "@lark.js/mvc/runtime";
4665
- export default function(data, viewId, refData) {
4666
- let $data = data || {},
4667
- $viewId = viewId || '';
4668
- return (${funcWithVars})($data, $viewId, refData,
4669
- __larkEncHtml, __larkStrSafe, __larkEncUri, __larkRefFn, __larkEncQuote
4670
- );
4671
- }`;
4672
- }
4673
- function extractGlobalVars(source) {
4674
- const { protectedSource, comments: _comments } = protectComments(source);
4675
- const viewEventProcessed = processViewEvents(protectedSource);
4676
- const converted = convertArtSyntax(viewEventProcessed, false);
4677
- const template = restoreComments(converted, _comments);
4678
- const templateCmdRegExp = /<%([@=!:])?([\s\S]*?)%>|$/g;
4679
- const fnParts = [];
4680
- const htmlStore = {};
4681
- let htmlIndex = 0;
4682
- let lastIndex = 0;
4683
- const htmlKey = String.fromCharCode(5);
4684
- template.replace(
4685
- templateCmdRegExp,
4686
- (match, operate, content, offset) => {
4687
- const start = operate ? 3 : 2;
4688
- const htmlText = template.substring(lastIndex, offset + start);
4689
- const key = htmlKey + htmlIndex++ + htmlKey;
4690
- htmlStore[key] = htmlText;
4691
- lastIndex = offset + match.length - 2;
4692
- if (operate && content.trim()) {
4693
- fnParts.push(';"' + key + '";', "[" + content + "]");
4694
- } else {
4695
- fnParts.push(';"' + key + '";', content || "");
4696
- }
4697
- return match;
4698
- }
4699
- );
4700
- let fn = fnParts.join("");
4701
- fn = `(function(){${fn}})`;
4702
- let ast;
4703
- try {
4704
- ast = babelParse(fn, {
4705
- sourceType: "script",
4706
- allowReturnOutsideFunction: true,
4707
- allowAwaitOutsideFunction: true
4708
- });
4709
- } catch {
4710
- return fallbackExtractVariables(source);
4711
- }
4712
- const globalExists = { ...BUILTIN_GLOBALS };
4713
- const globalVars = /* @__PURE__ */ Object.create(null);
4714
- const fnRange = [];
4715
- walkAst(ast, {
4716
- VariableDeclarator(node) {
4717
- if (node.id.type === "Identifier") {
4718
- const name = node.id.name;
4719
- globalExists[name] = node.init ? 3 : 2;
4720
- }
4721
- },
4722
- FunctionDeclaration(node) {
4723
- if (node.id) {
4724
- globalExists[node.id.name] = 3;
4725
- }
4726
- fnRange.push(node);
4727
- },
4728
- FunctionExpression(node) {
4729
- fnRange.push(node);
4730
- },
4731
- ArrowFunctionExpression(node) {
4732
- fnRange.push(node);
4733
- },
4734
- CallExpression(node) {
4735
- if (node.callee.type === "Identifier") {
4736
- globalExists[node.callee.name] = 1;
4737
- }
4738
- }
4739
- });
4740
- const functionParams = /* @__PURE__ */ Object.create(null);
4741
- for (const fnNode of fnRange) {
4742
- const params = "params" in fnNode ? fnNode.params : [];
4743
- for (const p of params) {
4744
- if (p.type === "Identifier") {
4745
- functionParams[p.name] = 1;
4746
- } else if (p.type === "AssignmentPattern" && p.left.type === "Identifier") {
4747
- functionParams[p.left.name] = 1;
4748
- } else if (p.type === "RestElement" && p.argument.type === "Identifier") {
4749
- functionParams[p.argument.name] = 1;
4750
- }
4751
- }
4752
- }
4753
- walkAst(ast, {
4754
- Identifier(node) {
4755
- const name = node.name;
4756
- if (globalExists[name]) return;
4757
- if (functionParams[name]) return;
4758
- globalVars[name] = 1;
4759
- },
4760
- AssignmentExpression(node) {
4761
- if (node.left.type === "Identifier") {
4762
- const name = node.left.name;
4763
- if (!globalExists[name] || globalExists[name] === 1) {
4764
- globalExists[name] = (globalExists[name] || 0) + 1;
4765
- }
4766
- }
4767
- }
4768
- });
4769
- return Object.keys(globalVars);
4770
- }
4771
- function fallbackExtractVariables(source) {
4772
- const vars = /* @__PURE__ */ new Set();
4773
- const outputRegExp = /\{\{[:=!@]\s*([a-zA-Z_$][\w$]*)[^}]*\}\}/g;
4774
- let m;
4775
- while ((m = outputRegExp.exec(source)) !== null) {
4776
- vars.add(m[1]);
4777
- }
4778
- const eachRegExp = /\{\{forOf\s+([a-zA-Z_$][\w$]*)\s+as/g;
4779
- while ((m = eachRegExp.exec(source)) !== null) {
4780
- vars.add(m[1]);
4781
- }
4782
- const ifRegExp = /\{\{(?:else\s+)?if\s+([a-zA-Z_$][\w$]*)[^}]*\}\}/g;
4783
- while ((m = ifRegExp.exec(source)) !== null) {
4784
- vars.add(m[1]);
4785
- }
4786
- return Array.from(vars).filter((v) => !BUILTIN_GLOBAL_SET.has(v));
4787
- }
4788
- function walkAst(ast, visitors) {
4789
- function visit(node) {
4790
- const type = node.type;
4791
- if (visitors[type]) {
4792
- visitors[type](node);
4793
- }
4794
- const bag = node;
4795
- for (const key of Object.keys(node)) {
4796
- if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
4797
- continue;
4798
- if (type === "MemberExpression" && key === "property") {
4799
- const me = node;
4800
- if (!me.computed) continue;
4801
- }
4802
- if (type === "ObjectProperty" && key === "key") {
4803
- const op = node;
4804
- if (!op.computed) continue;
4805
- }
4806
- if (type === "ObjectMethod" && key === "key") {
4807
- const om = node;
4808
- if (!om.computed) continue;
4809
- }
4810
- const child = bag[key];
4811
- if (Array.isArray(child)) {
4812
- for (const item of child) {
4813
- if (isAstNode(item)) visit(item);
4814
- }
4815
- } else if (isAstNode(child)) {
4816
- visit(child);
4817
- }
4818
- }
4819
- }
4820
- visit(ast);
4821
- }
4822
- function isAstNode(v) {
4823
- return !!v && typeof v === "object" && typeof v.type === "string";
4824
- }
4825
- var BUILTIN_GLOBALS = {
4826
- // ─── Template runtime helpers (injected by compileToFunction) ───────
4827
- //
4828
- // These variables appear in the generated template function signature
4829
- // or body. They must be excluded from extractGlobalVars() so that
4830
- // they are not mistaken for user data variables and destructured from $data.
4831
- // SPLITTER character constant (same as \x1e), used as namespace separator
4832
- // for refData keys, event attribute encoding, and internal data structures.
4833
- // Declared as: let $splitter='\x1e'
4834
- $splitter: 1,
4835
- // Data — the data object passed from Updater to the template function.
4836
- // User variables are destructured from $data at the top of the function:
4837
- // let {name, age} = $data;
4838
- // This is the first parameter of the generated arrow function.
4839
- $data: 1,
4840
- // Null-safe toString: v => '' + (v == null ? '' : v)
4841
- // Converts null/undefined to empty string, otherwise calls toString().
4842
- // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
4843
- $strSafe: 1,
4844
- // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
4845
- // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
4846
- // Applied to all {{=escaped}} and {{:binding}} outputs.
4847
- $encHtml: 1,
4848
- // HTML entity map — internal object used by $encHtml:
4849
- // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
4850
- // Not a standalone function; referenced inside $encHtml's closure.
4851
- $entMap: 1,
4852
- // HTML entity RegExp — internal regexp used by $encHtml:
4853
- // /[&<>"'`]/g
4854
- $entReg: 1,
4855
- // HTML entity replacer function — internal helper used by $encHtml:
4856
- // m => '&' + $entMap[m] + ';'
4857
- // Maps matched character to its entity string.
4858
- $entFn: 1,
4859
- // Output buffer — the string accumulator for rendered HTML.
4860
- // All template output is appended via $out += '...'.
4861
- // Declared as: let $out = ''
4862
- $out: 1,
4863
- // Reference lookup: (refData, value) => key
4864
- // Finds or allocates a SPLITTER-prefixed key in refData for a given
4865
- // object reference. Used by {{@ref}} operator for passing object
4866
- // references to child views via v-lark attributes.
4867
- $refFn: 1,
4868
- // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
4869
- // Extends encodeURIComponent with encoding of ! ' ( ) *.
4870
- // Applied to values in @event URL parameters and {{!uri}} contexts.
4871
- $encUri: 1,
4872
- // URI encode map — internal object used by $encUri:
4873
- // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
4874
- $uriMap: 1,
4875
- // URI encode replacer — internal helper used by $encUri:
4876
- // m => $uriMap[m]
4877
- $uriFn: 1,
4878
- // URI encode regexp — internal regexp used by $encUri:
4879
- // /[!')(*]/g
4880
- $uriReg: 1,
4881
- // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
4882
- // Escapes quotes and backslashes for safe embedding in HTML attribute
4883
- // values (e.g. data-json='...').
4884
- $encQuote: 1,
4885
- // Quote encode regexp — internal regexp used by $encQuote:
4886
- // /['"\\]/g
4887
- $qReg: 1,
4888
- // View ID — the unique identifier of the owning View instance.
4889
- // Injected into @event attribute values at render time so that
4890
- // EventDelegator can dispatch events to the correct View handler.
4891
- // The \x1f placeholder in compiled output is replaced with '+$viewId+'.
4892
- $viewId: 1,
4893
- // Debug: current expression text — stores the template expression being
4894
- // evaluated, for error reporting. Only present in debug mode.
4895
- // e.g. $dbgExpr='<%=user.name%>'
4896
- $dbgExpr: 1,
4897
- // Debug: original art syntax — stores the {{}} template syntax before
4898
- // conversion, for error reporting. Only present in debug mode.
4899
- // e.g. $dbgArt='{{=user.name}}'
4900
- $dbgArt: 1,
4901
- // Debug: source line number — tracks the current line in the template
4902
- // source, for error reporting. Only present in debug mode.
4903
- $dbgLine: 1,
4904
- // RefData alias — fallback reference lookup table.
4905
- // Defaults to $data when no explicit $refAlt is provided.
4906
- // Ensures $refFn() does not crash when @ operator is used without refData.
4907
- $refAlt: 1,
4908
- // Temporary variable — used by the compiler for intermediate
4909
- // expression results in generated code (e.g. loop variables,
4910
- // conditional branches). Declared as: let $tmp
4911
- $tmp: 1,
4912
- // JS literals
4913
- undefined: 1,
4914
- null: 1,
4915
- true: 1,
4916
- false: 1,
4917
- NaN: 1,
4918
- Infinity: 1,
4919
- // JS built-in globals
4920
- window: 1,
4921
- self: 1,
4922
- globalThis: 1,
4923
- document: 1,
4924
- console: 1,
4925
- JSON: 1,
4926
- Math: 1,
4927
- Intl: 1,
4928
- Promise: 1,
4929
- Symbol: 1,
4930
- Number: 1,
4931
- String: 1,
4932
- Boolean: 1,
4933
- Array: 1,
4934
- Object: 1,
4935
- Date: 1,
4936
- RegExp: 1,
4937
- Error: 1,
4938
- TypeError: 1,
4939
- RangeError: 1,
4940
- SyntaxError: 1,
4941
- Map: 1,
4942
- Set: 1,
4943
- WeakMap: 1,
4944
- WeakSet: 1,
4945
- Proxy: 1,
4946
- Reflect: 1,
4947
- ArrayBuffer: 1,
4948
- DataView: 1,
4949
- Float32Array: 1,
4950
- Float64Array: 1,
4951
- Int8Array: 1,
4952
- Int16Array: 1,
4953
- Int32Array: 1,
4954
- Uint8Array: 1,
4955
- Uint16Array: 1,
4956
- Uint32Array: 1,
4957
- Uint8ClampedArray: 1,
4958
- // Functions
4959
- parseInt: 1,
4960
- parseFloat: 1,
4961
- isNaN: 1,
4962
- isFinite: 1,
4963
- encodeURIComponent: 1,
4964
- decodeURIComponent: 1,
4965
- encodeURI: 1,
4966
- decodeURI: 1,
4967
- // Babel helpers
4968
- arguments: 1,
4969
- this: 1,
4970
- require: 1,
4971
- // Lark framework
4972
- Lark: 1
4973
- };
4974
- var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
4975
4237
  export {
4976
4238
  CALL_BREAK_TIME,
4977
4239
  Cache,
@@ -4998,7 +4260,6 @@ export {
4998
4260
  applyVdomOps,
4999
4261
  assign,
5000
4262
  bindStore,
5001
- compileTemplate,
5002
4263
  computed,
5003
4264
  create,
5004
4265
  createVdomRef,
@@ -5009,7 +4270,6 @@ export {
5009
4270
  encodeSafe,
5010
4271
  encodeURIExtra,
5011
4272
  ensureElementId,
5012
- extractGlobalVars,
5013
4273
  config as frameworkConfig,
5014
4274
  funcWithTry,
5015
4275
  generateId,