@dimina/compiler 1.0.16 → 1.1.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.
@@ -1,5 +1,5 @@
1
- import { _ as tagWhiteList, a as getContentByPath, d as resetStoreInfo, h as getAbsolutePath, i as getComponent, l as getTargetPath, m as collectAssets, n as getAppId, u as getWorkPath, v as transformRpx } from "../env-p9Az8lv5.js";
2
- import { t as checkTemplateCompatibility } from "../compatibility-C_zWVjYl.js";
1
+ import { C as tagWhiteList, T as toMiniProgramModuleId, a as getComponent, b as getAbsolutePath, d as getTargetPath, f as getTemplateExts, g as resetStoreInfo, h as getWorkPath, m as getViewScriptTags, n as getAppId, o as getContentByPath, p as getViewScriptExts, w as transformRpx, y as collectAssets } from "../env-OH3--qg8.js";
2
+ import { r as takeCompatibilityWarnings, t as checkTemplateCompatibility } from "../compatibility-DdxfVhcK.js";
3
3
  import path from "node:path";
4
4
  import { isMainThread, parentPort } from "node:worker_threads";
5
5
  import fs from "node:fs";
@@ -14,7 +14,7 @@ import * as htmlparser2 from "htmlparser2";
14
14
  /**
15
15
  * 表达式解析器 - 使用 Oxc AST 解析器提取依赖
16
16
  */
