@weapp-vite/web 1.0.1 → 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";
@@ -361,6 +505,25 @@ function parseEventAttribute(name) {
361
505
  }
362
506
  return { prefix: name.slice(0, name.length - match[1].length), rawEvent: match[1] };
363
507
  }
508
+ function resolveComponentTagName(name, componentTags) {
509
+ if (!componentTags) {
510
+ return void 0;
511
+ }
512
+ return componentTags[name] ?? componentTags[name.toLowerCase()];
513
+ }
514
+ var PROPERTY_BIND_EXCLUSIONS = /* @__PURE__ */ new Set(["class", "style", "id", "slot"]);
515
+ function shouldBindAsProperty(name) {
516
+ if (PROPERTY_BIND_EXCLUSIONS.has(name)) {
517
+ return false;
518
+ }
519
+ if (name.startsWith("data-") || name.startsWith("aria-")) {
520
+ return false;
521
+ }
522
+ return true;
523
+ }
524
+ function normalizePropertyName(name) {
525
+ return name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
526
+ }
364
527
  function renderAttributes(attribs, scopeVar, wxsVar, options) {
365
528
  let buffer = "";
366
529
  for (const [rawName, rawValue] of Object.entries(attribs)) {
@@ -379,24 +542,31 @@ function renderAttributes(attribs, scopeVar, wxsVar, options) {
379
542
  buffer += ` @${domEvent}=\${ctx.event(${JSON.stringify(event)}, ${handlerExpr}, ${scopeVar}, ${wxsVar}, ${JSON.stringify(flags)})}`;
380
543
  continue;
381
544
  }
382
- const name = normalizeAttributeName(rawName);
545
+ const useProperty = options?.preferProperty && shouldBindAsProperty(rawName);
546
+ const name = useProperty ? normalizePropertyName(rawName) : normalizeAttributeName(rawName);
383
547
  const expr = buildExpression(parseInterpolations(rawValue ?? ""), scopeVar, wxsVar);
384
- buffer += ` ${name}=\${${expr}}`;
548
+ buffer += ` ${useProperty ? "." : ""}${name}=\${${expr}}`;
385
549
  }
386
550
  return buffer;
387
551
  }
