@wp-typia/project-tools 0.16.11 → 0.16.13
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/README.md +9 -3
- package/dist/runtime/block-generator-service-core.d.ts +8 -0
- package/dist/runtime/block-generator-service-core.js +274 -0
- package/dist/runtime/block-generator-service-spec.d.ts +104 -0
- package/dist/runtime/block-generator-service-spec.js +139 -0
- package/dist/runtime/block-generator-service.d.ts +2 -110
- package/dist/runtime/block-generator-service.js +2 -389
- package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
- package/dist/runtime/built-in-block-artifact-documents.js +2 -0
- package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
- package/dist/runtime/built-in-block-artifact-types.js +304 -0
- package/dist/runtime/built-in-block-artifacts.js +4 -803
- package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
- package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
- package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
- package/dist/runtime/built-in-block-attribute-specs.js +358 -0
- package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
- package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
- package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
- package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
- package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
- package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
- package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
- package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
- package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
- package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
- package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
- package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
- package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
- package/dist/runtime/built-in-block-code-templates.js +5 -2230
- package/dist/runtime/cli-add-block-config.d.ts +6 -0
- package/dist/runtime/cli-add-block-config.js +143 -0
- package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
- package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
- package/dist/runtime/cli-add-block.js +3 -301
- package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
- package/dist/runtime/cli-add-workspace-assets.js +399 -0
- package/dist/runtime/cli-add-workspace.d.ts +2 -38
- package/dist/runtime/cli-add-workspace.js +5 -396
- package/dist/runtime/cli-diagnostics.js +76 -4
- package/dist/runtime/cli-doctor-environment.d.ts +12 -0
- package/dist/runtime/cli-doctor-environment.js +123 -0
- package/dist/runtime/cli-doctor-workspace.d.ts +18 -0
- package/dist/runtime/cli-doctor-workspace.js +308 -0
- package/dist/runtime/cli-doctor.d.ts +4 -2
- package/dist/runtime/cli-doctor.js +10 -405
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-scaffold.d.ts +8 -1
- package/dist/runtime/cli-scaffold.js +47 -4
- package/dist/runtime/migration-command-surface.d.ts +67 -0
- package/dist/runtime/migration-command-surface.js +189 -0
- package/dist/runtime/migration-diff-rename.d.ts +13 -0
- package/dist/runtime/migration-diff-rename.js +192 -0
- package/dist/runtime/migration-diff-transform.d.ts +14 -0
- package/dist/runtime/migration-diff-transform.js +105 -0
- package/dist/runtime/migration-diff.js +12 -297
- package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
- package/dist/runtime/migration-generated-artifacts.js +41 -0
- package/dist/runtime/migration-maintenance-fixtures.d.ts +23 -0
- package/dist/runtime/migration-maintenance-fixtures.js +126 -0
- package/dist/runtime/migration-maintenance-verify.d.ts +26 -0
- package/dist/runtime/migration-maintenance-verify.js +262 -0
- package/dist/runtime/migration-maintenance.d.ts +2 -0
- package/dist/runtime/migration-maintenance.js +2 -0
- package/dist/runtime/migration-planning.d.ts +23 -0
- package/dist/runtime/migration-planning.js +131 -0
- package/dist/runtime/migration-project-config-source.d.ts +6 -0
- package/dist/runtime/migration-project-config-source.js +424 -0
- package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
- package/dist/runtime/migration-project-layout-discovery.js +337 -0
- package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
- package/dist/runtime/migration-project-layout-paths.js +288 -0
- package/dist/runtime/migration-project-layout.d.ts +3 -0
- package/dist/runtime/migration-project-layout.js +2 -0
- package/dist/runtime/migration-project-workspace.d.ts +47 -0
- package/dist/runtime/migration-project-workspace.js +212 -0
- package/dist/runtime/migration-project.d.ts +4 -94
- package/dist/runtime/migration-project.js +3 -1101
- package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
- package/dist/runtime/migration-render-diff-rule.js +120 -0
- package/dist/runtime/migration-render-execution.d.ts +3 -0
- package/dist/runtime/migration-render-execution.js +428 -0
- package/dist/runtime/migration-render-generated.d.ts +27 -0
- package/dist/runtime/migration-render-generated.js +230 -0
- package/dist/runtime/migration-render-support.d.ts +3 -0
- package/dist/runtime/migration-render-support.js +16 -0
- package/dist/runtime/migration-render.d.ts +3 -33
- package/dist/runtime/migration-render.js +3 -789
- package/dist/runtime/migrations.d.ts +24 -121
- package/dist/runtime/migrations.js +12 -700
- package/dist/runtime/scaffold-apply-utils.d.ts +9 -0
- package/dist/runtime/scaffold-apply-utils.js +27 -4
- package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
- package/dist/runtime/scaffold-bootstrap.js +185 -0
- package/dist/runtime/scaffold-onboarding.d.ts +12 -0
- package/dist/runtime/scaffold-onboarding.js +42 -5
- package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
- package/dist/runtime/scaffold-package-manager-files.js +79 -0
- package/dist/runtime/scaffold.d.ts +1 -12
- package/dist/runtime/scaffold.js +11 -394
- package/dist/runtime/template-source-contracts.d.ts +81 -0
- package/dist/runtime/template-source-contracts.js +1 -0
- package/dist/runtime/template-source-external.d.ts +21 -0
- package/dist/runtime/template-source-external.js +184 -0
- package/dist/runtime/template-source-locators.d.ts +4 -0
- package/dist/runtime/template-source-locators.js +72 -0
- package/dist/runtime/template-source-normalization.d.ts +7 -0
- package/dist/runtime/template-source-normalization.js +53 -0
- package/dist/runtime/template-source-remote.d.ts +23 -0
- package/dist/runtime/template-source-remote.js +336 -0
- package/dist/runtime/template-source-seeds.d.ts +12 -0
- package/dist/runtime/template-source-seeds.js +243 -0
- package/dist/runtime/template-source.d.ts +4 -86
- package/dist/runtime/template-source.js +9 -828
- package/package.json +4 -4
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { promises as fsp } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { resolveWorkspaceProject
|
|
5
|
-
import { appendWorkspaceInventoryEntries, readWorkspaceInventory
|
|
6
|
-
import { toKebabCase, toTitleCase
|
|
7
|
-
import {
|
|
4
|
+
import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
5
|
+
import { appendWorkspaceInventoryEntries, readWorkspaceInventory } from "./workspace-inventory.js";
|
|
6
|
+
import { toKebabCase, toTitleCase } from "./string-case.js";
|
|
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
9
|
const VARIATIONS_CALL_LINE = "registerWorkspaceVariations();";
|
|
10
|
-
const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
|
|
11
|
-
const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
|
|
12
|
-
const BINDING_SOURCE_EDITOR_SCRIPT = "build/bindings/index.js";
|
|
13
|
-
const BINDING_SOURCE_EDITOR_ASSET = "build/bindings/index.asset.php";
|
|
14
|
-
function escapeRegex(value) {
|
|
15
|
-
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
16
|
-
}
|
|
17
|
-
function quotePhpString(value) {
|
|
18
|
-
return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
|
|
19
|
-
}
|
|
20
10
|
function buildVariationConfigEntry(blockSlug, variationSlug) {
|
|
21
11
|
return [
|
|
22
12
|
"\t{",
|
|
@@ -26,23 +16,6 @@ function buildVariationConfigEntry(blockSlug, variationSlug) {
|
|
|
26
16
|
"\t},",
|
|
27
17
|
].join("\n");
|
|
28
18
|
}
|
|
29
|
-
function buildPatternConfigEntry(patternSlug) {
|
|
30
|
-
return [
|
|
31
|
-
"\t{",
|
|
32
|
-
`\t\tfile: ${quoteTsString(`src/patterns/${patternSlug}.php`)},`,
|
|
33
|
-
`\t\tslug: ${quoteTsString(patternSlug)},`,
|
|
34
|
-
"\t},",
|
|
35
|
-
].join("\n");
|
|
36
|
-
}
|
|
37
|
-
function buildBindingSourceConfigEntry(bindingSourceSlug) {
|
|
38
|
-
return [
|
|
39
|
-
"\t{",
|
|
40
|
-
`\t\teditorFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/editor.ts`)},`,
|
|
41
|
-
`\t\tserverFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/server.php`)},`,
|
|
42
|
-
`\t\tslug: ${quoteTsString(bindingSourceSlug)},`,
|
|
43
|
-
"\t},",
|
|
44
|
-
].join("\n");
|
|
45
|
-
}
|
|
46
19
|
function buildVariationConstName(variationSlug) {
|
|
47
20
|
const identifierSegments = toKebabCase(variationSlug)
|
|
48
21
|
.split("-")
|
|
@@ -105,124 +78,6 @@ export function registerWorkspaceVariations() {
|
|
|
105
78
|
}
|
|
106
79
|
`;
|
|
107
80
|
}
|
|
108
|
-
function buildPatternSource(patternSlug, namespace, textDomain) {
|
|
109
|
-
const patternTitle = toTitleCase(patternSlug);
|
|
110
|
-
return `<?php
|
|
111
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
112
|
-
\treturn;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
register_block_pattern(
|
|
116
|
-
\t'${namespace}/${patternSlug}',
|
|
117
|
-
\tarray(
|
|
118
|
-
\t\t'title' => __( ${JSON.stringify(patternTitle)}, '${textDomain}' ),
|
|
119
|
-
\t\t'description' => __( ${JSON.stringify(`A starter pattern for ${patternTitle}.`)}, '${textDomain}' ),
|
|
120
|
-
\t\t'categories' => array( '${namespace}' ),
|
|
121
|
-
\t\t'content' => '<!-- wp:paragraph --><p>' . esc_html__( 'Describe this pattern here.', '${textDomain}' ) . '</p><!-- /wp:paragraph -->',
|
|
122
|
-
\t)
|
|
123
|
-
);
|
|
124
|
-
`;
|
|
125
|
-
}
|
|
126
|
-
function buildBindingSourceServerSource(bindingSourceSlug, phpPrefix, namespace, textDomain) {
|
|
127
|
-
const bindingSourceTitle = toTitleCase(bindingSourceSlug);
|
|
128
|
-
const bindingSourcePhpId = bindingSourceSlug.replace(/-/g, "_");
|
|
129
|
-
const bindingSourceValueFunctionName = `${phpPrefix}_${bindingSourcePhpId}_binding_source_values`;
|
|
130
|
-
const bindingSourceResolveFunctionName = `${phpPrefix}_${bindingSourcePhpId}_resolve_binding_source_value`;
|
|
131
|
-
const starterValue = `${bindingSourceTitle} starter value`;
|
|
132
|
-
return `<?php
|
|
133
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
134
|
-
\treturn;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if ( ! function_exists( 'register_block_bindings_source' ) ) {
|
|
138
|
-
\treturn;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if ( ! function_exists( '${bindingSourceValueFunctionName}' ) ) {
|
|
142
|
-
\tfunction ${bindingSourceValueFunctionName}() : array {
|
|
143
|
-
\t\treturn array(
|
|
144
|
-
\t\t\t${quotePhpString(bindingSourceSlug)} => ${quotePhpString(starterValue)},
|
|
145
|
-
\t\t);
|
|
146
|
-
\t}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if ( ! function_exists( '${bindingSourceResolveFunctionName}' ) ) {
|
|
150
|
-
\tfunction ${bindingSourceResolveFunctionName}( array $source_args ) : string {
|
|
151
|
-
\t\t$field = isset( $source_args['field'] ) && is_string( $source_args['field'] )
|
|
152
|
-
\t\t\t? $source_args['field']
|
|
153
|
-
\t\t\t: '${bindingSourceSlug}';
|
|
154
|
-
\t\t$binding_source_values = ${bindingSourceValueFunctionName}();
|
|
155
|
-
\t\t$value = $binding_source_values[ $field ] ?? '';
|
|
156
|
-
|
|
157
|
-
\t\treturn is_string( $value ) ? $value : '';
|
|
158
|
-
\t}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
register_block_bindings_source(
|
|
162
|
-
\t${quotePhpString(`${namespace}/${bindingSourceSlug}`)},
|
|
163
|
-
\tarray(
|
|
164
|
-
\t\t'label' => __( ${quotePhpString(bindingSourceTitle)}, ${quotePhpString(textDomain)} ),
|
|
165
|
-
\t\t'get_value_callback' => ${quotePhpString(bindingSourceResolveFunctionName)},
|
|
166
|
-
\t)
|
|
167
|
-
);
|
|
168
|
-
`;
|
|
169
|
-
}
|
|
170
|
-
function buildBindingSourceEditorSource(bindingSourceSlug, namespace, textDomain) {
|
|
171
|
-
const bindingSourceTitle = toTitleCase(bindingSourceSlug);
|
|
172
|
-
const starterValue = `${bindingSourceTitle} starter value`;
|
|
173
|
-
return `import { registerBlockBindingsSource } from '@wordpress/blocks';
|
|
174
|
-
import { __ } from '@wordpress/i18n';
|
|
175
|
-
|
|
176
|
-
interface BindingSourceRegistration {
|
|
177
|
-
\targs?: {
|
|
178
|
-
\t\tfield?: string;
|
|
179
|
-
\t};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const BINDING_SOURCE_VALUES: Record<string, string> = {
|
|
183
|
-
\t${quoteTsString(bindingSourceSlug)}: ${quoteTsString(starterValue)},
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
function resolveBindingSourceValue( field: string ): string {
|
|
187
|
-
\treturn BINDING_SOURCE_VALUES[ field ] ?? '';
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
registerBlockBindingsSource( {
|
|
191
|
-
\tname: ${quoteTsString(`${namespace}/${bindingSourceSlug}`)},
|
|
192
|
-
\tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
|
|
193
|
-
\tgetFieldsList() {
|
|
194
|
-
\t\treturn [
|
|
195
|
-
\t\t\t{
|
|
196
|
-
\t\t\t\tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
|
|
197
|
-
\t\t\t\ttype: 'string',
|
|
198
|
-
\t\t\t\targs: {
|
|
199
|
-
\t\t\t\t\tfield: ${quoteTsString(bindingSourceSlug)},
|
|
200
|
-
\t\t\t\t},
|
|
201
|
-
\t\t\t},
|
|
202
|
-
\t\t];
|
|
203
|
-
\t},
|
|
204
|
-
\tgetValues( { bindings } ) {
|
|
205
|
-
\t\tconst values: Record<string, string> = {};
|
|
206
|
-
\t\tfor ( const [ attributeName, binding ] of Object.entries(
|
|
207
|
-
\t\t\tbindings as Record<string, BindingSourceRegistration>
|
|
208
|
-
\t\t) ) {
|
|
209
|
-
\t\t\tconst field =
|
|
210
|
-
\t\t\t\ttypeof binding?.args?.field === 'string'
|
|
211
|
-
\t\t\t\t\t? binding.args.field
|
|
212
|
-
\t\t\t\t\t: ${quoteTsString(bindingSourceSlug)};
|
|
213
|
-
\t\t\tvalues[ attributeName ] = resolveBindingSourceValue( field );
|
|
214
|
-
\t\t}
|
|
215
|
-
\t\treturn values;
|
|
216
|
-
\t},
|
|
217
|
-
} );
|
|
218
|
-
`;
|
|
219
|
-
}
|
|
220
|
-
function buildBindingSourceIndexSource(bindingSourceSlugs) {
|
|
221
|
-
const importLines = bindingSourceSlugs
|
|
222
|
-
.map((bindingSourceSlug) => `import './${bindingSourceSlug}/editor';`)
|
|
223
|
-
.join("\n");
|
|
224
|
-
return `${importLines}${importLines ? "\n\n" : ""}// wp-typia add binding-source entries\n`;
|
|
225
|
-
}
|
|
226
81
|
async function ensureVariationRegistrationHook(blockIndexPath) {
|
|
227
82
|
await patchFile(blockIndexPath, (source) => {
|
|
228
83
|
let nextSource = source;
|
|
@@ -264,159 +119,7 @@ async function writeVariationRegistry(projectDir, blockSlug, variationSlug) {
|
|
|
264
119
|
const nextVariationSlugs = Array.from(new Set([...existingVariationSlugs, variationSlug])).sort();
|
|
265
120
|
await fsp.writeFile(variationsIndexPath, buildVariationIndexSource(nextVariationSlugs), "utf8");
|
|
266
121
|
}
|
|
267
|
-
|
|
268
|
-
const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
|
|
269
|
-
const bootstrapPath = getWorkspaceBootstrapPath(workspace);
|
|
270
|
-
await patchFile(bootstrapPath, (source) => {
|
|
271
|
-
let nextSource = source;
|
|
272
|
-
const patternCategoryFunctionName = `${workspace.workspace.phpPrefix}_register_pattern_category`;
|
|
273
|
-
const patternRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_patterns`;
|
|
274
|
-
const patternCategoryHook = `add_action( 'init', '${patternCategoryFunctionName}' );`;
|
|
275
|
-
const patternRegistrationHook = `add_action( 'init', '${patternRegistrationFunctionName}', 20 );`;
|
|
276
|
-
const patternFunctions = `
|
|
277
|
-
|
|
278
|
-
function ${patternCategoryFunctionName}() {
|
|
279
|
-
\tif ( function_exists( 'register_block_pattern_category' ) ) {
|
|
280
|
-
\t\tregister_block_pattern_category(
|
|
281
|
-
\t\t\t'${workspace.workspace.namespace}',
|
|
282
|
-
\t\t\tarray(
|
|
283
|
-
\t\t\t\t'label' => __( ${JSON.stringify(`${toTitleCase(workspaceBaseName)} Patterns`)}, '${workspace.workspace.textDomain}' ),
|
|
284
|
-
\t\t\t)
|
|
285
|
-
\t\t);
|
|
286
|
-
\t}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function ${patternRegistrationFunctionName}() {
|
|
290
|
-
\tforeach ( glob( __DIR__ . '/src/patterns/*.php' ) ?: array() as $pattern_module ) {
|
|
291
|
-
\t\trequire $pattern_module;
|
|
292
|
-
\t}
|
|
293
|
-
}
|
|
294
|
-
`;
|
|
295
|
-
if (!nextSource.includes(PATTERN_BOOTSTRAP_CATEGORY)) {
|
|
296
|
-
const insertionAnchors = [
|
|
297
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
298
|
-
/\?>\s*$/u,
|
|
299
|
-
];
|
|
300
|
-
let inserted = false;
|
|
301
|
-
for (const anchor of insertionAnchors) {
|
|
302
|
-
const candidate = nextSource.replace(anchor, (match) => `${patternFunctions}\n${match}`);
|
|
303
|
-
if (candidate !== nextSource) {
|
|
304
|
-
nextSource = candidate;
|
|
305
|
-
inserted = true;
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
if (!inserted) {
|
|
310
|
-
nextSource = `${nextSource.trimEnd()}\n${patternFunctions}\n`;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
if (!nextSource.includes(patternCategoryFunctionName) ||
|
|
314
|
-
!nextSource.includes(patternRegistrationFunctionName)) {
|
|
315
|
-
throw new Error(`Unable to inject pattern bootstrap functions into ${path.basename(bootstrapPath)}.`);
|
|
316
|
-
}
|
|
317
|
-
if (!nextSource.includes(patternCategoryHook)) {
|
|
318
|
-
nextSource = `${nextSource.trimEnd()}\n${patternCategoryHook}\n`;
|
|
319
|
-
}
|
|
320
|
-
if (!nextSource.includes(patternRegistrationHook)) {
|
|
321
|
-
nextSource = `${nextSource.trimEnd()}\n${patternRegistrationHook}\n`;
|
|
322
|
-
}
|
|
323
|
-
return nextSource;
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
async function ensureBindingSourceBootstrapAnchors(workspace) {
|
|
327
|
-
const bootstrapPath = getWorkspaceBootstrapPath(workspace);
|
|
328
|
-
await patchFile(bootstrapPath, (source) => {
|
|
329
|
-
let nextSource = source;
|
|
330
|
-
const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
|
|
331
|
-
const bindingRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_binding_sources`;
|
|
332
|
-
const bindingEditorEnqueueFunctionName = `${workspace.workspace.phpPrefix}_enqueue_binding_sources_editor`;
|
|
333
|
-
const bindingRegistrationHook = `add_action( 'init', '${bindingRegistrationFunctionName}', 20 );`;
|
|
334
|
-
const bindingEditorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${bindingEditorEnqueueFunctionName}' );`;
|
|
335
|
-
const bindingRegistrationFunction = `
|
|
336
|
-
|
|
337
|
-
function ${bindingRegistrationFunctionName}() {
|
|
338
|
-
\tforeach ( glob( __DIR__ . '${BINDING_SOURCE_SERVER_GLOB}' ) ?: array() as $binding_source_module ) {
|
|
339
|
-
\t\trequire_once $binding_source_module;
|
|
340
|
-
\t}
|
|
341
|
-
}
|
|
342
|
-
`;
|
|
343
|
-
const bindingEditorEnqueueFunction = `
|
|
344
|
-
|
|
345
|
-
function ${bindingEditorEnqueueFunctionName}() {
|
|
346
|
-
\t$script_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_SCRIPT}';
|
|
347
|
-
\t$asset_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_ASSET}';
|
|
348
|
-
|
|
349
|
-
\tif ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
|
|
350
|
-
\t\treturn;
|
|
351
|
-
\t}
|
|
352
|
-
|
|
353
|
-
\t$asset = require $asset_path;
|
|
354
|
-
\tif ( ! is_array( $asset ) ) {
|
|
355
|
-
\t\t$asset = array();
|
|
356
|
-
\t}
|
|
357
|
-
|
|
358
|
-
\twp_enqueue_script(
|
|
359
|
-
\t\t'${workspaceBaseName}-binding-sources',
|
|
360
|
-
\t\tplugins_url( '${BINDING_SOURCE_EDITOR_SCRIPT}', __FILE__ ),
|
|
361
|
-
\t\tisset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
|
|
362
|
-
\t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
|
|
363
|
-
\t\ttrue
|
|
364
|
-
\t);
|
|
365
|
-
}
|
|
366
|
-
`;
|
|
367
|
-
const insertionAnchors = [
|
|
368
|
-
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
369
|
-
/\?>\s*$/u,
|
|
370
|
-
];
|
|
371
|
-
const hasPhpFunctionDefinition = (functionName) => new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(nextSource);
|
|
372
|
-
const insertPhpSnippet = (snippet) => {
|
|
373
|
-
for (const anchor of insertionAnchors) {
|
|
374
|
-
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
375
|
-
if (candidate !== nextSource) {
|
|
376
|
-
nextSource = candidate;
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
381
|
-
};
|
|
382
|
-
const appendPhpSnippet = (snippet) => {
|
|
383
|
-
const closingTagPattern = /\?>\s*$/u;
|
|
384
|
-
if (closingTagPattern.test(nextSource)) {
|
|
385
|
-
nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
389
|
-
};
|
|
390
|
-
if (!hasPhpFunctionDefinition(bindingRegistrationFunctionName)) {
|
|
391
|
-
insertPhpSnippet(bindingRegistrationFunction);
|
|
392
|
-
}
|
|
393
|
-
if (!hasPhpFunctionDefinition(bindingEditorEnqueueFunctionName)) {
|
|
394
|
-
insertPhpSnippet(bindingEditorEnqueueFunction);
|
|
395
|
-
}
|
|
396
|
-
if (!nextSource.includes(bindingRegistrationHook)) {
|
|
397
|
-
appendPhpSnippet(bindingRegistrationHook);
|
|
398
|
-
}
|
|
399
|
-
if (!nextSource.includes(bindingEditorEnqueueHook)) {
|
|
400
|
-
appendPhpSnippet(bindingEditorEnqueueHook);
|
|
401
|
-
}
|
|
402
|
-
return nextSource;
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
function resolveBindingSourceRegistryPath(projectDir) {
|
|
406
|
-
const bindingsDir = path.join(projectDir, "src", "bindings");
|
|
407
|
-
return [path.join(bindingsDir, "index.ts"), path.join(bindingsDir, "index.js")].find((candidatePath) => fs.existsSync(candidatePath)) ?? path.join(bindingsDir, "index.ts");
|
|
408
|
-
}
|
|
409
|
-
async function writeBindingSourceRegistry(projectDir, bindingSourceSlug) {
|
|
410
|
-
const bindingsDir = path.join(projectDir, "src", "bindings");
|
|
411
|
-
const bindingsIndexPath = resolveBindingSourceRegistryPath(projectDir);
|
|
412
|
-
await fsp.mkdir(bindingsDir, { recursive: true });
|
|
413
|
-
const existingBindingSourceSlugs = fs
|
|
414
|
-
.readdirSync(bindingsDir, { withFileTypes: true })
|
|
415
|
-
.filter((entry) => entry.isDirectory())
|
|
416
|
-
.map((entry) => entry.name);
|
|
417
|
-
const nextBindingSourceSlugs = Array.from(new Set([...existingBindingSourceSlugs, bindingSourceSlug])).sort();
|
|
418
|
-
await fsp.writeFile(bindingsIndexPath, buildBindingSourceIndexSource(nextBindingSourceSlugs), "utf8");
|
|
419
|
-
}
|
|
122
|
+
export { runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
|
|
420
123
|
/**
|
|
421
124
|
* Add one variation entry to an existing workspace block.
|
|
422
125
|
*
|
|
@@ -473,100 +176,6 @@ export async function runAddVariationCommand({ blockName, cwd = process.cwd(), v
|
|
|
473
176
|
throw error;
|
|
474
177
|
}
|
|
475
178
|
}
|
|
476
|
-
/**
|
|
477
|
-
* Add one PHP block pattern shell to an official workspace project.
|
|
478
|
-
*
|
|
479
|
-
* @param options Command options for the pattern scaffold workflow.
|
|
480
|
-
* @param options.cwd Working directory used to resolve the nearest official workspace.
|
|
481
|
-
* Defaults to `process.cwd()`.
|
|
482
|
-
* @param options.patternName Human-entered pattern name that will be normalized
|
|
483
|
-
* and validated before files are written.
|
|
484
|
-
* @returns A promise that resolves with the normalized `patternSlug` and
|
|
485
|
-
* owning `projectDir` after the pattern file and inventory entry have been
|
|
486
|
-
* written successfully.
|
|
487
|
-
* @throws {Error} When the command is run outside an official workspace, when
|
|
488
|
-
* the pattern slug is invalid, or when a conflicting file or inventory entry
|
|
489
|
-
* already exists.
|
|
490
|
-
*/
|
|
491
|
-
export async function runAddPatternCommand({ cwd = process.cwd(), patternName, }) {
|
|
492
|
-
const workspace = resolveWorkspaceProject(cwd);
|
|
493
|
-
const patternSlug = assertValidGeneratedSlug("Pattern name", normalizeBlockSlug(patternName), "wp-typia add pattern <name>");
|
|
494
|
-
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
495
|
-
assertPatternDoesNotExist(workspace.projectDir, patternSlug, inventory);
|
|
496
|
-
const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
|
|
497
|
-
const bootstrapPath = getWorkspaceBootstrapPath(workspace);
|
|
498
|
-
const patternFilePath = path.join(workspace.projectDir, "src", "patterns", `${patternSlug}.php`);
|
|
499
|
-
const mutationSnapshot = {
|
|
500
|
-
fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath]),
|
|
501
|
-
snapshotDirs: [],
|
|
502
|
-
targetPaths: [patternFilePath],
|
|
503
|
-
};
|
|
504
|
-
try {
|
|
505
|
-
await fsp.mkdir(path.dirname(patternFilePath), { recursive: true });
|
|
506
|
-
await ensurePatternBootstrapAnchors(workspace);
|
|
507
|
-
await fsp.writeFile(patternFilePath, buildPatternSource(patternSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
|
|
508
|
-
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
509
|
-
patternEntries: [buildPatternConfigEntry(patternSlug)],
|
|
510
|
-
});
|
|
511
|
-
return {
|
|
512
|
-
patternSlug,
|
|
513
|
-
projectDir: workspace.projectDir,
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
catch (error) {
|
|
517
|
-
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
518
|
-
throw error;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* Add one block binding source scaffold to an official workspace project.
|
|
523
|
-
*
|
|
524
|
-
* @param options Command options for the binding-source scaffold workflow.
|
|
525
|
-
* @param options.bindingSourceName Human-entered binding source name that will
|
|
526
|
-
* be normalized and validated before files are written.
|
|
527
|
-
* @param options.cwd Working directory used to resolve the nearest official
|
|
528
|
-
* workspace. Defaults to `process.cwd()`.
|
|
529
|
-
* @returns A promise that resolves with the normalized `bindingSourceSlug` and
|
|
530
|
-
* owning `projectDir` after the server/editor files and inventory entry have
|
|
531
|
-
* been written successfully.
|
|
532
|
-
* @throws {Error} When the command is run outside an official workspace, when
|
|
533
|
-
* the slug is invalid, or when a conflicting file or inventory entry exists.
|
|
534
|
-
*/
|
|
535
|
-
export async function runAddBindingSourceCommand({ bindingSourceName, cwd = process.cwd(), }) {
|
|
536
|
-
const workspace = resolveWorkspaceProject(cwd);
|
|
537
|
-
const bindingSourceSlug = assertValidGeneratedSlug("Binding source name", normalizeBlockSlug(bindingSourceName), "wp-typia add binding-source <name>");
|
|
538
|
-
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
539
|
-
assertBindingSourceDoesNotExist(workspace.projectDir, bindingSourceSlug, inventory);
|
|
540
|
-
const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
|
|
541
|
-
const bootstrapPath = getWorkspaceBootstrapPath(workspace);
|
|
542
|
-
const bindingsIndexPath = resolveBindingSourceRegistryPath(workspace.projectDir);
|
|
543
|
-
const bindingSourceDir = path.join(workspace.projectDir, "src", "bindings", bindingSourceSlug);
|
|
544
|
-
const serverFilePath = path.join(bindingSourceDir, "server.php");
|
|
545
|
-
const editorFilePath = path.join(bindingSourceDir, "editor.ts");
|
|
546
|
-
const mutationSnapshot = {
|
|
547
|
-
fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath, bindingsIndexPath]),
|
|
548
|
-
snapshotDirs: [],
|
|
549
|
-
targetPaths: [bindingSourceDir],
|
|
550
|
-
};
|
|
551
|
-
try {
|
|
552
|
-
await fsp.mkdir(bindingSourceDir, { recursive: true });
|
|
553
|
-
await ensureBindingSourceBootstrapAnchors(workspace);
|
|
554
|
-
await fsp.writeFile(serverFilePath, buildBindingSourceServerSource(bindingSourceSlug, workspace.workspace.phpPrefix, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
|
|
555
|
-
await fsp.writeFile(editorFilePath, buildBindingSourceEditorSource(bindingSourceSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
|
|
556
|
-
await writeBindingSourceRegistry(workspace.projectDir, bindingSourceSlug);
|
|
557
|
-
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
558
|
-
bindingSourceEntries: [buildBindingSourceConfigEntry(bindingSourceSlug)],
|
|
559
|
-
});
|
|
560
|
-
return {
|
|
561
|
-
bindingSourceSlug,
|
|
562
|
-
projectDir: workspace.projectDir,
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
catch (error) {
|
|
566
|
-
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
567
|
-
throw error;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
179
|
/**
|
|
571
180
|
* Add one `blockHooks` entry to an existing official workspace block.
|
|
572
181
|
*
|
|
@@ -4,12 +4,84 @@ const DEFAULT_CLI_FAILURE_SUMMARIES = {
|
|
|
4
4
|
doctor: "One or more doctor checks failed.",
|
|
5
5
|
migrate: "Unable to complete the requested migration command.",
|
|
6
6
|
};
|
|
7
|
+
const MIN_CLI_WRAP_COLUMNS = 32;
|
|
8
|
+
function parseCliColumns(value) {
|
|
9
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const parsed = Number.parseInt(value, 10);
|
|
13
|
+
return Number.isFinite(parsed) && parsed >= MIN_CLI_WRAP_COLUMNS ? parsed : null;
|
|
14
|
+
}
|
|
15
|
+
function resolveCliWrapColumns(streamColumns) {
|
|
16
|
+
return parseCliColumns(process.env.COLUMNS) ??
|
|
17
|
+
(typeof streamColumns === "number" && streamColumns >= MIN_CLI_WRAP_COLUMNS
|
|
18
|
+
? streamColumns
|
|
19
|
+
: null);
|
|
20
|
+
}
|
|
21
|
+
function wrapCliText(text, maxWidth) {
|
|
22
|
+
const words = text.trim().split(/\s+/u).filter((word) => word.length > 0);
|
|
23
|
+
if (words.length === 0) {
|
|
24
|
+
return [""];
|
|
25
|
+
}
|
|
26
|
+
const lines = [];
|
|
27
|
+
let currentLine = words[0] ?? "";
|
|
28
|
+
for (const word of words.slice(1)) {
|
|
29
|
+
const nextLine = `${currentLine} ${word}`;
|
|
30
|
+
if (nextLine.length <= maxWidth) {
|
|
31
|
+
currentLine = nextLine;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
lines.push(currentLine);
|
|
35
|
+
currentLine = word;
|
|
36
|
+
}
|
|
37
|
+
lines.push(currentLine);
|
|
38
|
+
return lines;
|
|
39
|
+
}
|
|
40
|
+
function formatWrappedPrefixedLine(prefix, text, columns, continuationIndent = " ") {
|
|
41
|
+
const singleLine = `${prefix}${text}`;
|
|
42
|
+
if (columns === null || singleLine.length <= columns) {
|
|
43
|
+
return [singleLine];
|
|
44
|
+
}
|
|
45
|
+
const words = text.trim().split(/\s+/u).filter((word) => word.length > 0);
|
|
46
|
+
if (words.length === 0) {
|
|
47
|
+
return [prefix.trimEnd()];
|
|
48
|
+
}
|
|
49
|
+
const continuationWidth = Math.max(1, columns - continuationIndent.length);
|
|
50
|
+
const firstLineWidth = columns - prefix.length;
|
|
51
|
+
if (firstLineWidth <= 0 || (words[0]?.length ?? 0) > firstLineWidth) {
|
|
52
|
+
return [
|
|
53
|
+
prefix.trimEnd(),
|
|
54
|
+
...wrapCliText(text, continuationWidth).map((line) => `${continuationIndent}${line}`),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
const lines = [];
|
|
58
|
+
let currentPrefix = prefix;
|
|
59
|
+
let currentWidth = Math.max(1, columns - currentPrefix.length);
|
|
60
|
+
let currentLine = words[0] ?? "";
|
|
61
|
+
for (const word of words.slice(1)) {
|
|
62
|
+
const nextLine = `${currentLine} ${word}`;
|
|
63
|
+
if (nextLine.length <= currentWidth) {
|
|
64
|
+
currentLine = nextLine;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
lines.push(`${currentPrefix}${currentLine}`);
|
|
68
|
+
currentPrefix = continuationIndent;
|
|
69
|
+
currentWidth = continuationWidth;
|
|
70
|
+
currentLine = word;
|
|
71
|
+
}
|
|
72
|
+
lines.push(`${currentPrefix}${currentLine}`);
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
7
75
|
function formatCliDiagnosticBlock(message) {
|
|
8
|
-
const
|
|
76
|
+
const columns = resolveCliWrapColumns(process.stderr.columns);
|
|
77
|
+
const lines = [
|
|
78
|
+
`wp-typia ${message.command} failed`,
|
|
79
|
+
...formatWrappedPrefixedLine("Summary: ", message.summary, columns),
|
|
80
|
+
];
|
|
9
81
|
if (message.detailLines.length > 0) {
|
|
10
82
|
lines.push("Details:");
|
|
11
83
|
for (const detailLine of message.detailLines) {
|
|
12
|
-
lines.push(
|
|
84
|
+
lines.push(...formatWrappedPrefixedLine("- ", detailLine, columns));
|
|
13
85
|
}
|
|
14
86
|
}
|
|
15
87
|
return lines.join("\n");
|
|
@@ -77,7 +149,7 @@ export function formatCliDiagnosticError(error) {
|
|
|
77
149
|
* Format one human-readable doctor check row.
|
|
78
150
|
*/
|
|
79
151
|
export function formatDoctorCheckLine(check) {
|
|
80
|
-
return `${check.status === "pass" ? "PASS" : "FAIL"} ${check.label}:
|
|
152
|
+
return formatWrappedPrefixedLine(`${check.status === "pass" ? "PASS" : "FAIL"} ${check.label}: `, check.detail, resolveCliWrapColumns(process.stdout.columns)).join("\n");
|
|
81
153
|
}
|
|
82
154
|
/**
|
|
83
155
|
* Return the failing doctor checks from one doctor run.
|
|
@@ -90,7 +162,7 @@ export function getFailingDoctorChecks(checks) {
|
|
|
90
162
|
*/
|
|
91
163
|
export function formatDoctorSummaryLine(checks) {
|
|
92
164
|
const failedChecks = getFailingDoctorChecks(checks);
|
|
93
|
-
return `${failedChecks.length === 0 ? "PASS" : "FAIL"} wp-typia doctor summary:
|
|
165
|
+
return formatWrappedPrefixedLine(`${failedChecks.length === 0 ? "PASS" : "FAIL"} wp-typia doctor summary: `, `${checks.length - failedChecks.length}/${checks.length} checks passed`, resolveCliWrapColumns(process.stdout.columns)).join("\n");
|
|
94
166
|
}
|
|
95
167
|
/**
|
|
96
168
|
* Build detail lines for doctor failures so the non-interactive formatter can
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DoctorCheck } from "./cli-doctor.js";
|
|
2
|
+
/**
|
|
3
|
+
* Collect environment-scoped doctor checks for the current working directory.
|
|
4
|
+
*
|
|
5
|
+
* The returned rows cover Bun/Node/git availability, writability of the
|
|
6
|
+
* current working directory and OS temp directory, and built-in or external
|
|
7
|
+
* template asset integrity in display order.
|
|
8
|
+
*
|
|
9
|
+
* @param cwd Working directory validated for writability.
|
|
10
|
+
* @returns Ordered environment check rows ready for CLI rendering.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getEnvironmentDoctorChecks(cwd: string): Promise<DoctorCheck[]>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { access, constants as fsConstants, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { getBuiltInTemplateLayerDirs, isOmittableBuiltInTemplateLayerDir, } from "./template-builtins.js";
|
|
7
|
+
import { isBuiltInTemplateId, listTemplates } from "./template-registry.js";
|
|
8
|
+
function readCommandVersion(command, args = ["--version"]) {
|
|
9
|
+
try {
|
|
10
|
+
return execFileSync(command, args, {
|
|
11
|
+
encoding: "utf8",
|
|
12
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
13
|
+
}).trim();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function compareMajorVersion(actualVersion, minimumMajor) {
|
|
20
|
+
const parsed = Number.parseInt(actualVersion.replace(/^v/, "").split(".")[0] ?? "", 10);
|
|
21
|
+
return Number.isFinite(parsed) && parsed >= minimumMajor;
|
|
22
|
+
}
|
|
23
|
+
async function checkWritableDirectory(directory) {
|
|
24
|
+
try {
|
|
25
|
+
await access(directory, fsConstants.W_OK);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function checkTempDirectory() {
|
|
33
|
+
const tempFile = path.join(os.tmpdir(), `wp-typia-${Date.now()}.tmp`);
|
|
34
|
+
try {
|
|
35
|
+
await writeFile(tempFile, "ok", "utf8");
|
|
36
|
+
await rm(tempFile, { force: true });
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function createDoctorCheck(label, status, detail) {
|
|
44
|
+
return { detail, label, status };
|
|
45
|
+
}
|
|
46
|
+
function getTemplateDoctorChecks() {
|
|
47
|
+
const checks = [];
|
|
48
|
+
for (const template of listTemplates()) {
|
|
49
|
+
if (!isBuiltInTemplateId(template.id)) {
|
|
50
|
+
const templateDirExists = fs.existsSync(template.templateDir);
|
|
51
|
+
const hasAssets = templateDirExists &&
|
|
52
|
+
fs.existsSync(path.join(template.templateDir, "package.json.mustache"));
|
|
53
|
+
checks.push({
|
|
54
|
+
status: !templateDirExists || hasAssets ? "pass" : "fail",
|
|
55
|
+
label: `Template ${template.id}`,
|
|
56
|
+
detail: !templateDirExists
|
|
57
|
+
? "External template metadata only; local overlay package is not installed."
|
|
58
|
+
: hasAssets
|
|
59
|
+
? template.templateDir
|
|
60
|
+
: "Missing core template assets",
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const builtInTemplateId = template.id;
|
|
65
|
+
const layerDirs = builtInTemplateId === "persistence"
|
|
66
|
+
? Array.from(new Set([
|
|
67
|
+
...getBuiltInTemplateLayerDirs(builtInTemplateId, { persistencePolicy: "authenticated" }),
|
|
68
|
+
...getBuiltInTemplateLayerDirs(builtInTemplateId, { persistencePolicy: "public" }),
|
|
69
|
+
]))
|
|
70
|
+
: builtInTemplateId === "compound"
|
|
71
|
+
? Array.from(new Set([
|
|
72
|
+
...getBuiltInTemplateLayerDirs(builtInTemplateId),
|
|
73
|
+
...getBuiltInTemplateLayerDirs(builtInTemplateId, {
|
|
74
|
+
persistenceEnabled: true,
|
|
75
|
+
persistencePolicy: "authenticated",
|
|
76
|
+
}),
|
|
77
|
+
...getBuiltInTemplateLayerDirs(builtInTemplateId, {
|
|
78
|
+
persistenceEnabled: true,
|
|
79
|
+
persistencePolicy: "public",
|
|
80
|
+
}),
|
|
81
|
+
]))
|
|
82
|
+
: getBuiltInTemplateLayerDirs(builtInTemplateId);
|
|
83
|
+
const missingRequiredLayer = layerDirs.some((layerDir) => !fs.existsSync(layerDir) &&
|
|
84
|
+
!isOmittableBuiltInTemplateLayerDir(builtInTemplateId, layerDir));
|
|
85
|
+
const existingLayerDirs = layerDirs.filter((layerDir) => fs.existsSync(layerDir));
|
|
86
|
+
const hasAssets = !missingRequiredLayer &&
|
|
87
|
+
existingLayerDirs.some((layerDir) => fs.existsSync(path.join(layerDir, "package.json.mustache"))) &&
|
|
88
|
+
existingLayerDirs.some((layerDir) => fs.existsSync(path.join(layerDir, "src")));
|
|
89
|
+
checks.push({
|
|
90
|
+
status: hasAssets ? "pass" : "fail",
|
|
91
|
+
label: `Template ${template.id}`,
|
|
92
|
+
detail: hasAssets
|
|
93
|
+
? existingLayerDirs.join(" + ")
|
|
94
|
+
: "Missing core template assets",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return checks;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Collect environment-scoped doctor checks for the current working directory.
|
|
101
|
+
*
|
|
102
|
+
* The returned rows cover Bun/Node/git availability, writability of the
|
|
103
|
+
* current working directory and OS temp directory, and built-in or external
|
|
104
|
+
* template asset integrity in display order.
|
|
105
|
+
*
|
|
106
|
+
* @param cwd Working directory validated for writability.
|
|
107
|
+
* @returns Ordered environment check rows ready for CLI rendering.
|
|
108
|
+
*/
|
|
109
|
+
export async function getEnvironmentDoctorChecks(cwd) {
|
|
110
|
+
const bunVersion = readCommandVersion("bun");
|
|
111
|
+
const nodeVersion = readCommandVersion("node");
|
|
112
|
+
const gitVersion = readCommandVersion("git");
|
|
113
|
+
const cwdWritable = await checkWritableDirectory(cwd);
|
|
114
|
+
const tempWritable = await checkTempDirectory();
|
|
115
|
+
return [
|
|
116
|
+
createDoctorCheck("Bun", bunVersion && compareMajorVersion(bunVersion, 1) ? "pass" : "fail", bunVersion ? `Detected ${bunVersion}` : "Not available"),
|
|
117
|
+
createDoctorCheck("Node", nodeVersion && compareMajorVersion(nodeVersion, 20) ? "pass" : "fail", nodeVersion ? `Detected ${nodeVersion}` : "Not available"),
|
|
118
|
+
createDoctorCheck("git", gitVersion ? "pass" : "fail", gitVersion ?? "Not available"),
|
|
119
|
+
createDoctorCheck("Current directory", cwdWritable ? "pass" : "fail", cwdWritable ? "Writable" : "Not writable"),
|
|
120
|
+
createDoctorCheck("Temp directory", tempWritable ? "pass" : "fail", tempWritable ? "Writable" : "Not writable"),
|
|
121
|
+
...getTemplateDoctorChecks(),
|
|
122
|
+
];
|
|
123
|
+
}
|