create-wp-typia 0.1.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 +33 -0
- package/dist/cli.js +87837 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/lib/entry.js +29 -0
- package/lib/node-cli.js +326 -0
- package/lib/package-managers.d.ts +29 -0
- package/lib/package-managers.js +170 -0
- package/lib/scaffold.d.ts +64 -0
- package/lib/scaffold.js +565 -0
- package/lib/template-registry.d.ts +18 -0
- package/lib/template-registry.js +58 -0
- package/package.json +64 -0
- package/src/cli.ts +329 -0
- package/templates/advanced/README.md.mustache +70 -0
- package/templates/advanced/block.json.mustache +42 -0
- package/templates/advanced/index.js +21 -0
- package/templates/advanced/package.json.mustache +48 -0
- package/templates/advanced/scripts/generate-migrations.ts.mustache +267 -0
- package/templates/advanced/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/advanced/scripts/migration-cli.ts.mustache +260 -0
- package/templates/advanced/scripts/sync-types-to-block-json.ts.mustache +25 -0
- package/templates/advanced/src/admin/migration-dashboard.tsx.mustache +450 -0
- package/templates/advanced/src/components/ErrorBoundary.tsx.mustache +47 -0
- package/templates/advanced/src/deprecated.ts.mustache +184 -0
- package/templates/advanced/src/edit.tsx.mustache +93 -0
- package/templates/advanced/src/hooks/useDebounce.ts.mustache +20 -0
- package/templates/advanced/src/hooks/useLocalStorage.ts.mustache +31 -0
- package/templates/advanced/src/hooks.ts.mustache +56 -0
- package/templates/advanced/src/index.tsx.mustache +16 -0
- package/templates/advanced/src/migration-detector.ts.mustache +417 -0
- package/templates/advanced/src/migrations/index.ts.mustache +361 -0
- package/templates/advanced/src/save.tsx.mustache +40 -0
- package/templates/advanced/src/style.scss.mustache +84 -0
- package/templates/advanced/src/types/versions.ts.mustache +108 -0
- package/templates/advanced/src/types.ts.mustache +45 -0
- package/templates/advanced/src/utils/classnames.ts.mustache +51 -0
- package/templates/advanced/src/utils/debounce.ts.mustache +37 -0
- package/templates/advanced/src/utils/index.ts.mustache +7 -0
- package/templates/advanced/src/utils/uuid.ts.mustache +17 -0
- package/templates/advanced/src/validators.ts.mustache +39 -0
- package/templates/advanced/src/view.ts.mustache +59 -0
- package/templates/advanced/tsconfig.json.mustache +9 -0
- package/templates/advanced/webpack.config.js.mustache +85 -0
- package/templates/basic/package.json.mustache +39 -0
- package/templates/basic/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/basic/scripts/sync-types-to-block-json.ts +25 -0
- package/templates/basic/src/block.json +51 -0
- package/templates/basic/src/edit.tsx +85 -0
- package/templates/basic/src/hooks.ts +75 -0
- package/templates/basic/src/index.tsx +37 -0
- package/templates/basic/src/save.tsx +27 -0
- package/templates/basic/src/style.scss +42 -0
- package/templates/basic/src/types.ts +47 -0
- package/templates/basic/src/validators.ts +39 -0
- package/templates/basic/tsconfig.json +20 -0
- package/templates/basic/webpack.config.js +85 -0
- package/templates/full/package.json.mustache +40 -0
- package/templates/full/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/full/scripts/sync-types-to-block-json.ts.mustache +25 -0
- package/templates/full/src/block.json.mustache +121 -0
- package/templates/full/src/edit.tsx.mustache +300 -0
- package/templates/full/src/editor.scss.mustache +251 -0
- package/templates/full/src/hooks.ts.mustache +140 -0
- package/templates/full/src/index.tsx.mustache +27 -0
- package/templates/full/src/save.tsx.mustache +39 -0
- package/templates/full/src/style.scss.mustache +224 -0
- package/templates/full/src/types.ts.mustache +34 -0
- package/templates/full/src/validators.ts.mustache +84 -0
- package/templates/full/tsconfig.json.mustache +9 -0
- package/templates/full/webpack.config.js.mustache +85 -0
- package/templates/interactivity/package.json.mustache +41 -0
- package/templates/interactivity/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/interactivity/scripts/sync-types-to-block-json.ts.mustache +25 -0
- package/templates/interactivity/src/block.json.mustache +75 -0
- package/templates/interactivity/src/edit.tsx.mustache +206 -0
- package/templates/interactivity/src/interactivity.ts.mustache +183 -0
- package/templates/interactivity/src/save.tsx.mustache +87 -0
- package/templates/interactivity/src/types.ts.mustache +29 -0
- package/templates/interactivity/tsconfig.json.mustache +9 -0
- package/templates/interactivity/webpack.config.js.mustache +85 -0
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-wp-typia",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bun-first scaffolding CLI for WordPress Typia block templates",
|
|
5
|
+
"packageManager": "bun@1.3.10",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "lib/scaffold.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"create-wp-typia": "lib/entry.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"lib/",
|
|
14
|
+
"src/",
|
|
15
|
+
"templates/",
|
|
16
|
+
"README.md",
|
|
17
|
+
"package.json"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "bunli build --runtime node",
|
|
21
|
+
"dev": "bunli dev --runtime node",
|
|
22
|
+
"test": "bunli test --pattern ./tests/create-cli.test.ts",
|
|
23
|
+
"test:coverage": "bunli test --pattern ./tests/create-cli.test.ts --coverage && bun test tests/create-cli.test.ts --coverage --coverage-reporter=lcov --coverage-dir=coverage",
|
|
24
|
+
"clean": "rm -rf dist",
|
|
25
|
+
"prepack": "bun run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"wordpress",
|
|
29
|
+
"gutenberg",
|
|
30
|
+
"typia",
|
|
31
|
+
"template",
|
|
32
|
+
"scaffold",
|
|
33
|
+
"cli",
|
|
34
|
+
"bun"
|
|
35
|
+
],
|
|
36
|
+
"author": "imjlk",
|
|
37
|
+
"license": "GPL-2.0-or-later",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/imjlk/wp-typia-templates.git",
|
|
41
|
+
"directory": "packages/create-wp-typia"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/imjlk/wp-typia-templates/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/imjlk/wp-typia-templates/tree/main/packages/create-wp-typia#readme",
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=16.0.0",
|
|
52
|
+
"bun": ">=1.3.10"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@bunli/core": "^0.8.2",
|
|
56
|
+
"react": "^19.2.4",
|
|
57
|
+
"react-dom": "^19.2.4",
|
|
58
|
+
"zod": "^3.25.76"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"bunli": "^0.8.2",
|
|
62
|
+
"typescript": "^5.9.2"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { access, constants as fsConstants, rm, writeFile } from "node:fs/promises";
|
|
7
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
8
|
+
|
|
9
|
+
import { createCLI, defineCommand, defineGroup, option } from "@bunli/core";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
import packageJson from "../package.json";
|
|
13
|
+
import {
|
|
14
|
+
collectScaffoldAnswers,
|
|
15
|
+
resolvePackageManagerId,
|
|
16
|
+
resolveTemplateId,
|
|
17
|
+
scaffoldProject,
|
|
18
|
+
} from "../lib/scaffold.js";
|
|
19
|
+
import {
|
|
20
|
+
PACKAGE_MANAGER_IDS,
|
|
21
|
+
formatInstallCommand,
|
|
22
|
+
formatRunScript,
|
|
23
|
+
getPackageManager,
|
|
24
|
+
getPackageManagerSelectOptions,
|
|
25
|
+
} from "../lib/package-managers.js";
|
|
26
|
+
import {
|
|
27
|
+
TEMPLATE_IDS,
|
|
28
|
+
getTemplateById,
|
|
29
|
+
getTemplateSelectOptions,
|
|
30
|
+
listTemplates,
|
|
31
|
+
} from "../lib/template-registry.js";
|
|
32
|
+
|
|
33
|
+
const TOP_LEVEL_COMMANDS = new Set(["scaffold", "templates", "doctor"]);
|
|
34
|
+
type TemplateRecord = ReturnType<typeof getTemplateById>;
|
|
35
|
+
|
|
36
|
+
function normalizeRootArgv(argv: string[]) {
|
|
37
|
+
if (argv.length === 0) {
|
|
38
|
+
return argv;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const [first] = argv;
|
|
42
|
+
if (!first || first.startsWith("-") || TOP_LEVEL_COMMANDS.has(first)) {
|
|
43
|
+
return argv;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return ["scaffold", ...argv];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatTemplateSummary(template: TemplateRecord) {
|
|
50
|
+
return `${template.id.padEnd(14)} ${template.description}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function collectInteractiveAnswers(prompt: any, projectName: string, templateId: string, yes: boolean) {
|
|
54
|
+
return collectScaffoldAnswers({
|
|
55
|
+
projectName,
|
|
56
|
+
templateId,
|
|
57
|
+
yes,
|
|
58
|
+
promptText: async (
|
|
59
|
+
message: string,
|
|
60
|
+
defaultValue: string,
|
|
61
|
+
validate?: (input: string) => boolean | string,
|
|
62
|
+
) =>
|
|
63
|
+
prompt.text(message, {
|
|
64
|
+
default: defaultValue,
|
|
65
|
+
validate: validate
|
|
66
|
+
? (value: string) => {
|
|
67
|
+
const result = validate(value);
|
|
68
|
+
return result === true ? true : result;
|
|
69
|
+
}
|
|
70
|
+
: undefined,
|
|
71
|
+
}),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function runShellCommand(command: string, cwd: string) {
|
|
76
|
+
execSync(command, {
|
|
77
|
+
cwd,
|
|
78
|
+
stdio: "inherit",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function compareMajorVersion(actualVersion: string, minimumMajor: number) {
|
|
83
|
+
const parsed = Number.parseInt(actualVersion.replace(/^v/, "").split(".")[0] ?? "", 10);
|
|
84
|
+
return Number.isFinite(parsed) && parsed >= minimumMajor;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readCommandVersion(command: string, args: string[] = ["--version"]) {
|
|
88
|
+
try {
|
|
89
|
+
return execFileSync(command, args, {
|
|
90
|
+
encoding: "utf8",
|
|
91
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
92
|
+
}).trim();
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function checkWritableDirectory(directory: string) {
|
|
99
|
+
try {
|
|
100
|
+
await access(directory, fsConstants.W_OK);
|
|
101
|
+
return true;
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function checkTempDirectory() {
|
|
108
|
+
const tempFile = path.join(os.tmpdir(), `create-wp-typia-${Date.now()}.tmp`);
|
|
109
|
+
try {
|
|
110
|
+
await writeFile(tempFile, "ok", "utf8");
|
|
111
|
+
await rm(tempFile, { force: true });
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function printDoctorResult(colors: any, status: "pass" | "warn" | "fail", label: string, detail: string) {
|
|
119
|
+
const icon =
|
|
120
|
+
status === "pass" ? colors.green("PASS") : status === "warn" ? colors.yellow("WARN") : colors.red("FAIL");
|
|
121
|
+
console.log(`${icon} ${label}: ${detail}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const scaffoldCommand = defineCommand({
|
|
125
|
+
name: "scaffold",
|
|
126
|
+
description: "Scaffold a WordPress Typia block project",
|
|
127
|
+
options: {
|
|
128
|
+
template: option(z.enum(TEMPLATE_IDS as [string, ...string[]]).optional(), {
|
|
129
|
+
description: "Template ID to scaffold",
|
|
130
|
+
short: "t",
|
|
131
|
+
}),
|
|
132
|
+
yes: option(z.coerce.boolean().default(false), {
|
|
133
|
+
description: "Skip prompts and use defaults",
|
|
134
|
+
short: "y",
|
|
135
|
+
}),
|
|
136
|
+
noInstall: option(z.coerce.boolean().default(false), {
|
|
137
|
+
description: "Skip dependency installation",
|
|
138
|
+
}),
|
|
139
|
+
packageManager: option(z.enum(PACKAGE_MANAGER_IDS as [string, ...string[]]).optional(), {
|
|
140
|
+
description: "Package manager for the generated project",
|
|
141
|
+
short: "p",
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
handler: async ({ colors, cwd, flags, positional, prompt, spinner, terminal }) => {
|
|
145
|
+
const projectInput = positional[0];
|
|
146
|
+
if (!projectInput) {
|
|
147
|
+
throw new Error("Project directory is required. Usage: create-wp-typia <project-dir>");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const projectDir = path.resolve(cwd, projectInput);
|
|
151
|
+
const projectName = path.basename(projectDir);
|
|
152
|
+
const templateId = await resolveTemplateId({
|
|
153
|
+
templateId: flags.template,
|
|
154
|
+
yes: flags.yes,
|
|
155
|
+
isInteractive: terminal.isInteractive,
|
|
156
|
+
selectTemplate: () =>
|
|
157
|
+
prompt.select("Select a template", {
|
|
158
|
+
default: "basic",
|
|
159
|
+
options: getTemplateSelectOptions(),
|
|
160
|
+
}),
|
|
161
|
+
});
|
|
162
|
+
const packageManager = await resolvePackageManagerId({
|
|
163
|
+
packageManager: flags.packageManager as "bun" | "npm" | "pnpm" | "yarn" | undefined,
|
|
164
|
+
yes: flags.yes,
|
|
165
|
+
isInteractive: terminal.isInteractive,
|
|
166
|
+
selectPackageManager: () =>
|
|
167
|
+
prompt.select("Choose a package manager", {
|
|
168
|
+
default: "bun",
|
|
169
|
+
options: getPackageManagerSelectOptions(),
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
const answers = await collectInteractiveAnswers(prompt, projectName, templateId, flags.yes);
|
|
173
|
+
|
|
174
|
+
const copySpinner = spinner({ text: `Scaffolding ${templateId} template...` });
|
|
175
|
+
copySpinner.start();
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const result = await scaffoldProject({
|
|
179
|
+
answers,
|
|
180
|
+
installDependencies: flags.noInstall
|
|
181
|
+
? undefined
|
|
182
|
+
: async ({ packageManager: selectedPackageManager, projectDir: targetDir }) => {
|
|
183
|
+
const installSpinner = spinner({
|
|
184
|
+
text: `Installing dependencies with ${getPackageManager(selectedPackageManager).label}...`,
|
|
185
|
+
});
|
|
186
|
+
installSpinner.start();
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await runShellCommand(formatInstallCommand(selectedPackageManager), targetDir);
|
|
190
|
+
installSpinner.succeed("Dependencies installed");
|
|
191
|
+
} catch (error) {
|
|
192
|
+
installSpinner.fail("Dependency installation failed");
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
noInstall: flags.noInstall,
|
|
197
|
+
packageManager,
|
|
198
|
+
projectDir,
|
|
199
|
+
templateId,
|
|
200
|
+
});
|
|
201
|
+
copySpinner.succeed(`Created ${result.variables.title}`);
|
|
202
|
+
|
|
203
|
+
console.log(`\n${colors.green("✓")} Project ready in ${projectDir}`);
|
|
204
|
+
console.log("Next steps:");
|
|
205
|
+
console.log(` cd ${path.isAbsolute(projectInput) ? projectDir : projectInput}`);
|
|
206
|
+
if (flags.noInstall) {
|
|
207
|
+
console.log(` ${formatInstallCommand(packageManager)}`);
|
|
208
|
+
}
|
|
209
|
+
console.log(` ${formatRunScript(packageManager, "start")}`);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
copySpinner.fail("Scaffolding failed");
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const templatesGroup = defineGroup({
|
|
218
|
+
name: "templates",
|
|
219
|
+
description: "Inspect available block templates",
|
|
220
|
+
commands: [
|
|
221
|
+
defineCommand({
|
|
222
|
+
name: "list",
|
|
223
|
+
description: "List available templates",
|
|
224
|
+
handler: async ({ colors }) => {
|
|
225
|
+
console.log(colors.bold("Available templates:\n"));
|
|
226
|
+
for (const template of listTemplates()) {
|
|
227
|
+
console.log(formatTemplateSummary(template));
|
|
228
|
+
console.log(` ${colors.dim(template.features.join(" • "))}`);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
}),
|
|
232
|
+
defineCommand({
|
|
233
|
+
name: "inspect",
|
|
234
|
+
description: "Inspect a template",
|
|
235
|
+
handler: async ({ colors, positional }) => {
|
|
236
|
+
const templateId = positional[0];
|
|
237
|
+
if (!templateId) {
|
|
238
|
+
throw new Error(`Template ID is required. Use one of: ${TEMPLATE_IDS.join(", ")}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const template = getTemplateById(templateId);
|
|
242
|
+
console.log(colors.bold(template.id));
|
|
243
|
+
console.log(template.description);
|
|
244
|
+
console.log(`Category: ${template.defaultCategory}`);
|
|
245
|
+
console.log(`Path: ${template.templateDir}`);
|
|
246
|
+
console.log(`Features: ${template.features.join(", ")}`);
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
],
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const doctorCommand = defineCommand({
|
|
253
|
+
name: "doctor",
|
|
254
|
+
description: "Check local CLI prerequisites",
|
|
255
|
+
handler: async ({ colors, cwd }) => {
|
|
256
|
+
const bunVersion = readCommandVersion("bun");
|
|
257
|
+
const nodeVersion = readCommandVersion("node");
|
|
258
|
+
const gitVersion = readCommandVersion("git");
|
|
259
|
+
const cwdWritable = await checkWritableDirectory(cwd);
|
|
260
|
+
const tempWritable = await checkTempDirectory();
|
|
261
|
+
let hasFailures = false;
|
|
262
|
+
|
|
263
|
+
printDoctorResult(
|
|
264
|
+
colors,
|
|
265
|
+
bunVersion && compareMajorVersion(bunVersion, 1) ? "pass" : "fail",
|
|
266
|
+
"Bun",
|
|
267
|
+
bunVersion ? `Detected ${bunVersion}` : "Not available",
|
|
268
|
+
);
|
|
269
|
+
hasFailures ||= !(bunVersion && compareMajorVersion(bunVersion, 1));
|
|
270
|
+
|
|
271
|
+
printDoctorResult(
|
|
272
|
+
colors,
|
|
273
|
+
nodeVersion && compareMajorVersion(nodeVersion, 16) ? "pass" : "fail",
|
|
274
|
+
"Node",
|
|
275
|
+
nodeVersion ? `Detected ${nodeVersion}` : "Not available",
|
|
276
|
+
);
|
|
277
|
+
hasFailures ||= !(nodeVersion && compareMajorVersion(nodeVersion, 16));
|
|
278
|
+
|
|
279
|
+
printDoctorResult(
|
|
280
|
+
colors,
|
|
281
|
+
gitVersion ? "pass" : "fail",
|
|
282
|
+
"git",
|
|
283
|
+
gitVersion ?? "Not available",
|
|
284
|
+
);
|
|
285
|
+
hasFailures ||= !gitVersion;
|
|
286
|
+
|
|
287
|
+
printDoctorResult(colors, cwdWritable ? "pass" : "fail", "Current directory", cwdWritable ? "Writable" : "Not writable");
|
|
288
|
+
hasFailures ||= !cwdWritable;
|
|
289
|
+
|
|
290
|
+
printDoctorResult(colors, tempWritable ? "pass" : "fail", "Temp directory", tempWritable ? "Writable" : "Not writable");
|
|
291
|
+
hasFailures ||= !tempWritable;
|
|
292
|
+
|
|
293
|
+
for (const template of listTemplates()) {
|
|
294
|
+
const hasAssets =
|
|
295
|
+
fs.existsSync(template.templateDir) &&
|
|
296
|
+
fs.existsSync(path.join(template.templateDir, "package.json.mustache"));
|
|
297
|
+
printDoctorResult(
|
|
298
|
+
colors,
|
|
299
|
+
hasAssets ? "pass" : "fail",
|
|
300
|
+
`Template ${template.id}`,
|
|
301
|
+
hasAssets ? template.templateDir : "Missing core template assets",
|
|
302
|
+
);
|
|
303
|
+
hasFailures ||= !hasAssets;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (hasFailures) {
|
|
307
|
+
throw new Error("Doctor found one or more failing checks.");
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
async function main() {
|
|
313
|
+
const cli = await createCLI({
|
|
314
|
+
description: packageJson.description,
|
|
315
|
+
name: packageJson.name,
|
|
316
|
+
version: packageJson.version,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
cli.command(scaffoldCommand);
|
|
320
|
+
cli.command(templatesGroup);
|
|
321
|
+
cli.command(doctorCommand);
|
|
322
|
+
|
|
323
|
+
await cli.run(normalizeRootArgv(process.argv.slice(2)));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
main().catch((error: unknown) => {
|
|
327
|
+
console.error("❌ create-wp-typia failed:", error instanceof Error ? error.message : error);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# {{title}}
|
|
2
|
+
|
|
3
|
+
A WordPress block with **Typia validation** and **Interactivity API** support.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **✅ Typia Validation**: Type-safe attributes with compile-time and runtime validation
|
|
8
|
+
- **⚡ Interactivity API**: Lightweight frontend interactions
|
|
9
|
+
- **🎯 Auto-Sync**: Types automatically sync to `block.json` and `typia.manifest.json`
|
|
10
|
+
- **🔧 TypeScript**: Full type safety throughout
|
|
11
|
+
|
|
12
|
+
## 🏗️ Development
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Install dependencies
|
|
16
|
+
bun install
|
|
17
|
+
|
|
18
|
+
# Development with hot reload
|
|
19
|
+
bun run start
|
|
20
|
+
|
|
21
|
+
# Build for production
|
|
22
|
+
bun run build
|
|
23
|
+
|
|
24
|
+
# Type checking
|
|
25
|
+
npx tsc --noEmit
|
|
26
|
+
|
|
27
|
+
# Sync block metadata manually
|
|
28
|
+
bun run sync-types
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 📝 Type System
|
|
32
|
+
|
|
33
|
+
Edit `src/types.ts` to define your block attributes:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
export interface {{titleCase}}Attributes {
|
|
37
|
+
content: string & tags.MinLength<1> & tags.Default<"Hello World">;
|
|
38
|
+
isVisible: boolean & tags.Default<true>;
|
|
39
|
+
count: number & tags.Minimum<0> & tags.Maximum<100>;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**`block.json` and `typia.manifest.json` will be automatically updated** with these attributes when you run `bun run start` or `bun run build`!
|
|
44
|
+
|
|
45
|
+
## 🎨 Styling
|
|
46
|
+
|
|
47
|
+
- `src/style.scss` - Frontend and editor styles
|
|
48
|
+
- Block CSS class: `.wp-block-{{slugKebabCase}}`
|
|
49
|
+
|
|
50
|
+
## ⚡ Interactivity
|
|
51
|
+
|
|
52
|
+
Frontend interactions are defined in `src/view.ts`:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const { state, actions } = store('{{namespace}}/{{slug}}', {
|
|
56
|
+
state: { isActive: false },
|
|
57
|
+
actions: { toggle() { state.isActive = !state.isActive; } }
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 📦 Generated Files
|
|
62
|
+
|
|
63
|
+
- `block.json` - WordPress-facing attribute metadata generated from TypeScript types
|
|
64
|
+
- `typia.manifest.json` - Full Typia constraint manifest for future PHP validation
|
|
65
|
+
- `build/` - Compiled JavaScript and CSS
|
|
66
|
+
- `src/types.ts` - **Edit this to change block attributes**
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
*Generated with [@wordpress/create-block](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/) and Typia validation*
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://schemas.wp.org/trunk/block.json",
|
|
3
|
+
"apiVersion": 3,
|
|
4
|
+
"name": "{{namespace}}/{{slug}}",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"title": "{{title}}",
|
|
7
|
+
"category": "{{category}}",
|
|
8
|
+
"icon": "{{dashicon}}",
|
|
9
|
+
"description": "{{description}}",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"typia",
|
|
12
|
+
"{{slug}}",
|
|
13
|
+
"custom"
|
|
14
|
+
],
|
|
15
|
+
"example": {
|
|
16
|
+
"attributes": {
|
|
17
|
+
"id": "Example id",
|
|
18
|
+
"version": 1,
|
|
19
|
+
"className": "Example className",
|
|
20
|
+
"content": "",
|
|
21
|
+
"alignment": "left",
|
|
22
|
+
"isVisible": true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"supports": {
|
|
26
|
+
"html": false,
|
|
27
|
+
"interactivity": {
|
|
28
|
+
"clientNavigation": true
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"textdomain": "{{textdomain}}",
|
|
32
|
+
"editorScript": "file:./index.js",
|
|
33
|
+
"editorStyle": "file:./index.css",
|
|
34
|
+
"style": "file:./style-index.css",
|
|
35
|
+
"viewScript": "file:./view.js",
|
|
36
|
+
"attributes": {
|
|
37
|
+
"content": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"default": ""
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { join } = require( 'path' );
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
templatesPath: join( __dirname ),
|
|
5
|
+
defaultValues: {
|
|
6
|
+
namespace: 'create-block',
|
|
7
|
+
category: 'widgets',
|
|
8
|
+
dashicon: 'admin-site-alt3',
|
|
9
|
+
textdomain: '',
|
|
10
|
+
editorScript: 'file:./index.js',
|
|
11
|
+
editorStyle: 'file:./index.css',
|
|
12
|
+
style: 'file:./style-index.css',
|
|
13
|
+
viewScript: 'file:./view.js',
|
|
14
|
+
},
|
|
15
|
+
variants: {
|
|
16
|
+
'typia': {
|
|
17
|
+
title: 'Typia Block',
|
|
18
|
+
description: 'A WordPress block with Typia validation and Interactivity API',
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{slugSnakeCase}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"packageManager": "bun@1.3.10",
|
|
5
|
+
"description": "{{description}}",
|
|
6
|
+
"author": "{{author}}",
|
|
7
|
+
"license": "GPL-2.0-or-later",
|
|
8
|
+
"main": "build/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
11
|
+
"generate-migrations": "tsx scripts/generate-migrations.ts",
|
|
12
|
+
"prebuild": "bun run sync-types && bun run generate-migrations",
|
|
13
|
+
"build": "wp-scripts build",
|
|
14
|
+
"start": "bun run sync-types && bun run generate-migrations && wp-scripts start",
|
|
15
|
+
"dev": "bun run start",
|
|
16
|
+
"test-migrations": "tsx -e \"import('./src/deprecated.js').then(m => m.migrationUtils.testMigration())\"",
|
|
17
|
+
"migration-stats": "tsx -e \"import('./src/deprecated.js').then(m => console.table(m.migrationUtils.getStats()))\"",
|
|
18
|
+
"migration": "tsx scripts/migration-cli.ts",
|
|
19
|
+
"migration:detect": "bun run migration detect",
|
|
20
|
+
"migration:scan": "bun run migration scan",
|
|
21
|
+
"migration:migrate": "bun run migration migrate",
|
|
22
|
+
"lint:js": "wp-scripts lint-js",
|
|
23
|
+
"lint:css": "wp-scripts lint-style",
|
|
24
|
+
"lint": "bun run lint:js && bun run lint:css",
|
|
25
|
+
"format": "wp-scripts format"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"ajv": "^8.18.0",
|
|
29
|
+
"@types/wordpress__block-editor": "^11.5.17",
|
|
30
|
+
"@types/wordpress__blocks": "^12.5.18",
|
|
31
|
+
"@types/wordpress__components": "^23.8.0",
|
|
32
|
+
"@wordpress/browserslist-config": "^6.42.0",
|
|
33
|
+
"@wordpress/scripts": "^30.22.0",
|
|
34
|
+
"@typia/unplugin": "^12.0.1",
|
|
35
|
+
"tsx": "^4.20.5",
|
|
36
|
+
"typescript": "^5.9.2",
|
|
37
|
+
"typia": "^12.0.1",
|
|
38
|
+
"commander": "^11.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@wordpress/block-editor": "^15.2.0",
|
|
42
|
+
"@wordpress/blocks": "^15.2.0",
|
|
43
|
+
"@wordpress/components": "^30.2.0",
|
|
44
|
+
"@wordpress/element": "^6.29.0",
|
|
45
|
+
"@wordpress/i18n": "^6.2.0",
|
|
46
|
+
"@wordpress/interactivity": "^6.29.0"
|
|
47
|
+
}
|
|
48
|
+
}
|