@weapp-vite/web 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -30,6 +30,7 @@ import MagicString from "magic-string";
30
30
  import { dirname as dirname2, extname, join, normalize, posix, relative as relative2, resolve } from "pathe";
31
31
 
32
32
  // src/compiler/wxml.ts
33
+ import { readFileSync } from "fs";
33
34
  import { parseDocument } from "htmlparser2";
34
35
  import { dirname, relative } from "pathe";
35
36
 
@@ -96,6 +97,8 @@ function normalizeTagName(name) {
96
97
  return "select";
97
98
  case "block":
98
99
  return "#fragment";
100
+ case "slot":
101
+ return "slot";
99
102
  default:
100
103
  return name || "div";
101
104
  }
@@ -111,6 +114,13 @@ function normalizeAttributeName(name) {
111
114
  }
112
115
 
113
116
  // src/compiler/wxml.ts
117
+ function shouldMarkWxsImport(pathname) {
118
+ const lower = pathname.toLowerCase();
119
+ if (lower.endsWith(".wxs") || lower.endsWith(".wxs.ts") || lower.endsWith(".wxs.js")) {
120
+ return false;
121
+ }
122
+ return lower.endsWith(".ts") || lower.endsWith(".js");
123
+ }
114
124
  function isRenderableNode(node) {
115
125
  if (node.type === "directive" || node.type === "comment") {
116
126
  return false;
@@ -317,6 +327,140 @@ function buildExpression(parts, scopeVar, wxsVar) {
317
327
  });
318
328
  return `(${segments.join(" + ")})`;
319
329
  }
