|
| 1 | +const { writeFileSync, mkdirSync, readdirSync } = require('fs'); |
| 2 | +const { join } = require('path'); |
| 3 | +const prompts = require('prompts'); |
| 4 | + |
| 5 | +const getBlockJsonTemplate = ({ pluginSlug, slugName, name, description, hasViewScript }) => `{ |
| 6 | + "$schema": "https://schemas.wp.org/trunk/block.json", |
| 7 | + "apiVersion": 3, |
| 8 | + "name": "${pluginSlug}/${slugName}", |
| 9 | + "version": "0.1.0", |
| 10 | + "title": "${name}", |
| 11 | + "category": "${pluginSlug}", |
| 12 | + "icon": "admin-generic", |
| 13 | + "description": "${description}", |
| 14 | + "supports": { |
| 15 | + "html": false |
| 16 | + }, |
| 17 | + "textdomain": "${pluginSlug}", |
| 18 | + "editorScript": "file:index.js", |
| 19 | + "editorStyle": "file:index.css", |
| 20 | + "style": "file:style-index.css"${ |
| 21 | + hasViewScript |
| 22 | + ? `, |
| 23 | + "viewScript": "file:view.js"` |
| 24 | + : '' |
| 25 | + } |
| 26 | +} |
| 27 | +`; |
| 28 | + |
| 29 | +const getEditJSTemplate = ({ name }) => `/** |
| 30 | + * React hook that is used to mark the block wrapper element. |
| 31 | + * It provides all the necessary props like the class name. |
| 32 | + * |
| 33 | + * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops |
| 34 | + */ |
| 35 | +import { useBlockProps } from '@wordpress/block-editor'; |
| 36 | +
|
| 37 | +/** |
| 38 | + * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files. |
| 39 | + * Those files can contain any CSS code that gets applied to the editor. |
| 40 | + * |
| 41 | + * @see https://www.npmjs.com/package/@wordpress/scripts#using-css |
| 42 | + */ |
| 43 | +import './editor.scss'; |
| 44 | +
|
| 45 | +/** |
| 46 | + * The edit function describes the structure of your block in the context of the |
| 47 | + * editor. This represents what the editor will render when the block is used. |
| 48 | + * |
| 49 | + * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit |
| 50 | + * |
| 51 | + * @return {WPElement} Element to render. |
| 52 | + */ |
| 53 | +export default function Edit() { |
| 54 | + return <p {...useBlockProps()}>${name}</p>; |
| 55 | +} |
| 56 | +`; |
| 57 | + |
| 58 | +const getEditorSCSSTemplate = ({ pluginSlug, slugName }) => `/** |
| 59 | + * The following styles get applied inside the editor only. |
| 60 | + * |
| 61 | + * Replace them with your own styles or remove the file completely. |
| 62 | + */ |
| 63 | +
|
| 64 | +.wp-block-${pluginSlug}-${slugName} { |
| 65 | + /* insert custom styles here */ |
| 66 | +} |
| 67 | +`; |
| 68 | + |
| 69 | +const getIndexJSTemplate = () => `/** |
| 70 | + * Registers a new block provided a unique name and an object defining its behavior. |
| 71 | + * |
| 72 | + * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/ |
| 73 | + */ |
| 74 | +import { registerBlockType } from '@wordpress/blocks'; |
| 75 | +
|
| 76 | +/** |
| 77 | + * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files. |
| 78 | + * All files containing \`style\` keyword are bundled together. The code used |
| 79 | + * gets applied both to the front of your site and to the editor. |
| 80 | + * |
| 81 | + * @see https://www.npmjs.com/package/@wordpress/scripts#using-css |
| 82 | + */ |
| 83 | +import './style.scss'; |
| 84 | +
|
| 85 | +/** |
| 86 | + * Internal dependencies |
| 87 | + */ |
| 88 | +import Edit from './edit'; |
| 89 | +import save from './save'; |
| 90 | +import metadata from './block.json'; |
| 91 | +
|
| 92 | +/** |
| 93 | + * Every block starts by registering a new block type definition. |
| 94 | + * |
| 95 | + * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/ |
| 96 | + */ |
| 97 | +registerBlockType(metadata.name, { |
| 98 | + /** |
| 99 | + * @see ./edit.js |
| 100 | + */ |
| 101 | + edit: Edit, |
| 102 | +
|
| 103 | + /** |
| 104 | + * @see ./save.js |
| 105 | + */ |
| 106 | + save, |
| 107 | +}); |
| 108 | +`; |
| 109 | + |
| 110 | +const getSaveJSTemplate = ({ name }) => `/** |
| 111 | + * React hook that is used to mark the block wrapper element. |
| 112 | + * It provides all the necessary props like the class name. |
| 113 | + * |
| 114 | + * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops |
| 115 | + */ |
| 116 | +import { useBlockProps } from '@wordpress/block-editor'; |
| 117 | +
|
| 118 | +/** |
| 119 | + * The save function defines the way in which the different attributes should |
| 120 | + * be combined into the final markup, which is then serialized by the block |
| 121 | + * editor into \`post_content\`. |
| 122 | + * |
| 123 | + * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save |
| 124 | + * |
| 125 | + * @return {WPElement} Element to render. |
| 126 | + */ |
| 127 | +export default function save() { |
| 128 | + return <p {...useBlockProps.save()}>{'${name}'}</p>; |
| 129 | +} |
| 130 | +`; |
| 131 | + |
| 132 | +const getStyleSCSSTemplate = ({ pluginSlug, slugName }) => `/** |
| 133 | + * The following styles get applied both on the front of your site |
| 134 | + * and in the editor. |
| 135 | + * |
| 136 | + * Replace them with your own styles or remove the file completely. |
| 137 | + */ |
| 138 | +
|
| 139 | +.wp-block-${pluginSlug}-${slugName} { |
| 140 | + /* insert custom styles here */ |
| 141 | +} |
| 142 | +`; |
| 143 | + |
| 144 | +const getViewJSTemplate = ({ name }) => `/** |
| 145 | + * Put any JS here that is needed for your block to function when rendered outside of the editor. |
| 146 | + */ |
| 147 | +console.log('Hello from the ${name} block!'); |
| 148 | +`; |
| 149 | + |
| 150 | +const getCustomBlockPluginOptions = () => { |
| 151 | + const directories = readdirSync(join(__dirname, '../src/plugins')); |
| 152 | + return directories |
| 153 | + .filter((dir) => dir !== '.gitkeep') |
| 154 | + .map((dir) => ({ |
| 155 | + title: dir, |
| 156 | + value: dir, |
| 157 | + })); |
| 158 | +}; |
| 159 | + |
| 160 | +const getDetails = async () => { |
| 161 | + const pluginOptions = getCustomBlockPluginOptions(); |
| 162 | + if (!pluginOptions.length) { |
| 163 | + console.log( |
| 164 | + 'There are no existing plugins in `src/plugins` to add custom blocks to. Please run `npm run generate:custom-blocks-plugin` to scaffold a plugin before running `npm run generate:custom-block`' |
| 165 | + ); |
| 166 | + return; |
| 167 | + } |
| 168 | + |
| 169 | + const questions = [ |
| 170 | + { |
| 171 | + type: 'select', |
| 172 | + name: 'pluginSlug', |
| 173 | + message: 'Which custom blocks plugin should this block belong to?', |
| 174 | + choices: pluginOptions, |
| 175 | + initial: 0, |
| 176 | + }, |
| 177 | + { |
| 178 | + type: 'text', |
| 179 | + name: 'name', |
| 180 | + message: |
| 181 | + 'What should the custom block be called? (This is the name editors will use to search for the block)', |
| 182 | + }, |
| 183 | + { |
| 184 | + type: 'text', |
| 185 | + name: 'description', |
| 186 | + message: |
| 187 | + 'Please describe the custom block. (This description will help editors understand how to use the block)', |
| 188 | + }, |
| 189 | + { |
| 190 | + type: 'select', |
| 191 | + name: 'hasViewScript', |
| 192 | + message: |
| 193 | + 'Will this block require JavaScript to function when rendered on the site? (If yes, a `view.js` file will be created)', |
| 194 | + choices: [ |
| 195 | + { |
| 196 | + title: 'Yes', |
| 197 | + value: true, |
| 198 | + }, |
| 199 | + { |
| 200 | + title: 'No', |
| 201 | + value: false, |
| 202 | + }, |
| 203 | + ], |
| 204 | + initial: 0, |
| 205 | + }, |
| 206 | + ]; |
| 207 | + |
| 208 | + const response = await prompts(questions); |
| 209 | + |
| 210 | + return response; |
| 211 | +}; |
| 212 | + |
| 213 | +const generateCustomBlocksPlugin = async () => { |
| 214 | + const { pluginSlug, name, description, hasViewScript } = await getDetails(); |
| 215 | + const slugName = name.toLowerCase().replace(/\W/g, '-'); |
| 216 | + const templateParams = { pluginSlug, slugName, name, description, hasViewScript }; |
| 217 | + |
| 218 | + const blockPath = join(__dirname, '../src/plugins', pluginSlug, 'src', slugName); |
| 219 | + mkdirSync(blockPath); |
| 220 | + |
| 221 | + const blockJsonTemplate = getBlockJsonTemplate(templateParams); |
| 222 | + const blockJsonPath = join(blockPath, 'block.json'); |
| 223 | + writeFileSync(blockJsonPath, blockJsonTemplate, 'utf-8'); |
| 224 | + console.log(`Created ${blockJsonPath}`); |
| 225 | + |
| 226 | + const editJSTemplate = getEditJSTemplate(templateParams); |
| 227 | + const editJSPath = join(blockPath, 'edit.js'); |
| 228 | + writeFileSync(editJSPath, editJSTemplate, 'utf-8'); |
| 229 | + console.log(`Created ${editJSPath}`); |
| 230 | + |
| 231 | + const editorSCSSTemplate = getEditorSCSSTemplate(templateParams); |
| 232 | + const editorSCSSPath = join(blockPath, 'editor.scss'); |
| 233 | + writeFileSync(editorSCSSPath, editorSCSSTemplate, 'utf-8'); |
| 234 | + console.log(`Created ${editorSCSSPath}`); |
| 235 | + |
| 236 | + const indexJSTemplate = getIndexJSTemplate(templateParams); |
| 237 | + const indexJSPath = join(blockPath, 'index.js'); |
| 238 | + writeFileSync(indexJSPath, indexJSTemplate, 'utf-8'); |
| 239 | + console.log(`Created ${indexJSPath}`); |
| 240 | + |
| 241 | + const saveJSTemplate = getSaveJSTemplate(templateParams); |
| 242 | + const saveJSPath = join(blockPath, 'save.js'); |
| 243 | + writeFileSync(saveJSPath, saveJSTemplate, 'utf-8'); |
| 244 | + console.log(`Created ${saveJSPath}`); |
| 245 | + |
| 246 | + const styleSCSSTemplate = getStyleSCSSTemplate(templateParams); |
| 247 | + const styleSCSSPath = join(blockPath, 'style.scss'); |
| 248 | + writeFileSync(styleSCSSPath, styleSCSSTemplate, 'utf-8'); |
| 249 | + console.log(`Created ${styleSCSSPath}`); |
| 250 | + |
| 251 | + if (hasViewScript) { |
| 252 | + const viewJSTemplate = getViewJSTemplate(templateParams); |
| 253 | + const viewJSPath = join(blockPath, 'view.js'); |
| 254 | + writeFileSync(viewJSPath, viewJSTemplate, 'utf-8'); |
| 255 | + console.log(`Created ${viewJSPath}`); |
| 256 | + } |
| 257 | +}; |
| 258 | + |
| 259 | +generateCustomBlocksPlugin(); |
0 commit comments