@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
|
@@ -6,9 +6,73 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
import semver from 'semver';
|
|
7
7
|
import { x as extractTarball } from 'tar';
|
|
8
8
|
import { createExternalTemplateTimeoutError, fetchWithExternalTemplateTimeout, getExternalTemplateMetadataMaxBytes, getExternalTemplateTarballMaxBytes, getExternalTemplateTimeoutMs, readBufferResponseWithLimit, readJsonResponseWithLimit, } from './external-template-guards.js';
|
|
9
|
-
import {
|
|
9
|
+
import { findReusableExternalTemplateSourceCache, isExternalTemplateCacheEnabled, resolveExternalTemplateSourceCache, } from './template-source-cache.js';
|
|
10
|
+
import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
|
|
11
|
+
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, PROJECT_TOOLS_PACKAGE_ROOT, TEMPLATE_IDS, } from './template-registry.js';
|
|
10
12
|
import { isPlainObject } from './object-utils.js';
|
|
11
13
|
import { createManagedTempRoot } from './temp-roots.js';
|
|
14
|
+
const USER_FACING_TEMPLATE_IDS = [
|
|
15
|
+
...TEMPLATE_IDS,
|
|
16
|
+
OFFICIAL_WORKSPACE_TEMPLATE_ALIAS,
|
|
17
|
+
];
|
|
18
|
+
const GITHUB_TEMPLATE_CACHE_REVISION_RACE_CODE = 'github-template-cache-revision-race';
|
|
19
|
+
function createGitHubTemplateCacheRevisionRaceError(message) {
|
|
20
|
+
const error = new Error(message);
|
|
21
|
+
error.code = GITHUB_TEMPLATE_CACHE_REVISION_RACE_CODE;
|
|
22
|
+
return error;
|
|
23
|
+
}
|
|
24
|
+
function isGitHubTemplateCacheRevisionRaceError(error) {
|
|
25
|
+
return (typeof error === 'object' &&
|
|
26
|
+
error !== null &&
|
|
27
|
+
'code' in error &&
|
|
28
|
+
error.code ===
|
|
29
|
+
GITHUB_TEMPLATE_CACHE_REVISION_RACE_CODE);
|
|
30
|
+
}
|
|
31
|
+
function getUnknownNpmTemplateMessage(templateId) {
|
|
32
|
+
return [
|
|
33
|
+
`Unknown template "${templateId}". Expected one of: ${USER_FACING_TEMPLATE_IDS.join(', ')}.`,
|
|
34
|
+
'Run `wp-typia templates list` to inspect available templates.',
|
|
35
|
+
'If you meant an npm template package, verify the package name and configured npm registry.',
|
|
36
|
+
].join(' ');
|
|
37
|
+
}
|
|
38
|
+
function readOptionalDistString(dist, key) {
|
|
39
|
+
const value = dist[key];
|
|
40
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
41
|
+
}
|
|
42
|
+
function normalizeNpmRegistryCacheKey(registryBase) {
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(registryBase);
|
|
45
|
+
url.username = '';
|
|
46
|
+
url.password = '';
|
|
47
|
+
url.search = '';
|
|
48
|
+
url.hash = '';
|
|
49
|
+
return url.toString().replace(/\/$/u, '');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return registryBase;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir) {
|
|
56
|
+
const tarballResponse = await fetchWithExternalTemplateTimeout(tarballUrl, {
|
|
57
|
+
label: `downloading npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
58
|
+
});
|
|
59
|
+
if (!tarballResponse.ok) {
|
|
60
|
+
throw new Error(`Failed to download npm template tarball for ${locator.raw}: ${tarballResponse.status}`);
|
|
61
|
+
}
|
|
62
|
+
const tarballPath = path.join(path.dirname(unpackDir), 'template.tgz');
|
|
63
|
+
await fsp.mkdir(unpackDir, { recursive: true });
|
|
64
|
+
await fsp.writeFile(tarballPath, await readBufferResponseWithLimit(tarballResponse, {
|
|
65
|
+
label: `npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
66
|
+
maxBytes: getExternalTemplateTarballMaxBytes(),
|
|
67
|
+
}));
|
|
68
|
+
await extractTarball({
|
|
69
|
+
cwd: unpackDir,
|
|
70
|
+
file: tarballPath,
|
|
71
|
+
strip: 1,
|
|
72
|
+
});
|
|
73
|
+
await fsp.rm(tarballPath, { force: true });
|
|
74
|
+
await assertNoSymlinks(unpackDir);
|
|
75
|
+
}
|
|
12
76
|
function selectRegistryVersion(metadata, locator) {
|
|
13
77
|
const distTags = isPlainObject(metadata['dist-tags'])
|
|
14
78
|
? metadata['dist-tags']
|
|
@@ -49,6 +113,9 @@ async function fetchNpmTemplateSource(locator) {
|
|
|
49
113
|
label: metadataLabel,
|
|
50
114
|
});
|
|
51
115
|
if (!metadataResponse.ok) {
|
|
116
|
+
if (metadataResponse.status === 404) {
|
|
117
|
+
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, getUnknownNpmTemplateMessage(locator.raw));
|
|
118
|
+
}
|
|
52
119
|
throw new Error(`Failed to fetch npm template metadata for ${locator.raw}: ${metadataResponse.status}`);
|
|
53
120
|
}
|
|
54
121
|
const metadata = await readJsonResponseWithLimit(metadataResponse, {
|
|
@@ -65,27 +132,43 @@ async function fetchNpmTemplateSource(locator) {
|
|
|
65
132
|
if (typeof tarballUrl !== 'string' || tarballUrl.length === 0) {
|
|
66
133
|
throw new Error(`npm template metadata is missing tarball URL for ${locator.raw}@${resolvedVersion}.`);
|
|
67
134
|
}
|
|
135
|
+
const tarballIntegrity = readOptionalDistString(versionMetadata.dist, 'integrity');
|
|
136
|
+
const tarballShasum = readOptionalDistString(versionMetadata.dist, 'shasum');
|
|
137
|
+
if (tarballIntegrity || tarballShasum) {
|
|
138
|
+
const registryCacheKey = normalizeNpmRegistryCacheKey(registryBase);
|
|
139
|
+
const cachedSource = await resolveExternalTemplateSourceCache({
|
|
140
|
+
keyParts: [
|
|
141
|
+
'npm',
|
|
142
|
+
registryCacheKey,
|
|
143
|
+
locator.name,
|
|
144
|
+
locator.raw,
|
|
145
|
+
resolvedVersion,
|
|
146
|
+
tarballIntegrity ?? '',
|
|
147
|
+
tarballShasum ?? '',
|
|
148
|
+
],
|
|
149
|
+
metadata: {
|
|
150
|
+
integrity: tarballIntegrity,
|
|
151
|
+
package: locator.name,
|
|
152
|
+
raw: locator.raw,
|
|
153
|
+
registry: registryBase,
|
|
154
|
+
shasum: tarballShasum,
|
|
155
|
+
tarball: tarballUrl,
|
|
156
|
+
version: resolvedVersion,
|
|
157
|
+
},
|
|
158
|
+
namespace: 'npm',
|
|
159
|
+
}, (unpackDir) => downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir));
|
|
160
|
+
if (cachedSource) {
|
|
161
|
+
await assertNoSymlinks(cachedSource.sourceDir);
|
|
162
|
+
return {
|
|
163
|
+
blockDir: cachedSource.sourceDir,
|
|
164
|
+
rootDir: cachedSource.sourceDir,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
68
168
|
const { path: tempRoot, cleanup } = await createManagedTempRoot('wp-typia-template-source-');
|
|
69
169
|
try {
|
|
70
|
-
const tarballResponse = await fetchWithExternalTemplateTimeout(tarballUrl, {
|
|
71
|
-
label: `downloading npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
72
|
-
});
|
|
73
|
-
if (!tarballResponse.ok) {
|
|
74
|
-
throw new Error(`Failed to download npm template tarball for ${locator.raw}: ${tarballResponse.status}`);
|
|
75
|
-
}
|
|
76
|
-
const tarballPath = path.join(tempRoot, 'template.tgz');
|
|
77
170
|
const unpackDir = path.join(tempRoot, 'source');
|
|
78
|
-
await
|
|
79
|
-
await fsp.writeFile(tarballPath, await readBufferResponseWithLimit(tarballResponse, {
|
|
80
|
-
label: `npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
81
|
-
maxBytes: getExternalTemplateTarballMaxBytes(),
|
|
82
|
-
}));
|
|
83
|
-
await extractTarball({
|
|
84
|
-
cwd: unpackDir,
|
|
85
|
-
file: tarballPath,
|
|
86
|
-
strip: 1,
|
|
87
|
-
});
|
|
88
|
-
await assertNoSymlinks(unpackDir);
|
|
171
|
+
await downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir);
|
|
89
172
|
return {
|
|
90
173
|
blockDir: unpackDir,
|
|
91
174
|
cleanup,
|
|
@@ -186,47 +269,244 @@ export async function assertNoSymlinks(sourceDir) {
|
|
|
186
269
|
await assertNoSymlinks(path.join(sourceDir, entry));
|
|
187
270
|
}
|
|
188
271
|
}
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const cloneTimeoutMs = getExternalTemplateTimeoutMs();
|
|
199
|
-
const cloneResult = spawnSync('git', args, {
|
|
272
|
+
function runGitTemplateCommand(args, label, options = {}) {
|
|
273
|
+
const timeoutMs = getExternalTemplateTimeoutMs();
|
|
274
|
+
const result = options.captureOutput
|
|
275
|
+
? spawnSync('git', args, {
|
|
276
|
+
encoding: 'utf8',
|
|
277
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
278
|
+
timeout: timeoutMs,
|
|
279
|
+
})
|
|
280
|
+
: spawnSync('git', args, {
|
|
200
281
|
stdio: 'ignore',
|
|
201
|
-
timeout:
|
|
282
|
+
timeout: timeoutMs,
|
|
202
283
|
});
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
throw cloneResult.error;
|
|
284
|
+
if (result.error) {
|
|
285
|
+
const errorCode = typeof result.error === 'object' &&
|
|
286
|
+
result.error !== null &&
|
|
287
|
+
'code' in result.error
|
|
288
|
+
? String(result.error.code)
|
|
289
|
+
: '';
|
|
290
|
+
if (errorCode === 'ETIMEDOUT') {
|
|
291
|
+
throw createExternalTemplateTimeoutError(label, timeoutMs);
|
|
213
292
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
293
|
+
throw result.error;
|
|
294
|
+
}
|
|
295
|
+
if (result.signal === 'SIGTERM' || result.signal === 'SIGKILL') {
|
|
296
|
+
throw createExternalTemplateTimeoutError(label, timeoutMs);
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
function getGitHubTemplateRepositoryUrl(locator) {
|
|
301
|
+
return `https://github.com/${locator.owner}/${locator.repo}.git`;
|
|
302
|
+
}
|
|
303
|
+
function resolveGitHubTemplateDirectory(checkoutDir, locator) {
|
|
304
|
+
const sourceDir = path.resolve(checkoutDir, locator.sourcePath);
|
|
305
|
+
const relativeSourceDir = path.relative(checkoutDir, sourceDir);
|
|
306
|
+
if (relativeSourceDir.startsWith('..') || path.isAbsolute(relativeSourceDir)) {
|
|
307
|
+
throw new Error('GitHub template path must stay within the cloned repository.');
|
|
308
|
+
}
|
|
309
|
+
if (!fs.existsSync(sourceDir)) {
|
|
310
|
+
throw new Error(`GitHub template path does not exist: ${locator.sourcePath}`);
|
|
311
|
+
}
|
|
312
|
+
return sourceDir;
|
|
313
|
+
}
|
|
314
|
+
function cloneGitHubTemplateSource(locator, checkoutDir) {
|
|
315
|
+
const args = ['clone', '--depth', '1'];
|
|
316
|
+
if (locator.ref) {
|
|
317
|
+
args.push('--branch', locator.ref);
|
|
318
|
+
}
|
|
319
|
+
args.push(getGitHubTemplateRepositoryUrl(locator), checkoutDir);
|
|
320
|
+
const cloneResult = runGitTemplateCommand(args, `cloning GitHub template ${locator.owner}/${locator.repo}`);
|
|
321
|
+
if (cloneResult.status !== 0) {
|
|
322
|
+
throw new Error(`Failed to clone GitHub template source ${locator.owner}/${locator.repo}.`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function readGitHubTemplateHeadRevision(checkoutDir) {
|
|
326
|
+
const result = runGitTemplateCommand(['-C', checkoutDir, 'rev-parse', 'HEAD'], 'reading GitHub template checkout revision', { captureOutput: true });
|
|
327
|
+
if (result.status !== 0 || typeof result.stdout !== 'string') {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const revision = result.stdout.trim().split(/\s+/u)[0];
|
|
331
|
+
return /^[0-9a-f]{40}$/iu.test(revision) ? revision.toLowerCase() : null;
|
|
332
|
+
}
|
|
333
|
+
function pinGitHubTemplateCacheRevision(locator, checkoutDir, cacheRevision) {
|
|
334
|
+
const normalizedCacheRevision = cacheRevision.toLowerCase();
|
|
335
|
+
if (readGitHubTemplateHeadRevision(checkoutDir) === normalizedCacheRevision) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const fetchResult = runGitTemplateCommand(['-C', checkoutDir, 'fetch', '--depth', '1', 'origin', cacheRevision], `fetching GitHub template revision ${locator.owner}/${locator.repo}`);
|
|
339
|
+
if (fetchResult.status !== 0) {
|
|
340
|
+
throw createGitHubTemplateCacheRevisionRaceError(`Failed to fetch GitHub template revision ${cacheRevision} for ${locator.owner}/${locator.repo}.`);
|
|
341
|
+
}
|
|
342
|
+
const checkoutResult = runGitTemplateCommand(['-C', checkoutDir, 'checkout', '--detach', cacheRevision], `checking out GitHub template revision ${locator.owner}/${locator.repo}`);
|
|
343
|
+
if (checkoutResult.status !== 0) {
|
|
344
|
+
throw createGitHubTemplateCacheRevisionRaceError(`Failed to check out GitHub template revision ${cacheRevision} for ${locator.owner}/${locator.repo}.`);
|
|
345
|
+
}
|
|
346
|
+
if (readGitHubTemplateHeadRevision(checkoutDir) !== normalizedCacheRevision) {
|
|
347
|
+
throw createGitHubTemplateCacheRevisionRaceError(`GitHub template checkout did not match resolved revision ${cacheRevision} for ${locator.owner}/${locator.repo}.`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function getGitHubTemplateRevisionPatterns(locator) {
|
|
351
|
+
const ref = locator.ref ?? 'HEAD';
|
|
352
|
+
if (!locator.ref) {
|
|
353
|
+
return [ref];
|
|
354
|
+
}
|
|
355
|
+
if (ref.startsWith('refs/')) {
|
|
356
|
+
return [ref, `${ref}^{}`];
|
|
357
|
+
}
|
|
358
|
+
return [ref, `refs/heads/${ref}`, `refs/tags/${ref}`, `refs/tags/${ref}^{}`];
|
|
359
|
+
}
|
|
360
|
+
function pickGitHubTemplateCacheRevision(locator, revisions) {
|
|
361
|
+
const ref = locator.ref ?? 'HEAD';
|
|
362
|
+
if (!locator.ref) {
|
|
363
|
+
return revisions[0]?.revision ?? null;
|
|
364
|
+
}
|
|
365
|
+
if (!ref.startsWith('refs/')) {
|
|
366
|
+
const branchRevision = revisions.find((entry) => entry.resolvedRef === `refs/heads/${ref}`);
|
|
367
|
+
if (branchRevision) {
|
|
368
|
+
return branchRevision.revision;
|
|
217
369
|
}
|
|
218
|
-
|
|
219
|
-
|
|
370
|
+
const peeledTagRevision = revisions.find((entry) => entry.resolvedRef === `refs/tags/${ref}^{}`);
|
|
371
|
+
if (peeledTagRevision) {
|
|
372
|
+
return peeledTagRevision.revision;
|
|
220
373
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
path.isAbsolute(relativeSourceDir)) {
|
|
225
|
-
throw new Error('GitHub template path must stay within the cloned repository.');
|
|
374
|
+
const tagRevision = revisions.find((entry) => entry.resolvedRef === `refs/tags/${ref}`);
|
|
375
|
+
if (tagRevision) {
|
|
376
|
+
return tagRevision.revision;
|
|
226
377
|
}
|
|
227
|
-
|
|
228
|
-
|
|
378
|
+
}
|
|
379
|
+
if (ref.startsWith('refs/tags/')) {
|
|
380
|
+
const peeledRevision = revisions.find((entry) => entry.resolvedRef === `${ref}^{}`);
|
|
381
|
+
if (peeledRevision) {
|
|
382
|
+
return peeledRevision.revision;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const exactRevision = revisions.find((entry) => entry.resolvedRef === ref);
|
|
386
|
+
return (exactRevision ?? revisions[0])?.revision ?? null;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Resolves a GitHub template ref to a stable revision for cache keying.
|
|
390
|
+
*
|
|
391
|
+
* @param locator GitHub template locator containing owner, repo, and optional ref.
|
|
392
|
+
* @returns Revision lookup result, including whether `git ls-remote` was unavailable.
|
|
393
|
+
*/
|
|
394
|
+
function resolveGitHubTemplateCacheRevision(locator) {
|
|
395
|
+
const result = runGitTemplateCommand([
|
|
396
|
+
'ls-remote',
|
|
397
|
+
getGitHubTemplateRepositoryUrl(locator),
|
|
398
|
+
...getGitHubTemplateRevisionPatterns(locator),
|
|
399
|
+
], `checking GitHub template revision ${locator.owner}/${locator.repo}`, { captureOutput: true });
|
|
400
|
+
if (result.status !== 0 || typeof result.stdout !== 'string') {
|
|
401
|
+
return {
|
|
402
|
+
lookupUnavailable: true,
|
|
403
|
+
revision: null,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const revisions = result.stdout
|
|
407
|
+
.split('\n')
|
|
408
|
+
.map((line) => {
|
|
409
|
+
const [revision, resolvedRef] = line.trim().split(/\s+/u);
|
|
410
|
+
if (!/^[0-9a-f]{40}$/iu.test(revision) || !resolvedRef) {
|
|
411
|
+
return null;
|
|
229
412
|
}
|
|
413
|
+
return {
|
|
414
|
+
resolvedRef,
|
|
415
|
+
revision: revision.toLowerCase(),
|
|
416
|
+
};
|
|
417
|
+
})
|
|
418
|
+
.filter((entry) => entry !== null);
|
|
419
|
+
return {
|
|
420
|
+
lookupUnavailable: false,
|
|
421
|
+
revision: pickGitHubTemplateCacheRevision(locator, revisions),
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function getGitHubTemplateCacheMetadata(locator, revision) {
|
|
425
|
+
return {
|
|
426
|
+
...(revision ? { revision } : {}),
|
|
427
|
+
owner: locator.owner,
|
|
428
|
+
ref: locator.ref,
|
|
429
|
+
repo: locator.repo,
|
|
430
|
+
sourcePath: locator.sourcePath,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
async function reuseGitHubTemplateCacheByMetadata(locator) {
|
|
434
|
+
const cachedSource = await findReusableExternalTemplateSourceCache({
|
|
435
|
+
metadata: getGitHubTemplateCacheMetadata(locator),
|
|
436
|
+
namespace: 'github',
|
|
437
|
+
});
|
|
438
|
+
if (!cachedSource) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
442
|
+
await assertNoSymlinks(sourceDir);
|
|
443
|
+
return {
|
|
444
|
+
blockDir: sourceDir,
|
|
445
|
+
rootDir: sourceDir,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
async function resolveGitHubTemplateSource(locator) {
|
|
449
|
+
const cacheEnabled = isExternalTemplateCacheEnabled();
|
|
450
|
+
let cacheRevisionLookupUnavailable = false;
|
|
451
|
+
let cacheRevision = null;
|
|
452
|
+
if (cacheEnabled) {
|
|
453
|
+
try {
|
|
454
|
+
const resolvedRevision = resolveGitHubTemplateCacheRevision(locator);
|
|
455
|
+
cacheRevision = resolvedRevision.revision;
|
|
456
|
+
cacheRevisionLookupUnavailable = resolvedRevision.lookupUnavailable;
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
cacheRevision = null;
|
|
460
|
+
cacheRevisionLookupUnavailable = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (cacheEnabled && !cacheRevision && cacheRevisionLookupUnavailable) {
|
|
464
|
+
const cachedSource = await reuseGitHubTemplateCacheByMetadata(locator);
|
|
465
|
+
if (cachedSource) {
|
|
466
|
+
return cachedSource;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (cacheRevision) {
|
|
470
|
+
const resolvedCacheRevision = cacheRevision;
|
|
471
|
+
try {
|
|
472
|
+
const cachedSource = await resolveExternalTemplateSourceCache({
|
|
473
|
+
keyParts: [
|
|
474
|
+
'github',
|
|
475
|
+
locator.owner,
|
|
476
|
+
locator.repo,
|
|
477
|
+
locator.sourcePath,
|
|
478
|
+
locator.ref ?? '',
|
|
479
|
+
resolvedCacheRevision,
|
|
480
|
+
],
|
|
481
|
+
metadata: getGitHubTemplateCacheMetadata(locator, resolvedCacheRevision),
|
|
482
|
+
namespace: 'github',
|
|
483
|
+
}, async (checkoutDir) => {
|
|
484
|
+
cloneGitHubTemplateSource(locator, checkoutDir);
|
|
485
|
+
const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
486
|
+
await assertNoSymlinks(sourceDir);
|
|
487
|
+
pinGitHubTemplateCacheRevision(locator, checkoutDir, resolvedCacheRevision);
|
|
488
|
+
});
|
|
489
|
+
if (cachedSource) {
|
|
490
|
+
const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
491
|
+
await assertNoSymlinks(sourceDir);
|
|
492
|
+
return {
|
|
493
|
+
blockDir: sourceDir,
|
|
494
|
+
rootDir: sourceDir,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
if (!isGitHubTemplateCacheRevisionRaceError(error)) {
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
// Fall back to the existing uncached clone path if revision pinning races.
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const { path: remoteRoot, cleanup } = await createManagedTempRoot('wp-typia-template-source-');
|
|
506
|
+
const checkoutDir = path.join(remoteRoot, 'source');
|
|
507
|
+
try {
|
|
508
|
+
cloneGitHubTemplateSource(locator, checkoutDir);
|
|
509
|
+
const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
230
510
|
await assertNoSymlinks(sourceDir);
|
|
231
511
|
return {
|
|
232
512
|
blockDir: sourceDir,
|
|
@@ -10,11 +10,25 @@ export interface WorkspaceVariationInventoryEntry {
|
|
|
10
10
|
file: string;
|
|
11
11
|
slug: string;
|
|
12
12
|
}
|
|
13
|
+
export interface WorkspaceBlockStyleInventoryEntry {
|
|
14
|
+
block: string;
|
|
15
|
+
file: string;
|
|
16
|
+
slug: string;
|
|
17
|
+
}
|
|
18
|
+
export interface WorkspaceBlockTransformInventoryEntry {
|
|
19
|
+
block: string;
|
|
20
|
+
file: string;
|
|
21
|
+
from: string;
|
|
22
|
+
slug: string;
|
|
23
|
+
to: string;
|
|
24
|
+
}
|
|
13
25
|
export interface WorkspacePatternInventoryEntry {
|
|
14
26
|
file: string;
|
|
15
27
|
slug: string;
|
|
16
28
|
}
|
|
17
29
|
export interface WorkspaceBindingSourceInventoryEntry {
|
|
30
|
+
attribute?: string;
|
|
31
|
+
block?: string;
|
|
18
32
|
editorFile: string;
|
|
19
33
|
serverFile: string;
|
|
20
34
|
slug: string;
|
|
@@ -76,6 +90,20 @@ export interface WorkspaceAiFeatureInventoryEntry {
|
|
|
76
90
|
typesFile: string;
|
|
77
91
|
validatorsFile: string;
|
|
78
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* DataViews admin-screen entry parsed from `scripts/block-config.ts`.
|
|
95
|
+
*
|
|
96
|
+
* @property file Relative path to the generated admin view shared entry file.
|
|
97
|
+
* @property phpFile Relative path to the generated WordPress admin page glue.
|
|
98
|
+
* @property slug Normalized admin view slug.
|
|
99
|
+
* @property source Optional source locator such as `rest-resource:products`.
|
|
100
|
+
*/
|
|
101
|
+
export interface WorkspaceAdminViewInventoryEntry {
|
|
102
|
+
file: string;
|
|
103
|
+
phpFile: string;
|
|
104
|
+
slug: string;
|
|
105
|
+
source?: string;
|
|
106
|
+
}
|
|
79
107
|
/**
|
|
80
108
|
* Editor-plugin entry parsed from `scripts/block-config.ts`.
|
|
81
109
|
*
|
|
@@ -89,14 +117,20 @@ export interface WorkspaceEditorPluginInventoryEntry {
|
|
|
89
117
|
slot: string;
|
|
90
118
|
}
|
|
91
119
|
export interface WorkspaceInventory {
|
|
120
|
+
adminViews: WorkspaceAdminViewInventoryEntry[];
|
|
92
121
|
bindingSources: WorkspaceBindingSourceInventoryEntry[];
|
|
93
122
|
blockConfigPath: string;
|
|
94
123
|
blocks: WorkspaceBlockInventoryEntry[];
|
|
124
|
+
blockStyles: WorkspaceBlockStyleInventoryEntry[];
|
|
125
|
+
blockTransforms: WorkspaceBlockTransformInventoryEntry[];
|
|
95
126
|
abilities: WorkspaceAbilityInventoryEntry[];
|
|
96
127
|
aiFeatures: WorkspaceAiFeatureInventoryEntry[];
|
|
97
128
|
hasAbilitiesSection: boolean;
|
|
129
|
+
hasAdminViewsSection: boolean;
|
|
98
130
|
hasBindingSourcesSection: boolean;
|
|
99
131
|
hasAiFeaturesSection: boolean;
|
|
132
|
+
hasBlockStylesSection: boolean;
|
|
133
|
+
hasBlockTransformsSection: boolean;
|
|
100
134
|
hasEditorPluginsSection: boolean;
|
|
101
135
|
hasPatternsSection: boolean;
|
|
102
136
|
hasRestResourcesSection: boolean;
|
|
@@ -109,11 +143,14 @@ export interface WorkspaceInventory {
|
|
|
109
143
|
}
|
|
110
144
|
export declare const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
|
|
111
145
|
export declare const VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
|
|
146
|
+
export declare const BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
|
|
147
|
+
export declare const BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER = "\t// wp-typia add transform entries";
|
|
112
148
|
export declare const PATTERN_CONFIG_ENTRY_MARKER = "\t// wp-typia add pattern entries";
|
|
113
149
|
export declare const BINDING_SOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add binding-source entries";
|
|
114
150
|
export declare const REST_RESOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add rest-resource entries";
|
|
115
151
|
export declare const ABILITY_CONFIG_ENTRY_MARKER = "\t// wp-typia add ability entries";
|
|
116
152
|
export declare const AI_FEATURE_CONFIG_ENTRY_MARKER = "\t// wp-typia add ai-feature entries";
|
|
153
|
+
export declare const ADMIN_VIEW_CONFIG_ENTRY_MARKER = "\t// wp-typia add admin-view entries";
|
|
117
154
|
/**
|
|
118
155
|
* Marker used to append generated editor-plugin entries into `EDITOR_PLUGINS`.
|
|
119
156
|
*/
|
|
@@ -152,7 +189,8 @@ export declare function getWorkspaceBlockSelectOptions(projectDir: string): Arra
|
|
|
152
189
|
* Update `scripts/block-config.ts` source text with additional inventory entries.
|
|
153
190
|
*
|
|
154
191
|
* Missing inventory sections for variations, patterns, binding sources, REST
|
|
155
|
-
* resources, workflow abilities, AI features,
|
|
192
|
+
* resources, workflow abilities, AI features, editor plugins, block styles, and
|
|
193
|
+
* block transforms are created
|
|
156
194
|
* automatically before new entries are appended at their marker comments.
|
|
157
195
|
* When provided, `transformSource` runs before any entries are inserted.
|
|
158
196
|
*
|
|
@@ -160,10 +198,13 @@ export declare function getWorkspaceBlockSelectOptions(projectDir: string): Arra
|
|
|
160
198
|
* @param options Entry lists plus an optional source transformer.
|
|
161
199
|
* @returns Updated source text with all requested inventory entries appended.
|
|
162
200
|
*/
|
|
163
|
-
export declare function updateWorkspaceInventorySource(source: string, { blockEntries, bindingSourceEntries, abilityEntries, aiFeatureEntries, editorPluginEntries, patternEntries, restResourceEntries, variationEntries, transformSource, }?: {
|
|
201
|
+
export declare function updateWorkspaceInventorySource(source: string, { blockEntries, blockStyleEntries, blockTransformEntries, bindingSourceEntries, abilityEntries, adminViewEntries, aiFeatureEntries, editorPluginEntries, patternEntries, restResourceEntries, variationEntries, transformSource, }?: {
|
|
164
202
|
abilityEntries?: string[];
|
|
203
|
+
adminViewEntries?: string[];
|
|
165
204
|
aiFeatureEntries?: string[];
|
|
166
205
|
blockEntries?: string[];
|
|
206
|
+
blockStyleEntries?: string[];
|
|
207
|
+
blockTransformEntries?: string[];
|
|
167
208
|
bindingSourceEntries?: string[];
|
|
168
209
|
editorPluginEntries?: string[];
|
|
169
210
|
patternEntries?: string[];
|