330
+ function hasTopLevelColon(expression) {
331
+ let depth = 0;
332
+ let inSingleQuote = false;
333
+ let inDoubleQuote = false;
334
+ let inTemplate = false;
335
+ let escaped = false;
336
+ let sawTopLevelQuestion = false;
337
+ for (let index = 0; index < expression.length; index += 1) {
338
+ const char = expression[index];
339
+ if (inSingleQuote) {
340
+ if (escaped) {
341
+ escaped = false;
342
+ continue;
343
+ }
344
+ if (char === "\\") {
345
+ escaped = true;
346
+ continue;
347
+ }
348
+ if (char === "'") {
349
+ inSingleQuote = false;
350
+ }
351
+ continue;
352
+ }
353
+ if (inDoubleQuote) {
354
+ if (escaped) {
355
+ escaped = false;
356
+ continue;
357
+ }
358
+ if (char === "\\") {
359
+ escaped = true;
360
+ continue;
361
+ }
362
+ if (char === '"') {
363
+ inDoubleQuote = false;
364
+ }
365
+ continue;
366
+ }
367
+ if (inTemplate) {
368
+ if (escaped) {
369
+ escaped = false;
370
+ continue;
371
+ }
372
+ if (char === "\\") {
373
+ escaped = true;
374
+ continue;
375
+ }
376
+ if (char === "`") {
377
+ inTemplate = false;
378
+ }
379
+ continue;
380
+ }
381
+ if (char === "'") {
382
+ inSingleQuote = true;
383
+ continue;
384
+ }
385
+ if (char === '"') {
386
+ inDoubleQuote = true;
387
+ continue;
388
+ }
389
+ if (char === "`") {
390
+ inTemplate = true;
391
+ continue;
392
+ }
393
+ if (char === "(" || char === "[" || char === "{") {
394
+ depth += 1;
395
+ continue;
396
+ }
397
+ if (char === ")" || char === "]" || char === "}") {
398
+ depth = Math.max(0, depth - 1);
399
+ continue;
400
+ }
401
+ if (depth !== 0) {
402
+ continue;
403
+ }
404
+ if (char === "?") {
405
+ sawTopLevelQuestion = true;
406
+ continue;
407
+ }
408
+ if (char === ":") {
409
+ return !sawTopLevelQuestion;
410
+ }
411
+ }
412
+ return false;
413
+ }
414
+ function shouldWrapShorthandObject(expression) {
415
+ const trimmed = expression.trim();
416
+ if (!trimmed) {
417
+ return false;
418
+ }
419
+ if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith("(")) {
420
+ return false;
421
+ }
422
+ return hasTopLevelColon(trimmed);
423
+ }
424
+ function buildTemplateDataExpression(raw, scopeVar, wxsVar) {
425
+ const trimmed = raw.trim();
426
+ const parts = parseInterpolations(trimmed);
427
+ if (parts.length === 1 && parts[0]?.type === "expr") {
428
+ const expr = parts[0].value.trim();
429
+ if (expr) {
430
+ const normalizedExpr = shouldWrapShorthandObject(expr) ? `{ ${expr} }` : expr;
431
+ return buildExpression([{ type: "expr", value: normalizedExpr }], scopeVar, wxsVar);
432
+ }
433
+ }
434
+ return buildExpression(parseInterpolations(raw), scopeVar, wxsVar);
435
+ }
436
+ function createDependencyContext() {
437
+ return {
438
+ warnings: [],
439
+ dependencies: [],
440
+ dependencySet: /* @__PURE__ */ new Set(),
441
+ visited: /* @__PURE__ */ new Set(),
442
+ active: /* @__PURE__ */ new Set(),
443
+ circularWarnings: /* @__PURE__ */ new Set()
444
+ };
445
+ }
446
+ function addDependency(value, context, direct) {
447
+ if (!context.dependencySet.has(value)) {
448
+ context.dependencySet.add(value);
449
+ context.dependencies.push(value);
450
+ direct?.push(value);
451
+ }
452
+ }
453
+ function warnReadTemplate(context, target) {
454
+ context.warnings.push(`[web] \u65E0\u6CD5\u8BFB\u53D6\u6A21\u677F\u4F9D\u8D56: ${target}`);
455
+ }
456
+ function warnCircularTemplate(context, from, target) {
457
+ const key = `${from}=>${target}`;
458
+ if (context.circularWarnings.has(key)) {
459
+ return;
460
+ }
461
+ context.circularWarnings.add(key);
462
+ context.warnings.push(`[web] WXML \u5FAA\u73AF\u5F15\u7528: ${from} -> ${target}`);
463
+ }
320
464
  function extractFor(attribs) {
321
465
  const expr = attribs["wx:for"];
322
466
  const itemName = attribs["wx:for-item"]?.trim() || "item";
@@ -491,7 +635,7 @@ var Renderer = class {
491
635
  renderTemplateInvoke(node, scopeVar, wxsVar, _componentTags) {
492
636
  const attribs = node.attribs ?? {};
493
637
  const isExpr = buildExpression(parseInterpolations(attribs.is ?? ""), scopeVar, wxsVar);
494
- const dataExpr = attribs.data ? buildExpression(parseInterpolations(attribs.data), scopeVar, wxsVar) : void 0;
638
+ const dataExpr = attribs.data ? buildTemplateDataExpression(attribs.data, scopeVar, wxsVar) : void 0;
495
639
  const scopeExpr = dataExpr ? `ctx.mergeScope(${scopeVar}, ${dataExpr})` : scopeVar;
496
640
  return `ctx.renderTemplate(__templates, ${isExpr}, ${scopeExpr}, ctx)`;
497
641
  }
@@ -546,29 +690,38 @@ function collectSpecialNodes(nodes, context) {
546
690
  });
547
691
  continue;
548
692
  }
