@wp-typia/project-tools 0.22.3 → 0.22.5
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-anchors.js +3 -24
- 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-assets.js +7 -50
- 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-rest-anchors.js +3 -24
- 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 +368 -284
- 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
|
}
|
|
@@ -2,6 +2,7 @@ import { promises as fsp } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { getPackageVersions } from "./package-versions.js";
|
|
4
4
|
import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
|
|
5
|
+
import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
|
|
5
6
|
import { hasPhpFunctionDefinition } from "./php-utils.js";
|
|
6
7
|
const AI_FEATURE_SERVER_GLOB = "/inc/ai-features/*.php";
|
|
7
8
|
/**
|
|
@@ -21,30 +22,8 @@ function ${registerFunctionName}() {
|
|
|
21
22
|
\t}
|
|
22
23
|
}
|
|
23
24
|
`;
|
|
24
|
-
const insertionAnchors = [
|
|
25
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
26
|
-
/\?>\s*$/u,
|
|
27
|
-
];
|
|
28
|
-
const insertPhpSnippet = (snippet) => {
|
|
29
|
-
for (const anchor of insertionAnchors) {
|
|
30
|
-
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
31
|
-
if (candidate !== nextSource) {
|
|
32
|
-
nextSource = candidate;
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
37
|
-
};
|
|
38
|
-
const appendPhpSnippet = (snippet) => {
|
|
39
|
-
const closingTagPattern = /\?>\s*$/u;
|
|
40
|
-
if (closingTagPattern.test(nextSource)) {
|
|
41
|
-
nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
45
|
-
};
|
|
46
25
|
if (!hasPhpFunctionDefinition(nextSource, registerFunctionName)) {
|
|
47
|
-
|
|
26
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, registerFunction);
|
|
48
27
|
}
|
|
49
28
|
else if (!nextSource.includes(AI_FEATURE_SERVER_GLOB)) {
|
|
50
29
|
throw new Error([
|
|
@@ -54,7 +33,7 @@ function ${registerFunctionName}() {
|
|
|
54
33
|
].join(" "));
|
|
55
34
|
}
|
|
56
35
|
if (!nextSource.includes(registerHook)) {
|
|
57
|
-
|
|
36
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, registerHook);
|
|
58
37
|
}
|
|
59
38
|
return nextSource;
|
|
60
39
|
});
|
|
@@ -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}' ) ) {
|
|
@@ -8,6 +8,7 @@ import { readWorkspaceInventory, appendWorkspaceInventoryEntries, } from "./work
|
|
|
8
8
|
import { toPascalCase, toTitleCase } from "./string-case.js";
|
|
9
9
|
import { findPhpFunctionRange, hasPhpFunctionDefinition, quotePhpString, replacePhpFunctionDefinition, } from "./php-utils.js";
|
|
10
10
|
import { assertBindingSourceDoesNotExist, assertEditorPluginDoesNotExist, assertPatternDoesNotExist, assertValidEditorPluginSlot, assertValidGeneratedSlug, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
11
|
+
import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
|
|
11
12
|
import { normalizeOptionalCliString } from "./cli-validation.js";
|
|
12
13
|
const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
|
|
13
14
|
const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
|
|
@@ -597,39 +598,17 @@ function ${bindingEditorEnqueueFunctionName}() {
|
|
|
597
598
|
\t);
|
|
598
599
|
}
|
|
599
600
|
`;
|
|
600
|
-
const insertionAnchors = [
|
|
601
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
602
|
-
/\?>\s*$/u,
|
|
603
|
-
];
|
|
604
|
-
const insertPhpSnippet = (snippet) => {
|
|
605
|
-
for (const anchor of insertionAnchors) {
|
|
606
|
-
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
607
|
-
if (candidate !== nextSource) {
|
|
608
|
-
nextSource = candidate;
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
613
|
-
};
|
|
614
|
-
const appendPhpSnippet = (snippet) => {
|
|
615
|
-
const closingTagPattern = /\?>\s*$/u;
|
|
616
|
-
if (closingTagPattern.test(nextSource)) {
|
|
617
|
-
nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
621
|
-
};
|
|
622
601
|
if (!hasPhpFunctionDefinition(nextSource, bindingRegistrationFunctionName)) {
|
|
623
|
-
|
|
602
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, bindingRegistrationFunction);
|
|
624
603
|
}
|
|
625
604
|
if (!hasPhpFunctionDefinition(nextSource, bindingEditorEnqueueFunctionName)) {
|
|
626
|
-
|
|
605
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, bindingEditorEnqueueFunction);
|
|
627
606
|
}
|
|
628
607
|
if (!nextSource.includes(bindingRegistrationHook)) {
|
|
629
|
-
|
|
608
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, bindingRegistrationHook);
|
|
630
609
|
}
|
|
631
610
|
if (!nextSource.includes(bindingEditorEnqueueHook)) {
|
|
632
|
-
|
|
611
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, bindingEditorEnqueueHook);
|
|
633
612
|
}
|
|
634
613
|
return nextSource;
|
|
635
614
|
});
|
|
@@ -679,30 +658,8 @@ function ${enqueueFunctionName}() {
|
|
|
679
658
|
\t}
|
|
680
659
|
}
|
|
681
660
|
`;
|
|
682
|
-
const insertionAnchors = [
|
|
683
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
684
|
-
/\?>\s*$/u,
|
|
685
|
-
];
|
|
686
|
-
const insertPhpSnippet = (snippet) => {
|
|
687
|
-
for (const anchor of insertionAnchors) {
|
|
688
|
-
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
689
|
-
if (candidate !== nextSource) {
|
|
690
|
-
nextSource = candidate;
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
695
|
-
};
|
|
696
|
-
const appendPhpSnippet = (snippet) => {
|
|
697
|
-
const closingTagPattern = /\?>\s*$/u;
|
|
698
|
-
if (closingTagPattern.test(nextSource)) {
|
|
699
|
-
nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
703
|
-
};
|
|
704
661
|
if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
|
|
705
|
-
|
|
662
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, enqueueFunction);
|
|
706
663
|
}
|
|
707
664
|
else {
|
|
708
665
|
const requiredReferences = [
|
|
@@ -726,7 +683,7 @@ function ${enqueueFunctionName}() {
|
|
|
726
683
|
}
|
|
727
684
|
}
|
|
728
685
|
if (!nextSource.includes(enqueueHook)) {
|
|
729
|
-
|
|
686
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, enqueueHook);
|
|
730
687
|
}
|
|
731
688
|
return nextSource;
|
|
732
689
|
});
|
|
@@ -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;
|