@wp-typia/project-tools 0.23.0 → 0.24.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/ai-feature-artifacts.js +4 -1
- package/dist/runtime/block-generator-service-spec.js +2 -1
- package/dist/runtime/built-in-block-non-ts-basic-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-basic-artifacts.js +84 -0
- package/dist/runtime/built-in-block-non-ts-compound-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-compound-artifacts.js +36 -0
- package/dist/runtime/built-in-block-non-ts-compound-templates.d.ts +23 -0
- package/dist/runtime/built-in-block-non-ts-compound-templates.js +453 -0
- package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +8 -26
- package/dist/runtime/built-in-block-non-ts-family-artifacts.js +8 -1034
- package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.js +83 -0
- package/dist/runtime/built-in-block-non-ts-persistence-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-persistence-artifacts.js +33 -0
- package/dist/runtime/built-in-block-non-ts-persistence-templates.d.ts +23 -0
- package/dist/runtime/built-in-block-non-ts-persistence-templates.js +395 -0
- package/dist/runtime/cli-add-block-json.js +5 -1
- package/dist/runtime/cli-add-collision.js +8 -0
- package/dist/runtime/cli-add-help.js +14 -10
- package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
- package/dist/runtime/cli-add-kind-ids.js +1 -0
- package/dist/runtime/cli-add-types.d.ts +45 -6
- package/dist/runtime/cli-add-types.js +2 -0
- package/dist/runtime/cli-add-validation.d.ts +7 -0
- package/dist/runtime/cli-add-validation.js +9 -0
- package/dist/runtime/cli-add-workspace-ability-anchors.d.ts +24 -0
- package/dist/runtime/cli-add-workspace-ability-anchors.js +294 -0
- package/dist/runtime/cli-add-workspace-ability-registry.d.ts +10 -0
- package/dist/runtime/cli-add-workspace-ability-registry.js +51 -0
- package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +1 -1
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +5 -308
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +6 -2
- package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +18 -27
- package/dist/runtime/cli-add-workspace-admin-view-templates.js +30 -1326
- package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +4 -4
- package/dist/runtime/cli-add-workspace-ai-anchors.js +8 -233
- package/dist/runtime/cli-add-workspace-ai-scaffold.js +4 -2
- package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +1 -4
- package/dist/runtime/cli-add-workspace-ai-source-emitters.js +1 -129
- package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.d.ts +5 -0
- package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.js +236 -0
- package/dist/runtime/cli-add-workspace-ai-sync-script-source.d.ts +4 -0
- package/dist/runtime/cli-add-workspace-ai-sync-script-source.js +145 -0
- package/dist/runtime/cli-add-workspace-assets.d.ts +6 -63
- package/dist/runtime/cli-add-workspace-assets.js +6 -950
- package/dist/runtime/cli-add-workspace-binding-source-anchors.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-binding-source-anchors.js +112 -0
- package/dist/runtime/cli-add-workspace-binding-source-source-emitters.d.ts +33 -0
- package/dist/runtime/cli-add-workspace-binding-source-source-emitters.js +436 -0
- package/dist/runtime/cli-add-workspace-binding-source-types.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-binding-source-types.js +1 -0
- package/dist/runtime/cli-add-workspace-binding-source.d.ts +40 -0
- package/dist/runtime/cli-add-workspace-binding-source.js +275 -0
- package/dist/runtime/cli-add-workspace-block-style.d.ts +22 -0
- package/dist/runtime/cli-add-workspace-block-style.js +148 -0
- package/dist/runtime/cli-add-workspace-block-transform.d.ts +32 -0
- package/dist/runtime/cli-add-workspace-block-transform.js +197 -0
- package/dist/runtime/cli-add-workspace-contract.js +1 -1
- package/dist/runtime/cli-add-workspace-core-variation.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-core-variation.js +322 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-anchors.d.ts +37 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-anchors.js +206 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.d.ts +47 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.js +219 -0
- package/dist/runtime/cli-add-workspace-editor-plugin.d.ts +22 -0
- package/dist/runtime/cli-add-workspace-editor-plugin.js +78 -0
- package/dist/runtime/cli-add-workspace-hooked-block.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-hooked-block.js +57 -0
- package/dist/runtime/cli-add-workspace-integration-env-files.d.ts +33 -0
- package/dist/runtime/cli-add-workspace-integration-env-files.js +65 -0
- package/dist/runtime/cli-add-workspace-integration-env-package-json.d.ts +38 -0
- package/dist/runtime/cli-add-workspace-integration-env-package-json.js +122 -0
- package/dist/runtime/cli-add-workspace-integration-env-source-emitters.d.ts +44 -0
- package/dist/runtime/cli-add-workspace-integration-env-source-emitters.js +262 -0
- package/dist/runtime/cli-add-workspace-integration-env.d.ts +3 -1
- package/dist/runtime/cli-add-workspace-integration-env.js +10 -313
- package/dist/runtime/cli-add-workspace-pattern-anchors.d.ts +10 -0
- package/dist/runtime/cli-add-workspace-pattern-anchors.js +95 -0
- package/dist/runtime/cli-add-workspace-pattern-options.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-pattern-options.js +113 -0
- package/dist/runtime/cli-add-workspace-pattern-source-emitters.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-pattern-source-emitters.js +57 -0
- package/dist/runtime/cli-add-workspace-pattern.d.ts +42 -0
- package/dist/runtime/cli-add-workspace-pattern.js +99 -0
- package/dist/runtime/cli-add-workspace-post-meta.js +1 -1
- package/dist/runtime/cli-add-workspace-registration-hooks.d.ts +50 -0
- package/dist/runtime/cli-add-workspace-registration-hooks.js +162 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -4
- package/dist/runtime/cli-add-workspace-rest-anchors.js +9 -428
- package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.d.ts +17 -0
- package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.js +108 -0
- package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.js +142 -0
- package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.d.ts +51 -0
- package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.js +415 -0
- package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-generated.js +160 -0
- package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.d.ts +80 -0
- package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.js +238 -0
- package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
- package/dist/runtime/cli-add-workspace-rest-manual.js +266 -0
- package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +18 -0
- package/dist/runtime/cli-add-workspace-rest-php-templates.js +359 -0
- package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.d.ts +33 -0
- package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.js +145 -0
- package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.js +162 -0
- package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.d.ts +7 -0
- package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.js +193 -0
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +5 -91
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +5 -642
- package/dist/runtime/cli-add-workspace-rest-source-utils.d.ts +17 -0
- package/dist/runtime/cli-add-workspace-rest-source-utils.js +50 -0
- package/dist/runtime/cli-add-workspace-rest-sync-script-shared.d.ts +56 -0
- package/dist/runtime/cli-add-workspace-rest-sync-script-shared.js +122 -0
- package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
- package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
- package/dist/runtime/cli-add-workspace-rest.d.ts +3 -20
- package/dist/runtime/cli-add-workspace-rest.js +33 -788
- package/dist/runtime/cli-add-workspace-variation.d.ts +22 -0
- package/dist/runtime/cli-add-workspace-variation.js +162 -0
- package/dist/runtime/cli-add-workspace.d.ts +42 -107
- package/dist/runtime/cli-add-workspace.js +42 -674
- package/dist/runtime/cli-add.d.ts +3 -3
- package/dist/runtime/cli-add.js +2 -2
- package/dist/runtime/cli-core.d.ts +3 -2
- package/dist/runtime/cli-core.js +2 -2
- package/dist/runtime/cli-diagnostics.d.ts +3 -1
- package/dist/runtime/cli-diagnostics.js +17 -5
- package/dist/runtime/cli-doctor-workspace-bindings.js +63 -1
- package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
- package/dist/runtime/cli-doctor-workspace-block-addons.js +162 -0
- package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
- package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
- package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
- package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
- package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
- package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
- package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
- package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
- package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
- package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
- package/dist/runtime/cli-doctor-workspace-features.js +14 -487
- package/dist/runtime/cli-doctor.d.ts +54 -3
- package/dist/runtime/cli-doctor.js +92 -10
- package/dist/runtime/cli-help.js +12 -7
- package/dist/runtime/cli-init-package-json.js +4 -2
- package/dist/runtime/cli-prompt.d.ts +16 -2
- package/dist/runtime/cli-prompt.js +29 -12
- package/dist/runtime/cli-scaffold.d.ts +2 -1
- package/dist/runtime/cli-scaffold.js +19 -10
- package/dist/runtime/external-template-guards.js +4 -6
- package/dist/runtime/index.d.ts +6 -3
- package/dist/runtime/index.js +4 -2
- package/dist/runtime/json-utils.d.ts +62 -4
- package/dist/runtime/json-utils.js +78 -4
- package/dist/runtime/local-dev-presets.js +6 -2
- package/dist/runtime/migration-ui-capability.js +4 -1
- package/dist/runtime/migration-utils.js +4 -1
- package/dist/runtime/package-managers.js +6 -1
- package/dist/runtime/package-versions.d.ts +1 -0
- package/dist/runtime/package-versions.js +16 -3
- package/dist/runtime/pattern-catalog.d.ts +122 -0
- package/dist/runtime/pattern-catalog.js +471 -0
- package/dist/runtime/post-meta-binding-fields.d.ts +46 -0
- package/dist/runtime/post-meta-binding-fields.js +135 -0
- package/dist/runtime/scaffold-bootstrap.js +7 -2
- package/dist/runtime/scaffold-package-manager-files.js +5 -1
- package/dist/runtime/scaffold-repository-reference.js +4 -2
- package/dist/runtime/scaffold-template-variables.js +2 -1
- package/dist/runtime/scaffold.d.ts +18 -1
- package/dist/runtime/scaffold.js +55 -2
- package/dist/runtime/temp-roots.js +4 -1
- package/dist/runtime/template-layers.js +4 -1
- package/dist/runtime/template-registry.js +9 -3
- package/dist/runtime/template-source-contracts.d.ts +2 -0
- package/dist/runtime/template-source-normalization.js +2 -1
- package/dist/runtime/template-source-remote.js +18 -5
- package/dist/runtime/template-source-seeds.js +10 -3
- package/dist/runtime/typia-llm-json-schema.d.ts +24 -0
- package/dist/runtime/typia-llm-json-schema.js +33 -0
- package/dist/runtime/typia-llm-openapi-constraints.d.ts +20 -0
- package/dist/runtime/typia-llm-openapi-constraints.js +254 -0
- package/dist/runtime/typia-llm-projection.d.ts +25 -0
- package/dist/runtime/typia-llm-projection.js +58 -0
- package/dist/runtime/typia-llm-render.d.ts +21 -0
- package/dist/runtime/typia-llm-render.js +252 -0
- package/dist/runtime/typia-llm-sync.d.ts +10 -0
- package/dist/runtime/typia-llm-sync.js +63 -0
- package/dist/runtime/typia-llm-types.d.ts +197 -0
- package/dist/runtime/typia-llm-types.js +1 -0
- package/dist/runtime/typia-llm.d.ts +9 -255
- package/dist/runtime/typia-llm.js +5 -634
- package/dist/runtime/workspace-inventory-mutations.js +15 -1
- package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
- package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
- package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
- package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
- package/dist/runtime/workspace-inventory-parser.d.ts +3 -45
- package/dist/runtime/workspace-inventory-parser.js +3 -581
- package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
- package/dist/runtime/workspace-inventory-section-descriptors.js +443 -0
- package/dist/runtime/workspace-inventory-templates.d.ts +3 -3
- package/dist/runtime/workspace-inventory-templates.js +10 -1
- package/dist/runtime/workspace-inventory-types.d.ts +10 -1
- package/dist/runtime/workspace-project.js +4 -6
- package/package.json +8 -3
- package/templates/_shared/compound/core/scripts/block-config.ts.mustache +22 -0
- package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +103 -2
- package/templates/_shared/compound/core/src/inner-blocks-templates.ts.mustache +13 -0
- package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +22 -1
|
@@ -1,324 +1,18 @@
|
|
|
1
|
-
import { promises as fsp } from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { assertValidGeneratedSlug, assertValidIntegrationEnvService, normalizeBlockSlug, } from "./cli-add-shared.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { appendMissingLines, writeFileIfAbsent, writeNewScaffoldFile, } from "./cli-add-workspace-integration-env-files.js";
|
|
4
|
+
import { addIntegrationEnvPackageJsonEntries, mutateIntegrationEnvPackageJson, } from "./cli-add-workspace-integration-env-package-json.js";
|
|
5
|
+
import { buildDockerComposeSource, buildEnvExampleSource, buildIntegrationEnvReadmeSource, buildIntegrationSmokeScriptSource, buildWpEnvConfigSource, } from "./cli-add-workspace-integration-env-source-emitters.js";
|
|
6
6
|
import { executeWorkspaceMutationPlan } from "./cli-add-workspace-mutation.js";
|
|
7
|
+
import { pathExists } from "./fs-async.js";
|
|
7
8
|
import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
8
|
-
import { toTitleCase } from "./string-case.js";
|
|
9
|
-
const WP_ENV_PACKAGE_VERSION = "^11.2.0";
|
|
10
|
-
function buildWpEnvConfigSource() {
|
|
11
|
-
return `${JSON.stringify({
|
|
12
|
-
$schema: "https://schemas.wp.org/trunk/wp-env.json",
|
|
13
|
-
core: null,
|
|
14
|
-
port: 8888,
|
|
15
|
-
testsEnvironment: false,
|
|
16
|
-
plugins: ["."],
|
|
17
|
-
config: {
|
|
18
|
-
WP_DEBUG: true,
|
|
19
|
-
WP_DEBUG_LOG: true,
|
|
20
|
-
WP_DEBUG_DISPLAY: false,
|
|
21
|
-
SCRIPT_DEBUG: true,
|
|
22
|
-
WP_ENVIRONMENT_TYPE: "local",
|
|
23
|
-
},
|
|
24
|
-
}, null, 2)}\n`;
|
|
25
|
-
}
|
|
26
|
-
function buildDockerComposeSource() {
|
|
27
|
-
return `services:
|
|
28
|
-
integration-service:
|
|
29
|
-
image: node:22-alpine
|
|
30
|
-
working_dir: /workspace
|
|
31
|
-
volumes:
|
|
32
|
-
- .:/workspace
|
|
33
|
-
command: >
|
|
34
|
-
node -e "require('node:http').createServer((request, response) => {
|
|
35
|
-
response.writeHead(request.url === '/health' ? 200 : 404, {
|
|
36
|
-
'content-type': 'application/json'
|
|
37
|
-
});
|
|
38
|
-
response.end(JSON.stringify({
|
|
39
|
-
ok: request.url === '/health',
|
|
40
|
-
service: 'wp-typia-integration-starter'
|
|
41
|
-
}));
|
|
42
|
-
}).listen(3000, '0.0.0.0')"
|
|
43
|
-
ports:
|
|
44
|
-
- "3000:3000"
|
|
45
|
-
`;
|
|
46
|
-
}
|
|
47
|
-
function buildIntegrationSmokeScriptSource(integrationEnvSlug) {
|
|
48
|
-
return `import fs from "node:fs";
|
|
49
|
-
import path from "node:path";
|
|
50
|
-
import { fileURLToPath } from "node:url";
|
|
51
|
-
|
|
52
|
-
const ROOT_DIR = path.resolve(
|
|
53
|
-
fileURLToPath(new URL("../..", import.meta.url)),
|
|
54
|
-
);
|
|
55
|
-
const ENV_FILE = path.join(ROOT_DIR, ".env");
|
|
56
|
-
|
|
57
|
-
function parseEnvValue(value) {
|
|
58
|
-
const trimmed = value.trim();
|
|
59
|
-
if (
|
|
60
|
-
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
61
|
-
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
62
|
-
) {
|
|
63
|
-
return trimmed.slice(1, -1);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return trimmed;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function readEnvFile(filePath) {
|
|
70
|
-
if (!fs.existsSync(filePath)) {
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return Object.fromEntries(
|
|
75
|
-
fs
|
|
76
|
-
.readFileSync(filePath, "utf8")
|
|
77
|
-
.split(/\\r?\\n/u)
|
|
78
|
-
.map((line) => line.trim())
|
|
79
|
-
.filter((line) => line.length > 0 && !line.startsWith("#"))
|
|
80
|
-
.map((line) => {
|
|
81
|
-
const separatorIndex = line.indexOf("=");
|
|
82
|
-
if (separatorIndex === -1) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return [
|
|
87
|
-
line.slice(0, separatorIndex).trim(),
|
|
88
|
-
parseEnvValue(line.slice(separatorIndex + 1)),
|
|
89
|
-
];
|
|
90
|
-
})
|
|
91
|
-
.filter(Boolean),
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const envFile = readEnvFile(ENV_FILE);
|
|
96
|
-
|
|
97
|
-
function getEnv(name, fallback) {
|
|
98
|
-
return process.env[name] ?? envFile[name] ?? fallback;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function resolveEndpointUrl(baseUrl, endpointPath) {
|
|
102
|
-
const normalizedBaseUrl = new URL(baseUrl);
|
|
103
|
-
if (!normalizedBaseUrl.pathname.endsWith("/")) {
|
|
104
|
-
normalizedBaseUrl.pathname = \`\${normalizedBaseUrl.pathname}/\`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const relativePath = endpointPath.replace(/^\\/+/u, "");
|
|
108
|
-
return new URL(relativePath, normalizedBaseUrl);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function assertJsonEndpoint(label, url) {
|
|
112
|
-
const response = await fetch(url, {
|
|
113
|
-
headers: {
|
|
114
|
-
accept: "application/json",
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
if (!response.ok) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
\`\${label} failed at \${url} with HTTP \${response.status}.\`,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
125
|
-
if (!contentType.includes("application/json")) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
\`\${label} at \${url} did not return JSON (content-type: \${contentType || "missing"}).\`,
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return response.json();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const baseUrl = new URL(
|
|
135
|
-
getEnv("WP_TYPIA_SMOKE_BASE_URL", "http://localhost:8888"),
|
|
136
|
-
);
|
|
137
|
-
const serviceUrl = getEnv("WP_TYPIA_SERVICE_URL", "").trim();
|
|
138
|
-
|
|
139
|
-
await assertJsonEndpoint(
|
|
140
|
-
"WordPress REST index",
|
|
141
|
-
resolveEndpointUrl(baseUrl, "wp-json/"),
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
if (serviceUrl.length > 0) {
|
|
145
|
-
await assertJsonEndpoint(
|
|
146
|
-
"Local integration service healthcheck",
|
|
147
|
-
resolveEndpointUrl(serviceUrl, "health"),
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
console.log("wp-typia integration smoke passed: ${integrationEnvSlug}");
|
|
152
|
-
`;
|
|
153
|
-
}
|
|
154
|
-
function buildEnvExampleSource(service) {
|
|
155
|
-
return [
|
|
156
|
-
"# wp-typia integration smoke settings",
|
|
157
|
-
"WP_TYPIA_SMOKE_BASE_URL=http://localhost:8888",
|
|
158
|
-
"WP_TYPIA_SMOKE_USERNAME=admin",
|
|
159
|
-
"WP_TYPIA_SMOKE_PASSWORD=password",
|
|
160
|
-
...(service === "docker-compose"
|
|
161
|
-
? [
|
|
162
|
-
"",
|
|
163
|
-
"# Optional docker-compose integration service starter.",
|
|
164
|
-
"WP_TYPIA_SERVICE_URL=http://localhost:3000",
|
|
165
|
-
]
|
|
166
|
-
: [
|
|
167
|
-
"",
|
|
168
|
-
"# Set this when your smoke test needs a project-specific service.",
|
|
169
|
-
"# WP_TYPIA_SERVICE_URL=http://localhost:3000",
|
|
170
|
-
]),
|
|
171
|
-
"",
|
|
172
|
-
].join("\n");
|
|
173
|
-
}
|
|
174
|
-
function buildIntegrationEnvReadmeSource({ integrationEnvSlug, service, withWpEnv, }) {
|
|
175
|
-
const title = toTitleCase(integrationEnvSlug);
|
|
176
|
-
const setupSteps = [
|
|
177
|
-
"Copy `.env.example` to `.env` and adjust the URLs or credentials for your local project.",
|
|
178
|
-
...(withWpEnv
|
|
179
|
-
? [
|
|
180
|
-
"Run `npm run wp-env:start` to start the generated WordPress environment.",
|
|
181
|
-
]
|
|
182
|
-
: [
|
|
183
|
-
"Point `WP_TYPIA_SMOKE_BASE_URL` at the WordPress environment you already run locally.",
|
|
184
|
-
]),
|
|
185
|
-
...(service === "docker-compose"
|
|
186
|
-
? [
|
|
187
|
-
"Run `npm run service:start` if you want the placeholder docker-compose service available at `WP_TYPIA_SERVICE_URL`.",
|
|
188
|
-
]
|
|
189
|
-
: [
|
|
190
|
-
"Set `WP_TYPIA_SERVICE_URL` only when your integration smoke needs a local service dependency.",
|
|
191
|
-
]),
|
|
192
|
-
`Run \`npm run smoke:${integrationEnvSlug}\` to execute the starter smoke check.`,
|
|
193
|
-
];
|
|
194
|
-
return `# ${title} Integration Environment
|
|
195
|
-
|
|
196
|
-
This starter keeps local WordPress integration smoke checks opt-in and editable.
|
|
197
|
-
It does not change default block scaffolds or require wp-env unless this add
|
|
198
|
-
workflow was run with \`--wp-env\`.
|
|
199
|
-
|
|
200
|
-
## Setup
|
|
201
|
-
|
|
202
|
-
${setupSteps.map((step, index) => `${index + 1}. ${step}`).join("\n")}
|
|
203
|
-
|
|
204
|
-
## Adapting the Starter
|
|
205
|
-
|
|
206
|
-
- Extend \`scripts/integration-smoke/${integrationEnvSlug}.mjs\` with the REST,
|
|
207
|
-
editor, or service assertions that matter for this project.
|
|
208
|
-
- Keep secrets in \`.env\`; \`.env.example\` should document only safe defaults.
|
|
209
|
-
- If your project uses a real service stack, replace the placeholder
|
|
210
|
-
\`docker-compose.integration.yml\` service with your database, queue, API, or
|
|
211
|
-
emulator containers.
|
|
212
|
-
- Keep the smoke script focused on high-signal integration checks so CI and
|
|
213
|
-
local debugging stay fast.
|
|
214
|
-
`;
|
|
215
|
-
}
|
|
216
|
-
async function appendMissingLines(filePath, lines) {
|
|
217
|
-
const current = (await readOptionalUtf8File(filePath)) ?? "";
|
|
218
|
-
const missingLines = lines.filter((line) => !current.includes(`${line}\n`) && !current.endsWith(line));
|
|
219
|
-
if (missingLines.length === 0) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
|
|
223
|
-
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
224
|
-
await fsp.writeFile(filePath, `${current}${separator}${missingLines.join("\n")}\n`, "utf8");
|
|
225
|
-
}
|
|
226
|
-
async function writeFileIfAbsent({ filePath, source, warnings, }) {
|
|
227
|
-
if (await pathExists(filePath)) {
|
|
228
|
-
warnings.push(`Preserved existing ${path.basename(filePath)}; review it manually if you need different local integration settings.`);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
232
|
-
await fsp.writeFile(filePath, source, "utf8");
|
|
233
|
-
}
|
|
234
|
-
async function writeNewScaffoldFile(filePath, source) {
|
|
235
|
-
if (await pathExists(filePath)) {
|
|
236
|
-
throw new Error(`An integration environment scaffold already exists at ${filePath}. Choose a different name.`);
|
|
237
|
-
}
|
|
238
|
-
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
239
|
-
await fsp.writeFile(filePath, source, "utf8");
|
|
240
|
-
}
|
|
241
|
-
function addScriptIfMissing({ scriptName, scripts, scriptValue, warnings, }) {
|
|
242
|
-
if (scripts[scriptName] === undefined) {
|
|
243
|
-
scripts[scriptName] = scriptValue;
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
if (scripts[scriptName] !== scriptValue) {
|
|
247
|
-
warnings.push(`Preserved existing package script "${scriptName}"; add "${scriptValue}" manually if you want the generated integration command.`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
async function mutatePackageJson(projectDir, mutate) {
|
|
251
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
252
|
-
const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
|
|
253
|
-
mutate(packageJson);
|
|
254
|
-
await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
|
|
255
|
-
}
|
|
256
|
-
function addIntegrationEnvPackageJsonEntries({ integrationEnvSlug, packageManager, packageJson, service, warnings, withWpEnv, }) {
|
|
257
|
-
const devDependencies = {
|
|
258
|
-
...(packageJson.devDependencies ?? {}),
|
|
259
|
-
};
|
|
260
|
-
if (withWpEnv && devDependencies["@wordpress/env"] === undefined) {
|
|
261
|
-
devDependencies["@wordpress/env"] = WP_ENV_PACKAGE_VERSION;
|
|
262
|
-
}
|
|
263
|
-
packageJson.devDependencies = devDependencies;
|
|
264
|
-
const scripts = {
|
|
265
|
-
...(packageJson.scripts ?? {}),
|
|
266
|
-
};
|
|
267
|
-
addScriptIfMissing({
|
|
268
|
-
scriptName: `smoke:${integrationEnvSlug}`,
|
|
269
|
-
scriptValue: `node scripts/integration-smoke/${integrationEnvSlug}.mjs`,
|
|
270
|
-
scripts,
|
|
271
|
-
warnings,
|
|
272
|
-
});
|
|
273
|
-
addScriptIfMissing({
|
|
274
|
-
scriptName: "smoke:integration",
|
|
275
|
-
scriptValue: formatRunScript(packageManager, `smoke:${integrationEnvSlug}`),
|
|
276
|
-
scripts,
|
|
277
|
-
warnings,
|
|
278
|
-
});
|
|
279
|
-
if (withWpEnv) {
|
|
280
|
-
addScriptIfMissing({
|
|
281
|
-
scriptName: "wp-env:start",
|
|
282
|
-
scriptValue: "wp-env start",
|
|
283
|
-
scripts,
|
|
284
|
-
warnings,
|
|
285
|
-
});
|
|
286
|
-
addScriptIfMissing({
|
|
287
|
-
scriptName: "wp-env:stop",
|
|
288
|
-
scriptValue: "wp-env stop",
|
|
289
|
-
scripts,
|
|
290
|
-
warnings,
|
|
291
|
-
});
|
|
292
|
-
addScriptIfMissing({
|
|
293
|
-
scriptName: "wp-env:reset",
|
|
294
|
-
scriptValue: "wp-env destroy all && wp-env start",
|
|
295
|
-
scripts,
|
|
296
|
-
warnings,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
if (service === "docker-compose") {
|
|
300
|
-
addScriptIfMissing({
|
|
301
|
-
scriptName: "service:start",
|
|
302
|
-
scriptValue: "docker compose -f docker-compose.integration.yml up -d",
|
|
303
|
-
scripts,
|
|
304
|
-
warnings,
|
|
305
|
-
});
|
|
306
|
-
addScriptIfMissing({
|
|
307
|
-
scriptName: "service:stop",
|
|
308
|
-
scriptValue: "docker compose -f docker-compose.integration.yml down",
|
|
309
|
-
scripts,
|
|
310
|
-
warnings,
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
packageJson.scripts = scripts;
|
|
314
|
-
}
|
|
315
9
|
/**
|
|
316
10
|
* Add an opt-in local WordPress integration environment starter to an official
|
|
317
11
|
* workspace.
|
|
318
12
|
*/
|
|
319
|
-
export async function runAddIntegrationEnvCommand({ cwd = process.cwd(), integrationEnvName, service, withWpEnv = false, }) {
|
|
13
|
+
export async function runAddIntegrationEnvCommand({ cwd = process.cwd(), integrationEnvName, service, withReleaseZip = false, withWpEnv = false, }) {
|
|
320
14
|
const workspace = resolveWorkspaceProject(cwd);
|
|
321
|
-
const integrationEnvSlug = assertValidGeneratedSlug("Integration environment name", normalizeBlockSlug(integrationEnvName), "wp-typia add integration-env <name> [--wp-env]");
|
|
15
|
+
const integrationEnvSlug = assertValidGeneratedSlug("Integration environment name", normalizeBlockSlug(integrationEnvName), "wp-typia add integration-env <name> [--wp-env] [--release-zip]");
|
|
322
16
|
const serviceId = assertValidIntegrationEnvService(service);
|
|
323
17
|
const warnings = [];
|
|
324
18
|
const packageJsonPath = path.join(workspace.projectDir, "package.json");
|
|
@@ -351,6 +45,7 @@ export async function runAddIntegrationEnvCommand({ cwd = process.cwd(), integra
|
|
|
351
45
|
await writeNewScaffoldFile(docsPath, buildIntegrationEnvReadmeSource({
|
|
352
46
|
integrationEnvSlug,
|
|
353
47
|
service: serviceId,
|
|
48
|
+
withReleaseZip,
|
|
354
49
|
withWpEnv,
|
|
355
50
|
}));
|
|
356
51
|
await appendMissingLines(envExamplePath, [
|
|
@@ -371,11 +66,12 @@ export async function runAddIntegrationEnvCommand({ cwd = process.cwd(), integra
|
|
|
371
66
|
warnings,
|
|
372
67
|
});
|
|
373
68
|
}
|
|
374
|
-
await
|
|
69
|
+
await mutateIntegrationEnvPackageJson(workspace.projectDir, (packageJson) => addIntegrationEnvPackageJsonEntries({
|
|
375
70
|
integrationEnvSlug,
|
|
376
71
|
packageManager: workspace.packageManager,
|
|
377
72
|
packageJson,
|
|
378
73
|
service: serviceId,
|
|
74
|
+
withReleaseZip,
|
|
379
75
|
warnings,
|
|
380
76
|
withWpEnv,
|
|
381
77
|
}));
|
|
@@ -386,6 +82,7 @@ export async function runAddIntegrationEnvCommand({ cwd = process.cwd(), integra
|
|
|
386
82
|
projectDir: workspace.projectDir,
|
|
387
83
|
service: serviceId,
|
|
388
84
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
85
|
+
withReleaseZip,
|
|
389
86
|
withWpEnv,
|
|
390
87
|
};
|
|
391
88
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WorkspaceProject } from "./workspace-project.js";
|
|
2
|
+
/**
|
|
3
|
+
* Ensure workspace bootstrap PHP registers pattern categories and loads
|
|
4
|
+
* generated pattern modules from both flat and nested pattern directories.
|
|
5
|
+
*
|
|
6
|
+
* @param workspace Resolved official workspace project metadata.
|
|
7
|
+
* @returns A promise that resolves after the workspace bootstrap is patched.
|
|
8
|
+
* @throws {Error} When existing bootstrap source cannot be safely patched.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensurePatternBootstrapAnchors(workspace: WorkspaceProject): Promise<void>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { getWorkspaceBootstrapPath, patchFile } from "./cli-add-shared.js";
|
|
3
|
+
import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
|
|
4
|
+
import { hasPhpFunctionDefinition } from "./php-utils.js";
|
|
5
|
+
import { toTitleCase } from "./string-case.js";
|
|
6
|
+
const FLAT_PATTERN_GLOB = "glob( __DIR__ . '/src/patterns/*.php' ) ?: array()";
|
|
7
|
+
const NESTED_PATTERN_GLOB = "glob( __DIR__ . '/src/patterns/*/*.php' ) ?: array()";
|
|
8
|
+
const LEGACY_FLAT_PATTERN_MODULES_ASSIGNMENT_PATTERN = /^[ \t]*\$pattern_modules\s*=\s*glob\( __DIR__ \. '\/src\/patterns\/\*\.php' \) \?: array\(\);\s*$/mu;
|
|
9
|
+
const LEGACY_FLAT_PATTERN_FOREACH_PATTERN = /^[ \t]*foreach\s*\(\s*glob\( __DIR__ \. '\/src\/patterns\/\*\.php' \) \?: array\(\)\s+as\s+\$pattern_module\s*\)\s*\{\r?\n[ \t]*require\s+\$pattern_module;\r?\n[ \t]*\}/mu;
|
|
10
|
+
function buildNestedPatternModulesAssignment() {
|
|
11
|
+
return [
|
|
12
|
+
"\t$pattern_modules = array_merge(",
|
|
13
|
+
`\t\t${FLAT_PATTERN_GLOB},`,
|
|
14
|
+
`\t\t${NESTED_PATTERN_GLOB}`,
|
|
15
|
+
"\t);",
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
function ensureNestedPatternLoaderSource(source, bootstrapPath) {
|
|
19
|
+
if (source.includes(NESTED_PATTERN_GLOB)) {
|
|
20
|
+
return source;
|
|
21
|
+
}
|
|
22
|
+
if (LEGACY_FLAT_PATTERN_FOREACH_PATTERN.test(source)) {
|
|
23
|
+
return source.replace(LEGACY_FLAT_PATTERN_FOREACH_PATTERN, [
|
|
24
|
+
buildNestedPatternModulesAssignment(),
|
|
25
|
+
"\tforeach ( $pattern_modules as $pattern_module ) {",
|
|
26
|
+
"\t\trequire $pattern_module;",
|
|
27
|
+
"\t}",
|
|
28
|
+
].join("\n"));
|
|
29
|
+
}
|
|
30
|
+
if (LEGACY_FLAT_PATTERN_MODULES_ASSIGNMENT_PATTERN.test(source)) {
|
|
31
|
+
return source.replace(LEGACY_FLAT_PATTERN_MODULES_ASSIGNMENT_PATTERN, buildNestedPatternModulesAssignment());
|
|
32
|
+
}
|
|
33
|
+
if (source.includes("array_merge(") && source.includes(FLAT_PATTERN_GLOB)) {
|
|
34
|
+
return source.replace(FLAT_PATTERN_GLOB, `${FLAT_PATTERN_GLOB},\n\t\t${NESTED_PATTERN_GLOB}`);
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unable to repair ${path.basename(bootstrapPath)} pattern loader for nested src/patterns directories.`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Ensure workspace bootstrap PHP registers pattern categories and loads
|
|
40
|
+
* generated pattern modules from both flat and nested pattern directories.
|
|
41
|
+
*
|
|
42
|
+
* @param workspace Resolved official workspace project metadata.
|
|
43
|
+
* @returns A promise that resolves after the workspace bootstrap is patched.
|
|
44
|
+
* @throws {Error} When existing bootstrap source cannot be safely patched.
|
|
45
|
+
*/
|
|
46
|
+
export async function ensurePatternBootstrapAnchors(workspace) {
|
|
47
|
+
const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
|
|
48
|
+
const bootstrapPath = getWorkspaceBootstrapPath(workspace);
|
|
49
|
+
await patchFile(bootstrapPath, (source) => {
|
|
50
|
+
let nextSource = source;
|
|
51
|
+
const patternCategoryFunctionName = `${workspace.workspace.phpPrefix}_register_pattern_category`;
|
|
52
|
+
const patternRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_patterns`;
|
|
53
|
+
const patternCategoryHook = `add_action( 'init', '${patternCategoryFunctionName}' );`;
|
|
54
|
+
const patternRegistrationHook = `add_action( 'init', '${patternRegistrationFunctionName}', 20 );`;
|
|
55
|
+
const patternFunctions = `
|
|
56
|
+
|
|
57
|
+
function ${patternCategoryFunctionName}() {
|
|
58
|
+
if ( function_exists( 'register_block_pattern_category' ) ) {
|
|
59
|
+
register_block_pattern_category(
|
|
60
|
+
'${workspace.workspace.namespace}',
|
|
61
|
+
array(
|
|
62
|
+
'label' => __( ${JSON.stringify(`${toTitleCase(workspaceBaseName)} Patterns`)}, '${workspace.workspace.textDomain}' ),
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function ${patternRegistrationFunctionName}() {
|
|
69
|
+
$pattern_modules = array_merge(
|
|
70
|
+
${FLAT_PATTERN_GLOB},
|
|
71
|
+
${NESTED_PATTERN_GLOB}
|
|
72
|
+
);
|
|
73
|
+
foreach ( $pattern_modules as $pattern_module ) {
|
|
74
|
+
require $pattern_module;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
if (!hasPhpFunctionDefinition(nextSource, patternCategoryFunctionName) &&
|
|
79
|
+
!hasPhpFunctionDefinition(nextSource, patternRegistrationFunctionName)) {
|
|
80
|
+
nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, patternFunctions);
|
|
81
|
+
}
|
|
82
|
+
if (!hasPhpFunctionDefinition(nextSource, patternCategoryFunctionName) ||
|
|
83
|
+
!hasPhpFunctionDefinition(nextSource, patternRegistrationFunctionName)) {
|
|
84
|
+
throw new Error(`Unable to inject pattern bootstrap functions into ${path.basename(bootstrapPath)}.`);
|
|
85
|
+
}
|
|
86
|
+
nextSource = ensureNestedPatternLoaderSource(nextSource, bootstrapPath);
|
|
87
|
+
if (!nextSource.includes(patternCategoryHook)) {
|
|
88
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, patternCategoryHook);
|
|
89
|
+
}
|
|
90
|
+
if (!nextSource.includes(patternRegistrationHook)) {
|
|
91
|
+
nextSource = appendPhpSnippetBeforeClosingTag(nextSource, patternRegistrationHook);
|
|
92
|
+
}
|
|
93
|
+
return nextSource;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RunAddPatternCommandOptions } from "./cli-add-shared.js";
|
|
2
|
+
import { type PatternCatalogScope } from "./pattern-catalog.js";
|
|
3
|
+
export type ResolvedPatternCatalogOptions = {
|
|
4
|
+
contentFile: string;
|
|
5
|
+
patternScope: PatternCatalogScope;
|
|
6
|
+
sectionRole?: string;
|
|
7
|
+
tags: string[];
|
|
8
|
+
thumbnailUrl?: string;
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Resolve and validate catalog metadata for a generated workspace pattern.
|
|
13
|
+
*
|
|
14
|
+
* @param patternSlug Normalized and validated pattern slug.
|
|
15
|
+
* @param options Raw add-pattern command options from the CLI layer.
|
|
16
|
+
* @returns Resolved catalog metadata and safe relative content path.
|
|
17
|
+
* @throws {Error} When scope, section-role coupling, tags, thumbnail URL, or
|
|
18
|
+
* content file paths are invalid.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolvePatternCatalogOptions(patternSlug: string, options: RunAddPatternCommandOptions): ResolvedPatternCatalogOptions;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { assertValidGeneratedSlug, normalizeBlockSlug, } from "./cli-add-shared.js";
|
|
3
|
+
import { isValidPatternThumbnailUrl, PATTERN_CATALOG_SCOPE_IDS, } from "./pattern-catalog.js";
|
|
4
|
+
import { toTitleCase } from "./string-case.js";
|
|
5
|
+
const PATTERN_CONTENT_FILE_ROOT = "src/patterns/";
|
|
6
|
+
const PATTERN_TAG_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
7
|
+
function assertValidPatternRelativePath(label, value, usage) {
|
|
8
|
+
const normalizedPath = value.trim().replace(/\\/gu, "/");
|
|
9
|
+
if (normalizedPath.length === 0 ||
|
|
10
|
+
path.isAbsolute(normalizedPath) ||
|
|
11
|
+
normalizedPath.split("/").includes("..") ||
|
|
12
|
+
/[<>:"|?*\u0000-\u001F]/u.test(normalizedPath)) {
|
|
13
|
+
throw new Error(`${label} must be a safe relative project path. Use \`${usage}\`.`);
|
|
14
|
+
}
|
|
15
|
+
return normalizedPath;
|
|
16
|
+
}
|
|
17
|
+
function assertValidPatternContentFile(value, usage) {
|
|
18
|
+
const normalizedPath = assertValidPatternRelativePath("Pattern content file", value, usage);
|
|
19
|
+
if (!isLoadablePatternContentFilePath(normalizedPath)) {
|
|
20
|
+
throw new Error("Pattern content file must live directly under `src/patterns/` or one nested directory under `src/patterns/` and end in `.php` so the generated PHP loader can require it.");
|
|
21
|
+
}
|
|
22
|
+
return normalizedPath;
|
|
23
|
+
}
|
|
24
|
+
function isLoadablePatternContentFilePath(normalizedPath) {
|
|
25
|
+
if (!normalizedPath.startsWith(PATTERN_CONTENT_FILE_ROOT) ||
|
|
26
|
+
!normalizedPath.endsWith(".php")) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const patternRelativePath = normalizedPath.slice(PATTERN_CONTENT_FILE_ROOT.length);
|
|
30
|
+
const segments = patternRelativePath.split("/");
|
|
31
|
+
return ((segments.length === 1 || segments.length === 2) &&
|
|
32
|
+
segments.every((segment) => segment.length > 0));
|
|
33
|
+
}
|
|
34
|
+
function resolvePatternScope(scope) {
|
|
35
|
+
if (scope === undefined || scope.trim() === "") {
|
|
36
|
+
return "full";
|
|
37
|
+
}
|
|
38
|
+
const normalizedScope = scope.trim();
|
|
39
|
+
if (PATTERN_CATALOG_SCOPE_IDS.includes(normalizedScope)) {
|
|
40
|
+
return normalizedScope;
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`Pattern scope must be one of: ${PATTERN_CATALOG_SCOPE_IDS.join(", ")}.`);
|
|
43
|
+
}
|
|
44
|
+
function normalizeOptionalSlug(label, value, usage) {
|
|
45
|
+
if (value === undefined || value.trim() === "") {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return assertValidGeneratedSlug(label, normalizeBlockSlug(value), usage);
|
|
49
|
+
}
|
|
50
|
+
function normalizePatternTags(tags) {
|
|
51
|
+
const rawTags = typeof tags === "string"
|
|
52
|
+
? tags.split(",")
|
|
53
|
+
: Array.isArray(tags)
|
|
54
|
+
? [...tags]
|
|
55
|
+
: [];
|
|
56
|
+
const normalizedTags = rawTags
|
|
57
|
+
.map((tag) => normalizeBlockSlug(tag))
|
|
58
|
+
.filter((tag) => tag.length > 0);
|
|
59
|
+
for (const tag of normalizedTags) {
|
|
60
|
+
if (!PATTERN_TAG_PATTERN.test(tag)) {
|
|
61
|
+
throw new Error(`Pattern tag "${tag}" must contain only lowercase letters, numbers, and hyphens.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [...new Set(normalizedTags)].sort();
|
|
65
|
+
}
|
|
66
|
+
function normalizePatternThumbnailUrl(value) {
|
|
67
|
+
if (value === undefined || value.trim() === "") {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const thumbnailUrl = value.trim();
|
|
71
|
+
if (!isValidPatternThumbnailUrl(thumbnailUrl)) {
|
|
72
|
+
throw new Error("Pattern thumbnail URL must be an http(s) URL or safe relative project path.");
|
|
73
|
+
}
|
|
74
|
+
return thumbnailUrl;
|
|
75
|
+
}
|
|
76
|
+
function resolvePatternContentFile(patternSlug, patternScope, contentFile) {
|
|
77
|
+
if (contentFile && contentFile.trim() !== "") {
|
|
78
|
+
return assertValidPatternContentFile(contentFile, "wp-typia add pattern <name> [--scope <full|section>]");
|
|
79
|
+
}
|
|
80
|
+
const scopeDirectory = patternScope === "section" ? "sections" : "full";
|
|
81
|
+
return path.posix.join("src", "patterns", scopeDirectory, `${patternSlug}.php`);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve and validate catalog metadata for a generated workspace pattern.
|
|
85
|
+
*
|
|
86
|
+
* @param patternSlug Normalized and validated pattern slug.
|
|
87
|
+
* @param options Raw add-pattern command options from the CLI layer.
|
|
88
|
+
* @returns Resolved catalog metadata and safe relative content path.
|
|
89
|
+
* @throws {Error} When scope, section-role coupling, tags, thumbnail URL, or
|
|
90
|
+
* content file paths are invalid.
|
|
91
|
+
*/
|
|
92
|
+
export function resolvePatternCatalogOptions(patternSlug, options) {
|
|
93
|
+
const patternScope = resolvePatternScope(options.patternScope);
|
|
94
|
+
const sectionRole = normalizeOptionalSlug("Pattern section role", options.sectionRole, "wp-typia add pattern <name> --scope section --section-role <role>");
|
|
95
|
+
if (patternScope === "section" && !sectionRole) {
|
|
96
|
+
throw new Error("`wp-typia add pattern --scope section` requires --section-role <role>.");
|
|
97
|
+
}
|
|
98
|
+
if (patternScope !== "section" && sectionRole) {
|
|
99
|
+
throw new Error("`--section-role` requires `--scope section`.");
|
|
100
|
+
}
|
|
101
|
+
const title = options.catalogTitle && options.catalogTitle.trim() !== ""
|
|
102
|
+
? options.catalogTitle.trim()
|
|
103
|
+
: toTitleCase(patternSlug);
|
|
104
|
+
const thumbnailUrl = normalizePatternThumbnailUrl(options.thumbnailUrl);
|
|
105
|
+
return {
|
|
106
|
+
contentFile: resolvePatternContentFile(patternSlug, patternScope, options.contentFile),
|
|
107
|
+
patternScope,
|
|
108
|
+
...(sectionRole ? { sectionRole } : {}),
|
|
109
|
+
tags: normalizePatternTags(options.tags),
|
|
110
|
+
...(thumbnailUrl ? { thumbnailUrl } : {}),
|
|
111
|
+
title,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ResolvedPatternCatalogOptions } from "./cli-add-workspace-pattern-options.js";
|
|
2
|
+
/**
|
|
3
|
+
* Render the block-config inventory entry for a generated pattern.
|
|
4
|
+
*
|
|
5
|
+
* @param patternSlug Normalized and validated pattern slug.
|
|
6
|
+
* @param options Resolved pattern catalog metadata.
|
|
7
|
+
* @returns A TypeScript object literal snippet for the workspace inventory.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildPatternConfigEntry(patternSlug: string, options: ResolvedPatternCatalogOptions): string;
|
|
10
|
+
/**
|
|
11
|
+
* Render the PHP pattern module registered by the workspace add command.
|
|
12
|
+
*
|
|
13
|
+
* @param patternSlug Normalized and validated pattern slug.
|
|
14
|
+
* @param namespace WordPress block namespace for the workspace.
|
|
15
|
+
* @param sectionRole Optional section role for section-scoped patterns.
|
|
16
|
+
* @param textDomain Translation text domain for generated labels.
|
|
17
|
+
* @param title Human-readable pattern title.
|
|
18
|
+
* @returns PHP source for a single generated block pattern module.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildPatternSource(patternSlug: string, namespace: string, sectionRole: string | undefined, textDomain: string, title: string): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { quoteTsString } from "./cli-add-shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Render the block-config inventory entry for a generated pattern.
|
|
4
|
+
*
|
|
5
|
+
* @param patternSlug Normalized and validated pattern slug.
|
|
6
|
+
* @param options Resolved pattern catalog metadata.
|
|
7
|
+
* @returns A TypeScript object literal snippet for the workspace inventory.
|
|
8
|
+
*/
|
|
9
|
+
export function buildPatternConfigEntry(patternSlug, options) {
|
|
10
|
+
const lines = [
|
|
11
|
+
"\t{",
|
|
12
|
+
`\t\tcontentFile: ${quoteTsString(options.contentFile)},`,
|
|
13
|
+
`\t\tfile: ${quoteTsString(options.contentFile)},`,
|
|
14
|
+
`\t\tscope: ${quoteTsString(options.patternScope)},`,
|
|
15
|
+
...(options.sectionRole
|
|
16
|
+
? [`\t\tsectionRole: ${quoteTsString(options.sectionRole)},`]
|
|
17
|
+
: []),
|
|
18
|
+
`\t\tslug: ${quoteTsString(patternSlug)},`,
|
|
19
|
+
`\t\ttags: [${options.tags.map((tag) => quoteTsString(tag)).join(", ")}],`,
|
|
20
|
+
...(options.thumbnailUrl
|
|
21
|
+
? [`\t\tthumbnailUrl: ${quoteTsString(options.thumbnailUrl)},`]
|
|
22
|
+
: []),
|
|
23
|
+
`\t\ttitle: ${quoteTsString(options.title)},`,
|
|
24
|
+
"\t},",
|
|
25
|
+
];
|
|
26
|
+
return lines.join("\n");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Render the PHP pattern module registered by the workspace add command.
|
|
30
|
+
*
|
|
31
|
+
* @param patternSlug Normalized and validated pattern slug.
|
|
32
|
+
* @param namespace WordPress block namespace for the workspace.
|
|
33
|
+
* @param sectionRole Optional section role for section-scoped patterns.
|
|
34
|
+
* @param textDomain Translation text domain for generated labels.
|
|
35
|
+
* @param title Human-readable pattern title.
|
|
36
|
+
* @returns PHP source for a single generated block pattern module.
|
|
37
|
+
*/
|
|
38
|
+
export function buildPatternSource(patternSlug, namespace, sectionRole, textDomain, title) {
|
|
39
|
+
const content = sectionRole
|
|
40
|
+
? `'<!-- wp:group {"className":"section section--${sectionRole}"} --><div class="wp-block-group section section--${sectionRole}"><!-- wp:paragraph --><p>' . esc_html__( 'Describe this section pattern here.', '${textDomain}' ) . '</p><!-- /wp:paragraph --></div><!-- /wp:group -->'`
|
|
41
|
+
: `'<!-- wp:paragraph --><p>' . esc_html__( 'Describe this pattern here.', '${textDomain}' ) . '</p><!-- /wp:paragraph -->'`;
|
|
42
|
+
return `<?php
|
|
43
|
+
if ( ! defined( 'ABSPATH' ) ) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
register_block_pattern(
|
|
48
|
+
'${namespace}/${patternSlug}',
|
|
49
|
+
array(
|
|
50
|
+
'title' => __( ${JSON.stringify(title)}, '${textDomain}' ),
|
|
51
|
+
'description' => __( ${JSON.stringify(`A starter pattern for ${title}.`)}, '${textDomain}' ),
|
|
52
|
+
'categories' => array( '${namespace}' ),
|
|
53
|
+
'content' => ${content},
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
`;
|
|
57
|
+
}
|