388
552
  var Renderer = class {
389
- renderNodes(nodes, scopeVar, wxsVar) {
553
+ renderNodes(nodes, scopeVar, wxsVar, componentTags) {
390
554
  const parts = [];
391
555
  for (let index = 0; index < nodes.length; index += 1) {
392
556
  const node = nodes[index];
393
557
  if (isConditionalElement(node)) {
394
- const { rendered, endIndex } = this.renderConditionalSequence(nodes, index, scopeVar, wxsVar);
558
+ const { rendered, endIndex } = this.renderConditionalSequence(
559
+ nodes,
560
+ index,
561
+ scopeVar,
562
+ wxsVar,
563
+ componentTags
564
+ );
395
565
  parts.push(rendered);
396
566
  index = endIndex;
397
567
  continue;
398
568
  }
399
- parts.push(this.renderNode(node, scopeVar, wxsVar));
569
+ parts.push(this.renderNode(node, scopeVar, wxsVar, componentTags));
400
570
  }
401
571
  if (parts.length === 0) {
402
572
  return '""';
@@ -406,7 +576,7 @@ var Renderer = class {
406
576
  }
407
577
  return `[${parts.join(", ")}]`;
408
578
  }
409
- renderConditionalSequence(nodes, startIndex, scopeVar, wxsVar) {
579
+ renderConditionalSequence(nodes, startIndex, scopeVar, wxsVar, componentTags) {
410
580
  const branches = [];
411
581
  let cursor = startIndex;
412
582
  while (cursor < nodes.length) {
@@ -432,44 +602,44 @@ var Renderer = class {
432
602
  if (!node) {
433
603
  return { rendered: '""', endIndex: startIndex };
434
604
  }
435
- return { rendered: this.renderNode(node, scopeVar, wxsVar), endIndex: startIndex };
605
+ return { rendered: this.renderNode(node, scopeVar, wxsVar, componentTags), endIndex: startIndex };
436
606
  }
437
607
  let expr = '""';
438
608
  for (let index = branches.length - 1; index >= 0; index -= 1) {
439
609
  const { node, attribs } = branches[index];
440
610
  const cleanedAttribs = stripControlAttributes(attribs);
441
611
  if ("wx:else" in attribs) {
442
- expr = this.renderElement(node, scopeVar, wxsVar, { overrideAttribs: cleanedAttribs });
612
+ expr = this.renderElement(node, scopeVar, wxsVar, componentTags, { overrideAttribs: cleanedAttribs });
443
613
  continue;
444
614
  }
445
615
  const conditionExpr = attribs["wx:if"] ?? attribs["wx:elif"] ?? "";
446
- const rendered = this.renderElement(node, scopeVar, wxsVar, { overrideAttribs: cleanedAttribs });
616
+ const rendered = this.renderElement(node, scopeVar, wxsVar, componentTags, { overrideAttribs: cleanedAttribs });
447
617
  const condition = buildExpression(parseInterpolations(conditionExpr), scopeVar, wxsVar);
448
618
  expr = `(${condition} ? ${rendered} : ${expr})`;
449
619
  }
450
620
  return { rendered: expr, endIndex: startIndex + branches.length - 1 };
451
621
  }
452
- renderNode(node, scopeVar, wxsVar) {
622
+ renderNode(node, scopeVar, wxsVar, componentTags) {
453
623
  if (node.type === "text") {
454
624
  const parts = parseInterpolations(node.data ?? "");
455
625
  return buildExpression(parts, scopeVar, wxsVar);
456
626
  }
457
627
  if (node.type === "element") {
458
628
  if (node.name === "template" && node.attribs?.is) {
459
- return this.renderTemplateInvoke(node, scopeVar, wxsVar);
629
+ return this.renderTemplateInvoke(node, scopeVar, wxsVar, componentTags);
460
630
  }
461
- return this.renderElement(node, scopeVar, wxsVar);
631
+ return this.renderElement(node, scopeVar, wxsVar, componentTags);
462
632
  }
463
633
  return '""';
464
634
  }
465
- renderTemplateInvoke(node, scopeVar, wxsVar) {
635
+ renderTemplateInvoke(node, scopeVar, wxsVar, _componentTags) {
466
636
  const attribs = node.attribs ?? {};
467
637
  const isExpr = buildExpression(parseInterpolations(attribs.is ?? ""), scopeVar, wxsVar);
468
- const dataExpr = attribs.data ? buildExpression(parseInterpolations(attribs.data), scopeVar, wxsVar) : void 0;
638
+ const dataExpr = attribs.data ? buildTemplateDataExpression(attribs.data, scopeVar, wxsVar) : void 0;
469
639
  const scopeExpr = dataExpr ? `ctx.mergeScope(${scopeVar}, ${dataExpr})` : scopeVar;
470
640
  return `ctx.renderTemplate(__templates, ${isExpr}, ${scopeExpr}, ctx)`;
471
641
  }
472
- renderElement(node, scopeVar, wxsVar, options = {}) {
642
+ renderElement(node, scopeVar, wxsVar, componentTags, options = {}) {
473
643
  const attribs = options.overrideAttribs ?? node.attribs ?? {};
474
644
  if (!options.skipFor) {
475
645
  const forInfo = extractFor(node.attribs ?? {});
@@ -483,19 +653,24 @@ var Renderer = class {
483
653
  node,
484
654
  "__scope",
485
655
  wxsVar,
656
+ componentTags,
486
657
  { skipFor: true, overrideAttribs: forInfo.restAttribs }
487
658
  );
488
659
  const keyExpr = `ctx.key(${JSON.stringify(forInfo.key ?? "")}, ${itemVar}, ${indexVar}, ${scopeExpr}, ${wxsVar})`;
489
660
  return `repeat(${listExpr}, (${itemVar}, ${indexVar}) => ${keyExpr}, (${itemVar}, ${indexVar}) => { const __scope = ${scopeExpr}; return ${itemRender}; })`;
490
661
  }
491
662
  }
492
- const tagName = normalizeTagName(node.name ?? "");
663
+ const customTag = resolveComponentTagName(node.name ?? "", componentTags);
664
+ const tagName = customTag ?? normalizeTagName(node.name ?? "");
493
665
  if (tagName === "#fragment") {
494
- return this.renderNodes(node.children ?? [], scopeVar, wxsVar);
666
+ return this.renderNodes(node.children ?? [], scopeVar, wxsVar, componentTags);
495
667
  }
496
- const attrs = renderAttributes(attribs, scopeVar, wxsVar, { skipControl: true });
668
+ const attrs = renderAttributes(attribs, scopeVar, wxsVar, {
669
+ skipControl: true,
670
+ preferProperty: Boolean(customTag)
671
+ });
497
672
  const childNodes = node.children ?? [];
498
- const children = childNodes.map((child) => `\${${this.renderNode(child, scopeVar, wxsVar)}}`).join("");
673
+ const children = childNodes.map((child) => `\${${this.renderNode(child, scopeVar, wxsVar, componentTags)}}`).join("");
499
674
  if (SELF_CLOSING_TAGS.has(tagName) && childNodes.length === 0) {
500
675
  return `html\`<${tagName}${attrs} />\``;
501
676
  }
@@ -515,29 +690,38 @@ function collectSpecialNodes(nodes, context) {
515
690
  });
516
691
  continue;
517
692
  }
518
- if (name === "import" && node.attribs?.src) {
693
+ if ((name === "import" || name === "wx-import") && node.attribs?.src) {
519
694
  const resolved = context.resolveTemplate(node.attribs.src);
520
695
  if (resolved) {
521
696
  context.imports.push({
522
697
  id: resolved,
523
698
  importName: `__wxml_import_${context.imports.length}`
524
699
  });
700
+ } else {
701
+ context.warnings.push(`[web] \u65E0\u6CD5\u89E3\u6790\u6A21\u677F\u4F9D\u8D56: ${node.attribs.src} (from ${context.sourceId})`);
525
702
  }
526
703
  continue;
527
704
  }
528
- if (name === "include" && node.attribs?.src) {
705
+ if ((name === "include" || name === "wx-include") && node.attribs?.src) {
529
706
  const resolved = context.resolveTemplate(node.attribs.src);
530
707
  if (resolved) {
531
708
  context.includes.push({
532
709
  id: resolved,
533
710
  importName: `__wxml_include_${context.includes.length}`
534
711
  });
712
+ } else {
713
+ context.warnings.push(`[web] \u65E0\u6CD5\u89E3\u6790\u6A21\u677F\u4F9D\u8D56: ${node.attribs.src} (from ${context.sourceId})`);
535
714
  }
536
715
  continue;
537
716
  }
538
717
  if (name === "wxs") {
539
718
  const moduleName = node.attribs?.module?.trim();
540
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);
541
725
  if (node.attribs?.src) {
542
726
  const resolved = context.resolveWxs(node.attribs.src);
543
727
  if (resolved) {
@@ -580,8 +764,50 @@ function normalizeTemplatePath(pathname) {
580
764
  return pathname.split("\\").join("/");
581
765
  }
582
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
+ };
583
810
  let nodes = parseWxml(options.source);
584
- const warnings = [];
585
811
  let navigationBarAttrs;
586
812
  if (options.navigationBar) {
587
813
  const extracted = extractNavigationBarFromPageMeta(nodes);
@@ -595,11 +821,15 @@ function compileWxml(options) {
595
821
  const includes = [];
596
822
  const imports = [];
597
823
  const wxs = [];
824
+ const wxsModules = /* @__PURE__ */ new Map();
598
825
  const renderNodesList = collectSpecialNodes(nodes, {
599
826
  templates,
600
827
  includes,
601
828
  imports,
602
829
  wxs,
830
+ wxsModules,
831
+ warnings,
832
+ sourceId: options.id,
603
833
  resolveTemplate: (raw) => options.resolveTemplatePath(raw, options.id),
604
834
  resolveWxs: (raw) => options.resolveWxsPath(raw, options.id)
605
835
  });
@@ -616,22 +846,23 @@ function compileWxml(options) {
616
846
  `import { repeat } from 'lit/directives/repeat.js'`
617
847
  ];
618
848
  const bodyLines = [];
619
- const dependencies = [];
849
+ const directDependencies = [];
620
850
  for (const entry of imports) {
621
851
  const importPath = normalizeTemplatePath(toRelativeImport(options.id, entry.id));
622
852
  importLines.push(`import { templates as ${entry.importName} } from '${importPath}'`);
623
- dependencies.push(entry.id);
853
+ addDependency(entry.id, dependencyContext, directDependencies);
624
854
  }
625
855
  for (const entry of includes) {
626
856
  const importPath = normalizeTemplatePath(toRelativeImport(options.id, entry.id));
627
857
  importLines.push(`import { render as ${entry.importName} } from '${importPath}'`);
628
- dependencies.push(entry.id);
858
+ addDependency(entry.id, dependencyContext, directDependencies);
629
859
  }
630
860
  for (const entry of wxs) {
631
861
  if (entry.kind === "src") {
632
- 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;
633
864
  importLines.push(`import ${entry.importName} from '${importPath}'`);
634
- dependencies.push(entry.value);
865
+ addDependency(entry.value, dependencyContext, directDependencies);
635
866
  }
636
867
  }
637
868
  if (templates.length > 0 || imports.length > 0) {
@@ -640,7 +871,7 @@ function compileWxml(options) {
640
871
  templatePairs.push(`...${entry.importName}`);
641
872
  }
642
873
  for (const template of templates) {
643
- const rendered = renderer.renderNodes(template.nodes, "scope", "__wxs_modules");
874
+ const rendered = renderer.renderNodes(template.nodes, "scope", "__wxs_modules", options.componentTags);
644
875
  templatePairs.push(`${JSON.stringify(template.name)}: (scope, ctx) => ${rendered}`);
645
876
  }
646
877
  bodyLines.push(`const __templates = { ${templatePairs.join(", ")} }`);
@@ -677,7 +908,7 @@ function compileWxml(options) {
677
908
  bodyLines.push(`const __wxs_modules = {}`);
678
909
  }
679
910
  const includesRender = includes.map((entry) => `${entry.importName}(scope, ctx)`);
680
- const renderContent = renderer.renderNodes(renderNodesList, "scope", "__wxs_modules");
911
+ const renderContent = renderer.renderNodes(renderNodesList, "scope", "__wxs_modules", options.componentTags);
681
912
  const contentExpr = includesRender.length > 0 ? `[${[...includesRender, renderContent].join(", ")}]` : renderContent;
682
913
  bodyLines.push(`export function render(scope, ctx) {`);
683
914
  if (wxs.length > 0) {
@@ -687,7 +918,14 @@ function compileWxml(options) {
687
918
  bodyLines.push(`}`);
688
919
  bodyLines.push(`export const templates = __templates`);
689
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
+ }
690
927
  const code = [...importLines, "", ...bodyLines].join("\n");
928
+ const dependencies = expandDependencies ? dependencyContext.dependencies : directDependencies;
691
929
  return {
692
930
  code,
693
931
  dependencies,
@@ -696,37 +934,76 @@ function compileWxml(options) {
696
934
  }
697
935
 
698
936
  // src/compiler/wxs.ts
699
- var REQUIRE_RE = /require\\(\\s*['"]([^'"]+)['"]\\s*\\)/g;
937
+ var REQUIRE_RE = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
700
938
  function normalizePath(p) {
701
939
  return p.split("\\\\").join("/");
702
940
  }
703
- function ensureWxsExtension(pathname) {
704
- 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")) {
705
950
  return pathname;
706
951
  }
707
- return `${pathname}.wxs`;
952
+ return `${pathname}${pathname.includes("?") ? "&" : "?"}wxs`;
953
+ }
954
+ function isSupportedRequirePath(request) {
955
+ return request.startsWith(".") || request.startsWith("/");
708
956
  }
709
957
  function transformWxsToEsm(code, id, options) {
710
958
  const dependencies = [];
711
959
  const importLines = [];
712
960
  const mapEntries = [];
713
- let match;
961
+ const warnings = [];
714
962
  const seen = /* @__PURE__ */ new Set();
715
- while (match = REQUIRE_RE.exec(code)) {
963
+ while (true) {
964
+ const match = REQUIRE_RE.exec(code);
965
+ if (!match) {
966
+ break;
967
+ }
716
968
  const request = match[1];
717
969
  if (!request || seen.has(request)) {
718
970
  continue;
719
971
  }
720
972
  seen.add(request);
721
- const resolved = ensureWxsExtension(options.resolvePath(request, id) ?? request);
722
- 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
+ }
723
986
  const importName = `__wxs_dep_${dependencies.length}`;
724
987
  importLines.push(`import ${importName} from '${importPath}'`);
725
988
  mapEntries.push(`[${JSON.stringify(request)}, ${importName}]`);
726
989
  dependencies.push(resolved);
727
990
  }
728
991
  const requireMap = `const __wxs_require_map = new Map([${mapEntries.join(", ")}])`;
729
- 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");
730
1007
  const moduleInit = `const module = { exports: {} }\\nconst exports = module.exports`;
731
1008
  const helpers = `const getRegExp = (pattern, flags) => new RegExp(pattern, flags)\\nconst getDate = (value) => (value == null ? new Date() : new Date(value))`;
732
1009
  const body = [
@@ -738,7 +1015,17 @@ function transformWxsToEsm(code, id, options) {
738
1015
  code,
739
1016
  `export default module.exports`
740
1017
  ].join("\\n");
741
- return { code: body, dependencies };
1018
+ return {
1019
+ code: body,
1020
+ dependencies,
1021
+ warnings: warnings.length > 0 ? warnings : void 0
1022
+ };
1023
+ }
1024
+
1025
+ // src/shared/slugify.ts
1026
+ function slugify(id, prefix) {
1027
+ const normalized = id.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase();
1028
+ return `${prefix}-${normalized || "index"}`;
742
1029
  }
743
1030
 
744
1031
  // src/plugin.ts
@@ -764,6 +1051,7 @@ var STYLE_EXTS = [".wxss", ".scss", ".less", ".css"];
764
1051
  var TRANSFORM_STYLE_EXTS = [".wxss"];
765
1052
  var TEMPLATE_EXTS = [".wxml", ".axml", ".swan", ".ttml", ".qml", ".ksml", ".xhsml", ".html"];
766
1053
  var WXS_EXTS = [".wxs", ".wxs.ts", ".wxs.js"];
1054
+ var WXS_RESOLVE_EXTS = [".wxs", ".wxs.ts", ".wxs.js", ".ts", ".js"];
767
1055
  var ENTRY_ID = "\0@weapp-vite/web/entry";
768
1056
  function isTemplateFile(id) {
769
1057
  const lower = id.toLowerCase();
@@ -773,13 +1061,20 @@ function isWxsFile(id) {
773
1061
  const lower = id.toLowerCase();
774
1062
  return WXS_EXTS.some((ext) => lower.endsWith(ext));
775
1063
  }
1064
+ function hasWxsQuery(id) {
1065
+ return id.includes("?wxs") || id.includes("&wxs");
1066
+ }
776
1067
  function weappWebPlugin(options = {}) {
777
1068
  let root = process.cwd();
778
1069
  let srcRoot = resolve(root, options.srcDir ?? "src");
779
1070
  let enableHmr = false;
780
1071
  const moduleMeta = /* @__PURE__ */ new Map();
781
1072
  const pageNavigationMap = /* @__PURE__ */ new Map();
1073
+ const templateComponentMap = /* @__PURE__ */ new Map();
1074
+ const componentTagMap = /* @__PURE__ */ new Map();
1075
+ const componentIdMap = /* @__PURE__ */ new Map();
782
1076
  let appNavigationDefaults = {};
1077
+ let appComponentTags = {};
783
1078
  let scanResult = {
784
1079
  app: void 0,
785
1080
  pages: [],
@@ -795,10 +1090,10 @@ function weappWebPlugin(options = {}) {
795
1090
  root = config.root;
796
1091
  srcRoot = resolve(root, options.srcDir ?? "src");
797
1092
  enableHmr = config.command === "serve";
798
- await scanProject();
1093
+ await scanProject(this.warn?.bind(this));
799
1094
  },
800
1095
  async buildStart() {
801
- await scanProject();
1096
+ await scanProject(this.warn?.bind(this));
802
1097
  },
803
1098
  resolveId(id) {
804
1099
  if (id === "/@weapp-vite/web/entry" || id === "@weapp-vite/web/entry") {
@@ -815,19 +1110,22 @@ function weappWebPlugin(options = {}) {
815
1110
  async handleHotUpdate(ctx) {
816
1111
  const clean = cleanUrl(ctx.file);
817
1112
  if (clean.endsWith(".json") || isTemplateFile(clean) || isWxsFile(clean) || clean.endsWith(".wxss") || SCRIPT_EXTS.includes(extname(clean))) {
818
- await scanProject();
1113
+ await scanProject(this.warn?.bind(this));
819
1114
  }
820
1115
  },
821
1116
  transform(code, id) {
822
1117
  const clean = cleanUrl(id);
823
1118
  if (isTemplateFile(clean)) {
824
- const navigationConfig = pageNavigationMap.get(normalizePath2(clean));
1119
+ const normalizedId = normalizePath2(clean);
1120
+ const navigationConfig = pageNavigationMap.get(normalizedId);
1121
+ const componentTags = templateComponentMap.get(normalizedId);
825
1122
  const { code: compiled, dependencies, warnings } = compileWxml({
826
1123
  id: clean,
827
1124
  source: code,
828
1125
  resolveTemplatePath,
829
1126
  resolveWxsPath,
830
- navigationBar: navigationConfig ? { config: navigationConfig } : void 0
1127
+ navigationBar: navigationConfig ? { config: navigationConfig } : void 0,
1128
+ componentTags
831
1129
  });
832
1130
  if (dependencies.length > 0 && "addWatchFile" in this) {
833
1131
  for (const dep of dependencies) {
@@ -841,8 +1139,8 @@ function weappWebPlugin(options = {}) {
841
1139
  }
842
1140
  return { code: compiled, map: null };
843
1141
  }
844
- if (isWxsFile(clean)) {
845
- const { code: compiled, dependencies } = transformWxsToEsm(code, clean, {
1142
+ if (isWxsFile(clean) || hasWxsQuery(id)) {
1143
+ const { code: compiled, dependencies, warnings } = transformWxsToEsm(code, clean, {
846
1144
  resolvePath: resolveWxsPath,
847
1145
  toImportPath: (resolved, importer) => normalizePath2(toRelativeImport2(importer, resolved))
848
1146
  });
@@ -851,6 +1149,11 @@ function weappWebPlugin(options = {}) {
851
1149
  this.addWatchFile(dep);
852
1150
  }
853
1151
  }
1152
+ if (warnings?.length && "warn" in this) {
1153
+ for (const warning of warnings) {
1154
+ this.warn(warning);
1155
+ }
1156
+ }
854
1157
  return { code: compiled, map: null };
855
1158
  }
856
1159
  if (TRANSFORM_STYLE_EXTS.some((ext) => clean.endsWith(ext))) {
@@ -981,12 +1284,25 @@ if (import.meta.hot) { import.meta.hot.accept() }
981
1284
  };
982
1285
  }
983
1286
  };
984
- async function scanProject() {
1287
+ async function scanProject(warn) {
985
1288
  moduleMeta.clear();
986
1289
  pageNavigationMap.clear();
1290
+ templateComponentMap.clear();
1291
+ componentTagMap.clear();
1292
+ componentIdMap.clear();
987
1293
  appNavigationDefaults = {};
1294
+ appComponentTags = {};
988
1295
  const pages = /* @__PURE__ */ new Map();
989
1296
  const components = /* @__PURE__ */ new Map();
1297
+ const reportWarning = (message) => {
1298
+ if (warn) {
1299
+ warn(message);
1300
+ return;
1301
+ }
1302
+ if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
1303
+ process.emitWarning(message);
1304
+ }
1305
+ };
990
1306
  const appScript = await resolveScriptFile(join(srcRoot, "app"));
991
1307
  if (appScript) {
992
1308
  moduleMeta.set(
@@ -1002,6 +1318,11 @@ if (import.meta.hot) { import.meta.hot.accept() }
1002
1318
  const appJsonPath = join(srcRoot, "app.json");
1003
1319
  if (await fs.pathExists(appJsonPath)) {
1004
1320
  const appJson = await readJsonFile(appJsonPath);
1321
+ if (appJson) {
1322
+ appComponentTags = await collectComponentTagsFromConfig(appJson, srcRoot, appJsonPath, reportWarning, (tags) => {
1323
+ appComponentTags = tags;
1324
+ });
1325
+ }
1005
1326
  if (appJson?.pages && Array.isArray(appJson.pages)) {
1006
1327
  for (const page of appJson.pages) {
1007
1328
  if (typeof page === "string") {
@@ -1059,17 +1380,22 @@ if (import.meta.hot) { import.meta.hot.accept() }
1059
1380
  script,
1060
1381
  id: toPosixId(pageId)
1061
1382
  });
1383
+ const pageComponentTags = pageJson ? await collectComponentTagsFromConfig(pageJson, dirname2(script), pageJsonPath, reportWarning) : await collectComponentTagsFromJson(pageJsonPath, dirname2(script), reportWarning);
1384
+ if (template) {
1385
+ const mergedTags = mergeComponentTags(appComponentTags, pageComponentTags);
1386
+ if (Object.keys(mergedTags).length > 0) {
1387
+ templateComponentMap.set(normalizePath2(template), mergedTags);
1388
+ } else {
1389
+ templateComponentMap.delete(normalizePath2(template));
1390
+ }
1391
+ }
1062
1392
  if (pageJson) {
1063
- await collectComponentsFromConfig(pageJson, dirname2(script));
1064
1393
  if (template) {
1065
1394
  const config = mergeNavigationConfig(appNavigationDefaults, pickNavigationConfig(pageJson));
1066
1395
  pageNavigationMap.set(normalizePath2(template), config);
1067
1396
  }
1068
- } else {
1069
- await collectComponentsFromJson(pageJsonPath, dirname2(script));
1070
- if (template) {
1071
- pageNavigationMap.set(normalizePath2(template), { ...appNavigationDefaults });
1072
- }
1397
+ } else if (template) {
1398
+ pageNavigationMap.set(normalizePath2(template), { ...appNavigationDefaults });
1073
1399
  }
1074
1400
  }
1075
1401
  async function collectComponent(componentId, importerDir) {
@@ -1099,26 +1425,94 @@ if (import.meta.hot) { import.meta.hot.accept() }
1099
1425
  script,
1100
1426
  id: componentIdPosix
1101
1427
  });
1102
- await collectComponentsFromJson(`${script.replace(new RegExp(`${extname(script)}$`), "")}.json`, dirname2(script));
1428
+ const componentJsonPath = `${script.replace(new RegExp(`${extname(script)}$`), "")}.json`;
1429
+ const componentTags = await collectComponentTagsFromJson(componentJsonPath, dirname2(script), reportWarning);
1430
+ if (template) {
1431
+ const mergedTags = mergeComponentTags(appComponentTags, componentTags);
1432
+ if (Object.keys(mergedTags).length > 0) {
1433
+ templateComponentMap.set(normalizePath2(template), mergedTags);
1434
+ } else {
1435
+ templateComponentMap.delete(normalizePath2(template));
1436
+ }
1437
+ }
1103
1438
  }
1104
- async function collectComponentsFromConfig(json, importerDir) {
1439
+ async function collectComponentTagsFromConfig(json, importerDir, jsonPath, warn2, onResolved) {
1105
1440
  const usingComponents = json.usingComponents;
1106
1441
  if (!usingComponents || typeof usingComponents !== "object") {
1107
- return;
1442
+ return {};
1108
1443
  }
1109
- for (const value of Object.values(usingComponents)) {
1110
- if (typeof value !== "string") {
1444
+ const tags = {};
1445
+ const resolved = [];
1446
+ for (const [rawKey, rawValue] of Object.entries(usingComponents)) {
1447
+ if (typeof rawValue !== "string") {
1448
+ continue;
1449
+ }
1450
+ const key = normalizeComponentKey(rawKey);
1451
+ if (!key) {
1111
1452
  continue;
1112
1453
  }
1113
- await collectComponent(value, importerDir);
1454
+ const script = await resolveComponentScript(rawValue, importerDir);
1455
+ if (!script) {
1456
+ warn2(`[@weapp-vite/web] usingComponents entry "${rawKey}" not found: ${rawValue} (${jsonPath})`);
1457
+ continue;
1458
+ }
1459
+ const tag = getComponentTag(script);
1460
+ if (tag) {
1461
+ tags[key] = tag;
1462
+ resolved.push({ rawValue });
1463
+ }
1464
+ }
1465
+ onResolved?.(tags);
1466
+ for (const entry of resolved) {
1467
+ await collectComponent(entry.rawValue, importerDir);
1114
1468
  }
1469
+ return tags;
1115
1470
  }
1116
- async function collectComponentsFromJson(jsonPath, importerDir) {
1471
+ async function collectComponentTagsFromJson(jsonPath, importerDir, warn2) {
1117
1472
  const json = await readJsonFile(jsonPath);
1118
1473
  if (!json) {
1119
- return;
1474
+ return {};
1475
+ }
1476
+ return collectComponentTagsFromConfig(json, importerDir, jsonPath, warn2);
1477
+ }
1478
+ function mergeComponentTags(base, overrides) {
1479
+ if (!Object.keys(base).length && !Object.keys(overrides).length) {
1480
+ return {};
1120
1481
  }
1121
- await collectComponentsFromConfig(json, importerDir);
1482
+ return {
1483
+ ...base,
1484
+ ...overrides
1485
+ };
1486
+ }
1487
+ function normalizeComponentKey(raw) {
1488
+ return raw.trim().toLowerCase();
1489
+ }
1490
+ function getComponentId(script) {
1491
+ const cached = componentIdMap.get(script);
1492
+ if (cached) {
1493
+ return cached;
1494
+ }
1495
+ const idRelative = relative2(srcRoot, script).replace(new RegExp(`${extname(script)}$`), "");
1496
+ const componentIdPosix = toPosixId(idRelative);
1497
+ componentIdMap.set(script, componentIdPosix);
1498
+ return componentIdPosix;
1499
+ }
1500
+ function getComponentTag(script) {
1501
+ const cached = componentTagMap.get(script);
1502
+ if (cached) {
1503
+ return cached;
1504
+ }
1505
+ const id = getComponentId(script);
1506
+ const tag = slugify(id, "wv-component");
1507
+ componentTagMap.set(script, tag);
1508
+ return tag;
1509
+ }
1510
+ async function resolveComponentScript(raw, importerDir) {
1511
+ const base = resolveComponentBase(raw, importerDir);
1512
+ if (!base) {
1513
+ return void 0;
1514
+ }
1515
+ return resolveScriptFile(base);
1122
1516
  }
1123
1517
  function resolveComponentBase(raw, importerDir) {
1124
1518
  if (!raw) {
@@ -1214,11 +1608,18 @@ function resolveTemplatePathSync(raw, importer, srcRoot) {
1214
1608
  return resolveFileWithExtensionsSync(base, TEMPLATE_EXTS);
1215
1609
  }
1216
1610
  function resolveWxsPathSync(raw, importer, srcRoot) {
1217
- const base = resolveImportBase(raw, importer, srcRoot);
1218
- 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 {
1219
1620
  return void 0;
1220
1621
  }
1221
- return resolveFileWithExtensionsSync(base, WXS_EXTS);
1622
+ return resolveFileWithExtensionsSync(base, WXS_RESOLVE_EXTS);
1222
1623
  }
1223
1624
  async function resolveScriptFile(basePath) {
1224
1625
  const ext = extname(basePath);
@@ -1397,7 +1798,23 @@ var NAV_BUTTON_TAG = "weapp-button";
1397
1798
  var BaseElement = globalThis.HTMLElement ?? class {
1398
1799
  };
1399
1800
  var formConfig = { ...DEFAULT_FORM_CONFIG };
1400
- var styleInjected = false;
1801
+ var styleTargets = /* @__PURE__ */ new WeakSet();
1802
+ var sharedSheet;
1803
+ function resolveAdoptedStyleSheets(root) {
1804
+ const doc = root.ownerDocument ?? document;
1805
+ if (!doc || typeof doc.createElement !== "function") {
1806
+ return void 0;
1807
+ }
1808
+ if (!("adoptedStyleSheets" in doc)) {
1809
+ return void 0;
1810
+ }
1811
+ if (!sharedSheet && "replaceSync" in CSSStyleSheet.prototype) {
1812
+ const sheet = new CSSStyleSheet();
1813
+ sheet.replaceSync(BUTTON_STYLE);
1814
+ sharedSheet = sheet;
1815
+ }
1816
+ return sharedSheet;
1817
+ }
1401
1818
  var BUTTON_STYLE = `
1402
1819
  weapp-button {
1403
1820
  display: block;
@@ -1415,8 +1832,8 @@ weapp-button .weapp-btn {
1415
1832
  -webkit-appearance: none;
1416
1833
  box-sizing: border-box;
1417
1834
  width: 100%;
1418
- border-radius: 6px;
1419
- border: 1px solid #d7d7d7;
1835
+ border-radius: 5px;
1836
+ border: 1px solid #d9d9d9;
1420
1837
  padding: 0 16px;
1421
1838
  height: 44px;
1422
1839
  line-height: 44px;
@@ -1447,7 +1864,7 @@ weapp-button.weapp-btn--plain .weapp-btn {
1447
1864
  }
1448
1865
 
1449
1866
  weapp-button.weapp-btn--plain.weapp-btn--default .weapp-btn {
1450
- border-color: #353535;
1867
+ border-color: #b2b2b2;
1451
1868
  color: #353535;
1452
1869
  }
1453
1870
 
@@ -1463,21 +1880,34 @@ weapp-button.weapp-btn--plain.weapp-btn--warn .weapp-btn {
1463
1880
 
1464
1881
  weapp-button.weapp-btn--loading .weapp-btn,
1465
1882
  weapp-button.weapp-btn--disabled .weapp-btn {
1466
- opacity: 0.6;
1883
+ background-color: #f7f7f7;
1884
+ border-color: #d9d9d9;
1885
+ color: #9b9b9b;
1467
1886
  cursor: not-allowed;
1468
1887
  }
1469
1888
 
1470
1889
  weapp-button.button-hover .weapp-btn {
1471
- opacity: 0.7;
1890
+ background-color: #ededed;
1891
+ border-color: #d2d2d2;
1892
+ }
1893
+
1894
+ weapp-button.button-hover.weapp-btn--primary .weapp-btn {
1895
+ background-color: #06ad56;
1896
+ border-color: #06ad56;
1897
+ }
1898
+
1899
+ weapp-button.button-hover.weapp-btn--warn .weapp-btn {
1900
+ background-color: #d93c37;
1901
+ border-color: #d93c37;
1472
1902
  }
1473
1903
 
1474
1904
  weapp-button.button-hover.weapp-btn--plain .weapp-btn {
1475
- background-color: rgba(0, 0, 0, 0.1);
1905
+ background-color: rgba(0, 0, 0, 0.06);
1476
1906
  }
1477
1907
 
1478
1908
  weapp-button.weapp-btn--mini .weapp-btn {
1479
- height: 28px;
1480
- line-height: 28px;
1909
+ height: 32px;
1910
+ line-height: 32px;
1481
1911
  font-size: 13px;
1482
1912
  padding: 0 12px;
1483
1913
  border-radius: 4px;
@@ -1509,12 +1939,36 @@ weapp-button .weapp-btn__loading[hidden] {
1509
1939
  to { transform: rotate(360deg); }
1510
1940
  }
1511
1941
  `;
1512
- function ensureButtonStyle() {
1513
- if (styleInjected) {
1942
+ function ensureButtonStyle(root) {
1943
+ if (typeof document === "undefined") {
1944
+ return;
1945
+ }
1946
+ if (root && styleTargets.has(root)) {
1947
+ return;
1948
+ }
1949
+ if (!root) {
1950
+ const target = document.head;
1951
+ if (!target || styleTargets.has(target)) {
1952
+ return;
1953
+ }
1954
+ injectStyle(BUTTON_STYLE, BUTTON_STYLE_ID);
1955
+ styleTargets.add(target);
1956
+ return;
1957
+ }
1958
+ const sheet = resolveAdoptedStyleSheets(root);
1959
+ if (sheet) {
1960
+ const existing = root.adoptedStyleSheets ?? [];
1961
+ if (!existing.includes(sheet)) {
1962
+ root.adoptedStyleSheets = [...existing, sheet];
1963
+ }
1964
+ styleTargets.add(root);
1514
1965
  return;
1515
1966
  }
1516
- injectStyle(BUTTON_STYLE, BUTTON_STYLE_ID);
1517
- styleInjected = true;
1967
+ const style = document.createElement("style");
1968
+ style.id = BUTTON_STYLE_ID;
1969
+ style.textContent = BUTTON_STYLE;
1970
+ root.appendChild(style);
1971
+ styleTargets.add(root);
1518
1972
  }
1519
1973
  function toBoolean(value) {
1520
1974
  if (value === null) {
@@ -1665,7 +2119,12 @@ var WeappButton = class extends BaseElement {
1665
2119
  #lastTouchTime = 0;
1666
2120
  #observer;
1667
2121
  connectedCallback() {
1668
- ensureButtonStyle();
2122
+ const root = this.getRootNode();
2123
+ if (root instanceof ShadowRoot) {
2124
+ ensureButtonStyle(root);
2125
+ } else {
2126
+ ensureButtonStyle();
2127
+ }
1669
2128
  this.#ensureStructure();
1670
2129
  this.#applyState();
1671
2130
  this.#bindEvents();
@@ -1952,7 +2411,7 @@ function normalizeKey(rawKey, item, index, scope) {
1952
2411
  const expr = key.replace(/^\{\{\s*/, "").replace(/\s*\}\}$/, "");
1953
2412
  return evaluateExpression(expr, scope);
1954
2413
  }
1955
- if (!/[\.\[\(\)]/.test(key) && item && typeof item === "object" && key in item) {
2414
+ if (!/[.[()]/.test(key) && item && typeof item === "object" && key in item) {
1956
2415
  return item[key];
1957
2416
  }
1958
2417
  return evaluateExpression(key, scope);
@@ -2001,6 +2460,16 @@ function createWxsModule(code, id, requireMap) {
2001
2460
  return module.exports;
2002
2461
  }
2003
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
+ };
2004
2473
  return {
2005
2474
  instance,
2006
2475
  eval: (expression, scope, wxs) => {
@@ -2023,11 +2492,10 @@ function createRenderContext(instance, methods) {
2023
2492
  return normalizeKey(rawKey, item, index, renderScope);
2024
2493
  },
2025
2494
  renderTemplate: (templates, name, scope, ctx) => {
2026
- if (!name) {
2027
- return "";
2028
- }
2029
- const template = templates?.[name];
2495
+ const resolvedName = typeof name === "string" ? name : String(name);
2496
+ const template = templates?.[resolvedName];
2030
2497
  if (typeof template !== "function") {
2498
+ warnMissingTemplate(resolvedName);
2031
2499
  return "";
2032
2500
  }
2033
2501
  return template(scope, ctx);
@@ -2136,6 +2604,78 @@ function coerceValue(value, type) {
2136
2604
  }
2137
2605
  return value;
2138
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
+ }
2139
2679
  function defineComponent(tagName, options) {
2140
2680
  if (!options || typeof options !== "object") {
2141
2681
  throw new TypeError("[@weapp-vite/web] defineComponent \u9700\u8981\u63D0\u4F9B\u914D\u7F6E\u5BF9\u8C61\u3002");
@@ -2146,13 +2686,20 @@ function defineComponent(tagName, options) {
2146
2686
  return existing;
2147
2687
  }
2148
2688
  const BaseElement3 = supportsLit ? LitElement : globalThis.HTMLElement ?? FallbackElement;
2149
- const { template, style = "", component = {} } = options;
2689
+ const { template, style = "", component = {}, observerInit = false } = options;
2150
2690
  if (!template) {
2151
2691
  throw new Error("[@weapp-vite/web] defineComponent \u9700\u8981\u63D0\u4F9B\u6A21\u677F\u6E32\u67D3\u51FD\u6570\u3002");
2152
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
+ }
2153
2699
  let templateRef = template;
2154
2700
  let styleRef = style;
2155
- let componentRef = component;
2701
+ let componentRef = normalized.component ?? component;
2702
+ let observerInitEnabled = Boolean(observerInit);
2156
2703
  let propertyEntries = Object.entries(componentRef.properties ?? {});
2157
2704
  let observedAttributes = propertyEntries.map(([name]) => hyphenate(name));
2158
2705
  let defaultPropertyValues = propertyEntries.reduce((acc, [name, prop]) => {
@@ -2176,6 +2723,8 @@ function defineComponent(tagName, options) {
2176
2723
  #renderContext = createRenderContext(this, {});
2177
2724
  #usesLegacyTemplate = false;
2178
2725
  #readyFired = false;
2726
+ #observerInitDone = false;
2727
+ #observedKeys = /* @__PURE__ */ new Set();
2179
2728
  constructor() {
2180
2729
  super();
2181
2730
  const dataOption = typeof componentRef.data === "function" ? componentRef.data() : componentRef.data ?? {};
@@ -2226,6 +2775,9 @@ function defineComponent(tagName, options) {
2226
2775
  superConnected.call(this);
2227
2776
  }
2228
2777
  this.#applyAttributes();
2778
+ if (observerInitEnabled) {
2779
+ this.#runInitialObservers();
2780
+ }
2229
2781
  lifetimes.attached?.call(this);
2230
2782
  this.#isMounted = true;
2231
2783
  if (!supportsLit) {
@@ -2306,7 +2858,11 @@ function defineComponent(tagName, options) {
2306
2858
  this.#state[key] = value;
2307
2859
  if (Object.prototype.hasOwnProperty.call(this.#properties, key)) {
2308
2860
  this.#properties[key] = value;
2309
- 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
+ }
2310
2866
  }
2311
2867
  changed = true;
2312
2868
  }
@@ -2327,7 +2883,24 @@ function defineComponent(tagName, options) {
2327
2883
  if (this.#isMounted) {
2328
2884
  this.requestUpdate();
2329
2885
  }
2330
- 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
+ }
2331
2904
  }
2332
2905
  #syncMethods(nextMethods) {
2333
2906
  const resolved = nextMethods ?? {};
@@ -2375,7 +2948,14 @@ function defineComponent(tagName, options) {
2375
2948
  }
2376
2949
  templateRef = nextOptions.template;
2377
2950
  styleRef = nextOptions.style ?? "";
2378
- 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);
2379
2959
  lifetimes = componentRef.lifetimes ?? {};
2380
2960
  propertyEntries = Object.entries(componentRef.properties ?? {});
2381
2961
  observedAttributes = propertyEntries.map(([name]) => hyphenate(name));
@@ -2728,10 +3308,6 @@ var RESERVED_COMPONENT_METHOD_KEYS = /* @__PURE__ */ new Set([
2728
3308
  "observers",
2729
3309
  "mixins"
2730
3310
  ]);
2731
- function slugify(id, prefix) {
2732
- const normalized = id.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase();
2733
- return `${prefix}-${normalized || "index"}`;
2734
- }
2735
3311
  function ensureDocumentReady(callback) {
2736
3312
  if (typeof document === "undefined") {
2737
3313
  return;
@@ -3309,6 +3885,8 @@ function normalizeTagName2(name) {
3309
3885
  return "select";
3310
3886
  case "block":
3311
3887
  return "#fragment";
3888
+ case "slot":
3889
+ return "slot";
3312
3890
  default:
3313
3891
  return name || "div";
3314
3892
  }