17
- var KEYWORDS = new Set([
17
+ var KEYWORDS = /* @__PURE__ */ new Set([
18
18
  "true",
19
19
  "false",
20
20
  "null",
@@ -123,7 +123,19 @@ function parseBindings(bindings) {
123
123
  }
124
124
  //#endregion
125
125
  //#region src/core/view-compiler.js
126
- var fileType = [".wxml", ".ddml"];
126
+ /**
127
+ * 根据扩展名列表生成匹配尾部扩展名的正则,如 ['.wxs', '.qds'] -> /(\.wxs|\.qds)$/
128
+ */
129
+ function buildExtStripRegex(exts) {
130
+ const alt = exts.map((e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
131
+ return new RegExp(`(${alt})$`);
132
+ }
133
+ /**
134
+ * 移除视图脚本文件路径末尾的扩展名,支持 .wxs 和自定义扩展名
135
+ */
136
+ function stripViewScriptExt(p) {
137
+ return p.replace(buildExtStripRegex(getViewScriptExts()), "");
138
+ }
127
139
  /**
128
140
  * 解析 JavaScript 代码
129
141
  * @param {string} code
@@ -241,7 +253,10 @@ if (!isMainThread) parentPort.on("message", async ({ pages, storeInfo }) => {
241
253
  compileResCache.clear();
242
254
  wxsModuleRegistry.clear();
243
255
  wxsFilePathMap.clear();
244
- parentPort.postMessage({ success: true });
256
+ parentPort.postMessage({
257
+ success: true,
258
+ compatibilityWarnings: takeCompatibilityWarnings()
259
+ });
245
260
  } catch (error) {
246
261
  compileResCache.clear();
247
262
  wxsModuleRegistry.clear();
@@ -263,17 +278,26 @@ async function compileML(pages, root, progress) {
263
278
  initWxsFilePathMap(getWorkPath());
264
279
  for (const page of pages) {
265
280
  const scriptRes = /* @__PURE__ */ new Map();
266
- buildCompileView(page, false, scriptRes, [], /* @__PURE__ */ new Set());
281
+ buildCompileView(page, false, scriptRes, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
267
282
  let mergeRender = "";
268
283
  for (const [key, value] of scriptRes.entries()) {
269
- const { code: minifiedCode } = await transform(`modDefine('${key}', function(require, module, exports) {
284
+ const amdFormat = `modDefine('${key}', function(require, module, exports) {
270
285
  ${value}
271
- });`, {
272
- minify: true,
273
- target: ["es2020"],
274
- platform: "browser"
275
- });
276
- mergeRender += minifiedCode;
286
+ });`;
287
+ try {
288
+ const { code: minifiedCode } = await transform(amdFormat, {
289
+ minify: true,
290
+ target: ["es2020"],
291
+ platform: "browser"
292
+ });
293
+ mergeRender += minifiedCode;
294
+ } catch (error) {
295
+ const location = error.errors?.[0]?.location;
296
+ const sourceLines = amdFormat.split("\n");
297
+ const sourceHint = location?.line ? sourceLines.slice(Math.max(0, location.line - 3), location.line + 2).map((line, index) => `${Math.max(1, location.line - 2) + index}: ${line.trim()}`).join("\n") : "";
298
+ error.message = `视图模块 ${key} 转换失败: ${error.message}${sourceHint ? `\n${sourceHint}` : ""}`;
299
+ throw error;
300
+ }
277
301
  }
278
302
  scriptRes.clear();
279
303
  const filename = `${page.path.replace(/\//g, "_")}`;
@@ -310,8 +334,8 @@ function scanWxsFiles(dir, workPath) {
310
334
  const fullPath = path.join(dir, item);
311
335
  const stat = fs.statSync(fullPath);
312
336
  if (stat.isDirectory()) scanWxsFiles(fullPath, workPath);
313
- else if (stat.isFile() && item.endsWith(".wxs")) {
314
- const moduleName = fullPath.replace(workPath, "").replace(/\.wxs$/, "").replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
337
+ else if (stat.isFile() && getViewScriptExts().some((ext) => item.endsWith(ext))) {
338
+ const moduleName = stripViewScriptExt(fullPath.replace(workPath, "")).replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
315
339
  wxsFilePathMap.set(moduleName, fullPath);
316
340
  }
317
341
  }
@@ -324,6 +348,17 @@ function scanWxsFiles(dir, workPath) {
324
348
  function registerWxsModule(modulePath) {
325
349
  wxsModuleRegistry.add(modulePath);
326
350
  }
351
+ function getTemplateCompilerOptions(scopeId) {
352
+ return {
353
+ prefixIdentifiers: true,
354
+ hoistStatic: false,
355
+ cacheHandlers: true,
356
+ scopeId,
357
+ mode: "function",
358
+ inline: true,
359
+ isCustomElement: (tag) => !tag.startsWith("dd-")
360
+ };
361
+ }
327
362
  /**
328
363
  * 检查是否为已注册的 wxs 模块
329
364
  * @param {string} modulePath - 模块路径
@@ -332,17 +367,10 @@ function registerWxsModule(modulePath) {
332
367
  function isRegisteredWxsModule(modulePath) {
333
368
  return wxsModuleRegistry.has(modulePath);
334
369
  }
335
- function buildCompileView(module, isComponent = false, scriptRes, depthChain = [], inheritedTemplatePaths = /* @__PURE__ */ new Set()) {
370
+ function buildCompileView(module, isComponent = false, scriptRes, activePaths = /* @__PURE__ */ new Set(), inheritedTemplatePaths = /* @__PURE__ */ new Set()) {
336
371
  const currentPath = module.path;
337
- if (depthChain.includes(currentPath)) {
338
- console.warn("[view]", `检测到循环依赖: ${[...depthChain, currentPath].join(" -> ")}`);
339
- return;
340
- }
341
- if (depthChain.length > 20) {
342
- console.warn("[view]", `检测到深度依赖: ${[...depthChain, currentPath].join(" -> ")}`);
343
- return;
344
- }
345
- depthChain = [...depthChain, currentPath];
372
+ if (activePaths.has(currentPath)) return;
373
+ activePaths.add(currentPath);
346
374
  const allScriptModules = [];
347
375
  const currentInstruction = compileModule(module, isComponent, scriptRes, { skipTemplatePaths: isComponent ? inheritedTemplatePaths : /* @__PURE__ */ new Set() });
348
376
  if (currentInstruction && currentInstruction.scriptModule) allScriptModules.push(...currentInstruction.scriptModule);
@@ -351,11 +379,8 @@ function buildCompileView(module, isComponent = false, scriptRes, depthChain = [
351
379
  if (module.usingComponents) for (const componentInfo of Object.values(module.usingComponents)) {
352
380
  const componentModule = getComponent(componentInfo);
353
381
  if (!componentModule) continue;
354
- if (componentModule.path === module.path) {
355
- console.warn("[view]", `检测到循环依赖,跳过处理: ${module.path}`);
356
- continue;
357
- }
358
- const componentInstruction = buildCompileView(componentModule, true, scriptRes, depthChain, childInheritedTemplatePaths);
382
+ if (componentModule.path === module.path) continue;
383
+ const componentInstruction = buildCompileView(componentModule, true, scriptRes, activePaths, childInheritedTemplatePaths);
359
384
  if (componentInstruction && componentInstruction.scriptModule) {
360
385
  for (const sm of componentInstruction.scriptModule) if (!allScriptModules.find((existing) => existing.path === sm.path)) allScriptModules.push(sm);
361
386
  }
@@ -364,6 +389,7 @@ function buildCompileView(module, isComponent = false, scriptRes, depthChain = [
364
389
  for (const sm of allScriptModules) if (!scriptRes.has(sm.path)) scriptRes.set(sm.path, sm.code);
365
390
  compileModuleWithAllWxs(module, scriptRes, allScriptModules);
366
391
  }
392
+ activePaths.delete(currentPath);
367
393
  return {
368
394
  scriptModule: allScriptModules,
369
395
  templateModule: currentInstruction?.templateModule || []
@@ -416,14 +442,7 @@ function compileModule(module, isComponent, scriptRes, options = {}) {
416
442
  filename: module.path,
417
443
  id: `data-v-${module.id}`,
418
444
  scoped: true,
419
- compilerOptions: {
420
- prefixIdentifiers: true,
421
- hoistStatic: false,
422
- cacheHandlers: true,
423
- scopeId: `data-v-${module.id}`,
424
- mode: "function",
425
- inline: true
426
- }
445
+ compilerOptions: getTemplateCompilerOptions(`data-v-${module.id}`)
427
446
  });
428
447
  let tplComponents = "{";
429
448
  for (const tm of compileInstruction.templateModule) {
@@ -432,14 +451,7 @@ function compileModule(module, isComponent, scriptRes, options = {}) {
432
451
  filename: tm.path,
433
452
  id: `data-v-${module.id}`,
434
453
  scoped: true,
435
- compilerOptions: {
436
- prefixIdentifiers: true,
437
- hoistStatic: false,
438
- cacheHandlers: true,
439
- scopeId: `data-v-${module.id}`,
440
- mode: "function",
441
- inline: true
442
- }
454
+ compilerOptions: getTemplateCompilerOptions(`data-v-${module.id}`)
443
455
  });
444
456
  code = insertWxsToRenderCode(code, compileInstruction.scriptModule, scriptRes, tm.path);
445
457
  tplComponents += `'${tm.path}':${code},`;
@@ -449,8 +461,12 @@ function compileModule(module, isComponent, scriptRes, options = {}) {
449
461
  const code = `Module({
450
462
  path: '${module.path}',
451
463
  id: '${module.id}',
464
+ appStyleScopeId: ${JSON.stringify(module.appStyleScopeId || null)},
465
+ sharedStyleScopeIds: ${JSON.stringify(module.sharedStyleScopeIds || [])},
466
+ styleIsolation: ${JSON.stringify(module.styleIsolation || "isolated")},
452
467
  render: ${transCode},
453
468
  usingComponents: ${JSON.stringify(module.usingComponents)},
469
+ customTabBar: ${JSON.stringify(module.customTabBar || null)},
454
470
  tplComponents: ${tplComponents},
455
471
  });`;
456
472
  const allWxsModules = collectAllWxsModules(scriptRes, /* @__PURE__ */ new Set(), compileInstruction.scriptModule || []);
@@ -523,7 +539,7 @@ function processWxsContent(wxsContent, wxsFilePath, scriptModule, workPath, file
523
539
  if (filePath && filePath.includes("/miniprogram_npm/")) {
524
540
  const currentWxsDir = path.dirname(wxsFilePath);
525
541
  resolvedWxsPath = path.resolve(currentWxsDir, requirePath);
526
- const moduleName = resolvedWxsPath.replace(workPath, "").replace(/\.wxs$/, "").replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
542
+ const moduleName = stripViewScriptExt(resolvedWxsPath.replace(workPath, "")).replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
527
543
  processWxsDependency(resolvedWxsPath, moduleName, scriptModule, workPath, filePath);
528
544
  replacements.push({
529
545
  start: node.arguments[0].start,
@@ -533,7 +549,7 @@ function processWxsContent(wxsContent, wxsFilePath, scriptModule, workPath, file
533
549
  } else {
534
550
  const currentWxsDir = path.dirname(wxsFilePath);
535
551
  resolvedWxsPath = path.resolve(currentWxsDir, requirePath);
536
- const depModuleName = resolvedWxsPath.replace(workPath, "").replace(/\.wxs$/, "").replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
552
+ const depModuleName = stripViewScriptExt(resolvedWxsPath.replace(workPath, "")).replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
537
553
  processWxsDependency(resolvedWxsPath, depModuleName, scriptModule, workPath, filePath);
538
554
  replacements.push({
539
555
  start: node.arguments[0].start,
@@ -601,14 +617,7 @@ function compileModuleWithAllWxs(module, scriptRes, allScriptModules) {
601
617
  filename: module.path,
602
618
  id: `data-v-${module.id}`,
603
619
  scoped: true,
604
- compilerOptions: {
605
- prefixIdentifiers: true,
606
- hoistStatic: false,
607
- cacheHandlers: true,
608
- scopeId: `data-v-${module.id}`,
609
- mode: "function",
610
- inline: true
611
- }
620
+ compilerOptions: getTemplateCompilerOptions(`data-v-${module.id}`)
612
621
  });
613
622
  let tplComponents = "{";
614
623
  for (const tm of mergedInstruction.templateModule) {
@@ -617,14 +626,7 @@ function compileModuleWithAllWxs(module, scriptRes, allScriptModules) {
617
626
  filename: tm.path,
618
627
  id: `data-v-${module.id}`,
619
628
  scoped: true,
620
- compilerOptions: {
621
- prefixIdentifiers: true,
622
- hoistStatic: false,
623
- cacheHandlers: true,
624
- scopeId: `data-v-${module.id}`,
625
- mode: "function",
626
- inline: true
627
- }
629
+ compilerOptions: getTemplateCompilerOptions(`data-v-${module.id}`)
628
630
  });
629
631
  code = insertWxsToRenderCode(code, allScriptModules, scriptRes, tm.path);
630
632
  tplComponents += `'${tm.path}':${code},`;
@@ -634,8 +636,12 @@ function compileModuleWithAllWxs(module, scriptRes, allScriptModules) {
634
636
  const code = `Module({
635
637
  path: '${module.path}',
636
638
  id: '${module.id}',
639
+ appStyleScopeId: ${JSON.stringify(module.appStyleScopeId || null)},
640
+ sharedStyleScopeIds: ${JSON.stringify(module.sharedStyleScopeIds || [])},
641
+ styleIsolation: ${JSON.stringify(module.styleIsolation || "isolated")},
637
642
  render: ${transCode},
638
643
  usingComponents: ${JSON.stringify(module.usingComponents)},
644
+ customTabBar: ${JSON.stringify(module.customTabBar || null)},
639
645
  tplComponents: ${tplComponents},
640
646
  });`;
641
647
  const cacheData = {
@@ -717,12 +723,13 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
717
723
  const workPath = getWorkPath();
718
724
  const fullPath = getViewPath(workPath, path);
719
725
  if (!fullPath) return { tpl: void 0 };
726
+ const sourcePath = toMiniProgramModuleId(fullPath, workPath).replace(buildExtStripRegex(getTemplateExts()), "");
720
727
  const diagnosticSource = fullPath.startsWith(workPath) ? fullPath.slice(workPath.length) : path;
721
728
  let content = getContentByPath(fullPath).trim();
722
729
  if (!content) content = "<block></block>";
723
730
  else {
724
731
  checkTemplateCompatibility(content, diagnosticSource, components);
725
- if (isComponent) content = `<wrapper name="${path}">${content}</wrapper>`;
732
+ if (isComponent) content = `<component-host name="${path}">${content}</component-host>`;
726
733
  else if (cheerio.load(content, {
727
734
  xmlMode: true,
728
735
  decodeEntities: false
@@ -740,8 +747,8 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
740
747
  $("include").each((_, elem) => {
741
748
  const src = $(elem).attr("src");
742
749
  if (src) {
743
- const includeFullPath = getAbsolutePath(workPath, path, src);
744
- let includePath = includeFullPath.replace(workPath, "").replace(/\.(wxml|ddml)$/, "");
750
+ const includeFullPath = resolveTemplateDependencyPath(workPath, sourcePath, src);
751
+ let includePath = includeFullPath.replace(workPath, "").replace(buildExtStripRegex(getTemplateExts()), "");
745
752
  const includeDiagnosticSource = includeFullPath.startsWith(workPath) ? includeFullPath.slice(workPath.length) : includePath;
746
753
  if (!includePath.startsWith("/")) includePath = "/" + includePath;
747
754
  const includeContent = getContentByPath(includeFullPath).trim();
@@ -755,21 +762,20 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
755
762
  transTagWxs($includeContent, scriptModule, includePath);
756
763
  processIncludedFileWxsDependencies(includeContent, includePath, scriptModule, components, processedPaths);
757
764
  $includeContent("template").remove();
758
- $includeContent("wxs").remove();
759
- $includeContent("dds").remove();
765
+ $includeContent(getViewScriptTags().join(",")).remove();
760
766
  const processedContent = processIncludeConditionalAttrs($, elem, $includeContent.html());
761
767
  $(elem).replaceWith(processedContent);
762
768
  } else $(elem).remove();
763
769
  } else $(elem).remove();
764
770
  });
765
- transTagTemplate($, templateModule, path, components, componentPlaceholder);
766
- transTagWxs($, scriptModule, path);
771
+ transTagTemplate($, templateModule, sourcePath, components, componentPlaceholder);
772
+ transTagWxs($, scriptModule, sourcePath);
767
773
  const importNodes = $("import");
768
774
  importNodes.each((_, elem) => {
769
775
  const src = $(elem).attr("src");
770
776
  if (src) {
771
- const importFullPath = getAbsolutePath(workPath, path, src);
772
- let importPath = importFullPath.replace(workPath, "").replace(/\.(wxml|ddml)$/, "");
777
+ const importFullPath = resolveTemplateDependencyPath(workPath, sourcePath, src);
778
+ let importPath = importFullPath.replace(workPath, "").replace(buildExtStripRegex(getTemplateExts()), "");
773
779
  const importDiagnosticSource = importFullPath.startsWith(workPath) ? importFullPath.slice(workPath.length) : importPath;
774
780
  if (!importPath.startsWith("/")) importPath = "/" + importPath;
775
781
  const importContent = getContentByPath(importFullPath).trim();
@@ -779,14 +785,14 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
779
785
  xmlMode: true,
780
786
  decodeEntities: false
781
787
  });
782
- transTagTemplate($$, templateModule, path, components, componentPlaceholder);
788
+ transTagTemplate($$, templateModule, importPath, components, componentPlaceholder);
783
789
  transTagWxs($$, scriptModule, importPath);
784
790
  processIncludedFileWxsDependencies(importContent, importPath, scriptModule, components, processedPaths);
785
791
  }
786
792
  }
787
793
  });
788
794
  importNodes.remove();
789
- transAsses($, $("image"), path);
795
+ transAsses($, $("image"), sourcePath);
790
796
  const res = [];
791
797
  transHtmlTag($.html(), res, components, componentPlaceholder);
792
798
  return {
@@ -804,8 +810,7 @@ function transTagTemplate($, templateModule, path, components, componentPlacehol
804
810
  const templateContent = $(elem);
805
811
  templateContent.find("import").remove();
806
812
  templateContent.find("include").remove();
807
- templateContent.find("wxs").remove();
808
- templateContent.find("dds").remove();
813
+ templateContent.find(getViewScriptTags().join(",")).remove();
809
814
  transAsses($, templateContent.find("image"), path);
810
815
  const res = [];
811
816
  transHtmlTag(templateContent.html(), res, components, componentPlaceholder);
@@ -822,6 +827,72 @@ function transAsses($, imageNodes, path) {
822
827
  if (!imgSrc.startsWith("{{")) $(elem).attr("src", collectAssets(getWorkPath(), path, imgSrc, getTargetPath(), getAppId()));
823
828
  });
824
829
  }
830
+ var DIMINA_SLOT_GROUP_TAG = "dimina-slot-group";
831
+ var DIMINA_FOR_SCOPE_TAG = "dimina-for-scope";
832
+ function getDirectiveAttributeNames(attrs, suffixes) {
833
+ return Object.keys(attrs || {}).filter((name) => suffixes.some((suffix) => name.endsWith(suffix)));
834
+ }
835
+ function hasForAndIf(attrs) {
836
+ return getDirectiveAttributeNames(attrs, [":for", ":for-items"]).length > 0 && getDirectiveAttributeNames(attrs, [":if"]).length > 0;
837
+ }
838
+ function groupDuplicateNamedSlots($, components) {
839
+ const slotHosts = $("*").toArray().filter((element) => {
840
+ const tag = element.tagName;
841
+ return tag === "component" || Boolean(components?.[tag]);
842
+ });
843
+ for (const host of slotHosts) {
844
+ const groups = /* @__PURE__ */ new Map();
845
+ for (const child of $(host).children().toArray()) {
846
+ const slotName = child.attribs?.slot;
847
+ if (!slotName) continue;
848
+ const group = groups.get(slotName) || [];
849
+ group.push(child);
850
+ groups.set(slotName, group);
851
+ }
852
+ for (const [slotName, nodes] of groups) {
853
+ if (nodes.length === 1 && !hasForAndIf(nodes[0].attribs)) continue;
854
+ const wrapper = $(`<${DIMINA_SLOT_GROUP_TAG}></${DIMINA_SLOT_GROUP_TAG}>`);
855
+ wrapper.attr("name", slotName);
856
+ $(nodes[0]).before(wrapper);
857
+ for (const node of nodes) {
858
+ $(node).removeAttr("slot");
859
+ wrapper.append(node);
860
+ }
861
+ }
862
+ }
863
+ }
864
+ function wrapForIfScopes($) {
865
+ const nodes = $("*").toArray();
866
+ for (const node of nodes) {
867
+ if (node.tagName === DIMINA_FOR_SCOPE_TAG || !hasForAndIf(node.attribs)) continue;
868
+ const attrsToMove = getDirectiveAttributeNames(node.attribs, [
869
+ ":for",
870
+ ":for-items",
871
+ ":for-item",
872
+ ":for-index",
873
+ ":key"
874
+ ]);
875
+ const wrapper = $(`<${DIMINA_FOR_SCOPE_TAG}></${DIMINA_FOR_SCOPE_TAG}>`);
876
+ for (const name of attrsToMove) {
877
+ wrapper.attr(name, node.attribs[name]);
878
+ $(node).removeAttr(name);
879
+ }
880
+ $(node).before(wrapper);
881
+ wrapper.append(node);
882
+ }
883
+ }
884
+ function normalizeTemplateSyntax(html, components) {
885
+ const $ = cheerio.load(html, {
886
+ xmlMode: true,
887
+ decodeEntities: false,
888
+ _useHtmlParser2: true,
889
+ lowerCaseTags: false,
890
+ lowerCaseAttributeNames: false
891
+ });
892
+ groupDuplicateNamedSlots($, components);
893
+ wrapForIfScopes($);
894
+ return $.html();
895
+ }
825
896
  function transHtmlTag(html, res, components, componentPlaceholder) {
826
897
  const attrsList = [];
827
898
  const parser = new htmlparser2.Parser({
@@ -849,7 +920,7 @@ function transHtmlTag(html, res, components, componentPlaceholder) {
849
920
  console.error(error);
850
921
  }
851
922
  }, { xmlMode: true });
852
- parser.write(html);
923
+ parser.write(normalizeTemplateSyntax(html, components));
853
924
  parser.end();
854
925
  }
855
926
  /**
@@ -859,11 +930,15 @@ function transHtmlTag(html, res, components, componentPlaceholder) {
859
930
  function transTag(opts) {
860
931
  const { isStart, tag, attrs, components } = opts;
861
932
  let res;
862
- if (tag === "slot") res = tag;
933
+ if (tag === DIMINA_SLOT_GROUP_TAG) {
934
+ if (isStart) return `<template ${generateSlotDirective(attrs.name)}>`;
935
+ return "</template>";
936
+ } else if (tag === DIMINA_FOR_SCOPE_TAG) res = "template";
937
+ else if (tag === "slot") res = tag;
863
938
  else if (components && components[tag]) res = `dd-${tag}`;
864
939
  else if (tag === "component" || tag === "canvas") res = tag;
865
- else if (!tagWhiteList.includes(tag)) res = "dd-text";
866
- else res = `dd-${tag}`;
940
+ else if (tagWhiteList.includes(tag)) res = `dd-${tag}`;
941
+ else res = tag;
867
942
  let tagRes;
868
943
  const propsAry = isStart ? getProps(attrs, tag, components) : [];
869
944
  const multipleSlots = attrs?.slot;
@@ -908,7 +983,17 @@ function generateSlotDirective(slotValue) {
908
983
  */
909
984
  function getProps(attrs, tag, components) {
910
985
  const attrsList = [];
986
+ const isCustomComponent = Boolean(components && components[tag]);
911
987
  const propBindings = {};
988
+ const hasEventBindings = Object.keys(attrs).some((name) => /^(?:capture-)?(?:bind|catch)(?::)?.+/.test(name));
989
+ if (tag === "page-meta") attrsList.push({
990
+ name: "dimina-rpx-unit",
991
+ value: "vw"
992
+ });
993
+ if (hasEventBindings) attrsList.push({
994
+ name: "v-c-event-node",
995
+ value: components && components[tag] ? "'component'" : "'node'"
996
+ });
912
997
  Object.entries(attrs).forEach(([name, value]) => {
913
998
  if (name.endsWith(":if")) attrsList.push({
914
999
  name: "v-if",
@@ -932,11 +1017,20 @@ function getProps(attrs, tag, components) {
932
1017
  name: ":key",
933
1018
  value: tranValue
934
1019
  });
935
- } else if (name === "style") attrsList.push({
936
- name: "v-c-style",
937
- value: transformRpx(parseSafeBraceExp(value))
938
- });
939
- else if (name === "class") {
1020
+ } else if (name === "style") {
1021
+ const parsedStyle = parseSafeBraceExp(value);
1022
+ attrsList.push({
1023
+ name: "v-c-style",
1024
+ value: transformRpx(parsedStyle)
1025
+ });
1026
+ if (isCustomComponent) {
1027
+ attrsList.push({
1028
+ name: ":dimina-wxml-style",
1029
+ value: parsedStyle
1030
+ });
1031
+ if (isWrappedByBraces(value) && parsedStyle) propBindings.style = parsedStyle;
1032
+ }
1033
+ } else if (name === "class") {
940
1034
  if (isWrappedByBraces(value)) attrsList.push({
941
1035
  name: ":class",
942
1036
  value: parseClassRules(value)
@@ -988,7 +1082,7 @@ function getProps(attrs, tag, components) {
988
1082
  });
989
1083
  else if (isWrappedByBraces(value)) {
990
1084
  const pVal = tag === "template" && name === "data" ? parseTemplateDataExp(value) : parseSafeBraceExp(value);
991
- if (components && components[tag]) {
1085
+ if (isCustomComponent) {
992
1086
  if (pVal && typeof pVal === "string") propBindings[name] = pVal;
993
1087
  }
994
1088
  attrsList.push({
@@ -1007,7 +1101,7 @@ function getProps(attrs, tag, components) {
1007
1101
  else if (/\$\{[^}]*\}/.test(value)) propsRes.push(`:${name}="\`${value}\`"`);
1008
1102
  else propsRes.push(`${name}="${escapeQuotes(value)}"`);
1009
1103
  });
1010
- if (components && components[tag] && Object.keys(propBindings).length > 0) try {
1104
+ if (isCustomComponent && Object.keys(propBindings).length > 0) try {
1011
1105
  const validBindings = {};
1012
1106
  for (const [key, value] of Object.entries(propBindings)) if (value !== void 0 && value !== null && typeof value === "string") validBindings[key] = value;
1013
1107
  if (Object.keys(validBindings).length > 0) {
@@ -1071,7 +1165,7 @@ function parseKeyExpression(exp, itemName = "item", indexName = "index") {
1071
1165
  */
1072
1166
  function getViewPath(workPath, src) {
1073
1167
  const aSrc = src.startsWith("/") ? src : `/${src}`;
1074
- for (const mlType of fileType) {
1168
+ for (const mlType of getTemplateExts()) {
1075
1169
  const mlFullPath = `${workPath}${aSrc}${mlType}`;
1076
1170
  if (fs.existsSync(mlFullPath)) return mlFullPath;
1077
1171
  const indexMlFullPath = `${workPath}${aSrc}/index${mlType}`;
@@ -1079,6 +1173,20 @@ function getViewPath(workPath, src) {
1079
1173
  }
1080
1174
  }
1081
1175
  /**
1176
+ * 解析 import/include 的模板文件路径。
1177
+ * 微信允许省略 .wxml;显式扩展名保持原样,无扩展名时按当前模板类型优先级补全。
1178
+ */
1179
+ function resolveTemplateDependencyPath(workPath, ownerPath, src) {
1180
+ const resolvedPath = getAbsolutePath(workPath, ownerPath, src);
1181
+ if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) return resolvedPath;
1182
+ if (path.extname(resolvedPath)) return resolvedPath;
1183
+ for (const ext of getTemplateExts()) {
1184
+ const candidate = `${resolvedPath}${ext}`;
1185
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return candidate;
1186
+ }
1187
+ return resolvedPath;
1188
+ }
1189
+ /**
1082
1190
  * 将字符串内部的双引号进行替换
1083
1191
  * @param {*} input
1084
1192
  */
@@ -1119,7 +1227,7 @@ function splitWithBraces(str) {
1119
1227
  function parseClassRules(cssRule) {
1120
1228
  let list = splitWithBraces(cssRule);
1121
1229
  list = list.map((item) => {
1122
- return parseBraceExp(item);
1230
+ return parseSafeBraceExp(item);
1123
1231
  });
1124
1232
  if (list.length === 1) return list.pop();
1125
1233
  return `[${list.join(",")}]`;
@@ -1147,11 +1255,16 @@ function getForIndexName(attrs) {
1147
1255
  * @param {*} attrs
1148
1256
  */
1149
1257
  function parseForExp(exp, attrs) {
1150
- return `(${getForItemName(attrs)}, ${getForIndexName(attrs)}) in ${parseBraceExp(exp)}`;
1258
+ return `(${getForItemName(attrs)}, ${getForIndexName(attrs)}) in ${parseSafeBraceExp(exp)}`;
1151
1259
  }
1152
1260
  var braceRegex = /(\{\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}\})|([^{}]+)/g;
1153
1261
  var noBraceRegex = /\{\{((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\}\}/;
1154
1262
  var ternaryRegex = /[^?]+\?.+:.+/;
1263
+ var RESERVED_TEMPLATE_CONTEXT_ALIASES = /* @__PURE__ */ new Map([["class", "__dimina_reserved_class"]]);
1264
+ var RESERVED_TEMPLATE_CONTEXT_NAMES = new Map([...RESERVED_TEMPLATE_CONTEXT_ALIASES].map(([name, alias]) => [alias, name]));
1265
+ function encodeReservedTemplateContextIdentifier(expression) {
1266
+ return RESERVED_TEMPLATE_CONTEXT_ALIASES.get(expression) || expression;
1267
+ }
1155
1268
  /**
1156
1269
  * 解析 {{}} 表达式的值
1157
1270
  * @param {*} exp
@@ -1163,7 +1276,7 @@ function parseBraceExp(exp) {
1163
1276
  if (result[1]) {
1164
1277
  const matchResult = result[1].match(noBraceRegex);
1165
1278
  if (matchResult) {
1166
- const statement = matchResult[1].trim();
1279
+ const statement = encodeReservedTemplateContextIdentifier(matchResult[1].trim());
1167
1280
  if (ternaryRegex.test(statement)) group.push(`(${statement})`);
1168
1281
  else group.push(statement);
1169
1282
  }
@@ -1179,12 +1292,11 @@ function parseBraceExp(exp) {
1179
1292
  */
1180
1293
  function parseTemplateDataExp(exp) {
1181
1294
  const matchResult = exp.trim().match(/^\{\{([\s\S]*)\}\}$/);
1182
- if (matchResult) return `{${matchResult[1].trim()}}`;
1183
- return `{${parseBraceExp(exp)}}`;
1295
+ if (matchResult) return addOptionalChaining(`{${matchResult[1].trim()}}`);
1296
+ return `{${parseSafeBraceExp(exp)}}`;
1184
1297
  }
1185
1298
  function transTagWxs($, scriptModule, filePath) {
1186
- let wxsNodes = $("wxs");
1187
- if (wxsNodes.length === 0) wxsNodes = $("dds");
1299
+ const wxsNodes = $(getViewScriptTags().join(","));
1188
1300
  wxsNodes.each((_, elem) => {
1189
1301
  const smName = $(elem).attr("module");
1190
1302
  if (smName) {
@@ -1200,7 +1312,7 @@ function transTagWxs($, scriptModule, filePath) {
1200
1312
  wxsFilePath = path.resolve(componentFullPath, src);
1201
1313
  } else wxsFilePath = getAbsolutePath(workPath, filePath, src);
1202
1314
  if (wxsFilePath) {
1203
- uniqueModuleName = wxsFilePath.replace(workPath, "").replace(/\.wxs$/, "").replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
1315
+ uniqueModuleName = stripViewScriptExt(wxsFilePath.replace(workPath, "")).replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
1204
1316
  cacheKey = wxsFilePath;
1205
1317
  }
1206
1318
  }
@@ -1248,7 +1360,7 @@ function collectAllWxsModules(scriptRes, collectedPaths = /* @__PURE__ */ new Se
1248
1360
  });
1249
1361
  const dependencies = extractWxsDependencies(moduleCode);
1250
1362
  for (const depPath of dependencies) if (!collectedPaths.has(depPath)) if (scriptRes.has(depPath)) {
1251
- const depModules = collectAllWxsModules(new Map([[depPath, scriptRes.get(depPath)]]), collectedPaths, scriptModule);
1363
+ const depModules = collectAllWxsModules(/* @__PURE__ */ new Map([[depPath, scriptRes.get(depPath)]]), collectedPaths, scriptModule);
1252
1364
  allWxsModules.push(...depModules);
1253
1365
  } else {
1254
1366
  const loaded = loadWxsModule(depPath, workPath, scriptModule);
@@ -1256,7 +1368,7 @@ function collectAllWxsModules(scriptRes, collectedPaths = /* @__PURE__ */ new Se
1256
1368
  scriptRes.set(depPath, loaded.code);
1257
1369
  allWxsModules.push(loaded);
1258
1370
  collectedPaths.add(depPath);
1259
- const depModules = collectAllWxsModules(new Map([[depPath, loaded.code]]), collectedPaths, scriptModule);
1371
+ const depModules = collectAllWxsModules(/* @__PURE__ */ new Map([[depPath, loaded.code]]), collectedPaths, scriptModule);
1260
1372
  allWxsModules.push(...depModules);
1261
1373
  }
1262
1374
  }
@@ -1272,12 +1384,8 @@ function collectAllWxsModules(scriptRes, collectedPaths = /* @__PURE__ */ new Se
1272
1384
  * @returns {Object|null} 加载的模块对象或 null
1273
1385
  */
1274
1386
  function loadWxsModule(modulePath, workPath, scriptModule) {
1275
- if (!modulePath.startsWith("miniprogram_npm__") || !modulePath.includes("_wxs_")) return null;
1276
1387
  const wxsFilePath = wxsFilePathMap.get(modulePath);
1277
- if (!wxsFilePath) {
1278
- console.warn(`[view] 无法找到 wxs 模块文件: ${modulePath}`);
1279
- return null;
1280
- }
1388
+ if (!wxsFilePath) return null;
1281
1389
  try {
1282
1390
  const wxsContent = getContentByPath(wxsFilePath).trim();
1283
1391
  if (!wxsContent) return null;
@@ -1325,8 +1433,7 @@ function insertWxsToRenderCode(code, scriptModule, scriptRes, filename = "render
1325
1433
  });
1326
1434
  declarations.push(`const ${localIdentifier} = require(${JSON.stringify(requireModuleName)});`);
1327
1435
  }
1328
- if (wxsBindings.length === 0) return getProgramCode(code, ast);
1329
- if (renderBody?.type === "BlockStatement") codeReplacements.push({
1436
+ if (wxsBindings.length > 0 && renderBody?.type === "BlockStatement") codeReplacements.push({
1330
1437
  type: "insert",
1331
1438
  start: renderBody.start + 1,
1332
1439
  end: renderBody.start + 1,
@@ -1334,6 +1441,15 @@ function insertWxsToRenderCode(code, scriptModule, scriptRes, filename = "render
1334
1441
  });
1335
1442
  walk(ast, { enter(node) {
1336
1443
  if (node.type === "MemberExpression" && node.object?.type === "Identifier" && node.object.name === "_ctx" && !node.computed && node.property?.type === "Identifier") {
1444
+ const reservedName = RESERVED_TEMPLATE_CONTEXT_NAMES.get(node.property.name);
1445
+ if (reservedName) {
1446
+ codeReplacements.push({
1447
+ start: node.property.start,
1448
+ end: node.property.end,
1449
+ value: reservedName
1450
+ });
1451
+ return;
1452
+ }
1337
1453
  const replacement = wxsBindings.find((item) => item.templatePropertyName === node.property.name);
1338
1454
  if (replacement) codeReplacements.push({
1339
1455
  start: node.start,
@@ -1342,8 +1458,9 @@ function insertWxsToRenderCode(code, scriptModule, scriptRes, filename = "render
1342
1458
  });
1343
1459
  }
1344
1460
  } });
1461
+ if (codeReplacements.length === 0) return getProgramCode(code, ast);
1345
1462
  const transformed = applyCodeReplacements(code, codeReplacements);
1346
1463
  return getProgramCode(transformed, parseJs(transformed, filename));
1347
1464
  }
1348
1465
  //#endregion
1349
- export { compileML, generateSlotDirective, generateVModelTemplate, parseBraceExp, parseClassRules, parseKeyExpression, parseTemplateDataExp, processIncludeConditionalAttrs, processWxsContent, splitWithBraces };
1466
+ export { compileML, generateSlotDirective, generateVModelTemplate, initWxsFilePathMap, loadWxsModule, normalizeTemplateSyntax, parseBraceExp, parseClassRules, parseKeyExpression, parseTemplateDataExp, processIncludeConditionalAttrs, processWxsContent, splitWithBraces };