@wp-typia/project-tools 0.20.1 → 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-block.js +1 -1
- 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 +84 -1
- package/dist/runtime/cli-diagnostics.js +90 -3
- 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 +35 -10
- 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 +333 -53
- package/dist/runtime/workspace-inventory.d.ts +43 -2
- package/dist/runtime/workspace-inventory.js +138 -5
- package/package.json +2 -2
|
@@ -3,10 +3,178 @@ import { promises as fsp } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
5
5
|
import { appendWorkspaceInventoryEntries, readWorkspaceInventory } from "./workspace-inventory.js";
|
|
6
|
-
import { toKebabCase, toTitleCase } from "./string-case.js";
|
|
6
|
+
import { toKebabCase, toSnakeCase, toTitleCase } from "./string-case.js";
|
|
7
7
|
import { assertValidGeneratedSlug, assertValidHookAnchor, assertValidHookedBlockPosition, assertVariationDoesNotExist, getMutableBlockHooks, normalizeBlockSlug, patchFile, quoteTsString, readWorkspaceBlockJson, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
8
8
|
const VARIATIONS_IMPORT_LINE = "import { registerWorkspaceVariations } from './variations';";
|
|
9
|
+
const VARIATIONS_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceVariations\s*\}\s*from\s*["']\.\/variations["']\s*;?\s*$/mu;
|
|
9
10
|
const VARIATIONS_CALL_LINE = "registerWorkspaceVariations();";
|
|
11
|
+
const VARIATIONS_CALL_PATTERN = /registerWorkspaceVariations\s*\(\s*\)\s*;?/u;
|
|
12
|
+
const BLOCK_STYLES_IMPORT_LINE = "import { registerWorkspaceBlockStyles } from './styles';";
|
|
13
|
+
const BLOCK_STYLES_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceBlockStyles\s*\}\s*from\s*["']\.\/styles["']\s*;?\s*$/mu;
|
|
14
|
+
const BLOCK_STYLES_CALL_LINE = "registerWorkspaceBlockStyles();";
|
|
15
|
+
const BLOCK_STYLES_CALL_PATTERN = /registerWorkspaceBlockStyles\s*\(\s*\)\s*;?/u;
|
|
16
|
+
const BLOCK_TRANSFORMS_IMPORT_LINE = "import { applyWorkspaceBlockTransforms } from './transforms';";
|
|
17
|
+
const BLOCK_TRANSFORMS_IMPORT_PATTERN = /^\s*import\s*\{\s*applyWorkspaceBlockTransforms\s*\}\s*from\s*["']\.\/transforms["']\s*;?\s*$/mu;
|
|
18
|
+
const BLOCK_TRANSFORMS_CALL_LINE = "applyWorkspaceBlockTransforms(registration.settings);";
|
|
19
|
+
const BLOCK_TRANSFORMS_CALL_PATTERN = /applyWorkspaceBlockTransforms\s*\(\s*registration\s*\.\s*settings\s*\)\s*;?/u;
|
|
20
|
+
const SCAFFOLD_REGISTRATION_SETTINGS_CALL_PATTERN = /registerScaffoldBlockType\s*\(\s*registration\s*\.\s*name\s*,\s*registration\s*\.\s*settings\s*\)\s*;?/u;
|
|
21
|
+
const FULL_BLOCK_NAME_PATTERN = /^[a-z0-9-]+\/[a-z0-9-]+$/u;
|
|
22
|
+
function maskSourceSegment(segment) {
|
|
23
|
+
return segment.replace(/[^\n\r]/gu, " ");
|
|
24
|
+
}
|
|
25
|
+
function maskTypeScriptComments(source) {
|
|
26
|
+
return source
|
|
27
|
+
.replace(/\/\*[\s\S]*?\*\//gu, maskSourceSegment)
|
|
28
|
+
.replace(/\/\/[^\n\r]*/gu, maskSourceSegment);
|
|
29
|
+
}
|
|
30
|
+
// Preserve source offsets so executable-code match indexes still map to the original file.
|
|
31
|
+
function maskTypeScriptCommentsAndLiterals(source) {
|
|
32
|
+
let maskedSource = "";
|
|
33
|
+
let index = 0;
|
|
34
|
+
while (index < source.length) {
|
|
35
|
+
const current = source[index];
|
|
36
|
+
const next = source[index + 1];
|
|
37
|
+
if (current === "/" && next === "/") {
|
|
38
|
+
const start = index;
|
|
39
|
+
index += 2;
|
|
40
|
+
while (index < source.length &&
|
|
41
|
+
source[index] !== "\n" &&
|
|
42
|
+
source[index] !== "\r") {
|
|
43
|
+
index += 1;
|
|
44
|
+
}
|
|
45
|
+
maskedSource += maskSourceSegment(source.slice(start, index));
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (current === "/" && next === "*") {
|
|
49
|
+
const start = index;
|
|
50
|
+
index += 2;
|
|
51
|
+
while (index < source.length &&
|
|
52
|
+
!(source[index] === "*" && source[index + 1] === "/")) {
|
|
53
|
+
index += 1;
|
|
54
|
+
}
|
|
55
|
+
index = Math.min(index + 2, source.length);
|
|
56
|
+
maskedSource += maskSourceSegment(source.slice(start, index));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
60
|
+
const start = index;
|
|
61
|
+
const quote = current;
|
|
62
|
+
index += 1;
|
|
63
|
+
while (index < source.length) {
|
|
64
|
+
const char = source[index];
|
|
65
|
+
if (char === "\\") {
|
|
66
|
+
index += 2;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
index += 1;
|
|
70
|
+
if (char === quote) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
maskedSource += maskSourceSegment(source.slice(start, index));
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
maskedSource += current;
|
|
78
|
+
index += 1;
|
|
79
|
+
}
|
|
80
|
+
return maskedSource;
|
|
81
|
+
}
|
|
82
|
+
function hasExecutablePattern(source, pattern) {
|
|
83
|
+
return pattern.test(maskTypeScriptCommentsAndLiterals(source));
|
|
84
|
+
}
|
|
85
|
+
function hasUncommentedPattern(source, pattern) {
|
|
86
|
+
return pattern.test(maskTypeScriptComments(source));
|
|
87
|
+
}
|
|
88
|
+
function findExecutablePatternMatch(source, patterns) {
|
|
89
|
+
const maskedSource = maskTypeScriptCommentsAndLiterals(source);
|
|
90
|
+
for (const pattern of patterns) {
|
|
91
|
+
const match = pattern.exec(maskedSource);
|
|
92
|
+
if (match && match.index !== undefined) {
|
|
93
|
+
return {
|
|
94
|
+
end: match.index + match[0].length,
|
|
95
|
+
start: match.index,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function isIdentifierBoundary(source, index) {
|
|
102
|
+
if (index < 0 || index >= source.length) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return !/[A-Za-z0-9_$]/u.test(source[index] ?? "");
|
|
106
|
+
}
|
|
107
|
+
function skipWhitespace(source, index) {
|
|
108
|
+
let cursor = index;
|
|
109
|
+
while (cursor < source.length && /\s/u.test(source[cursor] ?? "")) {
|
|
110
|
+
cursor += 1;
|
|
111
|
+
}
|
|
112
|
+
return cursor;
|
|
113
|
+
}
|
|
114
|
+
function findMatchingDelimiterEnd(source, openIndex, openDelimiter, closeDelimiter) {
|
|
115
|
+
let depth = 0;
|
|
116
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
117
|
+
const char = source[index];
|
|
118
|
+
if (char === openDelimiter) {
|
|
119
|
+
depth += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (char === closeDelimiter) {
|
|
123
|
+
depth -= 1;
|
|
124
|
+
if (depth === 0) {
|
|
125
|
+
return index + 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
function findExecutableCallRange(source, callName) {
|
|
132
|
+
const maskedSource = maskTypeScriptCommentsAndLiterals(source);
|
|
133
|
+
let searchIndex = 0;
|
|
134
|
+
while (searchIndex < maskedSource.length) {
|
|
135
|
+
const callNameIndex = maskedSource.indexOf(callName, searchIndex);
|
|
136
|
+
if (callNameIndex === -1) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const callNameEnd = callNameIndex + callName.length;
|
|
140
|
+
if (!isIdentifierBoundary(maskedSource, callNameIndex - 1) ||
|
|
141
|
+
!isIdentifierBoundary(maskedSource, callNameEnd)) {
|
|
142
|
+
searchIndex = callNameEnd;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
let cursor = skipWhitespace(maskedSource, callNameEnd);
|
|
146
|
+
if (maskedSource[cursor] === "<") {
|
|
147
|
+
const genericEnd = findMatchingDelimiterEnd(maskedSource, cursor, "<", ">");
|
|
148
|
+
if (genericEnd === undefined) {
|
|
149
|
+
searchIndex = callNameEnd;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
cursor = skipWhitespace(maskedSource, genericEnd);
|
|
153
|
+
}
|
|
154
|
+
if (maskedSource[cursor] !== "(") {
|
|
155
|
+
searchIndex = callNameEnd;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const callEnd = findMatchingDelimiterEnd(maskedSource, cursor, "(", ")");
|
|
159
|
+
if (callEnd === undefined) {
|
|
160
|
+
searchIndex = callNameEnd;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
let end = skipWhitespace(maskedSource, callEnd);
|
|
164
|
+
if (maskedSource[end] === ";") {
|
|
165
|
+
end += 1;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
end,
|
|
169
|
+
start: callNameIndex,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
function findBlockRegistrationCallRange(source) {
|
|
175
|
+
return (findExecutableCallRange(source, "registerScaffoldBlockType") ??
|
|
176
|
+
findExecutableCallRange(source, "registerBlockType"));
|
|
177
|
+
}
|
|
10
178
|
function buildVariationConfigEntry(blockSlug, variationSlug) {
|
|
11
179
|
return [
|
|
12
180
|
"\t{",
|
|
@@ -78,36 +246,215 @@ export function registerWorkspaceVariations() {
|
|
|
78
246
|
}
|
|
79
247
|
`;
|
|
80
248
|
}
|
|
249
|
+
function buildWorkspaceConstName(prefix, slug) {
|
|
250
|
+
return `workspace${prefix}_${toSnakeCase(slug)}`;
|
|
251
|
+
}
|
|
252
|
+
function buildBlockStyleConfigEntry(blockSlug, styleSlug) {
|
|
253
|
+
return [
|
|
254
|
+
"\t{",
|
|
255
|
+
`\t\tblock: ${quoteTsString(blockSlug)},`,
|
|
256
|
+
`\t\tfile: ${quoteTsString(`src/blocks/${blockSlug}/styles/${styleSlug}.ts`)},`,
|
|
257
|
+
`\t\tslug: ${quoteTsString(styleSlug)},`,
|
|
258
|
+
"\t},",
|
|
259
|
+
].join("\n");
|
|
260
|
+
}
|
|
261
|
+
function buildBlockTransformConfigEntry(options) {
|
|
262
|
+
return [
|
|
263
|
+
"\t{",
|
|
264
|
+
`\t\tblock: ${quoteTsString(options.blockSlug)},`,
|
|
265
|
+
`\t\tfile: ${quoteTsString(`src/blocks/${options.blockSlug}/transforms/${options.transformSlug}.ts`)},`,
|
|
266
|
+
`\t\tfrom: ${quoteTsString(options.fromBlockName)},`,
|
|
267
|
+
`\t\tslug: ${quoteTsString(options.transformSlug)},`,
|
|
268
|
+
`\t\tto: ${quoteTsString(options.toBlockName)},`,
|
|
269
|
+
"\t},",
|
|
270
|
+
].join("\n");
|
|
271
|
+
}
|
|
272
|
+
function getBlockStyleConstBindings(styleSlugs) {
|
|
273
|
+
const seenConstNames = new Map();
|
|
274
|
+
return styleSlugs.map((styleSlug) => {
|
|
275
|
+
const constName = buildWorkspaceConstName("BlockStyle", styleSlug);
|
|
276
|
+
const previousSlug = seenConstNames.get(constName);
|
|
277
|
+
if (previousSlug && previousSlug !== styleSlug) {
|
|
278
|
+
throw new Error(`Style slugs "${previousSlug}" and "${styleSlug}" generate the same registry identifier "${constName}". Rename one of the styles.`);
|
|
279
|
+
}
|
|
280
|
+
seenConstNames.set(constName, styleSlug);
|
|
281
|
+
return { constName, styleSlug };
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function getBlockTransformConstBindings(transformSlugs) {
|
|
285
|
+
const seenConstNames = new Map();
|
|
286
|
+
return transformSlugs.map((transformSlug) => {
|
|
287
|
+
const constName = buildWorkspaceConstName("BlockTransform", transformSlug);
|
|
288
|
+
const previousSlug = seenConstNames.get(constName);
|
|
289
|
+
if (previousSlug && previousSlug !== transformSlug) {
|
|
290
|
+
throw new Error(`Transform slugs "${previousSlug}" and "${transformSlug}" generate the same registry identifier "${constName}". Rename one of the transforms.`);
|
|
291
|
+
}
|
|
292
|
+
seenConstNames.set(constName, transformSlug);
|
|
293
|
+
return { constName, transformSlug };
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function buildBlockStyleSource(styleSlug, textDomain) {
|
|
297
|
+
const styleTitle = toTitleCase(styleSlug);
|
|
298
|
+
const styleConstName = buildWorkspaceConstName("BlockStyle", styleSlug);
|
|
299
|
+
return `import { __ } from '@wordpress/i18n';
|
|
300
|
+
|
|
301
|
+
export const ${styleConstName} = {
|
|
302
|
+
\tname: ${quoteTsString(styleSlug)},
|
|
303
|
+
\tlabel: __( ${quoteTsString(styleTitle)}, ${quoteTsString(textDomain)} ),
|
|
304
|
+
} as const;
|
|
305
|
+
`;
|
|
306
|
+
}
|
|
307
|
+
function buildBlockStyleIndexSource(styleSlugs) {
|
|
308
|
+
const styleBindings = getBlockStyleConstBindings(styleSlugs);
|
|
309
|
+
const importLines = styleBindings
|
|
310
|
+
.map(({ constName, styleSlug }) => `import { ${constName} } from './${styleSlug}';`)
|
|
311
|
+
.join("\n");
|
|
312
|
+
const styleConstNames = styleBindings.map(({ constName }) => constName).join(",\n\t");
|
|
313
|
+
return `import { registerBlockStyle } from '@wordpress/blocks';
|
|
314
|
+
import metadata from '../block.json';
|
|
315
|
+
${importLines ? `\n${importLines}` : ""}
|
|
316
|
+
|
|
317
|
+
const WORKSPACE_BLOCK_STYLES = [
|
|
318
|
+
\t${styleConstNames}
|
|
319
|
+
\t// wp-typia add style entries
|
|
320
|
+
] as const;
|
|
321
|
+
|
|
322
|
+
export function registerWorkspaceBlockStyles() {
|
|
323
|
+
\tfor (const style of WORKSPACE_BLOCK_STYLES) {
|
|
324
|
+
\t\tregisterBlockStyle(metadata.name, style);
|
|
325
|
+
\t}
|
|
326
|
+
}
|
|
327
|
+
`;
|
|
328
|
+
}
|
|
329
|
+
function buildBlockTransformSource(options) {
|
|
330
|
+
const transformTitle = toTitleCase(options.transformSlug);
|
|
331
|
+
const transformConstName = buildWorkspaceConstName("BlockTransform", options.transformSlug);
|
|
332
|
+
return `import { createBlock } from '@wordpress/blocks';
|
|
333
|
+
import { __ } from '@wordpress/i18n';
|
|
334
|
+
import metadata from '../block.json';
|
|
335
|
+
|
|
336
|
+
type TransformAttributes = Record<string, unknown>;
|
|
337
|
+
type TransformInnerBlock = ReturnType<typeof createBlock>;
|
|
338
|
+
|
|
339
|
+
function mapTransformAttributes(attributes: TransformAttributes): TransformAttributes {
|
|
340
|
+
\tconst content = attributes.content;
|
|
341
|
+
|
|
342
|
+
\treturn typeof content === 'string' ? { content } : {};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export const ${transformConstName} = {
|
|
346
|
+
\ttype: 'block',
|
|
347
|
+
\tblocks: [${quoteTsString(options.fromBlockName)}],
|
|
348
|
+
\ttitle: __( ${quoteTsString(transformTitle)}, ${quoteTsString(options.textDomain)} ),
|
|
349
|
+
\ttransform: (
|
|
350
|
+
\t\tattributes: TransformAttributes,
|
|
351
|
+
\t\tinnerBlocks: TransformInnerBlock[] = [],
|
|
352
|
+
\t) => createBlock(metadata.name, mapTransformAttributes(attributes), innerBlocks),
|
|
353
|
+
} as const;
|
|
354
|
+
`;
|
|
355
|
+
}
|
|
356
|
+
function buildBlockTransformIndexSource(transformSlugs) {
|
|
357
|
+
const transformBindings = getBlockTransformConstBindings(transformSlugs);
|
|
358
|
+
const importLines = transformBindings
|
|
359
|
+
.map(({ constName, transformSlug }) => `import { ${constName} } from './${transformSlug}';`)
|
|
360
|
+
.join("\n");
|
|
361
|
+
const transformConstNames = transformBindings
|
|
362
|
+
.map(({ constName }) => constName)
|
|
363
|
+
.join(",\n\t");
|
|
364
|
+
return `${importLines ? `${importLines}\n\n` : ""}type BlockSettingsWithTransforms = {
|
|
365
|
+
\ttransforms?: {
|
|
366
|
+
\t\tfrom?: unknown[];
|
|
367
|
+
\t\tto?: unknown[];
|
|
368
|
+
\t};
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const WORKSPACE_BLOCK_TRANSFORMS = [
|
|
372
|
+
\t${transformConstNames}
|
|
373
|
+
\t// wp-typia add transform entries
|
|
374
|
+
] as const;
|
|
375
|
+
|
|
376
|
+
export function applyWorkspaceBlockTransforms(settings: BlockSettingsWithTransforms) {
|
|
377
|
+
\tconst transforms = settings.transforms ?? {};
|
|
378
|
+
|
|
379
|
+
\tsettings.transforms = {
|
|
380
|
+
\t\t...transforms,
|
|
381
|
+
\t\tfrom: [...(transforms.from ?? []), ...WORKSPACE_BLOCK_TRANSFORMS],
|
|
382
|
+
\t};
|
|
383
|
+
}
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
81
386
|
async function ensureVariationRegistrationHook(blockIndexPath) {
|
|
82
387
|
await patchFile(blockIndexPath, (source) => {
|
|
83
388
|
let nextSource = source;
|
|
84
|
-
if (!nextSource
|
|
389
|
+
if (!hasUncommentedPattern(nextSource, VARIATIONS_IMPORT_PATTERN)) {
|
|
85
390
|
nextSource = `${VARIATIONS_IMPORT_LINE}\n${nextSource}`;
|
|
86
391
|
}
|
|
87
|
-
if (!nextSource
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (candidate !== nextSource) {
|
|
96
|
-
nextSource = candidate;
|
|
97
|
-
inserted = true;
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
392
|
+
if (!hasExecutablePattern(nextSource, VARIATIONS_CALL_PATTERN)) {
|
|
393
|
+
const callRange = findBlockRegistrationCallRange(nextSource);
|
|
394
|
+
if (callRange) {
|
|
395
|
+
nextSource = [
|
|
396
|
+
nextSource.slice(0, callRange.end),
|
|
397
|
+
`\n${VARIATIONS_CALL_LINE}\n`,
|
|
398
|
+
nextSource.slice(callRange.end),
|
|
399
|
+
].join("");
|
|
100
400
|
}
|
|
101
|
-
|
|
401
|
+
else {
|
|
102
402
|
nextSource = `${nextSource.trimEnd()}\n\n${VARIATIONS_CALL_LINE}\n`;
|
|
103
403
|
}
|
|
104
404
|
}
|
|
105
|
-
if (!nextSource
|
|
405
|
+
if (!hasExecutablePattern(nextSource, VARIATIONS_CALL_PATTERN)) {
|
|
106
406
|
throw new Error(`Unable to inject ${VARIATIONS_CALL_LINE} into ${path.basename(blockIndexPath)}.`);
|
|
107
407
|
}
|
|
108
408
|
return nextSource;
|
|
109
409
|
});
|
|
110
410
|
}
|
|
411
|
+
async function ensureBlockStyleRegistrationHook(blockIndexPath) {
|
|
412
|
+
await patchFile(blockIndexPath, (source) => {
|
|
413
|
+
let nextSource = source;
|
|
414
|
+
if (!hasUncommentedPattern(nextSource, BLOCK_STYLES_IMPORT_PATTERN)) {
|
|
415
|
+
nextSource = `${BLOCK_STYLES_IMPORT_LINE}\n${nextSource}`;
|
|
416
|
+
}
|
|
417
|
+
if (!hasExecutablePattern(nextSource, BLOCK_STYLES_CALL_PATTERN)) {
|
|
418
|
+
const callRange = findBlockRegistrationCallRange(nextSource);
|
|
419
|
+
if (callRange) {
|
|
420
|
+
nextSource = [
|
|
421
|
+
nextSource.slice(0, callRange.end),
|
|
422
|
+
`\n${BLOCK_STYLES_CALL_LINE}\n`,
|
|
423
|
+
nextSource.slice(callRange.end),
|
|
424
|
+
].join("");
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
nextSource = `${nextSource.trimEnd()}\n\n${BLOCK_STYLES_CALL_LINE}\n`;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (!hasExecutablePattern(nextSource, BLOCK_STYLES_CALL_PATTERN)) {
|
|
431
|
+
throw new Error(`Unable to inject ${BLOCK_STYLES_CALL_LINE} into ${path.basename(blockIndexPath)}.`);
|
|
432
|
+
}
|
|
433
|
+
return nextSource;
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
async function ensureBlockTransformRegistrationHook(blockIndexPath) {
|
|
437
|
+
await patchFile(blockIndexPath, (source) => {
|
|
438
|
+
let nextSource = source;
|
|
439
|
+
if (!hasUncommentedPattern(nextSource, BLOCK_TRANSFORMS_IMPORT_PATTERN)) {
|
|
440
|
+
nextSource = `${BLOCK_TRANSFORMS_IMPORT_LINE}\n${nextSource}`;
|
|
441
|
+
}
|
|
442
|
+
if (!hasExecutablePattern(nextSource, BLOCK_TRANSFORMS_CALL_PATTERN)) {
|
|
443
|
+
const callRange = findExecutablePatternMatch(nextSource, [
|
|
444
|
+
SCAFFOLD_REGISTRATION_SETTINGS_CALL_PATTERN,
|
|
445
|
+
]);
|
|
446
|
+
if (!callRange) {
|
|
447
|
+
throw new Error(`Unable to inject ${BLOCK_TRANSFORMS_CALL_LINE} into ${path.basename(blockIndexPath)} because it does not expose a scaffold registration settings object.`);
|
|
448
|
+
}
|
|
449
|
+
nextSource = [
|
|
450
|
+
nextSource.slice(0, callRange.start),
|
|
451
|
+
`${BLOCK_TRANSFORMS_CALL_LINE}\n`,
|
|
452
|
+
nextSource.slice(callRange.start),
|
|
453
|
+
].join("");
|
|
454
|
+
}
|
|
455
|
+
return nextSource;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
111
458
|
async function writeVariationRegistry(projectDir, blockSlug, variationSlug) {
|
|
112
459
|
const variationsDir = path.join(projectDir, "src", "blocks", blockSlug, "variations");
|
|
113
460
|
const variationsIndexPath = path.join(variationsDir, "index.ts");
|
|
@@ -119,6 +466,83 @@ async function writeVariationRegistry(projectDir, blockSlug, variationSlug) {
|
|
|
119
466
|
const nextVariationSlugs = Array.from(new Set([...existingVariationSlugs, variationSlug])).sort();
|
|
120
467
|
await fsp.writeFile(variationsIndexPath, buildVariationIndexSource(nextVariationSlugs), "utf8");
|
|
121
468
|
}
|
|
469
|
+
async function writeBlockStyleRegistry(projectDir, blockSlug, styleSlug) {
|
|
470
|
+
const stylesDir = path.join(projectDir, "src", "blocks", blockSlug, "styles");
|
|
471
|
+
const stylesIndexPath = path.join(stylesDir, "index.ts");
|
|
472
|
+
await fsp.mkdir(stylesDir, { recursive: true });
|
|
473
|
+
const existingStyleSlugs = fs
|
|
474
|
+
.readdirSync(stylesDir)
|
|
475
|
+
.filter((entry) => entry.endsWith(".ts") && entry !== "index.ts")
|
|
476
|
+
.map((entry) => entry.replace(/\.ts$/u, ""));
|
|
477
|
+
const nextStyleSlugs = Array.from(new Set([...existingStyleSlugs, styleSlug])).sort();
|
|
478
|
+
await fsp.writeFile(stylesIndexPath, buildBlockStyleIndexSource(nextStyleSlugs), "utf8");
|
|
479
|
+
}
|
|
480
|
+
async function writeBlockTransformRegistry(projectDir, blockSlug, transformSlug) {
|
|
481
|
+
const transformsDir = path.join(projectDir, "src", "blocks", blockSlug, "transforms");
|
|
482
|
+
const transformsIndexPath = path.join(transformsDir, "index.ts");
|
|
483
|
+
await fsp.mkdir(transformsDir, { recursive: true });
|
|
484
|
+
const existingTransformSlugs = fs
|
|
485
|
+
.readdirSync(transformsDir)
|
|
486
|
+
.filter((entry) => entry.endsWith(".ts") && entry !== "index.ts")
|
|
487
|
+
.map((entry) => entry.replace(/\.ts$/u, ""));
|
|
488
|
+
const nextTransformSlugs = Array.from(new Set([...existingTransformSlugs, transformSlug])).sort();
|
|
489
|
+
await fsp.writeFile(transformsIndexPath, buildBlockTransformIndexSource(nextTransformSlugs), "utf8");
|
|
490
|
+
}
|
|
491
|
+
function assertBlockStyleDoesNotExist(projectDir, blockSlug, styleSlug, inventory) {
|
|
492
|
+
const stylePath = path.join(projectDir, "src", "blocks", blockSlug, "styles", `${styleSlug}.ts`);
|
|
493
|
+
if (fs.existsSync(stylePath)) {
|
|
494
|
+
throw new Error(`A block style already exists at ${path.relative(projectDir, stylePath)}. Choose a different name.`);
|
|
495
|
+
}
|
|
496
|
+
if (inventory.blockStyles.some((entry) => entry.block === blockSlug && entry.slug === styleSlug)) {
|
|
497
|
+
throw new Error(`A block style inventory entry already exists for ${blockSlug}/${styleSlug}. Choose a different name.`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function assertBlockTransformDoesNotExist(projectDir, blockSlug, transformSlug, inventory) {
|
|
501
|
+
const transformPath = path.join(projectDir, "src", "blocks", blockSlug, "transforms", `${transformSlug}.ts`);
|
|
502
|
+
if (fs.existsSync(transformPath)) {
|
|
503
|
+
throw new Error(`A block transform already exists at ${path.relative(projectDir, transformPath)}. Choose a different name.`);
|
|
504
|
+
}
|
|
505
|
+
if (inventory.blockTransforms.some((entry) => entry.block === blockSlug && entry.slug === transformSlug)) {
|
|
506
|
+
throw new Error(`A block transform inventory entry already exists for ${blockSlug}/${transformSlug}. Choose a different name.`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function assertFullBlockName(blockName, flagName) {
|
|
510
|
+
const trimmed = blockName.trim();
|
|
511
|
+
if (!trimmed) {
|
|
512
|
+
throw new Error(`\`${flagName}\` requires a block name.`);
|
|
513
|
+
}
|
|
514
|
+
if (!FULL_BLOCK_NAME_PATTERN.test(trimmed)) {
|
|
515
|
+
throw new Error(`\`${flagName}\` must use <namespace/block-slug> format.`);
|
|
516
|
+
}
|
|
517
|
+
return trimmed;
|
|
518
|
+
}
|
|
519
|
+
function resolveWorkspaceTargetBlockName(blockName, namespace, flagName) {
|
|
520
|
+
const trimmed = blockName.trim();
|
|
521
|
+
if (!trimmed) {
|
|
522
|
+
throw new Error(`\`${flagName}\` requires <block-slug|namespace/block-slug>.`);
|
|
523
|
+
}
|
|
524
|
+
const blockNameSegments = trimmed.split("/");
|
|
525
|
+
if (blockNameSegments.length > 2 ||
|
|
526
|
+
blockNameSegments.some((segment) => segment.trim() === "")) {
|
|
527
|
+
throw new Error(`\`${flagName}\` must use <block-slug|namespace/block-slug> format.`);
|
|
528
|
+
}
|
|
529
|
+
const [maybeNamespace, maybeSlug] = blockNameSegments.length === 2
|
|
530
|
+
? blockNameSegments
|
|
531
|
+
: [undefined, blockNameSegments[0]];
|
|
532
|
+
if (maybeNamespace && maybeNamespace !== namespace) {
|
|
533
|
+
throw new Error(`\`${flagName}\` references namespace "${maybeNamespace}". Expected "${namespace}".`);
|
|
534
|
+
}
|
|
535
|
+
const blockSlug = normalizeBlockSlug(maybeSlug ?? "");
|
|
536
|
+
return {
|
|
537
|
+
blockName: `${namespace}/${blockSlug}`,
|
|
538
|
+
blockSlug,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Re-export the DataViews admin screen scaffold workflow from the focused
|
|
543
|
+
* admin-view runtime helper module.
|
|
544
|
+
*/
|
|
545
|
+
export { runAddAdminViewCommand, } from "./cli-add-workspace-admin-view.js";
|
|
122
546
|
/**
|
|
123
547
|
* Re-export focused workspace asset scaffold commands from the companion
|
|
124
548
|
* `cli-add-workspace-assets` module.
|
|
@@ -167,6 +591,7 @@ export async function runAddVariationCommand({ blockName, cwd = process.cwd(), v
|
|
|
167
591
|
const variationsDir = path.join(workspace.projectDir, "src", "blocks", blockSlug, "variations");
|
|
168
592
|
const variationFilePath = path.join(variationsDir, `${variationSlug}.ts`);
|
|
169
593
|
const variationsIndexPath = path.join(variationsDir, "index.ts");
|
|
594
|
+
const shouldRemoveVariationsDirOnRollback = !fs.existsSync(variationsDir);
|
|
170
595
|
const mutationSnapshot = {
|
|
171
596
|
fileSources: await snapshotWorkspaceFiles([
|
|
172
597
|
blockConfigPath,
|
|
@@ -174,7 +599,10 @@ export async function runAddVariationCommand({ blockName, cwd = process.cwd(), v
|
|
|
174
599
|
variationsIndexPath,
|
|
175
600
|
]),
|
|
176
601
|
snapshotDirs: [],
|
|
177
|
-
targetPaths: [
|
|
602
|
+
targetPaths: [
|
|
603
|
+
variationFilePath,
|
|
604
|
+
...(shouldRemoveVariationsDirOnRollback ? [variationsDir] : []),
|
|
605
|
+
],
|
|
178
606
|
};
|
|
179
607
|
try {
|
|
180
608
|
await fsp.mkdir(variationsDir, { recursive: true });
|
|
@@ -195,6 +623,148 @@ export async function runAddVariationCommand({ blockName, cwd = process.cwd(), v
|
|
|
195
623
|
throw error;
|
|
196
624
|
}
|
|
197
625
|
}
|
|
626
|
+
/**
|
|
627
|
+
* Add one Block Styles registration to an existing workspace block.
|
|
628
|
+
*
|
|
629
|
+
* @param options Command options for the Block Styles scaffold workflow.
|
|
630
|
+
* @param options.blockName Target workspace block slug that will own the style.
|
|
631
|
+
* @param options.cwd Working directory used to resolve the nearest official workspace.
|
|
632
|
+
* Defaults to `process.cwd()`.
|
|
633
|
+
* @param options.styleName Human-entered style name that will be normalized and
|
|
634
|
+
* validated before files are written.
|
|
635
|
+
* @returns A promise that resolves with the normalized `blockSlug`, `styleSlug`,
|
|
636
|
+
* and owning `projectDir` after the style module, style registry, entrypoint
|
|
637
|
+
* hook, and inventory entry have been written successfully.
|
|
638
|
+
* @throws {Error} When the command is run outside an official workspace, when
|
|
639
|
+
* the target block is unknown, when the style slug is invalid, or when a
|
|
640
|
+
* conflicting file or inventory entry already exists.
|
|
641
|
+
*/
|
|
642
|
+
export async function runAddBlockStyleCommand({ blockName, cwd = process.cwd(), styleName, }) {
|
|
643
|
+
const workspace = resolveWorkspaceProject(cwd);
|
|
644
|
+
const blockSlug = normalizeBlockSlug(blockName);
|
|
645
|
+
const styleSlug = assertValidGeneratedSlug("Style name", normalizeBlockSlug(styleName), "wp-typia add style <name> --block <block-slug>");
|
|
646
|
+
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
647
|
+
resolveWorkspaceBlock(inventory, blockSlug);
|
|
648
|
+
assertBlockStyleDoesNotExist(workspace.projectDir, blockSlug, styleSlug, inventory);
|
|
649
|
+
const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
|
|
650
|
+
const blockIndexPath = path.join(workspace.projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
651
|
+
const stylesDir = path.join(workspace.projectDir, "src", "blocks", blockSlug, "styles");
|
|
652
|
+
const styleFilePath = path.join(stylesDir, `${styleSlug}.ts`);
|
|
653
|
+
const stylesIndexPath = path.join(stylesDir, "index.ts");
|
|
654
|
+
const shouldRemoveStylesDirOnRollback = !fs.existsSync(stylesDir);
|
|
655
|
+
const mutationSnapshot = {
|
|
656
|
+
fileSources: await snapshotWorkspaceFiles([
|
|
657
|
+
blockConfigPath,
|
|
658
|
+
blockIndexPath,
|
|
659
|
+
stylesIndexPath,
|
|
660
|
+
]),
|
|
661
|
+
snapshotDirs: [],
|
|
662
|
+
targetPaths: [
|
|
663
|
+
styleFilePath,
|
|
664
|
+
...(shouldRemoveStylesDirOnRollback ? [stylesDir] : []),
|
|
665
|
+
],
|
|
666
|
+
};
|
|
667
|
+
try {
|
|
668
|
+
await fsp.mkdir(stylesDir, { recursive: true });
|
|
669
|
+
await fsp.writeFile(styleFilePath, buildBlockStyleSource(styleSlug, workspace.workspace.textDomain), "utf8");
|
|
670
|
+
await writeBlockStyleRegistry(workspace.projectDir, blockSlug, styleSlug);
|
|
671
|
+
await ensureBlockStyleRegistrationHook(blockIndexPath);
|
|
672
|
+
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
673
|
+
blockStyleEntries: [buildBlockStyleConfigEntry(blockSlug, styleSlug)],
|
|
674
|
+
});
|
|
675
|
+
return {
|
|
676
|
+
blockSlug,
|
|
677
|
+
projectDir: workspace.projectDir,
|
|
678
|
+
styleSlug,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
683
|
+
throw error;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Add one block-to-block transform registration to an existing workspace block.
|
|
688
|
+
*
|
|
689
|
+
* @param options Command options for the block transform scaffold workflow.
|
|
690
|
+
* @param options.cwd Working directory used to resolve the nearest official workspace.
|
|
691
|
+
* Defaults to `process.cwd()`.
|
|
692
|
+
* @param options.fromBlockName Source block name for `--from`. This must be the
|
|
693
|
+
* full `namespace/block` form because transforms may originate from WordPress
|
|
694
|
+
* core or third-party blocks outside the workspace.
|
|
695
|
+
* @param options.toBlockName Target block for `--to`. A workspace block slug is
|
|
696
|
+
* resolved against the workspace namespace, while a full `namespace/block` name
|
|
697
|
+
* must still point at an existing workspace block.
|
|
698
|
+
* @param options.transformName Human-entered transform name that will be
|
|
699
|
+
* normalized and validated before files are written.
|
|
700
|
+
* @returns A promise that resolves with the normalized target `blockSlug`,
|
|
701
|
+
* resolved `fromBlockName`, resolved `toBlockName`, `transformSlug`, and owning
|
|
702
|
+
* `projectDir` after the transform module, transform registry, entrypoint hook,
|
|
703
|
+
* and inventory entry have been written successfully.
|
|
704
|
+
* @throws {Error} When the command is run outside an official workspace, when
|
|
705
|
+
* the target block is unknown, when `--from` is not a full block name, when
|
|
706
|
+
* `--to` uses a non-workspace namespace, when the target block entrypoint does
|
|
707
|
+
* not expose `registration.settings`, when the transform slug is invalid, or
|
|
708
|
+
* when a conflicting file or inventory entry already exists.
|
|
709
|
+
*/
|
|
710
|
+
export async function runAddBlockTransformCommand({ cwd = process.cwd(), fromBlockName, toBlockName, transformName, }) {
|
|
711
|
+
const workspace = resolveWorkspaceProject(cwd);
|
|
712
|
+
const transformSlug = assertValidGeneratedSlug("Transform name", normalizeBlockSlug(transformName), "wp-typia add transform <name> --from <namespace/block> --to <block-slug|namespace/block-slug>");
|
|
713
|
+
const resolvedFromBlockName = assertFullBlockName(fromBlockName, "--from");
|
|
714
|
+
const target = resolveWorkspaceTargetBlockName(toBlockName, workspace.workspace.namespace, "--to");
|
|
715
|
+
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
716
|
+
resolveWorkspaceBlock(inventory, target.blockSlug);
|
|
717
|
+
assertBlockTransformDoesNotExist(workspace.projectDir, target.blockSlug, transformSlug, inventory);
|
|
718
|
+
const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
|
|
719
|
+
const blockIndexPath = path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "index.tsx");
|
|
720
|
+
const transformsDir = path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "transforms");
|
|
721
|
+
const transformFilePath = path.join(transformsDir, `${transformSlug}.ts`);
|
|
722
|
+
const transformsIndexPath = path.join(transformsDir, "index.ts");
|
|
723
|
+
const shouldRemoveTransformsDirOnRollback = !fs.existsSync(transformsDir);
|
|
724
|
+
const mutationSnapshot = {
|
|
725
|
+
fileSources: await snapshotWorkspaceFiles([
|
|
726
|
+
blockConfigPath,
|
|
727
|
+
blockIndexPath,
|
|
728
|
+
transformsIndexPath,
|
|
729
|
+
]),
|
|
730
|
+
snapshotDirs: [],
|
|
731
|
+
targetPaths: [
|
|
732
|
+
transformFilePath,
|
|
733
|
+
...(shouldRemoveTransformsDirOnRollback ? [transformsDir] : []),
|
|
734
|
+
],
|
|
735
|
+
};
|
|
736
|
+
try {
|
|
737
|
+
await fsp.mkdir(transformsDir, { recursive: true });
|
|
738
|
+
await fsp.writeFile(transformFilePath, buildBlockTransformSource({
|
|
739
|
+
fromBlockName: resolvedFromBlockName,
|
|
740
|
+
textDomain: workspace.workspace.textDomain,
|
|
741
|
+
transformSlug,
|
|
742
|
+
}), "utf8");
|
|
743
|
+
await writeBlockTransformRegistry(workspace.projectDir, target.blockSlug, transformSlug);
|
|
744
|
+
await ensureBlockTransformRegistrationHook(blockIndexPath);
|
|
745
|
+
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
746
|
+
blockTransformEntries: [
|
|
747
|
+
buildBlockTransformConfigEntry({
|
|
748
|
+
blockSlug: target.blockSlug,
|
|
749
|
+
fromBlockName: resolvedFromBlockName,
|
|
750
|
+
toBlockName: target.blockName,
|
|
751
|
+
transformSlug,
|
|
752
|
+
}),
|
|
753
|
+
],
|
|
754
|
+
});
|
|
755
|
+
return {
|
|
756
|
+
blockSlug: target.blockSlug,
|
|
757
|
+
fromBlockName: resolvedFromBlockName,
|
|
758
|
+
projectDir: workspace.projectDir,
|
|
759
|
+
toBlockName: target.blockName,
|
|
760
|
+
transformSlug,
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
765
|
+
throw error;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
198
768
|
/**
|
|
199
769
|
* Add one `blockHooks` entry to an existing official workspace block.
|
|
200
770
|
*
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, } from "./cli-add-shared.js";
|
|
11
11
|
export type { AddBlockTemplateId, AddKindId, EditorPluginSlotId, } from "./cli-add-shared.js";
|
|
12
12
|
export { runAddBlockCommand, seedWorkspaceMigrationProject, } from "./cli-add-block.js";
|
|
13
|
-
export {
|
|
13
|
+
export { runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
|
|
14
14
|
export { getWorkspaceBlockSelectOptions } from "./workspace-inventory.js";
|
package/dist/runtime/cli-add.js
CHANGED
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, } from "./cli-add-shared.js";
|
|
11
11
|
export { runAddBlockCommand, seedWorkspaceMigrationProject, } from "./cli-add-block.js";
|
|
12
|
-
export {
|
|
12
|
+
export { runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
|
|
13
13
|
export { getWorkspaceBlockSelectOptions } from "./workspace-inventory.js";
|