efficient-web-builder 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/components/BlogPostSource.js +1 -1
- package/dist/components/Breadcrumb.js +1 -1
- package/dist/components/Btn.js +1 -1
- package/dist/components/CardShadowField.js +1 -1
- package/dist/components/CategoryBar.js +1 -1
- package/dist/components/ChartSection.js +1 -1
- package/dist/components/ChatSettingsPanel.js +1 -1
- package/dist/components/ClearColoursDialog.js +1 -1
- package/dist/components/ContactLocations.js +5 -5
- package/dist/components/CornersField.js +1 -1
- package/dist/components/DocBody.js +1 -1
- package/dist/components/DuplicateToPageMenu.js +1 -1
- package/dist/components/FeaturedCover.js +1 -1
- package/dist/components/FieldGlyph.js +1 -1
- package/dist/components/Footprint.js +1 -1
- package/dist/components/HeadingAudit.js +1 -1
- package/dist/components/HeroSection.js +1 -1
- package/dist/components/HoverEffectField.js +1 -1
- package/dist/components/ImageBlock.js +1 -1
- package/dist/components/ImageFocusRow.js +1 -1
- package/dist/components/LocationsGroupedField.js +1 -1
- package/dist/components/LongRead.js +1 -1
- package/dist/components/MediaUploadField.js +1 -1
- package/dist/components/MockupSection.js +1 -1
- package/dist/components/PagesPanel.js +1 -1
- package/dist/components/PartResetDot.js +1 -1
- package/dist/components/PartSettingsPanel.js +1 -1
- package/dist/components/PostCard.js +1 -1
- package/dist/components/ProcessSteps.js +1 -1
- package/dist/components/RMark.js +1 -1
- package/dist/components/Reel.js +1 -1
- package/dist/components/SectionEditor.js +1 -1
- package/dist/components/SectionHeader.js +1 -1
- package/dist/components/SectionThumbnail.js +1 -1
- package/dist/components/SlotChildRows.js +1 -1
- package/dist/components/SourceStatus.js +1 -1
- package/dist/components/Split.js +1 -1
- package/dist/components/StandaloneSectionEditor.js +1 -1
- package/dist/components/StylesPanel.js +1 -1
- package/dist/components/Testimonials.js +1 -1
- package/dist/components/TextStylesField.js +1 -1
- package/dist/components/ThemeCard.js +1 -1
- package/dist/components/ThemeColourField.js +1 -1
- package/dist/components/ThemeField.js +1 -1
- package/dist/components/TickerSettingsPanel.js +1 -1
- package/dist/components/Timeline.js +1 -1
- package/dist/components/TimelineTrack.js +1 -1
- package/dist/components/TitleWithButtons.js +1 -1
- package/dist/components/WebsiteScreenParts.js +1 -1
- package/dist/components/WebsiteSettingsPanel.js +1 -1
- package/dist/components/goldPhrases.js +1 -1
- package/dist/components/storefront-editor.css +25 -212
- package/dist/lib/content/contact.js +3 -3
- package/dist/lib/content/subscribe.js +1 -1
- package/dist/lib/puck/MarkupToolbar.js +1 -1
- package/dist/lib/puck/blocks/general/CategoryBar/fields.js +1 -1
- package/dist/lib/puck/blocks/general/Chart/fields.js +1 -1
- package/dist/lib/puck/blocks/general/FAQ/render.js +1 -1
- package/dist/lib/puck/blocks/general/FeaturedCover/render.js +1 -1
- package/dist/lib/puck/blocks/general/Footprint/fields.js +1 -1
- package/dist/lib/puck/blocks/general/IntroBleed/render.js +1 -1
- package/dist/lib/puck/blocks/general/LogoBlock/fields.js +1 -1
- package/dist/lib/puck/blocks/general/LongRead/render.js +1 -1
- package/dist/lib/puck/blocks/general/Masthead/render.js +1 -1
- package/dist/lib/puck/blocks/general/Mockup/fields.js +1 -1
- package/dist/lib/puck/blocks/general/OrderSummary/render.js +1 -1
- package/dist/lib/puck/blocks/general/ParagraphsA/render.js +1 -1
- package/dist/lib/puck/blocks/general/PostCard/render.js +1 -1
- package/dist/lib/puck/blocks/general/ProcessSteps/render.js +1 -1
- package/dist/lib/puck/blocks/general/Series/render.js +1 -1
- package/dist/lib/puck/blocks/general/Split/render.js +1 -1
- package/dist/lib/puck/blocks/general/Testimonials/fields.js +1 -1
- package/dist/lib/puck/blocks/general/Testimonials/render.js +1 -1
- package/dist/lib/puck/blocks/general/TimelineTrack/render.js +1 -1
- package/dist/lib/puck/blocks/placeholders.js +1 -1
- package/dist/lib/puck/controls.js +1 -1
- package/dist/lib/puck/field-groups.js +1 -1
- package/dist/lib/puck/footer-block-row-name.js +1 -1
- package/dist/lib/puck/geocode-resolve.js +1 -1
- package/dist/lib/puck/locations-tree.js +2 -2
- package/dist/lib/puck/neutralization-contracts.js +1 -1
- package/dist/lib/puck/part-rows.js +1 -1
- package/dist/lib/puck/section-catalog.js +1 -1
- package/dist/lib/puck/section-descriptions.js +1 -1
- package/dist/lib/sections/divider-style.js +1 -1
- package/dist/lib/sections/product-card-style.js +1 -1
- package/dist/lib/sections/ticker-style.js +1 -1
- package/dist/lib/source-rules/accent-token-name.js +2 -2
- package/dist/lib/source-rules/button-corners.js +1 -1
- package/dist/lib/source-rules/editor-chrome-note.js +2 -2
- package/dist/lib/source-rules/editor-context-menu.js +2 -2
- package/dist/lib/source-rules/editor-puck-drag-off.js +2 -2
- package/dist/lib/source-rules/efficient-easel-version.js +1 -1
- package/dist/lib/source-rules/every-button-follows-the-button-colour.js +4 -4
- package/dist/lib/source-rules/flat-theme-palette.js +2 -2
- package/dist/lib/source-rules/full-width-means-full.js +1 -1
- package/dist/lib/source-rules/heading-ceiling-sweep.js +3 -3
- package/dist/lib/source-rules/label-hints.js +4 -4
- package/dist/lib/source-rules/label-vocabulary.js +4 -4
- package/dist/lib/source-rules/mirrored-rows-agree.js +4 -4
- package/dist/lib/source-rules/no-comment-in-jsx.js +2 -2
- package/dist/lib/source-rules/no-glyph-rows.js +2 -2
- package/dist/lib/source-rules/one-colour-control.js +2 -2
- package/dist/lib/source-rules/one-dropdown.js +2 -2
- package/dist/lib/source-rules/one-editable-list.js +3 -3
- package/dist/lib/source-rules/one-editor-list.js +1 -1
- package/dist/lib/source-rules/one-right-click.js +2 -2
- package/dist/lib/source-rules/one-switch.js +1 -1
- package/dist/lib/source-rules/one-up-level-link.js +7 -7
- package/dist/lib/source-rules/panel-colour-tokens.js +7 -7
- package/dist/lib/source-rules/panel-no-divider-lines.js +5 -5
- package/dist/lib/source-rules/panel-one-arrow.js +1 -1
- package/dist/lib/source-rules/panel-one-hover.js +2 -2
- package/dist/lib/source-rules/panel-rows-use-the-shared-row.js +5 -5
- package/dist/lib/source-rules/part-slider-fills-row.js +8 -8
- package/dist/lib/source-rules/part-style-naming.js +1 -1
- package/dist/lib/source-rules/puck-document-surfaces.js +6 -6
- package/dist/lib/source-rules/reduced-motion.js +2 -2
- package/dist/lib/source-rules/route-content-width.js +2 -2
- package/dist/lib/source-rules/rule.js +1 -1
- package/dist/lib/source-rules/section-theme-preview.js +2 -2
- package/dist/lib/source-rules/site-square-by-default.js +1 -1
- package/dist/lib/source-rules/site-width.js +2 -2
- package/dist/lib/source-rules/size-token-paints.js +2 -2
- package/dist/lib/source-rules/social-icons-import.js +1 -1
- package/dist/lib/source-rules/source-status-is-shared.js +1 -1
- package/dist/lib/source-rules/style-face-matches-size.js +3 -3
- package/dist/lib/source-rules/support/part-slider-call-sites.js +4 -4
- package/dist/lib/source-rules/support/registry-source.js +1 -1
- package/dist/lib/source-rules/theme-mode-controls-gone.js +3 -3
- package/dist/lib/source-rules/upload-intake-guard.js +2 -2
- package/dist/lib/source-rules/word-styles.js +2 -2
- package/package.json +5 -5
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import{ruleChecks as u}from"./rule.js";const o="sf-theme-page",d=new RegExp(`["\`\\s]${o}`),w=/from\s+"@\/components\/ThemeCard"/,m=/ThemeColourRow\b(?!s)/;function f(r,n){const t=r.indexOf(`
|
|
2
2
|
${n} {`);if(t<0)return"";const i=r.indexOf("{",t);return r.slice(i+1,r.indexOf(`
|
|
3
|
-
}`,i)).replace(/\/\*[\s\S]*?\*\//g,"")}function b(r,n){const{check:t,report:i}=u("section theme preview"),h=r.filter(e=>e.path.endsWith(".tsx")),c=e=>r.find(s=>s.path===e)?.text;t("the class reader finds the preview class written into markup",d.test(`<div className="${o} x" />`)),t("
|
|
3
|
+
}`,i)).replace(/\/\*[\s\S]*?\*\//g,"")}function b(r,n){const{check:t,report:i}=u("section theme preview"),h=r.filter(e=>e.path.endsWith(".tsx")),c=e=>r.find(s=>s.path===e)?.text;t("the class reader finds the preview class written into markup",d.test(`<div className="${o} x" />`)),t("…and not a longer class that only starts the same",!d.test(`<div className="x-${o}" />`)),t("a single colour row is told apart from the shared run",m.test("<ThemeColourRow label />")&&!m.test("<ThemeColourRows colors={c} />")),t("the rule-body reader cuts the comment and keeps the declaration",f(`
|
|
4
4
|
.m {
|
|
5
5
|
/* not #eff6ff */
|
|
6
6
|
background: var(--sf-accent-tint);
|
|
7
7
|
}
|
|
8
|
-
`,".m").trim()==="background: var(--sf-accent-tint);"),t(`read the .tsx files (${h.length}, at least ${n.minFiles})`,h.length>=n.minFiles,"fewer means the walk broke");const a=h.filter(e=>d.test(e.text)).map(e=>e.path);t(n.home?`\`.${o}\` is written by exactly one component, and it is ${n.home}`:`no file here writes \`.${o}\``,n.home?a.length===1&&a[0]===n.home:a.length===0,`it is written by ${a.length?a.join(", "):"nothing"}. Two files drawing one preview is the defect card 66503 closes, one level along: they match on the day they are written and drift on every day after
|
|
8
|
+
`,".m").trim()==="background: var(--sf-accent-tint);"),t(`read the .tsx files (${h.length}, at least ${n.minFiles})`,h.length>=n.minFiles,"fewer means the walk broke");const a=h.filter(e=>d.test(e.text)).map(e=>e.path);t(n.home?`\`.${o}\` is written by exactly one component, and it is ${n.home}`:`no file here writes \`.${o}\``,n.home?a.length===1&&a[0]===n.home:a.length===0,`it is written by ${a.length?a.join(", "):"nothing"}. Two files drawing one preview is the defect card 66503 closes, one level along: they match on the day they are written and drift on every day after — draw it with the builder's components/ThemeCard.tsx`);for(const e of n.panels??[]){const s=c(e)??"";t(`${e} reaches the drawing by import`,w.test(s),"it imports no shared drawing, so whatever it puts on screen is its own"),t(`${e} draws no colour row of its own`,s.length>0&&!m.test(s),"it reaches a row directly instead of drawing the shared run, which is how the two lists drifted apart the first time")}for(const e of n.triggerPanels??[])t(`${e} reaches the closed drawing by import too`,/ThemeCardTrigger/.test(c(e)??""),"a trigger assembled in the panel is a second file drawing a theme");if(n.menuSheet){const e=c(n.menuSheet)??"",s=f(e,".sf-secttheme-menu"),l=f(e,".sf-multi-menu");t(`the instrument found both rules in ${n.menuSheet}, so the absences below mean something`,/background/.test(s)&&/border/.test(l),`.sf-secttheme-menu read ${s.length} chars and .sf-multi-menu ${l.length}`),t("the theme list fills itself with the accent tint",/background:\s*var\(--sf-accent-tint\)/.test(s)),t("…as the FILL, leaving the shared grey frame alone",!/border(-color)?:/.test(s)),t("…and NO literal colour is written there",!/#[0-9a-fA-F]{3,8}\b/.test(s)&&!/\brgba?\(/.test(s),"it must be `--sf-accent-tint`"),t("the SHARED dropdown container is still untinted",!/--sf-accent-tint/.test(l),"`.sf-multi-menu` is worn by MultiSelectField too; tinting every dropdown was offered and declined")}return i()}export{b as sectionThemePreview};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{ruleChecks as f}from"./rule.js";const c=["--radius","--radius-xs","--radius-sm","--radius-md","--radius-lg","--radius-xl","--radius-2xl","--radius-3xl","--radius-4xl"],i=(a,r)=>JSON.stringify(a)===JSON.stringify(r);function h(a){const r=[];return a.text.split(`
|
|
2
|
-
`).forEach((s,u)=>{for(const o of s.matchAll(/["'`]([^"'`]*\brounded-full\b[^"'`]*)["'`]/g))/(^|\s)px-(?!0\b|0\.5\b|1\b|px\b)[\d[]/.test(o[1])&&r.push(`${a.path}:${u+1}`)}),r}function g(a,r){const{check:s,report:u}=f("site square by default"),o=e=>a.find(n=>n.path===e),t=r.corners;if(t){const e=t.siteCornerRadiusTokens(null);s("a website that has chosen no corners publishes every step of the radius scale a `rounded-*` utility reads",c.every(n=>n in e),`missing: ${c.filter(n=>!(n in e)).join(", ")}`),s("
|
|
2
|
+
`).forEach((s,u)=>{for(const o of s.matchAll(/["'`]([^"'`]*\brounded-full\b[^"'`]*)["'`]/g))/(^|\s)px-(?!0\b|0\.5\b|1\b|px\b)[\d[]/.test(o[1])&&r.push(`${a.path}:${u+1}`)}),r}function g(a,r){const{check:s,report:u}=f("site square by default"),o=e=>a.find(n=>n.path===e),t=r.corners;if(t){const e=t.siteCornerRadiusTokens(null);s("a website that has chosen no corners publishes every step of the radius scale a `rounded-*` utility reads",c.every(n=>n in e),`missing: ${c.filter(n=>!(n in e)).join(", ")}`),s("…and the card's and the button's own properties","--sf-card-radius"in e&&"--sf-button-radius"in e),s("…every one of them 0 — square",Object.values(e).every(n=>n==="0"),JSON.stringify(e)),s("…exactly what a site that chose 0 publishes",i(e,t.cornerRadiusTokens(0))),s("an absent value and a blank one are the same statement",i(t.siteCornerRadiusTokens(void 0),e)&&i(t.siteCornerRadiusTokens(""),e)&&i(t.siteCornerRadiusTokens(" "),e)),s("a website that has chosen keeps its number",i(t.siteCornerRadiusTokens(16),t.cornerRadiusTokens(16))&&t.siteCornerRadiusTokens(16)["--radius-lg"]==="1rem"),s('the retired `large` word is still 16, and `small` is still "not set"',t.cornerStyleToPx[t.normalizeCornerStyle("large")]===16&&t.cornerStyleToPx[t.normalizeCornerStyle("small")]===null),s('`cornerRadiusTokens(null)` publishes NOTHING — not set there means "follow the site"',i(t.cornerRadiusTokens(null),{})),s("…so an untouched section declares no radius of its own",t.sectionCornerStyle(null)===void 0,JSON.stringify(t.sectionCornerStyle(null)))}if(r.livePage!==void 0){const e=o(r.livePage);if(s(`read the live page's layout ${r.livePage}`,!!e)){const n=e.text;s("the live page publishes the site's corners through siteCornerRadiusTokens",/const siteCornerTokens = siteCornerRadiusTokens\(/.test(n)&&/\.\.\.siteCornerTokens,/.test(n)),s("…and never through the two that publish nothing for an untouched site",!/\bcornerRadiusTokens\(|\bcornerStyleTokens\(/.test(n))}}if(r.canvas!==void 0){const e=o(r.canvas);if(s(`read the editor canvas ${r.canvas}`,!!e)){const n=e.text;s("the editor canvas publishes them through the same function",/\.\.\.siteCornerRadiusTokens\(cornerRadiusPx\),/.test(n)&&/siteCardRadiusTokens\(siteCornerRadiusTokens\(cornerRadiusPx\)\)/.test(n)),s("…and nowhere through the bare one",!/(?<![A-Za-z])cornerRadiusTokens\(/.test(n))}}{const e=h({path:"fixture.tsx",text:['<button className="rounded-full px-4 py-2">Sign in</button>','<span className="rounded-full px-1 text-xs">3</span>','<span className="h-2 w-2 rounded-full bg-fg" />',"<i className={`rounded-full px-[10px]`} />"].join(`
|
|
3
3
|
`)});s("the pill sweep flags a padded circle, and neither a count badge nor a dot",i(e,["fixture.tsx:1","fixture.tsx:4"]),JSON.stringify(e))}const l=a.filter(e=>e.path.endsWith(".tsx"));s(`the pill sweep read the .tsx files (${l.length}, at least ${r.minFiles})`,l.length>=r.minFiles&&l.length>0);const d=l.flatMap(h);return s("no control with horizontal padding wears the circle utility",d.length===0,d.join(", ")),u()}export{g as siteSquareByDefault};
|
|
@@ -2,6 +2,6 @@ import{CHROME_WIDTH_CAP_CLASS as S,PAGE_WIDTH as f,PRE_CHROME_WIDTH_CAP_CLASS as
|
|
|
2
2
|
`).forEach((r,h)=>{i.test(r)&&!T(r)&&t.push({file:o.path,line:h+1,text:r})});return t}function u(a,i){const t=/max-w-\[([^\]]+)\]/.exec(a);if(!t)return null;const o=/^var\((--[\w-]+),\s*([^)]+)\)$/.exec(t[1].trim());return o?i[o[1]]??o[2]:t[1].trim()}function E(a,i){const{check:t,report:o}=C("site width"),r=e=>a.find(n=>n.path===e);{const e=["max-w-[",f.default,"px]"].join(""),n={path:"fixture.tsx",text:[`const a = "mx-auto ${e}";`,`// a note naming ${e} is not a use`,`const b = "${d} px-10";`,`const c = "max-w-[var(${g},1400px)]";`].join(`
|
|
3
3
|
`)},s=m([n],new RegExp(y(_)));t("R1 flags a bare 1600 cap, and not a comment naming one",s.length===1&&s[0].line===1,JSON.stringify(s));const l=m([n],new RegExp(`max-w-\\[var\\(${g}`));t("R2 finds every cap that reads the token, and only the drifted one differs",l.length===2&&l.filter(c=>!c.text.includes(d)).map(c=>c.line).join()==="4",JSON.stringify(l)),t("the cascade reader takes the fallback unset and the published width when set",u(d,p(void 0))===`${f.default}px`&&u(d,p(1400))==="1400px")}const h=a.filter(e=>/\.tsx?$/.test(e.path));t(`read the source (${h.length} TS/TSX files, at least ${i.minFiles})`,h.length>=i.minFiles,"fewer means the walk broke, and every absence below is vacuous");const w=m(h,new RegExp(y(_)));t("R1 no page-width cap is a hand-written 1600 literal any more",w.length===0,`page width is still hard-coded in ${w.length} place(s):
|
|
4
4
|
${w.map(e=>`${e.file}:${e.line}`).join(`
|
|
5
|
-
`)}`);const $=m(h,new RegExp(`max-w-\\[var\\(${g}`));t(`found >= ${i.capFloor} caps that read the site width token (found ${$.length})`,$.length>=i.capFloor,"fewer means caps went back to a number, or the reader broke");const x=$.filter(e=>/max-w-\[var\(--sf-site-width[^\]]*\]/.exec(e.text)?.[0]!==d).map(e=>`${e.file}:${e.line}
|
|
5
|
+
`)}`);const $=m(h,new RegExp(`max-w-\\[var\\(${g}`));t(`found >= ${i.capFloor} caps that read the site width token (found ${$.length})`,$.length>=i.capFloor,"fewer means caps went back to a number, or the reader broke");const x=$.filter(e=>/max-w-\[var\(--sf-site-width[^\]]*\]/.exec(e.text)?.[0]!==d).map(e=>`${e.file}:${e.line} — ${e.text.trim()}`);t("R2 every page-width cap reads the site token, with the built-in as its fallback",x.length===0,`${x.length} cap(s) do not match SITE_WIDTH_CAP_CLASS (${d}):
|
|
6
6
|
${x.join(`
|
|
7
|
-
`)}`);for(const e of i.sections??[]){const n=r(e),s=[...(n?.text??"").matchAll(/"([^"]*max-w-\[[^\]]+\][^"]*)"/g)].map(c=>c[1]);if(!t(`${e} carries a page-width cap`,s.length>0,n?"it no longer carries one":"it is not among the files handed in"))continue;const l=s.filter(c=>u(c,p(void 0))!==`${f.default}px`||u(c,p(1400))!=="1400px");t(`${e} renders at ${f.default} unset, and at the site's width when set`,l.length===0,l.join("
|
|
7
|
+
`)}`);for(const e of i.sections??[]){const n=r(e),s=[...(n?.text??"").matchAll(/"([^"]*max-w-\[[^\]]+\][^"]*)"/g)].map(c=>c[1]);if(!t(`${e} carries a page-width cap`,s.length>0,n?"it no longer carries one":"it is not among the files handed in"))continue;const l=s.filter(c=>u(c,p(void 0))!==`${f.default}px`||u(c,p(1400))!=="1400px");t(`${e} renders at ${f.default} unset, and at the site's width when set`,l.length===0,l.join(" · "))}for(const e of i.tokenSheets??[]){const n=/--sf-site-width:\s*([^;]+);/.exec(r(e)?.text??"");t(`${e} defines the token at the built-in width`,n?.[1].trim()===`${f.default}px`,n?`it defines ${n[1].trim()}`:`${e} defines no --sf-site-width (or was not read)`)}for(const e of i.chromeStyled??[]){const n=r(e)?.text??"";t(`${e} caps at the site's width (chromeWidthStyle)`,/chromeWidthStyle\(/.test(n),`${e} does not cap at the site's width`),t(`${e} does not cap at the built-in number (pageWidthStyle)`,n.length>0&&!/^(?!.*\/\/).*\bpageWidthStyle\(/m.test(n),`${e} still caps at the built-in number, so the chrome ignores the site's width`)}for(const e of i.chromeClassed??[]){const n=r(e)?.text??"";t(`${e} carries ${S}`,n.includes(S),"its tabs sit one gutter wide of the page"),t(`${e} no longer carries the old ${b}`,n.length>0&&!n.includes(b))}if(i.carrier){const e=r(i.carrier)?.text??"",n=/const carrierStyle = \{([\s\S]*?)\} as CSSProperties;/.exec(e),s=/const contentThemeStyle = \{([\s\S]*?)\} as CSSProperties;/.exec(e);t(`${i.carrier} builds a carrierStyle`,!!n)&&n&&t("the site width is published on the accent carrier, so the chrome can read it",/siteWidthTokens\(/.test(n[1]),"the site width is not published on the accent carrier, so the chrome cannot read it"),t(`${i.carrier} builds a contentThemeStyle`,!!s)&&s&&t("…and not on <main> as well — one emitter, not two",!/siteWidthTokens\(/.test(s[1]),"the site width is still published on <main> as well")}return o()}export{E as siteWidth};
|
|
@@ -4,8 +4,8 @@ import{ruleChecks as d}from"./rule.js";const b=/text-\[(?!length:)[^\]]*--size-[
|
|
|
4
4
|
`:" ",e++;i+=" ",e+=2;continue}if(a==="`"){for(i+=a,e++;e<l;){if(t[e]==="\\"){i+=t[e]+(t[e+1]??""),e+=2;continue}if(t[e]==="`"){i+="`",e++;break}i+=t[e],e++}continue}if(a==='"'||a==="'"){let o=e+1,r=!1;for(;o<l&&t[o]!==`
|
|
5
5
|
`;){if(t[o]==="\\"){o+=2;continue}if(t[o]===a){r=!0;break}o++}if(!r){i+=a,e++;continue}i+=t.slice(e,o+1),e=o+1;continue}i+=a,e++}return i}function f(t){return(w(t).match(b)??[]).length}function x(){const t="text-[";return[{name:"empty source",src:"",want:0},{name:"no size token at all",src:`<p className="${t}13px] font-mono">hi</p>`,want:0},{name:"the broken form",src:`<p className="${t}var(--size-label,13px)]">hi</p>`,want:1},{name:"the working form",src:`<p className="${t}length:var(--size-label,13px)]">hi</p>`,want:0},{name:"two on one line, desktop and phone",src:`<h2 className="${t}var(--size-headingMedium,44px)] max-[800px]:${t}var(--size-headingMedium,32px)]">t</h2>`,want:2},{name:"wrapped in min(), still broken",src:`<h1 className="${t}min(var(--size-headingLarge,64px),var(--sf-heading-max,9999px))]">t</h1>`,want:1},{name:"a line comment is not code",src:`// ${t}var(--size-label,13px)] is the broken form`,want:0},{name:"a block comment is not code",src:`/* ${t}var(--size-label,13px)] */`,want:0},{name:"an apostrophe does not stop a later comment being stripped",src:`<p>it doesn't paint</p>
|
|
6
6
|
// ${t}var(--size-label,13px)]
|
|
7
|
-
<p>that's why</p>`,want:0}]}function $(t,i){const{check:e,report:l}=d("size token paints"),a=i.baseline??{};let h=!0;for(const n of x()){const s=f(n.src);h=e(`fixture "${n.name}": ${n.want}`,s===n.want,`got ${s}`)&&h}if(!h)return e("the extractor is right, so its verdict on the tree means something",!1,"a fixture failed above"),l();const o=t.filter(n=>/\.tsx?$/.test(n.path));e(`read the source (${o.length} TS/TSX files, at least ${i.minFiles})`,o.length>=i.minFiles,"fewer means the walk broke, and the check below is vacuous");const r=[],p=[];let m=0;for(const n of o){const s=f(n.text);m+=s;const c=a[n.path]??0;s>c?r.push(`${n.path}
|
|
7
|
+
<p>that's why</p>`,want:0}]}function $(t,i){const{check:e,report:l}=d("size token paints"),a=i.baseline??{};let h=!0;for(const n of x()){const s=f(n.src);h=e(`fixture "${n.name}": ${n.want}`,s===n.want,`got ${s}`)&&h}if(!h)return e("the extractor is right, so its verdict on the tree means something",!1,"a fixture failed above"),l();const o=t.filter(n=>/\.tsx?$/.test(n.path));e(`read the source (${o.length} TS/TSX files, at least ${i.minFiles})`,o.length>=i.minFiles,"fewer means the walk broke, and the check below is vacuous");const r=[],p=[];let m=0;for(const n of o){const s=f(n.text);m+=s;const c=a[n.path]??0;s>c?r.push(`${n.path} — ${s} broken, baseline allows ${c}`):s<c&&p.push(`${n.path} — ${s} broken, baseline says ${c}`)}const u=Object.values(a).reduce((n,s)=>n+s,0);e(`no text size is written in the form that compiles to \`color:\` (${m} broken place(s) found, the baseline allows ${u})`,r.length===0,`${r.length} file(s) over budget:
|
|
8
8
|
${r.join(`
|
|
9
9
|
`)}
|
|
10
10
|
Write it as \`text-[length:var(--size-<role>,<built-in>)]\`. Without \`length:\` Tailwind reads the var() as a
|
|
11
|
-
colour, the declaration is dropped as invalid, and the site's text style setting reaches nothing.`);for(const[n]of Object.entries(a))e(`the baseline entry is still a file: ${n}`,o.some(s=>s.path===n),"delete the stale entry");for(const n of p)e(`improved
|
|
11
|
+
colour, the declaration is dropped as invalid, and the site's text style setting reaches nothing.`);for(const[n]of Object.entries(a))e(`the baseline entry is still a file: ${n}`,o.some(s=>s.path===n),"delete the stale entry");for(const n of p)e(`improved — lower its baseline entry: ${n}`,!0);return l()}export{f as brokenCount,$ as sizeTokenPaints,w as stripComments};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{ruleChecks as d}from"./rule.js";const a=n=>JSON.stringify(n),g="the website builder's components/socialIcons.tsx (`<SocialMark>`)",$=/\.(ts|tsx|js|jsx|mjs|cjs)$/,u=/(?:from\s*|import\s*\(\s*|require\s*\(\s*|import\s+)["'](@tabler\/[^"']+)["']/g,f=/(?:import\s*\*\s*as\s+\w+\s*from|export\s*\*\s*(?:as\s+\w+\s*)?from)\s*["']@tabler\/[^"']+["']/,x=["Instagram","Facebook","Tiktok","Youtube","X","Linkedin","Pinterest","Whatsapp"],c=n=>[...n.matchAll(u)].map(t=>t[1]);function k(n,t){const{check:r,report:b}=d("social icons import");{const e=["@tabler","icons-react"].join("/"),s=[["a named import",`import { IconBrandX } from ${a(e)};`,!1],["a dynamic import()",`const m = await import(${a(e)});`,!1],["a require()",`const t = require(${a(e)});`,!1],["a side-effect import",`import ${a(e)};`,!1],["a namespace import",`import * as Tabler from ${a(e)};`,!0],["a star re-export",`export * from ${a(e)};`,!0],["a named star re-export",`export * as T from ${a(e)};`,!0]];for(const[l,o,p]of s)r(`the reader finds ${l}`,c(o).length===1),r(
|
|
1
|
+
import{ruleChecks as d}from"./rule.js";const a=n=>JSON.stringify(n),g="the website builder's components/socialIcons.tsx (`<SocialMark>`)",$=/\.(ts|tsx|js|jsx|mjs|cjs)$/,u=/(?:from\s*|import\s*\(\s*|require\s*\(\s*|import\s+)["'](@tabler\/[^"']+)["']/g,f=/(?:import\s*\*\s*as\s+\w+\s*from|export\s*\*\s*(?:as\s+\w+\s*)?from)\s*["']@tabler\/[^"']+["']/,x=["Instagram","Facebook","Tiktok","Youtube","X","Linkedin","Pinterest","Whatsapp"],c=n=>[...n.matchAll(u)].map(t=>t[1]);function k(n,t){const{check:r,report:b}=d("social icons import");{const e=["@tabler","icons-react"].join("/"),s=[["a named import",`import { IconBrandX } from ${a(e)};`,!1],["a dynamic import()",`const m = await import(${a(e)});`,!1],["a require()",`const t = require(${a(e)});`,!1],["a side-effect import",`import ${a(e)};`,!1],["a namespace import",`import * as Tabler from ${a(e)};`,!0],["a star re-export",`export * from ${a(e)};`,!0],["a named star re-export",`export * as T from ${a(e)};`,!0]];for(const[l,o,p]of s)r(`the reader finds ${l}`,c(o).length===1),r(`…and ${p?"flags":"does not flag"} it as the whole set`,f.test(o)===p);r("a package that is not Tabler is not Tabler",c('import x from "@tablerish/x";').length===0)}const i=n.filter(e=>$.test(e.path));r(`read the source (${i.length} JS/TS files, at least ${t.minFiles})`,i.length>=t.minFiles,"fewer means the walk broke, and every check below is vacuous");const m=i.filter(e=>c(e.text).length>0&&e.path!==t.helper).map(e=>e.path);r(t.helper?`only ${t.helper} imports Tabler`:"no file here imports Tabler",m.length===0,`Tabler is imported outside its one home:
|
|
2
2
|
${m.join(`
|
|
3
3
|
`)}
|
|
4
4
|
Draw social marks through ${g} instead.`);const h=i.filter(e=>f.test(e.text)).map(e=>e.path);if(r("nothing imports the whole Tabler set",h.length===0,`a namespace or star import from Tabler ships all ~6,000 icons (552 KB gzip):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ruleChecks as d}from"./rule.js";const n=/sf-blogsource-status/,r=/from "@\/components\/SourceStatus"/;function f(m,t){const{check:s,report:i}=d("source status is shared");s("the import reader finds the shared block's import",r.test('import SourceStatus from "@/components/SourceStatus";')),s("
|
|
1
|
+
import{ruleChecks as d}from"./rule.js";const n=/sf-blogsource-status/,r=/from "@\/components\/SourceStatus"/;function f(m,t){const{check:s,report:i}=d("source status is shared");s("the import reader finds the shared block's import",r.test('import SourceStatus from "@/components/SourceStatus";')),s("…and not a module whose name only starts the same",!r.test('import X from "@/components/SourceStatusLegacy";')),s("the class reader finds a hand-drawn status line",n.test('<p className="sf-blogsource-status" data-state={s}>'));const a=m.filter(e=>e.path.endsWith(".tsx"));s(`read the .tsx files (${a.length}, at least ${t.minFiles})`,a.length>=t.minFiles,"fewer means the walk broke, and every absence below is vacuous");const h=a.filter(e=>e.path!==t.home&&n.test(e.text)).map(e=>e.path);if(s(t.home?`only ${t.home} draws the status line`:"no file here draws the status line",h.length===0,`${h.join(" · ")} — draw it with the builder's components/SourceStatus.tsx`),t.home){const e=a.find(l=>l.path===t.home);s(`…and ${t.home} really does draw it`,!!e&&n.test(e?.text??""))}const o=t.callers.filter(e=>!r.test(a.find(l=>l.path===e)?.text??""));s(`every named caller imports the shared block (${t.callers.length})`,t.callers.length>0&&o.length===0,t.callers.length===0?"no caller is named — this half measures nothing":o.join(" · "));const c=a.filter(e=>e.path!==t.home&&!t.callers.includes(e.path)&&r.test(e.text)).map(e=>e.path);return s("…and no caller is missing from the list",c.length===0,c.join(" · ")),i()}export{f as sourceStatusIsShared};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{ruleChecks as m}from"./rule.js";const u={"font-display":"headingLarge","font-heading2":"headingMedium","font-heading3":"headingSmall","font-serif":"accent","font-body":"body","font-mono":"label"};function p(i){const n=/(?<![-\w:])((?:[a-z0-9-]+(?:-\[[^\]]*\])?:)*)text-\[length:[^"'`]*?--size-(headingLarge|headingMedium|headingSmall|accent|body|label)\b/g,t=new Set;for(const o of i.matchAll(n))o[1]===""&&t.add(o[2]);return t.size===1?[...t][0]:null}function $(i){const n=/(?<![-\w])((?:[a-z0-9-]+(?:-\[[^\]]*\])?:)*)(font-display|font-heading2|font-heading3|font-serif|font-body|font-mono)(?![-\w])/g,t=new Set;for(const o of i.matchAll(n))o[1].includes("&")||t.add(o[2]);return[...t]}function f(i){const n=p(i);if(!n)return null;const t=$(i);return t.length===1?{step:n,face:t[0]}:null}const w=i=>i.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");function S(i,n){const{check:t,report:o}=m("style face matches size"),d=e=>i.find(s=>s.path===e)?.text;{const e=r=>`text-[length:var(--size-${r},44px)]`,s=r=>JSON.stringify(f(r));t("a face and a base size are read as a pair",s(`"font-display ${e("headingMedium")}"`)==='{"step":"headingMedium","face":"font-display"}'),t("
|
|
2
|
-
`).forEach((s,r)=>{const a=f(s);if(!a||(h+=1,a.step==="body"))return;const c=u[a.face];c!==a.step&&g.push(`${e.path}:${r+1}
|
|
1
|
+
import{ruleChecks as m}from"./rule.js";const u={"font-display":"headingLarge","font-heading2":"headingMedium","font-heading3":"headingSmall","font-serif":"accent","font-body":"body","font-mono":"label"};function p(i){const n=/(?<![-\w:])((?:[a-z0-9-]+(?:-\[[^\]]*\])?:)*)text-\[length:[^"'`]*?--size-(headingLarge|headingMedium|headingSmall|accent|body|label)\b/g,t=new Set;for(const o of i.matchAll(n))o[1]===""&&t.add(o[2]);return t.size===1?[...t][0]:null}function $(i){const n=/(?<![-\w])((?:[a-z0-9-]+(?:-\[[^\]]*\])?:)*)(font-display|font-heading2|font-heading3|font-serif|font-body|font-mono)(?![-\w])/g,t=new Set;for(const o of i.matchAll(n))o[1].includes("&")||t.add(o[2]);return[...t]}function f(i){const n=p(i);if(!n)return null;const t=$(i);return t.length===1?{step:n,face:t[0]}:null}const w=i=>i.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");function S(i,n){const{check:t,report:o}=m("style face matches size"),d=e=>i.find(s=>s.path===e)?.text;{const e=r=>`text-[length:var(--size-${r},44px)]`,s=r=>JSON.stringify(f(r));t("a face and a base size are read as a pair",s(`"font-display ${e("headingMedium")}"`)==='{"step":"headingMedium","face":"font-display"}'),t("…a size inside a variant is not the base",f(`"font-heading2 max-[800px]:${e("headingSmall")}"`)===null),t("…the base wins over a variant on the same line",s(`"font-heading3 ${e("headingSmall")} md:${e("headingMedium")}"`)==='{"step":"headingSmall","face":"font-heading3"}'),t("…a face behind a variant naming `&` is a descendant's, not this element's",f(`"group-[&_em]:font-serif ${e("headingLarge")}"`)===null),t("…while a bare `[&_em]:` face is still read as this element's (the reader as it shipped)",s(`"[&_em]:font-serif ${e("accent")}"`)==='{"step":"accent","face":"font-serif"}'),t("…and two faces on one line are left to a reader",f(`"font-display font-mono ${e("label")}"`)===null)}const l=i.filter(e=>/\.tsx?$/.test(e.path)&&n.drawRoots.some(s=>e.path.startsWith(s)));t(`read the drawing roots ${n.drawRoots.join(", ")} (${l.length} TS/TSX files, at least ${n.minFiles})`,l.length>=n.minFiles,"fewer means the walk broke, and the sweep below is vacuous");let h=0;const g=[];for(const e of l)e.text.split(`
|
|
2
|
+
`).forEach((s,r)=>{const a=f(s);if(!a||(h+=1,a.step==="body"))return;const c=u[a.face];c!==a.step&&g.push(`${e.path}:${r+1} — size says ${a.step}, face says ${a.face} (${c})`)});if(t(`found >= ${n.pairFloor} lines naming one face and one size (found ${h})`,h>=n.pairFloor,"fewer means the reader broke"),t("§3 no text takes its face from one style row and its size from another",g.length===0,`these lines name one style for their size and another for their face, so the merchant cannot fix the text from one row:
|
|
3
3
|
`+g.join(`
|
|
4
|
-
`)),n.themeSheet){const e=d(n.themeSheet)??"";if(t(`read the site stylesheet ${n.themeSheet}`,e.length>0)){for(const a of["2","3"])t(
|
|
4
|
+
`)),n.themeSheet){const e=d(n.themeSheet)??"";if(t(`read the site stylesheet ${n.themeSheet}`,e.length>0)){for(const a of["2","3"])t(`§1 --font-heading${a} exists and resolves --font-role-heading${a}`,new RegExp(`--font-heading${a}:\\s*var\\(--font-role-heading${a}\\)`).test(e),`without it a section cannot DRAW the face Site styles ▸ Heading ${a} sets, and that Font control moves zero pixels again`);const s=e.slice(e.indexOf("@theme inline"));for(const a of["2","3"]){const c=s.match(new RegExp(`--font-heading${a}:[^;]*;`))?.[0]??"";t(`§2 …and in @theme inline --font-heading${a} points at its OWN role, never back at Heading 1`,c.includes(`--font-role-heading${a}`),`\`--font-heading${a}\` resolves ${c.trim()||"nothing"}`)}t("§4 the Accent size is read, in CSS (`em.text-accent` sets var(--size-accent, inherit))",/em\.text-accent\s*\{[^}]*font-size:\s*var\(--size-accent,\s*inherit\)/.test(e.replace(/\/\*[\s\S]*?\*\//g,"")),"the `em.text-accent` rule is gone, so Site styles ▸ Accent ▸ Size is a dead control again"),t("§4 …and it falls back to `inherit`, never a number",!/--size-accent,\s*\d/.test(e));const r=e.slice(e.indexOf(":root"),e.indexOf("@theme inline"));for(const a of["2","3"])t(`§5 :root defaults --font-role-heading${a} to Heading 1's face, so the swap is invisible until a face is chosen`,new RegExp(`--font-role-heading${a}:\\s*var\\(--font-role-heading\\)`).test(r),"every site that has never opened that row would change typeface")}}for(const e of n.accentMarkup??[]){const s=d(e);t(`§4 ${e} does not set the accent size in the MARKUP`,s!==void 0&&!w(s).includes("--size-accent"),s===void 0?"it is not among the files handed in":"that moves 900 published strings for a size `em.text-accent` already delivers")}if(n.fontStore){const e=d(n.fontStore)??"",s=e.slice(e.indexOf("const pick ="),e.indexOf("// DERIVED from FONT_ROLES"));t(`§5 ${n.fontStore}'s normalizeFonts falls Heading 2 and Heading 3 back to Heading 1`,/role === "heading2" \|\| role === "heading3"/.test(s),"a site that chose Oswald for its headings would get two Space Grotesk ones")}return o()}export{S as styleFaceMatchesSize};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
const a="components/PartSettingsPanel.tsx";function w(t){let n="",e=0,o=null;for(;e<t.length;){const s=t[e],i=t[e+1];if(o){if(n+=s,s==="\\"){n+=i??"",e+=2;continue}s===o&&(o=null),e+=1;continue}if(s==='"'||s==="'"||s==="`"){o=s,n+=s,e+=1;continue}if(s==="/"&&i==="/"){for(;e<t.length&&t[e]!==`
|
|
2
2
|
`;)e+=1;continue}if(s==="/"&&i==="*"){for(e+=2;e<t.length&&!(t[e]==="*"&&t[e+1]==="/");)t[e]===`
|
|
3
3
|
`&&(n+=`
|
|
4
|
-
`),e+=1;e+=2;continue}n+=s,e+=1}return n}function l(t){throw new Error(t)}function S(t){const n=/const PART_FIELDS\s*=\s*\[([\s\S]*?)\]\s*as const/.exec(t);n||l(`${a}: no \`const PART_FIELDS = [
|
|
4
|
+
`),e+=1;e+=2;continue}n+=s,e+=1}return n}function l(t){throw new Error(t)}function S(t){const n=/const PART_FIELDS\s*=\s*\[([\s\S]*?)\]\s*as const/.exec(t);n||l(`${a}: no \`const PART_FIELDS = [ … ] as const\` — this reader takes the panel's row labels from that one table, and cannot find it. Has it been renamed?`);const e=new Map;for(const o of n[1].matchAll(/\{\s*key:\s*"([^"]+)"\s*,\s*label:\s*"([^"]+)"/g))e.set(o[1],o[2]);return e.size===0&&l(`${a}: PART_FIELDS holds no \`key\`/\`label\` pairs.`),e}function m(t){const n=w(t),e=S(n),o=[];for(const s of n.matchAll(/<Slider[\s/>]/g)){const i=s.index??0,d=n.indexOf("/>",i);d===-1&&l(`${a}: a <Slider at offset ${i} is not self-closing. This reader reads a call site's props up to \`/>\`; a Slider with children needs a different reader.`);const u=n.slice(i,d),c=n.slice(0,i),h=/((?:\s*case\s+"[^"]+"\s*:)+)\s*return\s*\(\s*$/.exec(c),f=h?[...h[1].matchAll(/case\s+"([^"]+)"\s*:/g)].map(r=>r[1]):[];f.length===0&&l(`${a}: the <Slider at line ${c.split(`
|
|
5
5
|
`).length} is not returned DIRECTLY by a
|
|
6
|
-
\`case "
|
|
6
|
+
\`case "…": return (\` branch, so this reader cannot say which field it draws.
|
|
7
7
|
|
|
8
8
|
IF SOMETHING NOW WRAPS THE SLIDER, THAT ROW IS BACK TO 129px AND NOTHING ELSE WOULD SAY SO.
|
|
9
|
-
The rule that gives it the row is \`.es-field-ctl > .sf-slider\`
|
|
9
|
+
The rule that gives it the row is \`.es-field-ctl > .sf-slider\` — a DIRECT child, because the
|
|
10
10
|
slot is the flex container and only its own children are flex items. A wrapper becomes the
|
|
11
11
|
item, sizes to the track it holds, and the track fills the wrapper instead of the row. A
|
|
12
12
|
descendant selector would NOT save it: it would paint \`flex: 1\` on an element whose parent
|
|
13
13
|
is the wrapper, not the slot, and the row would look fixed while measuring the same 129px.
|
|
14
14
|
|
|
15
|
-
So a wrap needs a decision, not a wider selector
|
|
15
|
+
So a wrap needs a decision, not a wider selector — either unwrap it, or give the wrapper the
|
|
16
16
|
row and say why in \`components/storefront-editor.css\`. Card 66247.`),o.push({fields:f,line:c.split(`
|
|
17
17
|
`).length,readoutOff:/readout=\{false\}/.test(u),labelFor(r){const p=e.get(r);return p||l(`${a}: field \`${r}\` draws a slider but is not in PART_FIELDS, so it has no row label. The two declarations have drifted apart.`),p}})}return o}const b=t=>t.flatMap(n=>n.fields);export{a as PART_PANEL,m as readSliderCallSites,b as sliderFields};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
const o="lib/puck/config.tsx",x=["lib/puck/blocks","lib/puck/passes"],g=["lib/puck/page-root.tsx","lib/puck/page-root-fields.tsx","lib/puck/registry.ts"],d=t=>/\.tsx?$/.test(t)&&!t.endsWith(".d.ts");function m(t){return t===o||g.includes(t)?!0:d(t)&&x.some(e=>t.startsWith(`${e}/`))}function k(t){const e=new Map(t.map(n=>[n.path,n])),r=[o,...g].map(n=>e.get(n)).filter(n=>!!n),i=x.flatMap(n=>t.filter(s=>d(s.path)&&s.path.startsWith(`${n}/`)).sort((s,c)=>s.path<c.path?-1:1));return[...r,...i]}function y(t){return k(t).map(e=>e.text).join(`
|
|
2
2
|
`)}function h(t,e){const r=t.find(i=>new RegExp(`^lib/puck/blocks/[^/]+/${e}/render\\.tsx$`).test(i.path));return r?r.path.slice(0,-11):null}function E(t){return t.map(e=>/^lib\/puck\/blocks\/[^/]+\/([^/]+)\/render\.tsx$/.exec(e.path)?.[1]).filter(e=>!!e).sort()}function b(t,e){const r=h(t,e);return r?[`${r}/render.tsx`,`${r}/fields.tsx`].filter(i=>t.some(n=>n.path===i)):t.some(i=>i.path===o)?[o]:[]}function p(t,e){const r=e.find(l=>l.path===o);if(!r)return null;const i=r.text,n=t.createSourceFile(o,i,t.ScriptTarget.Latest,!0,t.ScriptKind.TSX),s={keys:[],literals:new Map,registeredAs:new Map};let c=!1;const f=l=>{if(!c){if(t.isPropertyAssignment(l)&&l.name.getText(n)==="components"&&t.isObjectLiteralExpression(l.initializer)){c=!0;for(const a of l.initializer.properties){if(!t.isPropertyAssignment(a))continue;const u=a.name.getText(n);s.keys.push(u),t.isObjectLiteralExpression(a.initializer)?s.literals.set(u,i.slice(a.getFullStart(),a.end)):s.registeredAs.set(u,a.initializer.getText(n))}return}t.forEachChild(l,f)}};if(f(n),!c)throw new Error(`registry-source: no \`components\` object in ${o}`);return s}function S(t,e){return[...p(t,e)?.keys??[]]}function $(t,e){return[...p(t,e)?.literals.keys()??[]]}function T(t,e,r){return p(t,e)?.registeredAs.get(r)}function w(t,e,r){if(h(e,r))return b(e,r).map(s=>e.find(c=>c.path===s).text).join(`
|
|
3
|
-
`);const n=p(t,e)?.literals.get(r);if(n===void 0)throw new Error(`registry-source: no block "${r}" among the files handed in
|
|
3
|
+
`);const n=p(t,e)?.literals.get(r);if(n===void 0)throw new Error(`registry-source: no block "${r}" among the files handed in — no folder under lib/puck/blocks/ and no literal in ${o}'s \`components\``);return n}export{o as CONFIG_FILE,x as REGISTRY_DIRS,g as REGISTRY_MODULES,b as blockFiles,w as blockSource,$ as blocksWrittenInConfig,m as isRegistryPath,T as registeredAs,S as registeredBlockKeys,k as registryFiles,y as registryText,E as splitBlockTypes};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{ruleChecks as $}from"./rule.js";const n=["Default","mode"].join(" "),r=["Show Light","Dark switch"].join(" / "),p=[["Theme","Switch","Field"].join(""),["Theme","Mode","Root","Field"].join("")],w=`components/${p[0]}.tsx`,m=c=>c.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");function S(c,h){const{check:t,report:u}=$("theme mode controls gone"),i=c.filter(e=>/\.tsx?$/.test(e.path)),l=e=>i.find(o=>o.path===e)?.text;{const e=m(`export const Row = () => <label>${n}</label>; // ${r}`);t(`the matcher sees "${n}" in code that plainly says it`,e.includes(n));const o=m(`// card 66312 removed the ${n} row
|
|
2
2
|
/* and the ${r} one */
|
|
3
|
-
export const x = 1;`);t("
|
|
4
|
-
`)),t("
|
|
5
|
-
`)),h.liveWiring){const{layout:e,chrome:o}=h.liveWiring,s=l(e)??"";t(
|
|
3
|
+
export const x = 1;`);t("…and a note that names a row, on its own line or in a block, is prose, not code",!o.includes(n)&&!o.includes(r))}t(`§1a the component that drew both rows is gone (${w})`,!i.some(e=>e.path===w),`${w} still exists — it drew both rows and has no other job`),t(`§1b the sweep is real (${i.length} TS/TSX files, at least ${h.minFiles})`,i.length>=h.minFiles,"too few to be this repository, so the absence checks below are passing over nothing");for(const e of h.mustRead??[])t(`§1b the sweep reaches ${e}, a file the rows lived in`,l(e)!==void 0);const a=[],d=[];for(const e of i){const o=m(e.text);for(const s of[n,r])o.includes(s)&&a.push(`${e.path} still names the "${s}" row`);for(const s of p)o.includes(s)&&d.push(`${e.path} still references ${s}`)}if(t("§1b no file names either row",a.length===0,a.join(`
|
|
4
|
+
`)),t("§1c no file wires the retired field or its root",d.length===0,d.join(`
|
|
5
|
+
`)),h.liveWiring){const{layout:e,chrome:o}=h.liveWiring,s=l(e)??"";t(`§3d ${e} builds the boot script from the site's STORED defaultMode / showThemeSwitch`,/buildThemeBootScript\(\{\s*mode:\s*theme\.defaultMode,\s*switchShown:\s*theme\.showThemeSwitch,?\s*\}\)/.test(s),"every site would paint whatever replaced them");for(const f of["loaded.defaultMode","loaded.showThemeSwitch"])t(`§3d …and still reads ${f} from the backend theme row`,s.includes(f));t("§3d …and threads showThemeSwitch to the chrome",/showThemeSwitch=\{theme\.showThemeSwitch\}/.test(s)),t(`§3d ${o} threads the site's switch setting to the Header`,/showThemeToggle=\{showThemeSwitch\}/.test(l(o)??""))}return u()}export{S as themeModeControlsGone};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{ruleChecks as w}from"./rule.js";const j=/\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/,b=[/type=["']file["']/g,/dataTransfer\??\.files/g,/clipboardData\??\.(files|items)/g],c=/from\s+["']efficient-shell["']/;function p(r){return b.reduce((s,n)=>s+(r.match(n)?.length??0),0)}function I(r,s){const{check:n,report:$}=w("upload intake guard"),f=s.allowlist??{};{const e=o=>["type=",o,"file",o].join(""),t=["dataTransfer","files"],h=["clipboardData","items"],k=["clipboardData","files"],y=[["a double-quoted file input",`<input ${e('"')} accept="image/*" />`],["a single-quoted file input",`<input ${e("'")} />`],["a drop zone reading files",`openPicker(e.${t.join(".")}?.[0] ?? null)`],["
|
|
1
|
+
import{ruleChecks as w}from"./rule.js";const j=/\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/,b=[/type=["']file["']/g,/dataTransfer\??\.files/g,/clipboardData\??\.(files|items)/g],c=/from\s+["']efficient-shell["']/;function p(r){return b.reduce((s,n)=>s+(r.match(n)?.length??0),0)}function I(r,s){const{check:n,report:$}=w("upload intake guard"),f=s.allowlist??{};{const e=o=>["type=",o,"file",o].join(""),t=["dataTransfer","files"],h=["clipboardData","items"],k=["clipboardData","files"],y=[["a double-quoted file input",`<input ${e('"')} accept="image/*" />`],["a single-quoted file input",`<input ${e("'")} />`],["a drop zone reading files",`openPicker(e.${t.join(".")}?.[0] ?? null)`],["…through optional chaining",`const f = e.${t.join("?.")};`],["a paste handler reading items",`Array.from(e.${h.join(".")})`],["a paste handler reading files",`e.${k.join("?.")}`]];for(const[o,l]of y)n(`the scan flags ${o}`,p(l)===1,l);const x=[["a text input",'<input type="text" />'],["a drop zone that only reads the types","e.dataTransfer.types.includes('Files')"],["a paste handler that only reads text","e.clipboardData.getData('text/plain')"],["the shell's own input spread","<input {...intake.inputProps} hidden />"]];for(const[o,l]of x)n(`the scan stays quiet on ${o}`,p(l)===0,l);const g=["efficient","shell"].join("-");n("the root-entry reader finds an import of the shell's root",c.test(`import { x } from "${g}";`)),n("…and not one of a subpath",!c.test(`import { useFileIntake } from "${g}/file-intake";`))}const a=r.filter(e=>j.test(e.path));n(`read the source (${a.length} JS/TS files, at least ${s.minFiles})`,a.length>=s.minFiles,"the walk found too few source files — it is looking in the wrong place, and every check below is vacuous");const i=new Map;for(const e of a){const t=p(e.text);t>0&&i.set(e.path,t)}const d=[...i].filter(([e])=>!(e in f)).map(([e,t])=>`${e} (${t})`);n("no file outside the allowlist hand-rolls a file input, drop zone or paste handler",d.length===0,`take files through useFileIntake from efficient-shell/file-intake (harness UI-15 / PAT-10):
|
|
2
2
|
${d.join(`
|
|
3
3
|
`)}`);const u=Object.entries(f).filter(([e,t])=>(i.get(e)??0)>t.max).map(([e,t])=>`${e}: ${i.get(e)} > ${t.max}`);n("no allowlisted file grows its hand-rolled intake",u.length===0,u.join(`
|
|
4
|
-
`));for(const[e,t]of Object.entries(f))n(`the allowlist entry is still real: ${e} (${t.why})`,i.has(e),"remove the entry
|
|
4
|
+
`));for(const[e,t]of Object.entries(f))n(`the allowlist entry is still real: ${e} (${t.why})`,i.has(e),"remove the entry — the ratchet only tightens");for(const e of s.migrated??[]){const t=a.find(h=>h.path===e);!n(`read the migrated surface ${e}`,!!t,"it is not among the files handed in")||!t||(n(`${e} imports the shell's intake`,/from "efficient-shell\/file-intake"/.test(t.text)),n(`${e} drives its zone with useFileIntake`,/useFileIntake\(/.test(t.text)),n(`${e} spreads the intake's drop handlers on its zone`,/\.\.\.intake\.zoneProps/.test(t.text)))}const m=a.filter(e=>c.test(e.text)).map(e=>e.path);return n("nothing imports the shell's root entry",m.length===0,`the root pulls the shell's toasts, stylesheet and window manager; take a React-only subpath:
|
|
5
5
|
${m.join(`
|
|
6
6
|
`)}`),$()}export{p as intakeMarkerCount,I as uploadIntakeGuard};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import{ruleChecks as p}from"./rule.js";const m=/(?:\baria-label|\blabel|\btitle|\bplaceholder)\s*[:=]\s*\{?\s*(["'])([^"']*)\1/g,u=/^(Emphasis|Strong)\b/;function i(s){return[...s.matchAll(m)].map(e=>e[2])}function h(s){return i(s.text).filter(e=>u.test(e)).map(e=>`${s.path}: "${e}"`)}function y(s,e){const{check:l,report:f}=p("word styles"),g=e.exempt??{};{const t={path:"fixture.tsx",text:['const a = { style: "wordHighlight", title: "Emphasis word style" };','const b = <button aria-label="Strong word style" />;','const c = <input placeholder={"Emphasis"} />;',"const d = { label: 'Strong' };","// the Emphasis button and the Strong one are upstream's",'toggle("Strong", false);','const e = { title: "Highlight word style", label: "Bold" };','const f = { title: "A strong claim", label: "Strongest" };'].join(`
|
|
2
|
-
`)},r=i(t.text),a=["Emphasis word style","Strong word style","Emphasis","Strong","Highlight word style","Bold","A strong claim","Strongest"];l("the reader reads a label, title, aria-label and placeholder, and neither prose nor a positional argument",JSON.stringify(r)===JSON.stringify(a),JSON.stringify(r));const o=h(t);l("the rule flags a label that begins Emphasis or Strong, and passes Highlight, Bold, `A strong
|
|
2
|
+
`)},r=i(t.text),a=["Emphasis word style","Strong word style","Emphasis","Strong","Highlight word style","Bold","A strong claim","Strongest"];l("the reader reads a label, title, aria-label and placeholder, and neither prose nor a positional argument",JSON.stringify(r)===JSON.stringify(a),JSON.stringify(r));const o=h(t);l("the rule flags a label that begins Emphasis or Strong, and passes Highlight, Bold, `A strong …` and `Strongest`",JSON.stringify(o)===JSON.stringify(a.slice(0,4).map(b=>`fixture.tsx: "${b}"`)),JSON.stringify(o))}const n=s.filter(t=>/\.tsx?$/.test(t.path)),d=n.reduce((t,r)=>t+i(r.text).length,0);l(`read the source (${n.length} .ts/.tsx files, at least ${e.minFiles})`,n.length>=e.minFiles),l(`read the labels (${d}, at least ${e.labelFloor})`,d>=e.labelFloor,"fewer means the label reader broke, and an absence over a reader that matches nothing is free");const c=n.filter(t=>!(t.path in g)).flatMap(h);l("no merchant-visible label calls our word styles Emphasis or Strong",c.length===0,`our word styles are called Highlight and Bold (2026-10-04) — Emphasis and Strong belong to the upstream ◆ and bold buttons, whose descriptors live in efficient-easel and are not written here:
|
|
3
3
|
${c.join(`
|
|
4
|
-
`)}`);for(const[t,r]of Object.entries(g)){const a=n.find(o=>o.path===t);l(`the exemption is still real: ${t} (${r})`,!!a&&h(a).length>0,"the file is gone or no longer writes the retired words
|
|
4
|
+
`)}`);for(const[t,r]of Object.entries(g)){const a=n.find(o=>o.path===t);l(`the exemption is still real: ${t} (${r})`,!!a&&h(a).length>0,"the file is gone or no longer writes the retired words — remove the stale entry")}return f()}export{i as labelsIn,y as wordStyles};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "efficient-web-builder",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "The EFFICIENT website builder: the website editor, the general sections and the renderer that draws them, compiled. Hostable by any EFFICIENT product; nothing here imports a host.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Efficient Pty Ltd",
|
|
@@ -31,19 +31,19 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
32
|
"test:boundary": "node scripts/test-boundary.mjs",
|
|
33
33
|
"test:screen": "node scripts/test-screen-in-easel.mjs",
|
|
34
|
+
"test:own-ui": "node scripts/test-own-ui-ratchet.mjs",
|
|
34
35
|
"build": "node scripts/build.mjs",
|
|
35
36
|
"verify:dist": "node scripts/verify-dist.mjs",
|
|
36
|
-
"verify": "pnpm run typecheck && pnpm run test:boundary && pnpm run test:screen && pnpm run test:guards && pnpm run build && pnpm run verify:dist",
|
|
37
|
+
"verify": "pnpm run typecheck && pnpm run test:boundary && pnpm run test:screen && pnpm run test:own-ui && pnpm run test:guards && pnpm run build && pnpm run verify:dist",
|
|
37
38
|
"test:guards": "tsc --noEmit -p scripts/tsconfig.json && node scripts/run-guards.mjs"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"@tabler/icons-react": "^3.46.0",
|
|
41
42
|
"efficient-easel": "^0.106.0",
|
|
42
|
-
"efficient-shell": "^4.
|
|
43
|
+
"efficient-shell": "^4.145.0",
|
|
43
44
|
"isomorphic-dompurify": "^3.18.0",
|
|
44
45
|
"next": ">=16",
|
|
45
46
|
"react": ">=19",
|
|
46
|
-
"react-dom": ">=19",
|
|
47
47
|
"simple-icons": "^16.26.0",
|
|
48
48
|
"typescript": "^5"
|
|
49
49
|
},
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@types/react": "^19",
|
|
60
60
|
"@types/react-dom": "^19",
|
|
61
61
|
"efficient-easel": "^0.106.0",
|
|
62
|
-
"efficient-shell": "^4.
|
|
62
|
+
"efficient-shell": "^4.145.0",
|
|
63
63
|
"esbuild": "^0.28.0",
|
|
64
64
|
"happy-dom": "^20.14.5",
|
|
65
65
|
"isomorphic-dompurify": "^3.18.0",
|