@webto-id/variant-check 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1198,7 +1198,7 @@ function lintAttr(name, v, tag, ctx) {
1198
1198
  const s = staticValue(v);
1199
1199
  if (s !== null) {
1200
1200
  if (/url\s*\(|expression\s*\(|@import|behavior\s*:/i.test(s)) push(ctx, "error", "style-url", `inline style on <${tag}> must not use url()/expression()`);
1201
- if (/position\s*:\s*fixed/i.test(s) && ctx.sectionType !== "navbar" && ctx.sectionType !== "banner") push(ctx, "error", "fixed", `position: fixed is only allowed in navbar/banner variants`);
1201
+ if (/position\s*:\s*fixed/i.test(s)) push(ctx, "error", "fixed", `position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow`);
1202
1202
  if (/#[0-9a-f]{3,8}\b/i.test(s) || /\b(rgb|hsl)a?\(/i.test(s)) push(ctx, "warning", "hardcoded-color", `inline style on <${tag}> hardcodes a color \u2014 use var(--color-*)`);
1203
1203
  if (/border-radius\s*:\s*\d/i.test(s)) push(ctx, "warning", "hardcoded-radius", `inline style on <${tag}> hardcodes border-radius \u2014 use var(--radius)`);
1204
1204
  if (/font-family\s*:(?![^;]*var\(--font-)/i.test(s)) push(ctx, "warning", "hardcoded-font", `inline style on <${tag}> hardcodes font-family \u2014 use var(--font-heading|--font-body)`);
@@ -1225,7 +1225,7 @@ __name(lintHtml, "lintHtml");
1225
1225
 
1226
1226
  // ../variant-compiler/src/lint/css.ts
1227
1227
  var ALLOWED_URL_HOSTS = ["images.unsplash.com", "images.pexels.com"];
1228
- function lintCss(css, sectionType2, attrs = {}) {
1228
+ function lintCss(css, _sectionType, attrs = {}) {
1229
1229
  const out = [];
1230
1230
  const err = /* @__PURE__ */ __name((code, message) => out.push({ level: "error", code, message }), "err");
1231
1231
  const warn = /* @__PURE__ */ __name((code, message) => out.push({ level: "warning", code, message }), "warn");
@@ -1238,7 +1238,7 @@ function lintCss(css, sectionType2, attrs = {}) {
1238
1238
  if (/@font-face\b/i.test(stripped)) err("css-font-face", "@font-face is not allowed \u2014 fonts come from the site theme (var(--font-heading|--font-body))");
1239
1239
  if (/expression\s*\(/i.test(stripped)) err("css-expression", "expression() is not allowed");
1240
1240
  if (/behavior\s*:|-moz-binding\s*:/i.test(stripped)) err("css-behavior", "behavior/-moz-binding are not allowed");
1241
- if (/\bposition\s*:\s*fixed\b/i.test(stripped) && sectionType2 !== "navbar" && sectionType2 !== "banner") err("fixed", "position: fixed is only allowed in navbar/banner variants");
1241
+ if (/\bposition\s*:\s*fixed\b/i.test(stripped)) err("fixed", "position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow");
1242
1242
  for (const m of stripped.matchAll(/url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi)) {
1243
1243
  const u = m[2].trim();
1244
1244
  if (/^data:image\/svg\+xml/i.test(u)) continue;
@@ -1739,6 +1739,19 @@ async function compileTailwind(candidates) {
1739
1739
  __name(compileTailwind, "compileTailwind");
1740
1740
 
1741
1741
  // ../variant-compiler/src/schema.ts
1742
+ var CHROME_CONTEXT_PROPS = /* @__PURE__ */ new Set([
1743
+ "pages",
1744
+ "currentSlug",
1745
+ "linkPrefix",
1746
+ "colorMode",
1747
+ "footerPages",
1748
+ "imageCredits"
1749
+ ]);
1750
+ var CHROME_SECTION_TYPES = /* @__PURE__ */ new Set(["navbar", "banner", "footer"]);
1751
+ var NEVER_HIDDEN = {
1752
+ products: ["source"],
1753
+ blog: ["source"]
1754
+ };
1742
1755
  function typeOf(s) {
1743
1756
  if (!s) return "unknown";
1744
1757
  const t = s.type;
@@ -1751,13 +1764,16 @@ function typeOf(s) {
1751
1764
  return "unknown";
1752
1765
  }
1753
1766
  __name(typeOf, "typeOf");
1754
- function deriveSchema(fields, props, base2) {
1767
+ function deriveSchema(fields, props, base2, sectionType2) {
1755
1768
  const lint = [];
1769
+ const isChrome = sectionType2 !== void 0 && CHROME_SECTION_TYPES.has(sectionType2);
1770
+ const isContextProp = /* @__PURE__ */ __name((key) => isChrome && CHROME_CONTEXT_PROPS.has(key), "isContextProp");
1756
1771
  const readKeys = new Set(props.map((p) => p.key));
1757
1772
  const declared = new Map(fields.map((f) => [f.key, f]));
1758
1773
  const extProps = {};
1759
1774
  const usedFields = [];
1760
1775
  for (const f of fields) {
1776
+ if (isContextProp(f.key)) continue;
1761
1777
  if (!f.optional) lint.push({ level: "warning", code: "props-optional", message: `prop '${f.key}' should be optional (\`${f.key}?:\`) \u2014 content may omit it` });
1762
1778
  const baseField = base2?.properties[f.key];
1763
1779
  if (baseField) {
@@ -1770,11 +1786,13 @@ function deriveSchema(fields, props, base2) {
1770
1786
  extProps[f.key] = { ...f.schema, ...f.description ? { description: f.description } : {} };
1771
1787
  }
1772
1788
  for (const k of readKeys) {
1789
+ if (isContextProp(k)) continue;
1773
1790
  if (!declared.has(k)) lint.push({ level: "warning", code: "props-undeclared", message: `'${k}' is read from Astro.props but not declared in interface Props` });
1774
1791
  else if (!base2?.properties[k] && !(k in extProps)) extProps[k] = { type: "string" };
1775
1792
  if (base2?.properties[k] && !usedFields.includes(k)) usedFields.push(k);
1776
1793
  }
1777
- const hiddenFields = base2 ? Object.keys(base2.properties).filter((k) => !usedFields.includes(k)) : [];
1794
+ const keepVisible = new Set(sectionType2 !== void 0 ? NEVER_HIDDEN[sectionType2] ?? [] : []);
1795
+ const hiddenFields = base2 ? Object.keys(base2.properties).filter((k) => !usedFields.includes(k) && !keepVisible.has(k)) : [];
1778
1796
  const extension = { type: "object", properties: extProps, additionalProperties: true };
1779
1797
  return { extension, hiddenFields, usedFields, lint };
1780
1798
  }
@@ -1878,6 +1896,25 @@ async function compile2(source2, opts) {
1878
1896
  lint.push({ level: "error", code: "tailwind", message: `Tailwind compile failed: ${e.message}` });
1879
1897
  }
1880
1898
  }
1899
+ if (!opts.skipTailwind && twCss) {
1900
+ const unknown = [];
1901
+ for (const t of classTokens) {
1902
+ if (!isCandidate(t)) continue;
1903
+ if (/^(group|peer)([/]|$)/.test(t)) continue;
1904
+ if (/^(js|wv)-/.test(t)) continue;
1905
+ if (userCss.includes(t) || script.includes(t)) continue;
1906
+ const cssName = "." + t.replace(/[^a-zA-Z0-9_-]/g, (ch) => "\\" + ch);
1907
+ const boundary = new RegExp(cssName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?![\\w-])");
1908
+ if (!boundary.test(twCss)) unknown.push(t);
1909
+ }
1910
+ if (unknown.length) {
1911
+ lint.push({
1912
+ level: "warning",
1913
+ code: "unknown-class",
1914
+ message: `class${unknown.length > 1 ? "es" : ""} '${unknown.join("', '")}' compile to no CSS: not a Tailwind utility, and neither this variant's <style> nor its <script> mentions ${unknown.length > 1 ? "them" : "it"}. Leftover from another CSS framework? Rewrite as Tailwind utilities/tokens or style it in <style>; a deliberate script hook belongs under a js- prefix`
1915
+ });
1916
+ }
1917
+ }
1881
1918
  let css = "";
1882
1919
  try {
1883
1920
  css = scopeCss(twCss, ROOT_PLACEHOLDER) + (userCss.trim() ? "\n" + scopeCss(userCss, ROOT_PLACEHOLDER) : "");
@@ -1885,7 +1922,7 @@ async function compile2(source2, opts) {
1885
1922
  lint.push({ level: "error", code: "css-parse", message: `could not scope CSS: ${e.message}` });
1886
1923
  }
1887
1924
  if (css.length > CSS_MAX_BYTES) lint.push({ level: "error", code: "css-size", message: `compiled CSS exceeds ${CSS_MAX_BYTES / 1024} KB` });
1888
- const sch = deriveSchema(front.fields, front.props, opts.base ?? null);
1925
+ const sch = deriveSchema(front.fields, front.props, opts.base ?? null, opts.sectionType);
1889
1926
  lint.push(...sch.lint);
1890
1927
  if (opts.strict) {
1891
1928
  for (const m of lint) if (m.level === "warning" && STRICT_CODES.has(m.code)) m.level = "error";
@@ -2451,7 +2488,12 @@ function renderVariant(ir, content, ctx, opts = {}) {
2451
2488
  }
2452
2489
  for (const c of ir.consts) scope[c.name] = evalExpr(c.e, scope, budget);
2453
2490
  const rctx = { ...ctx, marks: { addImageButton: false } };
2454
- const html = withAddImageFallback(renderNodes(ir.root, scope, rctx, budget), content, rctx);
2491
+ const isDbMode = (ir.sectionType === "products" || ir.sectionType === "blog") && content.source === "database";
2492
+ let rendered = renderNodes(ir.root, scope, rctx, budget);
2493
+ if (isDbMode) {
2494
+ rendered = rendered.replace(/<div class="webto-add-row[^"]*">\s*<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>\s*<\/div>/g, "").replace(/<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>/g, "").replace(/\s(?:data-edit-(?:field|image|add-image))="(?:products|posts)\.[^"]*"/g, "");
2495
+ }
2496
+ const html = withAddImageFallback(rendered, content, rctx);
2455
2497
  const css = ir.css ? layerize(ir.css.replace(/__WV_ROOT__/g, `[data-wv-inst="${uid}"]`)) : "";
2456
2498
  const script = ir.script ? wrapScript(ir.script, uid) : "";
2457
2499
  return { html, css, script, uid };
@@ -3710,6 +3752,114 @@ var base_schemas_default = {
3710
3752
  type: "string"
3711
3753
  }
3712
3754
  }
3755
+ },
3756
+ navbar: {
3757
+ properties: {
3758
+ siteName: {
3759
+ description: "Nama yang ditampilkan di navigasi",
3760
+ type: "string"
3761
+ },
3762
+ siteNameSize: {
3763
+ default: 18,
3764
+ description: "Ukuran font nama (px)",
3765
+ type: "number",
3766
+ minimum: 12,
3767
+ maximum: 48
3768
+ },
3769
+ logoUrl: {
3770
+ description: "URL logo (image or vectorized SVG)",
3771
+ type: "string"
3772
+ },
3773
+ logoUrlDark: {
3774
+ description: "Optional logo shown when the site is in dark mode \u2014 falls back to the main logo",
3775
+ type: "string"
3776
+ },
3777
+ showSiteNameWithLogo: {
3778
+ description: "Also show the site name text when a logo image is set",
3779
+ type: "boolean"
3780
+ },
3781
+ ctaText: {
3782
+ description: "Teks tombol CTA",
3783
+ type: "string"
3784
+ },
3785
+ ctaUrl: {
3786
+ description: "URL tombol CTA",
3787
+ type: "string"
3788
+ },
3789
+ links: {
3790
+ description: "Custom navbar links (shown alongside page links)",
3791
+ type: "array",
3792
+ items: {
3793
+ type: "object",
3794
+ properties: {
3795
+ label: {
3796
+ type: "string",
3797
+ description: "Link text"
3798
+ },
3799
+ url: {
3800
+ type: "string",
3801
+ description: "Link URL (e.g. /#pricing, /about, https://...)"
3802
+ }
3803
+ },
3804
+ required: [
3805
+ "label",
3806
+ "url"
3807
+ ],
3808
+ additionalProperties: false
3809
+ }
3810
+ }
3811
+ }
3812
+ },
3813
+ banner: {
3814
+ properties: {
3815
+ text: {
3816
+ type: "string",
3817
+ description: "Banner announcement text"
3818
+ },
3819
+ ctaText: {
3820
+ description: "Call-to-action button text",
3821
+ type: "string"
3822
+ },
3823
+ ctaUrl: {
3824
+ description: "Call-to-action button URL",
3825
+ type: "string"
3826
+ },
3827
+ icon: {
3828
+ description: "Icon emoji or name",
3829
+ type: "string"
3830
+ }
3831
+ }
3832
+ },
3833
+ footer: {
3834
+ properties: {
3835
+ text: {
3836
+ default: "\xA9 2025 All rights reserved.",
3837
+ description: "Footer copyright text",
3838
+ type: "string"
3839
+ },
3840
+ links: {
3841
+ description: "Footer navigation links",
3842
+ type: "array",
3843
+ items: {
3844
+ type: "object",
3845
+ properties: {
3846
+ label: {
3847
+ type: "string",
3848
+ description: "Link text"
3849
+ },
3850
+ url: {
3851
+ type: "string",
3852
+ description: "Link URL"
3853
+ }
3854
+ },
3855
+ required: [
3856
+ "label",
3857
+ "url"
3858
+ ],
3859
+ additionalProperties: false
3860
+ }
3861
+ }
3862
+ }
3713
3863
  }
3714
3864
  };
3715
3865
 
package/dist/index.js CHANGED
@@ -1195,7 +1195,7 @@ function lintAttr(name, v, tag, ctx) {
1195
1195
  const s = staticValue(v);
1196
1196
  if (s !== null) {
1197
1197
  if (/url\s*\(|expression\s*\(|@import|behavior\s*:/i.test(s)) push(ctx, "error", "style-url", `inline style on <${tag}> must not use url()/expression()`);
1198
- if (/position\s*:\s*fixed/i.test(s) && ctx.sectionType !== "navbar" && ctx.sectionType !== "banner") push(ctx, "error", "fixed", `position: fixed is only allowed in navbar/banner variants`);
1198
+ if (/position\s*:\s*fixed/i.test(s)) push(ctx, "error", "fixed", `position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow`);
1199
1199
  if (/#[0-9a-f]{3,8}\b/i.test(s) || /\b(rgb|hsl)a?\(/i.test(s)) push(ctx, "warning", "hardcoded-color", `inline style on <${tag}> hardcodes a color \u2014 use var(--color-*)`);
1200
1200
  if (/border-radius\s*:\s*\d/i.test(s)) push(ctx, "warning", "hardcoded-radius", `inline style on <${tag}> hardcodes border-radius \u2014 use var(--radius)`);
1201
1201
  if (/font-family\s*:(?![^;]*var\(--font-)/i.test(s)) push(ctx, "warning", "hardcoded-font", `inline style on <${tag}> hardcodes font-family \u2014 use var(--font-heading|--font-body)`);
@@ -1222,7 +1222,7 @@ __name(lintHtml, "lintHtml");
1222
1222
 
1223
1223
  // ../variant-compiler/src/lint/css.ts
1224
1224
  var ALLOWED_URL_HOSTS = ["images.unsplash.com", "images.pexels.com"];
1225
- function lintCss(css, sectionType, attrs = {}) {
1225
+ function lintCss(css, _sectionType, attrs = {}) {
1226
1226
  const out = [];
1227
1227
  const err = /* @__PURE__ */ __name((code, message) => out.push({ level: "error", code, message }), "err");
1228
1228
  const warn = /* @__PURE__ */ __name((code, message) => out.push({ level: "warning", code, message }), "warn");
@@ -1235,7 +1235,7 @@ function lintCss(css, sectionType, attrs = {}) {
1235
1235
  if (/@font-face\b/i.test(stripped)) err("css-font-face", "@font-face is not allowed \u2014 fonts come from the site theme (var(--font-heading|--font-body))");
1236
1236
  if (/expression\s*\(/i.test(stripped)) err("css-expression", "expression() is not allowed");
1237
1237
  if (/behavior\s*:|-moz-binding\s*:/i.test(stripped)) err("css-behavior", "behavior/-moz-binding are not allowed");
1238
- if (/\bposition\s*:\s*fixed\b/i.test(stripped) && sectionType !== "navbar" && sectionType !== "banner") err("fixed", "position: fixed is only allowed in navbar/banner variants");
1238
+ if (/\bposition\s*:\s*fixed\b/i.test(stripped)) err("fixed", "position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow");
1239
1239
  for (const m of stripped.matchAll(/url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi)) {
1240
1240
  const u = m[2].trim();
1241
1241
  if (/^data:image\/svg\+xml/i.test(u)) continue;
@@ -1736,6 +1736,19 @@ async function compileTailwind(candidates) {
1736
1736
  __name(compileTailwind, "compileTailwind");
1737
1737
 
1738
1738
  // ../variant-compiler/src/schema.ts
1739
+ var CHROME_CONTEXT_PROPS = /* @__PURE__ */ new Set([
1740
+ "pages",
1741
+ "currentSlug",
1742
+ "linkPrefix",
1743
+ "colorMode",
1744
+ "footerPages",
1745
+ "imageCredits"
1746
+ ]);
1747
+ var CHROME_SECTION_TYPES = /* @__PURE__ */ new Set(["navbar", "banner", "footer"]);
1748
+ var NEVER_HIDDEN = {
1749
+ products: ["source"],
1750
+ blog: ["source"]
1751
+ };
1739
1752
  function typeOf(s) {
1740
1753
  if (!s) return "unknown";
1741
1754
  const t = s.type;
@@ -1748,13 +1761,16 @@ function typeOf(s) {
1748
1761
  return "unknown";
1749
1762
  }
1750
1763
  __name(typeOf, "typeOf");
1751
- function deriveSchema(fields, props, base) {
1764
+ function deriveSchema(fields, props, base, sectionType) {
1752
1765
  const lint = [];
1766
+ const isChrome = sectionType !== void 0 && CHROME_SECTION_TYPES.has(sectionType);
1767
+ const isContextProp = /* @__PURE__ */ __name((key) => isChrome && CHROME_CONTEXT_PROPS.has(key), "isContextProp");
1753
1768
  const readKeys = new Set(props.map((p) => p.key));
1754
1769
  const declared = new Map(fields.map((f) => [f.key, f]));
1755
1770
  const extProps = {};
1756
1771
  const usedFields = [];
1757
1772
  for (const f of fields) {
1773
+ if (isContextProp(f.key)) continue;
1758
1774
  if (!f.optional) lint.push({ level: "warning", code: "props-optional", message: `prop '${f.key}' should be optional (\`${f.key}?:\`) \u2014 content may omit it` });
1759
1775
  const baseField = base?.properties[f.key];
1760
1776
  if (baseField) {
@@ -1767,11 +1783,13 @@ function deriveSchema(fields, props, base) {
1767
1783
  extProps[f.key] = { ...f.schema, ...f.description ? { description: f.description } : {} };
1768
1784
  }
1769
1785
  for (const k of readKeys) {
1786
+ if (isContextProp(k)) continue;
1770
1787
  if (!declared.has(k)) lint.push({ level: "warning", code: "props-undeclared", message: `'${k}' is read from Astro.props but not declared in interface Props` });
1771
1788
  else if (!base?.properties[k] && !(k in extProps)) extProps[k] = { type: "string" };
1772
1789
  if (base?.properties[k] && !usedFields.includes(k)) usedFields.push(k);
1773
1790
  }
1774
- const hiddenFields = base ? Object.keys(base.properties).filter((k) => !usedFields.includes(k)) : [];
1791
+ const keepVisible = new Set(sectionType !== void 0 ? NEVER_HIDDEN[sectionType] ?? [] : []);
1792
+ const hiddenFields = base ? Object.keys(base.properties).filter((k) => !usedFields.includes(k) && !keepVisible.has(k)) : [];
1775
1793
  const extension = { type: "object", properties: extProps, additionalProperties: true };
1776
1794
  return { extension, hiddenFields, usedFields, lint };
1777
1795
  }
@@ -1875,6 +1893,25 @@ async function compile2(source, opts) {
1875
1893
  lint.push({ level: "error", code: "tailwind", message: `Tailwind compile failed: ${e.message}` });
1876
1894
  }
1877
1895
  }
1896
+ if (!opts.skipTailwind && twCss) {
1897
+ const unknown = [];
1898
+ for (const t of classTokens) {
1899
+ if (!isCandidate(t)) continue;
1900
+ if (/^(group|peer)([/]|$)/.test(t)) continue;
1901
+ if (/^(js|wv)-/.test(t)) continue;
1902
+ if (userCss.includes(t) || script.includes(t)) continue;
1903
+ const cssName = "." + t.replace(/[^a-zA-Z0-9_-]/g, (ch) => "\\" + ch);
1904
+ const boundary = new RegExp(cssName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?![\\w-])");
1905
+ if (!boundary.test(twCss)) unknown.push(t);
1906
+ }
1907
+ if (unknown.length) {
1908
+ lint.push({
1909
+ level: "warning",
1910
+ code: "unknown-class",
1911
+ message: `class${unknown.length > 1 ? "es" : ""} '${unknown.join("', '")}' compile to no CSS: not a Tailwind utility, and neither this variant's <style> nor its <script> mentions ${unknown.length > 1 ? "them" : "it"}. Leftover from another CSS framework? Rewrite as Tailwind utilities/tokens or style it in <style>; a deliberate script hook belongs under a js- prefix`
1912
+ });
1913
+ }
1914
+ }
1878
1915
  let css = "";
1879
1916
  try {
1880
1917
  css = scopeCss(twCss, ROOT_PLACEHOLDER) + (userCss.trim() ? "\n" + scopeCss(userCss, ROOT_PLACEHOLDER) : "");
@@ -1882,7 +1919,7 @@ async function compile2(source, opts) {
1882
1919
  lint.push({ level: "error", code: "css-parse", message: `could not scope CSS: ${e.message}` });
1883
1920
  }
1884
1921
  if (css.length > CSS_MAX_BYTES) lint.push({ level: "error", code: "css-size", message: `compiled CSS exceeds ${CSS_MAX_BYTES / 1024} KB` });
1885
- const sch = deriveSchema(front.fields, front.props, opts.base ?? null);
1922
+ const sch = deriveSchema(front.fields, front.props, opts.base ?? null, opts.sectionType);
1886
1923
  lint.push(...sch.lint);
1887
1924
  if (opts.strict) {
1888
1925
  for (const m of lint) if (m.level === "warning" && STRICT_CODES.has(m.code)) m.level = "error";
@@ -2448,7 +2485,12 @@ function renderVariant(ir, content, ctx, opts = {}) {
2448
2485
  }
2449
2486
  for (const c of ir.consts) scope[c.name] = evalExpr(c.e, scope, budget);
2450
2487
  const rctx = { ...ctx, marks: { addImageButton: false } };
2451
- const html = withAddImageFallback(renderNodes(ir.root, scope, rctx, budget), content, rctx);
2488
+ const isDbMode = (ir.sectionType === "products" || ir.sectionType === "blog") && content.source === "database";
2489
+ let rendered = renderNodes(ir.root, scope, rctx, budget);
2490
+ if (isDbMode) {
2491
+ rendered = rendered.replace(/<div class="webto-add-row[^"]*">\s*<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>\s*<\/div>/g, "").replace(/<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>/g, "").replace(/\s(?:data-edit-(?:field|image|add-image))="(?:products|posts)\.[^"]*"/g, "");
2492
+ }
2493
+ const html = withAddImageFallback(rendered, content, rctx);
2452
2494
  const css = ir.css ? layerize(ir.css.replace(/__WV_ROOT__/g, `[data-wv-inst="${uid}"]`)) : "";
2453
2495
  const script = ir.script ? wrapScript(ir.script, uid) : "";
2454
2496
  return { html, css, script, uid };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webto-id/variant-check",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Lint, compile and preview a Webto Variant Format (.astro subset) section variant with the exact compiler the webto.id marketplace uses.",
5
5
  "license": "MIT",
6
6
  "type": "module",