549
- if (name === "import" && node.attribs?.src) {
693
+ if ((name === "import" || name === "wx-import") && node.attribs?.src) {
550
694
  const resolved = context.resolveTemplate(node.attribs.src);
551
695
  if (resolved) {
552
696
  context.imports.push({
553
697
  id: resolved,
554
698
  importName: `__wxml_import_${context.imports.length}`
555
699
  });
700
+ } else {
701
+ context.warnings.push(`[web] \u65E0\u6CD5\u89E3\u6790\u6A21\u677F\u4F9D\u8D56: ${node.attribs.src} (from ${context.sourceId})`);
556
702
  }
557
703
  continue;
558
704
  }
559
- if (name === "include" && node.attribs?.src) {
705
+ if ((name === "include" || name === "wx-include") && node.attribs?.src) {
560
706
  const resolved = context.resolveTemplate(node.attribs.src);
561
707
  if (resolved) {
562
708
  context.includes.push({
563
709
  id: resolved,
564
710
  importName: `__wxml_include_${context.includes.length}`
565
711
  });
712
+ } else {
713
+ context.warnings.push(`[web] \u65E0\u6CD5\u89E3\u6790\u6A21\u677F\u4F9D\u8D56: ${node.attribs.src} (from ${context.sourceId})`);
566
714
  }
567
715
  continue;
568
716
  }
569
717
  if (name === "wxs") {
570
718
  const moduleName = node.attribs?.module?.trim();
571
719
  if (moduleName) {
720
+ const previousSource = context.wxsModules.get(moduleName);
721
+ if (previousSource) {
722
+ context.warnings.push(`[web] WXS \u6A21\u5757\u540D\u91CD\u590D: ${moduleName} (from ${context.sourceId})`);
723
+ }
724
+ context.wxsModules.set(moduleName, context.sourceId);
572
725
  if (node.attribs?.src) {
573
726
  const resolved = context.resolveWxs(node.attribs.src);
574
727
  if (resolved) {
@@ -611,8 +764,50 @@ function normalizeTemplatePath(pathname) {
611
764
  return pathname.split("\\").join("/");
612
765
  }
613
766
  function compileWxml(options) {
767
+ const dependencyContext = options.dependencyContext ?? createDependencyContext();
768
+ const expandDependencies = options.expandDependencies ?? !options.dependencyContext;
769
+ const warnings = dependencyContext.warnings;
770
+ const expandDependencyTree = (dependencies2, importer) => {
771
+ for (const target of dependencies2) {
772
+ if (!target) {
773
+ continue;
774
+ }
775
+ if (dependencyContext.active.has(target)) {
776
+ warnCircularTemplate(dependencyContext, importer, target);
777
+ continue;
778
+ }
779
+ if (dependencyContext.visited.has(target)) {
780
+ continue;
781
+ }
782
+ dependencyContext.visited.add(target);
783
+ dependencyContext.active.add(target);
784
+ let source;
785
+ try {
786
+ source = readFileSync(target, "utf8");
787
+ } catch {
788
+ warnReadTemplate(dependencyContext, target);
789
+ dependencyContext.active.delete(target);
790
+ continue;
791
+ }
792
+ try {
793
+ const result = compileWxml({
794
+ id: target,
795
+ source,
796
+ resolveTemplatePath: options.resolveTemplatePath,
797
+ resolveWxsPath: options.resolveWxsPath,
798
+ dependencyContext,
799
+ expandDependencies: false
800
+ });
801
+ expandDependencyTree(result.dependencies, target);
802
+ } catch (error) {
803
+ if (error instanceof Error && error.message) {
804
+ warnings.push(`[web] \u65E0\u6CD5\u89E3\u6790\u6A21\u677F\u4F9D\u8D56: ${target} ${error.message}`);
805
+ }
806
+ }
807
+ dependencyContext.active.delete(target);
808
+ }
809
+ };
614
810
  let nodes = parseWxml(options.source);
615
- const warnings = [];
616
811
  let navigationBarAttrs;
617
812
  if (options.navigationBar) {
618
813
  const extracted = extractNavigationBarFromPageMeta(nodes);
@@ -626,11 +821,15 @@ function compileWxml(options) {
626
821
  const includes = [];
627
822
  const imports = [];
628
823
  const wxs = [];
824
+ const wxsModules = /* @__PURE__ */ new Map();
629
825
  const renderNodesList = collectSpecialNodes(nodes, {
630
826
  templates,
631
827
  includes,
632
828
  imports,
633
829
  wxs,
830
+ wxsModules,
831
+ warnings,
832
+ sourceId: options.id,
634
833
  resolveTemplate: (raw) => options.resolveTemplatePath(raw, options.id),
635
834
  resolveWxs: (raw) => options.resolveWxsPath(raw, options.id)
636
835
  });
@@ -647,22 +846,23 @@ function compileWxml(options) {
647
846
  `import { repeat } from 'lit/directives/repeat.js'`
648
847
  ];
649
848
  const bodyLines = [];
650
- const dependencies = [];
849
+ const directDependencies = [];
651
850
  for (const entry of imports) {
652
851
  const importPath = normalizeTemplatePath(toRelativeImport(options.id, entry.id));
653
852
  importLines.push(`import { templates as ${entry.importName} } from '${importPath}'`);
654
- dependencies.push(entry.id);
853
+ addDependency(entry.id, dependencyContext, directDependencies);
655
854
  }
656
855
  for (const entry of includes) {
657
856
  const importPath = normalizeTemplatePath(toRelativeImport(options.id, entry.id));
658
857
  importLines.push(`import { render as ${entry.importName} } from '${importPath}'`);
659
- dependencies.push(entry.id);
858
+ addDependency(entry.id, dependencyContext, directDependencies);
660
859
  }
661
860
  for (const entry of wxs) {
662
861
  if (entry.kind === "src") {
663
- const importPath = normalizeTemplatePath(toRelativeImport(options.id, entry.value));
862
+ const baseImport = normalizeTemplatePath(toRelativeImport(options.id, entry.value));
863
+ const importPath = shouldMarkWxsImport(entry.value) ? `${baseImport}?wxs` : baseImport;
664
864
  importLines.push(`import ${entry.importName} from '${importPath}'`);
665
- dependencies.push(entry.value);
865
+ addDependency(entry.value, dependencyContext, directDependencies);
666
866
  }
667
867
  }
668
868
  if (templates.length > 0 || imports.length > 0) {
@@ -718,7 +918,14 @@ function compileWxml(options) {
718
918
  bodyLines.push(`}`);
719
919
  bodyLines.push(`export const templates = __templates`);
720
920
  bodyLines.push(`export default render`);
921
+ if (expandDependencies) {
922
+ dependencyContext.visited.add(options.id);
923
+ dependencyContext.active.add(options.id);
924
+ expandDependencyTree(directDependencies, options.id);
925
+ dependencyContext.active.delete(options.id);
926
+ }
721
927
  const code = [...importLines, "", ...bodyLines].join("\n");
928
+ const dependencies = expandDependencies ? dependencyContext.dependencies : directDependencies;
722
929
  return {
723
930
  code,
724
931
  dependencies,
@@ -727,37 +934,76 @@ function compileWxml(options) {
727
934
  }
728
935
 
729
936
  // src/compiler/wxs.ts
730
- var REQUIRE_RE = /require\\(\\s*['"]([^'"]+)['"]\\s*\\)/g;
937
+ var REQUIRE_RE = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
731
938
  function normalizePath(p) {
732
939
  return p.split("\\\\").join("/");
733
940
  }
734
- function ensureWxsExtension(pathname) {
735
- if (pathname.endsWith(".wxs") || pathname.endsWith(".wxs.ts") || pathname.endsWith(".wxs.js")) {
941
+ function isPlainWxsScript(pathname) {
942
+ const lower = pathname.toLowerCase();
943
+ if (lower.endsWith(".wxs") || lower.endsWith(".wxs.ts") || lower.endsWith(".wxs.js")) {
944
+ return false;
945
+ }
946
+ return lower.endsWith(".ts") || lower.endsWith(".js");
947
+ }
948
+ function appendWxsQuery(pathname) {
949
+ if (pathname.includes("?wxs") || pathname.includes("&wxs")) {
736
950
  return pathname;
737
951
  }
738
- return `${pathname}.wxs`;
952
+ return `${pathname}${pathname.includes("?") ? "&" : "?"}wxs`;
953
+ }
954
+ function isSupportedRequirePath(request) {
955
+ return request.startsWith(".") || request.startsWith("/");
739
956
  }
740
957
  function transformWxsToEsm(code, id, options) {
741
958
  const dependencies = [];
742
959
  const importLines = [];
743
960
  const mapEntries = [];
744
- let match;
961
+ const warnings = [];
745
962
  const seen = /* @__PURE__ */ new Set();
746
- while (match = REQUIRE_RE.exec(code)) {
963
+ while (true) {
964
+ const match = REQUIRE_RE.exec(code);
965
+ if (!match) {
966
+ break;
967
+ }
747
968
  const request = match[1];
748
969
  if (!request || seen.has(request)) {
749
970
  continue;
750
971
  }
751
972
  seen.add(request);
752
- const resolved = ensureWxsExtension(options.resolvePath(request, id) ?? request);
753
- const importPath = normalizePath(options.toImportPath?.(resolved, id) ?? resolved);
973
+ if (!isSupportedRequirePath(request)) {
974
+ warnings.push(`[@weapp-vite/web] WXS require \u4EC5\u652F\u6301\u76F8\u5BF9\u6216\u7EDD\u5BF9\u8DEF\u5F84: ${request} (from ${id})`);
975
+ continue;
976
+ }
977
+ const resolved = options.resolvePath(request, id);
978
+ if (!resolved) {
979
+ warnings.push(`[@weapp-vite/web] \u65E0\u6CD5\u89E3\u6790 WXS require: ${request} (from ${id})`);
980
+ continue;
981
+ }
982
+ let importPath = normalizePath(options.toImportPath?.(resolved, id) ?? resolved);
983
+ if (isPlainWxsScript(resolved)) {
984
+ importPath = appendWxsQuery(importPath);
985
+ }
754
986
  const importName = `__wxs_dep_${dependencies.length}`;
755
987
  importLines.push(`import ${importName} from '${importPath}'`);
756
988
  mapEntries.push(`[${JSON.stringify(request)}, ${importName}]`);
757
989
  dependencies.push(resolved);
758
990
  }
759
991
  const requireMap = `const __wxs_require_map = new Map([${mapEntries.join(", ")}])`;
760
- const requireFn = `function require(id) { return __wxs_require_map.get(id) }`;
992
+ const requireFn = [
993
+ `const __wxs_require_warned = new Set()`,
994
+ `function require(id) {`,
995
+ ` if (__wxs_require_map.has(id)) {`,
996
+ ` return __wxs_require_map.get(id)`,
997
+ ` }`,
998
+ ` if (!__wxs_require_warned.has(id)) {`,
999
+ ` __wxs_require_warned.add(id)`,
1000
+ ` if (typeof console !== 'undefined' && typeof console.warn === 'function') {`,
1001
+ ` console.warn(\`[@weapp-vite/web] WXS require \u672A\u89E3\u6790: \${id}\`)`,
1002
+ ` }`,
1003
+ ` }`,
1004
+ ` return undefined`,
1005
+ `}`
1006
+ ].join("\\n");
761
1007
  const moduleInit = `const module = { exports: {} }\\nconst exports = module.exports`;
762
1008
  const helpers = `const getRegExp = (pattern, flags) => new RegExp(pattern, flags)\\nconst getDate = (value) => (value == null ? new Date() : new Date(value))`;
763
1009
  const body = [
@@ -769,7 +1015,11 @@ function transformWxsToEsm(code, id, options) {
769
1015
  code,
770
1016
  `export default module.exports`
771
1017
  ].join("\\n");
772
- return { code: body, dependencies };
1018
+ return {
1019
+ code: body,
1020
+ dependencies,
1021
+ warnings: warnings.length > 0 ? warnings : void 0
1022
+ };
773
1023
  }
774
1024
 
775
1025
  // src/shared/slugify.ts
@@ -801,6 +1051,7 @@ var STYLE_EXTS = [".wxss", ".scss", ".less", ".css"];
801
1051
  var TRANSFORM_STYLE_EXTS = [".wxss"];
802
1052
  var TEMPLATE_EXTS = [".wxml", ".axml", ".swan", ".ttml", ".qml", ".ksml", ".xhsml", ".html"];
803
1053
  var WXS_EXTS = [".wxs", ".wxs.ts", ".wxs.js"];
1054
+ var WXS_RESOLVE_EXTS = [".wxs", ".wxs.ts", ".wxs.js", ".ts", ".js"];
804
1055
  var ENTRY_ID = "\0@weapp-vite/web/entry";
805
1056
  function isTemplateFile(id) {
806
1057
  const lower = id.toLowerCase();
@@ -810,6 +1061,9 @@ function isWxsFile(id) {
810
1061
  const lower = id.toLowerCase();
811
1062
  return WXS_EXTS.some((ext) => lower.endsWith(ext));
812
1063
  }
1064
+ function hasWxsQuery(id) {
1065
+ return id.includes("?wxs") || id.includes("&wxs");
1066
+ }
813
1067
  function weappWebPlugin(options = {}) {
814
1068
  let root = process.cwd();
815
1069
  let srcRoot = resolve(root, options.srcDir ?? "src");
@@ -885,8 +1139,8 @@ function weappWebPlugin(options = {}) {
885
1139
  }
886
1140
  return { code: compiled, map: null };
887
1141
  }
888
- if (isWxsFile(clean)) {
889
- const { code: compiled, dependencies } = transformWxsToEsm(code, clean, {
1142
+ if (isWxsFile(clean) || hasWxsQuery(id)) {
1143
+ const { code: compiled, dependencies, warnings } = transformWxsToEsm(code, clean, {
890
1144
  resolvePath: resolveWxsPath,
891
1145
  toImportPath: (resolved, importer) => normalizePath2(toRelativeImport2(importer, resolved))
892
1146
  });
@@ -895,6 +1149,11 @@ function weappWebPlugin(options = {}) {
895
1149
  this.addWatchFile(dep);
896
1150
  }
897
1151
  }
1152
+ if (warnings?.length && "warn" in this) {
1153
+ for (const warning of warnings) {
1154
+ this.warn(warning);
1155
+ }
1156
+ }
898
1157
  return { code: compiled, map: null };
899
1158
  }
900
1159
  if (TRANSFORM_STYLE_EXTS.some((ext) => clean.endsWith(ext))) {
@@ -1040,7 +1299,9 @@ if (import.meta.hot) { import.meta.hot.accept() }
1040
1299
  warn(message);
1041
1300
  return;
1042
1301
  }
1043
- console.warn(message);
1302
+ if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
1303
+ process.emitWarning(message);
1304
+ }
1044
1305
  };
1045
1306
  const appScript = await resolveScriptFile(join(srcRoot, "app"));
1046
1307
  if (appScript) {
@@ -1347,11 +1608,18 @@ function resolveTemplatePathSync(raw, importer, srcRoot) {
1347
1608
  return resolveFileWithExtensionsSync(base, TEMPLATE_EXTS);
1348
1609
  }
1349
1610
  function resolveWxsPathSync(raw, importer, srcRoot) {
1350
- const base = resolveImportBase(raw, importer, srcRoot);
1351
- if (!base) {
1611
+ if (!raw) {
1612
+ return void 0;
1613
+ }
1614
+ let base;
1615
+ if (raw.startsWith(".")) {
1616
+ base = resolve(dirname2(importer), raw);
1617
+ } else if (raw.startsWith("/")) {
1618
+ base = resolve(srcRoot, raw.slice(1));
1619
+ } else {
1352
1620
  return void 0;
1353
1621
  }
1354
- return resolveFileWithExtensionsSync(base, WXS_EXTS);
1622
+ return resolveFileWithExtensionsSync(base, WXS_RESOLVE_EXTS);
1355
1623
  }
1356
1624
  async function resolveScriptFile(basePath) {
1357
1625
  const ext = extname(basePath);
@@ -2143,7 +2411,7 @@ function normalizeKey(rawKey, item, index, scope) {
2143
2411
  const expr = key.replace(/^\{\{\s*/, "").replace(/\s*\}\}$/, "");
2144
2412
  return evaluateExpression(expr, scope);
2145
2413
  }
2146
- if (!/[\.\[\(\)]/.test(key) && item && typeof item === "object" && key in item) {
2414
+ if (!/[.[()]/.test(key) && item && typeof item === "object" && key in item) {
2147
2415
  return item[key];
2148
2416
  }
2149
2417
  return evaluateExpression(key, scope);
@@ -2192,6 +2460,16 @@ function createWxsModule(code, id, requireMap) {
2192
2460
  return module.exports;
2193
2461
  }
2194
2462
  function createRenderContext(instance, methods) {
2463
+ const warnedTemplates = /* @__PURE__ */ new Set();
2464
+ const warnMissingTemplate = (name) => {
2465
+ if (warnedTemplates.has(name)) {
2466
+ return;
2467
+ }
2468
+ warnedTemplates.add(name);
2469
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
2470
+ console.warn(`[@weapp-vite/web] \u672A\u627E\u5230\u6A21\u677F: ${name}`);
2471
+ }
2472
+ };
2195
2473
  return {
2196
2474
  instance,
2197
2475
  eval: (expression, scope, wxs) => {
@@ -2214,11 +2492,10 @@ function createRenderContext(instance, methods) {
2214
2492
  return normalizeKey(rawKey, item, index, renderScope);
2215
2493
  },
2216
2494
  renderTemplate: (templates, name, scope, ctx) => {
2217
- if (!name) {
2218
- return "";
2219
- }
2220
- const template = templates?.[name];
2495
+ const resolvedName = typeof name === "string" ? name : String(name);
2496
+ const template = templates?.[resolvedName];
2221
2497
  if (typeof template !== "function") {
2498
+ warnMissingTemplate(resolvedName);
2222
2499
  return "";
2223
2500
  }
2224
2501
  return template(scope, ctx);
@@ -2327,6 +2604,78 @@ function coerceValue(value, type) {
2327
2604
  }
2328
2605
  return value;
2329
2606
  }
2607
+ function isPlainObject(value) {
2608
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2609
+ }
2610
+ function mergeLifetimes(target, source) {
2611
+ if (!source) {
2612
+ return;
2613
+ }
2614
+ const keys = ["created", "attached", "ready", "detached"];
2615
+ for (const key of keys) {
2616
+ const next = source[key];
2617
+ if (!next) {
2618
+ continue;
2619
+ }
2620
+ const current = target[key];
2621
+ target[key] = current ? function merged() {
2622
+ current.call(this);
2623
+ next.call(this);
2624
+ } : next;
2625
+ }
2626
+ }
2627
+ function normalizeBehaviors(component) {
2628
+ if (!component) {
2629
+ return { component: void 0, warnings: [] };
2630
+ }
2631
+ const warnings = [];
2632
+ const visited = /* @__PURE__ */ new Set();
2633
+ const merged = {};
2634
+ const mergeComponent = (source) => {
2635
+ if (source.properties) {
2636
+ merged.properties = { ...merged.properties ?? {}, ...source.properties };
2637
+ }
2638
+ if (source.data) {
2639
+ const nextData = typeof source.data === "function" ? source.data() : source.data;
2640
+ if (isPlainObject(nextData)) {
2641
+ merged.data = { ...merged.data ?? {}, ...nextData };
2642
+ }
2643
+ }
2644
+ if (source.methods) {
2645
+ merged.methods = { ...merged.methods ?? {}, ...source.methods };
2646
+ }
2647
+ if (source.lifetimes) {
2648
+ merged.lifetimes = merged.lifetimes ?? {};
2649
+ mergeLifetimes(merged.lifetimes, source.lifetimes);
2650
+ }
2651
+ };
2652
+ const walk = (source) => {
2653
+ if (visited.has(source)) {
2654
+ warnings.push("[@weapp-vite/web] behaviors \u5B58\u5728\u5FAA\u73AF\u5F15\u7528\uFF0C\u5DF2\u8DF3\u8FC7\u3002");
2655
+ return;
2656
+ }
2657
+ visited.add(source);
2658
+ const behaviors = source.behaviors ?? [];
2659
+ if (Array.isArray(behaviors)) {
2660
+ for (const behavior of behaviors) {
2661
+ if (!behavior || !isPlainObject(behavior)) {
2662
+ warnings.push("[@weapp-vite/web] behaviors \u4EC5\u652F\u6301\u5BF9\u8C61\uFF0C\u5DF2\u5FFD\u7565\u975E\u5BF9\u8C61\u6761\u76EE\u3002");
2663
+ continue;
2664
+ }
2665
+ walk(behavior);
2666
+ mergeComponent(behavior);
2667
+ }
2668
+ } else if (behaviors) {
2669
+ warnings.push("[@weapp-vite/web] behaviors \u4EC5\u652F\u6301\u6570\u7EC4\uFF0C\u5DF2\u5FFD\u7565\u3002");
2670
+ }
2671
+ };
2672
+ walk(component);
2673
+ mergeComponent(component);
2674
+ return {
2675
+ component: merged,
2676
+ warnings
2677
+ };
2678
+ }
2330
2679
  function defineComponent(tagName, options) {
2331
2680
  if (!options || typeof options !== "object") {
2332
2681
  throw new TypeError("[@weapp-vite/web] defineComponent \u9700\u8981\u63D0\u4F9B\u914D\u7F6E\u5BF9\u8C61\u3002");
@@ -2337,13 +2686,20 @@ function defineComponent(tagName, options) {
2337
2686
  return existing;
2338
2687
  }
2339
2688
  const BaseElement3 = supportsLit ? LitElement : globalThis.HTMLElement ?? FallbackElement;
2340
- const { template, style = "", component = {} } = options;
2689
+ const { template, style = "", component = {}, observerInit = false } = options;
2341
2690
  if (!template) {
2342
2691
  throw new Error("[@weapp-vite/web] defineComponent \u9700\u8981\u63D0\u4F9B\u6A21\u677F\u6E32\u67D3\u51FD\u6570\u3002");
2343
2692
  }
2693
+ const normalized = normalizeBehaviors(component);
2694
+ for (const warning of normalized.warnings) {
2695
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
2696
+ console.warn(warning);
2697
+ }
2698
+ }
2344
2699
  let templateRef = template;
2345
2700
  let styleRef = style;
2346
- let componentRef = component;
2701
+ let componentRef = normalized.component ?? component;
2702
+ let observerInitEnabled = Boolean(observerInit);
2347
2703
  let propertyEntries = Object.entries(componentRef.properties ?? {});
2348
2704
  let observedAttributes = propertyEntries.map(([name]) => hyphenate(name));
2349
2705
  let defaultPropertyValues = propertyEntries.reduce((acc, [name, prop]) => {
@@ -2367,6 +2723,8 @@ function defineComponent(tagName, options) {
2367
2723
  #renderContext = createRenderContext(this, {});
2368
2724
  #usesLegacyTemplate = false;
2369
2725
  #readyFired = false;
2726
+ #observerInitDone = false;
2727
+ #observedKeys = /* @__PURE__ */ new Set();
2370
2728
  constructor() {
2371
2729
  super();
2372
2730
  const dataOption = typeof componentRef.data === "function" ? componentRef.data() : componentRef.data ?? {};
@@ -2417,6 +2775,9 @@ function defineComponent(tagName, options) {
2417
2775
  superConnected.call(this);
2418
2776
  }
2419
2777
  this.#applyAttributes();
2778
+ if (observerInitEnabled) {
2779
+ this.#runInitialObservers();
2780
+ }
2420
2781
  lifetimes.attached?.call(this);
2421
2782
  this.#isMounted = true;
2422
2783
  if (!supportsLit) {
@@ -2497,7 +2858,11 @@ function defineComponent(tagName, options) {
2497
2858
  this.#state[key] = value;
2498
2859
  if (Object.prototype.hasOwnProperty.call(this.#properties, key)) {
2499
2860
  this.#properties[key] = value;
2500
- componentRef.properties?.[key]?.observer?.call(this, value, oldValue);
2861
+ const propOption = componentRef.properties?.[key];
2862
+ if (propOption?.observer) {
2863
+ propOption.observer.call(this, value, oldValue);
2864
+ this.#observedKeys.add(key);
2865
+ }
2501
2866
  }
2502
2867
  changed = true;
2503
2868
  }
@@ -2518,7 +2883,24 @@ function defineComponent(tagName, options) {
2518
2883
  if (this.#isMounted) {
2519
2884
  this.requestUpdate();
2520
2885
  }
2521
- propOption?.observer?.call(this, coerced, oldValue);
2886
+ if (propOption?.observer) {
2887
+ propOption.observer.call(this, coerced, oldValue);
2888
+ this.#observedKeys.add(name);
2889
+ }
2890
+ }
2891
+ #runInitialObservers() {
2892
+ if (this.#observerInitDone) {
2893
+ return;
2894
+ }
2895
+ this.#observerInitDone = true;
2896
+ for (const [propName, propOption] of propertyEntries) {
2897
+ if (!propOption.observer || this.#observedKeys.has(propName)) {
2898
+ continue;
2899
+ }
2900
+ const value = this.#state[propName];
2901
+ propOption.observer.call(this, value, void 0);
2902
+ this.#observedKeys.add(propName);
2903
+ }
2522
2904
  }
2523
2905
  #syncMethods(nextMethods) {
2524
2906
  const resolved = nextMethods ?? {};
@@ -2566,7 +2948,14 @@ function defineComponent(tagName, options) {
2566
2948
  }
2567
2949
  templateRef = nextOptions.template;
2568
2950
  styleRef = nextOptions.style ?? "";
2569
- componentRef = nextOptions.component ?? {};
2951
+ const nextNormalized = normalizeBehaviors(nextOptions.component ?? {});
2952
+ for (const warning of nextNormalized.warnings) {
2953
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
2954
+ console.warn(warning);
2955
+ }
2956
+ }
2957
+ componentRef = nextNormalized.component ?? nextOptions.component ?? {};
2958
+ observerInitEnabled = Boolean(nextOptions.observerInit);
2570
2959
  lifetimes = componentRef.lifetimes ?? {};
2571
2960
  propertyEntries = Object.entries(componentRef.properties ?? {});
2572
2961
  observedAttributes = propertyEntries.map(([name]) => hyphenate(name));
@@ -3496,6 +3885,8 @@ function normalizeTagName2(name) {
3496
3885
  return "select";
3497
3886
  case "block":
3498
3887
  return "#fragment";
3888
+ case "slot":
3889
+ return "slot";
3499
3890
  default:
3500
3891
  return name || "div";
3501
3892
  }