├── .gitignore ├── README.md ├── bin └── index.js ├── lib ├── create-acf-block-json.js ├── template-block.json └── template-php.txt ├── package-lock.json ├── package.json └── screenshot.gif /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | jspm_packages/ 3 | .npm 4 | *.7z 5 | *.dmg 6 | *.gz 7 | *.bz2 8 | *.iso 9 | *.jar 10 | *.rar 11 | *.tar 12 | *.zip 13 | *.tgz 14 | *.log 15 | *.sql 16 | .DS_Store* 17 | ehthumbs.db 18 | Icon? 19 | Thumbs.db 20 | ._* 21 | *.un~ 22 | .sass-cache 23 | .lando.local.yml 24 | .env 25 | .env.development.local 26 | .env.test.local 27 | .env.production.local 28 | .env.local 29 | docker-compose.override.yml 30 | .cache 31 | logs 32 | *.log 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | lerna-debug.log* 37 | .pnpm-debug.log* 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create ACF Block JSON 2 | 3 | > Quickly create a new [WordPress](https://wordpress.org) Block that uses [Advanced Custom Fields](https://www.advancedcustomfields.com) (ACF) and utilises block.json and block-specific CSS and PHP files. 4 | 5 | If you use ACF and make Gutenberg blocks, this simple utility should save you time scaffolding custom blocks. 6 | 7 | 8 | 9 | ## Quick Start 10 | 11 | Install globally via npm: 12 | 13 | ```sh 14 | npm install --global create-acf-block-json 15 | ``` 16 | 17 | then navigate to where you want your block to be (i.e. /theme/blocks/), and run: 18 | 19 | ``` 20 | $ create-acf-block-json 21 | ``` 22 | 23 | ## What does Create ACF Block JSON do 24 | 25 | Create ACF Block JSON is an npm package command line program that will create a new folder in your current directory containing scaffolding for a new WordPress block using Advanced Custom Fields. 26 | 27 | It will generate: 28 | 29 | - Folder - containing folder for your block. 30 | - block.json - prefilled with common block information and default values. 31 | - PHP file - setup with basic information, classes and innerblocks. 32 | - SCSS files - for the frontend and editor. SCSS is useful if you use a compiler to output CSS files. 33 | - CSS file - for the frontend and editor. To write straight CSS or be overwritten by your processed SCSS. 34 | 35 | The script handles generating a unique class (.wp-block-namespace-name) for your block which is then referenced in each file. 36 | 37 | This script doesn't handle registration of the block - we recommend directory scanning methods to auto load blocks without registering each one. This method is outlined by [Bill Erickson](https://www.billerickson.net/building-acf-blocks-with-block-json/#advanced-usage), but there are [other examples](https://github.com/cncf/cncf.io/blob/0233ccfa1fb24d46ce119049b010a18a0e3d91d3/web/wp-content/themes/cncf-twenty-two/includes/acf.php#L19) online. This blog post by ACF also talks about how to register an ACF block, [follow this guide](https://www.advancedcustomfields.com/resources/how-to-upgrade-a-legacy-block-to-block-json-with-acf-6/). FYI, many guides are out of date, make sure you use guides released after 28th September 2022 (when ACF 6.0 was released) which will properly use block.json. If you want a very quick bit of PHP to register your new block, you can use this: 38 | 39 | ``` 40 | /** 41 | * Load My Create ACF Block JSON Blocks 42 | */ 43 | function thetwopct_load_acf_blocks() { 44 | register_block_type( get_template_directory() . '/blocks/my-acf-block/block.json' ); 45 | // register_block_type( get_template_directory() . '/blocks/another-block/block.json' ); 46 | } 47 | add_action( 'init', 'thetwopct_load_acf_blocks' ); 48 | ```` 49 | 50 | ## Customisation Options 51 | 52 | When you run `create-acf-block-json` you are asked a few questions before your block is created: 53 | 54 | - Namespace: Specify a namespace for the block (defaults to `acf`) 55 | - Name: Give your block a name, i.e. "My Cool Block" (required) 56 | - Description: Describe the functionality of your block so editors and users can find it easily (optional) 57 | - Icon: The icon for your block - use any [Dashicons](https://developer.wordpress.org/resource/dashicons/) name (defaults to icon `star-filled`). Note, when copying the name of the Dashicon you must remove the prefix `dashicons-`, for example: `dashicons-smiley` should be written as `smiley`. 58 | 59 | The script will then generate all required files. From there, you can edit, delete, remove the files as you wish. 60 | 61 | ## Install Create ACF Block 62 | 63 | 1. Using terminal or another CLI, download from npm and install as a global package - `npm install -g create-acf-block-json` 64 | 2. Using terminal or another CLI, navigate to your WordPress theme folder where you want all of your blocks (we use /wp-content/themes/my-theme/blocks/) 65 | 3. Run `create-acf-block-json` and follow prompts. 66 | 67 | ## Update Create ACF Block 68 | 69 | To update to the latest version you can just run the install command again - `npm install -g create-acf-block-json` 70 | 71 | ## Feedback and issues 72 | 73 | Please open an issue in the [GitHub repo](https://github.com/thetwopct/create-acf-block-json/issues). Thanks. 74 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const create = require("../lib/create-acf-block-json"); 4 | 5 | create.acfBlock(); 6 | -------------------------------------------------------------------------------- /lib/create-acf-block-json.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const prompts = require('prompts'); 3 | 4 | const DIR = '.'; 5 | 6 | const QUESTIONS = [{ 7 | type: 'text', 8 | name: 'namespace', 9 | message: 'Block Namespace', 10 | initial: 'acf' 11 | }, 12 | { 13 | type: 'text', 14 | name: 'title', 15 | message: 'Block Name' 16 | }, 17 | { 18 | type: 'text', 19 | name: 'description', 20 | message: 'Block description', 21 | initial: '' 22 | }, 23 | { 24 | type: 'text', 25 | name: 'icon', 26 | message: 'Dashicon', 27 | initial: 'star-filled' 28 | } 29 | ]; 30 | 31 | exports.acfBlock = function() { 32 | 33 | console.clear(); 34 | console.log('Create ACF Block JSON is running...'); 35 | console.log(''); 36 | console.log('This will create a new folder in the current directory containing scaffolding for a new WordPress block using Advanced Custom Fields.'); 37 | console.log(''); 38 | 39 | let cancelled = false; 40 | 41 | // Listen for SIGINT signals and set the "cancelled" flag to true. 42 | process.on('SIGINT', () => { 43 | console.log('Cancelled.'); 44 | cancelled = true; 45 | }); 46 | 47 | // Helper function for creating files. 48 | const createFile = async (path, content, successMessage, errorMessage) => { 49 | try { 50 | fs.writeFileSync(path, content); 51 | console.log(successMessage); 52 | } catch (error) { 53 | console.error(errorMessage, error); 54 | } 55 | }; 56 | 57 | (async () => { 58 | const response = await prompts(QUESTIONS, {onCancel: () => {cancelled = true}}); 59 | 60 | // Check if the user cancelled the prompts. 61 | if (cancelled) { 62 | console.log('Aborting...'); 63 | return; 64 | } 65 | 66 | const title = response.title; 67 | const slug = response.title.replace(/\s+/g, '-').toLowerCase(); 68 | const namespace = response.namespace.replace(/\s+/g, '-').toLowerCase(); 69 | const qualifiedName = namespace + '/' + slug; 70 | const folder = '/' + slug; 71 | const absolute = DIR + folder; 72 | 73 | // Create default CSS class. 74 | const css = '.wp-block-' + namespace + '-' + slug + ' {}'; 75 | 76 | // Create Folder. 77 | if (!fs.existsSync(absolute)) { 78 | fs.mkdirSync(absolute); 79 | } else { 80 | console.log('Error: A directory called ' + slug + ' was already found. Aborting.') 81 | return; 82 | } 83 | 84 | // Handle cancellation. 85 | if (cancelled) { 86 | console.log('Aborting.'); 87 | return; 88 | } 89 | 90 | // Create files. 91 | await createFile( 92 | absolute + '/' + slug + '.css', 93 | `/* ${css} */`, 94 | `${slug}.css created`, 95 | 'Error creating CSS file:' 96 | ); 97 | await createFile( 98 | absolute + '/' + slug + '.scss', 99 | `// ${css}`, 100 | `${slug}.scss created`, 101 | 'Error creating SCSS file:' 102 | ); 103 | await createFile( 104 | absolute + '/editor.css', 105 | `/* ${css} */`, 106 | `editor.css created`, 107 | 'Error creating editor CSS file:' 108 | ); 109 | await createFile( 110 | absolute + '/editor.scss', 111 | `// ${css}`, 112 | `editor.scss created`, 113 | 'Error creating editor SCSS file:' 114 | ); 115 | 116 | // Handle cancellation. 117 | if (cancelled) { 118 | console.log('Aborting.'); 119 | return; 120 | } 121 | 122 | // Get the PHP template and turn in to PHP. 123 | let phpTemplate = '/template-php.txt'; 124 | try { 125 | let data = fs.readFileSync(__dirname + phpTemplate, 'utf8'); 126 | data = data.replace(/XYZ/g, title) 127 | .replace(/QWY/g, slug) 128 | .replace(/DX9S/g, namespace) 129 | .replace(/\r\n/g, '\n'); 130 | await createFile( 131 | absolute + '/' + slug + '.php', 132 | data, 133 | `${slug}.php created`, 134 | 'Error creating PHP template:' 135 | ); 136 | } catch (error) { 137 | console.error('Error creating PHP template:', error); 138 | } 139 | 140 | // Handle cancellation. 141 | if (cancelled) { 142 | console.log('Aborting.'); 143 | return; 144 | } 145 | 146 | // Get the Block.json template. 147 | let jsonTemplate = '/template-block.json'; 148 | try { 149 | let raw = fs.readFileSync(__dirname + jsonTemplate); 150 | let template = JSON.parse(raw); 151 | // Update Block.json values. 152 | template.name = qualifiedName; 153 | template.title = title; 154 | template.description = response.description; 155 | template.icon = response.icon; 156 | template.style = 'file:./' + slug + '.css'; 157 | template.editorStyle = 'file:./editor.css'; 158 | template.acf.renderTemplate = slug + '.php'; 159 | let jsonContent = JSON.stringify(template, null, "\t"); 160 | await createFile( 161 | absolute + '/block.json', 162 | jsonContent, 163 | 'block.json created', 164 | 'Error creating JSON template:' 165 | ); 166 | } catch (error) { 167 | console.error('Error creating JSON template:', error); 168 | } 169 | 170 | })(); 171 | } 172 | -------------------------------------------------------------------------------- /lib/template-block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 3, 4 | "name": "", 5 | "title": "", 6 | "description": "", 7 | "style": "", 8 | "editorStyle": "", 9 | "editorScript": [], 10 | "category": "common", 11 | "icon": "awards", 12 | "keywords": [ 13 | "", 14 | "", 15 | "" 16 | ], 17 | "acf": { 18 | "mode": "preview", 19 | "renderTemplate": "" 20 | }, 21 | "supports": { 22 | "mode": false, 23 | "align": [ 24 | "wide", 25 | "full", 26 | "left", 27 | "center", 28 | "right" 29 | ], 30 | "alignWide": true, 31 | "alignContent": false, 32 | "alignText": false, 33 | "anchor": true, 34 | "className": true, 35 | "color": { 36 | "background": false, 37 | "gradients": false, 38 | "link": false, 39 | "text": false, 40 | "enableContrastChecker": false 41 | }, 42 | "customClassName": false, 43 | "defaultStylePicker": true, 44 | "html": false, 45 | "multiple": true, 46 | "reusable": true, 47 | "spacing": { 48 | "margin": false, 49 | "padding": false 50 | }, 51 | "typography": { 52 | "fontSize": false, 53 | "lineHeight": false 54 | } 55 | }, 56 | "attributes": { 57 | "variable": { 58 | "type": "string", 59 | "default": "" 60 | } 61 | }, 62 | "example": {} 63 | } -------------------------------------------------------------------------------- /lib/template-php.txt: -------------------------------------------------------------------------------- 1 | 44 | 45 |
class=""> 46 |

Welcome to your ACF block.

47 |

Open the blocks PHP file to edit this text.

48 | '; 50 | ?> 51 |
52 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-acf-block-json", 3 | "version": "1.0.11", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "create-acf-block-json", 9 | "version": "1.0.11", 10 | "license": "ISC", 11 | "bin": { 12 | "create-acf-block": "bin/index.js" 13 | }, 14 | "devDependencies": { 15 | "prompts": "^2.4.2" 16 | } 17 | }, 18 | "node_modules/kleur": { 19 | "version": "3.0.3", 20 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", 21 | "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", 22 | "dev": true, 23 | "engines": { 24 | "node": ">=6" 25 | } 26 | }, 27 | "node_modules/prompts": { 28 | "version": "2.4.2", 29 | "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", 30 | "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", 31 | "dev": true, 32 | "dependencies": { 33 | "kleur": "^3.0.3", 34 | "sisteransi": "^1.0.5" 35 | }, 36 | "engines": { 37 | "node": ">= 6" 38 | } 39 | }, 40 | "node_modules/sisteransi": { 41 | "version": "1.0.5", 42 | "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", 43 | "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", 44 | "dev": true 45 | } 46 | }, 47 | "dependencies": { 48 | "kleur": { 49 | "version": "3.0.3", 50 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", 51 | "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", 52 | "dev": true 53 | }, 54 | "prompts": { 55 | "version": "2.4.2", 56 | "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", 57 | "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", 58 | "dev": true, 59 | "requires": { 60 | "kleur": "^3.0.3", 61 | "sisteransi": "^1.0.5" 62 | } 63 | }, 64 | "sisteransi": { 65 | "version": "1.0.5", 66 | "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", 67 | "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", 68 | "dev": true 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-acf-block-json", 3 | "description": "Quickly create an a new WordPress Block that uses Advanced Custom Fields (ACF) and utilised block.json and block-specific CSS.", 4 | "version": "1.0.11", 5 | "main": "./lib/create-acf-block-json.js", 6 | "bin": { 7 | "create-acf-block-json": "./bin/index.js" 8 | }, 9 | "keywords": [ 10 | "acf", 11 | "advanced custom fields", 12 | "wordpress", 13 | "gutenberg" 14 | ], 15 | "scripts": { 16 | "test": "echo \"No test specified\"" 17 | }, 18 | "files": [ 19 | "/lib", 20 | "/bin", 21 | "README.md", 22 | "screenshot.gif" 23 | ], 24 | "engines": { 25 | "node": ">=15.0.0" 26 | }, 27 | "author": "James Hunt <10615884+thetwopct@users.noreply.github.com>", 28 | "license": "ISC", 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/thetwopct/create-acf-block-json" 32 | }, 33 | "dependencies": { 34 | "prompts": "^2.4.2" 35 | }, 36 | "devDependencies": {} 37 | } 38 | -------------------------------------------------------------------------------- /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetwopct/create-acf-block-json/b51a5a682dd5f114cf60004fa1c694585b807ea3/screenshot.gif --------------------------------------------------------------------------------