✨feature: First build
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* Block Name: {{BLOCK_NAME}}
|
* Block Name: {{BLOCK_NAME}}
|
||||||
*
|
*
|
||||||
* Add a description of your block. Shows up in the block inserter
|
* Add a description of your block. Shows up in the block inserter.
|
||||||
*
|
*
|
||||||
* @package BasicWP
|
* @package BasicWP
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "acf/{{BLOCK_SLUG}}",
|
"name": "acf/{{BLOCK_SLUG}}",
|
||||||
"title": "{{BLOCK_NAME}}",
|
"title": "{{BLOCK_NAME}}",
|
||||||
"description": "",
|
"description": "Add a description of your block. Shows up in the block inserter.",
|
||||||
"style": [
|
"style": [
|
||||||
"file:./{{BLOCK_SLUG}}.css"
|
"file:./{{BLOCK_SLUG}}.css"
|
||||||
],
|
],
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
"{{BLOCK_NAME}}"
|
"{{BLOCK_NAME}}"
|
||||||
],
|
],
|
||||||
"acf": {
|
"acf": {
|
||||||
|
"blockVersion": 3,
|
||||||
|
"autoInlineEditing": true,
|
||||||
"mode": "preview",
|
"mode": "preview",
|
||||||
"renderTemplate": "{{BLOCK_SLUG}}.php"
|
"renderTemplate": "{{BLOCK_SLUG}}.php"
|
||||||
},
|
},
|
||||||
|
|||||||
53
package.json
Normal file
53
package.json
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"name": "vdi-acf-block-scaffold",
|
||||||
|
"displayName": "VDI ACF Block Scaffold",
|
||||||
|
"description": "Scaffold WordPress ACF blocks from a template.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"publisher": "vdi",
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.80.0"
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"activationEvents": [
|
||||||
|
"onCommand:vdi-acf-block-scaffold.createBlock"
|
||||||
|
],
|
||||||
|
"main": "./out/extension.js",
|
||||||
|
"contributes": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "vdi-acf-block-scaffold.createBlock",
|
||||||
|
"title": "VDI: Create ACF Block"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration": {
|
||||||
|
"title": "VDI ACF Block Scaffold",
|
||||||
|
"properties": {
|
||||||
|
"vdiAcfBlockScaffold.parentDirectory": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "views/blocks",
|
||||||
|
"description": "Workspace-relative parent directory where new blocks are created."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keybindings": [
|
||||||
|
{
|
||||||
|
"command": "vdi-acf-block-scaffold.createBlock",
|
||||||
|
"key": "ctrl+alt+b",
|
||||||
|
"mac": "cmd+alt+b",
|
||||||
|
"when": "editorTextFocus"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"vscode:prepublish": "npm run compile",
|
||||||
|
"compile": "tsc -p ./",
|
||||||
|
"watch": "tsc -watch -p ./"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.11.30",
|
||||||
|
"@types/vscode": "^1.80.0",
|
||||||
|
"typescript": "^5.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
156
src/extension.ts
Normal file
156
src/extension.ts
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import * as path from "path";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
const OUTPUT_CHANNEL = vscode.window.createOutputChannel("VDI ACF Block Scaffold");
|
||||||
|
|
||||||
|
export function activate(context: vscode.ExtensionContext): void {
|
||||||
|
const command = vscode.commands.registerCommand(
|
||||||
|
"vdi-acf-block-scaffold.createBlock",
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
if (!workspaceFolder) {
|
||||||
|
vscode.window.showErrorMessage("Open a workspace folder first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockNameInput = await vscode.window.showInputBox({
|
||||||
|
prompt: "Enter the new block name",
|
||||||
|
placeHolder: "Testimonial",
|
||||||
|
validateInput: (value) => {
|
||||||
|
if (!value || !value.trim()) {
|
||||||
|
return "Block name is required.";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!blockNameInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockName = blockNameInput.trim();
|
||||||
|
const blockSlug = toSlug(blockName);
|
||||||
|
if (!blockSlug) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
"Block name must include at least one letter or number."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = vscode.workspace.getConfiguration("vdiAcfBlockScaffold");
|
||||||
|
const parentSetting = config.get<string>(
|
||||||
|
"parentDirectory",
|
||||||
|
"views/blocks"
|
||||||
|
);
|
||||||
|
const parentDir = path.isAbsolute(parentSetting)
|
||||||
|
? parentSetting
|
||||||
|
: path.join(workspaceFolder.uri.fsPath, parentSetting);
|
||||||
|
const targetDir = path.join(parentDir, blockSlug);
|
||||||
|
|
||||||
|
if (await pathExists(targetDir)) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
`A block named "${blockSlug}" already exists.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateDir = path.join(context.extensionPath, "block-template");
|
||||||
|
if (!(await pathExists(templateDir))) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
"Block template folder is missing from the extension."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.mkdir(targetDir, { recursive: true });
|
||||||
|
|
||||||
|
const templateEntries = await fs.readdir(templateDir, {
|
||||||
|
withFileTypes: true
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const entry of templateEntries) {
|
||||||
|
if (!entry.isFile()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourcePath = path.join(templateDir, entry.name);
|
||||||
|
const content = await fs.readFile(sourcePath, "utf8");
|
||||||
|
const replaced = content
|
||||||
|
.replace(/{{BLOCK_NAME}}/g, blockName)
|
||||||
|
.replace(/{{BLOCK_SLUG}}/g, blockSlug);
|
||||||
|
|
||||||
|
const destinationName = mapTemplateFileName(entry.name, blockSlug);
|
||||||
|
const destinationPath = path.join(targetDir, destinationName);
|
||||||
|
|
||||||
|
if (await pathExists(destinationPath)) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
`File already exists: ${destinationName}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(destinationPath, replaced, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
await openCreatedFiles(targetDir, blockSlug);
|
||||||
|
vscode.window.showInformationMessage(
|
||||||
|
`Created ACF block: ${blockName}`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
OUTPUT_CHANNEL.appendLine(message);
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
"Failed to create ACF block. See output for details."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
context.subscriptions.push(command, OUTPUT_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deactivate(): void {
|
||||||
|
OUTPUT_CHANNEL.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSlug(value: string): string {
|
||||||
|
return value
|
||||||
|
.toLowerCase()
|
||||||
|
.trim()
|
||||||
|
.replace(/[^a-z0-9]+/g, "-")
|
||||||
|
.replace(/^-+|-+$/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapTemplateFileName(filename: string, slug: string): string {
|
||||||
|
if (filename === "block-template.php") {
|
||||||
|
return `${slug}.php`;
|
||||||
|
}
|
||||||
|
if (filename === "block-template.css") {
|
||||||
|
return `${slug}.css`;
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openCreatedFiles(dir: string, slug: string): Promise<void> {
|
||||||
|
const filesToOpen = ["block.json", `${slug}.php`, `${slug}.css`];
|
||||||
|
|
||||||
|
for (const filename of filesToOpen) {
|
||||||
|
const filePath = path.join(dir, filename);
|
||||||
|
if (!(await pathExists(filePath))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const doc = await vscode.workspace.openTextDocument(filePath);
|
||||||
|
await vscode.window.showTextDocument(doc, { preview: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pathExists(targetPath: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await fs.access(targetPath);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"lib": [
|
||||||
|
"ES2020"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "out",
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
".vscode-test"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user