feature: First build

This commit is contained in:
Keith Solomon
2026-01-26 08:24:46 -06:00
parent 27511d8ec0
commit d6df4c18a2
6 changed files with 240 additions and 10 deletions

View File

@@ -1 +1 @@
/* {{ BLOCK_NAME }} block styles */ /* {{BLOCK_NAME}} block styles */

View File

@@ -1,15 +1,15 @@
<?php <?php
/** /**
* 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
*/ */
namespace BasicWP; namespace BasicWP;
$classes = '{{ BLOCK_SLUG }}'; $classes = '{{BLOCK_SLUG}}';
/** /**
* NOTE: DO NOT remove this function call - it is required to avoid editor issues. * NOTE: DO NOT remove this function call - it is required to avoid editor issues.

View File

@@ -1,18 +1,20 @@
{ {
"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"
], ],
"category": "vdi-block", "category": "vdi-block",
"icon": "block-default", "icon": "block-default",
"keywords": [ "keywords": [
"{{ BLOCK_NAME }}" "{{BLOCK_NAME}}"
], ],
"acf": { "acf": {
"blockVersion": 3,
"autoInlineEditing": true,
"mode": "preview", "mode": "preview",
"renderTemplate": "{{ BLOCK_SLUG }}.php" "renderTemplate": "{{BLOCK_SLUG}}.php"
}, },
"supports": { "supports": {
"align": true, "align": true,

53
package.json Normal file
View 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
View 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
View 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"
]
}