efficient-web-builder 23.1.0 → 23.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/lib/puck/show-when-attach.d.ts +5 -12
- package/dist/lib/puck/show-when-attach.js +1 -1
- package/dist/lib/puck/show-when-resolve.js +1 -1
- package/dist/lib/puck/show-when.d.ts +1 -1
- package/dist/lib/puck/show-when.js +1 -1
- package/dist/lib/source-rules/one-show-when.js +4 -4
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 23.2.0 - 2026-10-19
|
|
6
|
+
|
|
7
|
+
**The show-when helpers are the shell's** (SSOT batch 1, GG 2026-10-19). The website editor and the email designer are one editor, one rule, built once (Lewis). `efficient-shell/format` 7.75.0 holds them, and this package re-exports them under the names it had. Nothing a merchant sees moves.
|
|
8
|
+
|
|
9
|
+
### Changed for hosts
|
|
10
|
+
|
|
11
|
+
- `lib/puck/show-when-attach` (and `lib/puck/show-when`) re-export `withRowsShownWhen`, `withRowRules`, `RowRule`, `shownForOption`, `addShowWhenFacts` and `ShowWhenFactData` from `efficient-shell/format`, and add `truthy` (a value read as `if (value)` does). `BlockShowWhenFacts` is the shell's `ShowWhenFacts<ShowWhenFactParams>`. `ShowWhenFactParams`, what this editor hands a resolver, stays here.
|
|
12
|
+
- `resolverContext` and `rowsShownForNode` build their lazy context with the shell's `lazyShowWhenContext` and `showWhenFactReaders`.
|
|
13
|
+
- The efficient-shell peer and dev range rises to `^7.75.0`.
|
|
14
|
+
|
|
15
|
+
### Guard
|
|
16
|
+
|
|
17
|
+
- `lib/source-rules/one-show-when` R6 now covers every show-when helper of the shell's (`SHELL_HELPERS`, 11 names), not only `withShowWhen` and `showWhenOf`. A repository that writes one of them again fails. The storefront-blocks' own `truthy` (`commerce/page-rules`) goes with its bump to this release.
|
|
18
|
+
- `scripts/test-show-when-rows.tsx` §10b: the five helpers are the shell's functions.
|
|
19
|
+
|
|
5
20
|
## 23.1.0 - 2026-10-19
|
|
6
21
|
|
|
7
22
|
**A list item's rows read the section's settings, and a rule that hides a whole tab keeps the tab strip** (SSOT batch 1, GG 2026-10-19). Nothing a merchant sees moves: no rule uses either yet. They let the last two hand-written hides in the storefront blocks move (DetailRows' Image shape, ProductHero's dynamic-page cut).
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { showWhenOf, withShowWhen };
|
|
3
|
-
export type { ShowWhenRule, WithShowWhen };
|
|
4
|
-
export declare function withRowsShownWhen<T extends Record<string, unknown>>(fields: T, keys: readonly string[], rule: ShowWhenRule): T;
|
|
5
|
-
export type RowRule = readonly [keys: readonly string[], rule: ShowWhenRule];
|
|
6
|
-
export declare function withRowRules<T extends Record<string, unknown>>(fields: T, table: readonly RowRule[]): T;
|
|
7
|
-
export declare function shownForOption(field: string, known: readonly string[], fallback: string, shownFor: readonly string[]): ShowWhenRule;
|
|
8
|
-
export type ShowWhenFactData = {
|
|
9
|
-
props?: Record<string, unknown>;
|
|
10
|
-
};
|
|
1
|
+
import { type ShowWhenFacts } from "efficient-shell/format";
|
|
2
|
+
export { showWhenOf, shownForOption, truthy, withRowRules, withRowsShownWhen, withShowWhen, } from "efficient-shell/format";
|
|
3
|
+
export type { RowRule, ShowWhenFactData, ShowWhenRule, WithShowWhen } from "efficient-shell/format";
|
|
11
4
|
export type ShowWhenFactParams = {
|
|
12
5
|
fields: Record<string, unknown>;
|
|
13
6
|
appState?: unknown;
|
|
@@ -15,5 +8,5 @@ export type ShowWhenFactParams = {
|
|
|
15
8
|
type?: string;
|
|
16
9
|
} | null;
|
|
17
10
|
};
|
|
18
|
-
export type BlockShowWhenFacts =
|
|
19
|
-
export declare
|
|
11
|
+
export type BlockShowWhenFacts = ShowWhenFacts<ShowWhenFactParams>;
|
|
12
|
+
export declare const addShowWhenFacts: (block: object, facts: BlockShowWhenFacts) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{addShowWhenFacts as o}from"efficient-shell/format";import{showWhenOf as s,shownForOption as e,truthy as a,withRowRules as r,withRowsShownWhen as d,withShowWhen as i}from"efficient-shell/format";const t=o;export{t as addShowWhenFacts,s as showWhenOf,e as shownForOption,a as truthy,r as withRowRules,d as withRowsShownWhen,i as withShowWhen};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isShown as
|
|
1
|
+
import{isShown as O,showWhenFactReaders as b}from"efficient-shell/format";import{containerOffersPositions as k}from"../content/grid-span.js";import{heroGroupNestDepth as j}from"../hero-group-placement.js";import{BUCKET_KEY as x,reseatBucketTabs as y}from"./field-groups.js";import{readsContext as E,resolverContext as v,showWhenOf as B}from"./show-when.js";import{findParentNode as C}from"./passes/document-position.js";const m=new WeakMap,D=new Set(Object.values(x));function A(e,p,r,w={}){if(!e)return e;const u=p?.props??{},i=u.id,d=b(w,p,r),S=v(r,{...d,parentOffersPositions:()=>{const t=C(r.appState,i);return t===void 0?void 0:k(t)},heroGroupDepth:()=>j(r.appState?.data,typeof i=="string"?i:"")}),o=[];for(const[t,c]of Object.entries(e)){const f=B(c);!f||E(f,"device")||O(f,u,S)||o.push(t)}if(o.length===0)return e;let s=m.get(e);s||m.set(e,s=new Map);const h=o.join("\0"),a=s.get(h);if(a)return a;const g=new Set(o);let n={};for(const[t,c]of Object.entries(e))g.has(t)||(n[t]=c);return o.some(t=>D.has(t))&&(n=y(n)),s.set(h,n),n}export{A as rowsShownForNode};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ShowWhenContext, type ShowWhenRule } from "efficient-shell/format";
|
|
2
2
|
import type { SlotConfigView, TreeItem } from "efficient-easel/draw";
|
|
3
3
|
export type { ShowWhenRule, ShowWhenContext };
|
|
4
|
-
export { showWhenOf, shownForOption, withRowRules, withRowsShownWhen, withShowWhen, type RowRule, type WithShowWhen } from "./show-when-attach.js";
|
|
4
|
+
export { showWhenOf, shownForOption, truthy, withRowRules, withRowsShownWhen, withShowWhen, type RowRule, type WithShowWhen } from "./show-when-attach.js";
|
|
5
5
|
export declare function visibleFields<T extends Record<string, unknown>>(fields: T, values: Readonly<Record<string, unknown>> | null | undefined, context: ShowWhenContext): T;
|
|
6
6
|
export type ShowWhenSelector = {
|
|
7
7
|
index: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isShown as s}from"efficient-shell/format";import{isEditorOnDynamicPage as
|
|
1
|
+
import{isShown as s,lazyShowWhenContext as a}from"efficient-shell/format";import{isEditorOnDynamicPage as h}from"./editor-page-context.js";import{parentOfSelection as u}from"./selection-parent.js";import{showWhenOf as C,shownForOption as b,truthy as g,withRowRules as R,withRowsShownWhen as j,withShowWhen as v}from"./show-when-attach.js";import{showWhenOf as c}from"./show-when-attach.js";function d(t,o,e){let r=!1;const n={};for(const[f,i]of Object.entries(t))s(c(i),o??{},e)?n[f]=i:r=!0;return r?n:t}function m(t,o,e,r){const n=u(o,e,r);return{device:t,parentType:n?.item.type,parent:n?.item.props}}function p(t,o){if(!t||typeof t!="object")return!1;const e=t;if(typeof e.context=="string"&&(e.context===o||e.context.startsWith(`${o}.`)))return!0;if("not"in e)return p(e.not,o);for(const r of[e.all,e.any])if(Array.isArray(r)&&r.some(n=>p(n,o)))return!0;return!1}function W(t,o={}){return a({parentType:()=>t.parent?.type??void 0,parent:()=>t.parent?.props??void 0,dynamicPage:()=>h(),root:()=>t.appState?.data?.root?.props,...o})}export{p as readsContext,W as resolverContext,m as showWhenContext,C as showWhenOf,b as shownForOption,g as truthy,d as visibleFields,R as withRowRules,j as withRowsShownWhen,v as withShowWhen};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{ruleChecks as
|
|
2
|
-
`),
|
|
3
|
-
`+
|
|
4
|
-
`)),i(`read the source (${f.length} TS/TSX files, at least ${c.minFiles})`,f.length>=c.minFiles),i(`found >= ${c.ruleFloor} rows given a rule (found ${
|
|
1
|
+
import{ruleChecks as b}from"./rule.js";const W=["lib/puck/show-when.ts","lib/puck/show-when-resolve.ts"],k=(s,l=[])=>/(^|\/)fields\.tsx?$/.test(s)||s.includes("lib/puck/passes/")||l.includes(s),y=new Set(["SettingRow","SettingsField","SettingsSwitch","SettingsSegmented","SettingsDrill","SettingsAction","SettingsGroupSub","InspectorField","FieldText","FieldNumber","FieldSelect","FieldSlider","ListField"]),S=/\b(?:props|values|settings|data\.props)(?:\.|\[|\?\.)/,E=new Set(["withShowWhen","showWhenOf","withRowsShownWhen","withRowRules","truthy","shownForOption","addShowWhenFacts","withShowWhenFacts","showWhenFactsContext","showWhenFactReaders","lazyShowWhenContext"]);function g(s,l,c=[]){const i=s.createSourceFile(l.path,l.text,s.ScriptTarget.Latest,!0,s.ScriptKind.TSX),h=e=>i.getLineAndCharacterOfPosition(e.getStart(i)).line+1,a=[];let f=0;const w=W.some(e=>l.path.endsWith(e)),x=e=>s.isPropertyAssignment(e)&&e.name.getText(i)==="resolveFields"||s.isBinaryExpression(e)&&e.operatorToken.kind===s.SyntaxKind.EqualsToken&&s.isPropertyAccessExpression(e.left)&&e.left.name.text==="resolveFields",p=(e,n)=>{const d=n||x(e);if(s.isDeleteExpression(e)&&(s.isPropertyAccessExpression(e.expression)||s.isElementAccessExpression(e.expression))&&(d?a.push({file:l.path,line:h(e),what:"R1 a `delete` in a resolveFields — give the row a showWhen rule"}):k(l.path,c)&&a.push({file:l.path,line:h(e),what:"R2 a `delete` in a field file or a pass — give the row a showWhen rule"})),d&&s.isObjectBindingPattern(e)&&e.elements.some(o=>o.dotDotDotToken)&&a.push({file:l.path,line:h(e),what:"R1 a destructure that leaves a key out, in a resolveFields — give the row a showWhen rule"}),d&&s.isCallExpression(e)&&s.isPropertyAccessExpression(e.expression)&&e.expression.name.text==="filter"&&/^Object\.entries\(/.test(e.expression.expression.getText(i))&&a.push({file:l.path,line:h(e),what:"R1 a filter over Object.entries in a resolveFields — give the row a showWhen rule"}),d&&s.isForOfStatement(e)&&/^Object\.entries\(/.test(e.expression.getText(i))&&a.push({file:l.path,line:h(e),what:"R4 a loop over Object.entries in a resolveFields — give the row a showWhen rule"}),s.isJsxExpression(e)&&e.expression){const o=e.expression,u=r=>s.isParenthesizedExpression(r)?u(r.expression):r,t=r=>{if(!r)return!1;const R=s.isJsxElement(r)?r.openingElement:s.isJsxSelfClosingElement(r)?r:s.isJsxFragment(r)?null:void 0;return R===null?r.getChildren(i).some(F=>F.getChildren(i).some(v=>t(v))):!!R&&y.has(R.tagName.getText(i))};(s.isBinaryExpression(o)&&o.operatorToken.kind===s.SyntaxKind.AmpersandAmpersandToken&&S.test(o.left.getText(i))&&t(u(o.right))||s.isConditionalExpression(o)&&S.test(o.condition.getText(i))&&(t(u(o.whenTrue))||t(u(o.whenFalse))))&&a.push({file:l.path,line:h(e),what:"R5 a settings row drawn by a stored value — use the row's showWhen (SettingRow / ShowWhen)"})}const m=o=>!!o&&s.isIdentifier(o)&&E.has(o.text);if((s.isFunctionDeclaration(e)&&m(e.name)||s.isVariableDeclaration(e)&&m(e.name)&&e.initializer&&(s.isArrowFunction(e.initializer)||s.isFunctionExpression(e.initializer)))&&a.push({file:l.path,line:h(e),what:"R6 a second copy of a show-when helper of the shell's — import it from efficient-shell/format"}),s.isCallExpression(e)&&s.isIdentifier(e.expression)){const o=e.expression.text;o==="isShown"&&!w&&a.push({file:l.path,line:h(e),what:"R3 a second reader of the rule — `isShown(` lives in lib/puck/show-when"}),(o==="withShowWhen"||o==="withRowsShownWhen"||o==="withRowRules")&&f++}s.forEachChild(e,o=>p(o,d))};return p(i,!1),{bad:a,attached:f}}function A(s,l,c){const{check:i,report:h}=b("one show-when rule"),a=c.kept??{};{const e=["export const A = { resolveFields: (data, { fields }) => { const f = { ...fields }; delete f.alt; return f; } };","export const B = { resolveFields: (d, { fields }) => { const { align: _x, ...rest } = fields; return rest; } };",'comp.resolveFields = (d, p) => Object.fromEntries(Object.entries(p.fields).filter(([k]) => k !== "x"));',"function cull(f) { delete f[key]; }","const shown = isShown(rule, values);","// delete f.alt in a comment is not a use",'export const ok = { fields: { alt: withShowWhen(text("Alt"), { field: "src", set: true }) } };',"export const C = { resolveFields: (d, { fields }) => { const out = {}; for (const [k, v] of Object.entries(fields)) if (keep.includes(k)) out[k] = v; return out; } };",'const p = <div>{settings.logo_position === "center" && <SettingRow label="Left items" />}</div>;','const q = <div>{props.on ? (<SettingsField label="x" />) : null}</div>;','const r = <div>{style ? <SettingsField label="Style" /> : null}</div>;',"export function withShowWhen(field, rule) { return { ...field, showWhen: rule }; }","const showWhenOf = (f) => f.showWhen;",'export { showWhenOf, withShowWhen } from "efficient-shell/format";','export function truthy(field) { return { not: { field, in: [false, 0, "", null] } }; }',"const withRowRules = (fields, table) => fields;"].join(`
|
|
2
|
+
`),n=g(s,{path:"lib/puck/blocks/general/X/fields.tsx",text:e}),d=n.bad.map(t=>t.line);i("R1 flags a delete in a resolveFields",n.bad.some(t=>t.line===1&&t.what.startsWith("R1"))),i("R1 flags a destructure that leaves a key out",n.bad.some(t=>t.line===2&&t.what.startsWith("R1"))),i("R1 flags a filter over Object.entries",n.bad.some(t=>t.line===3&&t.what.startsWith("R1"))),i("R2 flags a delete in a field file's helper",n.bad.some(t=>t.line===4&&t.what.startsWith("R2"))),i("R3 flags a second isShown(",n.bad.some(t=>t.line===5&&t.what.startsWith("R3"))),i("a comment is not a use, and a rule passes and is counted",!d.includes(6)&&!d.includes(7)&&n.attached===1),i("R4 flags an allow-list loop over Object.entries",n.bad.some(t=>t.line===8&&t.what.startsWith("R4"))),i("R5 flags a row drawn by a stored value (&&)",n.bad.some(t=>t.line===9&&t.what.startsWith("R5"))),i("R5 flags a row drawn by a stored value (? :)",n.bad.some(t=>t.line===10&&t.what.startsWith("R5"))),i("R5 lets a part the host passed through",!d.includes(11)),i("R6 flags a second withShowWhen (a function)",n.bad.some(t=>t.line===12&&t.what.startsWith("R6"))),i("R6 flags a second showWhenOf (a const arrow)",n.bad.some(t=>t.line===13&&t.what.startsWith("R6"))),i("R6 lets a re-export from the shell through",!d.includes(14)),i("R6 flags a second truthy (a function)",n.bad.some(t=>t.line===15&&t.what.startsWith("R6"))),i("R6 flags a second withRowRules (a const arrow)",n.bad.some(t=>t.line===16&&t.what.startsWith("R6")));const m=g(s,{path:"lib/puck/cull.ts",text:"function cull(f) { delete f.x; }"},["lib/puck/cull.ts"]);i("R2 reads a file a repository names as a field file",m.bad.length===1);const o=g(s,{path:"lib/puck/part-styles.ts",text:"function f(next) { delete next[k]; }"});i("R2 reads only field files and passes (a delete of a VALUE elsewhere is not a hide)",o.bad.length===0);const u=g(s,{path:"lib/puck/show-when.ts",text:"isShown(rule, values);"});i("R3 lets the reader read",u.bad.length===0)}const f=l.filter(e=>/\.tsx?$/.test(e.path)&&!e.path.endsWith(".d.ts")),w=[],x=new Map;let p=0;for(const e of f){const n=g(s,e,c.alsoFieldFiles);p+=n.attached,x.set(e.path,n.bad.length),e.path in a||w.push(...n.bad)}i("no settings row hidden by hand: no delete or key-dropping filter in a resolver, field file or pass; one reader",w.length===0,`${w.length} hit(s):
|
|
3
|
+
`+w.map(e=>`${e.file}:${e.line} ${e.what}`).join(`
|
|
4
|
+
`)),i(`read the source (${f.length} TS/TSX files, at least ${c.minFiles})`,f.length>=c.minFiles),i(`found >= ${c.ruleFloor} rows given a rule (found ${p})`,p>=c.ruleFloor,"fewer means rows went back to a hand-written hide, or the reader broke");for(const e of c.alsoFieldFiles??[])i(`read the named field file ${e}`,f.some(n=>n.path===e));for(const[e,n]of Object.entries(a))i(`the exception is still real: ${e} (${n})`,(x.get(e)??0)>0,"remove the stale entry");return h()}export{A as oneShowWhen};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "efficient-web-builder",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.2.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",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@tabler/icons-react": "^3.46.0",
|
|
47
47
|
"efficient-easel": "^13.1.1",
|
|
48
|
-
"efficient-shell": "^7.
|
|
48
|
+
"efficient-shell": "^7.75.0",
|
|
49
49
|
"isomorphic-dompurify": "^3.18.0",
|
|
50
50
|
"next": ">=16",
|
|
51
51
|
"react": ">=19",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@types/react": "^19",
|
|
66
66
|
"@types/react-dom": "^19",
|
|
67
67
|
"efficient-easel": "^13.1.1",
|
|
68
|
-
"efficient-shell": "^7.
|
|
68
|
+
"efficient-shell": "^7.75.0",
|
|
69
69
|
"esbuild": "^0.28.0",
|
|
70
70
|
"happy-dom": "^20.14.5",
|
|
71
71
|
"isomorphic-dompurify": "^3.18.0",
|