@jfdevelops/react-layout 2.2.0 → 2.4.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/dist/create-config/create-defined-resource-layout.cjs +2 -0
- package/dist/create-config/create-defined-resource-layout.cjs.map +1 -1
- package/dist/create-config/create-defined-resource-layout.d.cts +15 -12
- package/dist/create-config/create-defined-resource-layout.d.cts.map +1 -1
- package/dist/create-config/create-defined-resource-layout.d.mts +15 -12
- package/dist/create-config/create-defined-resource-layout.d.mts.map +1 -1
- package/dist/create-config/create-defined-resource-layout.mjs +2 -0
- package/dist/create-config/create-defined-resource-layout.mjs.map +1 -1
- package/dist/create-config/create-resource-links.cjs.map +1 -1
- package/dist/create-config/create-resource-links.d.cts +3 -1
- package/dist/create-config/create-resource-links.d.cts.map +1 -1
- package/dist/create-config/create-resource-links.d.mts +3 -1
- package/dist/create-config/create-resource-links.d.mts.map +1 -1
- package/dist/create-config/create-resource-links.mjs.map +1 -1
- package/dist/create-config/create-reusable.cjs +213 -0
- package/dist/create-config/create-reusable.cjs.map +1 -0
- package/dist/create-config/create-reusable.d.cts +1 -0
- package/dist/create-config/create-reusable.d.mts +1 -0
- package/dist/create-config/create-reusable.mjs +213 -0
- package/dist/create-config/create-reusable.mjs.map +1 -0
- package/dist/create-config/define-layout.cjs +11 -8
- package/dist/create-config/define-layout.cjs.map +1 -1
- package/dist/create-config/define-layout.d.cts +43 -42
- package/dist/create-config/define-layout.d.cts.map +1 -1
- package/dist/create-config/define-layout.d.mts +43 -42
- package/dist/create-config/define-layout.d.mts.map +1 -1
- package/dist/create-config/define-layout.mjs +11 -8
- package/dist/create-config/define-layout.mjs.map +1 -1
- package/dist/create-config/for-paths/builder.cjs +1 -0
- package/dist/create-config/for-paths/builder.cjs.map +1 -1
- package/dist/create-config/for-paths/builder.d.cts +5 -3
- package/dist/create-config/for-paths/builder.d.cts.map +1 -1
- package/dist/create-config/for-paths/builder.d.mts +5 -3
- package/dist/create-config/for-paths/builder.d.mts.map +1 -1
- package/dist/create-config/for-paths/builder.mjs +1 -0
- package/dist/create-config/for-paths/builder.mjs.map +1 -1
- package/dist/create-config/for-resource.cjs.map +1 -1
- package/dist/create-config/for-resource.d.cts +7 -6
- package/dist/create-config/for-resource.d.cts.map +1 -1
- package/dist/create-config/for-resource.d.mts +7 -6
- package/dist/create-config/for-resource.d.mts.map +1 -1
- package/dist/create-config/for-resource.mjs.map +1 -1
- package/dist/create-config/for-resources/create-component.d.cts +34 -33
- package/dist/create-config/for-resources/create-component.d.cts.map +1 -1
- package/dist/create-config/for-resources/create-component.d.mts +34 -33
- package/dist/create-config/for-resources/create-component.d.mts.map +1 -1
- package/dist/create-config/for-resources/create-for-resources.cjs +7 -6
- package/dist/create-config/for-resources/create-for-resources.cjs.map +1 -1
- package/dist/create-config/for-resources/create-for-resources.d.cts +2 -1
- package/dist/create-config/for-resources/create-for-resources.d.mts +2 -1
- package/dist/create-config/for-resources/create-for-resources.mjs +7 -6
- package/dist/create-config/for-resources/create-for-resources.mjs.map +1 -1
- package/dist/create-config/for-resources/scoped-layout.d.cts +5 -4
- package/dist/create-config/for-resources/scoped-layout.d.cts.map +1 -1
- package/dist/create-config/for-resources/scoped-layout.d.mts +5 -4
- package/dist/create-config/for-resources/scoped-layout.d.mts.map +1 -1
- package/dist/create-config/index.d.cts +1 -0
- package/dist/create-config/index.d.mts +1 -0
- package/dist/create-config/layout-name.cjs +1 -1
- package/dist/create-config/layout-name.mjs +1 -1
- package/dist/create-config/lazy-resource-layout.cjs +1 -1
- package/dist/create-config/lazy-resource-layout.d.cts +1 -1
- package/dist/create-config/lazy-resource-layout.d.mts +1 -1
- package/dist/create-config/lazy-resource-layout.mjs +1 -1
- package/dist/create-config/merge-layout-props.cjs.map +1 -1
- package/dist/create-config/merge-layout-props.mjs.map +1 -1
- package/dist/create-config/render-state.cjs +3 -2
- package/dist/create-config/render-state.cjs.map +1 -1
- package/dist/create-config/render-state.d.cts +27 -25
- package/dist/create-config/render-state.d.cts.map +1 -1
- package/dist/create-config/render-state.d.mts +27 -25
- package/dist/create-config/render-state.d.mts.map +1 -1
- package/dist/create-config/render-state.mjs +3 -2
- package/dist/create-config/render-state.mjs.map +1 -1
- package/dist/create-config/reusable-types.d.cts +131 -0
- package/dist/create-config/reusable-types.d.cts.map +1 -0
- package/dist/create-config/reusable-types.d.mts +131 -0
- package/dist/create-config/reusable-types.d.mts.map +1 -0
- package/dist/create-config/state-composable.cjs +1 -1
- package/dist/create-config/state-composable.mjs +1 -1
- package/dist/custom-props.cjs +15 -0
- package/dist/custom-props.cjs.map +1 -0
- package/dist/custom-props.d.cts +17 -0
- package/dist/custom-props.d.cts.map +1 -0
- package/dist/custom-props.d.mts +17 -0
- package/dist/custom-props.d.mts.map +1 -0
- package/dist/custom-props.mjs +15 -0
- package/dist/custom-props.mjs.map +1 -0
- package/dist/errors/react-layout-error.cjs +1 -1
- package/dist/errors/react-layout-error.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +4 -2
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +3 -3
- package/dist/lazy/create-render-state.cjs.map +1 -1
- package/dist/lazy/create-render-state.d.cts +5 -5
- package/dist/lazy/create-render-state.d.cts.map +1 -1
- package/dist/lazy/create-render-state.d.mts +5 -5
- package/dist/lazy/create-render-state.d.mts.map +1 -1
- package/dist/lazy/create-render-state.mjs.map +1 -1
- package/dist/lazy/lazy-boundary.cjs +1 -1
- package/dist/lazy/lazy-boundary.mjs +1 -1
- package/dist/lazy/types.cjs.map +1 -1
- package/dist/lazy/types.d.cts +5 -4
- package/dist/lazy/types.d.cts.map +1 -1
- package/dist/lazy/types.d.mts +5 -4
- package/dist/lazy/types.d.mts.map +1 -1
- package/dist/lazy/types.mjs.map +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.cts +10 -4
- package/dist/props.d.cts.map +1 -1
- package/dist/props.d.mts +10 -4
- package/dist/props.d.mts.map +1 -1
- package/dist/props.mjs.map +1 -1
- package/dist/resource.d.cts +1 -1
- package/dist/resource.d.mts +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const require_layout_configuration_error = require('../errors/layout-configuration-error.cjs');
|
|
2
|
+
const require_define_layout = require('./define-layout.cjs');
|
|
3
|
+
let _jfdevelops_react_layout_validator = require("@jfdevelops/react-layout-validator");
|
|
4
|
+
|
|
5
|
+
//#region src/create-config/create-reusable.ts
|
|
6
|
+
function objectPlan(children, optional = false, guards = [[]]) {
|
|
7
|
+
return {
|
|
8
|
+
kind: "object",
|
|
9
|
+
children,
|
|
10
|
+
optional,
|
|
11
|
+
guards
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function optionPlan(definitions, selection, from, path = [], optionalPaths = []) {
|
|
15
|
+
const entries = selection === true ? Object.fromEntries(Object.keys(definitions).map((key) => [key, true])) : selection;
|
|
16
|
+
const children = {};
|
|
17
|
+
for (const [key, value] of Object.entries(entries ?? {})) {
|
|
18
|
+
if (!Object.hasOwn(definitions, key)) throw new require_layout_configuration_error.LayoutConfigurationError(`Unknown reusable option "${key}".`);
|
|
19
|
+
const definition = definitions[key];
|
|
20
|
+
const location = {
|
|
21
|
+
from,
|
|
22
|
+
path: [...path, key]
|
|
23
|
+
};
|
|
24
|
+
if (typeof value === "function") children[key] = {
|
|
25
|
+
...location,
|
|
26
|
+
from: "options",
|
|
27
|
+
guards: optionalPaths.map((path) => ({
|
|
28
|
+
from: "options",
|
|
29
|
+
path
|
|
30
|
+
})),
|
|
31
|
+
kind: "value",
|
|
32
|
+
source: "option",
|
|
33
|
+
selector: value,
|
|
34
|
+
definition
|
|
35
|
+
};
|
|
36
|
+
else if ("properties" in definition && "type" in definition && definition.type === "object" && (value === true || value && typeof value === "object")) children[key] = {
|
|
37
|
+
...optionPlan(definition.properties, value, from, location.path, definition.visibility === "optional" ? [...optionalPaths, location.path] : optionalPaths),
|
|
38
|
+
optional: definition.visibility === "optional"
|
|
39
|
+
};
|
|
40
|
+
else if (value === true) children[key] = {
|
|
41
|
+
...location,
|
|
42
|
+
guards: optionalPaths.map((path) => ({
|
|
43
|
+
from,
|
|
44
|
+
path
|
|
45
|
+
})),
|
|
46
|
+
kind: "value",
|
|
47
|
+
source: "option",
|
|
48
|
+
definition
|
|
49
|
+
};
|
|
50
|
+
else throw new require_layout_configuration_error.LayoutConfigurationError(`Invalid reusable selection for "${key}".`);
|
|
51
|
+
}
|
|
52
|
+
const origins = /* @__PURE__ */ new Set();
|
|
53
|
+
function collectOrigins(node) {
|
|
54
|
+
if (node.kind === "value") origins.add(node.from);
|
|
55
|
+
else Object.values(node.children).forEach(collectOrigins);
|
|
56
|
+
}
|
|
57
|
+
Object.values(children).forEach(collectOrigins);
|
|
58
|
+
if (!origins.size) origins.add(from);
|
|
59
|
+
return objectPlan(children, false, [...origins].map((origin) => optionalPaths.map((path) => ({
|
|
60
|
+
from: origin,
|
|
61
|
+
path
|
|
62
|
+
}))));
|
|
63
|
+
}
|
|
64
|
+
function sameSelection(left, right) {
|
|
65
|
+
return left.source === right.source && left.selector === right.selector && left.path.join(".") === right.path.join(".") && (left.source === "option" || left.from === right.from);
|
|
66
|
+
}
|
|
67
|
+
function mergePlans(left, right, overlap, path = []) {
|
|
68
|
+
const children = { ...left.children };
|
|
69
|
+
for (const [key, other] of Object.entries(right.children)) {
|
|
70
|
+
const current = children[key];
|
|
71
|
+
if (!current) {
|
|
72
|
+
children[key] = other;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (current.kind === "object" && other.kind === "object") children[key] = mergePlans(current, other, overlap, [...path, key]);
|
|
76
|
+
else if (current.kind === "value" && other.kind === "value" && sameSelection(current, other)) children[key] = other.from === "options" ? other : current;
|
|
77
|
+
else if (overlap) children[key] = overlap === "preferProps" ? current : other;
|
|
78
|
+
else throw new require_layout_configuration_error.LayoutConfigurationError(`Ambiguous reusable property "${[...path, key].join(".")}". Rename one result or set overlap to "preferProps" or "preferSelect".`);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
kind: "object",
|
|
82
|
+
children,
|
|
83
|
+
optional: left.optional && right.optional,
|
|
84
|
+
guards: [...left.guards, ...right.guards]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function withoutSharedCallerValues(left, right) {
|
|
88
|
+
const children = { ...left.children };
|
|
89
|
+
for (const [key, other] of Object.entries(right.children)) {
|
|
90
|
+
const current = children[key];
|
|
91
|
+
if (current?.kind === "object" && other.kind === "object") children[key] = withoutSharedCallerValues(current, other);
|
|
92
|
+
else if (current?.kind === "value" && other.kind === "value" && sameSelection(current, other) && other.from === "options") delete children[key];
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
...left,
|
|
96
|
+
children
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function planDefinitions(plan, from) {
|
|
100
|
+
const definitions = {};
|
|
101
|
+
for (const [key, value] of Object.entries(plan.children)) if (value.kind === "value") {
|
|
102
|
+
if (value.from === from && value.definition) definitions[key] = value.definition;
|
|
103
|
+
} else {
|
|
104
|
+
const nested = planDefinitions(value, from);
|
|
105
|
+
if (Object.keys(nested).length) {
|
|
106
|
+
const definition = _jfdevelops_react_layout_validator.createProp.object(nested);
|
|
107
|
+
definitions[key] = value.optional ? definition.optional() : definition;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return definitions;
|
|
111
|
+
}
|
|
112
|
+
function sourceSelection(selection, callbacksOnly) {
|
|
113
|
+
if (typeof selection === "function") return true;
|
|
114
|
+
if (selection === true) return callbacksOnly ? {} : true;
|
|
115
|
+
const source = {};
|
|
116
|
+
for (const [key, value] of Object.entries(selection ?? {})) {
|
|
117
|
+
const nested = sourceSelection(value, callbacksOnly);
|
|
118
|
+
if (nested === true || Object.keys(nested).length) source[key] = nested;
|
|
119
|
+
}
|
|
120
|
+
return source;
|
|
121
|
+
}
|
|
122
|
+
function mergeSelections(left, right) {
|
|
123
|
+
if (left === true || right === true) return true;
|
|
124
|
+
const merged = { ...left };
|
|
125
|
+
for (const [key, value] of Object.entries(right)) merged[key] = Object.hasOwn(left, key) ? mergeSelections(left[key], value) : value;
|
|
126
|
+
return merged;
|
|
127
|
+
}
|
|
128
|
+
function readLocation(location, props, options, resources) {
|
|
129
|
+
let value = location.from === "props" ? props : location.from === "options" ? options : resources;
|
|
130
|
+
for (const key of location.path) value = value?.[key];
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
function resolvePlan(plan, props, options, resources) {
|
|
134
|
+
const result = {};
|
|
135
|
+
for (const [key, node] of Object.entries(plan.children)) if (node.kind === "object") {
|
|
136
|
+
if (node.guards.some((group) => group.every((location) => readLocation(location, props, options, resources) !== void 0))) result[key] = resolvePlan(node, props, options, resources);
|
|
137
|
+
} else {
|
|
138
|
+
if (node.guards?.some((location) => readLocation(location, props, options, resources) === void 0)) continue;
|
|
139
|
+
const value = readLocation(node, props, options, resources);
|
|
140
|
+
if (node.selector) result[key] = node.selector(value);
|
|
141
|
+
else if (value !== void 0 || node.definition?.visibility !== "optional") result[key] = value;
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
function createReusableBuilder(resources, options) {
|
|
146
|
+
const keys = resources.map((entry) => typeof entry === "string" ? entry : entry.value);
|
|
147
|
+
const resource = _jfdevelops_react_layout_validator.createProp.string.enum(keys);
|
|
148
|
+
const resourcesAccessor = require_define_layout.createLayoutResourcesAccessor(resources);
|
|
149
|
+
function createReusable(definition) {
|
|
150
|
+
const include = definition.props?.include ?? {};
|
|
151
|
+
const select = definition.select ?? {};
|
|
152
|
+
if (definition.overlap !== void 0 && definition.overlap !== "preferProps" && definition.overlap !== "preferSelect") throw new require_layout_configuration_error.LayoutConfigurationError("Unknown reusable overlap policy.");
|
|
153
|
+
const optionDefinitions = typeof options === "function" ? options({
|
|
154
|
+
name: _jfdevelops_react_layout_validator.createProp.string.literal("Reusable"),
|
|
155
|
+
resource
|
|
156
|
+
}) : options;
|
|
157
|
+
function compile(selection, from) {
|
|
158
|
+
let plan = objectPlan({});
|
|
159
|
+
for (const [key, value] of Object.entries(selection)) {
|
|
160
|
+
let next;
|
|
161
|
+
if (key === "options") {
|
|
162
|
+
if (value !== true && typeof value !== "function" && (!value || typeof value !== "object" || Array.isArray(value))) throw new require_layout_configuration_error.LayoutConfigurationError("Invalid reusable options selection.");
|
|
163
|
+
next = typeof value === "function" ? objectPlan({ options: {
|
|
164
|
+
kind: "value",
|
|
165
|
+
source: "option",
|
|
166
|
+
from: "options",
|
|
167
|
+
path: [],
|
|
168
|
+
selector: value
|
|
169
|
+
} }) : optionPlan(optionDefinitions, value, from);
|
|
170
|
+
} else if (key === "resources" && (value === true || typeof value === "function")) next = objectPlan({ resources: {
|
|
171
|
+
kind: "value",
|
|
172
|
+
source: "resource",
|
|
173
|
+
from: value === true && from === "props" ? "props" : "resources",
|
|
174
|
+
path: value === true && from === "props" ? ["resources"] : [],
|
|
175
|
+
selector: typeof value === "function" ? value : void 0,
|
|
176
|
+
definition: value === true && from === "props" ? _jfdevelops_react_layout_validator.createProp.array(resource) : void 0
|
|
177
|
+
} });
|
|
178
|
+
else throw new require_layout_configuration_error.LayoutConfigurationError(`Invalid reusable source "${key}".`);
|
|
179
|
+
plan = mergePlans(plan, next);
|
|
180
|
+
}
|
|
181
|
+
return plan;
|
|
182
|
+
}
|
|
183
|
+
let included = compile(include, "props");
|
|
184
|
+
const custom = objectPlan(Object.fromEntries(Object.entries(definition.props?.custom ?? {}).map(([key, value]) => [key, {
|
|
185
|
+
kind: "value",
|
|
186
|
+
source: "custom",
|
|
187
|
+
from: "props",
|
|
188
|
+
path: [key],
|
|
189
|
+
definition: value
|
|
190
|
+
}])));
|
|
191
|
+
included = mergePlans(included, custom);
|
|
192
|
+
const selected = compile(select, "options");
|
|
193
|
+
const plan = mergePlans(included, selected, definition.overlap);
|
|
194
|
+
const callerDefinitions = planDefinitions(withoutSharedCallerValues(included, selected), "props");
|
|
195
|
+
const effectiveCallerDefinitions = planDefinitions(plan, "props");
|
|
196
|
+
const sources = mergeSelections(sourceSelection(include.options, true), sourceSelection(select.options, false));
|
|
197
|
+
const sourceDefinitions = sources === true ? optionDefinitions : planDefinitions(optionPlan(optionDefinitions, sources, "options"), "options");
|
|
198
|
+
const needsOptions = Object.keys(sourceDefinitions).length > 0;
|
|
199
|
+
if (needsOptions && Object.hasOwn(callerDefinitions, "options")) throw new require_layout_configuration_error.LayoutConfigurationError("Reusable prop \"options\" is reserved for source values.");
|
|
200
|
+
return function reusable(props = {}) {
|
|
201
|
+
(0, _jfdevelops_react_layout_validator.validateProps)(definition.overlap ? callerDefinitions : effectiveCallerDefinitions, props);
|
|
202
|
+
if (needsOptions && props.options !== void 0 && (props.options === null || typeof props.options !== "object" || Array.isArray(props.options))) throw new require_layout_configuration_error.LayoutConfigurationError("Reusable source options must be an object.");
|
|
203
|
+
const values = props.options ?? {};
|
|
204
|
+
(0, _jfdevelops_react_layout_validator.validateProps)(sourceDefinitions, values);
|
|
205
|
+
return definition.fn(resolvePlan(plan, props, values, resourcesAccessor));
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return createReusable;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
exports.createReusableBuilder = createReusableBuilder;
|
|
213
|
+
//# sourceMappingURL=create-reusable.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-reusable.cjs","names":["LayoutConfigurationError","createProp","createLayoutResourcesAccessor"],"sources":["../../src/create-config/create-reusable.ts"],"sourcesContent":["import {\n createProp,\n validateProps,\n type AnyBuiltPropDefinition,\n} from '@jfdevelops/react-layout-validator';\nimport type { InPropsDefinition, InPropsObject, PropsContract } from '../props';\nimport type { ResourceDefinition } from '../resource';\nimport { LayoutConfigurationError } from '../errors';\nimport { createLayoutResourcesAccessor } from './define-layout';\nimport type { CreateReusable, ReusableOverlap } from './reusable-types';\nexport type * from './reusable-types';\n\ntype RuntimeSelection = Record<string, unknown>;\ntype PlanLocation = { from: 'props' | 'options' | 'resources'; path: string[] };\ntype ValuePlan = PlanLocation & {\n kind: 'value';\n // Optional ancestors remain tied to the value's original source after merging.\n guards?: PlanLocation[];\n source: 'option' | 'resource' | 'custom';\n selector?: (value: any) => unknown;\n definition?: AnyBuiltPropDefinition;\n};\ntype ObjectPlan = {\n kind: 'object';\n children: Record<string, Plan>;\n optional: boolean;\n guards: PlanLocation[][];\n};\ntype Plan = ValuePlan | ObjectPlan;\n\nfunction objectPlan(\n children: Record<string, Plan>,\n optional = false,\n guards: PlanLocation[][] = [[]],\n): ObjectPlan {\n return { kind: 'object', children, optional, guards };\n}\n\nfunction optionPlan(\n definitions: InPropsObject,\n selection: unknown,\n from: 'props' | 'options',\n path: string[] = [],\n optionalPaths: string[][] = [],\n): ObjectPlan {\n const entries =\n selection === true\n ? Object.fromEntries(Object.keys(definitions).map((key) => [key, true]))\n : (selection as RuntimeSelection);\n const children: Record<string, Plan> = {};\n for (const [key, value] of Object.entries(entries ?? {})) {\n if (!Object.hasOwn(definitions, key))\n throw new LayoutConfigurationError(`Unknown reusable option \"${key}\".`);\n const definition = definitions[key];\n const location: PlanLocation = { from, path: [...path, key] };\n if (typeof value === 'function') {\n children[key] = {\n ...location,\n from: 'options',\n guards: optionalPaths.map((path) => ({ from: 'options', path })),\n kind: 'value',\n source: 'option',\n selector: value as ValuePlan['selector'],\n definition,\n };\n } else if (\n 'properties' in definition &&\n 'type' in definition &&\n definition.type === 'object' &&\n (value === true || (value && typeof value === 'object'))\n ) {\n const nested = optionPlan(\n definition.properties as InPropsObject,\n value,\n from,\n location.path,\n definition.visibility === 'optional'\n ? [...optionalPaths, location.path]\n : optionalPaths,\n );\n children[key] = {\n ...nested,\n optional: definition.visibility === 'optional',\n };\n } else if (value === true) {\n children[key] = {\n ...location,\n guards: optionalPaths.map((path) => ({ from, path })),\n kind: 'value',\n source: 'option',\n definition,\n };\n } else\n throw new LayoutConfigurationError(\n `Invalid reusable selection for \"${key}\".`,\n );\n }\n const origins = new Set<PlanLocation['from']>();\n function collectOrigins(node: Plan) {\n if (node.kind === 'value') origins.add(node.from);\n else Object.values(node.children).forEach(collectOrigins);\n }\n Object.values(children).forEach(collectOrigins);\n if (!origins.size) origins.add(from);\n return objectPlan(\n children,\n false,\n [...origins].map((origin) =>\n optionalPaths.map((path) => ({ from: origin, path })),\n ),\n );\n}\n\nfunction sameSelection(left: ValuePlan, right: ValuePlan) {\n return (\n left.source === right.source &&\n left.selector === right.selector &&\n left.path.join('.') === right.path.join('.') &&\n (left.source === 'option' || left.from === right.from)\n );\n}\n\nfunction mergePlans(\n left: ObjectPlan,\n right: ObjectPlan,\n overlap?: ReusableOverlap,\n path: string[] = [],\n): ObjectPlan {\n const children = { ...left.children };\n for (const [key, other] of Object.entries(right.children)) {\n const current = children[key];\n if (!current) {\n children[key] = other;\n continue;\n }\n if (current.kind === 'object' && other.kind === 'object') {\n children[key] = mergePlans(current, other, overlap, [...path, key]);\n } else if (\n current.kind === 'value' &&\n other.kind === 'value' &&\n sameSelection(current, other)\n ) {\n // A shared option selection uses one source value and executes only once.\n children[key] = other.from === 'options' ? other : current;\n } else if (overlap)\n children[key] = overlap === 'preferProps' ? current : other;\n else\n throw new LayoutConfigurationError(\n `Ambiguous reusable property \"${[...path, key].join(\n '.',\n )}\". Rename one result or set overlap to \"preferProps\" or \"preferSelect\".`,\n );\n }\n return {\n kind: 'object',\n children,\n optional: left.optional && right.optional,\n guards: [...left.guards, ...right.guards],\n };\n}\n\nfunction withoutSharedCallerValues(\n left: ObjectPlan,\n right: ObjectPlan,\n): ObjectPlan {\n const children = { ...left.children };\n for (const [key, other] of Object.entries(right.children)) {\n const current = children[key];\n if (current?.kind === 'object' && other.kind === 'object')\n children[key] = withoutSharedCallerValues(current, other);\n else if (\n current?.kind === 'value' &&\n other.kind === 'value' &&\n sameSelection(current, other) &&\n other.from === 'options'\n )\n delete children[key];\n }\n return { ...left, children };\n}\n\nfunction planDefinitions(\n plan: ObjectPlan,\n from: 'props' | 'options',\n): InPropsObject {\n const definitions: InPropsObject = {};\n for (const [key, value] of Object.entries(plan.children)) {\n if (value.kind === 'value') {\n if (value.from === from && value.definition)\n definitions[key] = value.definition;\n } else {\n const nested = planDefinitions(value, from);\n if (Object.keys(nested).length) {\n const definition = createProp.object(nested);\n definitions[key] = value.optional ? definition.optional() : definition;\n }\n }\n }\n return definitions;\n}\n\nfunction sourceSelection(\n selection: unknown,\n callbacksOnly: boolean,\n): true | RuntimeSelection {\n if (typeof selection === 'function') return true;\n if (selection === true) return callbacksOnly ? {} : true;\n const source: RuntimeSelection = {};\n for (const [key, value] of Object.entries(\n (selection ?? {}) as RuntimeSelection,\n )) {\n const nested = sourceSelection(value, callbacksOnly);\n if (nested === true || Object.keys(nested).length) source[key] = nested;\n }\n return source;\n}\n\nfunction mergeSelections(\n left: true | RuntimeSelection,\n right: true | RuntimeSelection,\n): true | RuntimeSelection {\n if (left === true || right === true) return true;\n const merged = { ...left };\n for (const [key, value] of Object.entries(right))\n merged[key] = Object.hasOwn(left, key)\n ? mergeSelections(\n left[key] as true | RuntimeSelection,\n value as true | RuntimeSelection,\n )\n : value;\n return merged;\n}\n\nfunction readLocation(\n location: PlanLocation,\n props: Record<string, unknown>,\n options: Record<string, unknown>,\n resources: unknown,\n) {\n let value: any =\n location.from === 'props'\n ? props\n : location.from === 'options'\n ? options\n : resources;\n for (const key of location.path) value = value?.[key];\n return value;\n}\n\nfunction resolvePlan(\n plan: ObjectPlan,\n props: Record<string, unknown>,\n options: Record<string, unknown>,\n resources: unknown,\n) {\n const result: Record<string, unknown> = {};\n for (const [key, node] of Object.entries(plan.children)) {\n if (node.kind === 'object') {\n if (\n node.guards.some((group) =>\n group.every(\n (location) =>\n readLocation(location, props, options, resources) !== undefined,\n ),\n )\n ) {\n result[key] = resolvePlan(node, props, options, resources);\n }\n } else {\n if (\n node.guards?.some(\n (location) =>\n readLocation(location, props, options, resources) === undefined,\n )\n )\n continue;\n const value = readLocation(node, props, options, resources);\n if (node.selector) result[key] = node.selector(value);\n else if (\n value !== undefined ||\n node.definition?.visibility !== 'optional'\n )\n result[key] = value;\n }\n }\n return result;\n}\n\nexport function createReusableBuilder<\n const Resources extends readonly ResourceDefinition[],\n Options extends InPropsDefinition<Resources>,\n>(resources: Resources, options: Options): CreateReusable<Resources, Options>;\nexport function createReusableBuilder(\n resources: readonly ResourceDefinition[],\n options: InPropsObject | ((...args: any[]) => InPropsObject),\n) {\n const keys = resources.map((entry) =>\n typeof entry === 'string' ? entry : entry.value,\n );\n const resource = createProp.string.enum(keys as [string, ...string[]]);\n const resourcesAccessor = createLayoutResourcesAccessor(resources);\n\n function createReusable(definition: {\n props?: PropsContract<RuntimeSelection, InPropsObject>;\n select?: RuntimeSelection;\n overlap?: ReusableOverlap;\n fn: (props: Record<string, unknown>) => unknown;\n }) {\n const include = definition.props?.include ?? {};\n const select = definition.select ?? {};\n if (\n definition.overlap !== undefined &&\n definition.overlap !== 'preferProps' &&\n definition.overlap !== 'preferSelect'\n ) {\n throw new LayoutConfigurationError('Unknown reusable overlap policy.');\n }\n const optionDefinitions =\n typeof options === 'function'\n ? options({ name: createProp.string.literal('Reusable'), resource })\n : options;\n\n function compile(selection: RuntimeSelection, from: 'props' | 'options') {\n let plan = objectPlan({});\n for (const [key, value] of Object.entries(selection)) {\n let next: ObjectPlan;\n if (key === 'options') {\n if (\n value !== true &&\n typeof value !== 'function' &&\n (!value || typeof value !== 'object' || Array.isArray(value))\n ) {\n throw new LayoutConfigurationError(\n 'Invalid reusable options selection.',\n );\n }\n next =\n typeof value === 'function'\n ? objectPlan({\n options: {\n kind: 'value',\n source: 'option',\n from: 'options',\n path: [],\n selector: value as ValuePlan['selector'],\n },\n })\n : optionPlan(optionDefinitions, value, from);\n } else if (\n key === 'resources' &&\n (value === true || typeof value === 'function')\n ) {\n next = objectPlan({\n resources: {\n kind: 'value',\n source: 'resource',\n from: value === true && from === 'props' ? 'props' : 'resources',\n path: value === true && from === 'props' ? ['resources'] : [],\n selector:\n typeof value === 'function'\n ? (value as ValuePlan['selector'])\n : undefined,\n definition:\n value === true && from === 'props'\n ? createProp.array(resource)\n : undefined,\n },\n });\n } else\n throw new LayoutConfigurationError(\n `Invalid reusable source \"${key}\".`,\n );\n plan = mergePlans(plan, next);\n }\n return plan;\n }\n\n let included = compile(include, 'props');\n const custom = objectPlan(\n Object.fromEntries(\n Object.entries(definition.props?.custom ?? {}).map(([key, value]) => [\n key,\n {\n kind: 'value',\n source: 'custom',\n from: 'props',\n path: [key],\n definition: value,\n } as ValuePlan,\n ]),\n ),\n );\n included = mergePlans(included, custom);\n const selected = compile(select, 'options');\n const plan = mergePlans(included, selected, definition.overlap);\n const callerDefinitions = planDefinitions(\n withoutSharedCallerValues(included, selected),\n 'props',\n );\n // Identical true selections supplied by select are removed from the caller props contract.\n const effectiveCallerDefinitions = planDefinitions(plan, 'props');\n const sources = mergeSelections(\n sourceSelection(include.options, true),\n sourceSelection(select.options, false),\n );\n const sourceDefinitions =\n sources === true\n ? optionDefinitions\n : planDefinitions(\n optionPlan(optionDefinitions, sources, 'options'),\n 'options',\n );\n const needsOptions = Object.keys(sourceDefinitions).length > 0;\n if (needsOptions && Object.hasOwn(callerDefinitions, 'options')) {\n throw new LayoutConfigurationError(\n 'Reusable prop \"options\" is reserved for source values.',\n );\n }\n\n return function reusable(props: Record<string, unknown> = {}) {\n // Validate configured caller props too when an explicit overlap policy discards their output.\n const definitions = definition.overlap\n ? callerDefinitions\n : effectiveCallerDefinitions;\n validateProps(definitions, props);\n if (\n needsOptions &&\n props.options !== undefined &&\n (props.options === null ||\n typeof props.options !== 'object' ||\n Array.isArray(props.options))\n ) {\n throw new LayoutConfigurationError(\n 'Reusable source options must be an object.',\n );\n }\n const values = (props.options ?? {}) as Record<string, unknown>;\n validateProps(sourceDefinitions, values);\n return definition.fn(resolvePlan(plan, props, values, resourcesAccessor));\n };\n }\n return createReusable as unknown;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { CreateReusable, ReusableArguments, ReusableDefinition, ReusableFunction, ReusableInclude, ReusableInput, ReusableOptionSelection, ReusableOverlap, ReusableProps, ReusableResolved, ReusableSelected } from "./reusable-types.cjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { CreateReusable, ReusableArguments, ReusableDefinition, ReusableFunction, ReusableInclude, ReusableInput, ReusableOptionSelection, ReusableOverlap, ReusableProps, ReusableResolved, ReusableSelected } from "./reusable-types.mjs";
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { LayoutConfigurationError } from "../errors/layout-configuration-error.mjs";
|
|
2
|
+
import { createLayoutResourcesAccessor } from "./define-layout.mjs";
|
|
3
|
+
import { createProp, validateProps } from "@jfdevelops/react-layout-validator";
|
|
4
|
+
|
|
5
|
+
//#region src/create-config/create-reusable.ts
|
|
6
|
+
function objectPlan(children, optional = false, guards = [[]]) {
|
|
7
|
+
return {
|
|
8
|
+
kind: "object",
|
|
9
|
+
children,
|
|
10
|
+
optional,
|
|
11
|
+
guards
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function optionPlan(definitions, selection, from, path = [], optionalPaths = []) {
|
|
15
|
+
const entries = selection === true ? Object.fromEntries(Object.keys(definitions).map((key) => [key, true])) : selection;
|
|
16
|
+
const children = {};
|
|
17
|
+
for (const [key, value] of Object.entries(entries ?? {})) {
|
|
18
|
+
if (!Object.hasOwn(definitions, key)) throw new LayoutConfigurationError(`Unknown reusable option "${key}".`);
|
|
19
|
+
const definition = definitions[key];
|
|
20
|
+
const location = {
|
|
21
|
+
from,
|
|
22
|
+
path: [...path, key]
|
|
23
|
+
};
|
|
24
|
+
if (typeof value === "function") children[key] = {
|
|
25
|
+
...location,
|
|
26
|
+
from: "options",
|
|
27
|
+
guards: optionalPaths.map((path) => ({
|
|
28
|
+
from: "options",
|
|
29
|
+
path
|
|
30
|
+
})),
|
|
31
|
+
kind: "value",
|
|
32
|
+
source: "option",
|
|
33
|
+
selector: value,
|
|
34
|
+
definition
|
|
35
|
+
};
|
|
36
|
+
else if ("properties" in definition && "type" in definition && definition.type === "object" && (value === true || value && typeof value === "object")) children[key] = {
|
|
37
|
+
...optionPlan(definition.properties, value, from, location.path, definition.visibility === "optional" ? [...optionalPaths, location.path] : optionalPaths),
|
|
38
|
+
optional: definition.visibility === "optional"
|
|
39
|
+
};
|
|
40
|
+
else if (value === true) children[key] = {
|
|
41
|
+
...location,
|
|
42
|
+
guards: optionalPaths.map((path) => ({
|
|
43
|
+
from,
|
|
44
|
+
path
|
|
45
|
+
})),
|
|
46
|
+
kind: "value",
|
|
47
|
+
source: "option",
|
|
48
|
+
definition
|
|
49
|
+
};
|
|
50
|
+
else throw new LayoutConfigurationError(`Invalid reusable selection for "${key}".`);
|
|
51
|
+
}
|
|
52
|
+
const origins = /* @__PURE__ */ new Set();
|
|
53
|
+
function collectOrigins(node) {
|
|
54
|
+
if (node.kind === "value") origins.add(node.from);
|
|
55
|
+
else Object.values(node.children).forEach(collectOrigins);
|
|
56
|
+
}
|
|
57
|
+
Object.values(children).forEach(collectOrigins);
|
|
58
|
+
if (!origins.size) origins.add(from);
|
|
59
|
+
return objectPlan(children, false, [...origins].map((origin) => optionalPaths.map((path) => ({
|
|
60
|
+
from: origin,
|
|
61
|
+
path
|
|
62
|
+
}))));
|
|
63
|
+
}
|
|
64
|
+
function sameSelection(left, right) {
|
|
65
|
+
return left.source === right.source && left.selector === right.selector && left.path.join(".") === right.path.join(".") && (left.source === "option" || left.from === right.from);
|
|
66
|
+
}
|
|
67
|
+
function mergePlans(left, right, overlap, path = []) {
|
|
68
|
+
const children = { ...left.children };
|
|
69
|
+
for (const [key, other] of Object.entries(right.children)) {
|
|
70
|
+
const current = children[key];
|
|
71
|
+
if (!current) {
|
|
72
|
+
children[key] = other;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (current.kind === "object" && other.kind === "object") children[key] = mergePlans(current, other, overlap, [...path, key]);
|
|
76
|
+
else if (current.kind === "value" && other.kind === "value" && sameSelection(current, other)) children[key] = other.from === "options" ? other : current;
|
|
77
|
+
else if (overlap) children[key] = overlap === "preferProps" ? current : other;
|
|
78
|
+
else throw new LayoutConfigurationError(`Ambiguous reusable property "${[...path, key].join(".")}". Rename one result or set overlap to "preferProps" or "preferSelect".`);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
kind: "object",
|
|
82
|
+
children,
|
|
83
|
+
optional: left.optional && right.optional,
|
|
84
|
+
guards: [...left.guards, ...right.guards]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function withoutSharedCallerValues(left, right) {
|
|
88
|
+
const children = { ...left.children };
|
|
89
|
+
for (const [key, other] of Object.entries(right.children)) {
|
|
90
|
+
const current = children[key];
|
|
91
|
+
if (current?.kind === "object" && other.kind === "object") children[key] = withoutSharedCallerValues(current, other);
|
|
92
|
+
else if (current?.kind === "value" && other.kind === "value" && sameSelection(current, other) && other.from === "options") delete children[key];
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
...left,
|
|
96
|
+
children
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function planDefinitions(plan, from) {
|
|
100
|
+
const definitions = {};
|
|
101
|
+
for (const [key, value] of Object.entries(plan.children)) if (value.kind === "value") {
|
|
102
|
+
if (value.from === from && value.definition) definitions[key] = value.definition;
|
|
103
|
+
} else {
|
|
104
|
+
const nested = planDefinitions(value, from);
|
|
105
|
+
if (Object.keys(nested).length) {
|
|
106
|
+
const definition = createProp.object(nested);
|
|
107
|
+
definitions[key] = value.optional ? definition.optional() : definition;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return definitions;
|
|
111
|
+
}
|
|
112
|
+
function sourceSelection(selection, callbacksOnly) {
|
|
113
|
+
if (typeof selection === "function") return true;
|
|
114
|
+
if (selection === true) return callbacksOnly ? {} : true;
|
|
115
|
+
const source = {};
|
|
116
|
+
for (const [key, value] of Object.entries(selection ?? {})) {
|
|
117
|
+
const nested = sourceSelection(value, callbacksOnly);
|
|
118
|
+
if (nested === true || Object.keys(nested).length) source[key] = nested;
|
|
119
|
+
}
|
|
120
|
+
return source;
|
|
121
|
+
}
|
|
122
|
+
function mergeSelections(left, right) {
|
|
123
|
+
if (left === true || right === true) return true;
|
|
124
|
+
const merged = { ...left };
|
|
125
|
+
for (const [key, value] of Object.entries(right)) merged[key] = Object.hasOwn(left, key) ? mergeSelections(left[key], value) : value;
|
|
126
|
+
return merged;
|
|
127
|
+
}
|
|
128
|
+
function readLocation(location, props, options, resources) {
|
|
129
|
+
let value = location.from === "props" ? props : location.from === "options" ? options : resources;
|
|
130
|
+
for (const key of location.path) value = value?.[key];
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
function resolvePlan(plan, props, options, resources) {
|
|
134
|
+
const result = {};
|
|
135
|
+
for (const [key, node] of Object.entries(plan.children)) if (node.kind === "object") {
|
|
136
|
+
if (node.guards.some((group) => group.every((location) => readLocation(location, props, options, resources) !== void 0))) result[key] = resolvePlan(node, props, options, resources);
|
|
137
|
+
} else {
|
|
138
|
+
if (node.guards?.some((location) => readLocation(location, props, options, resources) === void 0)) continue;
|
|
139
|
+
const value = readLocation(node, props, options, resources);
|
|
140
|
+
if (node.selector) result[key] = node.selector(value);
|
|
141
|
+
else if (value !== void 0 || node.definition?.visibility !== "optional") result[key] = value;
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
function createReusableBuilder(resources, options) {
|
|
146
|
+
const keys = resources.map((entry) => typeof entry === "string" ? entry : entry.value);
|
|
147
|
+
const resource = createProp.string.enum(keys);
|
|
148
|
+
const resourcesAccessor = createLayoutResourcesAccessor(resources);
|
|
149
|
+
function createReusable(definition) {
|
|
150
|
+
const include = definition.props?.include ?? {};
|
|
151
|
+
const select = definition.select ?? {};
|
|
152
|
+
if (definition.overlap !== void 0 && definition.overlap !== "preferProps" && definition.overlap !== "preferSelect") throw new LayoutConfigurationError("Unknown reusable overlap policy.");
|
|
153
|
+
const optionDefinitions = typeof options === "function" ? options({
|
|
154
|
+
name: createProp.string.literal("Reusable"),
|
|
155
|
+
resource
|
|
156
|
+
}) : options;
|
|
157
|
+
function compile(selection, from) {
|
|
158
|
+
let plan = objectPlan({});
|
|
159
|
+
for (const [key, value] of Object.entries(selection)) {
|
|
160
|
+
let next;
|
|
161
|
+
if (key === "options") {
|
|
162
|
+
if (value !== true && typeof value !== "function" && (!value || typeof value !== "object" || Array.isArray(value))) throw new LayoutConfigurationError("Invalid reusable options selection.");
|
|
163
|
+
next = typeof value === "function" ? objectPlan({ options: {
|
|
164
|
+
kind: "value",
|
|
165
|
+
source: "option",
|
|
166
|
+
from: "options",
|
|
167
|
+
path: [],
|
|
168
|
+
selector: value
|
|
169
|
+
} }) : optionPlan(optionDefinitions, value, from);
|
|
170
|
+
} else if (key === "resources" && (value === true || typeof value === "function")) next = objectPlan({ resources: {
|
|
171
|
+
kind: "value",
|
|
172
|
+
source: "resource",
|
|
173
|
+
from: value === true && from === "props" ? "props" : "resources",
|
|
174
|
+
path: value === true && from === "props" ? ["resources"] : [],
|
|
175
|
+
selector: typeof value === "function" ? value : void 0,
|
|
176
|
+
definition: value === true && from === "props" ? createProp.array(resource) : void 0
|
|
177
|
+
} });
|
|
178
|
+
else throw new LayoutConfigurationError(`Invalid reusable source "${key}".`);
|
|
179
|
+
plan = mergePlans(plan, next);
|
|
180
|
+
}
|
|
181
|
+
return plan;
|
|
182
|
+
}
|
|
183
|
+
let included = compile(include, "props");
|
|
184
|
+
const custom = objectPlan(Object.fromEntries(Object.entries(definition.props?.custom ?? {}).map(([key, value]) => [key, {
|
|
185
|
+
kind: "value",
|
|
186
|
+
source: "custom",
|
|
187
|
+
from: "props",
|
|
188
|
+
path: [key],
|
|
189
|
+
definition: value
|
|
190
|
+
}])));
|
|
191
|
+
included = mergePlans(included, custom);
|
|
192
|
+
const selected = compile(select, "options");
|
|
193
|
+
const plan = mergePlans(included, selected, definition.overlap);
|
|
194
|
+
const callerDefinitions = planDefinitions(withoutSharedCallerValues(included, selected), "props");
|
|
195
|
+
const effectiveCallerDefinitions = planDefinitions(plan, "props");
|
|
196
|
+
const sources = mergeSelections(sourceSelection(include.options, true), sourceSelection(select.options, false));
|
|
197
|
+
const sourceDefinitions = sources === true ? optionDefinitions : planDefinitions(optionPlan(optionDefinitions, sources, "options"), "options");
|
|
198
|
+
const needsOptions = Object.keys(sourceDefinitions).length > 0;
|
|
199
|
+
if (needsOptions && Object.hasOwn(callerDefinitions, "options")) throw new LayoutConfigurationError("Reusable prop \"options\" is reserved for source values.");
|
|
200
|
+
return function reusable(props = {}) {
|
|
201
|
+
validateProps(definition.overlap ? callerDefinitions : effectiveCallerDefinitions, props);
|
|
202
|
+
if (needsOptions && props.options !== void 0 && (props.options === null || typeof props.options !== "object" || Array.isArray(props.options))) throw new LayoutConfigurationError("Reusable source options must be an object.");
|
|
203
|
+
const values = props.options ?? {};
|
|
204
|
+
validateProps(sourceDefinitions, values);
|
|
205
|
+
return definition.fn(resolvePlan(plan, props, values, resourcesAccessor));
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return createReusable;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
export { createReusableBuilder };
|
|
213
|
+
//# sourceMappingURL=create-reusable.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-reusable.mjs","names":[],"sources":["../../src/create-config/create-reusable.ts"],"sourcesContent":["import {\n createProp,\n validateProps,\n type AnyBuiltPropDefinition,\n} from '@jfdevelops/react-layout-validator';\nimport type { InPropsDefinition, InPropsObject, PropsContract } from '../props';\nimport type { ResourceDefinition } from '../resource';\nimport { LayoutConfigurationError } from '../errors';\nimport { createLayoutResourcesAccessor } from './define-layout';\nimport type { CreateReusable, ReusableOverlap } from './reusable-types';\nexport type * from './reusable-types';\n\ntype RuntimeSelection = Record<string, unknown>;\ntype PlanLocation = { from: 'props' | 'options' | 'resources'; path: string[] };\ntype ValuePlan = PlanLocation & {\n kind: 'value';\n // Optional ancestors remain tied to the value's original source after merging.\n guards?: PlanLocation[];\n source: 'option' | 'resource' | 'custom';\n selector?: (value: any) => unknown;\n definition?: AnyBuiltPropDefinition;\n};\ntype ObjectPlan = {\n kind: 'object';\n children: Record<string, Plan>;\n optional: boolean;\n guards: PlanLocation[][];\n};\ntype Plan = ValuePlan | ObjectPlan;\n\nfunction objectPlan(\n children: Record<string, Plan>,\n optional = false,\n guards: PlanLocation[][] = [[]],\n): ObjectPlan {\n return { kind: 'object', children, optional, guards };\n}\n\nfunction optionPlan(\n definitions: InPropsObject,\n selection: unknown,\n from: 'props' | 'options',\n path: string[] = [],\n optionalPaths: string[][] = [],\n): ObjectPlan {\n const entries =\n selection === true\n ? Object.fromEntries(Object.keys(definitions).map((key) => [key, true]))\n : (selection as RuntimeSelection);\n const children: Record<string, Plan> = {};\n for (const [key, value] of Object.entries(entries ?? {})) {\n if (!Object.hasOwn(definitions, key))\n throw new LayoutConfigurationError(`Unknown reusable option \"${key}\".`);\n const definition = definitions[key];\n const location: PlanLocation = { from, path: [...path, key] };\n if (typeof value === 'function') {\n children[key] = {\n ...location,\n from: 'options',\n guards: optionalPaths.map((path) => ({ from: 'options', path })),\n kind: 'value',\n source: 'option',\n selector: value as ValuePlan['selector'],\n definition,\n };\n } else if (\n 'properties' in definition &&\n 'type' in definition &&\n definition.type === 'object' &&\n (value === true || (value && typeof value === 'object'))\n ) {\n const nested = optionPlan(\n definition.properties as InPropsObject,\n value,\n from,\n location.path,\n definition.visibility === 'optional'\n ? [...optionalPaths, location.path]\n : optionalPaths,\n );\n children[key] = {\n ...nested,\n optional: definition.visibility === 'optional',\n };\n } else if (value === true) {\n children[key] = {\n ...location,\n guards: optionalPaths.map((path) => ({ from, path })),\n kind: 'value',\n source: 'option',\n definition,\n };\n } else\n throw new LayoutConfigurationError(\n `Invalid reusable selection for \"${key}\".`,\n );\n }\n const origins = new Set<PlanLocation['from']>();\n function collectOrigins(node: Plan) {\n if (node.kind === 'value') origins.add(node.from);\n else Object.values(node.children).forEach(collectOrigins);\n }\n Object.values(children).forEach(collectOrigins);\n if (!origins.size) origins.add(from);\n return objectPlan(\n children,\n false,\n [...origins].map((origin) =>\n optionalPaths.map((path) => ({ from: origin, path })),\n ),\n );\n}\n\nfunction sameSelection(left: ValuePlan, right: ValuePlan) {\n return (\n left.source === right.source &&\n left.selector === right.selector &&\n left.path.join('.') === right.path.join('.') &&\n (left.source === 'option' || left.from === right.from)\n );\n}\n\nfunction mergePlans(\n left: ObjectPlan,\n right: ObjectPlan,\n overlap?: ReusableOverlap,\n path: string[] = [],\n): ObjectPlan {\n const children = { ...left.children };\n for (const [key, other] of Object.entries(right.children)) {\n const current = children[key];\n if (!current) {\n children[key] = other;\n continue;\n }\n if (current.kind === 'object' && other.kind === 'object') {\n children[key] = mergePlans(current, other, overlap, [...path, key]);\n } else if (\n current.kind === 'value' &&\n other.kind === 'value' &&\n sameSelection(current, other)\n ) {\n // A shared option selection uses one source value and executes only once.\n children[key] = other.from === 'options' ? other : current;\n } else if (overlap)\n children[key] = overlap === 'preferProps' ? current : other;\n else\n throw new LayoutConfigurationError(\n `Ambiguous reusable property \"${[...path, key].join(\n '.',\n )}\". Rename one result or set overlap to \"preferProps\" or \"preferSelect\".`,\n );\n }\n return {\n kind: 'object',\n children,\n optional: left.optional && right.optional,\n guards: [...left.guards, ...right.guards],\n };\n}\n\nfunction withoutSharedCallerValues(\n left: ObjectPlan,\n right: ObjectPlan,\n): ObjectPlan {\n const children = { ...left.children };\n for (const [key, other] of Object.entries(right.children)) {\n const current = children[key];\n if (current?.kind === 'object' && other.kind === 'object')\n children[key] = withoutSharedCallerValues(current, other);\n else if (\n current?.kind === 'value' &&\n other.kind === 'value' &&\n sameSelection(current, other) &&\n other.from === 'options'\n )\n delete children[key];\n }\n return { ...left, children };\n}\n\nfunction planDefinitions(\n plan: ObjectPlan,\n from: 'props' | 'options',\n): InPropsObject {\n const definitions: InPropsObject = {};\n for (const [key, value] of Object.entries(plan.children)) {\n if (value.kind === 'value') {\n if (value.from === from && value.definition)\n definitions[key] = value.definition;\n } else {\n const nested = planDefinitions(value, from);\n if (Object.keys(nested).length) {\n const definition = createProp.object(nested);\n definitions[key] = value.optional ? definition.optional() : definition;\n }\n }\n }\n return definitions;\n}\n\nfunction sourceSelection(\n selection: unknown,\n callbacksOnly: boolean,\n): true | RuntimeSelection {\n if (typeof selection === 'function') return true;\n if (selection === true) return callbacksOnly ? {} : true;\n const source: RuntimeSelection = {};\n for (const [key, value] of Object.entries(\n (selection ?? {}) as RuntimeSelection,\n )) {\n const nested = sourceSelection(value, callbacksOnly);\n if (nested === true || Object.keys(nested).length) source[key] = nested;\n }\n return source;\n}\n\nfunction mergeSelections(\n left: true | RuntimeSelection,\n right: true | RuntimeSelection,\n): true | RuntimeSelection {\n if (left === true || right === true) return true;\n const merged = { ...left };\n for (const [key, value] of Object.entries(right))\n merged[key] = Object.hasOwn(left, key)\n ? mergeSelections(\n left[key] as true | RuntimeSelection,\n value as true | RuntimeSelection,\n )\n : value;\n return merged;\n}\n\nfunction readLocation(\n location: PlanLocation,\n props: Record<string, unknown>,\n options: Record<string, unknown>,\n resources: unknown,\n) {\n let value: any =\n location.from === 'props'\n ? props\n : location.from === 'options'\n ? options\n : resources;\n for (const key of location.path) value = value?.[key];\n return value;\n}\n\nfunction resolvePlan(\n plan: ObjectPlan,\n props: Record<string, unknown>,\n options: Record<string, unknown>,\n resources: unknown,\n) {\n const result: Record<string, unknown> = {};\n for (const [key, node] of Object.entries(plan.children)) {\n if (node.kind === 'object') {\n if (\n node.guards.some((group) =>\n group.every(\n (location) =>\n readLocation(location, props, options, resources) !== undefined,\n ),\n )\n ) {\n result[key] = resolvePlan(node, props, options, resources);\n }\n } else {\n if (\n node.guards?.some(\n (location) =>\n readLocation(location, props, options, resources) === undefined,\n )\n )\n continue;\n const value = readLocation(node, props, options, resources);\n if (node.selector) result[key] = node.selector(value);\n else if (\n value !== undefined ||\n node.definition?.visibility !== 'optional'\n )\n result[key] = value;\n }\n }\n return result;\n}\n\nexport function createReusableBuilder<\n const Resources extends readonly ResourceDefinition[],\n Options extends InPropsDefinition<Resources>,\n>(resources: Resources, options: Options): CreateReusable<Resources, Options>;\nexport function createReusableBuilder(\n resources: readonly ResourceDefinition[],\n options: InPropsObject | ((...args: any[]) => InPropsObject),\n) {\n const keys = resources.map((entry) =>\n typeof entry === 'string' ? entry : entry.value,\n );\n const resource = createProp.string.enum(keys as [string, ...string[]]);\n const resourcesAccessor = createLayoutResourcesAccessor(resources);\n\n function createReusable(definition: {\n props?: PropsContract<RuntimeSelection, InPropsObject>;\n select?: RuntimeSelection;\n overlap?: ReusableOverlap;\n fn: (props: Record<string, unknown>) => unknown;\n }) {\n const include = definition.props?.include ?? {};\n const select = definition.select ?? {};\n if (\n definition.overlap !== undefined &&\n definition.overlap !== 'preferProps' &&\n definition.overlap !== 'preferSelect'\n ) {\n throw new LayoutConfigurationError('Unknown reusable overlap policy.');\n }\n const optionDefinitions =\n typeof options === 'function'\n ? options({ name: createProp.string.literal('Reusable'), resource })\n : options;\n\n function compile(selection: RuntimeSelection, from: 'props' | 'options') {\n let plan = objectPlan({});\n for (const [key, value] of Object.entries(selection)) {\n let next: ObjectPlan;\n if (key === 'options') {\n if (\n value !== true &&\n typeof value !== 'function' &&\n (!value || typeof value !== 'object' || Array.isArray(value))\n ) {\n throw new LayoutConfigurationError(\n 'Invalid reusable options selection.',\n );\n }\n next =\n typeof value === 'function'\n ? objectPlan({\n options: {\n kind: 'value',\n source: 'option',\n from: 'options',\n path: [],\n selector: value as ValuePlan['selector'],\n },\n })\n : optionPlan(optionDefinitions, value, from);\n } else if (\n key === 'resources' &&\n (value === true || typeof value === 'function')\n ) {\n next = objectPlan({\n resources: {\n kind: 'value',\n source: 'resource',\n from: value === true && from === 'props' ? 'props' : 'resources',\n path: value === true && from === 'props' ? ['resources'] : [],\n selector:\n typeof value === 'function'\n ? (value as ValuePlan['selector'])\n : undefined,\n definition:\n value === true && from === 'props'\n ? createProp.array(resource)\n : undefined,\n },\n });\n } else\n throw new LayoutConfigurationError(\n `Invalid reusable source \"${key}\".`,\n );\n plan = mergePlans(plan, next);\n }\n return plan;\n }\n\n let included = compile(include, 'props');\n const custom = objectPlan(\n Object.fromEntries(\n Object.entries(definition.props?.custom ?? {}).map(([key, value]) => [\n key,\n {\n kind: 'value',\n source: 'custom',\n from: 'props',\n path: [key],\n definition: value,\n } as ValuePlan,\n ]),\n ),\n );\n included = mergePlans(included, custom);\n const selected = compile(select, 'options');\n const plan = mergePlans(included, selected, definition.overlap);\n const callerDefinitions = planDefinitions(\n withoutSharedCallerValues(included, selected),\n 'props',\n );\n // Identical true selections supplied by select are removed from the caller props contract.\n const effectiveCallerDefinitions = planDefinitions(plan, 'props');\n const sources = mergeSelections(\n sourceSelection(include.options, true),\n sourceSelection(select.options, false),\n );\n const sourceDefinitions =\n sources === true\n ? optionDefinitions\n : planDefinitions(\n optionPlan(optionDefinitions, sources, 'options'),\n 'options',\n );\n const needsOptions = Object.keys(sourceDefinitions).length > 0;\n if (needsOptions && Object.hasOwn(callerDefinitions, 'options')) {\n throw new LayoutConfigurationError(\n 'Reusable prop \"options\" is reserved for source values.',\n );\n }\n\n return function reusable(props: Record<string, unknown> = {}) {\n // Validate configured caller props too when an explicit overlap policy discards their output.\n const definitions = definition.overlap\n ? callerDefinitions\n : effectiveCallerDefinitions;\n validateProps(definitions, props);\n if (\n needsOptions &&\n props.options !== undefined &&\n (props.options === null ||\n typeof props.options !== 'object' ||\n Array.isArray(props.options))\n ) {\n throw new LayoutConfigurationError(\n 'Reusable source options must be an object.',\n );\n }\n const values = (props.options ?? {}) as Record<string, unknown>;\n validateProps(sourceDefinitions, values);\n return definition.fn(resolvePlan(plan, props, values, resourcesAccessor));\n };\n }\n return createReusable as unknown;\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_invalid_config = require('../errors/invalid-config.cjs');
|
|
2
|
+
const require_missing_resource_provider = require('../errors/missing-resource-provider.cjs');
|
|
2
3
|
const require_layout_configuration_error = require('../errors/layout-configuration-error.cjs');
|
|
3
4
|
const require_resource_state_error = require('../errors/resource-state-error.cjs');
|
|
4
5
|
const require_lazy_resource_error = require('../errors/lazy-resource-error.cjs');
|
|
6
|
+
const require_custom_props = require('../custom-props.cjs');
|
|
7
|
+
const require_render_state_context = require('./render-state-context.cjs');
|
|
5
8
|
const require_lazy_boundary = require('../lazy/lazy-boundary.cjs');
|
|
6
9
|
const require_fallback_props = require('../lazy/fallback-props.cjs');
|
|
7
10
|
const require_types = require('../lazy/types.cjs');
|
|
8
|
-
const require_invalid_config = require('../errors/invalid-config.cjs');
|
|
9
|
-
const require_missing_resource_provider = require('../errors/missing-resource-provider.cjs');
|
|
10
11
|
const require_capitalize = require('../utils/capitalize.cjs');
|
|
11
12
|
const require_state_composable = require('./state-composable.cjs');
|
|
12
13
|
const require_merge_layout_props = require('./merge-layout-props.cjs');
|
|
@@ -23,10 +24,10 @@ const require_render_state = require('./render-state.cjs');
|
|
|
23
24
|
const require_create_for_resources = require('./for-resources/create-for-resources.cjs');
|
|
24
25
|
const require_shell_context = require('./shell-context.cjs');
|
|
25
26
|
let _jfdevelops_react_layout_composables = require("@jfdevelops/react-layout-composables");
|
|
27
|
+
let _jfdevelops_react_layout_validator = require("@jfdevelops/react-layout-validator");
|
|
26
28
|
let react = require("react");
|
|
27
29
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
28
30
|
let _jfdevelops_react_layout_devtools_core = require("@jfdevelops/react-layout-devtools-core");
|
|
29
|
-
let _jfdevelops_react_layout_validator = require("@jfdevelops/react-layout-validator");
|
|
30
31
|
|
|
31
32
|
//#region src/create-config/define-layout.tsx
|
|
32
33
|
const RenderStateRetryContext = (0, react.createContext)(void 0);
|
|
@@ -131,6 +132,7 @@ function resolveLayoutOptionDefaults(defaults, options) {
|
|
|
131
132
|
function optionalStateChildren(props) {
|
|
132
133
|
if (!props) return props;
|
|
133
134
|
const children = props.custom?.children;
|
|
135
|
+
const stateChildren = children && typeof children !== "string" && children.visibility === "required" && "optional" in children && typeof children.optional === "function" ? children.optional() : children;
|
|
134
136
|
const childrenInclude = props.include?.children;
|
|
135
137
|
return {
|
|
136
138
|
...props,
|
|
@@ -146,7 +148,7 @@ function optionalStateChildren(props) {
|
|
|
146
148
|
},
|
|
147
149
|
custom: props.custom && {
|
|
148
150
|
...props.custom,
|
|
149
|
-
...children ? { children:
|
|
151
|
+
...children ? { children: stateChildren } : {}
|
|
150
152
|
}
|
|
151
153
|
};
|
|
152
154
|
}
|
|
@@ -301,7 +303,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope, inheritedInspect
|
|
|
301
303
|
});
|
|
302
304
|
const render = defaultState.render;
|
|
303
305
|
const layoutProps = require_merge_layout_props.mergeLayoutProps(legacyLayoutProps, defaultState.props);
|
|
304
|
-
const customLayoutProps = layoutProps?.custom;
|
|
306
|
+
const customLayoutProps = require_custom_props.normalizeCustomProps(layoutProps?.custom ?? {});
|
|
305
307
|
const includeLayoutProps = layoutProps?.include;
|
|
306
308
|
const layoutOptionValues = layoutOptionProps;
|
|
307
309
|
const resolvedLayoutProps = { ...customLayoutProps };
|
|
@@ -666,7 +668,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope, inheritedInspect
|
|
|
666
668
|
for (const { props: presetPropDefinitions } of (0, _jfdevelops_react_layout_composables.collectComposablePresetEntries)(resolvedComposables)) Object.assign(resolvedInProps, presetPropDefinitions);
|
|
667
669
|
return {
|
|
668
670
|
...resolvedInProps,
|
|
669
|
-
...require_merge_layout_props.mergeLayoutProps(layout.props, defaultState?.props)?.custom
|
|
671
|
+
...require_custom_props.normalizeCustomProps(require_merge_layout_props.mergeLayoutProps(layout.props, defaultState?.props)?.custom ?? {})
|
|
670
672
|
};
|
|
671
673
|
}
|
|
672
674
|
const { createLayoutForResourceBuilder, forResource } = require_for_resource.createForResource({
|
|
@@ -747,7 +749,7 @@ function createResourceLayoutShellComponent(resources, inProps, shell, shellId)
|
|
|
747
749
|
}) : inProps);
|
|
748
750
|
const includeShellProps = shell.props?.include;
|
|
749
751
|
const includeShellPropKeys = Object.keys(includeShellProps ?? {});
|
|
750
|
-
const shellCustomDefinitions =
|
|
752
|
+
const shellCustomDefinitions = require_custom_props.normalizeCustomProps(shell.props?.custom ?? {});
|
|
751
753
|
function syncShellComposableDisplayNames(stable, resource) {
|
|
752
754
|
if (!shell.composables) return;
|
|
753
755
|
const named = (0, _jfdevelops_react_layout_composables.resolveLayoutComposables)(shell.composables, {
|
|
@@ -834,6 +836,7 @@ function createResourceLayoutShellComponent(resources, inProps, shell, shellId)
|
|
|
834
836
|
...validatedCustomProps,
|
|
835
837
|
...renderIncludedProps
|
|
836
838
|
};
|
|
839
|
+
if (typeof shell.props?.custom?.children !== "function") renderProps.children = outlet == null ? outlet : shellChildren;
|
|
837
840
|
const renderContext = {
|
|
838
841
|
composables: resolvedComposables,
|
|
839
842
|
resource,
|