@wp-typia/project-tools 0.22.3 → 0.22.4
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-json.d.ts +31 -0
- package/dist/runtime/cli-add-block-json.js +65 -0
- package/dist/runtime/cli-add-collision.d.ts +129 -0
- package/dist/runtime/cli-add-collision.js +293 -0
- package/dist/runtime/cli-add-filesystem.d.ts +29 -0
- package/dist/runtime/cli-add-filesystem.js +77 -0
- package/dist/runtime/cli-add-help.d.ts +4 -0
- package/dist/runtime/cli-add-help.js +41 -0
- package/dist/runtime/cli-add-shared.d.ts +6 -304
- package/dist/runtime/cli-add-shared.js +6 -524
- package/dist/runtime/cli-add-types.d.ts +247 -0
- package/dist/runtime/cli-add-types.js +64 -0
- package/dist/runtime/cli-add-validation.d.ts +87 -0
- package/dist/runtime/cli-add-validation.js +147 -0
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +46 -72
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +35 -61
- package/dist/runtime/cli-add-workspace-ai-scaffold.js +53 -57
- package/dist/runtime/cli-add-workspace-ai-templates.js +2 -0
- package/dist/runtime/cli-add-workspace-mutation.d.ts +30 -0
- package/dist/runtime/cli-add-workspace-mutation.js +60 -0
- package/dist/runtime/cli-add-workspace.js +1 -79
- package/dist/runtime/cli-add.d.ts +2 -2
- package/dist/runtime/cli-add.js +2 -2
- package/dist/runtime/cli-doctor-workspace-blocks.js +1 -66
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/migration-utils.d.ts +2 -1
- package/dist/runtime/migration-utils.js +3 -11
- package/dist/runtime/package-managers.d.ts +19 -0
- package/dist/runtime/package-managers.js +62 -0
- package/dist/runtime/template-source-cache.d.ts +59 -0
- package/dist/runtime/template-source-cache.js +160 -0
- package/dist/runtime/ts-source-masking.d.ts +28 -0
- package/dist/runtime/ts-source-masking.js +104 -0
- package/dist/runtime/typia-llm.d.ts +9 -1
- package/dist/runtime/typia-llm.js +20 -5
- package/dist/runtime/workspace-inventory.js +116 -59
- package/dist/runtime/workspace-project.d.ts +1 -1
- package/dist/runtime/workspace-project.js +2 -10
- package/package.json +2 -2
|
@@ -6,7 +6,8 @@ import semver from "semver";
|
|
|
6
6
|
import { appendWorkspaceInventoryEntries, readWorkspaceInventory, } from "./workspace-inventory.js";
|
|
7
7
|
import { buildAbilityClientSource, buildAbilityConfigEntry, buildAbilityConfigSource, buildAbilityDataSource, buildAbilityPhpSource, buildAbilityRegistrySource, buildAbilitySyncScriptSource, buildAbilityTypesSource, } from "./cli-add-workspace-ability-templates.js";
|
|
8
8
|
import { ABILITY_EDITOR_ASSET, ABILITY_EDITOR_SCRIPT, ABILITY_REGISTRY_END_MARKER, ABILITY_REGISTRY_START_MARKER, ABILITY_SERVER_GLOB, WP_ABILITIES_SCRIPT_MODULE_ID, WP_CORE_ABILITIES_SCRIPT_MODULE_ID, } from "./cli-add-workspace-ability-types.js";
|
|
9
|
-
import { getWorkspaceBootstrapPath, patchFile,
|
|
9
|
+
import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
|
|
10
|
+
import { appendPhpSnippetBeforeClosingTag, executeWorkspaceMutationPlan, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
|
|
10
11
|
import { updatePluginHeaderCompatibility, } from "./scaffold-compatibility.js";
|
|
11
12
|
import { DEFAULT_WORDPRESS_ABILITIES_VERSION, DEFAULT_WORDPRESS_CORE_ABILITIES_VERSION, } from "./package-versions.js";
|
|
12
13
|
import { escapeRegex, findPhpFunctionRange, hasPhpFunctionDefinition, replacePhpFunctionDefinition, } from "./php-utils.js";
|
|
@@ -123,46 +124,24 @@ function ${enqueueFunctionName}() {
|
|
|
123
124
|
\t);
|
|
124
125
|
}
|
|
125
126
|
`;
|
|
126
|
-
const insertionAnchors = [
|
|
127
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
128
|
-
/\?>\s*$/u,
|
|
129
|
-
];
|
|
130
|
-
const insertPhpSnippet = (snippet) => {
|
|
131
|
-
for (const anchor of insertionAnchors) {
|
|
132
|
-
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
133
|
-
if (candidate !== nextSource) {
|
|
134
|
-
nextSource = candidate;
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
139
|
-
};
|
|
140
|
-
const appendPhpSnippet = (snippet) => {
|
|
141
|
-
const closingTagPattern = /\?>\s*$/u;
|
|
142
|
-
if (closingTagPattern.test(nextSource)) {
|
|
143
|
-
nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
147
|
-
};
|
|
148
127
|
if (!hasPhpFunctionDefinition(nextSource, loadFunctionName)) {
|
|
149
|
-
|
|
128
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, loadFunction);
|
|
150
129
|
}
|
|
151
130
|
if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
|
|
152
|
-
|
|
131
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, enqueueFunction);
|
|
153
132
|
}
|
|
154
133
|
else if (!findPhpFunctionRange(nextSource, enqueueFunctionName)?.source.includes("wp_enqueue_script_module")) {
|
|
155
134
|
nextSource =
|
|
156
135
|
replacePhpFunctionDefinition(nextSource, enqueueFunctionName, enqueueFunction, { trimReplacementStart: true }) ?? nextSource;
|
|
157
136
|
}
|
|
158
137
|
if (!nextSource.includes(loadHook)) {
|
|
159
|
-
|
|
138
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, loadHook);
|
|
160
139
|
}
|
|
161
140
|
if (!nextSource.includes(adminEnqueueHook)) {
|
|
162
|
-
|
|
141
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, adminEnqueueHook);
|
|
163
142
|
}
|
|
164
143
|
if (!nextSource.includes(editorEnqueueHook)) {
|
|
165
|
-
|
|
144
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, editorEnqueueHook);
|
|
166
145
|
}
|
|
167
146
|
return nextSource;
|
|
168
147
|
});
|
|
@@ -336,8 +315,8 @@ export async function scaffoldAbilityWorkspace({ abilitySlug, compatibilityPolic
|
|
|
336
315
|
const dataFilePath = path.join(abilityDir, "data.ts");
|
|
337
316
|
const clientFilePath = path.join(abilityDir, "client.ts");
|
|
338
317
|
const phpFilePath = path.join(workspace.projectDir, "inc", "abilities", `${abilitySlug}.php`);
|
|
339
|
-
|
|
340
|
-
|
|
318
|
+
await executeWorkspaceMutationPlan({
|
|
319
|
+
filePaths: [
|
|
341
320
|
blockConfigPath,
|
|
342
321
|
bootstrapPath,
|
|
343
322
|
buildScriptPath,
|
|
@@ -346,47 +325,42 @@ export async function scaffoldAbilityWorkspace({ abilitySlug, compatibilityPolic
|
|
|
346
325
|
syncProjectScriptPath,
|
|
347
326
|
webpackConfigPath,
|
|
348
327
|
abilitiesIndexPath,
|
|
349
|
-
]
|
|
350
|
-
snapshotDirs: [],
|
|
328
|
+
],
|
|
351
329
|
targetPaths: [abilityDir, phpFilePath, syncAbilitiesScriptPath],
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
catch (error) {
|
|
389
|
-
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
390
|
-
throw error;
|
|
391
|
-
}
|
|
330
|
+
run: async () => {
|
|
331
|
+
await fsp.mkdir(abilityDir, { recursive: true });
|
|
332
|
+
await fsp.mkdir(path.dirname(phpFilePath), { recursive: true });
|
|
333
|
+
await ensureAbilityBootstrapAnchors(workspace);
|
|
334
|
+
await patchFile(bootstrapPath, (source) => updatePluginHeaderCompatibility(source, compatibilityPolicy));
|
|
335
|
+
await ensureAbilityPackageScripts(workspace);
|
|
336
|
+
await ensureAbilitySyncProjectAnchors(workspace);
|
|
337
|
+
await ensureAbilityBuildScriptAnchors(workspace);
|
|
338
|
+
await ensureAbilityWebpackAnchors(workspace);
|
|
339
|
+
await fsp.writeFile(syncAbilitiesScriptPath, buildAbilitySyncScriptSource(), "utf8");
|
|
340
|
+
await fsp.writeFile(configFilePath, buildAbilityConfigSource(abilitySlug, workspace.workspace.namespace), "utf8");
|
|
341
|
+
await fsp.writeFile(typesFilePath, buildAbilityTypesSource(abilitySlug), "utf8");
|
|
342
|
+
await fsp.writeFile(dataFilePath, buildAbilityDataSource(abilitySlug), "utf8");
|
|
343
|
+
await fsp.writeFile(clientFilePath, buildAbilityClientSource(abilitySlug), "utf8");
|
|
344
|
+
await fsp.writeFile(phpFilePath, buildAbilityPhpSource(abilitySlug, workspace), "utf8");
|
|
345
|
+
const pascalCase = toPascalCase(abilitySlug);
|
|
346
|
+
await syncTypeSchemas({
|
|
347
|
+
jsonSchemaFile: `src/abilities/${abilitySlug}/input.schema.json`,
|
|
348
|
+
projectRoot: workspace.projectDir,
|
|
349
|
+
sourceTypeName: `${pascalCase}AbilityInput`,
|
|
350
|
+
typesFile: `src/abilities/${abilitySlug}/types.ts`,
|
|
351
|
+
});
|
|
352
|
+
await syncTypeSchemas({
|
|
353
|
+
jsonSchemaFile: `src/abilities/${abilitySlug}/output.schema.json`,
|
|
354
|
+
projectRoot: workspace.projectDir,
|
|
355
|
+
sourceTypeName: `${pascalCase}AbilityOutput`,
|
|
356
|
+
typesFile: `src/abilities/${abilitySlug}/types.ts`,
|
|
357
|
+
});
|
|
358
|
+
await writeAbilityRegistry(workspace.projectDir, abilitySlug);
|
|
359
|
+
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
360
|
+
abilityEntries: [
|
|
361
|
+
buildAbilityConfigEntry(abilitySlug, compatibilityPolicy),
|
|
362
|
+
],
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
});
|
|
392
366
|
}
|
|
@@ -4,7 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { appendWorkspaceInventoryEntries, readWorkspaceInventory, } from './workspace-inventory.js';
|
|
5
5
|
import { buildAdminViewConfigEntry, buildAdminViewConfigSource, buildAdminViewEntrySource, buildAdminViewPhpSource, buildAdminViewRegistrySource, buildAdminViewScreenSource, buildAdminViewStyleSource, buildAdminViewTypesSource, buildCoreDataAdminViewDataSource, buildCoreDataAdminViewScreenSource, buildDefaultAdminViewDataSource, buildRestAdminViewDataSource, } from './cli-add-workspace-admin-view-templates.js';
|
|
6
6
|
import { ADMIN_VIEWS_PHP_GLOB, isAdminViewCoreDataSource, } from './cli-add-workspace-admin-view-types.js';
|
|
7
|
-
import { getWorkspaceBootstrapPath, patchFile,
|
|
7
|
+
import { getWorkspaceBootstrapPath, patchFile, } from './cli-add-shared.js';
|
|
8
|
+
import { appendPhpSnippetBeforeClosingTag, executeWorkspaceMutationPlan, insertPhpSnippetBeforeWorkspaceAnchors, } from './cli-add-workspace-mutation.js';
|
|
8
9
|
import { DEFAULT_WORDPRESS_CORE_DATA_VERSION, DEFAULT_WORDPRESS_DATA_VERSION, DEFAULT_WORDPRESS_DATAVIEWS_VERSION, DEFAULT_WP_TYPIA_DATAVIEWS_VERSION, resolveManagedPackageVersionRange, } from './package-versions.js';
|
|
9
10
|
import { findPhpFunctionRange, hasPhpFunctionDefinition, replacePhpFunctionDefinition, } from './php-utils.js';
|
|
10
11
|
function detectJsonIndent(source) {
|
|
@@ -77,30 +78,8 @@ function ${loadFunctionName}() {
|
|
|
77
78
|
\t}
|
|
78
79
|
}
|
|
79
80
|
`;
|
|
80
|
-
const insertionAnchors = [
|
|
81
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
82
|
-
/\?>\s*$/u,
|
|
83
|
-
];
|
|
84
|
-
const insertPhpSnippet = (snippet) => {
|
|
85
|
-
for (const anchor of insertionAnchors) {
|
|
86
|
-
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
87
|
-
if (candidate !== nextSource) {
|
|
88
|
-
nextSource = candidate;
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
93
|
-
};
|
|
94
|
-
const appendPhpSnippet = (snippet) => {
|
|
95
|
-
const closingTagPattern = /\?>\s*$/u;
|
|
96
|
-
if (closingTagPattern.test(nextSource)) {
|
|
97
|
-
nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
101
|
-
};
|
|
102
81
|
if (!hasPhpFunctionDefinition(nextSource, loadFunctionName)) {
|
|
103
|
-
|
|
82
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, loadFunction);
|
|
104
83
|
}
|
|
105
84
|
else {
|
|
106
85
|
const functionRange = findPhpFunctionRange(nextSource, loadFunctionName);
|
|
@@ -116,7 +95,7 @@ function ${loadFunctionName}() {
|
|
|
116
95
|
}
|
|
117
96
|
}
|
|
118
97
|
if (!loadHookPattern.test(nextSource)) {
|
|
119
|
-
|
|
98
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, loadHook);
|
|
120
99
|
}
|
|
121
100
|
return nextSource;
|
|
122
101
|
});
|
|
@@ -211,47 +190,42 @@ export async function scaffoldAdminViewWorkspace(options) {
|
|
|
211
190
|
const adminViewsIndexPath = resolveAdminViewRegistryPath(workspace.projectDir);
|
|
212
191
|
const adminViewDir = path.join(workspace.projectDir, 'src', 'admin-views', adminViewSlug);
|
|
213
192
|
const adminViewPhpPath = path.join(workspace.projectDir, 'inc', 'admin-views', `${adminViewSlug}.php`);
|
|
214
|
-
|
|
215
|
-
|
|
193
|
+
await executeWorkspaceMutationPlan({
|
|
194
|
+
filePaths: [
|
|
216
195
|
adminViewsIndexPath,
|
|
217
196
|
blockConfigPath,
|
|
218
197
|
bootstrapPath,
|
|
219
198
|
buildScriptPath,
|
|
220
199
|
packageJsonPath,
|
|
221
200
|
webpackConfigPath,
|
|
222
|
-
]
|
|
223
|
-
snapshotDirs: [],
|
|
201
|
+
],
|
|
224
202
|
targetPaths: [adminViewDir, adminViewPhpPath],
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
255
|
-
throw error;
|
|
256
|
-
}
|
|
203
|
+
run: async () => {
|
|
204
|
+
await fsp.mkdir(adminViewDir, { recursive: true });
|
|
205
|
+
await fsp.mkdir(path.dirname(adminViewPhpPath), { recursive: true });
|
|
206
|
+
await ensureAdminViewPackageDependencies(workspace, parsedSource);
|
|
207
|
+
await ensureAdminViewBootstrapAnchors(workspace);
|
|
208
|
+
await ensureAdminViewBuildScriptAnchors(workspace);
|
|
209
|
+
await ensureAdminViewWebpackAnchors(workspace);
|
|
210
|
+
await fsp.writeFile(path.join(adminViewDir, 'types.ts'), buildAdminViewTypesSource(adminViewSlug, restResource, coreDataSource), 'utf8');
|
|
211
|
+
await fsp.writeFile(path.join(adminViewDir, 'config.ts'), buildAdminViewConfigSource(adminViewSlug, workspace.workspace.textDomain, parsedSource, restResource), 'utf8');
|
|
212
|
+
await fsp.writeFile(path.join(adminViewDir, 'data.ts'), coreDataSource
|
|
213
|
+
? buildCoreDataAdminViewDataSource(adminViewSlug, coreDataSource)
|
|
214
|
+
: restResource
|
|
215
|
+
? buildRestAdminViewDataSource(adminViewSlug, restResource)
|
|
216
|
+
: buildDefaultAdminViewDataSource(adminViewSlug), 'utf8');
|
|
217
|
+
await fsp.writeFile(path.join(adminViewDir, 'Screen.tsx'), coreDataSource
|
|
218
|
+
? buildCoreDataAdminViewScreenSource(adminViewSlug, workspace.workspace.textDomain)
|
|
219
|
+
: buildAdminViewScreenSource(adminViewSlug, workspace.workspace.textDomain), 'utf8');
|
|
220
|
+
await fsp.writeFile(path.join(adminViewDir, 'index.tsx'), buildAdminViewEntrySource(adminViewSlug), 'utf8');
|
|
221
|
+
await fsp.writeFile(path.join(adminViewDir, 'style.scss'), buildAdminViewStyleSource(), 'utf8');
|
|
222
|
+
await fsp.writeFile(adminViewPhpPath, buildAdminViewPhpSource(adminViewSlug, workspace), 'utf8');
|
|
223
|
+
await writeAdminViewRegistry(workspace.projectDir, adminViewSlug);
|
|
224
|
+
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
225
|
+
adminViewEntries: [
|
|
226
|
+
buildAdminViewConfigEntry(adminViewSlug, parsedSource),
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
},
|
|
230
|
+
});
|
|
257
231
|
}
|
|
@@ -5,7 +5,8 @@ import { buildAiFeatureConfigEntry, buildAiFeatureDataSource, buildAiFeatureSync
|
|
|
5
5
|
import { ensureAiFeatureBootstrapAnchors, ensureAiFeaturePackageScripts, ensureAiFeatureSyncProjectAnchors, ensureAiFeatureSyncRestAnchors, } from "./cli-add-workspace-ai-anchors.js";
|
|
6
6
|
import { buildAiFeaturePhpSource } from "./cli-add-workspace-ai-templates.js";
|
|
7
7
|
import { appendWorkspaceInventoryEntries } from "./workspace-inventory.js";
|
|
8
|
-
import { getWorkspaceBootstrapPath, patchFile,
|
|
8
|
+
import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
|
|
9
|
+
import { executeWorkspaceMutationPlan } from "./cli-add-workspace-mutation.js";
|
|
9
10
|
import { updatePluginHeaderCompatibility } from "./scaffold-compatibility.js";
|
|
10
11
|
import { toPascalCase, toTitleCase } from "./string-case.js";
|
|
11
12
|
import { syncAiFeatureRestArtifacts, syncAiFeatureSchemaArtifact, } from "./ai-feature-artifacts.js";
|
|
@@ -25,67 +26,62 @@ export async function scaffoldAiFeatureWorkspace({ aiFeatureSlug, compatibilityP
|
|
|
25
26
|
const apiFilePath = path.join(aiFeatureDir, "api.ts");
|
|
26
27
|
const dataFilePath = path.join(aiFeatureDir, "data.ts");
|
|
27
28
|
const phpFilePath = path.join(workspace.projectDir, "inc", "ai-features", `${aiFeatureSlug}.php`);
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
return executeWorkspaceMutationPlan({
|
|
30
|
+
filePaths: [
|
|
30
31
|
blockConfigPath,
|
|
31
32
|
bootstrapPath,
|
|
32
33
|
packageJsonPath,
|
|
33
34
|
syncAiScriptPath,
|
|
34
35
|
syncProjectScriptPath,
|
|
35
36
|
syncRestScriptPath,
|
|
36
|
-
]
|
|
37
|
-
snapshotDirs: [],
|
|
37
|
+
],
|
|
38
38
|
targetPaths: [aiFeatureDir, phpFilePath, syncAiScriptPath],
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
39
|
+
run: async () => {
|
|
40
|
+
await fsp.mkdir(aiFeatureDir, { recursive: true });
|
|
41
|
+
await fsp.mkdir(path.dirname(phpFilePath), { recursive: true });
|
|
42
|
+
await ensureAiFeatureBootstrapAnchors(workspace);
|
|
43
|
+
await patchFile(bootstrapPath, (source) => updatePluginHeaderCompatibility(source, compatibilityPolicy));
|
|
44
|
+
const packageScriptChanges = await ensureAiFeaturePackageScripts(workspace);
|
|
45
|
+
await ensureAiFeatureSyncProjectAnchors(workspace);
|
|
46
|
+
await ensureAiFeatureSyncRestAnchors(workspace);
|
|
47
|
+
await fsp.writeFile(syncAiScriptPath, buildAiFeatureSyncScriptSource(), "utf8");
|
|
48
|
+
await fsp.writeFile(typesFilePath, buildAiFeatureTypesSource(aiFeatureSlug), "utf8");
|
|
49
|
+
await fsp.writeFile(validatorsFilePath, buildAiFeatureValidatorsSource(aiFeatureSlug), "utf8");
|
|
50
|
+
await fsp.writeFile(apiFilePath, buildAiFeatureApiSource(aiFeatureSlug), "utf8");
|
|
51
|
+
await fsp.writeFile(dataFilePath, buildAiFeatureDataSource(aiFeatureSlug), "utf8");
|
|
52
|
+
await fsp.writeFile(phpFilePath, buildAiFeaturePhpSource(aiFeatureSlug, namespace, workspace.workspace.phpPrefix, workspace.workspace.textDomain), "utf8");
|
|
53
|
+
const pascalCase = toPascalCase(aiFeatureSlug);
|
|
54
|
+
await syncAiFeatureRestArtifacts({
|
|
55
|
+
clientFile: `src/ai-features/${aiFeatureSlug}/api-client.ts`,
|
|
56
|
+
outputDir: path.join("src", "ai-features", aiFeatureSlug),
|
|
57
|
+
projectDir: workspace.projectDir,
|
|
58
|
+
typesFile: `src/ai-features/${aiFeatureSlug}/api-types.ts`,
|
|
59
|
+
validatorsFile: `src/ai-features/${aiFeatureSlug}/api-validators.ts`,
|
|
60
|
+
variables: {
|
|
61
|
+
namespace,
|
|
62
|
+
pascalCase,
|
|
63
|
+
slugKebabCase: aiFeatureSlug,
|
|
64
|
+
title: toTitleCase(aiFeatureSlug),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
await syncAiFeatureSchemaArtifact({
|
|
68
|
+
aiSchemaFile: `src/ai-features/${aiFeatureSlug}/ai-schemas/feature-result.ai.schema.json`,
|
|
69
|
+
outputDir: path.join("src", "ai-features", aiFeatureSlug),
|
|
70
|
+
projectDir: workspace.projectDir,
|
|
71
|
+
});
|
|
72
|
+
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
73
|
+
aiFeatureEntries: [
|
|
74
|
+
buildAiFeatureConfigEntry(aiFeatureSlug, namespace),
|
|
75
|
+
],
|
|
76
|
+
transformSource: ensureBlockConfigCanAddRestManifests,
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
warnings: packageScriptChanges.addedProjectToolsDependency
|
|
80
|
+
? [
|
|
81
|
+
"Added `@wp-typia/project-tools` to devDependencies for `sync-ai`. If this workspace was already installed, rerun your package manager install command before the first `wp-typia sync ai`.",
|
|
82
|
+
]
|
|
83
|
+
: [],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
});
|
|
91
87
|
}
|
|
@@ -43,6 +43,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
|
43
43
|
* - ${quotePhpString(adminNoticeMessageFilterHook)} filters the wp-admin notice shown when AI support is unavailable.
|
|
44
44
|
* - ${quotePhpString(unavailableMessageFilterHook)} filters REST-facing unavailable messages by reason code.
|
|
45
45
|
* - ${quotePhpString(telemetryFilterHook)} filters the response telemetry array before schema validation. Return a schema-compatible array.
|
|
46
|
+
*
|
|
47
|
+
* Compatibility note: this server-only endpoint avoids WordPress script-module enqueue APIs so older sites can load the generated feature file safely.
|
|
46
48
|
*/
|
|
47
49
|
|
|
48
50
|
if ( ! function_exists( '${loadSchemaFunctionName}' ) ) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface WorkspaceMutationPlan<TResult> {
|
|
2
|
+
/** Files to capture before the mutation starts. Missing files are restored as absent. */
|
|
3
|
+
filePaths: string[];
|
|
4
|
+
/** Snapshot directories created by the mutation, usually migration fixtures. */
|
|
5
|
+
snapshotDirs?: string[];
|
|
6
|
+
/** Created files or directories to remove if the mutation fails. */
|
|
7
|
+
targetPaths?: string[];
|
|
8
|
+
/** Mutating work to execute after the snapshot is captured. */
|
|
9
|
+
run: () => Promise<TResult>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when the mutation and its rollback both fail.
|
|
13
|
+
*/
|
|
14
|
+
export declare class WorkspaceMutationRollbackError extends Error {
|
|
15
|
+
readonly mutationError: unknown;
|
|
16
|
+
readonly rollbackError: unknown;
|
|
17
|
+
constructor(mutationError: unknown, rollbackError: unknown);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute a workspace add mutation with rollback on any failure.
|
|
21
|
+
*/
|
|
22
|
+
export declare function executeWorkspaceMutationPlan<TResult>({ filePaths, run, snapshotDirs, targetPaths, }: WorkspaceMutationPlan<TResult>): Promise<TResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Insert a PHP snippet before the workspace textdomain hook or closing tag.
|
|
25
|
+
*/
|
|
26
|
+
export declare function insertPhpSnippetBeforeWorkspaceAnchors(source: string, snippet: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Append a PHP snippet before the closing tag when one is present.
|
|
29
|
+
*/
|
|
30
|
+
export declare function appendPhpSnippetBeforeClosingTag(source: string, snippet: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
2
|
+
const DEFAULT_PHP_SNIPPET_INSERTION_ANCHORS = [
|
|
3
|
+
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
4
|
+
/\?>\s*$/u,
|
|
5
|
+
];
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when the mutation and its rollback both fail.
|
|
8
|
+
*/
|
|
9
|
+
export class WorkspaceMutationRollbackError extends Error {
|
|
10
|
+
constructor(mutationError, rollbackError) {
|
|
11
|
+
super("Workspace mutation failed and rollback also failed.");
|
|
12
|
+
this.name = "WorkspaceMutationRollbackError";
|
|
13
|
+
this.mutationError = mutationError;
|
|
14
|
+
this.rollbackError = rollbackError;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute a workspace add mutation with rollback on any failure.
|
|
19
|
+
*/
|
|
20
|
+
export async function executeWorkspaceMutationPlan({ filePaths, run, snapshotDirs = [], targetPaths = [], }) {
|
|
21
|
+
const mutationSnapshot = {
|
|
22
|
+
fileSources: await snapshotWorkspaceFiles(filePaths),
|
|
23
|
+
snapshotDirs: [...snapshotDirs],
|
|
24
|
+
targetPaths: [...targetPaths],
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
return await run();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
try {
|
|
31
|
+
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
32
|
+
}
|
|
33
|
+
catch (rollbackError) {
|
|
34
|
+
throw new WorkspaceMutationRollbackError(error, rollbackError);
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Insert a PHP snippet before the workspace textdomain hook or closing tag.
|
|
41
|
+
*/
|
|
42
|
+
export function insertPhpSnippetBeforeWorkspaceAnchors(source, snippet) {
|
|
43
|
+
for (const anchor of DEFAULT_PHP_SNIPPET_INSERTION_ANCHORS) {
|
|
44
|
+
const candidate = source.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
45
|
+
if (candidate !== source) {
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return `${source.trimEnd()}\n${snippet}\n`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Append a PHP snippet before the closing tag when one is present.
|
|
53
|
+
*/
|
|
54
|
+
export function appendPhpSnippetBeforeClosingTag(source, snippet) {
|
|
55
|
+
const closingTagPattern = /\?>\s*$/u;
|
|
56
|
+
if (closingTagPattern.test(source)) {
|
|
57
|
+
return source.replace(closingTagPattern, `${snippet}\n?>`);
|
|
58
|
+
}
|
|
59
|
+
return `${source.trimEnd()}\n${snippet}\n`;
|
|
60
|
+
}
|
|
@@ -5,6 +5,7 @@ import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
|
5
5
|
import { appendWorkspaceInventoryEntries, readWorkspaceInventory } from "./workspace-inventory.js";
|
|
6
6
|
import { toKebabCase, toSnakeCase, toTitleCase } from "./string-case.js";
|
|
7
7
|
import { assertBlockStyleDoesNotExist, assertBlockTransformDoesNotExist, assertValidGeneratedSlug, assertValidHookAnchor, assertValidHookedBlockPosition, assertVariationDoesNotExist, getMutableBlockHooks, normalizeBlockSlug, patchFile, quoteTsString, readWorkspaceBlockJson, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
8
|
+
import { findExecutablePatternMatch, hasExecutablePattern, hasUncommentedPattern, maskTypeScriptCommentsAndLiterals, } from "./ts-source-masking.js";
|
|
8
9
|
const VARIATIONS_IMPORT_LINE = "import { registerWorkspaceVariations } from './variations';";
|
|
9
10
|
const VARIATIONS_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceVariations\s*\}\s*from\s*["']\.\/variations["']\s*;?\s*$/mu;
|
|
10
11
|
const VARIATIONS_CALL_LINE = "registerWorkspaceVariations();";
|
|
@@ -19,85 +20,6 @@ const BLOCK_TRANSFORMS_CALL_LINE = "applyWorkspaceBlockTransforms(registration.s
|
|
|
19
20
|
const BLOCK_TRANSFORMS_CALL_PATTERN = /applyWorkspaceBlockTransforms\s*\(\s*registration\s*\.\s*settings\s*\)\s*;?/u;
|
|
20
21
|
const SCAFFOLD_REGISTRATION_SETTINGS_CALL_PATTERN = /registerScaffoldBlockType\s*\(\s*registration\s*\.\s*name\s*,\s*registration\s*\.\s*settings\s*\)\s*;?/u;
|
|
21
22
|
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
23
|
function isIdentifierBoundary(source, index) {
|
|
102
24
|
if (index < 0 || index >= source.length) {
|
|
103
25
|
return true;
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The canonical CLI surface stays stable here while the implementation lives
|
|
5
5
|
* in focused internal modules:
|
|
6
|
-
* - `cli-add-shared`
|
|
6
|
+
* - `cli-add-shared` as a compatibility barrel around focused add helpers
|
|
7
7
|
* - `cli-add-block` for built-in block scaffolding
|
|
8
8
|
* - `cli-add-workspace` for workspace mutation commands
|
|
9
9
|
*/
|
|
10
|
-
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, } from "./cli-add-shared.js";
|
|
10
|
+
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, isAddBlockTemplateId, } 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
13
|
export { runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
|