Skip to content

Commit 4e20e0e

Browse files
committed
feat: add generators for custom blocks
- add generator for scaffolding custom block plugins - add generator for individual custom blocks - install js-yaml - update volume mapping for new plugins - remove example-blocks plugin - update clean and copy scripts
1 parent 01418ab commit 4e20e0e

File tree

22 files changed

+526
-468
lines changed

22 files changed

+526
-468
lines changed

docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ services:
1414
volumes:
1515
- ./uploads:/var/www/html/wp-content/uploads
1616
- ./theme:/var/www/html/wp-content/themes/sparkpress-theme
17-
- ./src/plugins/example-blocks:/var/www/html/wp-content/plugins/example-blocks
1817
- ./wp-configs/wp-config.php:/var/www/html/wp-config.php
1918
- ./wp-configs/php.ini:/var/www/html/php.ini
2019
- ./.env:/var/www/html/.env

generators/custom-block.js

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
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

Comments
 (0)