@maizzle/framework 6.0.0-rc.24 → 6.0.0-rc.26

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.
Files changed (143) hide show
  1. package/dist/build.d.ts +19 -1
  2. package/dist/build.d.ts.map +1 -1
  3. package/dist/build.js +139 -102
  4. package/dist/build.js.map +1 -1
  5. package/dist/components/Body.vue +12 -0
  6. package/dist/components/Button.vue +16 -29
  7. package/dist/components/CodeBlock.vue +5 -4
  8. package/dist/components/CodeInline.vue +9 -8
  9. package/dist/components/Column.vue +17 -4
  10. package/dist/components/Container.vue +7 -11
  11. package/dist/components/Hr.vue +1 -1
  12. package/dist/components/Img.vue +39 -22
  13. package/dist/components/Layout.vue +1 -1
  14. package/dist/components/Markdown.vue +9 -14
  15. package/dist/components/QrCode.vue +2 -2
  16. package/dist/components/Section.vue +9 -6
  17. package/dist/components/Text.vue +2 -2
  18. package/dist/components/utils.d.ts +25 -1
  19. package/dist/components/utils.d.ts.map +1 -1
  20. package/dist/components/utils.js +29 -1
  21. package/dist/components/utils.js.map +1 -1
  22. package/dist/components/utils.ts +46 -0
  23. package/dist/composables/useConfig.d.ts.map +1 -1
  24. package/dist/composables/useCurrentTemplate.d.ts.map +1 -1
  25. package/dist/composables/useEvent.d.ts.map +1 -1
  26. package/dist/composables/useFont.js.map +1 -1
  27. package/dist/config/index.js +1 -1
  28. package/dist/config/index.js.map +1 -1
  29. package/dist/events/index.d.ts.map +1 -1
  30. package/dist/events/index.js.map +1 -1
  31. package/dist/index.js +2 -2
  32. package/dist/plaintext.js.map +1 -1
  33. package/dist/plugins/postcss/mergeMediaQueries.js.map +1 -1
  34. package/dist/plugins/postcss/pruneVars.js.map +1 -1
  35. package/dist/plugins/postcss/quoteFontFamilies.d.ts.map +1 -1
  36. package/dist/plugins/postcss/quoteFontFamilies.js.map +1 -1
  37. package/dist/plugins/postcss/removeDeclarations.js.map +1 -1
  38. package/dist/plugins/postcss/resolveProps.d.ts.map +1 -1
  39. package/dist/plugins/postcss/resolveProps.js +0 -3
  40. package/dist/plugins/postcss/resolveProps.js.map +1 -1
  41. package/dist/plugins/postcss/tailwindCleanup.js.map +1 -1
  42. package/dist/prepare.js +1 -1
  43. package/dist/prepare.js.map +1 -1
  44. package/dist/render/active.d.ts.map +1 -1
  45. package/dist/render/buildTemplate.d.ts +49 -0
  46. package/dist/render/buildTemplate.d.ts.map +1 -0
  47. package/dist/render/buildTemplate.js +139 -0
  48. package/dist/render/buildTemplate.js.map +1 -0
  49. package/dist/render/createRenderer.d.ts +3 -1
  50. package/dist/render/createRenderer.d.ts.map +1 -1
  51. package/dist/render/createRenderer.js +43 -10
  52. package/dist/render/createRenderer.js.map +1 -1
  53. package/dist/render/index.js +1 -1
  54. package/dist/render/index.js.map +1 -1
  55. package/dist/render/injectFonts.js.map +1 -1
  56. package/dist/render/parallel/buildWorker.d.ts +31 -0
  57. package/dist/render/parallel/buildWorker.d.ts.map +1 -0
  58. package/dist/render/parallel/buildWorker.js +66 -0
  59. package/dist/render/parallel/buildWorker.js.map +1 -0
  60. package/dist/render/parallel/worker.mjs +28 -0
  61. package/dist/render/plugins/codeBlockExtract.d.ts.map +1 -1
  62. package/dist/render/plugins/codeBlockExtract.js.map +1 -1
  63. package/dist/render/plugins/markdownExtract.d.ts.map +1 -1
  64. package/dist/render/plugins/markdownExtract.js.map +1 -1
  65. package/dist/render/plugins/rawExtract.d.ts.map +1 -1
  66. package/dist/render/plugins/rawExtract.js.map +1 -1
  67. package/dist/render/plugins/rowSourceLocation.d.ts.map +1 -1
  68. package/dist/render/plugins/rowSourceLocation.js.map +1 -1
  69. package/dist/serve.d.ts.map +1 -1
  70. package/dist/serve.js +73 -53
  71. package/dist/serve.js.map +1 -1
  72. package/dist/server/compatibility.d.ts.map +1 -1
  73. package/dist/server/compatibility.js.map +1 -1
  74. package/dist/server/linter.js.map +1 -1
  75. package/dist/server/sfc-utils.js +1 -1
  76. package/dist/server/sfc-utils.js.map +1 -1
  77. package/dist/server/ui/pages/Preview.vue +34 -11
  78. package/dist/server/ui/vite-env.d.ts +1 -0
  79. package/dist/tests/render/_helpers.d.ts.map +1 -1
  80. package/dist/tests/render/_helpers.js.map +1 -1
  81. package/dist/transformers/addAttributes.js +2 -3
  82. package/dist/transformers/addAttributes.js.map +1 -1
  83. package/dist/transformers/base.d.ts +1 -1
  84. package/dist/transformers/base.d.ts.map +1 -1
  85. package/dist/transformers/base.js +5 -10
  86. package/dist/transformers/base.js.map +1 -1
  87. package/dist/transformers/columnWidth.d.ts.map +1 -1
  88. package/dist/transformers/columnWidth.js +2 -7
  89. package/dist/transformers/columnWidth.js.map +1 -1
  90. package/dist/transformers/entities.js.map +1 -1
  91. package/dist/transformers/filters/defaults.js.map +1 -1
  92. package/dist/transformers/filters/index.js.map +1 -1
  93. package/dist/transformers/format.js.map +1 -1
  94. package/dist/transformers/imgWidth.d.ts +20 -0
  95. package/dist/transformers/imgWidth.d.ts.map +1 -0
  96. package/dist/transformers/imgWidth.js +76 -0
  97. package/dist/transformers/imgWidth.js.map +1 -0
  98. package/dist/transformers/index.d.ts.map +1 -1
  99. package/dist/transformers/index.js +2 -0
  100. package/dist/transformers/index.js.map +1 -1
  101. package/dist/transformers/inlineCss.d.ts +3 -2
  102. package/dist/transformers/inlineCss.d.ts.map +1 -1
  103. package/dist/transformers/inlineCss.js.map +1 -1
  104. package/dist/transformers/inlineLink.js +1 -1
  105. package/dist/transformers/inlineLink.js.map +1 -1
  106. package/dist/transformers/minify.js.map +1 -1
  107. package/dist/transformers/minifyCodeInline.js.map +1 -1
  108. package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
  109. package/dist/transformers/msoPlaceholders.js +2 -7
  110. package/dist/transformers/msoPlaceholders.js.map +1 -1
  111. package/dist/transformers/purgeCss.js.map +1 -1
  112. package/dist/transformers/replaceStrings.js.map +1 -1
  113. package/dist/transformers/safeSelectors.js.map +1 -1
  114. package/dist/transformers/shorthandCss.js.map +1 -1
  115. package/dist/transformers/tailwindComponent.js.map +1 -1
  116. package/dist/transformers/tailwindcss.js +1 -1
  117. package/dist/transformers/tailwindcss.js.map +1 -1
  118. package/dist/transformers/urlQuery.js.map +1 -1
  119. package/dist/types/config.d.ts +26 -4
  120. package/dist/types/config.d.ts.map +1 -1
  121. package/dist/utils/ast/serializer.js.map +1 -1
  122. package/dist/utils/compileTailwindCss.js.map +1 -1
  123. package/dist/utils/componentSources.js.map +1 -1
  124. package/dist/utils/cssBox.d.ts.map +1 -1
  125. package/dist/utils/cssBox.js +2 -7
  126. package/dist/utils/cssBox.js.map +1 -1
  127. package/dist/utils/decodeStyleEntities.js.map +1 -1
  128. package/dist/utils/url.js.map +1 -1
  129. package/dist/utils/watchPaths.js.map +1 -1
  130. package/node_modules/@clack/core/CHANGELOG.md +30 -0
  131. package/node_modules/@clack/core/dist/index.d.mts +109 -3
  132. package/node_modules/@clack/core/dist/index.mjs +972 -17
  133. package/node_modules/@clack/core/package.json +2 -1
  134. package/node_modules/@clack/prompts/CHANGELOG.md +42 -0
  135. package/node_modules/@clack/prompts/README.md +30 -9
  136. package/node_modules/@clack/prompts/dist/index.d.mts +458 -27
  137. package/node_modules/@clack/prompts/dist/index.mjs +1378 -141
  138. package/node_modules/@clack/prompts/package.json +2 -2
  139. package/node_modules/tinyexec/package.json +4 -4
  140. package/package.json +13 -11
  141. package/dist/components/Overlap.vue +0 -156
  142. package/node_modules/@clack/core/dist/index.mjs.map +0 -1
  143. package/node_modules/@clack/prompts/dist/index.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"compatibility.js","names":["compileWithPipeline"],"sources":["../../src/server/compatibility.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport postcss, { type Declaration, type AtRule, type Rule } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport valueParser from 'postcss-value-parser'\nimport { Parser } from 'htmlparser2'\nimport { DomHandler, type ChildNode, type Element } from 'domhandler'\nimport { parseSfcBlocks, findComponentTags, buildComponentMap, isFrameworkComponent, type SfcBlock } from './sfc-utils.ts'\nimport { scanLint } from './linter.ts'\nimport { tailwindcss as compileWithPipeline } from '../transformers/tailwindcss.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport type { NormalizedComponentSource } from '../utils/componentSources.ts'\n\nconst API_URL = 'https://www.caniemail.com/api/data.json'\nconst DEFAULT_CLIENTS = new Set(['gmail', 'apple-mail', 'outlook', 'yahoo'])\n\ntype SupportLevel = 'unsupported' | 'mitigated' | 'unknown'\n\ninterface Feature {\n slug: string\n title: string\n url: string\n category: string\n stats: any\n}\n\ninterface Issue {\n kind: 'compat' | 'lint'\n slug?: string\n title: string\n url?: string\n category: string\n line?: number\n file: string\n // compat-only\n supportLevel?: SupportLevel\n supportLabel?: string\n affectedClients?: string[]\n // lint-only\n severity?: 'error' | 'warning'\n message?: string\n}\n\ninterface Indexes {\n nicenames: { supported: string, mitigated: string, unsupported: string, unknown: string, mixed: string }\n familyNicenames: Record<string, string>\n cssProp: Map<string, Feature[]>\n cssPropValue: Map<string, Array<{ value: string, feature: Feature }>>\n cssAtRule: Map<string, Feature[]>\n cssMediaFeature: Map<string, Feature[]>\n cssPseudoClass: Map<string, Feature[]>\n cssPseudoElement: Map<string, Feature[]>\n cssFunction: Map<string, Feature[]>\n cssUnit: Map<string, Feature[]>\n cssImportant?: Feature\n cssVariables?: Feature\n cssNesting?: Feature\n cssComments?: Feature\n cssModernColor?: Feature\n htmlTag: Map<string, Feature[]>\n htmlAttr: Map<string, Feature[]>\n htmlInputType: Map<string, Feature[]>\n htmlButtonType: Map<string, Feature[]>\n htmlDoctype?: Feature\n htmlComments?: Feature\n htmlAnchorLinks?: Feature\n htmlMailtoLinks?: Feature\n htmlMetaColorScheme?: Feature\n htmlSemantics?: Feature\n htmlStyleInBody?: Feature\n imageExt: Map<string, Feature[]>\n /** All features by slug — unfiltered, used for URL lookups (e.g. by lint). */\n bySlug: Map<string, { title: string, url: string }>\n}\n\nlet indexes: Indexes | null = null\nlet initPromise: Promise<Indexes | null> | null = null\n\nfunction mpush<K, V>(m: Map<K, V[]>, k: K, v: V) {\n const arr = m.get(k)\n if (arr) arr.push(v)\n else m.set(k, [v])\n}\n\nfunction emptyIndexes(nicenames: any, familyNicenames: Record<string, string>): Indexes {\n return {\n nicenames,\n familyNicenames,\n cssProp: new Map(), cssPropValue: new Map(), cssAtRule: new Map(),\n cssMediaFeature: new Map(), cssPseudoClass: new Map(), cssPseudoElement: new Map(),\n cssFunction: new Map(), cssUnit: new Map(),\n htmlTag: new Map(), htmlAttr: new Map(), htmlInputType: new Map(), htmlButtonType: new Map(),\n imageExt: new Map(),\n bySlug: new Map(),\n }\n}\n\nfunction hasAnyNonY(stats: any): boolean {\n if (!stats) return false\n for (const family in stats) {\n for (const plat in stats[family]) {\n for (const ver in stats[family][plat]) {\n const v = stripNotes(String(stats[family][plat][ver]).trim())\n if (v && v !== 'y') return true\n }\n }\n }\n return false\n}\n\n/** Strip `#N` note markers — `\"y #1\"` → `\"y\"`. Notes document edge cases but\n * don't change support semantics, so treat `y #1` as fully supported. */\nfunction stripNotes(v: string): string {\n return v.split(/\\s+/).filter(t => t && !t.startsWith('#')).join(' ')\n}\n\nfunction computeSupport(stats: any, familyNicenames: Record<string, string>, allowedClients: Set<string> | 'all'): { level: SupportLevel, affected: string[] } | null {\n let nY = 0, nN = 0, nU = 0, nPartial = 0, total = 0\n const affectedFamilies = new Set<string>()\n for (const family in stats) {\n if (allowedClients !== 'all' && !allowedClients.has(family)) continue\n let familyHasNonY = false\n for (const plat in stats[family]) {\n /**\n * Only score the latest version per (family, platform) — legacy\n * versions (Outlook 2007, etc.) otherwise flag modern-widely\n * supported features as partial forever.\n */\n const versions = Object.keys(stats[family][plat]).sort()\n const latest = versions[versions.length - 1]\n if (!latest) continue\n total++\n const v = stripNotes(String(stats[family][plat][latest]).trim())\n if (v === 'y') nY++\n else if (v === 'n') { nN++; familyHasNonY = true }\n else if (v === 'u') { nU++; familyHasNonY = true }\n else { nPartial++; familyHasNonY = true }\n }\n if (familyHasNonY) affectedFamilies.add(family)\n }\n if (!total) return null\n if (nY === total) return null\n const affected = [...affectedFamilies].map(f => familyNicenames[f] ?? f).sort()\n if (nN === total) return { level: 'unsupported', affected }\n if (nU === total) return { level: 'unknown', affected }\n return { level: 'mitigated', affected }\n}\n\n/**\n * Slugs we never report. Fundamental HTML (every email uses these) plus\n * CSS noise that's not actionable (comments, !important usage).\n */\nconst IGNORED_SLUGS = new Set([\n // Required/unavoidable tags\n 'html-doctype', 'html-comments',\n 'html-html', 'html-head', 'html-body', 'html-title',\n 'html-meta', 'html-meta-color-scheme',\n 'html-style', 'html-link',\n 'html-div', 'html-span', 'html-br', 'html-p', 'html-a', 'html-img',\n 'html-table', 'html-tr', 'html-td', 'html-th',\n 'html-thead', 'html-tbody', 'html-tfoot',\n 'html-h1-h6', 'html-lists',\n 'html-strong', 'html-em', 'html-b', 'html-i', 'html-u',\n 'html-semantics',\n // Ubiquitous attributes — always present, caveats aren't actionable.\n 'html-role', 'html-hidden', 'html-width', 'html-height',\n // CSS noise\n 'css-comments', 'css-important',\n /**\n * CSS fundamentals — universally used with known minor caveats; flagging\n * them as \"partial\" is noise rather than signal.\n */\n 'css-margin', 'css-padding', 'css-border',\n 'css-font-size', 'css-font-weight', 'css-font', 'css-font-family',\n 'css-line-height', 'css-letter-spacing', 'css-text-align',\n 'css-text-decoration', 'css-text-transform', 'css-color',\n 'css-background', 'css-background-color',\n 'css-width', 'css-height',\n 'css-display',\n])\n\nfunction classify(f: Feature, idx: Indexes) {\n const slug = f.slug\n /**\n * Retain html-style feature for the body-only detector even though it's\n * blacklisted from the normal html-tag detection path. Title is\n * suffixed so the flag reads as a body-placement warning, not a\n * blanket `<style>`.\n */\n if (slug === 'html-style') {\n idx.htmlStyleInBody = { ...f, title: `${f.title} in <body>` }\n return\n }\n if (IGNORED_SLUGS.has(slug)) return\n\n if (f.category === 'css') return classifyCss(f, slug, idx)\n if (f.category === 'html') return classifyHtml(f, slug, idx)\n if (f.category === 'image') {\n const ext = slug.slice('image-'.length)\n if (ext === 'base64') return\n mpush(idx.imageExt, ext, f)\n }\n // 'others' (amp, bimi) intentionally skipped\n}\n\nfunction classifyCss(f: Feature, slug: string, idx: Indexes) {\n // Specials first\n switch (slug) {\n case 'css-important': idx.cssImportant = f; return\n case 'css-variables': idx.cssVariables = f; return\n case 'css-nesting': idx.cssNesting = f; return\n case 'css-comments': idx.cssComments = f; return\n case 'css-modern-color': idx.cssModernColor = f; return\n case 'css-display-flex': mpushPropValue(idx, 'display', 'flex', f); return\n case 'css-display-grid': mpushPropValue(idx, 'display', 'grid', f); return\n case 'css-display-none': mpushPropValue(idx, 'display', 'none', f); return\n case 'css-rgb': mpush(idx.cssFunction, 'rgb', f); return\n case 'css-rgba': mpush(idx.cssFunction, 'rgba', f); return\n case 'css-linear-gradient': mpush(idx.cssFunction, 'linear-gradient', f); return\n case 'css-radial-gradient': mpush(idx.cssFunction, 'radial-gradient', f); return\n case 'css-conic-gradient': mpush(idx.cssFunction, 'conic-gradient', f); return\n }\n\n if (slug.startsWith('css-at-media-') && slug !== 'css-at-media') {\n mpush(idx.cssMediaFeature, slug.slice('css-at-media-'.length), f)\n return\n }\n if (slug.startsWith('css-at-')) {\n mpush(idx.cssAtRule, slug.slice('css-at-'.length), f)\n return\n }\n if (slug.startsWith('css-pseudo-class-')) {\n mpush(idx.cssPseudoClass, slug.slice('css-pseudo-class-'.length), f)\n return\n }\n if (slug.startsWith('css-pseudo-element-')) {\n mpush(idx.cssPseudoElement, slug.slice('css-pseudo-element-'.length), f)\n return\n }\n if (slug.startsWith('css-unit-')) {\n const u = slug.slice('css-unit-'.length)\n if (u === 'calc') { mpush(idx.cssFunction, 'calc', f); return }\n if (u === 'initial') return // keyword detection is noisy; skip\n const unit = u === 'percent' ? '%' : u\n mpush(idx.cssUnit, unit, f)\n return\n }\n if (slug.startsWith('css-function-')) {\n mpush(idx.cssFunction, slug.slice('css-function-'.length), f)\n return\n }\n // css-selector-* — skip (too broad to detect meaningfully)\n if (slug.startsWith('css-selector-')) return\n\n // Fallback: treat as property name\n mpush(idx.cssProp, slug.slice('css-'.length), f)\n}\n\nfunction mpushPropValue(idx: Indexes, prop: string, value: string, f: Feature) {\n const arr = idx.cssPropValue.get(prop)\n if (arr) arr.push({ value, feature: f })\n else idx.cssPropValue.set(prop, [{ value, feature: f }])\n}\n\nconst HTML_ATTR_SLUGS = new Set([\n 'align', 'background', 'cellpadding', 'cellspacing', 'height', 'width',\n 'valign', 'target', 'srcset', 'lang', 'dir', 'role', 'required', 'hidden',\n])\n\nfunction classifyHtml(f: Feature, slug: string, idx: Indexes) {\n // Specials\n switch (slug) {\n case 'html-doctype': idx.htmlDoctype = f; return\n case 'html-comments': idx.htmlComments = f; return\n case 'html-anchor-links': idx.htmlAnchorLinks = f; return\n case 'html-mailto-links': idx.htmlMailtoLinks = f; return\n case 'html-meta-color-scheme': idx.htmlMetaColorScheme = f; return\n case 'html-semantics': idx.htmlSemantics = f; return\n case 'html-loading-attribute': mpush(idx.htmlAttr, 'loading', f); return\n case 'html-image-maps':\n mpush(idx.htmlTag, 'map', f); mpush(idx.htmlTag, 'area', f); mpush(idx.htmlAttr, 'usemap', f); return\n case 'html-lists':\n for (const t of ['ul', 'ol', 'li', 'dl', 'dt', 'dd']) mpush(idx.htmlTag, t, f)\n return\n case 'html-h1-h6':\n for (const t of ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) mpush(idx.htmlTag, t, f)\n return\n }\n\n if (slug.startsWith('html-input-')) {\n mpush(idx.htmlInputType, slug.slice('html-input-'.length), f)\n return\n }\n if (slug.startsWith('html-button-')) {\n mpush(idx.htmlButtonType, slug.slice('html-button-'.length), f)\n return\n }\n if (slug.startsWith('html-aria-')) {\n mpush(idx.htmlAttr, slug.slice('html-'.length), f)\n return\n }\n const name = slug.slice('html-'.length)\n if (HTML_ATTR_SLUGS.has(name)) {\n mpush(idx.htmlAttr, name, f)\n return\n }\n mpush(idx.htmlTag, name, f)\n}\n\nexport async function initCompatibility(): Promise<Indexes | null> {\n if (indexes) return indexes\n if (initPromise) return initPromise\n initPromise = (async () => {\n try {\n const res = await fetch(API_URL)\n if (!res.ok) return null\n const data = await res.json()\n const idx = emptyIndexes(data.nicenames?.support ?? {}, data.nicenames?.family ?? {})\n for (const item of data.data ?? []) {\n /**\n * Record every slug's title/url so lint can look up caniemail\n * pages for issues that map to a known feature, even ignored.\n */\n if (item.slug && item.url) idx.bySlug.set(item.slug, { title: item.title, url: item.url })\n /**\n * Index the feature if any cell anywhere in the matrix is non-y.\n * Per-request aggregation (with the active client filter)\n * decides whether to actually surface the issue.\n */\n if (!hasAnyNonY(item.stats)) continue\n const f: Feature = {\n slug: item.slug,\n title: item.title,\n url: item.url,\n category: item.category,\n stats: item.stats,\n }\n classify(f, idx)\n }\n indexes = idx\n return idx\n } catch {\n return null\n }\n })()\n return initPromise\n}\n\n/**\n * Note: fetch of the caniemail dataset is lazy — it fires on the first\n * check request, not at module load, so `server.checks: false` pays\n * no network cost.\n */\n\ninterface FileStreams {\n path: string\n source: string\n template: SfcBlock | null\n styles: SfcBlock[]\n classes: Set<string>\n}\n\nfunction collectStreams(\n filePath: string,\n componentMap: Map<string, string>,\n visited: Set<string>,\n out: FileStreams[],\n) {\n if (visited.has(filePath)) return\n visited.add(filePath)\n\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch { return }\n\n const { template, styles } = parseSfcBlocks(source)\n const classes = new Set<string>()\n if (template) extractClasses(template.content, classes)\n\n out.push({ path: filePath, source, template, styles, classes })\n\n if (template) {\n for (const tag of findComponentTags(template.content)) {\n const cp = componentMap.get(tag.toLowerCase())\n if (cp) collectStreams(cp, componentMap, visited, out)\n }\n }\n}\n\nfunction extractClasses(html: string, out: Set<string>) {\n const parser = new Parser({\n onopentag(_tag, attrs) {\n const c = attrs.class\n if (!c) return\n for (const t of c.split(/\\s+/)) if (t) out.add(t)\n },\n }, { decodeEntities: true })\n parser.write(html)\n parser.end()\n}\n\nfunction parseWithIndices(html: string): ChildNode[] {\n const handler = new DomHandler(undefined, { withStartIndices: true })\n const parser = new Parser(handler)\n parser.write(html)\n parser.end()\n return handler.dom\n}\n\nfunction findStyleNodes(nodes: ChildNode[], out: Element[] = []): Element[] {\n for (const n of nodes) {\n const el = n as Element\n if (el.name === 'style') out.push(el)\n if (el.children?.length) findStyleNodes(el.children as ChildNode[], out)\n }\n return out\n}\n\n/**\n * Parse each file's template, collect every `<style>` node with its source\n * line (via htmlparser2 start indices), then pass the combined DOM through\n * the framework's real Tailwind pipeline. The pipeline resolves imports\n * (@maizzle/tailwindcss), compiles utilities from class attrs, lowers modern\n * CSS via lightningcss, and resolves static calc() — so what we walk matches\n * what ships.\n */\nasync function compileViaPipeline(\n streams: FileStreams[],\n config: MaizzleConfig,\n rootFile: string,\n): Promise<Array<{ file: string, css: string, line: number }>> {\n const all: ChildNode[] = []\n const tracked: Array<{ node: Element, file: string, line: number }> = []\n\n for (const s of streams) {\n if (!s.template) continue\n const templateStart = s.source.indexOf(s.template.content)\n const nodes = parseWithIndices(s.template.content)\n for (const styleNode of findStyleNodes(nodes)) {\n const startIdx = (styleNode as any).startIndex ?? 0\n const line = offsetToLine(s.source, templateStart + startIdx)\n tracked.push({ node: styleNode, file: s.path, line })\n }\n for (const n of nodes) all.push(n)\n }\n\n if (!tracked.length) return []\n\n try {\n await compileWithPipeline(all, config, rootFile)\n } catch { return [] }\n\n return tracked\n .map(t => {\n const txt = t.node.children?.find(c => (c as any).type === 'text') as any\n return txt?.data ? { file: t.file, css: txt.data as string, line: t.line } : null\n })\n .filter((x): x is { file: string, css: string, line: number } => x !== null)\n}\n\n/**\n * Walk CSS AST with detectors. Calls onHit per feature hit.\n * `selector` is the containing rule's selector (undefined if no rule ancestor).\n */\nfunction walkCss(\n css: string,\n idx: Indexes,\n onHit: (feature: Feature, node: { line?: number, selector?: string }) => void,\n) {\n let root: postcss.Root\n try { root = safeParser(css) } catch { return }\n\n const containingSelector = (n: postcss.Node | undefined): string | undefined => {\n let p = n?.parent\n while (p && p.type !== 'root') {\n if (p.type === 'rule') return (p as Rule).selector\n p = p.parent\n }\n return undefined\n }\n\n if (idx.cssComments) {\n root.walkComments((c) => { onHit(idx.cssComments!, { line: c.source?.start?.line, selector: containingSelector(c) }) })\n }\n\n root.walkAtRules((atRule: AtRule) => {\n const line = atRule.source?.start?.line\n let sel = containingSelector(atRule)\n if (atRule.name === 'media' && !sel) {\n const innerSelectors: string[] = []\n atRule.walkRules((r) => { innerSelectors.push(r.selector) })\n if (innerSelectors.length) sel = innerSelectors.join(', ')\n }\n\n if (atRule.name === 'media') {\n /**\n * Pick the most specific media-feature match (prefers-color-scheme,\n * hover, orientation, …). If one matches, skip the generic\n * `css-at-media` to avoid duplicate rows on the same line.\n */\n const specific: Feature[] = []\n if (idx.cssMediaFeature.size) {\n for (const [feat, fs2] of idx.cssMediaFeature) {\n if (atRule.params.includes(`(${feat}`) || atRule.params.includes(feat)) {\n specific.push(...fs2)\n }\n }\n }\n if (specific.length) {\n for (const f of specific) onHit(f, { line, selector: sel })\n } else {\n const fs = idx.cssAtRule.get('media')\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n }\n } else {\n const fs = idx.cssAtRule.get(atRule.name)\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n }\n })\n\n root.walkRules((rule: Rule) => {\n const line = rule.source?.start?.line\n const sel = rule.selector\n if (idx.cssPseudoClass.size) {\n for (const [name, fs] of idx.cssPseudoClass) {\n const re = new RegExp(`(^|[^:]):${escapeRe(name)}(\\\\b|\\\\()`)\n if (re.test(sel)) for (const f of fs) onHit(f, { line, selector: sel })\n }\n }\n if (idx.cssPseudoElement.size) {\n for (const [name, fs] of idx.cssPseudoElement) {\n const re = new RegExp(`::${escapeRe(name)}\\\\b`)\n if (re.test(sel)) for (const f of fs) onHit(f, { line, selector: sel })\n }\n }\n })\n\n root.walkDecls((decl: Declaration) => {\n const line = decl.source?.start?.line\n const sel = containingSelector(decl)\n const prop = decl.prop\n\n if (idx.cssImportant && decl.important) onHit(idx.cssImportant, { line, selector: sel })\n if (idx.cssVariables && prop.startsWith('--')) onHit(idx.cssVariables, { line, selector: sel })\n\n const fs = idx.cssProp.get(prop)\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n\n const pvs = idx.cssPropValue.get(prop)\n if (pvs) {\n const v = decl.value.trim().toLowerCase()\n for (const pv of pvs) if (v === pv.value) onHit(pv.feature, { line, selector: sel })\n }\n\n if (idx.cssFunction.size || idx.cssUnit.size || idx.cssVariables || idx.cssModernColor) {\n try {\n valueParser(decl.value).walk((n) => {\n if (n.type === 'function') {\n const fname = n.value.toLowerCase()\n const fs2 = idx.cssFunction.get(fname)\n if (fs2) for (const f of fs2) onHit(f, { line, selector: sel })\n if (idx.cssVariables && fname === 'var') onHit(idx.cssVariables, { line, selector: sel })\n if (idx.cssModernColor && MODERN_COLOR_FNS.has(fname)) onHit(idx.cssModernColor, { line, selector: sel })\n } else if (n.type === 'word') {\n const m = /^-?\\d*\\.?\\d+([a-z%]+)$/i.exec(n.value)\n if (m) {\n const unit = m[1].toLowerCase()\n const fs2 = idx.cssUnit.get(unit)\n if (fs2) for (const f of fs2) onHit(f, { line, selector: sel })\n }\n }\n })\n } catch {}\n }\n })\n}\n\nconst MODERN_COLOR_FNS = new Set(['oklch', 'oklab', 'lch', 'lab', 'color', 'color-mix', 'hwb'])\n\nfunction escapeRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction offsetToLine(source: string, offset: number): number {\n let line = 1\n for (let i = 0; i < offset && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++\n }\n return line\n}\n\nfunction walkTemplate(\n html: string,\n idx: Indexes,\n fileLineOffset: number,\n source: string,\n templateStartOffset: number,\n onHit: (feature: Feature, line: number) => void,\n) {\n const semanticTags = new Set(['article', 'aside', 'details', 'figcaption', 'figure',\n 'footer', 'header', 'main', 'mark', 'nav', 'section', 'time', 'summary'])\n\n /**\n * Stack of tags that opened a body-scope: a literal <body> or a\n * <Teleport to=\"body...\"> whose rendered contents land inside body.\n */\n const bodyScopeStack: string[] = []\n const parser = new Parser({\n onopentag(tag, attrs) {\n const startIdx = (parser as any).startIndex as number\n const line = offsetToLine(source, templateStartOffset + startIdx)\n\n const tagFs = idx.htmlTag.get(tag)\n if (tagFs) for (const f of tagFs) onHit(f, line)\n\n if (idx.htmlSemantics && semanticTags.has(tag)) onHit(idx.htmlSemantics, line)\n\n if (tag === 'style' && bodyScopeStack.length > 0 && idx.htmlStyleInBody) {\n onHit(idx.htmlStyleInBody, line)\n }\n if (tag === 'body') bodyScopeStack.push(tag)\n else if (tag === 'teleport' && /body/i.test(attrs.to ?? '')) bodyScopeStack.push(tag)\n\n for (const attr in attrs) {\n const attrFs = idx.htmlAttr.get(attr)\n if (attrFs) for (const f of attrFs) onHit(f, line)\n }\n\n if (tag === 'input' && attrs.type) {\n const fs = idx.htmlInputType.get(attrs.type.toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n if (tag === 'button' && attrs.type) {\n const fs = idx.htmlButtonType.get(attrs.type.toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n if (tag === 'a' && attrs.href) {\n const h = attrs.href.trim()\n if (idx.htmlMailtoLinks && /^mailto:/i.test(h)) onHit(idx.htmlMailtoLinks, line)\n else if (idx.htmlAnchorLinks && h.startsWith('#')) onHit(idx.htmlAnchorLinks, line)\n }\n if (tag === 'meta' && idx.htmlMetaColorScheme\n && attrs.name?.toLowerCase() === 'color-scheme') onHit(idx.htmlMetaColorScheme, line)\n\n // Image formats via src / srcset\n if (idx.imageExt.size && (attrs.src || attrs.srcset)) {\n const urls: string[] = []\n if (attrs.src) urls.push(attrs.src)\n if (attrs.srcset) for (const part of attrs.srcset.split(',')) urls.push(part.trim().split(/\\s+/)[0])\n for (const url of urls) {\n const m = /\\.([a-z0-9]+)(?:\\?|#|$)/i.exec(url)\n if (!m) continue\n const fs = idx.imageExt.get(m[1].toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n }\n\n // inline style attribute → scan as CSS decl list\n if (attrs.style) scanInlineStyle(attrs.style, idx, line, onHit)\n },\n onclosetag(tag) {\n const top = bodyScopeStack[bodyScopeStack.length - 1]\n if (top === tag) bodyScopeStack.pop()\n },\n onprocessinginstruction(name) {\n if (idx.htmlDoctype && name.toLowerCase() === '!doctype') {\n const startIdx = (parser as any).startIndex as number\n onHit(idx.htmlDoctype, offsetToLine(source, templateStartOffset + startIdx))\n }\n },\n oncomment() {\n if (idx.htmlComments) {\n const startIdx = (parser as any).startIndex as number\n onHit(idx.htmlComments, offsetToLine(source, templateStartOffset + startIdx))\n }\n },\n }, { decodeEntities: false, lowerCaseTags: true, lowerCaseAttributeNames: true })\n\n parser.write(html)\n parser.end()\n}\n\nfunction scanInlineStyle(\n style: string,\n idx: Indexes,\n line: number,\n onHit: (feature: Feature, line: number) => void,\n) {\n // Wrap in a rule so safeParser produces a Root with declarations\n const wrapped = `*{${style}}`\n try {\n const root = safeParser(wrapped)\n root.walkDecls((decl) => {\n if (idx.cssImportant && decl.important) onHit(idx.cssImportant, line)\n const fs = idx.cssProp.get(decl.prop)\n if (fs) for (const f of fs) onHit(f, line)\n if (idx.cssVariables && decl.prop.startsWith('--')) onHit(idx.cssVariables, line)\n const pvs = idx.cssPropValue.get(decl.prop)\n if (pvs) {\n const v = decl.value.trim().toLowerCase()\n for (const pv of pvs) if (v === pv.value) onHit(pv.feature, line)\n }\n if (idx.cssFunction.size || idx.cssUnit.size || idx.cssVariables || idx.cssModernColor) {\n try {\n valueParser(decl.value).walk((n) => {\n if (n.type === 'function') {\n const fname = n.value.toLowerCase()\n const fs2 = idx.cssFunction.get(fname)\n if (fs2) for (const f of fs2) onHit(f, line)\n if (idx.cssVariables && fname === 'var') onHit(idx.cssVariables, line)\n if (idx.cssModernColor && MODERN_COLOR_FNS.has(fname)) onHit(idx.cssModernColor, line)\n } else if (n.type === 'word') {\n const m = /^-?\\d*\\.?\\d+([a-z%]+)$/i.exec(n.value)\n if (m) {\n const fs2 = idx.cssUnit.get(m[1].toLowerCase())\n if (fs2) for (const f of fs2) onHit(f, line)\n }\n }\n })\n } catch {}\n }\n })\n } catch {}\n}\n\nfunction labelFor(idx: Indexes, level: SupportLevel): string {\n const n = idx.nicenames\n if (level === 'unsupported') return n.unsupported ?? 'Not supported'\n if (level === 'mitigated') return n.mitigated ?? 'Partially supported'\n return n.unknown ?? 'Support unknown'\n}\n\nasync function scan(\n rootFile: string,\n config: MaizzleConfig,\n componentDirs: NormalizedComponentSource[],\n allowedClients: Set<string> | 'all',\n): Promise<Issue[]> {\n const idx = await initCompatibility()\n if (!idx) return []\n\n const root = config.root ?? process.cwd()\n const componentMap = await buildComponentMap(root, componentDirs)\n const streams: FileStreams[] = []\n collectStreams(rootFile, componentMap, new Set(), streams)\n\n const issues: Issue[] = []\n const seen = new Set<string>()\n const resolvedCache = new Map<string, { level: SupportLevel, affected: string[] } | null>()\n const resolveSupport = (f: Feature) => {\n let cached = resolvedCache.get(f.slug)\n if (cached === undefined) {\n cached = computeSupport(f.stats, idx.familyNicenames, allowedClients)\n resolvedCache.set(f.slug, cached)\n }\n return cached\n }\n const add = (f: Feature, file: string, line?: number) => {\n const key = `${f.slug}|${file}|${line ?? 0}`\n if (seen.has(key)) return\n const support = resolveSupport(f)\n if (!support) return\n seen.add(key)\n issues.push({\n kind: 'compat',\n slug: f.slug, title: f.title, url: f.url, category: f.category,\n supportLevel: support.level, supportLabel: labelFor(idx, support.level),\n affectedClients: support.affected,\n line, file,\n })\n }\n\n /**\n * Stream A: compiled CSS from real pipeline — reflects shipped output\n * (Tailwind utilities resolved, @maizzle/tailwindcss imported, calc\n * resolved, modern CSS lowered). Filter hits whose containing\n * selector doesn't reference a user class — drops Tailwind\n * preflight noise. For hits without a class selector\n * (e.g. @media, user-written rules), attribute to the\n * file that owned the style block.\n */\n const compiledBlocks = await compileViaPipeline(streams, config, rootFile)\n for (const block of compiledBlocks) {\n walkCss(block.css, idx, (feature, node) => {\n const locations = classLocations(node.selector, streams)\n if (!locations.length) {\n add(feature, block.file, block.line)\n return\n }\n /**\n * @media features collapse to a single source line: the first use\n * of whatever class/variant triggered the wrapper. Other\n * features show up for every occurrence.\n */\n if (feature.slug.startsWith('css-at-media')) {\n add(feature, locations[0].file, locations[0].line)\n } else {\n for (const { file, line } of locations) add(feature, file, line)\n }\n })\n }\n\n // Stream C: source template per file\n for (const s of streams) {\n if (!s.template) continue\n walkTemplate(s.template.content, idx, s.template.offset, s.source,\n s.source.indexOf(s.template.content),\n (feature, line) => add(feature, s.path, line))\n }\n\n return issues\n}\n\n/**\n * Return every (file, line) where any class from the selector appears in a\n * template. Scans every stream so a shared utility class used in multiple\n * components surfaces once per occurrence.\n */\nfunction classLocations(\n selector: string | undefined,\n streams: FileStreams[],\n): Array<{ file: string, line: number }> {\n if (!selector) return []\n const classNames = extractSelectorClasses(selector)\n if (!classNames.length) return []\n const out: Array<{ file: string, line: number }> = []\n const seen = new Set<string>()\n for (const cn of classNames) {\n for (const s of streams) {\n if (!s.classes.has(cn) || !s.template) continue\n const tpl = s.template.content\n const tplStart = s.source.indexOf(tpl)\n let pos = 0\n while (true) {\n const i = tpl.indexOf(cn, pos)\n if (i < 0) break\n pos = i + cn.length\n // Whole-word boundary: adjacent char must be whitespace or quote\n const before = i > 0 ? tpl[i - 1] : ' '\n const after = i + cn.length < tpl.length ? tpl[i + cn.length] : ' '\n if (!isClassBoundary(before) || !isClassBoundary(after)) continue\n const line = offsetToLine(s.source, tplStart + i)\n const key = `${s.path}|${line}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ file: s.path, line })\n }\n }\n }\n return out\n}\n\nfunction isClassBoundary(c: string): boolean {\n return c === ' ' || c === '\\t' || c === '\\n' || c === '\\r' || c === '\"' || c === \"'\"\n}\n\nfunction extractSelectorClasses(selector: string): string[] {\n const out: string[] = []\n const re = /\\.((?:\\\\.|[\\w-])+)/g\n let m\n while ((m = re.exec(selector)) !== null) {\n out.push(m[1].replace(/\\\\(.)/g, '$1'))\n }\n return out\n}\n\nconst CATEGORY_ORDER = ['css', 'html', 'image', 'others']\nconst LEVEL_ORDER: Record<string, number> = { error: 0, unsupported: 1, warning: 2, mitigated: 3, unknown: 4 }\n\nfunction orderKey(i: Issue): number {\n if (i.kind === 'lint') return LEVEL_ORDER[i.severity!] ?? 99\n return LEVEL_ORDER[i.supportLevel!] ?? 99\n}\n\nfunction resolveChecksConfig(config: MaizzleConfig) {\n const raw = (config as any).server?.checks\n if (raw === false) return null\n const clients: Set<string> | 'all' = raw?.clients === 'all'\n ? 'all'\n : Array.isArray(raw?.clients) && raw.clients.length\n ? new Set(raw.clients as string[])\n : DEFAULT_CLIENTS\n const level: 'error' | 'warning' | 'lint' | null = raw?.level ?? null\n return { clients, level }\n}\n\nfunction passesLevelFilter(issue: Issue, level: 'error' | 'warning' | 'lint' | null): boolean {\n if (!level) return true\n if (level === 'lint') return issue.kind === 'lint'\n if (issue.kind === 'lint') {\n return level === 'error' ? issue.severity === 'error' : issue.severity === 'warning'\n }\n // compat\n return level === 'error'\n ? issue.supportLevel === 'unsupported'\n : issue.supportLevel === 'mitigated' || issue.supportLevel === 'unknown'\n}\n\nexport async function serveCompatibility(\n url: string,\n res: any,\n config: MaizzleConfig,\n componentDirs: NormalizedComponentSource[],\n) {\n const filePath = url.replace('/__maizzle/compatibility/', '').replace(/\\?.*$/, '')\n const checksCfg = resolveChecksConfig(config)\n try {\n res.setHeader('Content-Type', 'application/json')\n if (!checksCfg) {\n /**\n * Defensive: UI hides the tab using window.__MAIZZLE_CONFIG__ so\n * it shouldn't reach this endpoint when disabled, but if\n * something else does, return an empty list.\n */\n res.end(JSON.stringify([]))\n return\n }\n const absolutePath = resolve(filePath)\n const [compatIssues, lintIssues] = await Promise.all([\n scan(absolutePath, config, componentDirs, checksCfg.clients),\n scanLint(absolutePath, config, componentDirs),\n ])\n\n const idx = await initCompatibility()\n const lintAsIssues: Issue[] = lintIssues.map((li) => {\n const info = li.slug ? idx?.bySlug.get(li.slug) : undefined\n return {\n kind: 'lint',\n slug: li.slug,\n title: li.title,\n url: info?.url,\n category: li.category,\n severity: li.type,\n message: li.message,\n line: li.line,\n file: li.file,\n }\n })\n\n let issues: Issue[] = [...compatIssues, ...lintAsIssues]\n issues = issues.filter((i) => !isFrameworkComponent(i.file))\n if (checksCfg.level) issues = issues.filter((i) => passesLevelFilter(i, checksCfg.level))\n issues.sort((a, b) => {\n const c = CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category)\n if (c) return c\n const l = orderKey(a) - orderKey(b)\n if (l) return l\n return (a.slug ?? a.title).localeCompare(b.slug ?? b.title)\n })\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compatibility.js","names":["compileWithPipeline"],"sources":["../../src/server/compatibility.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport postcss, { type Declaration, type AtRule, type Rule } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport valueParser from 'postcss-value-parser'\nimport { Parser } from 'htmlparser2'\nimport { DomHandler, type ChildNode, type Element } from 'domhandler'\nimport { parseSfcBlocks, findComponentTags, buildComponentMap, isFrameworkComponent, type SfcBlock } from './sfc-utils.ts'\nimport { scanLint } from './linter.ts'\nimport { tailwindcss as compileWithPipeline } from '../transformers/tailwindcss.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport type { NormalizedComponentSource } from '../utils/componentSources.ts'\n\nconst API_URL = 'https://www.caniemail.com/api/data.json'\nconst DEFAULT_CLIENTS = new Set(['gmail', 'apple-mail', 'outlook', 'yahoo'])\n\ntype SupportLevel = 'unsupported' | 'mitigated' | 'unknown'\n\ninterface Feature {\n slug: string\n title: string\n url: string\n category: string\n stats: any\n}\n\ninterface Issue {\n kind: 'compat' | 'lint'\n slug?: string\n title: string\n url?: string\n category: string\n line?: number\n file: string\n // compat-only\n supportLevel?: SupportLevel\n supportLabel?: string\n affectedClients?: string[]\n // lint-only\n severity?: 'error' | 'warning'\n message?: string\n}\n\ninterface Indexes {\n nicenames: { supported: string, mitigated: string, unsupported: string, unknown: string, mixed: string }\n familyNicenames: Record<string, string>\n cssProp: Map<string, Feature[]>\n cssPropValue: Map<string, Array<{ value: string, feature: Feature }>>\n cssAtRule: Map<string, Feature[]>\n cssMediaFeature: Map<string, Feature[]>\n cssPseudoClass: Map<string, Feature[]>\n cssPseudoElement: Map<string, Feature[]>\n cssFunction: Map<string, Feature[]>\n cssUnit: Map<string, Feature[]>\n cssImportant?: Feature\n cssVariables?: Feature\n cssNesting?: Feature\n cssComments?: Feature\n cssModernColor?: Feature\n htmlTag: Map<string, Feature[]>\n htmlAttr: Map<string, Feature[]>\n htmlInputType: Map<string, Feature[]>\n htmlButtonType: Map<string, Feature[]>\n htmlDoctype?: Feature\n htmlComments?: Feature\n htmlAnchorLinks?: Feature\n htmlMailtoLinks?: Feature\n htmlMetaColorScheme?: Feature\n htmlSemantics?: Feature\n htmlStyleInBody?: Feature\n imageExt: Map<string, Feature[]>\n /** All features by slug — unfiltered, used for URL lookups (e.g. by lint). */\n bySlug: Map<string, { title: string, url: string }>\n}\n\nlet indexes: Indexes | null = null\nlet initPromise: Promise<Indexes | null> | null = null\n\nfunction mpush<K, V>(m: Map<K, V[]>, k: K, v: V) {\n const arr = m.get(k)\n if (arr) arr.push(v)\n else m.set(k, [v])\n}\n\nfunction emptyIndexes(nicenames: any, familyNicenames: Record<string, string>): Indexes {\n return {\n nicenames,\n familyNicenames,\n cssProp: new Map(), cssPropValue: new Map(), cssAtRule: new Map(),\n cssMediaFeature: new Map(), cssPseudoClass: new Map(), cssPseudoElement: new Map(),\n cssFunction: new Map(), cssUnit: new Map(),\n htmlTag: new Map(), htmlAttr: new Map(), htmlInputType: new Map(), htmlButtonType: new Map(),\n imageExt: new Map(),\n bySlug: new Map(),\n }\n}\n\nfunction hasAnyNonY(stats: any): boolean {\n if (!stats) return false\n for (const family in stats) {\n for (const plat in stats[family]) {\n for (const ver in stats[family][plat]) {\n const v = stripNotes(String(stats[family][plat][ver]).trim())\n if (v && v !== 'y') return true\n }\n }\n }\n return false\n}\n\n/** Strip `#N` note markers — `\"y #1\"` → `\"y\"`. Notes document edge cases but\n * don't change support semantics, so treat `y #1` as fully supported. */\nfunction stripNotes(v: string): string {\n return v.split(/\\s+/).filter(t => t && !t.startsWith('#')).join(' ')\n}\n\nfunction computeSupport(stats: any, familyNicenames: Record<string, string>, allowedClients: Set<string> | 'all'): { level: SupportLevel, affected: string[] } | null {\n let nY = 0, nN = 0, nU = 0, nPartial = 0, total = 0\n const affectedFamilies = new Set<string>()\n for (const family in stats) {\n if (allowedClients !== 'all' && !allowedClients.has(family)) continue\n let familyHasNonY = false\n for (const plat in stats[family]) {\n /**\n * Only score the latest version per (family, platform) — legacy\n * versions (Outlook 2007, etc.) otherwise flag modern-widely\n * supported features as partial forever.\n */\n const versions = Object.keys(stats[family][plat]).sort()\n const latest = versions[versions.length - 1]\n if (!latest) continue\n total++\n const v = stripNotes(String(stats[family][plat][latest]).trim())\n if (v === 'y') nY++\n else if (v === 'n') { nN++; familyHasNonY = true }\n else if (v === 'u') { nU++; familyHasNonY = true }\n else { nPartial++; familyHasNonY = true }\n }\n if (familyHasNonY) affectedFamilies.add(family)\n }\n if (!total) return null\n if (nY === total) return null\n const affected = [...affectedFamilies].map(f => familyNicenames[f] ?? f).sort()\n if (nN === total) return { level: 'unsupported', affected }\n if (nU === total) return { level: 'unknown', affected }\n return { level: 'mitigated', affected }\n}\n\n/**\n * Slugs we never report. Fundamental HTML (every email uses these) plus\n * CSS noise that's not actionable (comments, !important usage).\n */\nconst IGNORED_SLUGS = new Set([\n // Required/unavoidable tags\n 'html-doctype', 'html-comments',\n 'html-html', 'html-head', 'html-body', 'html-title',\n 'html-meta', 'html-meta-color-scheme',\n 'html-style', 'html-link',\n 'html-div', 'html-span', 'html-br', 'html-p', 'html-a', 'html-img',\n 'html-table', 'html-tr', 'html-td', 'html-th',\n 'html-thead', 'html-tbody', 'html-tfoot',\n 'html-h1-h6', 'html-lists',\n 'html-strong', 'html-em', 'html-b', 'html-i', 'html-u',\n 'html-semantics',\n // Ubiquitous attributes — always present, caveats aren't actionable.\n 'html-role', 'html-hidden', 'html-width', 'html-height',\n // CSS noise\n 'css-comments', 'css-important',\n /**\n * CSS fundamentals — universally used with known minor caveats; flagging\n * them as \"partial\" is noise rather than signal.\n */\n 'css-margin', 'css-padding', 'css-border',\n 'css-font-size', 'css-font-weight', 'css-font', 'css-font-family',\n 'css-line-height', 'css-letter-spacing', 'css-text-align',\n 'css-text-decoration', 'css-text-transform', 'css-color',\n 'css-background', 'css-background-color',\n 'css-width', 'css-height',\n 'css-display',\n])\n\nfunction classify(f: Feature, idx: Indexes) {\n const slug = f.slug\n /**\n * Retain html-style feature for the body-only detector even though it's\n * blacklisted from the normal html-tag detection path. Title is\n * suffixed so the flag reads as a body-placement warning, not a\n * blanket `<style>`.\n */\n if (slug === 'html-style') {\n idx.htmlStyleInBody = { ...f, title: `${f.title} in <body>` }\n return\n }\n if (IGNORED_SLUGS.has(slug)) return\n\n if (f.category === 'css') return classifyCss(f, slug, idx)\n if (f.category === 'html') return classifyHtml(f, slug, idx)\n if (f.category === 'image') {\n const ext = slug.slice('image-'.length)\n if (ext === 'base64') return\n mpush(idx.imageExt, ext, f)\n }\n // 'others' (amp, bimi) intentionally skipped\n}\n\nfunction classifyCss(f: Feature, slug: string, idx: Indexes) {\n // Specials first\n switch (slug) {\n case 'css-important': idx.cssImportant = f; return\n case 'css-variables': idx.cssVariables = f; return\n case 'css-nesting': idx.cssNesting = f; return\n case 'css-comments': idx.cssComments = f; return\n case 'css-modern-color': idx.cssModernColor = f; return\n case 'css-display-flex': mpushPropValue(idx, 'display', 'flex', f); return\n case 'css-display-grid': mpushPropValue(idx, 'display', 'grid', f); return\n case 'css-display-none': mpushPropValue(idx, 'display', 'none', f); return\n case 'css-rgb': mpush(idx.cssFunction, 'rgb', f); return\n case 'css-rgba': mpush(idx.cssFunction, 'rgba', f); return\n case 'css-linear-gradient': mpush(idx.cssFunction, 'linear-gradient', f); return\n case 'css-radial-gradient': mpush(idx.cssFunction, 'radial-gradient', f); return\n case 'css-conic-gradient': mpush(idx.cssFunction, 'conic-gradient', f); return\n }\n\n if (slug.startsWith('css-at-media-') && slug !== 'css-at-media') {\n mpush(idx.cssMediaFeature, slug.slice('css-at-media-'.length), f)\n return\n }\n if (slug.startsWith('css-at-')) {\n mpush(idx.cssAtRule, slug.slice('css-at-'.length), f)\n return\n }\n if (slug.startsWith('css-pseudo-class-')) {\n mpush(idx.cssPseudoClass, slug.slice('css-pseudo-class-'.length), f)\n return\n }\n if (slug.startsWith('css-pseudo-element-')) {\n mpush(idx.cssPseudoElement, slug.slice('css-pseudo-element-'.length), f)\n return\n }\n if (slug.startsWith('css-unit-')) {\n const u = slug.slice('css-unit-'.length)\n if (u === 'calc') { mpush(idx.cssFunction, 'calc', f); return }\n if (u === 'initial') return // keyword detection is noisy; skip\n const unit = u === 'percent' ? '%' : u\n mpush(idx.cssUnit, unit, f)\n return\n }\n if (slug.startsWith('css-function-')) {\n mpush(idx.cssFunction, slug.slice('css-function-'.length), f)\n return\n }\n // css-selector-* — skip (too broad to detect meaningfully)\n if (slug.startsWith('css-selector-')) return\n\n // Fallback: treat as property name\n mpush(idx.cssProp, slug.slice('css-'.length), f)\n}\n\nfunction mpushPropValue(idx: Indexes, prop: string, value: string, f: Feature) {\n const arr = idx.cssPropValue.get(prop)\n if (arr) arr.push({ value, feature: f })\n else idx.cssPropValue.set(prop, [{ value, feature: f }])\n}\n\nconst HTML_ATTR_SLUGS = new Set([\n 'align', 'background', 'cellpadding', 'cellspacing', 'height', 'width',\n 'valign', 'target', 'srcset', 'lang', 'dir', 'role', 'required', 'hidden',\n])\n\nfunction classifyHtml(f: Feature, slug: string, idx: Indexes) {\n // Specials\n switch (slug) {\n case 'html-doctype': idx.htmlDoctype = f; return\n case 'html-comments': idx.htmlComments = f; return\n case 'html-anchor-links': idx.htmlAnchorLinks = f; return\n case 'html-mailto-links': idx.htmlMailtoLinks = f; return\n case 'html-meta-color-scheme': idx.htmlMetaColorScheme = f; return\n case 'html-semantics': idx.htmlSemantics = f; return\n case 'html-loading-attribute': mpush(idx.htmlAttr, 'loading', f); return\n case 'html-image-maps':\n mpush(idx.htmlTag, 'map', f); mpush(idx.htmlTag, 'area', f); mpush(idx.htmlAttr, 'usemap', f); return\n case 'html-lists':\n for (const t of ['ul', 'ol', 'li', 'dl', 'dt', 'dd']) mpush(idx.htmlTag, t, f)\n return\n case 'html-h1-h6':\n for (const t of ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) mpush(idx.htmlTag, t, f)\n return\n }\n\n if (slug.startsWith('html-input-')) {\n mpush(idx.htmlInputType, slug.slice('html-input-'.length), f)\n return\n }\n if (slug.startsWith('html-button-')) {\n mpush(idx.htmlButtonType, slug.slice('html-button-'.length), f)\n return\n }\n if (slug.startsWith('html-aria-')) {\n mpush(idx.htmlAttr, slug.slice('html-'.length), f)\n return\n }\n const name = slug.slice('html-'.length)\n if (HTML_ATTR_SLUGS.has(name)) {\n mpush(idx.htmlAttr, name, f)\n return\n }\n mpush(idx.htmlTag, name, f)\n}\n\nexport async function initCompatibility(): Promise<Indexes | null> {\n if (indexes) return indexes\n if (initPromise) return initPromise\n initPromise = (async () => {\n try {\n const res = await fetch(API_URL)\n if (!res.ok) return null\n const data = await res.json()\n const idx = emptyIndexes(data.nicenames?.support ?? {}, data.nicenames?.family ?? {})\n for (const item of data.data ?? []) {\n /**\n * Record every slug's title/url so lint can look up caniemail\n * pages for issues that map to a known feature, even ignored.\n */\n if (item.slug && item.url) idx.bySlug.set(item.slug, { title: item.title, url: item.url })\n /**\n * Index the feature if any cell anywhere in the matrix is non-y.\n * Per-request aggregation (with the active client filter)\n * decides whether to actually surface the issue.\n */\n if (!hasAnyNonY(item.stats)) continue\n const f: Feature = {\n slug: item.slug,\n title: item.title,\n url: item.url,\n category: item.category,\n stats: item.stats,\n }\n classify(f, idx)\n }\n indexes = idx\n return idx\n } catch {\n return null\n }\n })()\n return initPromise\n}\n\n/**\n * Note: fetch of the caniemail dataset is lazy — it fires on the first\n * check request, not at module load, so `server.checks: false` pays\n * no network cost.\n */\n\ninterface FileStreams {\n path: string\n source: string\n template: SfcBlock | null\n styles: SfcBlock[]\n classes: Set<string>\n}\n\nfunction collectStreams(\n filePath: string,\n componentMap: Map<string, string>,\n visited: Set<string>,\n out: FileStreams[],\n) {\n if (visited.has(filePath)) return\n visited.add(filePath)\n\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch { return }\n\n const { template, styles } = parseSfcBlocks(source)\n const classes = new Set<string>()\n if (template) extractClasses(template.content, classes)\n\n out.push({ path: filePath, source, template, styles, classes })\n\n if (template) {\n for (const tag of findComponentTags(template.content)) {\n const cp = componentMap.get(tag.toLowerCase())\n if (cp) collectStreams(cp, componentMap, visited, out)\n }\n }\n}\n\nfunction extractClasses(html: string, out: Set<string>) {\n const parser = new Parser({\n onopentag(_tag, attrs) {\n const c = attrs.class\n if (!c) return\n for (const t of c.split(/\\s+/)) if (t) out.add(t)\n },\n }, { decodeEntities: true })\n parser.write(html)\n parser.end()\n}\n\nfunction parseWithIndices(html: string): ChildNode[] {\n const handler = new DomHandler(undefined, { withStartIndices: true })\n const parser = new Parser(handler)\n parser.write(html)\n parser.end()\n return handler.dom\n}\n\nfunction findStyleNodes(nodes: ChildNode[], out: Element[] = []): Element[] {\n for (const n of nodes) {\n const el = n as Element\n if (el.name === 'style') out.push(el)\n if (el.children?.length) findStyleNodes(el.children as ChildNode[], out)\n }\n return out\n}\n\n/**\n * Parse each file's template, collect every `<style>` node with its source\n * line (via htmlparser2 start indices), then pass the combined DOM through\n * the framework's real Tailwind pipeline. The pipeline resolves imports\n * (@maizzle/tailwindcss), compiles utilities from class attrs, lowers modern\n * CSS via lightningcss, and resolves static calc() — so what we walk matches\n * what ships.\n */\nasync function compileViaPipeline(\n streams: FileStreams[],\n config: MaizzleConfig,\n rootFile: string,\n): Promise<Array<{ file: string, css: string, line: number }>> {\n const all: ChildNode[] = []\n const tracked: Array<{ node: Element, file: string, line: number }> = []\n\n for (const s of streams) {\n if (!s.template) continue\n const templateStart = s.source.indexOf(s.template.content)\n const nodes = parseWithIndices(s.template.content)\n for (const styleNode of findStyleNodes(nodes)) {\n const startIdx = (styleNode as any).startIndex ?? 0\n const line = offsetToLine(s.source, templateStart + startIdx)\n tracked.push({ node: styleNode, file: s.path, line })\n }\n for (const n of nodes) all.push(n)\n }\n\n if (!tracked.length) return []\n\n try {\n await compileWithPipeline(all, config, rootFile)\n } catch { return [] }\n\n return tracked\n .map(t => {\n const txt = t.node.children?.find(c => (c as any).type === 'text') as any\n return txt?.data ? { file: t.file, css: txt.data as string, line: t.line } : null\n })\n .filter((x): x is { file: string, css: string, line: number } => x !== null)\n}\n\n/**\n * Walk CSS AST with detectors. Calls onHit per feature hit.\n * `selector` is the containing rule's selector (undefined if no rule ancestor).\n */\nfunction walkCss(\n css: string,\n idx: Indexes,\n onHit: (feature: Feature, node: { line?: number, selector?: string }) => void,\n) {\n let root: postcss.Root\n try { root = safeParser(css) } catch { return }\n\n const containingSelector = (n: postcss.Node | undefined): string | undefined => {\n let p = n?.parent\n while (p && p.type !== 'root') {\n if (p.type === 'rule') return (p as Rule).selector\n p = p.parent\n }\n return undefined\n }\n\n if (idx.cssComments) {\n root.walkComments((c) => { onHit(idx.cssComments!, { line: c.source?.start?.line, selector: containingSelector(c) }) })\n }\n\n root.walkAtRules((atRule: AtRule) => {\n const line = atRule.source?.start?.line\n let sel = containingSelector(atRule)\n if (atRule.name === 'media' && !sel) {\n const innerSelectors: string[] = []\n atRule.walkRules((r) => { innerSelectors.push(r.selector) })\n if (innerSelectors.length) sel = innerSelectors.join(', ')\n }\n\n if (atRule.name === 'media') {\n /**\n * Pick the most specific media-feature match (prefers-color-scheme,\n * hover, orientation, …). If one matches, skip the generic\n * `css-at-media` to avoid duplicate rows on the same line.\n */\n const specific: Feature[] = []\n if (idx.cssMediaFeature.size) {\n for (const [feat, fs2] of idx.cssMediaFeature) {\n if (atRule.params.includes(`(${feat}`) || atRule.params.includes(feat)) {\n specific.push(...fs2)\n }\n }\n }\n if (specific.length) {\n for (const f of specific) onHit(f, { line, selector: sel })\n } else {\n const fs = idx.cssAtRule.get('media')\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n }\n } else {\n const fs = idx.cssAtRule.get(atRule.name)\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n }\n })\n\n root.walkRules((rule: Rule) => {\n const line = rule.source?.start?.line\n const sel = rule.selector\n if (idx.cssPseudoClass.size) {\n for (const [name, fs] of idx.cssPseudoClass) {\n const re = new RegExp(`(^|[^:]):${escapeRe(name)}(\\\\b|\\\\()`)\n if (re.test(sel)) for (const f of fs) onHit(f, { line, selector: sel })\n }\n }\n if (idx.cssPseudoElement.size) {\n for (const [name, fs] of idx.cssPseudoElement) {\n const re = new RegExp(`::${escapeRe(name)}\\\\b`)\n if (re.test(sel)) for (const f of fs) onHit(f, { line, selector: sel })\n }\n }\n })\n\n root.walkDecls((decl: Declaration) => {\n const line = decl.source?.start?.line\n const sel = containingSelector(decl)\n const prop = decl.prop\n\n if (idx.cssImportant && decl.important) onHit(idx.cssImportant, { line, selector: sel })\n if (idx.cssVariables && prop.startsWith('--')) onHit(idx.cssVariables, { line, selector: sel })\n\n const fs = idx.cssProp.get(prop)\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n\n const pvs = idx.cssPropValue.get(prop)\n if (pvs) {\n const v = decl.value.trim().toLowerCase()\n for (const pv of pvs) if (v === pv.value) onHit(pv.feature, { line, selector: sel })\n }\n\n if (idx.cssFunction.size || idx.cssUnit.size || idx.cssVariables || idx.cssModernColor) {\n try {\n valueParser(decl.value).walk((n) => {\n if (n.type === 'function') {\n const fname = n.value.toLowerCase()\n const fs2 = idx.cssFunction.get(fname)\n if (fs2) for (const f of fs2) onHit(f, { line, selector: sel })\n if (idx.cssVariables && fname === 'var') onHit(idx.cssVariables, { line, selector: sel })\n if (idx.cssModernColor && MODERN_COLOR_FNS.has(fname)) onHit(idx.cssModernColor, { line, selector: sel })\n } else if (n.type === 'word') {\n const m = /^-?\\d*\\.?\\d+([a-z%]+)$/i.exec(n.value)\n if (m) {\n const unit = m[1].toLowerCase()\n const fs2 = idx.cssUnit.get(unit)\n if (fs2) for (const f of fs2) onHit(f, { line, selector: sel })\n }\n }\n })\n } catch {}\n }\n })\n}\n\nconst MODERN_COLOR_FNS = new Set(['oklch', 'oklab', 'lch', 'lab', 'color', 'color-mix', 'hwb'])\n\nfunction escapeRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction offsetToLine(source: string, offset: number): number {\n let line = 1\n for (let i = 0; i < offset && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++\n }\n return line\n}\n\nfunction walkTemplate(\n html: string,\n idx: Indexes,\n fileLineOffset: number,\n source: string,\n templateStartOffset: number,\n onHit: (feature: Feature, line: number) => void,\n) {\n const semanticTags = new Set(['article', 'aside', 'details', 'figcaption', 'figure',\n 'footer', 'header', 'main', 'mark', 'nav', 'section', 'time', 'summary'])\n\n /**\n * Stack of tags that opened a body-scope: a literal <body> or a\n * <Teleport to=\"body...\"> whose rendered contents land inside body.\n */\n const bodyScopeStack: string[] = []\n const parser = new Parser({\n onopentag(tag, attrs) {\n const startIdx = (parser as any).startIndex as number\n const line = offsetToLine(source, templateStartOffset + startIdx)\n\n const tagFs = idx.htmlTag.get(tag)\n if (tagFs) for (const f of tagFs) onHit(f, line)\n\n if (idx.htmlSemantics && semanticTags.has(tag)) onHit(idx.htmlSemantics, line)\n\n if (tag === 'style' && bodyScopeStack.length > 0 && idx.htmlStyleInBody) {\n onHit(idx.htmlStyleInBody, line)\n }\n if (tag === 'body') bodyScopeStack.push(tag)\n else if (tag === 'teleport' && /body/i.test(attrs.to ?? '')) bodyScopeStack.push(tag)\n\n for (const attr in attrs) {\n const attrFs = idx.htmlAttr.get(attr)\n if (attrFs) for (const f of attrFs) onHit(f, line)\n }\n\n if (tag === 'input' && attrs.type) {\n const fs = idx.htmlInputType.get(attrs.type.toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n if (tag === 'button' && attrs.type) {\n const fs = idx.htmlButtonType.get(attrs.type.toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n if (tag === 'a' && attrs.href) {\n const h = attrs.href.trim()\n if (idx.htmlMailtoLinks && /^mailto:/i.test(h)) onHit(idx.htmlMailtoLinks, line)\n else if (idx.htmlAnchorLinks && h.startsWith('#')) onHit(idx.htmlAnchorLinks, line)\n }\n if (tag === 'meta' && idx.htmlMetaColorScheme\n && attrs.name?.toLowerCase() === 'color-scheme') onHit(idx.htmlMetaColorScheme, line)\n\n // Image formats via src / srcset\n if (idx.imageExt.size && (attrs.src || attrs.srcset)) {\n const urls: string[] = []\n if (attrs.src) urls.push(attrs.src)\n if (attrs.srcset) for (const part of attrs.srcset.split(',')) urls.push(part.trim().split(/\\s+/)[0])\n for (const url of urls) {\n const m = /\\.([a-z0-9]+)(?:\\?|#|$)/i.exec(url)\n if (!m) continue\n const fs = idx.imageExt.get(m[1].toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n }\n\n // inline style attribute → scan as CSS decl list\n if (attrs.style) scanInlineStyle(attrs.style, idx, line, onHit)\n },\n onclosetag(tag) {\n const top = bodyScopeStack[bodyScopeStack.length - 1]\n if (top === tag) bodyScopeStack.pop()\n },\n onprocessinginstruction(name) {\n if (idx.htmlDoctype && name.toLowerCase() === '!doctype') {\n const startIdx = (parser as any).startIndex as number\n onHit(idx.htmlDoctype, offsetToLine(source, templateStartOffset + startIdx))\n }\n },\n oncomment() {\n if (idx.htmlComments) {\n const startIdx = (parser as any).startIndex as number\n onHit(idx.htmlComments, offsetToLine(source, templateStartOffset + startIdx))\n }\n },\n }, { decodeEntities: false, lowerCaseTags: true, lowerCaseAttributeNames: true })\n\n parser.write(html)\n parser.end()\n}\n\nfunction scanInlineStyle(\n style: string,\n idx: Indexes,\n line: number,\n onHit: (feature: Feature, line: number) => void,\n) {\n // Wrap in a rule so safeParser produces a Root with declarations\n const wrapped = `*{${style}}`\n try {\n const root = safeParser(wrapped)\n root.walkDecls((decl) => {\n if (idx.cssImportant && decl.important) onHit(idx.cssImportant, line)\n const fs = idx.cssProp.get(decl.prop)\n if (fs) for (const f of fs) onHit(f, line)\n if (idx.cssVariables && decl.prop.startsWith('--')) onHit(idx.cssVariables, line)\n const pvs = idx.cssPropValue.get(decl.prop)\n if (pvs) {\n const v = decl.value.trim().toLowerCase()\n for (const pv of pvs) if (v === pv.value) onHit(pv.feature, line)\n }\n if (idx.cssFunction.size || idx.cssUnit.size || idx.cssVariables || idx.cssModernColor) {\n try {\n valueParser(decl.value).walk((n) => {\n if (n.type === 'function') {\n const fname = n.value.toLowerCase()\n const fs2 = idx.cssFunction.get(fname)\n if (fs2) for (const f of fs2) onHit(f, line)\n if (idx.cssVariables && fname === 'var') onHit(idx.cssVariables, line)\n if (idx.cssModernColor && MODERN_COLOR_FNS.has(fname)) onHit(idx.cssModernColor, line)\n } else if (n.type === 'word') {\n const m = /^-?\\d*\\.?\\d+([a-z%]+)$/i.exec(n.value)\n if (m) {\n const fs2 = idx.cssUnit.get(m[1].toLowerCase())\n if (fs2) for (const f of fs2) onHit(f, line)\n }\n }\n })\n } catch {}\n }\n })\n } catch {}\n}\n\nfunction labelFor(idx: Indexes, level: SupportLevel): string {\n const n = idx.nicenames\n if (level === 'unsupported') return n.unsupported ?? 'Not supported'\n if (level === 'mitigated') return n.mitigated ?? 'Partially supported'\n return n.unknown ?? 'Support unknown'\n}\n\nasync function scan(\n rootFile: string,\n config: MaizzleConfig,\n componentDirs: NormalizedComponentSource[],\n allowedClients: Set<string> | 'all',\n): Promise<Issue[]> {\n const idx = await initCompatibility()\n if (!idx) return []\n\n const root = config.root ?? process.cwd()\n const componentMap = await buildComponentMap(root, componentDirs)\n const streams: FileStreams[] = []\n collectStreams(rootFile, componentMap, new Set(), streams)\n\n const issues: Issue[] = []\n const seen = new Set<string>()\n const resolvedCache = new Map<string, { level: SupportLevel, affected: string[] } | null>()\n const resolveSupport = (f: Feature) => {\n let cached = resolvedCache.get(f.slug)\n if (cached === undefined) {\n cached = computeSupport(f.stats, idx.familyNicenames, allowedClients)\n resolvedCache.set(f.slug, cached)\n }\n return cached\n }\n const add = (f: Feature, file: string, line?: number) => {\n const key = `${f.slug}|${file}|${line ?? 0}`\n if (seen.has(key)) return\n const support = resolveSupport(f)\n if (!support) return\n seen.add(key)\n issues.push({\n kind: 'compat',\n slug: f.slug, title: f.title, url: f.url, category: f.category,\n supportLevel: support.level, supportLabel: labelFor(idx, support.level),\n affectedClients: support.affected,\n line, file,\n })\n }\n\n /**\n * Stream A: compiled CSS from real pipeline — reflects shipped output\n * (Tailwind utilities resolved, @maizzle/tailwindcss imported, calc\n * resolved, modern CSS lowered). Filter hits whose containing\n * selector doesn't reference a user class — drops Tailwind\n * preflight noise. For hits without a class selector\n * (e.g. @media, user-written rules), attribute to the\n * file that owned the style block.\n */\n const compiledBlocks = await compileViaPipeline(streams, config, rootFile)\n for (const block of compiledBlocks) {\n walkCss(block.css, idx, (feature, node) => {\n const locations = classLocations(node.selector, streams)\n if (!locations.length) {\n add(feature, block.file, block.line)\n return\n }\n /**\n * @media features collapse to a single source line: the first use\n * of whatever class/variant triggered the wrapper. Other\n * features show up for every occurrence.\n */\n if (feature.slug.startsWith('css-at-media')) {\n add(feature, locations[0].file, locations[0].line)\n } else {\n for (const { file, line } of locations) add(feature, file, line)\n }\n })\n }\n\n // Stream C: source template per file\n for (const s of streams) {\n if (!s.template) continue\n walkTemplate(s.template.content, idx, s.template.offset, s.source,\n s.source.indexOf(s.template.content),\n (feature, line) => add(feature, s.path, line))\n }\n\n return issues\n}\n\n/**\n * Return every (file, line) where any class from the selector appears in a\n * template. Scans every stream so a shared utility class used in multiple\n * components surfaces once per occurrence.\n */\nfunction classLocations(\n selector: string | undefined,\n streams: FileStreams[],\n): Array<{ file: string, line: number }> {\n if (!selector) return []\n const classNames = extractSelectorClasses(selector)\n if (!classNames.length) return []\n const out: Array<{ file: string, line: number }> = []\n const seen = new Set<string>()\n for (const cn of classNames) {\n for (const s of streams) {\n if (!s.classes.has(cn) || !s.template) continue\n const tpl = s.template.content\n const tplStart = s.source.indexOf(tpl)\n let pos = 0\n while (true) {\n const i = tpl.indexOf(cn, pos)\n if (i < 0) break\n pos = i + cn.length\n // Whole-word boundary: adjacent char must be whitespace or quote\n const before = i > 0 ? tpl[i - 1] : ' '\n const after = i + cn.length < tpl.length ? tpl[i + cn.length] : ' '\n if (!isClassBoundary(before) || !isClassBoundary(after)) continue\n const line = offsetToLine(s.source, tplStart + i)\n const key = `${s.path}|${line}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ file: s.path, line })\n }\n }\n }\n return out\n}\n\nfunction isClassBoundary(c: string): boolean {\n return c === ' ' || c === '\\t' || c === '\\n' || c === '\\r' || c === '\"' || c === \"'\"\n}\n\nfunction extractSelectorClasses(selector: string): string[] {\n const out: string[] = []\n const re = /\\.((?:\\\\.|[\\w-])+)/g\n let m\n while ((m = re.exec(selector)) !== null) {\n out.push(m[1].replace(/\\\\(.)/g, '$1'))\n }\n return out\n}\n\nconst CATEGORY_ORDER = ['css', 'html', 'image', 'others']\nconst LEVEL_ORDER: Record<string, number> = { error: 0, unsupported: 1, warning: 2, mitigated: 3, unknown: 4 }\n\nfunction orderKey(i: Issue): number {\n if (i.kind === 'lint') return LEVEL_ORDER[i.severity!] ?? 99\n return LEVEL_ORDER[i.supportLevel!] ?? 99\n}\n\nfunction resolveChecksConfig(config: MaizzleConfig) {\n const raw = (config as any).server?.checks\n if (raw === false) return null\n const clients: Set<string> | 'all' = raw?.clients === 'all'\n ? 'all'\n : Array.isArray(raw?.clients) && raw.clients.length\n ? new Set(raw.clients as string[])\n : DEFAULT_CLIENTS\n const level: 'error' | 'warning' | 'lint' | null = raw?.level ?? null\n return { clients, level }\n}\n\nfunction passesLevelFilter(issue: Issue, level: 'error' | 'warning' | 'lint' | null): boolean {\n if (!level) return true\n if (level === 'lint') return issue.kind === 'lint'\n if (issue.kind === 'lint') {\n return level === 'error' ? issue.severity === 'error' : issue.severity === 'warning'\n }\n // compat\n return level === 'error'\n ? issue.supportLevel === 'unsupported'\n : issue.supportLevel === 'mitigated' || issue.supportLevel === 'unknown'\n}\n\nexport async function serveCompatibility(\n url: string,\n res: any,\n config: MaizzleConfig,\n componentDirs: NormalizedComponentSource[],\n) {\n const filePath = url.replace('/__maizzle/compatibility/', '').replace(/\\?.*$/, '')\n const checksCfg = resolveChecksConfig(config)\n try {\n res.setHeader('Content-Type', 'application/json')\n if (!checksCfg) {\n /**\n * Defensive: UI hides the tab using window.__MAIZZLE_CONFIG__ so\n * it shouldn't reach this endpoint when disabled, but if\n * something else does, return an empty list.\n */\n res.end(JSON.stringify([]))\n return\n }\n const absolutePath = resolve(filePath)\n const [compatIssues, lintIssues] = await Promise.all([\n scan(absolutePath, config, componentDirs, checksCfg.clients),\n scanLint(absolutePath, config, componentDirs),\n ])\n\n const idx = await initCompatibility()\n const lintAsIssues: Issue[] = lintIssues.map((li) => {\n const info = li.slug ? idx?.bySlug.get(li.slug) : undefined\n return {\n kind: 'lint',\n slug: li.slug,\n title: li.title,\n url: info?.url,\n category: li.category,\n severity: li.type,\n message: li.message,\n line: li.line,\n file: li.file,\n }\n })\n\n let issues: Issue[] = [...compatIssues, ...lintAsIssues]\n issues = issues.filter((i) => !isFrameworkComponent(i.file))\n if (checksCfg.level) issues = issues.filter((i) => passesLevelFilter(i, checksCfg.level))\n issues.sort((a, b) => {\n const c = CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category)\n if (c) return c\n const l = orderKey(a) - orderKey(b)\n if (l) return l\n return (a.slug ?? a.title).localeCompare(b.slug ?? b.title)\n })\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"linter.js","names":[],"sources":["../../src/server/linter.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { parseSfcBlocks, findComponentTags, buildComponentMap, isFrameworkComponent } from './sfc-utils.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport type { NormalizedComponentSource } from '../utils/componentSources.ts'\n\nexport interface LintIssue {\n type: 'error' | 'warning'\n title: string\n message: string\n /** Which tab this lands in when merged into the Checks panel. */\n category: 'css' | 'html' | 'image' | 'others'\n /** Optional caniemail slug for URL enrichment (e.g. \"html-html\"). */\n slug?: string\n line?: number\n file: string\n}\n\ninterface Presence {\n html: boolean\n head: boolean\n body: boolean\n}\n\n/**\n * Maizzle auto-adds role=\"none\" to every <table> by default via the\n * addAttributes transformer. Warn about missing role only when that won't\n * happen:\n * - useTransformers: false → whole pipeline off\n * - html.attributes.add: false → auto-add disabled globally\n * - add.table: false → table selector opted out\n * - add.table.role: false → role attribute specifically opted out\n * An empty `add.table: {}` still inherits role via defu merge, so it's fine.\n */\nfunction tableRoleAutoAdded(config: MaizzleConfig): boolean {\n if (config.useTransformers === false) return false\n const add = config.html?.attributes?.add\n if (add === false) return false\n if (!add || typeof add !== 'object') return true\n const table = (add as any).table\n if (table === false) return false\n if (table && typeof table === 'object' && table.role === false) return false\n return true\n}\n\nexport async function scanLint(\n rootFile: string,\n config: MaizzleConfig,\n componentDirs: NormalizedComponentSource[],\n): Promise<LintIssue[]> {\n const root = config.root ?? process.cwd()\n const componentMap = await buildComponentMap(root, componentDirs)\n const visited = new Set<string>()\n const presence: Presence = { html: false, head: false, body: false }\n const checkTableRole = !tableRoleAutoAdded(config)\n const issues = checkFile(rootFile, componentMap, visited, presence, checkTableRole)\n\n if (!presence.html) issues.push({ type: 'warning', category: 'html', title: 'Missing <html>', message: 'Root <html> tag not found in the template or any of its components.', slug: 'html-html', line: 1, file: rootFile })\n if (!presence.head) issues.push({ type: 'warning', category: 'html', title: 'Missing <head>', message: 'Root <head> tag not found in the template or any of its components.', slug: 'html-head', line: 1, file: rootFile })\n if (!presence.body) issues.push({ type: 'warning', category: 'html', title: 'Missing <body>', message: 'Root <body> tag not found in the template or any of its components.', slug: 'html-body', line: 1, file: rootFile })\n\n return issues\n}\n\nexport async function serveLint(url: string, res: any, config: MaizzleConfig, componentDirs: NormalizedComponentSource[]) {\n const filePath = url.replace('/__maizzle/lint/', '').replace(/\\?.*$/, '')\n\n try {\n const absolutePath = resolve(filePath)\n const issues = (await scanLint(absolutePath, config, componentDirs))\n .filter(i => !isFrameworkComponent(i.file))\n\n // Sort: errors first, then warnings, then by line\n issues.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'error' ? -1 : 1\n return (a.line ?? 0) - (b.line ?? 0)\n })\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nfunction checkFile(\n filePath: string,\n componentMap: Map<string, string>,\n visited: Set<string>,\n presence: Presence,\n checkTableRole: boolean,\n): LintIssue[] {\n if (visited.has(filePath)) return []\n visited.add(filePath)\n\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n return []\n }\n\n const { template } = parseSfcBlocks(source)\n const issues: LintIssue[] = []\n\n if (template) {\n issues.push(...lintHtml(template.content, template.offset, filePath, presence, checkTableRole))\n\n // Recurse into components\n const componentTags = findComponentTags(template.content)\n for (const tag of componentTags) {\n const componentPath = componentMap.get(tag.toLowerCase())\n if (componentPath) {\n issues.push(...checkFile(componentPath, componentMap, visited, presence, checkTableRole))\n }\n }\n }\n\n return issues\n}\n\nfunction lineAt(html: string, offset: number, lineOffset: number): number {\n return html.slice(0, offset).split('\\n').length + lineOffset\n}\n\n/**\n * True if the <img> tag has a width defined via any of:\n * - `width` attribute\n * - inline `style` with a `width` property\n * - class attribute with a Tailwind `w-` utility (any variant prefix like\n * sm:, hover:), or an arbitrary `[width:…]` utility\n */\nfunction hasWidthDefined(imgTag: string): boolean {\n if (/\\bwidth\\s*=/i.test(imgTag)) return true\n\n const styleMatch = imgTag.match(/\\bstyle\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (styleMatch && /(^|[;\\s])width\\s*:/i.test(styleMatch[1])) return true\n\n const classMatch = imgTag.match(/\\bclass\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (classMatch) {\n const classes = classMatch[1]\n if (/(?:^|\\s)(?:[a-z0-9-]+:)*w-\\S+/i.test(classes)) return true\n if (/\\[width:/i.test(classes)) return true\n }\n return false\n}\n\nfunction lintHtml(html: string, lineOffset: number, filePath: string, presence: Presence, checkTableRole: boolean): LintIssue[] {\n const issues: LintIssue[] = []\n\n // Match all tags (multiline) — [^>] doesn't cross > so use [\\s\\S] with lazy quantifier\n const tagRe = /<([a-zA-Z][a-zA-Z0-9]*)\\b([\\s\\S]*?)>/g\n\n for (const m of Array.from(html.matchAll(tagRe))) {\n const tag = m[0]\n const tagName = m[1].toLowerCase()\n const line = lineAt(html, m.index!, lineOffset)\n\n if (tagName === 'html') presence.html = true\n else if (tagName === 'head') presence.head = true\n else if (tagName === 'body') presence.body = true\n\n /**\n * Layout tables — accessibility requires role=\"none\" so screen readers\n * skip the table structure. Only surface the warning when the user\n * has disabled Maizzle's auto-role-add; otherwise every build\n * step output already has role=\"none\" set.\n */\n if (checkTableRole && tagName === 'table') {\n const roleMatch = tag.match(/\\brole\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!roleMatch) {\n const tableEndIdx = html.indexOf('</table>', m.index!)\n const inner = tableEndIdx >= 0 ? html.slice(m.index!, tableEndIdx) : ''\n const isDataTable = /<th\\b/i.test(inner) || /<caption\\b/i.test(inner)\n if (!isDataTable) {\n issues.push({ type: 'warning', category: 'html', title: 'Layout table missing role', message: 'Add role=\"none\" so screen readers skip this layout table.', slug: 'html-role', line, file: filePath })\n }\n }\n }\n\n // Images\n if (tagName === 'img') {\n if (!/\\balt\\s*=/i.test(tag)) {\n issues.push({ type: 'warning', category: 'image', title: 'Missing alt text', message: 'Image is missing the alt attribute', line, file: filePath })\n }\n\n const srcMatch = tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!srcMatch) {\n issues.push({ type: 'error', category: 'image', title: 'Missing image src', message: 'Image tag has no src attribute', line, file: filePath })\n } else if (!srcMatch[1].trim()) {\n issues.push({ type: 'error', category: 'image', title: 'Empty image src', message: 'Image src attribute is empty', line, file: filePath })\n } else if (srcMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', category: 'image', title: 'Insecure image src', message: 'Image loads over HTTP instead of HTTPS', line, file: filePath })\n }\n\n if (!hasWidthDefined(tag)) {\n issues.push({ type: 'warning', category: 'image', title: 'Missing image width', message: 'Use a `width=\"\"` attribute for best results in Outlook', line, file: filePath })\n }\n }\n\n // Any tag with href — skip resource tags handled below\n if (!['link', 'script', 'source'].includes(tagName)) {\n const hrefMatch = tag.match(/\\bhref\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (hrefMatch) {\n const href = hrefMatch[1].trim()\n if (!href) {\n issues.push({ type: 'error', category: 'html', title: 'Empty link href', message: 'Link href attribute is empty', line, file: filePath })\n } else if (href === '#' || href === '/') {\n issues.push({ type: 'error', category: 'html', title: 'Placeholder link', message: `Link href is \"${href}\"`, line, file: filePath })\n } else if (href.startsWith('http:')) {\n issues.push({ type: 'warning', category: 'html', title: 'Insecure link', message: 'Link uses HTTP instead of HTTPS', line, file: filePath })\n } else if (href.startsWith('http') && !/^https?:\\/\\/.+\\..+/i.test(href)) {\n issues.push({ type: 'error', category: 'html', title: 'Invalid link', message: `Link href \"${href}\" looks malformed`, line, file: filePath })\n }\n }\n }\n\n // Insecure resources (<link>, <script>, <source>)\n if (['link', 'script', 'source'].includes(tagName)) {\n const attrMatch = tag.match(/\\b(?:href|src)\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (attrMatch && attrMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', category: 'html', title: 'Insecure resource', message: 'Resource loads over HTTP instead of HTTPS', line, file: filePath })\n }\n }\n }\n\n // Insecure CSS url() references\n for (const m of Array.from(html.matchAll(/url\\s*\\(\\s*[\"']?(http:[^\"')]+)[\"']?\\s*\\)/gi))) {\n issues.push({ type: 'warning', category: 'css', title: 'Insecure CSS url()', message: 'CSS url() loads over HTTP instead of HTTPS', line: lineAt(html, m.index!, lineOffset), file: filePath })\n }\n\n // Check for unclosed tags (block-level and common inline elements)\n const voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n ])\n\n const trackedTags = new Set([\n 'a', 'b', 'body', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'head', 'html', 'i', 'li', 'ol', 'p', 'span', 'strong', 'style',\n 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'u', 'ul',\n ])\n\n const stack: Array<{ tag: string, line: number }> = []\n\n // Strip comments and content inside <style>/<script> to avoid false matches\n const stripped = html\n .replace(/<!--[\\s\\S]*?-->/g, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n .replace(/<(style|script)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n\n const strippedLines = stripped.split('\\n')\n\n for (let i = 0; i < strippedLines.length; i++) {\n const line = strippedLines[i]\n const tagRegex = /<\\/?([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*\\/?>/g\n let m\n\n while ((m = tagRegex.exec(line)) !== null) {\n const fullMatch = m[0]\n const tagName = m[1].toLowerCase()\n\n if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue\n if (fullMatch.endsWith('/>')) continue\n\n if (fullMatch.startsWith('</')) {\n // Closing tag\n let lastOpen = -1\n for (let j = stack.length - 1; j >= 0; j--) {\n if (stack[j].tag === tagName) { lastOpen = j; break }\n }\n if (lastOpen !== -1) {\n stack.splice(lastOpen, 1)\n }\n } else {\n // Opening tag\n stack.push({ tag: tagName, line: i + 1 + lineOffset })\n }\n }\n }\n\n for (const unclosed of stack) {\n issues.push({\n type: 'error',\n category: 'html',\n title: 'Unclosed tag',\n message: `<${unclosed.tag}> tag is not closed`,\n line: unclosed.line,\n file: filePath,\n })\n }\n\n return issues\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"linter.js","names":[],"sources":["../../src/server/linter.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { parseSfcBlocks, findComponentTags, buildComponentMap, isFrameworkComponent } from './sfc-utils.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport type { NormalizedComponentSource } from '../utils/componentSources.ts'\n\nexport interface LintIssue {\n type: 'error' | 'warning'\n title: string\n message: string\n /** Which tab this lands in when merged into the Checks panel. */\n category: 'css' | 'html' | 'image' | 'others'\n /** Optional caniemail slug for URL enrichment (e.g. \"html-html\"). */\n slug?: string\n line?: number\n file: string\n}\n\ninterface Presence {\n html: boolean\n head: boolean\n body: boolean\n}\n\n/**\n * Maizzle auto-adds role=\"none\" to every <table> by default via the\n * addAttributes transformer. Warn about missing role only when that won't\n * happen:\n * - useTransformers: false → whole pipeline off\n * - html.attributes.add: false → auto-add disabled globally\n * - add.table: false → table selector opted out\n * - add.table.role: false → role attribute specifically opted out\n * An empty `add.table: {}` still inherits role via defu merge, so it's fine.\n */\nfunction tableRoleAutoAdded(config: MaizzleConfig): boolean {\n if (config.useTransformers === false) return false\n const add = config.html?.attributes?.add\n if (add === false) return false\n if (!add || typeof add !== 'object') return true\n const table = (add as any).table\n if (table === false) return false\n if (table && typeof table === 'object' && table.role === false) return false\n return true\n}\n\nexport async function scanLint(\n rootFile: string,\n config: MaizzleConfig,\n componentDirs: NormalizedComponentSource[],\n): Promise<LintIssue[]> {\n const root = config.root ?? process.cwd()\n const componentMap = await buildComponentMap(root, componentDirs)\n const visited = new Set<string>()\n const presence: Presence = { html: false, head: false, body: false }\n const checkTableRole = !tableRoleAutoAdded(config)\n const issues = checkFile(rootFile, componentMap, visited, presence, checkTableRole)\n\n if (!presence.html) issues.push({ type: 'warning', category: 'html', title: 'Missing <html>', message: 'Root <html> tag not found in the template or any of its components.', slug: 'html-html', line: 1, file: rootFile })\n if (!presence.head) issues.push({ type: 'warning', category: 'html', title: 'Missing <head>', message: 'Root <head> tag not found in the template or any of its components.', slug: 'html-head', line: 1, file: rootFile })\n if (!presence.body) issues.push({ type: 'warning', category: 'html', title: 'Missing <body>', message: 'Root <body> tag not found in the template or any of its components.', slug: 'html-body', line: 1, file: rootFile })\n\n return issues\n}\n\nexport async function serveLint(url: string, res: any, config: MaizzleConfig, componentDirs: NormalizedComponentSource[]) {\n const filePath = url.replace('/__maizzle/lint/', '').replace(/\\?.*$/, '')\n\n try {\n const absolutePath = resolve(filePath)\n const issues = (await scanLint(absolutePath, config, componentDirs))\n .filter(i => !isFrameworkComponent(i.file))\n\n // Sort: errors first, then warnings, then by line\n issues.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'error' ? -1 : 1\n return (a.line ?? 0) - (b.line ?? 0)\n })\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nfunction checkFile(\n filePath: string,\n componentMap: Map<string, string>,\n visited: Set<string>,\n presence: Presence,\n checkTableRole: boolean,\n): LintIssue[] {\n if (visited.has(filePath)) return []\n visited.add(filePath)\n\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n return []\n }\n\n const { template } = parseSfcBlocks(source)\n const issues: LintIssue[] = []\n\n if (template) {\n issues.push(...lintHtml(template.content, template.offset, filePath, presence, checkTableRole))\n\n // Recurse into components\n const componentTags = findComponentTags(template.content)\n for (const tag of componentTags) {\n const componentPath = componentMap.get(tag.toLowerCase())\n if (componentPath) {\n issues.push(...checkFile(componentPath, componentMap, visited, presence, checkTableRole))\n }\n }\n }\n\n return issues\n}\n\nfunction lineAt(html: string, offset: number, lineOffset: number): number {\n return html.slice(0, offset).split('\\n').length + lineOffset\n}\n\n/**\n * True if the <img> tag has a width defined via any of:\n * - `width` attribute\n * - inline `style` with a `width` property\n * - class attribute with a Tailwind `w-` utility (any variant prefix like\n * sm:, hover:), or an arbitrary `[width:…]` utility\n */\nfunction hasWidthDefined(imgTag: string): boolean {\n if (/\\bwidth\\s*=/i.test(imgTag)) return true\n\n const styleMatch = imgTag.match(/\\bstyle\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (styleMatch && /(^|[;\\s])width\\s*:/i.test(styleMatch[1])) return true\n\n const classMatch = imgTag.match(/\\bclass\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (classMatch) {\n const classes = classMatch[1]\n if (/(?:^|\\s)(?:[a-z0-9-]+:)*w-\\S+/i.test(classes)) return true\n if (/\\[width:/i.test(classes)) return true\n }\n return false\n}\n\nfunction lintHtml(html: string, lineOffset: number, filePath: string, presence: Presence, checkTableRole: boolean): LintIssue[] {\n const issues: LintIssue[] = []\n\n // Match all tags (multiline) — [^>] doesn't cross > so use [\\s\\S] with lazy quantifier\n const tagRe = /<([a-zA-Z][a-zA-Z0-9]*)\\b([\\s\\S]*?)>/g\n\n for (const m of Array.from(html.matchAll(tagRe))) {\n const tag = m[0]\n const tagName = m[1].toLowerCase()\n const line = lineAt(html, m.index!, lineOffset)\n\n if (tagName === 'html') presence.html = true\n else if (tagName === 'head') presence.head = true\n else if (tagName === 'body') presence.body = true\n\n /**\n * Layout tables — accessibility requires role=\"none\" so screen readers\n * skip the table structure. Only surface the warning when the user\n * has disabled Maizzle's auto-role-add; otherwise every build\n * step output already has role=\"none\" set.\n */\n if (checkTableRole && tagName === 'table') {\n const roleMatch = tag.match(/\\brole\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!roleMatch) {\n const tableEndIdx = html.indexOf('</table>', m.index!)\n const inner = tableEndIdx >= 0 ? html.slice(m.index!, tableEndIdx) : ''\n const isDataTable = /<th\\b/i.test(inner) || /<caption\\b/i.test(inner)\n if (!isDataTable) {\n issues.push({ type: 'warning', category: 'html', title: 'Layout table missing role', message: 'Add role=\"none\" so screen readers skip this layout table.', slug: 'html-role', line, file: filePath })\n }\n }\n }\n\n // Images\n if (tagName === 'img') {\n if (!/\\balt\\s*=/i.test(tag)) {\n issues.push({ type: 'warning', category: 'image', title: 'Missing alt text', message: 'Image is missing the alt attribute', line, file: filePath })\n }\n\n const srcMatch = tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!srcMatch) {\n issues.push({ type: 'error', category: 'image', title: 'Missing image src', message: 'Image tag has no src attribute', line, file: filePath })\n } else if (!srcMatch[1].trim()) {\n issues.push({ type: 'error', category: 'image', title: 'Empty image src', message: 'Image src attribute is empty', line, file: filePath })\n } else if (srcMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', category: 'image', title: 'Insecure image src', message: 'Image loads over HTTP instead of HTTPS', line, file: filePath })\n }\n\n if (!hasWidthDefined(tag)) {\n issues.push({ type: 'warning', category: 'image', title: 'Missing image width', message: 'Use a `width=\"\"` attribute for best results in Outlook', line, file: filePath })\n }\n }\n\n // Any tag with href — skip resource tags handled below\n if (!['link', 'script', 'source'].includes(tagName)) {\n const hrefMatch = tag.match(/\\bhref\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (hrefMatch) {\n const href = hrefMatch[1].trim()\n if (!href) {\n issues.push({ type: 'error', category: 'html', title: 'Empty link href', message: 'Link href attribute is empty', line, file: filePath })\n } else if (href === '#' || href === '/') {\n issues.push({ type: 'error', category: 'html', title: 'Placeholder link', message: `Link href is \"${href}\"`, line, file: filePath })\n } else if (href.startsWith('http:')) {\n issues.push({ type: 'warning', category: 'html', title: 'Insecure link', message: 'Link uses HTTP instead of HTTPS', line, file: filePath })\n } else if (href.startsWith('http') && !/^https?:\\/\\/.+\\..+/i.test(href)) {\n issues.push({ type: 'error', category: 'html', title: 'Invalid link', message: `Link href \"${href}\" looks malformed`, line, file: filePath })\n }\n }\n }\n\n // Insecure resources (<link>, <script>, <source>)\n if (['link', 'script', 'source'].includes(tagName)) {\n const attrMatch = tag.match(/\\b(?:href|src)\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (attrMatch && attrMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', category: 'html', title: 'Insecure resource', message: 'Resource loads over HTTP instead of HTTPS', line, file: filePath })\n }\n }\n }\n\n // Insecure CSS url() references\n for (const m of Array.from(html.matchAll(/url\\s*\\(\\s*[\"']?(http:[^\"')]+)[\"']?\\s*\\)/gi))) {\n issues.push({ type: 'warning', category: 'css', title: 'Insecure CSS url()', message: 'CSS url() loads over HTTP instead of HTTPS', line: lineAt(html, m.index!, lineOffset), file: filePath })\n }\n\n // Check for unclosed tags (block-level and common inline elements)\n const voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n ])\n\n const trackedTags = new Set([\n 'a', 'b', 'body', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'head', 'html', 'i', 'li', 'ol', 'p', 'span', 'strong', 'style',\n 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'u', 'ul',\n ])\n\n const stack: Array<{ tag: string, line: number }> = []\n\n // Strip comments and content inside <style>/<script> to avoid false matches\n const stripped = html\n .replace(/<!--[\\s\\S]*?-->/g, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n .replace(/<(style|script)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n\n const strippedLines = stripped.split('\\n')\n\n for (let i = 0; i < strippedLines.length; i++) {\n const line = strippedLines[i]\n const tagRegex = /<\\/?([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*\\/?>/g\n let m\n\n while ((m = tagRegex.exec(line)) !== null) {\n const fullMatch = m[0]\n const tagName = m[1].toLowerCase()\n\n if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue\n if (fullMatch.endsWith('/>')) continue\n\n if (fullMatch.startsWith('</')) {\n // Closing tag\n let lastOpen = -1\n for (let j = stack.length - 1; j >= 0; j--) {\n if (stack[j].tag === tagName) { lastOpen = j; break }\n }\n if (lastOpen !== -1) {\n stack.splice(lastOpen, 1)\n }\n } else {\n // Opening tag\n stack.push({ tag: tagName, line: i + 1 + lineOffset })\n }\n }\n }\n\n for (const unclosed of stack) {\n issues.push({\n type: 'error',\n category: 'html',\n title: 'Unclosed tag',\n message: `<${unclosed.tag}> tag is not closed`,\n line: unclosed.line,\n file: filePath,\n })\n }\n\n return issues\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { componentNameFromPath } from "../utils/componentSources.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import { dirname, resolve } from "node:path";
4
- import { glob } from "tinyglobby";
5
4
  import { fileURLToPath } from "node:url";
5
+ import { glob } from "tinyglobby";
6
6
  //#region src/server/sfc-utils.ts
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
8
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sfc-utils.js","names":[],"sources":["../../src/server/sfc-utils.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { glob } from 'tinyglobby'\nimport { componentNameFromPath, type NormalizedComponentSource } from '../utils/componentSources.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/**\n * Built-in framework components Maizzle ships internally. Issues raised\n * against these files are framework-owned, not the user's responsibility,\n * so the dev UI's Checks tab filters them out.\n */\nconst FRAMEWORK_COMPONENTS_DIR = resolve(__dirname, '../components').replace(/\\\\/g, '/') + '/'\n\nexport function isFrameworkComponent(file: string): boolean {\n return file.replace(/\\\\/g, '/').startsWith(FRAMEWORK_COMPONENTS_DIR)\n}\n\nexport interface SfcBlock {\n content: string\n offset: number\n}\n\nexport function parseSfcBlocks(source: string): { template: SfcBlock | null, styles: SfcBlock[] } {\n let template: SfcBlock | null = null\n const styles: SfcBlock[] = []\n\n const templateMatch = source.match(/<template\\b[^>]*>([\\s\\S]*)<\\/template>/)\n if (templateMatch) {\n const contentStart = source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])\n const offset = source.slice(0, contentStart).split('\\n').length - 1\n template = { content: templateMatch[1], offset }\n }\n\n const styleRe = /<style\\b([^>]*)>([\\s\\S]*?)<\\/style>/g\n let m\n while ((m = styleRe.exec(source)) !== null) {\n // Skip preprocessor styles (scss, less, etc.) — caniemail only parses plain CSS\n if (/\\blang\\s*=\\s*[\"'](?!css)/i.test(m[1])) continue\n\n const contentStart = m.index + m[0].indexOf(m[2])\n const offset = source.slice(0, contentStart).split('\\n').length - 1\n styles.push({ content: m[2], offset })\n }\n\n return { template, styles }\n}\n\n/**\n * Standard HTML elements — anything not in this set is treated as a component.\n */\nexport const HTML_ELEMENTS = new Set([\n 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base',\n 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption',\n 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del',\n 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset',\n 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',\n 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img',\n 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map',\n 'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol',\n 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q',\n 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select',\n 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary',\n 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th',\n 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr',\n])\n\nexport function findComponentTags(templateContent: string): string[] {\n const tags = new Set<string>()\n\n // PascalCase tags like <Section>, <Button>\n const pascalRe = /<([A-Z][a-zA-Z0-9]*)\\b/g\n let m\n while ((m = pascalRe.exec(templateContent)) !== null) {\n tags.add(m[1])\n }\n\n // kebab-case tags like <my-component>\n const kebabRe = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\\b/g\n while ((m = kebabRe.exec(templateContent)) !== null) {\n if (!HTML_ELEMENTS.has(m[1])) {\n tags.add(m[1])\n }\n }\n\n return [...tags]\n}\n\nexport async function buildComponentMap(\n root: string,\n componentDirs: NormalizedComponentSource[],\n): Promise<Map<string, string>> {\n const map = new Map<string, string>()\n\n /**\n * Built-in framework components + the user's default `components/` dir\n * use unplugin's directoryAsNamespace + collapseSamePrefixes\n * behavior — i.e. no explicit prefix, folder name becomes\n * the namespace.\n */\n const implicitDirs = [\n resolve(__dirname, '../components'),\n resolve(root, 'components'),\n ].filter(existsSync)\n\n const allSources: NormalizedComponentSource[] = [\n ...implicitDirs.map(path => ({ path, prefix: undefined, pathPrefix: true })),\n ...componentDirs.filter(s => existsSync(s.path)),\n ]\n\n for (const source of allSources) {\n const files = await glob(['**/*.vue'], { cwd: source.path, absolute: true })\n for (const file of files) {\n const name = componentNameFromPath({\n filePath: file,\n dirRoot: source.path,\n prefix: source.prefix,\n pathPrefix: source.pathPrefix,\n })\n // Lowercased for case-insensitive lookups in the linter/compat scanners.\n map.set(name.toLowerCase(), file)\n }\n }\n\n return map\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sfc-utils.js","names":[],"sources":["../../src/server/sfc-utils.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { glob } from 'tinyglobby'\nimport { componentNameFromPath, type NormalizedComponentSource } from '../utils/componentSources.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/**\n * Built-in framework components Maizzle ships internally. Issues raised\n * against these files are framework-owned, not the user's responsibility,\n * so the dev UI's Checks tab filters them out.\n */\nconst FRAMEWORK_COMPONENTS_DIR = resolve(__dirname, '../components').replace(/\\\\/g, '/') + '/'\n\nexport function isFrameworkComponent(file: string): boolean {\n return file.replace(/\\\\/g, '/').startsWith(FRAMEWORK_COMPONENTS_DIR)\n}\n\nexport interface SfcBlock {\n content: string\n offset: number\n}\n\nexport function parseSfcBlocks(source: string): { template: SfcBlock | null, styles: SfcBlock[] } {\n let template: SfcBlock | null = null\n const styles: SfcBlock[] = []\n\n const templateMatch = source.match(/<template\\b[^>]*>([\\s\\S]*)<\\/template>/)\n if (templateMatch) {\n const contentStart = source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])\n const offset = source.slice(0, contentStart).split('\\n').length - 1\n template = { content: templateMatch[1], offset }\n }\n\n const styleRe = /<style\\b([^>]*)>([\\s\\S]*?)<\\/style>/g\n let m\n while ((m = styleRe.exec(source)) !== null) {\n // Skip preprocessor styles (scss, less, etc.) — caniemail only parses plain CSS\n if (/\\blang\\s*=\\s*[\"'](?!css)/i.test(m[1])) continue\n\n const contentStart = m.index + m[0].indexOf(m[2])\n const offset = source.slice(0, contentStart).split('\\n').length - 1\n styles.push({ content: m[2], offset })\n }\n\n return { template, styles }\n}\n\n/**\n * Standard HTML elements — anything not in this set is treated as a component.\n */\nexport const HTML_ELEMENTS = new Set([\n 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base',\n 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption',\n 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del',\n 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset',\n 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',\n 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img',\n 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map',\n 'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol',\n 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q',\n 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select',\n 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary',\n 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th',\n 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr',\n])\n\nexport function findComponentTags(templateContent: string): string[] {\n const tags = new Set<string>()\n\n // PascalCase tags like <Section>, <Button>\n const pascalRe = /<([A-Z][a-zA-Z0-9]*)\\b/g\n let m\n while ((m = pascalRe.exec(templateContent)) !== null) {\n tags.add(m[1])\n }\n\n // kebab-case tags like <my-component>\n const kebabRe = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\\b/g\n while ((m = kebabRe.exec(templateContent)) !== null) {\n if (!HTML_ELEMENTS.has(m[1])) {\n tags.add(m[1])\n }\n }\n\n return [...tags]\n}\n\nexport async function buildComponentMap(\n root: string,\n componentDirs: NormalizedComponentSource[],\n): Promise<Map<string, string>> {\n const map = new Map<string, string>()\n\n /**\n * Built-in framework components + the user's default `components/` dir\n * use unplugin's directoryAsNamespace + collapseSamePrefixes\n * behavior — i.e. no explicit prefix, folder name becomes\n * the namespace.\n */\n const implicitDirs = [\n resolve(__dirname, '../components'),\n resolve(root, 'components'),\n ].filter(existsSync)\n\n const allSources: NormalizedComponentSource[] = [\n ...implicitDirs.map(path => ({ path, prefix: undefined, pathPrefix: true })),\n ...componentDirs.filter(s => existsSync(s.path)),\n ]\n\n for (const source of allSources) {\n const files = await glob(['**/*.vue'], { cwd: source.path, absolute: true })\n for (const file of files) {\n const name = componentNameFromPath({\n filePath: file,\n dirRoot: source.path,\n prefix: source.prefix,\n pathPrefix: source.pathPrefix,\n })\n // Lowercased for case-insensitive lookups in the linter/compat scanners.\n map.set(name.toLowerCase(), file)\n }\n }\n\n return map\n}\n"],"mappings":"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"}
@@ -53,6 +53,25 @@ const plaintextContent = ref('')
53
53
  const sourceView = ref<'compiled' | 'vue' | 'plaintext'>('compiled')
54
54
  const copied = ref(false)
55
55
 
56
+ /**
57
+ * Source views (Compiled HTML, Vue source, Plaintext) refresh lazily. On a
58
+ * save we only refetch the view currently on screen and bump
59
+ * `sourcesGeneration` to mark the others stale. Switching to a stale view
60
+ * keeps its previous content visible while a background refetch replaces it
61
+ * in place — so panels never flash empty. Each fetch stamps the generation
62
+ * it was initiated at so a save mid-fetch correctly leaves the view stale.
63
+ */
64
+ const sourcesGeneration = ref(0)
65
+ const compiledGen = ref(-1)
66
+ const vueGen = ref(-1)
67
+ const plaintextGen = ref(-1)
68
+
69
+ function refreshSourceView(view: 'compiled' | 'vue' | 'plaintext') {
70
+ if (view === 'compiled' && (!sourceHtml.value || compiledGen.value < sourcesGeneration.value)) fetchSource()
71
+ if (view === 'vue' && (!vueSourceHtml.value || vueGen.value < sourcesGeneration.value)) fetchVueSource()
72
+ if (view === 'plaintext' && (!plaintextContent.value || plaintextGen.value < sourcesGeneration.value)) fetchPlaintext()
73
+ }
74
+
56
75
  const iframeEl = ref<HTMLIFrameElement>()
57
76
  const compiledSourceEl = ref<HTMLElement>()
58
77
  const vueSourceEl = ref<HTMLElement>()
@@ -300,9 +319,11 @@ const plaintextLoading = ref(false)
300
319
  async function fetchSource() {
301
320
  if (sourceLoading.value) return
302
321
  sourceLoading.value = true
322
+ const gen = sourcesGeneration.value
303
323
  try {
304
324
  const res = await fetch(`/__maizzle/source/${route.params.template}`)
305
325
  sourceHtml.value = await res.text()
326
+ compiledGen.value = gen
306
327
  } finally {
307
328
  sourceLoading.value = false
308
329
  }
@@ -311,9 +332,11 @@ async function fetchSource() {
311
332
  async function fetchVueSource() {
312
333
  if (vueSourceLoading.value) return
313
334
  vueSourceLoading.value = true
335
+ const gen = sourcesGeneration.value
314
336
  try {
315
337
  const res = await fetch(`/__maizzle/vue-source/${route.params.template}`)
316
338
  vueSourceHtml.value = await res.text()
339
+ vueGen.value = gen
317
340
  } finally {
318
341
  vueSourceLoading.value = false
319
342
  }
@@ -322,9 +345,11 @@ async function fetchVueSource() {
322
345
  async function fetchPlaintext() {
323
346
  if (plaintextLoading.value) return
324
347
  plaintextLoading.value = true
348
+ const gen = sourcesGeneration.value
325
349
  try {
326
350
  const res = await fetch(`/__maizzle/plaintext/${route.params.template}`)
327
351
  plaintextContent.value = await res.text()
352
+ plaintextGen.value = gen
328
353
  } finally {
329
354
  plaintextLoading.value = false
330
355
  }
@@ -430,17 +455,11 @@ watch(() => props.templates, (templates) => {
430
455
  })
431
456
 
432
457
  watch(viewMode, (mode) => {
433
- if (mode === 'source') {
434
- if (sourceView.value === 'compiled' && !sourceHtml.value) fetchSource()
435
- if (sourceView.value === 'vue' && !vueSourceHtml.value) fetchVueSource()
436
- if (sourceView.value === 'plaintext' && !plaintextContent.value) fetchPlaintext()
437
- }
458
+ if (mode === 'source') refreshSourceView(sourceView.value)
438
459
  })
439
460
 
440
461
  watch(sourceView, (view) => {
441
- if (view === 'vue' && !vueSourceHtml.value) fetchVueSource()
442
- if (view === 'compiled' && !sourceHtml.value) fetchSource()
443
- if (view === 'plaintext' && !plaintextContent.value) fetchPlaintext()
462
+ refreshSourceView(view)
444
463
  })
445
464
 
446
465
  /**
@@ -482,11 +501,15 @@ if ((import.meta as any).hot) {
482
501
  * long as the new content's height is similar. Plaintext
483
502
  * interpolation updates a single text node, so scroll
484
503
  * is naturally preserved.
504
+ *
505
+ * Only the preview iframe and the currently-visible source view
506
+ * refresh eagerly; hidden source views are marked stale (via the
507
+ * generation bump) and refresh on next switch, keeping their old
508
+ * content on screen until then so panels never flash empty.
485
509
  */
486
510
  fetchTemplate()
487
- fetchSource()
488
- fetchVueSource()
489
- fetchPlaintext()
511
+ sourcesGeneration.value++
512
+ if (viewMode.value === 'source') refreshSourceView(sourceView.value)
490
513
  })
491
514
 
492
515
  /**
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -1 +1 @@
1
- {"version":3,"file":"_helpers.d.ts","names":[],"sources":["../../../src/tests/render/_helpers.ts"],"mappings":";iBAIgB,iBAAA,CAAA;AAAA,iBAKA,QAAA,CAAS,GAAA,UAAa,IAAA,UAAc,OAAA"}
1
+ {"version":3,"file":"_helpers.d.ts","names":[],"sources":["../../../src/tests/render/_helpers.ts"],"mappings":";iBAIgB,iBAAA;AAAA,iBAKA,QAAA,CAAS,GAAA,UAAa,IAAA,UAAc,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"_helpers.js","names":[],"sources":["../../../src/tests/render/_helpers.ts"],"sourcesContent":["import { mkdtempSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\n\nexport function createTempProject() {\n const dir = mkdtempSync(join(tmpdir(), 'maizzle-render-'))\n return dir\n}\n\nexport function writeSfc(dir: string, path: string, content: string) {\n const full = join(dir, path)\n mkdirSync(join(dir, ...path.split('/').slice(0, -1)), { recursive: true })\n writeFileSync(full, content)\n}\n"],"mappings":";;;;AAIA,SAAgB,oBAAoB;CAElC,OADY,YAAY,KAAK,OAAO,GAAG,iBAAiB,CAC/C;AACX;AAEA,SAAgB,SAAS,KAAa,MAAc,SAAiB;CACnE,MAAM,OAAO,KAAK,KAAK,IAAI;CAC3B,UAAU,KAAK,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACzE,cAAc,MAAM,OAAO;AAC7B"}
1
+ {"version":3,"file":"_helpers.js","names":[],"sources":["../../../src/tests/render/_helpers.ts"],"sourcesContent":["import { mkdtempSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\n\nexport function createTempProject() {\n const dir = mkdtempSync(join(tmpdir(), 'maizzle-render-'))\n return dir\n}\n\nexport function writeSfc(dir: string, path: string, content: string) {\n const full = join(dir, path)\n mkdirSync(join(dir, ...path.split('/').slice(0, -1)), { recursive: true })\n writeFileSync(full, content)\n}\n"],"mappings":";;;;AAIA,SAAgB,oBAAoB;CAElC,OADY,YAAY,KAAK,OAAO,GAAG,iBAAiB,CAC/C;AACX;AAEA,SAAgB,SAAS,KAAa,MAAc,SAAiB;CACnE,MAAM,OAAO,KAAK,KAAK,IAAI;CAC3B,UAAU,KAAK,KAAK,GAAG,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACzE,cAAc,MAAM,OAAO;AAC7B"}
@@ -55,7 +55,6 @@ function addAttributesDom(dom, config = {}) {
55
55
  const addConfig = config.add;
56
56
  if (addConfig === false) return dom;
57
57
  const attributesToAdd = defu$1(typeof addConfig === "object" ? addConfig : {}, DEFAULT_ATTRIBUTES);
58
- if (Object.keys(attributesToAdd).length === 0) return dom;
59
58
  for (const [selectorPattern, attributes] of Object.entries(attributesToAdd)) {
60
59
  if (attributes === false) continue;
61
60
  const selectors = selectorPattern.split(",").map((s) => s.trim());
@@ -87,7 +86,7 @@ function elementMatches(el, selector) {
87
86
  const attrMatch = selector.match(/^\[([^\]=]+)(?:=([^\]]*))?\]$/);
88
87
  if (attrMatch) {
89
88
  const [, attrName, attrValue] = attrMatch;
90
- if (attrValue === void 0) return attrName in (el.attribs || {});
89
+ if (attrValue === void 0) return attrName in el.attribs;
91
90
  else return el.attribs?.[attrName] === attrValue;
92
91
  }
93
92
  if (selector.startsWith(".")) {
@@ -105,7 +104,7 @@ function elementMatches(el, selector) {
105
104
  const attrEqMatch = attrPart.match(/^([^=]+)(?:=(.*))?$/);
106
105
  if (attrEqMatch) {
107
106
  const [, attrName, attrValue] = attrEqMatch;
108
- if (attrValue === void 0) return attrName in (el.attribs || {});
107
+ if (attrValue === void 0) return attrName in el.attribs;
109
108
  else return el.attribs?.[attrName] === attrValue;
110
109
  }
111
110
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"addAttributes.js","names":["merge"],"sources":["../../src/transformers/addAttributes.ts"],"sourcesContent":["import { defu as merge } from 'defu'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize, walk } from '../utils/ast/index.ts'\nimport type { AttributesConfig } from '../types/config.ts'\n\n/**\n * Default attributes to add to elements.\n */\nconst DEFAULT_ATTRIBUTES: Record<string, Record<string, string | boolean | number>> = {\n table: {\n cellpadding: 0,\n cellspacing: 0,\n role: 'none',\n },\n img: {\n alt: '',\n },\n}\n\n/**\n * Add attributes transformer.\n *\n * Automatically adds attributes to HTML elements based on CSS selectors.\n *\n * Default attributes (can be disabled by setting `attributes.add` to false):\n * - table: cellpadding=\"0\", cellspacing=\"0\", role=\"none\"\n * - img: alt=\"\"\n *\n * Supports tag, class, id, and attribute selectors.\n * Multiple selectors can be specified by comma-separating them.\n *\n * @param html HTML string to transform.\n * @param config Attributes config (see {@link AttributesConfig}).\n * @returns The transformed HTML string.\n *\n * @example\n * import { addAttributes } from '@maizzle/framework'\n *\n * const out = addAttributes('<div></div>', {\n * add: {\n * div: { role: 'article' },\n * '.test': { editable: true },\n * '#header': { 'data-id': 'main' },\n * 'div, p': { class: 'content' },\n * },\n * })\n */\nexport function addAttributes(html: string, config: AttributesConfig = {}): string {\n return serialize(addAttributesDom(parse(html), config))\n}\n\n/**\n * DOM-form of {@link addAttributes} used by the internal transformer pipeline.\n * Takes a parsed DOM, returns a parsed DOM — avoids redundant\n * serialize/parse round-trips when chained with other transformers.\n */\nexport function addAttributesDom(dom: ChildNode[], config: AttributesConfig = {}): ChildNode[] {\n const addConfig = config.add\n\n // Disabled when explicitly set to false\n if (addConfig === false) {\n return dom\n }\n\n // Deep merge user attributes on top of defaults using defu\n const userAttributes = typeof addConfig === 'object' ? addConfig : {}\n const attributesToAdd = merge(userAttributes, DEFAULT_ATTRIBUTES) as Record<string, false | Record<string, false | string | boolean | number>>\n\n if (Object.keys(attributesToAdd).length === 0) {\n return dom\n }\n\n // Process each selector pattern\n for (const [selectorPattern, attributes] of Object.entries(attributesToAdd)) {\n // User opted out of this selector entirely (e.g. `table: false`)\n if (attributes === false) continue\n // Split by comma for multiple selectors\n const selectors = selectorPattern.split(',').map(s => s.trim())\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.name) return\n\n // Check if element matches any selector in the pattern\n const matches = selectors.some(selector => elementMatches(el, selector))\n\n if (matches) {\n // Initialize attribs if needed\n if (!el.attribs) {\n el.attribs = {}\n }\n\n for (const [attrName, attrValue] of Object.entries(attributes)) {\n // User opted out of this specific attribute (e.g. `role: false`)\n if (attrValue === false) continue\n // Special handling for class - merge instead of replace\n if (attrName === 'class' && el.attribs.class) {\n const existingClasses = el.attribs.class.split(/\\s+/).filter(Boolean)\n const newClasses = String(attrValue).split(/\\s+/).filter(Boolean)\n const mergedClasses = [...new Set([...existingClasses, ...newClasses])]\n if (mergedClasses.join(' ') !== el.attribs.class) {\n el.attribs.class = mergedClasses.join(' ')\n }\n } else {\n // Only add attribute if not already present\n if (!(attrName in el.attribs)) {\n el.attribs[attrName] = String(attrValue)\n }\n }\n }\n }\n })\n }\n\n return dom\n}\n\n/**\n * Check if an element matches a CSS selector.\n * Supports: tag, .class, #id, [attribute], [attribute=value]\n */\nfunction elementMatches(el: Element, selector: string): boolean {\n // Remove whitespace\n selector = selector.trim()\n\n // Check for attribute selector [attr] or [attr=value]\n const attrMatch = selector.match(/^\\[([^\\]=]+)(?:=([^\\]]*))?\\]$/)\n if (attrMatch) {\n const [, attrName, attrValue] = attrMatch\n if (attrValue === undefined) {\n // Just checking if attribute exists\n return attrName in (el.attribs || {})\n } else {\n // Check if attribute has specific value\n return el.attribs?.[attrName] === attrValue\n }\n }\n\n // Check for class selector .class\n if (selector.startsWith('.')) {\n const className = selector.slice(1)\n const classes = el.attribs?.class?.split(/\\s+/) || []\n return classes.includes(className)\n }\n\n // Check for id selector #id\n if (selector.startsWith('#')) {\n const id = selector.slice(1)\n return el.attribs?.id === id\n }\n\n // Check for tag selector (possibly with attribute)\n // Split tag from attribute if present, e.g., \"div[role=alert]\"\n const tagAttrMatch = selector.match(/^([a-z][a-z0-9]*)\\[([^\\]]+)\\]$/i)\n if (tagAttrMatch) {\n const [, tagName, attrPart] = tagAttrMatch\n if (el.name !== tagName) return false\n\n // Parse attribute part: could be \"attr\" or \"attr=value\"\n const attrEqMatch = attrPart.match(/^([^=]+)(?:=(.*))?$/)\n if (attrEqMatch) {\n const [, attrName, attrValue] = attrEqMatch\n if (attrValue === undefined) {\n return attrName in (el.attribs || {})\n } else {\n return el.attribs?.[attrName] === attrValue\n }\n }\n return false\n }\n\n // Simple tag selector\n return el.name === selector\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,qBAAgF;CACpF,OAAO;EACL,aAAa;EACb,aAAa;EACb,MAAM;CACR;CACA,KAAK,EACH,KAAK,GACP;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,cAAc,MAAc,SAA2B,CAAC,GAAW;CACjF,OAAO,UAAU,iBAAiB,MAAM,IAAI,GAAG,MAAM,CAAC;AACxD;;;;;;AAOA,SAAgB,iBAAiB,KAAkB,SAA2B,CAAC,GAAgB;CAC7F,MAAM,YAAY,OAAO;CAGzB,IAAI,cAAc,OAChB,OAAO;CAKT,MAAM,kBAAkBA,OADD,OAAO,cAAc,WAAW,YAAY,CAAC,GACtB,kBAAkB;CAEhE,IAAI,OAAO,KAAK,eAAe,EAAE,WAAW,GAC1C,OAAO;CAIT,KAAK,MAAM,CAAC,iBAAiB,eAAe,OAAO,QAAQ,eAAe,GAAG;EAE3E,IAAI,eAAe,OAAO;EAE1B,MAAM,YAAY,gBAAgB,MAAM,GAAG,EAAE,KAAI,MAAK,EAAE,KAAK,CAAC;EAE9D,KAAK,MAAM,SAAS;GAClB,MAAM,KAAK;GACX,IAAI,CAAC,GAAG,MAAM;GAKd,IAFgB,UAAU,MAAK,aAAY,eAAe,IAAI,QAAQ,CAE5D,GAAG;IAEX,IAAI,CAAC,GAAG,SACN,GAAG,UAAU,CAAC;IAGhB,KAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,UAAU,GAAG;KAE9D,IAAI,cAAc,OAAO;KAEzB,IAAI,aAAa,WAAW,GAAG,QAAQ,OAAO;MAC5C,MAAM,kBAAkB,GAAG,QAAQ,MAAM,MAAM,KAAK,EAAE,OAAO,OAAO;MACpE,MAAM,aAAa,OAAO,SAAS,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;MAChE,MAAM,gBAAgB,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,UAAU,CAAC,CAAC;MACtE,IAAI,cAAc,KAAK,GAAG,MAAM,GAAG,QAAQ,OACzC,GAAG,QAAQ,QAAQ,cAAc,KAAK,GAAG;KAE7C,OAEE,IAAI,EAAE,YAAY,GAAG,UACnB,GAAG,QAAQ,YAAY,OAAO,SAAS;IAG7C;GACF;EACF,CAAC;CACH;CAEA,OAAO;AACT;;;;;AAMA,SAAS,eAAe,IAAa,UAA2B;CAE9D,WAAW,SAAS,KAAK;CAGzB,MAAM,YAAY,SAAS,MAAM,+BAA+B;CAChE,IAAI,WAAW;EACb,MAAM,GAAG,UAAU,aAAa;EAChC,IAAI,cAAc,KAAA,GAEhB,OAAO,aAAa,GAAG,WAAW,CAAC;OAGnC,OAAO,GAAG,UAAU,cAAc;CAEtC;CAGA,IAAI,SAAS,WAAW,GAAG,GAAG;EAC5B,MAAM,YAAY,SAAS,MAAM,CAAC;EAElC,QADgB,GAAG,SAAS,OAAO,MAAM,KAAK,KAAK,CAAC,GACrC,SAAS,SAAS;CACnC;CAGA,IAAI,SAAS,WAAW,GAAG,GAAG;EAC5B,MAAM,KAAK,SAAS,MAAM,CAAC;EAC3B,OAAO,GAAG,SAAS,OAAO;CAC5B;CAIA,MAAM,eAAe,SAAS,MAAM,iCAAiC;CACrE,IAAI,cAAc;EAChB,MAAM,GAAG,SAAS,YAAY;EAC9B,IAAI,GAAG,SAAS,SAAS,OAAO;EAGhC,MAAM,cAAc,SAAS,MAAM,qBAAqB;EACxD,IAAI,aAAa;GACf,MAAM,GAAG,UAAU,aAAa;GAChC,IAAI,cAAc,KAAA,GAChB,OAAO,aAAa,GAAG,WAAW,CAAC;QAEnC,OAAO,GAAG,UAAU,cAAc;EAEtC;EACA,OAAO;CACT;CAGA,OAAO,GAAG,SAAS;AACrB"}
1
+ {"version":3,"file":"addAttributes.js","names":["merge"],"sources":["../../src/transformers/addAttributes.ts"],"sourcesContent":["import { defu as merge } from 'defu'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize, walk } from '../utils/ast/index.ts'\nimport type { AttributesConfig } from '../types/config.ts'\n\n/**\n * Default attributes to add to elements.\n */\nconst DEFAULT_ATTRIBUTES: Record<string, Record<string, string | boolean | number>> = {\n table: {\n cellpadding: 0,\n cellspacing: 0,\n role: 'none',\n },\n img: {\n alt: '',\n },\n}\n\n/**\n * Add attributes transformer.\n *\n * Automatically adds attributes to HTML elements based on CSS selectors.\n *\n * Default attributes (can be disabled by setting `attributes.add` to false):\n * - table: cellpadding=\"0\", cellspacing=\"0\", role=\"none\"\n * - img: alt=\"\"\n *\n * Supports tag, class, id, and attribute selectors.\n * Multiple selectors can be specified by comma-separating them.\n *\n * @param html HTML string to transform.\n * @param config Attributes config (see {@link AttributesConfig}).\n * @returns The transformed HTML string.\n *\n * @example\n * import { addAttributes } from '@maizzle/framework'\n *\n * const out = addAttributes('<div></div>', {\n * add: {\n * div: { role: 'article' },\n * '.test': { editable: true },\n * '#header': { 'data-id': 'main' },\n * 'div, p': { class: 'content' },\n * },\n * })\n */\nexport function addAttributes(html: string, config: AttributesConfig = {}): string {\n return serialize(addAttributesDom(parse(html), config))\n}\n\n/**\n * DOM-form of {@link addAttributes} used by the internal transformer pipeline.\n * Takes a parsed DOM, returns a parsed DOM — avoids redundant\n * serialize/parse round-trips when chained with other transformers.\n */\nexport function addAttributesDom(dom: ChildNode[], config: AttributesConfig = {}): ChildNode[] {\n const addConfig = config.add\n\n // Disabled when explicitly set to false\n if (addConfig === false) {\n return dom\n }\n\n // Deep merge user attributes on top of defaults using defu\n const userAttributes = typeof addConfig === 'object' ? addConfig : {}\n const attributesToAdd = merge(userAttributes, DEFAULT_ATTRIBUTES) as Record<string, false | Record<string, false | string | boolean | number>>\n\n // Process each selector pattern\n for (const [selectorPattern, attributes] of Object.entries(attributesToAdd)) {\n // User opted out of this selector entirely (e.g. `table: false`)\n if (attributes === false) continue\n // Split by comma for multiple selectors\n const selectors = selectorPattern.split(',').map(s => s.trim())\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.name) return\n\n // Check if element matches any selector in the pattern\n const matches = selectors.some(selector => elementMatches(el, selector))\n\n if (matches) {\n // Initialize attribs if needed\n if (!el.attribs) {\n el.attribs = {}\n }\n\n for (const [attrName, attrValue] of Object.entries(attributes)) {\n // User opted out of this specific attribute (e.g. `role: false`)\n if (attrValue === false) continue\n // Special handling for class - merge instead of replace\n if (attrName === 'class' && el.attribs.class) {\n const existingClasses = el.attribs.class.split(/\\s+/).filter(Boolean)\n const newClasses = String(attrValue).split(/\\s+/).filter(Boolean)\n const mergedClasses = [...new Set([...existingClasses, ...newClasses])]\n if (mergedClasses.join(' ') !== el.attribs.class) {\n el.attribs.class = mergedClasses.join(' ')\n }\n } else {\n // Only add attribute if not already present\n if (!(attrName in el.attribs)) {\n el.attribs[attrName] = String(attrValue)\n }\n }\n }\n }\n })\n }\n\n return dom\n}\n\n/**\n * Check if an element matches a CSS selector.\n * Supports: tag, .class, #id, [attribute], [attribute=value]\n */\nfunction elementMatches(el: Element, selector: string): boolean {\n // Remove whitespace\n selector = selector.trim()\n\n // Check for attribute selector [attr] or [attr=value]\n const attrMatch = selector.match(/^\\[([^\\]=]+)(?:=([^\\]]*))?\\]$/)\n if (attrMatch) {\n const [, attrName, attrValue] = attrMatch\n if (attrValue === undefined) {\n // Just checking if attribute exists\n return attrName in el.attribs\n } else {\n // Check if attribute has specific value\n return el.attribs?.[attrName] === attrValue\n }\n }\n\n // Check for class selector .class\n if (selector.startsWith('.')) {\n const className = selector.slice(1)\n const classes = el.attribs?.class?.split(/\\s+/) || []\n return classes.includes(className)\n }\n\n // Check for id selector #id\n if (selector.startsWith('#')) {\n const id = selector.slice(1)\n return el.attribs?.id === id\n }\n\n // Check for tag selector (possibly with attribute)\n // Split tag from attribute if present, e.g., \"div[role=alert]\"\n const tagAttrMatch = selector.match(/^([a-z][a-z0-9]*)\\[([^\\]]+)\\]$/i)\n if (tagAttrMatch) {\n const [, tagName, attrPart] = tagAttrMatch\n if (el.name !== tagName) return false\n\n // Parse attribute part: could be \"attr\" or \"attr=value\"\n const attrEqMatch = attrPart.match(/^([^=]+)(?:=(.*))?$/)\n if (attrEqMatch) {\n const [, attrName, attrValue] = attrEqMatch\n if (attrValue === undefined) {\n return attrName in el.attribs\n } else {\n return el.attribs?.[attrName] === attrValue\n }\n }\n return false\n }\n\n // Simple tag selector\n return el.name === selector\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,qBAAgF;CACpF,OAAO;EACL,aAAa;EACb,aAAa;EACb,MAAM;CACR;CACA,KAAK,EACH,KAAK,GACP;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,cAAc,MAAc,SAA2B,CAAC,GAAW;CACjF,OAAO,UAAU,iBAAiB,MAAM,IAAI,GAAG,MAAM,CAAC;AACxD;;;;;;AAOA,SAAgB,iBAAiB,KAAkB,SAA2B,CAAC,GAAgB;CAC7F,MAAM,YAAY,OAAO;CAGzB,IAAI,cAAc,OAChB,OAAO;CAKT,MAAM,kBAAkBA,OADD,OAAO,cAAc,WAAW,YAAY,CAAC,GACtB,kBAAkB;CAGhE,KAAK,MAAM,CAAC,iBAAiB,eAAe,OAAO,QAAQ,eAAe,GAAG;EAE3E,IAAI,eAAe,OAAO;EAE1B,MAAM,YAAY,gBAAgB,MAAM,GAAG,CAAC,CAAC,KAAI,MAAK,EAAE,KAAK,CAAC;EAE9D,KAAK,MAAM,SAAS;GAClB,MAAM,KAAK;GACX,IAAI,CAAC,GAAG,MAAM;GAKd,IAFgB,UAAU,MAAK,aAAY,eAAe,IAAI,QAAQ,CAE5D,GAAG;IAEX,IAAI,CAAC,GAAG,SACN,GAAG,UAAU,CAAC;IAGhB,KAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,UAAU,GAAG;KAE9D,IAAI,cAAc,OAAO;KAEzB,IAAI,aAAa,WAAW,GAAG,QAAQ,OAAO;MAC5C,MAAM,kBAAkB,GAAG,QAAQ,MAAM,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;MACpE,MAAM,aAAa,OAAO,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;MAChE,MAAM,gBAAgB,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,UAAU,CAAC,CAAC;MACtE,IAAI,cAAc,KAAK,GAAG,MAAM,GAAG,QAAQ,OACzC,GAAG,QAAQ,QAAQ,cAAc,KAAK,GAAG;KAE7C,OAEE,IAAI,EAAE,YAAY,GAAG,UACnB,GAAG,QAAQ,YAAY,OAAO,SAAS;IAG7C;GACF;EACF,CAAC;CACH;CAEA,OAAO;AACT;;;;;AAMA,SAAS,eAAe,IAAa,UAA2B;CAE9D,WAAW,SAAS,KAAK;CAGzB,MAAM,YAAY,SAAS,MAAM,+BAA+B;CAChE,IAAI,WAAW;EACb,MAAM,GAAG,UAAU,aAAa;EAChC,IAAI,cAAc,KAAA,GAEhB,OAAO,YAAY,GAAG;OAGtB,OAAO,GAAG,UAAU,cAAc;CAEtC;CAGA,IAAI,SAAS,WAAW,GAAG,GAAG;EAC5B,MAAM,YAAY,SAAS,MAAM,CAAC;EAElC,QADgB,GAAG,SAAS,OAAO,MAAM,KAAK,KAAK,CAAC,EAAA,CACrC,SAAS,SAAS;CACnC;CAGA,IAAI,SAAS,WAAW,GAAG,GAAG;EAC5B,MAAM,KAAK,SAAS,MAAM,CAAC;EAC3B,OAAO,GAAG,SAAS,OAAO;CAC5B;CAIA,MAAM,eAAe,SAAS,MAAM,iCAAiC;CACrE,IAAI,cAAc;EAChB,MAAM,GAAG,SAAS,YAAY;EAC9B,IAAI,GAAG,SAAS,SAAS,OAAO;EAGhC,MAAM,cAAc,SAAS,MAAM,qBAAqB;EACxD,IAAI,aAAa;GACf,MAAM,GAAG,UAAU,aAAa;GAChC,IAAI,cAAc,KAAA,GAChB,OAAO,YAAY,GAAG;QAEtB,OAAO,GAAG,UAAU,cAAc;EAEtC;EACA,OAAO;CACT;CAGA,OAAO,GAAG,SAAS;AACrB"}