@wp-typia/block-types 0.2.3 → 0.3.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/README.md +278 -15
- package/dist/block-editor/index.d.ts +7 -7
- package/dist/block-editor/index.js +7 -7
- package/dist/block-editor/style-attributes.d.ts +5 -5
- package/dist/blocks/bindings.d.ts +132 -0
- package/dist/blocks/bindings.js +598 -0
- package/dist/blocks/compatibility.d.ts +296 -0
- package/dist/blocks/compatibility.js +338 -0
- package/dist/blocks/index.d.ts +5 -2
- package/dist/blocks/index.js +5 -2
- package/dist/blocks/supports.d.ts +57 -5
- package/dist/blocks/supports.js +182 -0
- package/dist/blocks/variations.d.ts +84 -0
- package/dist/blocks/variations.js +298 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/package.json +18 -1
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import { DEFAULT_WORDPRESS_COMPATIBILITY_MIN_VERSION, evaluateWordPressBlockApiCompatibility, } from "./compatibility.js";
|
|
2
|
+
export const DEFINED_BLOCK_BINDING_SOURCE_METADATA = Symbol.for("@wp-typia/block-types/defined-binding-source");
|
|
3
|
+
const DEFINE_BINDING_SOURCE_INLINE_OPTION_KEYS = new Set([
|
|
4
|
+
"allowUnknownFutureKeys",
|
|
5
|
+
"editor",
|
|
6
|
+
"fieldsList",
|
|
7
|
+
"minVersion",
|
|
8
|
+
"minWordPress",
|
|
9
|
+
"minWordPressEditor",
|
|
10
|
+
"minWordPressFieldsList",
|
|
11
|
+
"minWordPressServer",
|
|
12
|
+
"minWordPressSupportedAttributesFilter",
|
|
13
|
+
"onDiagnostic",
|
|
14
|
+
"server",
|
|
15
|
+
"strict",
|
|
16
|
+
"supportedAttributesFilter",
|
|
17
|
+
]);
|
|
18
|
+
const SOURCE_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u;
|
|
19
|
+
const FIELD_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/u;
|
|
20
|
+
function isObjectRecord(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function isBindingSourceVersionGates(value) {
|
|
24
|
+
return isObjectRecord(value);
|
|
25
|
+
}
|
|
26
|
+
function splitDefineBindingSourceInput(source) {
|
|
27
|
+
const inlineOptions = {};
|
|
28
|
+
const normalizedSource = {};
|
|
29
|
+
for (const [key, value] of Object.entries(source)) {
|
|
30
|
+
if (DEFINE_BINDING_SOURCE_INLINE_OPTION_KEYS.has(key)) {
|
|
31
|
+
Object.assign(inlineOptions, { [key]: value });
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
normalizedSource[key] = value;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
inlineOptions,
|
|
38
|
+
source: normalizedSource,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function resolveMinWordPress(inlineOptions, options) {
|
|
42
|
+
const optionMinWordPress = options.minWordPress;
|
|
43
|
+
const inlineMinWordPress = inlineOptions.minWordPress;
|
|
44
|
+
const optionGates = isBindingSourceVersionGates(optionMinWordPress)
|
|
45
|
+
? optionMinWordPress
|
|
46
|
+
: {};
|
|
47
|
+
const inlineGates = isBindingSourceVersionGates(inlineMinWordPress)
|
|
48
|
+
? inlineMinWordPress
|
|
49
|
+
: {};
|
|
50
|
+
const minVersion = options.minVersion ??
|
|
51
|
+
(typeof optionMinWordPress === "string" ? optionMinWordPress : undefined) ??
|
|
52
|
+
inlineOptions.minVersion ??
|
|
53
|
+
(typeof inlineMinWordPress === "string" ? inlineMinWordPress : undefined);
|
|
54
|
+
const compatibility = {
|
|
55
|
+
strict: options.strict ?? inlineOptions.strict ?? true,
|
|
56
|
+
};
|
|
57
|
+
if (options.allowUnknownFutureKeys ?? inlineOptions.allowUnknownFutureKeys) {
|
|
58
|
+
Object.assign(compatibility, {
|
|
59
|
+
allowUnknownFutureKeys: options.allowUnknownFutureKeys ?? inlineOptions.allowUnknownFutureKeys,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (minVersion !== undefined) {
|
|
63
|
+
Object.assign(compatibility, { minVersion });
|
|
64
|
+
}
|
|
65
|
+
const gates = {};
|
|
66
|
+
const editor = options.minWordPressEditor ??
|
|
67
|
+
optionGates.editor ??
|
|
68
|
+
inlineOptions.minWordPressEditor ??
|
|
69
|
+
inlineGates.editor;
|
|
70
|
+
const fieldsList = options.minWordPressFieldsList ??
|
|
71
|
+
optionGates.fieldsList ??
|
|
72
|
+
inlineOptions.minWordPressFieldsList ??
|
|
73
|
+
inlineGates.fieldsList;
|
|
74
|
+
const server = options.minWordPressServer ??
|
|
75
|
+
optionGates.server ??
|
|
76
|
+
inlineOptions.minWordPressServer ??
|
|
77
|
+
inlineGates.server;
|
|
78
|
+
const supportedAttributesFilter = options.minWordPressSupportedAttributesFilter ??
|
|
79
|
+
optionGates.supportedAttributesFilter ??
|
|
80
|
+
inlineOptions.minWordPressSupportedAttributesFilter ??
|
|
81
|
+
inlineGates.supportedAttributesFilter;
|
|
82
|
+
if (editor !== undefined) {
|
|
83
|
+
Object.assign(gates, { editor });
|
|
84
|
+
}
|
|
85
|
+
if (fieldsList !== undefined) {
|
|
86
|
+
Object.assign(gates, { fieldsList });
|
|
87
|
+
}
|
|
88
|
+
if (server !== undefined) {
|
|
89
|
+
Object.assign(gates, { server });
|
|
90
|
+
}
|
|
91
|
+
if (supportedAttributesFilter !== undefined) {
|
|
92
|
+
Object.assign(gates, { supportedAttributesFilter });
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
compatibility,
|
|
96
|
+
gates,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function resolveDefineBindingSourceSettings(inlineOptions, options, source) {
|
|
100
|
+
const { compatibility, gates } = resolveMinWordPress(inlineOptions, options);
|
|
101
|
+
const strict = compatibility.strict ?? true;
|
|
102
|
+
const hasFields = (source.fields?.length ?? 0) > 0;
|
|
103
|
+
const hasBindableAttributes = (source.bindableAttributes?.length ?? 0) > 0;
|
|
104
|
+
return {
|
|
105
|
+
compatibility,
|
|
106
|
+
features: {
|
|
107
|
+
editor: options.editor ?? inlineOptions.editor ?? true,
|
|
108
|
+
fieldsList: options.fieldsList ?? inlineOptions.fieldsList ?? hasFields,
|
|
109
|
+
metadata: true,
|
|
110
|
+
server: options.server ?? inlineOptions.server ?? true,
|
|
111
|
+
supportedAttributesFilter: options.supportedAttributesFilter ??
|
|
112
|
+
inlineOptions.supportedAttributesFilter ??
|
|
113
|
+
hasBindableAttributes,
|
|
114
|
+
},
|
|
115
|
+
gates,
|
|
116
|
+
onDiagnostic: options.onDiagnostic ?? inlineOptions.onDiagnostic,
|
|
117
|
+
strict,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function getDiagnosticSeverity(strict) {
|
|
121
|
+
return strict ? "error" : "warning";
|
|
122
|
+
}
|
|
123
|
+
function getFeatureMinVersion(feature, fallback, gates) {
|
|
124
|
+
if (feature.area !== "blockBindings") {
|
|
125
|
+
return fallback;
|
|
126
|
+
}
|
|
127
|
+
switch (feature.feature) {
|
|
128
|
+
case "metadata.bindings":
|
|
129
|
+
case "serverRegistration":
|
|
130
|
+
return gates.server ?? fallback;
|
|
131
|
+
case "editorFieldsList":
|
|
132
|
+
return gates.fieldsList ?? fallback;
|
|
133
|
+
case "editorRegistration":
|
|
134
|
+
case "editorSourceLookup":
|
|
135
|
+
return gates.editor ?? fallback;
|
|
136
|
+
case "supportedAttributesFilter":
|
|
137
|
+
return gates.supportedAttributesFilter ?? gates.fieldsList ?? fallback;
|
|
138
|
+
default:
|
|
139
|
+
return fallback;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function createBindingCompatibilityManifest(features, settings, gates) {
|
|
143
|
+
const fallback = settings.minVersion ?? DEFAULT_WORDPRESS_COMPATIBILITY_MIN_VERSION;
|
|
144
|
+
const strict = settings.strict ?? true;
|
|
145
|
+
const allowUnknownFutureKeys = settings.allowUnknownFutureKeys ?? false;
|
|
146
|
+
const evaluations = features.map((feature) => evaluateWordPressBlockApiCompatibility(feature, {
|
|
147
|
+
allowUnknownFutureKeys,
|
|
148
|
+
minVersion: getFeatureMinVersion(feature, fallback, gates),
|
|
149
|
+
strict,
|
|
150
|
+
}));
|
|
151
|
+
const diagnostics = evaluations.flatMap((evaluation) => evaluation.diagnostic ? [evaluation.diagnostic] : []);
|
|
152
|
+
return {
|
|
153
|
+
allowUnknownFutureKeys,
|
|
154
|
+
diagnostics,
|
|
155
|
+
evaluations,
|
|
156
|
+
minVersion: fallback,
|
|
157
|
+
strict,
|
|
158
|
+
supported: evaluations.filter((evaluation) => evaluation.status === "supported"),
|
|
159
|
+
unknown: evaluations.filter((evaluation) => evaluation.status === "unknown"),
|
|
160
|
+
unsupported: evaluations.filter((evaluation) => evaluation.status === "unsupported"),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export function collectBindingSourceCompatibilityFeatures(settings = {}) {
|
|
164
|
+
const features = [];
|
|
165
|
+
if (settings.metadata ?? true) {
|
|
166
|
+
features.push({
|
|
167
|
+
area: "blockBindings",
|
|
168
|
+
feature: "metadata.bindings",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (settings.server ?? true) {
|
|
172
|
+
features.push({
|
|
173
|
+
area: "blockBindings",
|
|
174
|
+
feature: "serverRegistration",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (settings.editor ?? true) {
|
|
178
|
+
features.push({
|
|
179
|
+
area: "blockBindings",
|
|
180
|
+
feature: "editorRegistration",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (settings.fieldsList ?? false) {
|
|
184
|
+
features.push({
|
|
185
|
+
area: "blockBindings",
|
|
186
|
+
feature: "editorFieldsList",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (settings.supportedAttributesFilter ?? false) {
|
|
190
|
+
features.push({
|
|
191
|
+
area: "blockBindings",
|
|
192
|
+
feature: "supportedAttributesFilter",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return features;
|
|
196
|
+
}
|
|
197
|
+
export function createBindingSourceCompatibilityManifest(settings = {}) {
|
|
198
|
+
const resolved = resolveDefineBindingSourceSettings({}, settings, {
|
|
199
|
+
name: "wp-typia/binding-source",
|
|
200
|
+
});
|
|
201
|
+
return createBindingCompatibilityManifest(collectBindingSourceCompatibilityFeatures(resolved.features), resolved.compatibility, resolved.gates);
|
|
202
|
+
}
|
|
203
|
+
function createBindingSourceDiagnostics(source, options) {
|
|
204
|
+
const diagnostics = [];
|
|
205
|
+
const severity = getDiagnosticSeverity(options.strict);
|
|
206
|
+
if (!SOURCE_NAME_PATTERN.test(source.name)) {
|
|
207
|
+
diagnostics.push({
|
|
208
|
+
code: "invalid-source-name",
|
|
209
|
+
message: `Block binding source "${source.name}" must be lowercase and namespaced, such as "acme/profile-data".`,
|
|
210
|
+
severity,
|
|
211
|
+
sourceName: source.name,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (options.server && !source.getValueCallback) {
|
|
215
|
+
diagnostics.push({
|
|
216
|
+
code: "missing-php-callback",
|
|
217
|
+
message: `Block binding source "${source.name}" needs getValueCallback when server registration is enabled.`,
|
|
218
|
+
severity,
|
|
219
|
+
sourceName: source.name,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (options.fieldsList && !options.editor) {
|
|
223
|
+
diagnostics.push({
|
|
224
|
+
code: "fields-list-requires-editor",
|
|
225
|
+
message: `Block binding source "${source.name}" enables getFieldsList() without editor registration.`,
|
|
226
|
+
severity,
|
|
227
|
+
sourceName: source.name,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const seenFields = new Set();
|
|
231
|
+
for (const field of source.fields ?? []) {
|
|
232
|
+
if (!FIELD_NAME_PATTERN.test(field.name)) {
|
|
233
|
+
diagnostics.push({
|
|
234
|
+
code: "invalid-field-name",
|
|
235
|
+
fieldName: field.name,
|
|
236
|
+
message: `Block binding source "${source.name}" field "${field.name}" must be a stable identifier.`,
|
|
237
|
+
severity,
|
|
238
|
+
sourceName: source.name,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (seenFields.has(field.name)) {
|
|
242
|
+
diagnostics.push({
|
|
243
|
+
code: "duplicate-field-name",
|
|
244
|
+
fieldName: field.name,
|
|
245
|
+
message: `Block binding source "${source.name}" declares duplicate field "${field.name}".`,
|
|
246
|
+
severity,
|
|
247
|
+
sourceName: source.name,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
seenFields.add(field.name);
|
|
251
|
+
}
|
|
252
|
+
for (const target of source.bindableAttributes ?? []) {
|
|
253
|
+
if (!SOURCE_NAME_PATTERN.test(target.blockName)) {
|
|
254
|
+
diagnostics.push({
|
|
255
|
+
blockName: target.blockName,
|
|
256
|
+
code: "invalid-block-name",
|
|
257
|
+
message: `Bindable attributes target "${target.blockName}" must be a lowercase namespaced block name.`,
|
|
258
|
+
severity,
|
|
259
|
+
sourceName: source.name,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const seenAttributes = new Set();
|
|
263
|
+
for (const attribute of target.attributes) {
|
|
264
|
+
if (!FIELD_NAME_PATTERN.test(attribute)) {
|
|
265
|
+
diagnostics.push({
|
|
266
|
+
attribute,
|
|
267
|
+
blockName: target.blockName,
|
|
268
|
+
code: "invalid-bindable-attribute",
|
|
269
|
+
message: `Bindable attribute "${attribute}" for "${target.blockName}" must be a stable identifier.`,
|
|
270
|
+
severity,
|
|
271
|
+
sourceName: source.name,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (seenAttributes.has(attribute)) {
|
|
275
|
+
diagnostics.push({
|
|
276
|
+
attribute,
|
|
277
|
+
blockName: target.blockName,
|
|
278
|
+
code: "duplicate-bindable-attribute",
|
|
279
|
+
message: `Bindable attribute "${attribute}" for "${target.blockName}" is declared more than once.`,
|
|
280
|
+
severity,
|
|
281
|
+
sourceName: source.name,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
seenAttributes.add(attribute);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return diagnostics;
|
|
288
|
+
}
|
|
289
|
+
function handleBindingSourceDiagnostics(diagnostics, onDiagnostic) {
|
|
290
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
291
|
+
if (errors.length > 0) {
|
|
292
|
+
throw new Error([
|
|
293
|
+
"WordPress block binding source check failed:",
|
|
294
|
+
...errors.map((diagnostic) => `- ${diagnostic.message}`),
|
|
295
|
+
].join("\n"));
|
|
296
|
+
}
|
|
297
|
+
for (const diagnostic of diagnostics) {
|
|
298
|
+
if (onDiagnostic) {
|
|
299
|
+
onDiagnostic(diagnostic);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
console.warn(`[wp-typia] ${diagnostic.message}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
export function getDefinedBindingSourceMetadata(source) {
|
|
306
|
+
return isObjectRecord(source)
|
|
307
|
+
? source[DEFINED_BLOCK_BINDING_SOURCE_METADATA]
|
|
308
|
+
: undefined;
|
|
309
|
+
}
|
|
310
|
+
export function getDefinedBindingSourceCompatibilityManifest(source) {
|
|
311
|
+
return getDefinedBindingSourceMetadata(source)?.manifest;
|
|
312
|
+
}
|
|
313
|
+
export function defineBindingSource(source, options = {}) {
|
|
314
|
+
const { inlineOptions, source: normalizedSource } = splitDefineBindingSourceInput(source);
|
|
315
|
+
const resolved = resolveDefineBindingSourceSettings(inlineOptions, options, normalizedSource);
|
|
316
|
+
const features = collectBindingSourceCompatibilityFeatures(resolved.features);
|
|
317
|
+
const manifest = createBindingCompatibilityManifest(features, resolved.compatibility, resolved.gates);
|
|
318
|
+
const diagnostics = [
|
|
319
|
+
...manifest.diagnostics,
|
|
320
|
+
...createBindingSourceDiagnostics(normalizedSource, {
|
|
321
|
+
editor: resolved.features.editor,
|
|
322
|
+
fieldsList: resolved.features.fieldsList,
|
|
323
|
+
server: resolved.features.server,
|
|
324
|
+
strict: resolved.strict,
|
|
325
|
+
}),
|
|
326
|
+
];
|
|
327
|
+
handleBindingSourceDiagnostics(diagnostics, resolved.onDiagnostic);
|
|
328
|
+
Object.defineProperty(normalizedSource, DEFINED_BLOCK_BINDING_SOURCE_METADATA, {
|
|
329
|
+
configurable: false,
|
|
330
|
+
enumerable: false,
|
|
331
|
+
value: {
|
|
332
|
+
diagnostics,
|
|
333
|
+
features,
|
|
334
|
+
manifest,
|
|
335
|
+
},
|
|
336
|
+
writable: false,
|
|
337
|
+
});
|
|
338
|
+
return normalizedSource;
|
|
339
|
+
}
|
|
340
|
+
export function defineBindableAttributes(blockName, attributes) {
|
|
341
|
+
return {
|
|
342
|
+
attributes,
|
|
343
|
+
blockName,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
export function defineBlockMetadataBindings(bindings) {
|
|
347
|
+
return { bindings };
|
|
348
|
+
}
|
|
349
|
+
export function defineTypedBlockMetadataBindings(bindings) {
|
|
350
|
+
return { bindings };
|
|
351
|
+
}
|
|
352
|
+
export function createBindingSourceRegistrationPlan(sources) {
|
|
353
|
+
return sources.map((source) => {
|
|
354
|
+
const metadata = getDefinedBindingSourceMetadata(source);
|
|
355
|
+
if (!metadata) {
|
|
356
|
+
throw new Error(`Block binding source "${source.name}" was not created by defineBindingSource().`);
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
metadata,
|
|
360
|
+
source,
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function normalizeStaticRegistrationValue(value, path) {
|
|
365
|
+
if (value === undefined) {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
if (value === null ||
|
|
369
|
+
typeof value === "boolean" ||
|
|
370
|
+
typeof value === "number" ||
|
|
371
|
+
typeof value === "string") {
|
|
372
|
+
return value;
|
|
373
|
+
}
|
|
374
|
+
if (typeof value === "function") {
|
|
375
|
+
throw new Error(`Cannot generate static binding source registration code for function value at ${path}.`);
|
|
376
|
+
}
|
|
377
|
+
if (typeof value === "bigint" || typeof value === "symbol") {
|
|
378
|
+
throw new Error(`Cannot generate static binding source registration code for ${typeof value} value at ${path}.`);
|
|
379
|
+
}
|
|
380
|
+
if (Array.isArray(value)) {
|
|
381
|
+
return value.map((entry, index) => normalizeStaticRegistrationValue(entry, `${path}[${index}]`));
|
|
382
|
+
}
|
|
383
|
+
if (isObjectRecord(value)) {
|
|
384
|
+
const normalized = {};
|
|
385
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
386
|
+
const nextValue = normalizeStaticRegistrationValue(nestedValue, `${path}.${key}`);
|
|
387
|
+
if (nextValue !== undefined) {
|
|
388
|
+
normalized[key] = nextValue;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return normalized;
|
|
392
|
+
}
|
|
393
|
+
throw new Error(`Cannot generate static binding source registration code for unsupported value at ${path}.`);
|
|
394
|
+
}
|
|
395
|
+
function getBindingEvaluation(metadata, feature) {
|
|
396
|
+
return metadata.manifest.evaluations.find((evaluation) => evaluation.area === "blockBindings" && evaluation.feature === feature);
|
|
397
|
+
}
|
|
398
|
+
function shouldGenerateFeature(metadata, feature) {
|
|
399
|
+
const evaluation = getBindingEvaluation(metadata, feature);
|
|
400
|
+
return evaluation?.action === "generate";
|
|
401
|
+
}
|
|
402
|
+
function asSourceList(sources) {
|
|
403
|
+
if (Array.isArray(sources)) {
|
|
404
|
+
return sources;
|
|
405
|
+
}
|
|
406
|
+
return [sources];
|
|
407
|
+
}
|
|
408
|
+
function escapePhpSingleQuotedString(value) {
|
|
409
|
+
return value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'");
|
|
410
|
+
}
|
|
411
|
+
function phpString(value) {
|
|
412
|
+
return `'${escapePhpSingleQuotedString(value)}'`;
|
|
413
|
+
}
|
|
414
|
+
function phpStringArray(values) {
|
|
415
|
+
return values.length === 0
|
|
416
|
+
? "array()"
|
|
417
|
+
: `array( ${values.map((value) => phpString(value)).join(", ")} )`;
|
|
418
|
+
}
|
|
419
|
+
function sanitizePhpIdentifier(value) {
|
|
420
|
+
const sanitized = value.replace(/[^A-Za-z0-9_]/gu, "_").replace(/_+/gu, "_");
|
|
421
|
+
return /^[A-Za-z_]/u.test(sanitized) ? sanitized : `wp_typia_${sanitized}`;
|
|
422
|
+
}
|
|
423
|
+
function createPhpIdentifierHash(value) {
|
|
424
|
+
let hash = 0x811c9dc5;
|
|
425
|
+
for (const character of value) {
|
|
426
|
+
hash ^= character.codePointAt(0) ?? 0;
|
|
427
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
428
|
+
}
|
|
429
|
+
return hash.toString(36);
|
|
430
|
+
}
|
|
431
|
+
function createPhpSourceProperties(source, options) {
|
|
432
|
+
const properties = [];
|
|
433
|
+
if (source.label) {
|
|
434
|
+
const label = options.textDomain
|
|
435
|
+
? `__( ${phpString(source.label)}, ${phpString(options.textDomain)} )`
|
|
436
|
+
: phpString(source.label);
|
|
437
|
+
properties.push(`'label' => ${label}`);
|
|
438
|
+
}
|
|
439
|
+
if (source.usesContext && source.usesContext.length > 0) {
|
|
440
|
+
properties.push(`'uses_context' => ${phpStringArray(source.usesContext)}`);
|
|
441
|
+
}
|
|
442
|
+
if (source.getValueCallback) {
|
|
443
|
+
properties.push(`'get_value_callback' => ${phpString(source.getValueCallback)}`);
|
|
444
|
+
}
|
|
445
|
+
return properties;
|
|
446
|
+
}
|
|
447
|
+
function createPhpBindableAttributeFilterSource(entries, functionName) {
|
|
448
|
+
const lines = [];
|
|
449
|
+
const targets = entries.flatMap((entry) => (entry.source.bindableAttributes ?? []).map((target, index) => ({
|
|
450
|
+
index,
|
|
451
|
+
metadata: entry.metadata,
|
|
452
|
+
sourceName: entry.source.name,
|
|
453
|
+
target,
|
|
454
|
+
})));
|
|
455
|
+
if (targets.length === 0) {
|
|
456
|
+
return lines;
|
|
457
|
+
}
|
|
458
|
+
lines.push("");
|
|
459
|
+
lines.push("if ( function_exists( 'get_block_bindings_supported_attributes' ) ) {");
|
|
460
|
+
for (const [targetIndex, target] of targets.entries()) {
|
|
461
|
+
if (!shouldGenerateFeature(target.metadata, "supportedAttributesFilter")) {
|
|
462
|
+
lines.push(`\t// block_bindings_supported_attributes requires WordPress 6.9+ for ${target.target.blockName}.`);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const callbackSeed = [
|
|
466
|
+
functionName,
|
|
467
|
+
target.sourceName,
|
|
468
|
+
target.target.blockName,
|
|
469
|
+
String(target.index),
|
|
470
|
+
String(targetIndex),
|
|
471
|
+
].join("\0");
|
|
472
|
+
const callbackName = sanitizePhpIdentifier(`${functionName}_${target.sourceName}_${target.target.blockName}_${target.index}_${targetIndex}_${createPhpIdentifierHash(callbackSeed)}_supported_attributes`);
|
|
473
|
+
lines.push(`\tadd_filter( ${phpString(`block_bindings_supported_attributes_${target.target.blockName}`)}, ${phpString(callbackName)} );`);
|
|
474
|
+
lines.push(`\tfunction ${callbackName}( $supported_attributes ) {`);
|
|
475
|
+
lines.push(`\t\treturn array_values( array_unique( array_merge( $supported_attributes, ${phpStringArray(target.target.attributes)} ) ) );`);
|
|
476
|
+
lines.push("\t}");
|
|
477
|
+
}
|
|
478
|
+
lines.push("}");
|
|
479
|
+
return lines;
|
|
480
|
+
}
|
|
481
|
+
export function createPhpBindingSourceRegistrationSource(sources, options = {}) {
|
|
482
|
+
const functionName = options.functionName ?? "wp_typia_register_block_binding_sources";
|
|
483
|
+
const hook = options.hook ?? "init";
|
|
484
|
+
const entries = createBindingSourceRegistrationPlan(asSourceList(sources));
|
|
485
|
+
const lines = [];
|
|
486
|
+
if (options.includeOpeningTag ?? false) {
|
|
487
|
+
lines.push("<?php");
|
|
488
|
+
lines.push("");
|
|
489
|
+
}
|
|
490
|
+
lines.push(`function ${functionName}() {`);
|
|
491
|
+
lines.push("\tif ( ! function_exists( 'register_block_bindings_source' ) ) {");
|
|
492
|
+
lines.push("\t\treturn;");
|
|
493
|
+
lines.push("\t}");
|
|
494
|
+
lines.push("");
|
|
495
|
+
for (const entry of entries) {
|
|
496
|
+
if (!shouldGenerateFeature(entry.metadata, "serverRegistration")) {
|
|
497
|
+
lines.push(`\t// register_block_bindings_source() requires WordPress 6.5+ for ${entry.source.name}.`);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const properties = createPhpSourceProperties(entry.source, options);
|
|
501
|
+
lines.push(`\tregister_block_bindings_source( ${phpString(entry.source.name)}, array(`);
|
|
502
|
+
for (const property of properties) {
|
|
503
|
+
lines.push(`\t\t${property},`);
|
|
504
|
+
}
|
|
505
|
+
lines.push("\t) );");
|
|
506
|
+
}
|
|
507
|
+
lines.push("}");
|
|
508
|
+
lines.push(`add_action( ${phpString(hook)}, ${phpString(functionName)} );`);
|
|
509
|
+
lines.push(...createPhpBindableAttributeFilterSource(entries, functionName));
|
|
510
|
+
lines.push("");
|
|
511
|
+
return lines.join("\n");
|
|
512
|
+
}
|
|
513
|
+
function createEditorRegistrationEntries(entries) {
|
|
514
|
+
const fields = {};
|
|
515
|
+
const skipped = [];
|
|
516
|
+
const skippedFields = [];
|
|
517
|
+
const sources = [];
|
|
518
|
+
for (const entry of entries) {
|
|
519
|
+
const editorEvaluation = getBindingEvaluation(entry.metadata, "editorRegistration");
|
|
520
|
+
if (editorEvaluation === undefined) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (editorEvaluation.action !== "generate") {
|
|
524
|
+
skipped.push(entry.source.name);
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
sources.push(normalizeStaticRegistrationValue({
|
|
528
|
+
label: entry.source.label,
|
|
529
|
+
name: entry.source.name,
|
|
530
|
+
usesContext: entry.source.usesContext,
|
|
531
|
+
}, `sources.${entry.source.name}`));
|
|
532
|
+
if ((entry.source.fields?.length ?? 0) === 0) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
if (!shouldGenerateFeature(entry.metadata, "editorFieldsList")) {
|
|
536
|
+
skippedFields.push(entry.source.name);
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
fields[entry.source.name] = normalizeStaticRegistrationValue(entry.source.fields, `fields.${entry.source.name}`);
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
fields,
|
|
543
|
+
skipped,
|
|
544
|
+
skippedFields,
|
|
545
|
+
sources,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
export function createEditorBindingSourceRegistrationSource(sources, options = {}) {
|
|
549
|
+
const importSource = options.importSource ?? "@wordpress/blocks";
|
|
550
|
+
const functionName = options.functionName ?? "registerWpTypiaBindingSources";
|
|
551
|
+
const entries = createBindingSourceRegistrationPlan(asSourceList(sources));
|
|
552
|
+
const registration = createEditorRegistrationEntries(entries);
|
|
553
|
+
if (registration.sources.length === 0) {
|
|
554
|
+
return [
|
|
555
|
+
"// No editor block binding sources were generated.",
|
|
556
|
+
...(registration.skipped.length > 0
|
|
557
|
+
? [
|
|
558
|
+
`// Skipped editor registration for ${registration.skipped.join(", ")}; registerBlockBindingsSource() requires WordPress 6.7+.`,
|
|
559
|
+
]
|
|
560
|
+
: []),
|
|
561
|
+
"",
|
|
562
|
+
].join("\n");
|
|
563
|
+
}
|
|
564
|
+
const serializedSources = JSON.stringify(registration.sources, null, 2);
|
|
565
|
+
const serializedFields = JSON.stringify(registration.fields, null, 2);
|
|
566
|
+
const hasGeneratedFields = Object.keys(registration.fields).length > 0;
|
|
567
|
+
const lines = [
|
|
568
|
+
`import { registerBlockBindingsSource } from ${JSON.stringify(importSource)};`,
|
|
569
|
+
"",
|
|
570
|
+
`const sources = ${serializedSources};`,
|
|
571
|
+
...(hasGeneratedFields ? [`const fieldsBySource = ${serializedFields};`] : []),
|
|
572
|
+
"",
|
|
573
|
+
`export function ${functionName}() {`,
|
|
574
|
+
" if (typeof registerBlockBindingsSource !== \"function\") {",
|
|
575
|
+
" return;",
|
|
576
|
+
" }",
|
|
577
|
+
" for (const source of sources) {",
|
|
578
|
+
...(hasGeneratedFields
|
|
579
|
+
? [
|
|
580
|
+
" const fields = fieldsBySource[source.name];",
|
|
581
|
+
" registerBlockBindingsSource({",
|
|
582
|
+
" ...source,",
|
|
583
|
+
" ...(fields ? { getFieldsList: () => fields } : {}),",
|
|
584
|
+
" });",
|
|
585
|
+
]
|
|
586
|
+
: [" registerBlockBindingsSource(source);"]),
|
|
587
|
+
" }",
|
|
588
|
+
"}",
|
|
589
|
+
];
|
|
590
|
+
if (registration.skipped.length > 0) {
|
|
591
|
+
lines.push("", `// Skipped editor registration for ${registration.skipped.join(", ")}; registerBlockBindingsSource() requires WordPress 6.7+.`);
|
|
592
|
+
}
|
|
593
|
+
if (registration.skippedFields.length > 0) {
|
|
594
|
+
lines.push("", `// Skipped getFieldsList() for ${registration.skippedFields.join(", ")}; getFieldsList() requires WordPress 6.9+.`);
|
|
595
|
+
}
|
|
596
|
+
lines.push("");
|
|
597
|
+
return lines.join("\n");
|
|
598
|
+
}
|