@wp-typia/project-tools 0.20.2 → 0.21.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/runtime/cli-add-shared.d.ts +73 -5
- package/dist/runtime/cli-add-shared.js +58 -11
- package/dist/runtime/cli-add-workspace-ability.js +11 -57
- package/dist/runtime/cli-add-workspace-admin-view.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-admin-view.js +872 -0
- package/dist/runtime/cli-add-workspace-ai-anchors.js +2 -5
- package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +0 -4
- package/dist/runtime/cli-add-workspace-ai-source-emitters.js +7 -17
- package/dist/runtime/cli-add-workspace-ai.js +4 -6
- package/dist/runtime/cli-add-workspace-assets.d.ts +13 -5
- package/dist/runtime/cli-add-workspace-assets.js +290 -106
- package/dist/runtime/cli-add-workspace-rest-anchors.js +2 -5
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +0 -1
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +7 -14
- package/dist/runtime/cli-add-workspace-rest.js +4 -6
- package/dist/runtime/cli-add-workspace.d.ts +58 -1
- package/dist/runtime/cli-add-workspace.js +588 -18
- package/dist/runtime/cli-add.d.ts +1 -1
- package/dist/runtime/cli-add.js +1 -1
- package/dist/runtime/cli-core.d.ts +8 -5
- package/dist/runtime/cli-core.js +7 -4
- package/dist/runtime/cli-diagnostics.d.ts +83 -1
- package/dist/runtime/cli-diagnostics.js +85 -2
- package/dist/runtime/cli-doctor-workspace.js +552 -13
- package/dist/runtime/cli-doctor.d.ts +4 -2
- package/dist/runtime/cli-doctor.js +2 -1
- package/dist/runtime/cli-help.js +19 -9
- package/dist/runtime/cli-init.d.ts +67 -3
- package/dist/runtime/cli-init.js +603 -64
- package/dist/runtime/cli-validation.js +4 -3
- package/dist/runtime/index.d.ts +9 -4
- package/dist/runtime/index.js +7 -3
- package/dist/runtime/package-json-types.d.ts +12 -0
- package/dist/runtime/package-json-types.js +1 -0
- package/dist/runtime/package-versions.d.ts +17 -2
- package/dist/runtime/package-versions.js +46 -1
- package/dist/runtime/php-utils.d.ts +16 -0
- package/dist/runtime/php-utils.js +59 -0
- package/dist/runtime/scaffold-answer-resolution.js +7 -6
- package/dist/runtime/scaffold-apply-utils.d.ts +2 -3
- package/dist/runtime/scaffold-apply-utils.js +3 -43
- package/dist/runtime/template-source-cache.d.ts +112 -0
- package/dist/runtime/template-source-cache.js +434 -0
- package/dist/runtime/template-source-seeds.js +319 -53
- package/dist/runtime/workspace-inventory.d.ts +43 -2
- package/dist/runtime/workspace-inventory.js +138 -5
- package/package.json +2 -2
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { promises as fsp } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { syncBlockMetadata, } from "@wp-typia/block-runtime/metadata-core";
|
|
5
|
+
import ts from "typescript";
|
|
4
6
|
import { resolveWorkspaceProject, } from "./workspace-project.js";
|
|
5
|
-
import { readWorkspaceInventory, appendWorkspaceInventoryEntries } from "./workspace-inventory.js";
|
|
6
|
-
import { toTitleCase } from "./string-case.js";
|
|
7
|
-
import {
|
|
7
|
+
import { readWorkspaceInventory, appendWorkspaceInventoryEntries, } from "./workspace-inventory.js";
|
|
8
|
+
import { toPascalCase, toTitleCase } from "./string-case.js";
|
|
9
|
+
import { findPhpFunctionRange, hasPhpFunctionDefinition, quotePhpString, replacePhpFunctionDefinition, } from "./php-utils.js";
|
|
10
|
+
import { assertBindingSourceDoesNotExist, assertEditorPluginDoesNotExist, assertPatternDoesNotExist, assertValidEditorPluginSlot, assertValidGeneratedSlug, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
8
11
|
const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
|
|
9
12
|
const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
|
|
10
13
|
const BINDING_SOURCE_EDITOR_SCRIPT = "build/bindings/index.js";
|
|
@@ -13,58 +16,7 @@ const EDITOR_PLUGIN_EDITOR_SCRIPT = "build/editor-plugins/index.js";
|
|
|
13
16
|
const EDITOR_PLUGIN_EDITOR_ASSET = "build/editor-plugins/index.asset.php";
|
|
14
17
|
const EDITOR_PLUGIN_EDITOR_STYLE = "build/editor-plugins/style-index.css";
|
|
15
18
|
const EDITOR_PLUGIN_EDITOR_STYLE_RTL = "build/editor-plugins/style-index-rtl.css";
|
|
16
|
-
|
|
17
|
-
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
18
|
-
}
|
|
19
|
-
function quotePhpString(value) {
|
|
20
|
-
return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
|
|
21
|
-
}
|
|
22
|
-
function findPhpFunctionRange(source, functionName) {
|
|
23
|
-
const signaturePattern = new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u");
|
|
24
|
-
const signatureMatch = signaturePattern.exec(source);
|
|
25
|
-
if (!signatureMatch || signatureMatch.index === undefined) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
const functionStart = signatureMatch.index;
|
|
29
|
-
const openBraceIndex = source.indexOf("{", functionStart);
|
|
30
|
-
if (openBraceIndex === -1) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
let depth = 0;
|
|
34
|
-
for (let index = openBraceIndex; index < source.length; index += 1) {
|
|
35
|
-
const character = source[index];
|
|
36
|
-
if (character === "{") {
|
|
37
|
-
depth += 1;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (character !== "}") {
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
depth -= 1;
|
|
44
|
-
if (depth === 0) {
|
|
45
|
-
let functionEnd = index + 1;
|
|
46
|
-
while (functionEnd < source.length && /[\r\n]/u.test(source[functionEnd] ?? "")) {
|
|
47
|
-
functionEnd += 1;
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
end: functionEnd,
|
|
51
|
-
start: functionStart,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
function replacePhpFunctionDefinition(source, functionName, replacement) {
|
|
58
|
-
const functionRange = findPhpFunctionRange(source, functionName);
|
|
59
|
-
if (!functionRange) {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
return [
|
|
63
|
-
source.slice(0, functionRange.start),
|
|
64
|
-
replacement,
|
|
65
|
-
source.slice(functionRange.end),
|
|
66
|
-
].join("");
|
|
67
|
-
}
|
|
19
|
+
const BINDING_ATTRIBUTE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/u;
|
|
68
20
|
function buildPatternConfigEntry(patternSlug) {
|
|
69
21
|
return [
|
|
70
22
|
"\t{",
|
|
@@ -73,15 +25,47 @@ function buildPatternConfigEntry(patternSlug) {
|
|
|
73
25
|
"\t},",
|
|
74
26
|
].join("\n");
|
|
75
27
|
}
|
|
76
|
-
function buildBindingSourceConfigEntry(bindingSourceSlug) {
|
|
28
|
+
function buildBindingSourceConfigEntry(bindingSourceSlug, target) {
|
|
77
29
|
return [
|
|
78
30
|
"\t{",
|
|
31
|
+
...(target ? [`\t\tattribute: ${quoteTsString(target.attributeName)},`] : []),
|
|
32
|
+
...(target ? [`\t\tblock: ${quoteTsString(target.blockSlug)},`] : []),
|
|
79
33
|
`\t\teditorFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/editor.ts`)},`,
|
|
80
34
|
`\t\tserverFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/server.php`)},`,
|
|
81
35
|
`\t\tslug: ${quoteTsString(bindingSourceSlug)},`,
|
|
82
36
|
"\t},",
|
|
83
37
|
].join("\n");
|
|
84
38
|
}
|
|
39
|
+
function assertValidBindingAttributeName(attributeName) {
|
|
40
|
+
const trimmed = attributeName.trim();
|
|
41
|
+
if (!trimmed) {
|
|
42
|
+
throw new Error("`wp-typia add binding-source` requires --attribute <attribute> to include a value when --block is provided.");
|
|
43
|
+
}
|
|
44
|
+
if (!BINDING_ATTRIBUTE_NAME_PATTERN.test(trimmed)) {
|
|
45
|
+
throw new Error(`Binding attribute "${attributeName}" must start with a letter and use only letters, numbers, underscores, or hyphens.`);
|
|
46
|
+
}
|
|
47
|
+
return trimmed;
|
|
48
|
+
}
|
|
49
|
+
function resolveBindingTargetBlockSlug(blockName, namespace) {
|
|
50
|
+
const trimmed = blockName.trim();
|
|
51
|
+
if (!trimmed) {
|
|
52
|
+
throw new Error("`wp-typia add binding-source` requires --block <block-slug|namespace/block-slug> to include a value when --attribute is provided.");
|
|
53
|
+
}
|
|
54
|
+
const blockNameSegments = trimmed.split("/");
|
|
55
|
+
if (blockNameSegments.length > 2) {
|
|
56
|
+
throw new Error(`Binding target block "${trimmed}" must use <block-slug> or <namespace/block-slug> format.`);
|
|
57
|
+
}
|
|
58
|
+
if (blockNameSegments.some((segment) => segment.trim() === "")) {
|
|
59
|
+
throw new Error(`Binding target block "${trimmed}" must use <block-slug> or <namespace/block-slug> format without empty path segments.`);
|
|
60
|
+
}
|
|
61
|
+
const [maybeNamespace, maybeSlug] = blockNameSegments.length === 2
|
|
62
|
+
? blockNameSegments
|
|
63
|
+
: [undefined, blockNameSegments[0]];
|
|
64
|
+
if (maybeNamespace && maybeNamespace !== namespace) {
|
|
65
|
+
throw new Error(`Binding target block "${trimmed}" uses namespace "${maybeNamespace}". Expected "${namespace}".`);
|
|
66
|
+
}
|
|
67
|
+
return normalizeBlockSlug(maybeSlug ?? "");
|
|
68
|
+
}
|
|
85
69
|
function buildEditorPluginConfigEntry(editorPluginSlug, slot) {
|
|
86
70
|
return [
|
|
87
71
|
"\t{",
|
|
@@ -91,13 +75,6 @@ function buildEditorPluginConfigEntry(editorPluginSlug, slot) {
|
|
|
91
75
|
"\t},",
|
|
92
76
|
].join("\n");
|
|
93
77
|
}
|
|
94
|
-
function toPascalCaseFromSlug(slug) {
|
|
95
|
-
return normalizeBlockSlug(slug)
|
|
96
|
-
.split("-")
|
|
97
|
-
.filter(Boolean)
|
|
98
|
-
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
99
|
-
.join("");
|
|
100
|
-
}
|
|
101
78
|
function buildPatternSource(patternSlug, namespace, textDomain) {
|
|
102
79
|
const patternTitle = toTitleCase(patternSlug);
|
|
103
80
|
return `<?php
|
|
@@ -116,12 +93,36 @@ register_block_pattern(
|
|
|
116
93
|
);
|
|
117
94
|
`;
|
|
118
95
|
}
|
|
119
|
-
function buildBindingSourceServerSource(bindingSourceSlug, phpPrefix, namespace, textDomain) {
|
|
96
|
+
function buildBindingSourceServerSource(bindingSourceSlug, phpPrefix, namespace, textDomain, target) {
|
|
120
97
|
const bindingSourceTitle = toTitleCase(bindingSourceSlug);
|
|
121
98
|
const bindingSourcePhpId = bindingSourceSlug.replace(/-/g, "_");
|
|
122
99
|
const bindingSourceValueFunctionName = `${phpPrefix}_${bindingSourcePhpId}_binding_source_values`;
|
|
123
100
|
const bindingSourceResolveFunctionName = `${phpPrefix}_${bindingSourcePhpId}_resolve_binding_source_value`;
|
|
101
|
+
const bindingSourceSupportedAttributesFunctionName = `${phpPrefix}_${bindingSourcePhpId}_supported_binding_attributes`;
|
|
124
102
|
const starterValue = `${bindingSourceTitle} starter value`;
|
|
103
|
+
const supportedAttributesSource = target
|
|
104
|
+
? `
|
|
105
|
+
if ( ! function_exists( '${bindingSourceSupportedAttributesFunctionName}' ) ) {
|
|
106
|
+
\tfunction ${bindingSourceSupportedAttributesFunctionName}( array $supported_attributes ) : array {
|
|
107
|
+
\t\tif ( ! in_array( ${quotePhpString(target.attributeName)}, $supported_attributes, true ) ) {
|
|
108
|
+
\t\t\t$supported_attributes[] = ${quotePhpString(target.attributeName)};
|
|
109
|
+
\t\t}
|
|
110
|
+
|
|
111
|
+
\t\treturn $supported_attributes;
|
|
112
|
+
\t}
|
|
113
|
+
}
|
|
114
|
+
`
|
|
115
|
+
: "";
|
|
116
|
+
const supportedAttributesHook = target
|
|
117
|
+
? `
|
|
118
|
+
if ( function_exists( '${bindingSourceSupportedAttributesFunctionName}' ) ) {
|
|
119
|
+
\tadd_filter(
|
|
120
|
+
\t\t${quotePhpString(`block_bindings_supported_attributes_${namespace}/${target.blockSlug}`)},
|
|
121
|
+
\t\t${quotePhpString(bindingSourceSupportedAttributesFunctionName)}
|
|
122
|
+
\t);
|
|
123
|
+
}
|
|
124
|
+
`
|
|
125
|
+
: "";
|
|
125
126
|
return `<?php
|
|
126
127
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
127
128
|
\treturn;
|
|
@@ -150,6 +151,7 @@ if ( ! function_exists( '${bindingSourceResolveFunctionName}' ) ) {
|
|
|
150
151
|
\t\treturn is_string( $value ) ? $value : '';
|
|
151
152
|
\t}
|
|
152
153
|
}
|
|
154
|
+
${supportedAttributesSource}
|
|
153
155
|
|
|
154
156
|
register_block_bindings_source(
|
|
155
157
|
\t${quotePhpString(`${namespace}/${bindingSourceSlug}`)},
|
|
@@ -158,11 +160,22 @@ register_block_bindings_source(
|
|
|
158
160
|
\t\t'get_value_callback' => ${quotePhpString(bindingSourceResolveFunctionName)},
|
|
159
161
|
\t)
|
|
160
162
|
);
|
|
161
|
-
`;
|
|
163
|
+
${supportedAttributesHook}`;
|
|
162
164
|
}
|
|
163
|
-
function buildBindingSourceEditorSource(bindingSourceSlug, namespace, textDomain) {
|
|
165
|
+
function buildBindingSourceEditorSource(bindingSourceSlug, namespace, textDomain, target) {
|
|
164
166
|
const bindingSourceTitle = toTitleCase(bindingSourceSlug);
|
|
165
167
|
const starterValue = `${bindingSourceTitle} starter value`;
|
|
168
|
+
const bindingSourceName = `${namespace}/${bindingSourceSlug}`;
|
|
169
|
+
const targetSource = target
|
|
170
|
+
? `
|
|
171
|
+
export const BINDING_SOURCE_TARGET = {
|
|
172
|
+
\tattribute: ${quoteTsString(target.attributeName)},
|
|
173
|
+
\tblock: ${quoteTsString(`${namespace}/${target.blockSlug}`)},
|
|
174
|
+
\tfield: ${quoteTsString(bindingSourceSlug)},
|
|
175
|
+
\tsource: ${quoteTsString(bindingSourceName)},
|
|
176
|
+
} as const;
|
|
177
|
+
`
|
|
178
|
+
: "";
|
|
166
179
|
return `import { registerBlockBindingsSource } from '@wordpress/blocks';
|
|
167
180
|
import { __ } from '@wordpress/i18n';
|
|
168
181
|
|
|
@@ -175,13 +188,14 @@ interface BindingSourceRegistration {
|
|
|
175
188
|
const BINDING_SOURCE_VALUES: Record<string, string> = {
|
|
176
189
|
\t${quoteTsString(bindingSourceSlug)}: ${quoteTsString(starterValue)},
|
|
177
190
|
};
|
|
191
|
+
${targetSource}
|
|
178
192
|
|
|
179
193
|
function resolveBindingSourceValue( field: string ): string {
|
|
180
194
|
\treturn BINDING_SOURCE_VALUES[ field ] ?? '';
|
|
181
195
|
}
|
|
182
196
|
|
|
183
197
|
registerBlockBindingsSource( {
|
|
184
|
-
\tname: ${quoteTsString(
|
|
198
|
+
\tname: ${quoteTsString(bindingSourceName)},
|
|
185
199
|
\tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
|
|
186
200
|
\tgetFieldsList() {
|
|
187
201
|
\t\treturn [
|
|
@@ -210,8 +224,99 @@ registerBlockBindingsSource( {
|
|
|
210
224
|
} );
|
|
211
225
|
`;
|
|
212
226
|
}
|
|
227
|
+
function resolveBindingTarget(options, namespace) {
|
|
228
|
+
const hasBlock = options.blockName !== undefined && options.blockName.trim().length > 0;
|
|
229
|
+
const hasAttribute = options.attributeName !== undefined && options.attributeName.trim().length > 0;
|
|
230
|
+
if (!hasBlock && !hasAttribute) {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
if (!hasBlock || !hasAttribute) {
|
|
234
|
+
throw new Error("`wp-typia add binding-source` requires --block and --attribute to be provided together.");
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
attributeName: assertValidBindingAttributeName(options.attributeName ?? ""),
|
|
238
|
+
blockSlug: resolveBindingTargetBlockSlug(options.blockName ?? "", namespace),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function formatBindingAttributeTypeMember(attributeName) {
|
|
242
|
+
const propertyName = /^[A-Za-z_$][\w$]*$/u.test(attributeName)
|
|
243
|
+
? attributeName
|
|
244
|
+
: JSON.stringify(attributeName);
|
|
245
|
+
return [
|
|
246
|
+
"\t/**",
|
|
247
|
+
"\t * Starter string attribute declared for WordPress Block Bindings.",
|
|
248
|
+
"\t */",
|
|
249
|
+
`\t${propertyName}?: string;`,
|
|
250
|
+
].join("\n");
|
|
251
|
+
}
|
|
252
|
+
function getInterfaceDeclaration(source, interfaceName) {
|
|
253
|
+
const sourceFile = ts.createSourceFile("types.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
254
|
+
let declaration;
|
|
255
|
+
const visit = (node) => {
|
|
256
|
+
if (ts.isInterfaceDeclaration(node) && node.name.text === interfaceName) {
|
|
257
|
+
declaration = node;
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
return ts.forEachChild(node, (child) => (visit(child) ? true : undefined)) ?? false;
|
|
261
|
+
};
|
|
262
|
+
visit(sourceFile);
|
|
263
|
+
return declaration ? { declaration, sourceFile } : undefined;
|
|
264
|
+
}
|
|
265
|
+
function getPropertyNameText(name) {
|
|
266
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
|
|
267
|
+
return name.text;
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
function interfaceHasAttributeMember(declaration, attributeName) {
|
|
272
|
+
return declaration.members.some((member) => ts.isPropertySignature(member) &&
|
|
273
|
+
member.name !== undefined &&
|
|
274
|
+
getPropertyNameText(member.name) === attributeName);
|
|
275
|
+
}
|
|
276
|
+
function insertBindingAttributeTypeMember(source, declaration, attributeName) {
|
|
277
|
+
let closeBracePosition = declaration.end - 1;
|
|
278
|
+
while (closeBracePosition > declaration.pos && source[closeBracePosition] !== "}") {
|
|
279
|
+
closeBracePosition -= 1;
|
|
280
|
+
}
|
|
281
|
+
if (source[closeBracePosition] !== "}") {
|
|
282
|
+
throw new Error("Unable to locate the target interface closing brace.");
|
|
283
|
+
}
|
|
284
|
+
const lineEnding = source.includes("\r\n") ? "\r\n" : "\n";
|
|
285
|
+
const beforeCloseBrace = source.slice(0, closeBracePosition);
|
|
286
|
+
const afterCloseBrace = source.slice(closeBracePosition);
|
|
287
|
+
const memberSource = formatBindingAttributeTypeMember(attributeName)
|
|
288
|
+
.split("\n")
|
|
289
|
+
.join(lineEnding);
|
|
290
|
+
const prefix = beforeCloseBrace.endsWith(lineEnding) ? "" : lineEnding;
|
|
291
|
+
return `${beforeCloseBrace}${prefix}${memberSource}${lineEnding}${afterCloseBrace}`;
|
|
292
|
+
}
|
|
293
|
+
async function ensureBindingTargetBlockAttributeType(projectDir, block, target) {
|
|
294
|
+
if (!block.attributeTypeName) {
|
|
295
|
+
throw new Error(`Workspace block "${block.slug}" must include attributeTypeName in scripts/block-config.ts before it can receive binding-source targets.`);
|
|
296
|
+
}
|
|
297
|
+
const typesPath = path.join(projectDir, block.typesFile);
|
|
298
|
+
const source = await fsp.readFile(typesPath, "utf8");
|
|
299
|
+
const targetInterface = getInterfaceDeclaration(source, block.attributeTypeName);
|
|
300
|
+
if (!targetInterface) {
|
|
301
|
+
throw new Error(`Unable to locate interface ${block.attributeTypeName} in ${block.typesFile}.`);
|
|
302
|
+
}
|
|
303
|
+
let nextSource = source;
|
|
304
|
+
if (!interfaceHasAttributeMember(targetInterface.declaration, target.attributeName)) {
|
|
305
|
+
nextSource = insertBindingAttributeTypeMember(source, targetInterface.declaration, target.attributeName);
|
|
306
|
+
await fsp.writeFile(typesPath, nextSource, "utf8");
|
|
307
|
+
}
|
|
308
|
+
await syncBlockMetadata({
|
|
309
|
+
blockJsonFile: path.join("src", "blocks", block.slug, "block.json"),
|
|
310
|
+
jsonSchemaFile: path.join("src", "blocks", block.slug, "typia.schema.json"),
|
|
311
|
+
manifestFile: path.join("src", "blocks", block.slug, "typia.manifest.json"),
|
|
312
|
+
openApiFile: path.join("src", "blocks", block.slug, "typia.openapi.json"),
|
|
313
|
+
projectRoot: projectDir,
|
|
314
|
+
sourceTypeName: block.attributeTypeName,
|
|
315
|
+
typesFile: block.typesFile,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
213
318
|
function buildEditorPluginTypesSource(editorPluginSlug) {
|
|
214
|
-
const typeName = `${
|
|
319
|
+
const typeName = `${toPascalCase(editorPluginSlug)}EditorPluginModel`;
|
|
215
320
|
return `export interface ${typeName} {
|
|
216
321
|
\tprimaryActionLabel: string;
|
|
217
322
|
\tsummary: string;
|
|
@@ -219,22 +324,22 @@ function buildEditorPluginTypesSource(editorPluginSlug) {
|
|
|
219
324
|
`;
|
|
220
325
|
}
|
|
221
326
|
function buildEditorPluginDataSource(editorPluginSlug, slot) {
|
|
222
|
-
const typeName = `${
|
|
327
|
+
const typeName = `${toPascalCase(editorPluginSlug)}EditorPluginModel`;
|
|
223
328
|
const pluginTitle = toTitleCase(editorPluginSlug);
|
|
224
|
-
const modelFactoryName = `get${
|
|
225
|
-
const enabledFactoryName = `is${
|
|
329
|
+
const modelFactoryName = `get${toPascalCase(editorPluginSlug)}EditorPluginModel`;
|
|
330
|
+
const enabledFactoryName = `is${toPascalCase(editorPluginSlug)}Enabled`;
|
|
226
331
|
return `import type { ${typeName} } from './types';
|
|
227
332
|
|
|
228
333
|
export const EDITOR_PLUGIN_SLOT = ${quoteTsString(slot)} as const;
|
|
229
334
|
export const REQUIRED_CAPABILITY = 'edit_posts' as const;
|
|
230
335
|
|
|
231
|
-
const
|
|
336
|
+
const DEFAULT_EDITOR_PLUGIN_MODEL: ${typeName} = {
|
|
232
337
|
\tprimaryActionLabel: ${quoteTsString(`Review ${pluginTitle}`)},
|
|
233
338
|
\tsummary: ${quoteTsString(`Replace this summary with your ${pluginTitle} workflow state.`)},
|
|
234
339
|
};
|
|
235
340
|
|
|
236
341
|
export function ${modelFactoryName}(): ${typeName} {
|
|
237
|
-
\treturn
|
|
342
|
+
\treturn DEFAULT_EDITOR_PLUGIN_MODEL;
|
|
238
343
|
}
|
|
239
344
|
|
|
240
345
|
export function ${enabledFactoryName}(): boolean {
|
|
@@ -242,11 +347,52 @@ export function ${enabledFactoryName}(): boolean {
|
|
|
242
347
|
}
|
|
243
348
|
`;
|
|
244
349
|
}
|
|
245
|
-
function
|
|
246
|
-
const pascalName =
|
|
247
|
-
const modelFactoryName = `get${pascalName}
|
|
350
|
+
function buildEditorPluginSurfaceSource(editorPluginSlug, slot, textDomain) {
|
|
351
|
+
const pascalName = toPascalCase(editorPluginSlug);
|
|
352
|
+
const modelFactoryName = `get${pascalName}EditorPluginModel`;
|
|
248
353
|
const enabledFactoryName = `is${pascalName}Enabled`;
|
|
249
|
-
const componentName = `${pascalName}
|
|
354
|
+
const componentName = `${pascalName}Surface`;
|
|
355
|
+
if (slot === "document-setting-panel") {
|
|
356
|
+
return `import { Button } from '@wordpress/components';
|
|
357
|
+
import { PluginDocumentSettingPanel } from '@wordpress/editor';
|
|
358
|
+
import { __ } from '@wordpress/i18n';
|
|
359
|
+
|
|
360
|
+
import { ${modelFactoryName}, ${enabledFactoryName} } from './data';
|
|
361
|
+
import './style.scss';
|
|
362
|
+
|
|
363
|
+
export interface ${componentName}Props {
|
|
364
|
+
\tsurfaceName: string;
|
|
365
|
+
\ttitle: string;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function ${componentName}( {
|
|
369
|
+
\tsurfaceName,
|
|
370
|
+
\ttitle,
|
|
371
|
+
}: ${componentName}Props ) {
|
|
372
|
+
\tif ( ! ${enabledFactoryName}() ) {
|
|
373
|
+
\t\treturn null;
|
|
374
|
+
\t}
|
|
375
|
+
|
|
376
|
+
\tconst editorPluginModel = ${modelFactoryName}();
|
|
377
|
+
|
|
378
|
+
\treturn (
|
|
379
|
+
\t\t<PluginDocumentSettingPanel
|
|
380
|
+
\t\t\tclassName="wp-typia-editor-plugin-shell"
|
|
381
|
+
\t\t\tname={ surfaceName }
|
|
382
|
+
\t\t\ttitle={ title }
|
|
383
|
+
\t\t>
|
|
384
|
+
\t\t\t<p>{ editorPluginModel.summary }</p>
|
|
385
|
+
\t\t\t<Button variant="secondary">
|
|
386
|
+
\t\t\t\t{ editorPluginModel.primaryActionLabel }
|
|
387
|
+
\t\t\t</Button>
|
|
388
|
+
\t\t\t<p className="wp-typia-editor-plugin-shell__hint">
|
|
389
|
+
\t\t\t\t{ __( 'Use data.ts to add post type, capability, or editor context guards before showing this panel.', ${quoteTsString(textDomain)} ) }
|
|
390
|
+
\t\t\t</p>
|
|
391
|
+
\t\t</PluginDocumentSettingPanel>
|
|
392
|
+
\t);
|
|
393
|
+
}
|
|
394
|
+
`;
|
|
395
|
+
}
|
|
250
396
|
return `import { Button, PanelBody } from '@wordpress/components';
|
|
251
397
|
import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/editor';
|
|
252
398
|
import { __ } from '@wordpress/i18n';
|
|
@@ -255,34 +401,34 @@ import { ${modelFactoryName}, ${enabledFactoryName} } from './data';
|
|
|
255
401
|
import './style.scss';
|
|
256
402
|
|
|
257
403
|
export interface ${componentName}Props {
|
|
258
|
-
\
|
|
404
|
+
\tsurfaceName: string;
|
|
259
405
|
\ttitle: string;
|
|
260
406
|
}
|
|
261
407
|
|
|
262
408
|
export function ${componentName}( {
|
|
263
|
-
\
|
|
409
|
+
\tsurfaceName,
|
|
264
410
|
\ttitle,
|
|
265
411
|
}: ${componentName}Props ) {
|
|
266
412
|
\tif ( ! ${enabledFactoryName}() ) {
|
|
267
413
|
\t\treturn null;
|
|
268
414
|
\t}
|
|
269
415
|
|
|
270
|
-
\tconst
|
|
416
|
+
\tconst editorPluginModel = ${modelFactoryName}();
|
|
271
417
|
|
|
272
418
|
\treturn (
|
|
273
419
|
\t\t<>
|
|
274
|
-
\t\t\t<PluginSidebarMoreMenuItem target={
|
|
420
|
+
\t\t\t<PluginSidebarMoreMenuItem target={ surfaceName }>
|
|
275
421
|
\t\t\t\t{ title }
|
|
276
422
|
\t\t\t</PluginSidebarMoreMenuItem>
|
|
277
|
-
\t\t\t<PluginSidebar name={
|
|
423
|
+
\t\t\t<PluginSidebar name={ surfaceName } title={ title }>
|
|
278
424
|
\t\t\t\t<div className="wp-typia-editor-plugin-shell">
|
|
279
425
|
\t\t\t\t\t<PanelBody
|
|
280
426
|
\t\t\t\t\t\tinitialOpen
|
|
281
427
|
\t\t\t\t\t\ttitle={ __( 'Document workflow', ${quoteTsString(textDomain)} ) }
|
|
282
428
|
\t\t\t\t\t>
|
|
283
|
-
\t\t\t\t\t\t<p>{
|
|
429
|
+
\t\t\t\t\t\t<p>{ editorPluginModel.summary }</p>
|
|
284
430
|
\t\t\t\t\t\t<Button variant="secondary">
|
|
285
|
-
\t\t\t\t\t\t\t{
|
|
431
|
+
\t\t\t\t\t\t\t{ editorPluginModel.primaryActionLabel }
|
|
286
432
|
\t\t\t\t\t\t</Button>
|
|
287
433
|
\t\t\t\t\t</PanelBody>
|
|
288
434
|
\t\t\t\t</div>
|
|
@@ -293,24 +439,26 @@ export function ${componentName}( {
|
|
|
293
439
|
`;
|
|
294
440
|
}
|
|
295
441
|
function buildEditorPluginEntrySource(editorPluginSlug, namespace, textDomain) {
|
|
296
|
-
const pascalName =
|
|
297
|
-
const componentName = `${pascalName}
|
|
442
|
+
const pascalName = toPascalCase(editorPluginSlug);
|
|
443
|
+
const componentName = `${pascalName}Surface`;
|
|
298
444
|
const pluginName = `${namespace}-${editorPluginSlug}`;
|
|
445
|
+
const surfaceName = `${pluginName}-surface`;
|
|
299
446
|
const pluginTitle = toTitleCase(editorPluginSlug);
|
|
300
447
|
return `import { registerPlugin } from '@wordpress/plugins';
|
|
301
448
|
import { __ } from '@wordpress/i18n';
|
|
302
449
|
|
|
303
450
|
import { REQUIRED_CAPABILITY } from './data';
|
|
304
|
-
import { ${componentName} } from './
|
|
451
|
+
import { ${componentName} } from './Surface';
|
|
305
452
|
|
|
306
453
|
const EDITOR_PLUGIN_NAME = ${quoteTsString(pluginName)};
|
|
454
|
+
const EDITOR_PLUGIN_SURFACE_NAME = ${quoteTsString(surfaceName)};
|
|
307
455
|
const EDITOR_PLUGIN_TITLE = __( ${quoteTsString(pluginTitle)}, ${quoteTsString(textDomain)} );
|
|
308
456
|
|
|
309
457
|
registerPlugin( EDITOR_PLUGIN_NAME, {
|
|
310
458
|
\ticon: 'admin-generic',
|
|
311
459
|
\trender: () => (
|
|
312
460
|
\t\t<${componentName}
|
|
313
|
-
\t\t\
|
|
461
|
+
\t\t\tsurfaceName={ EDITOR_PLUGIN_SURFACE_NAME }
|
|
314
462
|
\t\t\ttitle={ EDITOR_PLUGIN_TITLE }
|
|
315
463
|
\t\t/>
|
|
316
464
|
\t),
|
|
@@ -327,6 +475,11 @@ function buildEditorPluginStyleSource() {
|
|
|
327
475
|
.wp-typia-editor-plugin-shell p {
|
|
328
476
|
\tmargin: 0 0 12px;
|
|
329
477
|
}
|
|
478
|
+
|
|
479
|
+
.wp-typia-editor-plugin-shell__hint {
|
|
480
|
+
\tcolor: #757575;
|
|
481
|
+
\tfont-size: 12px;
|
|
482
|
+
}
|
|
330
483
|
`;
|
|
331
484
|
}
|
|
332
485
|
function buildBindingSourceIndexSource(bindingSourceSlugs) {
|
|
@@ -445,7 +598,6 @@ function ${bindingEditorEnqueueFunctionName}() {
|
|
|
445
598
|
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
446
599
|
/\?>\s*$/u,
|
|
447
600
|
];
|
|
448
|
-
const hasPhpFunctionDefinition = (functionName) => new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(nextSource);
|
|
449
601
|
const insertPhpSnippet = (snippet) => {
|
|
450
602
|
for (const anchor of insertionAnchors) {
|
|
451
603
|
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
@@ -464,10 +616,10 @@ function ${bindingEditorEnqueueFunctionName}() {
|
|
|
464
616
|
}
|
|
465
617
|
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
466
618
|
};
|
|
467
|
-
if (!hasPhpFunctionDefinition(bindingRegistrationFunctionName)) {
|
|
619
|
+
if (!hasPhpFunctionDefinition(nextSource, bindingRegistrationFunctionName)) {
|
|
468
620
|
insertPhpSnippet(bindingRegistrationFunction);
|
|
469
621
|
}
|
|
470
|
-
if (!hasPhpFunctionDefinition(bindingEditorEnqueueFunctionName)) {
|
|
622
|
+
if (!hasPhpFunctionDefinition(nextSource, bindingEditorEnqueueFunctionName)) {
|
|
471
623
|
insertPhpSnippet(bindingEditorEnqueueFunction);
|
|
472
624
|
}
|
|
473
625
|
if (!nextSource.includes(bindingRegistrationHook)) {
|
|
@@ -528,7 +680,6 @@ function ${enqueueFunctionName}() {
|
|
|
528
680
|
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
529
681
|
/\?>\s*$/u,
|
|
530
682
|
];
|
|
531
|
-
const hasPhpFunctionDefinition = (functionName) => new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(nextSource);
|
|
532
683
|
const insertPhpSnippet = (snippet) => {
|
|
533
684
|
for (const anchor of insertionAnchors) {
|
|
534
685
|
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
@@ -547,7 +698,7 @@ function ${enqueueFunctionName}() {
|
|
|
547
698
|
}
|
|
548
699
|
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
549
700
|
};
|
|
550
|
-
if (!hasPhpFunctionDefinition(enqueueFunctionName)) {
|
|
701
|
+
if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
|
|
551
702
|
insertPhpSnippet(enqueueFunction);
|
|
552
703
|
}
|
|
553
704
|
else {
|
|
@@ -657,7 +808,7 @@ async function writeEditorPluginRegistry(projectDir, editorPluginSlug) {
|
|
|
657
808
|
* Defaults to `process.cwd()`.
|
|
658
809
|
* @param options.editorPluginName Human-entered editor-plugin name that will be
|
|
659
810
|
* normalized and validated before files are written.
|
|
660
|
-
* @param options.slot Optional editor plugin shell slot. Defaults to `
|
|
811
|
+
* @param options.slot Optional editor plugin shell slot. Defaults to `sidebar`.
|
|
661
812
|
* @returns A promise that resolves with the normalized `editorPluginSlug`, chosen
|
|
662
813
|
* `slot`, and owning `projectDir` after the scaffold files and inventory entry
|
|
663
814
|
* are written successfully.
|
|
@@ -666,7 +817,7 @@ async function writeEditorPluginRegistry(projectDir, editorPluginSlug) {
|
|
|
666
817
|
*/
|
|
667
818
|
export async function runAddEditorPluginCommand({ cwd = process.cwd(), editorPluginName, slot, }) {
|
|
668
819
|
const workspace = resolveWorkspaceProject(cwd);
|
|
669
|
-
const editorPluginSlug = assertValidGeneratedSlug("Editor plugin name", normalizeBlockSlug(editorPluginName), "wp-typia add editor-plugin <name> [--slot <
|
|
820
|
+
const editorPluginSlug = assertValidGeneratedSlug("Editor plugin name", normalizeBlockSlug(editorPluginName), "wp-typia add editor-plugin <name> [--slot <sidebar|document-setting-panel>]");
|
|
670
821
|
const resolvedSlot = assertValidEditorPluginSlot(slot);
|
|
671
822
|
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
672
823
|
assertEditorPluginDoesNotExist(workspace.projectDir, editorPluginSlug, inventory);
|
|
@@ -677,7 +828,7 @@ export async function runAddEditorPluginCommand({ cwd = process.cwd(), editorPlu
|
|
|
677
828
|
const webpackConfigPath = path.join(workspace.projectDir, "webpack.config.js");
|
|
678
829
|
const editorPluginDir = path.join(workspace.projectDir, "src", "editor-plugins", editorPluginSlug);
|
|
679
830
|
const entryFilePath = path.join(editorPluginDir, "index.tsx");
|
|
680
|
-
const
|
|
831
|
+
const surfaceFilePath = path.join(editorPluginDir, "Surface.tsx");
|
|
681
832
|
const dataFilePath = path.join(editorPluginDir, "data.ts");
|
|
682
833
|
const typesFilePath = path.join(editorPluginDir, "types.ts");
|
|
683
834
|
const styleFilePath = path.join(editorPluginDir, "style.scss");
|
|
@@ -698,7 +849,7 @@ export async function runAddEditorPluginCommand({ cwd = process.cwd(), editorPlu
|
|
|
698
849
|
await ensureEditorPluginBuildScriptAnchors(workspace);
|
|
699
850
|
await ensureEditorPluginWebpackAnchors(workspace);
|
|
700
851
|
await fsp.writeFile(entryFilePath, buildEditorPluginEntrySource(editorPluginSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
|
|
701
|
-
await fsp.writeFile(
|
|
852
|
+
await fsp.writeFile(surfaceFilePath, buildEditorPluginSurfaceSource(editorPluginSlug, resolvedSlot, workspace.workspace.textDomain), "utf8");
|
|
702
853
|
await fsp.writeFile(dataFilePath, buildEditorPluginDataSource(editorPluginSlug, resolvedSlot), "utf8");
|
|
703
854
|
await fsp.writeFile(typesFilePath, buildEditorPluginTypesSource(editorPluginSlug), "utf8");
|
|
704
855
|
await fsp.writeFile(styleFilePath, buildEditorPluginStyleSource(), "utf8");
|
|
@@ -768,42 +919,75 @@ export async function runAddPatternCommand({ cwd = process.cwd(), patternName, }
|
|
|
768
919
|
* Add one block binding source scaffold to an official workspace project.
|
|
769
920
|
*
|
|
770
921
|
* @param options Command options for the binding-source scaffold workflow.
|
|
922
|
+
* @param options.attributeName Optional generated block attribute to declare as
|
|
923
|
+
* bindable. Must be provided together with `blockName`.
|
|
924
|
+
* @param options.blockName Optional generated block slug or full block name to
|
|
925
|
+
* receive the bindable attribute wiring. Must be provided together with
|
|
926
|
+
* `attributeName`.
|
|
771
927
|
* @param options.bindingSourceName Human-entered binding source name that will
|
|
772
928
|
* be normalized and validated before files are written.
|
|
773
929
|
* @param options.cwd Working directory used to resolve the nearest official
|
|
774
930
|
* workspace. Defaults to `process.cwd()`.
|
|
775
931
|
* @returns A promise that resolves with the normalized `bindingSourceSlug` and
|
|
776
|
-
* owning `projectDir` after the server/editor files
|
|
777
|
-
* been written successfully.
|
|
932
|
+
* owning `projectDir` after the server/editor files, optional target block
|
|
933
|
+
* metadata, and inventory entry have been written successfully.
|
|
778
934
|
* @throws {Error} When the command is run outside an official workspace, when
|
|
779
|
-
* the slug is invalid,
|
|
935
|
+
* the slug is invalid, when a binding target is incomplete or unknown, or when
|
|
936
|
+
* a conflicting file or inventory entry exists.
|
|
780
937
|
*/
|
|
781
|
-
export async function runAddBindingSourceCommand({ bindingSourceName, cwd = process.cwd(), }) {
|
|
938
|
+
export async function runAddBindingSourceCommand({ attributeName, bindingSourceName, blockName, cwd = process.cwd(), }) {
|
|
782
939
|
const workspace = resolveWorkspaceProject(cwd);
|
|
783
|
-
const bindingSourceSlug = assertValidGeneratedSlug("Binding source name", normalizeBlockSlug(bindingSourceName), "wp-typia add binding-source <name>");
|
|
940
|
+
const bindingSourceSlug = assertValidGeneratedSlug("Binding source name", normalizeBlockSlug(bindingSourceName), "wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>]");
|
|
784
941
|
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
785
942
|
assertBindingSourceDoesNotExist(workspace.projectDir, bindingSourceSlug, inventory);
|
|
943
|
+
const target = resolveBindingTarget({
|
|
944
|
+
attributeName,
|
|
945
|
+
blockName,
|
|
946
|
+
}, workspace.workspace.namespace);
|
|
947
|
+
const targetBlock = target ? resolveWorkspaceBlock(inventory, target.blockSlug) : undefined;
|
|
786
948
|
const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
|
|
787
949
|
const bootstrapPath = getWorkspaceBootstrapPath(workspace);
|
|
788
950
|
const bindingsIndexPath = resolveBindingSourceRegistryPath(workspace.projectDir);
|
|
789
951
|
const bindingSourceDir = path.join(workspace.projectDir, "src", "bindings", bindingSourceSlug);
|
|
790
952
|
const serverFilePath = path.join(bindingSourceDir, "server.php");
|
|
791
953
|
const editorFilePath = path.join(bindingSourceDir, "editor.ts");
|
|
954
|
+
const blockJsonPath = target
|
|
955
|
+
? path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "block.json")
|
|
956
|
+
: undefined;
|
|
957
|
+
const targetGeneratedMetadataPaths = target
|
|
958
|
+
? [
|
|
959
|
+
path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia.manifest.json"),
|
|
960
|
+
path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia.openapi.json"),
|
|
961
|
+
path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia.schema.json"),
|
|
962
|
+
path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia-validator.php"),
|
|
963
|
+
]
|
|
964
|
+
: [];
|
|
792
965
|
const mutationSnapshot = {
|
|
793
|
-
fileSources: await snapshotWorkspaceFiles([
|
|
966
|
+
fileSources: await snapshotWorkspaceFiles([
|
|
967
|
+
blockConfigPath,
|
|
968
|
+
bootstrapPath,
|
|
969
|
+
bindingsIndexPath,
|
|
970
|
+
...(blockJsonPath ? [blockJsonPath] : []),
|
|
971
|
+
...(targetBlock ? [path.join(workspace.projectDir, targetBlock.typesFile)] : []),
|
|
972
|
+
...targetGeneratedMetadataPaths,
|
|
973
|
+
]),
|
|
794
974
|
snapshotDirs: [],
|
|
795
975
|
targetPaths: [bindingSourceDir],
|
|
796
976
|
};
|
|
797
977
|
try {
|
|
798
978
|
await fsp.mkdir(bindingSourceDir, { recursive: true });
|
|
799
979
|
await ensureBindingSourceBootstrapAnchors(workspace);
|
|
800
|
-
await fsp.writeFile(serverFilePath, buildBindingSourceServerSource(bindingSourceSlug, workspace.workspace.phpPrefix, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
|
|
801
|
-
await fsp.writeFile(editorFilePath, buildBindingSourceEditorSource(bindingSourceSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
|
|
980
|
+
await fsp.writeFile(serverFilePath, buildBindingSourceServerSource(bindingSourceSlug, workspace.workspace.phpPrefix, workspace.workspace.namespace, workspace.workspace.textDomain, target), "utf8");
|
|
981
|
+
await fsp.writeFile(editorFilePath, buildBindingSourceEditorSource(bindingSourceSlug, workspace.workspace.namespace, workspace.workspace.textDomain, target), "utf8");
|
|
982
|
+
if (target && targetBlock) {
|
|
983
|
+
await ensureBindingTargetBlockAttributeType(workspace.projectDir, targetBlock, target);
|
|
984
|
+
}
|
|
802
985
|
await writeBindingSourceRegistry(workspace.projectDir, bindingSourceSlug);
|
|
803
986
|
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
804
|
-
bindingSourceEntries: [buildBindingSourceConfigEntry(bindingSourceSlug)],
|
|
987
|
+
bindingSourceEntries: [buildBindingSourceConfigEntry(bindingSourceSlug, target)],
|
|
805
988
|
});
|
|
806
989
|
return {
|
|
990
|
+
...(target ? { attributeName: target.attributeName, blockSlug: target.blockSlug } : {}),
|
|
807
991
|
bindingSourceSlug,
|
|
808
992
|
projectDir: workspace.projectDir,
|
|
809
993
|
};
|