@wp-typia/project-tools 0.16.13 → 0.17.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/README.md +0 -1
- package/dist/runtime/block-generator-service-spec.d.ts +6 -0
- package/dist/runtime/block-generator-service-spec.js +27 -0
- package/dist/runtime/built-in-block-code-artifacts.js +14 -1
- package/dist/runtime/built-in-block-code-templates/query-loop.d.ts +1 -0
- package/dist/runtime/built-in-block-code-templates/query-loop.js +70 -0
- package/dist/runtime/built-in-block-code-templates.d.ts +1 -0
- package/dist/runtime/built-in-block-code-templates.js +1 -0
- package/dist/runtime/built-in-block-non-ts-artifacts.js +2 -0
- package/dist/runtime/cli-help.js +1 -0
- package/dist/runtime/cli-prompt.js +78 -19
- package/dist/runtime/cli-scaffold.d.ts +2 -1
- package/dist/runtime/cli-scaffold.js +2 -1
- package/dist/runtime/local-dev-presets.js +21 -11
- package/dist/runtime/scaffold-answer-resolution.d.ts +37 -0
- package/dist/runtime/scaffold-answer-resolution.js +163 -0
- package/dist/runtime/scaffold-apply-utils.d.ts +1 -16
- package/dist/runtime/scaffold-apply-utils.js +4 -128
- package/dist/runtime/scaffold-documents.d.ts +34 -0
- package/dist/runtime/scaffold-documents.js +143 -0
- package/dist/runtime/scaffold-onboarding.js +12 -0
- package/dist/runtime/scaffold-template-variables.d.ts +9 -0
- package/dist/runtime/scaffold-template-variables.js +117 -0
- package/dist/runtime/scaffold.d.ts +19 -9
- package/dist/runtime/scaffold.js +6 -202
- package/dist/runtime/template-defaults.d.ts +7 -0
- package/dist/runtime/template-defaults.js +4 -0
- package/dist/runtime/template-registry.d.ts +1 -1
- package/dist/runtime/template-registry.js +14 -1
- package/package.json +3 -3
- package/templates/query-loop/inc/query-runtime.php.mustache +85 -0
- package/templates/query-loop/package.json.mustache +32 -0
- package/templates/query-loop/src/patterns/grid.php.mustache +49 -0
- package/templates/query-loop/src/patterns/list.php.mustache +48 -0
- package/templates/query-loop/src/query-extension.ts.mustache +41 -0
- package/templates/query-loop/src/validator-toolkit.ts.mustache +1 -0
- package/templates/query-loop/webpack.config.js.mustache +16 -0
- package/templates/query-loop/{{slugKebabCase}}.php.mustache +84 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { buildTemplateVariablesFromBlockSpec, createBuiltInBlockSpec, } from './block-generator-service.js';
|
|
2
|
+
import { getPackageVersions } from './package-versions.js';
|
|
3
|
+
import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from './scaffold-identifiers.js';
|
|
4
|
+
import { BUILTIN_BLOCK_METADATA_VERSION, COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS, getBuiltInTemplateMetadataDefaults, } from './template-defaults.js';
|
|
5
|
+
import { getTemplateById, isBuiltInTemplateId, } from './template-registry.js';
|
|
6
|
+
import { toPascalCase, toSnakeCase, } from './string-case.js';
|
|
7
|
+
/**
|
|
8
|
+
* Build the normalized template variables used by scaffold rendering.
|
|
9
|
+
*
|
|
10
|
+
* @param templateId Selected scaffold template identifier.
|
|
11
|
+
* @param answers Normalized scaffold answers collected from defaults, flags, and prompts.
|
|
12
|
+
* @returns Template variables ready for file interpolation and generated artifacts.
|
|
13
|
+
*/
|
|
14
|
+
export function getTemplateVariables(templateId, answers) {
|
|
15
|
+
if (isBuiltInTemplateId(templateId)) {
|
|
16
|
+
return buildTemplateVariablesFromBlockSpec(createBuiltInBlockSpec({
|
|
17
|
+
answers,
|
|
18
|
+
dataStorageMode: answers.dataStorageMode,
|
|
19
|
+
persistencePolicy: answers.persistencePolicy,
|
|
20
|
+
templateId,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
|
|
24
|
+
const template = isBuiltInTemplateId(templateId) ? getTemplateById(templateId) : null;
|
|
25
|
+
const metadataDefaults = isBuiltInTemplateId(templateId)
|
|
26
|
+
? getBuiltInTemplateMetadataDefaults(templateId)
|
|
27
|
+
: null;
|
|
28
|
+
const identifiers = resolveScaffoldIdentifiers({
|
|
29
|
+
namespace: answers.namespace,
|
|
30
|
+
phpPrefix: answers.phpPrefix,
|
|
31
|
+
slug: answers.slug,
|
|
32
|
+
textDomain: answers.textDomain,
|
|
33
|
+
});
|
|
34
|
+
const slug = identifiers.slug;
|
|
35
|
+
const slugSnakeCase = toSnakeCase(slug);
|
|
36
|
+
const pascalCase = toPascalCase(slug);
|
|
37
|
+
const title = answers.title.trim();
|
|
38
|
+
const namespace = identifiers.namespace;
|
|
39
|
+
const description = answers.description.trim();
|
|
40
|
+
const textDomain = identifiers.textDomain;
|
|
41
|
+
const phpPrefix = identifiers.phpPrefix;
|
|
42
|
+
const phpPrefixUpper = phpPrefix.toUpperCase();
|
|
43
|
+
const compoundChildTitle = `${title} Item`;
|
|
44
|
+
const cssClassName = buildBlockCssClassName(namespace, slug);
|
|
45
|
+
const compoundChildCssClassName = buildBlockCssClassName(namespace, `${slug}-item`);
|
|
46
|
+
const compoundPersistenceEnabled = templateId === 'persistence'
|
|
47
|
+
? true
|
|
48
|
+
: templateId === 'compound'
|
|
49
|
+
? Boolean(answers.dataStorageMode || answers.persistencePolicy)
|
|
50
|
+
: false;
|
|
51
|
+
const dataStorageMode = templateId === 'persistence' || compoundPersistenceEnabled
|
|
52
|
+
? answers.dataStorageMode ?? 'custom-table'
|
|
53
|
+
: 'custom-table';
|
|
54
|
+
const persistencePolicy = templateId === 'persistence' || compoundPersistenceEnabled
|
|
55
|
+
? answers.persistencePolicy ?? 'authenticated'
|
|
56
|
+
: 'authenticated';
|
|
57
|
+
return {
|
|
58
|
+
apiClientPackageVersion,
|
|
59
|
+
author: answers.author.trim(),
|
|
60
|
+
blockRuntimePackageVersion,
|
|
61
|
+
blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
|
|
62
|
+
blockTypesPackageVersion,
|
|
63
|
+
category: metadataDefaults?.category ?? template?.defaultCategory ?? 'widgets',
|
|
64
|
+
icon: metadataDefaults?.icon ?? 'smiley',
|
|
65
|
+
compoundChildTitle,
|
|
66
|
+
compoundChildCategory: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.category,
|
|
67
|
+
compoundChildCssClassName,
|
|
68
|
+
compoundChildIcon: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.icon,
|
|
69
|
+
compoundChildTitleJson: JSON.stringify(compoundChildTitle),
|
|
70
|
+
compoundPersistenceEnabled: compoundPersistenceEnabled ? 'true' : 'false',
|
|
71
|
+
projectToolsPackageVersion,
|
|
72
|
+
cssClassName,
|
|
73
|
+
dataStorageMode,
|
|
74
|
+
dashCase: slug,
|
|
75
|
+
description,
|
|
76
|
+
descriptionJson: JSON.stringify(description),
|
|
77
|
+
frontendCssClassName: buildFrontendCssClassName(cssClassName),
|
|
78
|
+
queryAllowedControlsJson: JSON.stringify([], null, 2),
|
|
79
|
+
queryPostType: answers.queryPostType?.trim() || 'post',
|
|
80
|
+
queryPostTypeJson: JSON.stringify(answers.queryPostType?.trim() || 'post'),
|
|
81
|
+
queryVariationNamespace: `${namespace}/${slug}`,
|
|
82
|
+
queryVariationNamespaceJson: JSON.stringify(`${namespace}/${slug}`),
|
|
83
|
+
isAuthenticatedPersistencePolicy: persistencePolicy === 'authenticated' ? 'true' : 'false',
|
|
84
|
+
isPublicPersistencePolicy: persistencePolicy === 'public' ? 'true' : 'false',
|
|
85
|
+
bootstrapCredentialDeclarations: persistencePolicy === 'public'
|
|
86
|
+
? "publicWriteExpiresAt?: number & tags.Type< 'uint32' >;\n\tpublicWriteToken?: string & tags.MinLength< 1 > & tags.MaxLength< 512 >;"
|
|
87
|
+
: "restNonce?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
88
|
+
persistencePolicyDescriptionJson: JSON.stringify(persistencePolicy === 'authenticated'
|
|
89
|
+
? 'Writes require a logged-in user and a valid REST nonce.'
|
|
90
|
+
: 'Anonymous writes use signed short-lived public tokens, per-request ids, and coarse rate limiting.'),
|
|
91
|
+
keyword: slug.replace(/-/g, ' '),
|
|
92
|
+
namespace,
|
|
93
|
+
needsMigration: '{{needsMigration}}',
|
|
94
|
+
pascalCase,
|
|
95
|
+
phpPrefix,
|
|
96
|
+
phpPrefixUpper,
|
|
97
|
+
restPackageVersion,
|
|
98
|
+
publicWriteRequestIdDeclaration: persistencePolicy === 'public'
|
|
99
|
+
? "publicWriteRequestId: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;"
|
|
100
|
+
: "publicWriteRequestId?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
101
|
+
restWriteAuthIntent: persistencePolicy === 'public'
|
|
102
|
+
? 'public-write-protected'
|
|
103
|
+
: 'authenticated',
|
|
104
|
+
restWriteAuthMechanism: persistencePolicy === 'public' ? 'public-signed-token' : 'rest-nonce',
|
|
105
|
+
restWriteAuthMode: persistencePolicy === 'public' ? 'public-signed-token' : 'authenticated-rest-nonce',
|
|
106
|
+
slug,
|
|
107
|
+
slugCamelCase: pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1),
|
|
108
|
+
slugKebabCase: slug,
|
|
109
|
+
slugSnakeCase,
|
|
110
|
+
textDomain,
|
|
111
|
+
textdomain: textDomain,
|
|
112
|
+
title,
|
|
113
|
+
titleJson: JSON.stringify(title),
|
|
114
|
+
titleCase: pascalCase,
|
|
115
|
+
persistencePolicy,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -15,6 +15,7 @@ export interface ScaffoldAnswers {
|
|
|
15
15
|
persistencePolicy?: PersistencePolicy;
|
|
16
16
|
/** Snake_case PHP symbol prefix used for generated functions, constants, and keys. */
|
|
17
17
|
phpPrefix?: string;
|
|
18
|
+
queryPostType?: string;
|
|
18
19
|
slug: string;
|
|
19
20
|
/** Kebab-case WordPress text domain used in block metadata and i18n strings. */
|
|
20
21
|
textDomain?: string;
|
|
@@ -46,6 +47,7 @@ export interface ScaffoldTemplateVariables extends Record<string, string> {
|
|
|
46
47
|
dashCase: string;
|
|
47
48
|
dataStorageMode: DataStorageMode;
|
|
48
49
|
description: string;
|
|
50
|
+
descriptionJson: string;
|
|
49
51
|
frontendCssClassName: string;
|
|
50
52
|
keyword: string;
|
|
51
53
|
namespace: string;
|
|
@@ -53,6 +55,11 @@ export interface ScaffoldTemplateVariables extends Record<string, string> {
|
|
|
53
55
|
pascalCase: string;
|
|
54
56
|
phpPrefix: string;
|
|
55
57
|
phpPrefixUpper: string;
|
|
58
|
+
queryAllowedControlsJson: string;
|
|
59
|
+
queryPostTypeJson: string;
|
|
60
|
+
queryPostType: string;
|
|
61
|
+
queryVariationNamespace: string;
|
|
62
|
+
queryVariationNamespaceJson: string;
|
|
56
63
|
isAuthenticatedPersistencePolicy: "false" | "true";
|
|
57
64
|
isPublicPersistencePolicy: "false" | "true";
|
|
58
65
|
bootstrapCredentialDeclarations: string;
|
|
@@ -81,25 +88,32 @@ export interface ScaffoldTemplateVariables extends Record<string, string> {
|
|
|
81
88
|
* can surface custom ids. Downstream code uses `isBuiltInTemplateId()` to
|
|
82
89
|
* distinguish built-in templates from custom sources.
|
|
83
90
|
*/
|
|
84
|
-
interface ResolveTemplateOptions {
|
|
91
|
+
export interface ResolveTemplateOptions {
|
|
85
92
|
isInteractive?: boolean;
|
|
86
93
|
selectTemplate?: () => Promise<string>;
|
|
87
94
|
templateId?: string;
|
|
88
95
|
yes?: boolean;
|
|
89
96
|
}
|
|
90
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Options for resolving the package manager in CLI and interactive scaffold flows.
|
|
99
|
+
*/
|
|
100
|
+
export interface ResolvePackageManagerOptions {
|
|
91
101
|
isInteractive?: boolean;
|
|
92
102
|
packageManager?: string;
|
|
93
103
|
selectPackageManager?: () => Promise<PackageManagerId>;
|
|
94
104
|
yes?: boolean;
|
|
95
105
|
}
|
|
96
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Options for collecting scaffold answers from defaults, overrides, and prompts.
|
|
108
|
+
*/
|
|
109
|
+
export interface CollectScaffoldAnswersOptions {
|
|
97
110
|
dataStorageMode?: DataStorageMode;
|
|
98
111
|
namespace?: string;
|
|
99
112
|
phpPrefix?: string;
|
|
100
113
|
projectName: string;
|
|
101
114
|
promptText?: (message: string, defaultValue: string, validate?: (value: string) => true | string) => Promise<string>;
|
|
102
115
|
persistencePolicy?: PersistencePolicy;
|
|
116
|
+
queryPostType?: string;
|
|
103
117
|
textDomain?: string;
|
|
104
118
|
templateId: string;
|
|
105
119
|
withTestPreset?: boolean;
|
|
@@ -139,12 +153,8 @@ export interface ScaffoldProjectResult {
|
|
|
139
153
|
warnings: string[];
|
|
140
154
|
}
|
|
141
155
|
export { buildBlockCssClassName } from "./scaffold-identifiers.js";
|
|
156
|
+
export { collectScaffoldAnswers, detectAuthor, getDefaultAnswers, resolvePackageManagerId, resolveTemplateId, } from "./scaffold-answer-resolution.js";
|
|
157
|
+
export { getTemplateVariables } from "./scaffold-template-variables.js";
|
|
142
158
|
export declare function isDataStorageMode(value: string): value is DataStorageMode;
|
|
143
159
|
export declare function isPersistencePolicy(value: string): value is PersistencePolicy;
|
|
144
|
-
export declare function detectAuthor(): string;
|
|
145
|
-
export declare function getDefaultAnswers(projectName: string, templateId: string): ScaffoldAnswers;
|
|
146
|
-
export declare function resolveTemplateId({ templateId, yes, isInteractive, selectTemplate, }: ResolveTemplateOptions): Promise<string>;
|
|
147
|
-
export declare function resolvePackageManagerId({ packageManager, yes, isInteractive, selectPackageManager, }: ResolvePackageManagerOptions): Promise<PackageManagerId>;
|
|
148
|
-
export declare function collectScaffoldAnswers({ projectName, templateId, yes, dataStorageMode, namespace, persistencePolicy, phpPrefix, promptText, textDomain, }: CollectScaffoldAnswersOptions): Promise<ScaffoldAnswers>;
|
|
149
|
-
export declare function getTemplateVariables(templateId: string, answers: ScaffoldAnswers): ScaffoldTemplateVariables;
|
|
150
160
|
export declare function scaffoldProject({ projectDir, templateId, answers, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, repositoryReference, cwd, allowExistingDir, noInstall, installDependencies, variant, withMigrationUi, withTestPreset, withWpEnv, }: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
|
package/dist/runtime/scaffold.js
CHANGED
|
@@ -1,230 +1,34 @@
|
|
|
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 {
|
|
5
|
-
import { PACKAGE_MANAGER_IDS, getPackageManager, } from "./package-managers.js";
|
|
4
|
+
import { getPackageManager } from "./package-managers.js";
|
|
6
5
|
import { buildGitignore, buildReadme, mergeTextLines, replaceTextRecursively, } from "./scaffold-apply-utils.js";
|
|
7
|
-
import { buildBlockCssClassName, buildFrontendCssClassName, normalizeBlockSlug, resolveScaffoldIdentifiers, validateBlockSlug, validateNamespace, } from "./scaffold-identifiers.js";
|
|
8
6
|
import { applyGeneratedProjectDxPackageJson, applyLocalDevPresetFiles, } from "./local-dev-presets.js";
|
|
9
7
|
import { applyMigrationUiCapability } from "./migration-ui-capability.js";
|
|
10
|
-
import { getPackageVersions } from "./package-versions.js";
|
|
11
8
|
import { applyWorkspaceMigrationCapability, ensureScaffoldDirectory, isOfficialWorkspaceProject, seedBuiltInPersistenceArtifacts, writeStarterManifestFiles, } from "./scaffold-bootstrap.js";
|
|
12
9
|
import { defaultInstallDependencies, normalizePackageJson, normalizePackageManagerFiles, removeUnexpectedLockfiles, } from "./scaffold-package-manager-files.js";
|
|
13
|
-
import { toPascalCase, toSnakeCase, toTitleCase, } from "./string-case.js";
|
|
14
|
-
import { BUILTIN_BLOCK_METADATA_VERSION, COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS, getBuiltInTemplateMetadataDefaults, getRemovedBuiltInTemplateMessage, isRemovedBuiltInTemplateId, } from "./template-defaults.js";
|
|
15
10
|
import { copyInterpolatedDirectory } from "./template-render.js";
|
|
16
|
-
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE,
|
|
11
|
+
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from "./template-registry.js";
|
|
17
12
|
import { resolveTemplateSource } from "./template-source.js";
|
|
18
|
-
import { BlockGeneratorService,
|
|
13
|
+
import { BlockGeneratorService, } from "./block-generator-service.js";
|
|
14
|
+
import { getTemplateVariables } from "./scaffold-template-variables.js";
|
|
19
15
|
const WORKSPACE_TEMPLATE_ALIAS = "workspace";
|
|
20
16
|
export const DATA_STORAGE_MODES = ["post-meta", "custom-table"];
|
|
21
17
|
export const PERSISTENCE_POLICIES = ["authenticated", "public"];
|
|
22
18
|
export { buildBlockCssClassName } from "./scaffold-identifiers.js";
|
|
19
|
+
export { collectScaffoldAnswers, detectAuthor, getDefaultAnswers, resolvePackageManagerId, resolveTemplateId, } from "./scaffold-answer-resolution.js";
|
|
20
|
+
export { getTemplateVariables } from "./scaffold-template-variables.js";
|
|
23
21
|
export function isDataStorageMode(value) {
|
|
24
22
|
return DATA_STORAGE_MODES.includes(value);
|
|
25
23
|
}
|
|
26
24
|
export function isPersistencePolicy(value) {
|
|
27
25
|
return PERSISTENCE_POLICIES.includes(value);
|
|
28
26
|
}
|
|
29
|
-
export function detectAuthor() {
|
|
30
|
-
try {
|
|
31
|
-
return (execSync("git config user.name", {
|
|
32
|
-
encoding: "utf8",
|
|
33
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
34
|
-
}).trim() || "Your Name");
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return "Your Name";
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
export function getDefaultAnswers(projectName, templateId) {
|
|
41
|
-
const template = isBuiltInTemplateId(templateId) ? getTemplateById(templateId) : null;
|
|
42
|
-
const slugDefault = normalizeBlockSlug(projectName) || "my-wp-typia-block";
|
|
43
|
-
return {
|
|
44
|
-
author: detectAuthor(),
|
|
45
|
-
dataStorageMode: templateId === "persistence" ? "custom-table" : undefined,
|
|
46
|
-
description: template?.description ?? "A WordPress block scaffolded from a remote template",
|
|
47
|
-
namespace: slugDefault,
|
|
48
|
-
persistencePolicy: templateId === "persistence" ? "authenticated" : undefined,
|
|
49
|
-
phpPrefix: toSnakeCase(slugDefault),
|
|
50
|
-
slug: slugDefault,
|
|
51
|
-
textDomain: slugDefault,
|
|
52
|
-
title: toTitleCase(slugDefault),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
27
|
function normalizeTemplateSelection(templateId) {
|
|
56
28
|
return templateId === WORKSPACE_TEMPLATE_ALIAS
|
|
57
29
|
? OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE
|
|
58
30
|
: templateId;
|
|
59
31
|
}
|
|
60
|
-
export async function resolveTemplateId({ templateId, yes = false, isInteractive = false, selectTemplate, }) {
|
|
61
|
-
if (templateId) {
|
|
62
|
-
const normalizedTemplateId = normalizeTemplateSelection(templateId);
|
|
63
|
-
if (isRemovedBuiltInTemplateId(templateId)) {
|
|
64
|
-
throw new Error(getRemovedBuiltInTemplateMessage(templateId));
|
|
65
|
-
}
|
|
66
|
-
if (isBuiltInTemplateId(normalizedTemplateId)) {
|
|
67
|
-
return getTemplateById(normalizedTemplateId).id;
|
|
68
|
-
}
|
|
69
|
-
return normalizedTemplateId;
|
|
70
|
-
}
|
|
71
|
-
if (yes) {
|
|
72
|
-
return "basic";
|
|
73
|
-
}
|
|
74
|
-
if (!isInteractive || !selectTemplate) {
|
|
75
|
-
throw new Error(`Template is required in non-interactive mode. Use --template <${TEMPLATE_IDS.join("|")}|./path|github:owner/repo/path[#ref]|npm-package>.`);
|
|
76
|
-
}
|
|
77
|
-
return normalizeTemplateSelection(await selectTemplate());
|
|
78
|
-
}
|
|
79
|
-
export async function resolvePackageManagerId({ packageManager, yes = false, isInteractive = false, selectPackageManager, }) {
|
|
80
|
-
if (packageManager) {
|
|
81
|
-
return getPackageManager(packageManager).id;
|
|
82
|
-
}
|
|
83
|
-
if (yes) {
|
|
84
|
-
return "npm";
|
|
85
|
-
}
|
|
86
|
-
if (!isInteractive || !selectPackageManager) {
|
|
87
|
-
throw new Error(`Package manager is required in non-interactive mode. Use --package-manager <${PACKAGE_MANAGER_IDS.join("|")}>.`);
|
|
88
|
-
}
|
|
89
|
-
return selectPackageManager();
|
|
90
|
-
}
|
|
91
|
-
export async function collectScaffoldAnswers({ projectName, templateId, yes = false, dataStorageMode, namespace, persistencePolicy, phpPrefix, promptText, textDomain, }) {
|
|
92
|
-
const defaults = getDefaultAnswers(projectName, templateId);
|
|
93
|
-
if (yes) {
|
|
94
|
-
const identifiers = resolveScaffoldIdentifiers({
|
|
95
|
-
namespace: namespace ?? defaults.namespace,
|
|
96
|
-
phpPrefix,
|
|
97
|
-
slug: defaults.slug,
|
|
98
|
-
textDomain,
|
|
99
|
-
});
|
|
100
|
-
return {
|
|
101
|
-
...defaults,
|
|
102
|
-
dataStorageMode: dataStorageMode ?? defaults.dataStorageMode,
|
|
103
|
-
namespace: identifiers.namespace,
|
|
104
|
-
persistencePolicy: persistencePolicy ?? defaults.persistencePolicy,
|
|
105
|
-
phpPrefix: identifiers.phpPrefix,
|
|
106
|
-
textDomain: identifiers.textDomain,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
if (!promptText) {
|
|
110
|
-
throw new Error("Interactive answers require a promptText callback.");
|
|
111
|
-
}
|
|
112
|
-
const identifiers = resolveScaffoldIdentifiers({
|
|
113
|
-
namespace: namespace ?? (await promptText("Namespace", defaults.namespace, validateNamespace)),
|
|
114
|
-
phpPrefix,
|
|
115
|
-
slug: await promptText("Block slug", defaults.slug, validateBlockSlug),
|
|
116
|
-
textDomain,
|
|
117
|
-
});
|
|
118
|
-
return {
|
|
119
|
-
author: await promptText("Author", defaults.author),
|
|
120
|
-
dataStorageMode: dataStorageMode ?? defaults.dataStorageMode,
|
|
121
|
-
description: await promptText("Description", defaults.description),
|
|
122
|
-
namespace: identifiers.namespace,
|
|
123
|
-
persistencePolicy: persistencePolicy ?? defaults.persistencePolicy,
|
|
124
|
-
phpPrefix: identifiers.phpPrefix,
|
|
125
|
-
slug: identifiers.slug,
|
|
126
|
-
textDomain: identifiers.textDomain,
|
|
127
|
-
title: await promptText("Block title", toTitleCase(identifiers.slug)),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
export function getTemplateVariables(templateId, answers) {
|
|
131
|
-
if (isBuiltInTemplateId(templateId)) {
|
|
132
|
-
return buildTemplateVariablesFromBlockSpec(createBuiltInBlockSpec({
|
|
133
|
-
answers,
|
|
134
|
-
dataStorageMode: answers.dataStorageMode,
|
|
135
|
-
persistencePolicy: answers.persistencePolicy,
|
|
136
|
-
templateId,
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
|
|
140
|
-
const template = isBuiltInTemplateId(templateId) ? getTemplateById(templateId) : null;
|
|
141
|
-
const metadataDefaults = isBuiltInTemplateId(templateId)
|
|
142
|
-
? getBuiltInTemplateMetadataDefaults(templateId)
|
|
143
|
-
: null;
|
|
144
|
-
const identifiers = resolveScaffoldIdentifiers({
|
|
145
|
-
namespace: answers.namespace,
|
|
146
|
-
phpPrefix: answers.phpPrefix,
|
|
147
|
-
slug: answers.slug,
|
|
148
|
-
textDomain: answers.textDomain,
|
|
149
|
-
});
|
|
150
|
-
const slug = identifiers.slug;
|
|
151
|
-
const slugSnakeCase = toSnakeCase(slug);
|
|
152
|
-
const pascalCase = toPascalCase(slug);
|
|
153
|
-
const title = answers.title.trim();
|
|
154
|
-
const namespace = identifiers.namespace;
|
|
155
|
-
const description = answers.description.trim();
|
|
156
|
-
const textDomain = identifiers.textDomain;
|
|
157
|
-
const phpPrefix = identifiers.phpPrefix;
|
|
158
|
-
const phpPrefixUpper = phpPrefix.toUpperCase();
|
|
159
|
-
const compoundChildTitle = `${title} Item`;
|
|
160
|
-
const cssClassName = buildBlockCssClassName(namespace, slug);
|
|
161
|
-
const compoundChildCssClassName = buildBlockCssClassName(namespace, `${slug}-item`);
|
|
162
|
-
const compoundPersistenceEnabled = templateId === "persistence"
|
|
163
|
-
? true
|
|
164
|
-
: templateId === "compound"
|
|
165
|
-
? Boolean(answers.dataStorageMode || answers.persistencePolicy)
|
|
166
|
-
: false;
|
|
167
|
-
const dataStorageMode = templateId === "persistence" || compoundPersistenceEnabled
|
|
168
|
-
? answers.dataStorageMode ?? "custom-table"
|
|
169
|
-
: "custom-table";
|
|
170
|
-
const persistencePolicy = templateId === "persistence" || compoundPersistenceEnabled
|
|
171
|
-
? answers.persistencePolicy ?? "authenticated"
|
|
172
|
-
: "authenticated";
|
|
173
|
-
return {
|
|
174
|
-
apiClientPackageVersion,
|
|
175
|
-
author: answers.author.trim(),
|
|
176
|
-
blockRuntimePackageVersion,
|
|
177
|
-
blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
|
|
178
|
-
blockTypesPackageVersion,
|
|
179
|
-
category: metadataDefaults?.category ?? template?.defaultCategory ?? "widgets",
|
|
180
|
-
icon: metadataDefaults?.icon ?? "smiley",
|
|
181
|
-
compoundChildTitle,
|
|
182
|
-
compoundChildCategory: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.category,
|
|
183
|
-
compoundChildCssClassName,
|
|
184
|
-
compoundChildIcon: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.icon,
|
|
185
|
-
compoundChildTitleJson: JSON.stringify(compoundChildTitle),
|
|
186
|
-
compoundPersistenceEnabled: compoundPersistenceEnabled ? "true" : "false",
|
|
187
|
-
projectToolsPackageVersion,
|
|
188
|
-
cssClassName,
|
|
189
|
-
dataStorageMode,
|
|
190
|
-
dashCase: slug,
|
|
191
|
-
description,
|
|
192
|
-
frontendCssClassName: buildFrontendCssClassName(cssClassName),
|
|
193
|
-
isAuthenticatedPersistencePolicy: persistencePolicy === "authenticated" ? "true" : "false",
|
|
194
|
-
isPublicPersistencePolicy: persistencePolicy === "public" ? "true" : "false",
|
|
195
|
-
bootstrapCredentialDeclarations: persistencePolicy === "public"
|
|
196
|
-
? "publicWriteExpiresAt?: number & tags.Type< 'uint32' >;\n\tpublicWriteToken?: string & tags.MinLength< 1 > & tags.MaxLength< 512 >;"
|
|
197
|
-
: "restNonce?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
198
|
-
persistencePolicyDescriptionJson: JSON.stringify(persistencePolicy === "authenticated"
|
|
199
|
-
? "Writes require a logged-in user and a valid REST nonce."
|
|
200
|
-
: "Anonymous writes use signed short-lived public tokens, per-request ids, and coarse rate limiting."),
|
|
201
|
-
keyword: slug.replace(/-/g, " "),
|
|
202
|
-
namespace,
|
|
203
|
-
needsMigration: "{{needsMigration}}",
|
|
204
|
-
pascalCase,
|
|
205
|
-
phpPrefix,
|
|
206
|
-
phpPrefixUpper,
|
|
207
|
-
restPackageVersion,
|
|
208
|
-
publicWriteRequestIdDeclaration: persistencePolicy === "public"
|
|
209
|
-
? "publicWriteRequestId: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;"
|
|
210
|
-
: "publicWriteRequestId?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
211
|
-
restWriteAuthIntent: persistencePolicy === "public"
|
|
212
|
-
? "public-write-protected"
|
|
213
|
-
: "authenticated",
|
|
214
|
-
restWriteAuthMechanism: persistencePolicy === "public" ? "public-signed-token" : "rest-nonce",
|
|
215
|
-
restWriteAuthMode: persistencePolicy === "public" ? "public-signed-token" : "authenticated-rest-nonce",
|
|
216
|
-
slug,
|
|
217
|
-
slugCamelCase: pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1),
|
|
218
|
-
slugKebabCase: slug,
|
|
219
|
-
slugSnakeCase,
|
|
220
|
-
textDomain,
|
|
221
|
-
textdomain: textDomain,
|
|
222
|
-
title,
|
|
223
|
-
titleJson: JSON.stringify(title),
|
|
224
|
-
titleCase: pascalCase,
|
|
225
|
-
persistencePolicy,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
32
|
export async function scaffoldProject({ projectDir, templateId, answers, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, repositoryReference, cwd = process.cwd(), allowExistingDir = false, noInstall = false, installDependencies = undefined, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
|
|
229
33
|
const resolvedTemplateId = normalizeTemplateSelection(templateId);
|
|
230
34
|
const resolvedPackageManager = getPackageManager(packageManager).id;
|
|
@@ -22,6 +22,10 @@ export declare const BUILTIN_TEMPLATE_METADATA_DEFAULTS: Readonly<{
|
|
|
22
22
|
category: "widgets";
|
|
23
23
|
icon: "screenoptions";
|
|
24
24
|
}>;
|
|
25
|
+
"query-loop": Readonly<{
|
|
26
|
+
category: "widgets";
|
|
27
|
+
icon: "query-pagination";
|
|
28
|
+
}>;
|
|
25
29
|
}>;
|
|
26
30
|
/**
|
|
27
31
|
* Shared hidden child block metadata defaults for compound scaffolds.
|
|
@@ -56,6 +60,9 @@ export declare function getBuiltInTemplateMetadataDefaults(templateId: keyof typ
|
|
|
56
60
|
}> | Readonly<{
|
|
57
61
|
category: "widgets";
|
|
58
62
|
icon: "screenoptions";
|
|
63
|
+
}> | Readonly<{
|
|
64
|
+
category: "widgets";
|
|
65
|
+
icon: "query-pagination";
|
|
59
66
|
}>;
|
|
60
67
|
/**
|
|
61
68
|
* Checks whether a template id points at a removed built-in scaffold.
|
|
@@ -22,6 +22,10 @@ export const BUILTIN_TEMPLATE_METADATA_DEFAULTS = Object.freeze({
|
|
|
22
22
|
category: "widgets",
|
|
23
23
|
icon: "screenoptions",
|
|
24
24
|
}),
|
|
25
|
+
"query-loop": Object.freeze({
|
|
26
|
+
category: "widgets",
|
|
27
|
+
icon: "query-pagination",
|
|
28
|
+
}),
|
|
25
29
|
});
|
|
26
30
|
/**
|
|
27
31
|
* Shared hidden child block metadata defaults for compound scaffolds.
|
|
@@ -14,7 +14,7 @@ export declare const SHARED_MIGRATION_UI_TEMPLATE_ROOT: string;
|
|
|
14
14
|
export declare const SHARED_WORKSPACE_TEMPLATE_ROOT: string;
|
|
15
15
|
export declare const SHARED_TEST_PRESET_TEMPLATE_ROOT: string;
|
|
16
16
|
export declare const SHARED_WP_ENV_PRESET_TEMPLATE_ROOT: string;
|
|
17
|
-
export declare const BUILTIN_TEMPLATE_IDS: readonly ["basic", "interactivity", "persistence", "compound"];
|
|
17
|
+
export declare const BUILTIN_TEMPLATE_IDS: readonly ["basic", "interactivity", "persistence", "compound", "query-loop"];
|
|
18
18
|
export declare const OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE = "@wp-typia/create-workspace-template";
|
|
19
19
|
export type BuiltInTemplateId = (typeof BUILTIN_TEMPLATE_IDS)[number];
|
|
20
20
|
export type PersistencePolicy = "authenticated" | "public";
|
|
@@ -40,7 +40,13 @@ export const SHARED_MIGRATION_UI_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT,
|
|
|
40
40
|
export const SHARED_WORKSPACE_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "workspace");
|
|
41
41
|
export const SHARED_TEST_PRESET_TEMPLATE_ROOT = path.join(SHARED_PRESET_TEMPLATE_ROOT, "test-preset");
|
|
42
42
|
export const SHARED_WP_ENV_PRESET_TEMPLATE_ROOT = path.join(SHARED_PRESET_TEMPLATE_ROOT, "wp-env");
|
|
43
|
-
export const BUILTIN_TEMPLATE_IDS = [
|
|
43
|
+
export const BUILTIN_TEMPLATE_IDS = [
|
|
44
|
+
"basic",
|
|
45
|
+
"interactivity",
|
|
46
|
+
"persistence",
|
|
47
|
+
"compound",
|
|
48
|
+
"query-loop",
|
|
49
|
+
];
|
|
44
50
|
export const OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE = "@wp-typia/create-workspace-template";
|
|
45
51
|
export const TEMPLATE_REGISTRY = Object.freeze([
|
|
46
52
|
{
|
|
@@ -71,6 +77,13 @@ export const TEMPLATE_REGISTRY = Object.freeze([
|
|
|
71
77
|
features: ["InnerBlocks", "Hidden child blocks", "Optional persistence layer"],
|
|
72
78
|
templateDir: path.join(TEMPLATE_ROOT, "compound"),
|
|
73
79
|
},
|
|
80
|
+
{
|
|
81
|
+
id: "query-loop",
|
|
82
|
+
description: "A Query Loop block variation scaffold with stable namespace-based identity, inline starter layout, connected pattern presets, custom query seams, and runtime parity hooks",
|
|
83
|
+
defaultCategory: getBuiltInTemplateMetadataDefaults("query-loop").category,
|
|
84
|
+
features: ["core/query variation", "Default innerBlocks", "Connected patterns", "Custom query hooks", "Runtime parity hooks", "Allowed controls"],
|
|
85
|
+
templateDir: path.join(TEMPLATE_ROOT, "query-loop"),
|
|
86
|
+
},
|
|
74
87
|
{
|
|
75
88
|
id: OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE,
|
|
76
89
|
description: "The official empty workspace template that powers `wp-typia add ...` workflows",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wp-typia/project-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Project orchestration and programmatic tooling for wp-typia",
|
|
5
5
|
"packageManager": "bun@1.3.11",
|
|
6
6
|
"type": "module",
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
122
|
"@wp-typia/api-client": "^0.4.5",
|
|
123
|
-
"@wp-typia/block-runtime": "^0.4.
|
|
124
|
-
"@wp-typia/rest": "^0.3.
|
|
123
|
+
"@wp-typia/block-runtime": "^0.4.10",
|
|
124
|
+
"@wp-typia/rest": "^0.3.10",
|
|
125
125
|
"@wp-typia/block-types": "^0.2.4",
|
|
126
126
|
"mustache": "^4.2.0",
|
|
127
127
|
"npm-package-arg": "^13.0.0",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
if ( ! defined( 'ABSPATH' ) ) {
|
|
3
|
+
exit;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function {{phpPrefix}}_get_query_loop_variation_name() {
|
|
7
|
+
return '{{namespace}}/{{slug}}';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function {{phpPrefix}}_get_query_loop_variation_marker_key() {
|
|
11
|
+
return 'wpTypiaVariation';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function {{phpPrefix}}_extract_query_loop_request_query( WP_REST_Request $request ) {
|
|
15
|
+
$params = $request->get_params();
|
|
16
|
+
|
|
17
|
+
if ( isset( $params['query'] ) && is_array( $params['query'] ) ) {
|
|
18
|
+
return $params['query'];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return is_array( $params ) ? $params : array();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function {{phpPrefix}}_map_query_loop_custom_query_vars( array $query_vars, array $source_query ) {
|
|
25
|
+
unset( $query_vars[ {{phpPrefix}}_get_query_loop_variation_marker_key() ] );
|
|
26
|
+
|
|
27
|
+
$allowed_custom_query_values = array(
|
|
28
|
+
// 'featuredOnly' => isset( $source_query['featuredOnly'] ) ? (bool) $source_query['featuredOnly'] : null,
|
|
29
|
+
// 'eventStatus' => isset( $source_query['eventStatus'] ) ? sanitize_text_field( $source_query['eventStatus'] ) : null,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Mirror only known custom query keys from `src/query-extension.ts` here when
|
|
33
|
+
// they should affect frontend rendering or editor preview requests. Sanitize
|
|
34
|
+
// and cast values before copying them into WP_Query vars; do not merge the
|
|
35
|
+
// raw source query wholesale.
|
|
36
|
+
foreach ( $allowed_custom_query_values as $key => $value ) {
|
|
37
|
+
if ( null === $value ) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
$query_vars[ $key ] = $value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return $query_vars;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function {{phpPrefix}}_filter_query_loop_block_query_vars( $query_vars, $block, $page ) {
|
|
48
|
+
if ( ! is_array( $query_vars ) ) {
|
|
49
|
+
return $query_vars;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$attrs = is_array( $block ) && isset( $block['attrs'] ) && is_array( $block['attrs'] )
|
|
53
|
+
? $block['attrs']
|
|
54
|
+
: array();
|
|
55
|
+
$namespace = isset( $attrs['namespace'] ) && is_string( $attrs['namespace'] )
|
|
56
|
+
? $attrs['namespace']
|
|
57
|
+
: null;
|
|
58
|
+
$source_query = isset( $attrs['query'] ) && is_array( $attrs['query'] )
|
|
59
|
+
? $attrs['query']
|
|
60
|
+
: array();
|
|
61
|
+
|
|
62
|
+
if ( {{phpPrefix}}_get_query_loop_variation_name() !== $namespace ) {
|
|
63
|
+
return $query_vars;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {{phpPrefix}}_map_query_loop_custom_query_vars( $query_vars, $source_query );
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function {{phpPrefix}}_filter_query_loop_editor_preview_query_vars( $query_vars, $request ) {
|
|
70
|
+
if ( ! is_array( $query_vars ) || ! $request instanceof WP_REST_Request ) {
|
|
71
|
+
return $query_vars;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$source_query = {{phpPrefix}}_extract_query_loop_request_query( $request );
|
|
75
|
+
$marker_key = {{phpPrefix}}_get_query_loop_variation_marker_key();
|
|
76
|
+
$variation_name = isset( $source_query[ $marker_key ] ) && is_string( $source_query[ $marker_key ] )
|
|
77
|
+
? $source_query[ $marker_key ]
|
|
78
|
+
: null;
|
|
79
|
+
|
|
80
|
+
if ( {{phpPrefix}}_get_query_loop_variation_name() !== $variation_name ) {
|
|
81
|
+
return $query_vars;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {{phpPrefix}}_map_query_loop_custom_query_vars( $query_vars, $source_query );
|
|
85
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{slug}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"packageManager": "bun@1.3.11",
|
|
5
|
+
"description": "{{description}}",
|
|
6
|
+
"author": "{{author}}",
|
|
7
|
+
"license": "GPL-2.0-or-later",
|
|
8
|
+
"main": "build/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "wp-scripts build --experimental-modules",
|
|
11
|
+
"start": "wp-scripts start --experimental-modules",
|
|
12
|
+
"dev": "wp-scripts start --experimental-modules",
|
|
13
|
+
"lint:js": "wp-scripts lint-js",
|
|
14
|
+
"lint": "bun run lint:js",
|
|
15
|
+
"format": "wp-scripts format",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@wp-typia/block-types": "{{blockTypesPackageVersion}}",
|
|
20
|
+
"@types/wordpress__block-editor": "^11.5.17",
|
|
21
|
+
"@types/wordpress__blocks": "^12.5.18",
|
|
22
|
+
"@wordpress/browserslist-config": "^6.42.0",
|
|
23
|
+
"@wordpress/scripts": "^30.22.0",
|
|
24
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
25
|
+
"prettier": "3.8.2",
|
|
26
|
+
"typescript": "^5.9.2"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@wordpress/blocks": "^15.2.0",
|
|
30
|
+
"@wordpress/i18n": "^6.2.0"
|
|
31
|
+
}
|
|
32
|
+
}
|