Skip to content

Commit 0b1078c

Browse files
authored
feat: add generator for reusable patterns (#96)
1 parent cc9e0ff commit 0b1078c

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

docs/generators.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,17 @@ The following files will be created based on your input:
6565
- `src/php/views/<page-template-name>.twig`
6666

6767
[Page Template documentation](https://developer.wordpress.org/themes/template-files-section/page-template-files/)
68+
69+
## Reusable Pattern
70+
71+
The generator for reusable patterns will prompt you for a name, description, and categories for the pattern, then create a script to register a reusable pattern with metadata based on your inputs and instructions for how to create the markup for the pattern.
72+
73+
```sh
74+
npm run generate:pattern
75+
```
76+
77+
The following file will be created based on your input:
78+
79+
- `src/php/patterns/<pattern-name>.php`
80+
81+
[Reusable pattern (a.k.a. Block pattern) documentation](https://developer.wordpress.org/themes/advanced-topics/block-patterns/)

generators/pattern.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const { writeFileSync, readFileSync } = require('fs');
2+
const { join } = require('path');
3+
const prompts = require('prompts');
4+
5+
const getPatternScript = ({ patternSlug, themeSlug, description, categories }) => `<?php
6+
/**
7+
* Title: ${description}
8+
* Slug: ${themeSlug}/${patternSlug}
9+
* Categories: ${categories.join(', ')}
10+
*/
11+
?>
12+
<!-- wp:paragraph -->
13+
<p>Replace this sample content with markup for a pattern.</p>
14+
<!-- /wp:paragraph -->
15+
16+
<!-- wp:list -->
17+
<ul><!-- wp:list-item -->
18+
<li>Arrange whichever blocks you need to form the pattern in the WordPress editor</li>
19+
<!-- /wp:list-item -->
20+
21+
<!-- wp:list-item -->
22+
<li>Select all blocks (or the outermost block), the click the kebab menu > Copy (or Copy blocks)</li>
23+
<!-- /wp:list-item -->
24+
25+
<!-- wp:list-item -->
26+
<li>Paste the resulting content in ${patternSlug}.php</li>
27+
<!-- /wp:list-item --></ul>
28+
<!-- /wp:list -->
29+
`;
30+
31+
const getDetails = async () => {
32+
const questions = [
33+
{
34+
type: 'text',
35+
name: 'name',
36+
message: 'What should the pattern be called? Content editors will search for this name.',
37+
},
38+
{
39+
type: 'text',
40+
name: 'description',
41+
message:
42+
'Please describe this pattern. This will help content editors understand what the pattern should be used for.',
43+
},
44+
{
45+
type: 'multiselect',
46+
name: 'categories',
47+
message:
48+
'Which pattern categories should this pattern be included in? This will help content editors find the pattern.',
49+
hint: 'Space to select. Return to submit',
50+
instructions: false,
51+
choices: [
52+
{
53+
title: 'My patterns (recommended)',
54+
value: 'custom',
55+
selected: true,
56+
},
57+
{
58+
title: 'Featured',
59+
value: 'featured',
60+
selected: false,
61+
},
62+
{
63+
title: 'Posts',
64+
value: 'posts',
65+
selected: false,
66+
},
67+
{
68+
title: 'Text',
69+
value: 'text',
70+
selected: false,
71+
},
72+
{
73+
title: 'Gallery',
74+
value: 'gallery',
75+
selected: false,
76+
},
77+
{
78+
title: 'Call to Action',
79+
value: 'call-to-action',
80+
selected: false,
81+
},
82+
{
83+
title: 'Banners',
84+
value: 'banner',
85+
selected: false,
86+
},
87+
{
88+
title: 'Headers',
89+
value: 'header',
90+
selected: false,
91+
},
92+
{
93+
title: 'Footers',
94+
value: 'footer',
95+
selected: false,
96+
},
97+
],
98+
},
99+
];
100+
101+
const response = await prompts(questions);
102+
103+
return response;
104+
};
105+
106+
const getFirstGroup = (regexp, str) =>
107+
Array.from(str.matchAll(regexp), (match) => match[1])?.[0] ?? 'theme-slug';
108+
109+
const getThemeSlug = () => {
110+
const themeDefinitionPath = join(__dirname, '../src/php/style.css');
111+
const themeDefinition = readFileSync(themeDefinitionPath, { encoding: 'utf-8' });
112+
113+
const themeSlug = getFirstGroup(/Theme Name:\s(.*)/gm, themeDefinition);
114+
115+
return themeSlug.toLowerCase().replace(/\W/g, '-');
116+
};
117+
118+
const generatePattern = async () => {
119+
const themeSlug = getThemeSlug();
120+
const { name, description, categories } = await getDetails();
121+
const patternSlug = name.toLowerCase().replace(/\W/g, '-');
122+
const templateParams = { patternSlug, themeSlug, description, categories };
123+
124+
const patternScript = getPatternScript(templateParams);
125+
const patternScriptPath = join(__dirname, '../src/php/patterns', `${patternSlug}.php`);
126+
writeFileSync(patternScriptPath, patternScript, 'utf-8');
127+
console.log(`Created ${patternScriptPath}`);
128+
};
129+
130+
generatePattern();

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"import-db": "./scripts/import-db.sh",
4343
"preimport-db": "npm run backup-db",
4444
"generate:page-template": "node ./generators/page-template.js",
45+
"generate:pattern": "node ./generators/pattern.js",
4546
"generate:post-type": "node ./generators/post-type.js",
4647
"generate:shortcode": "node ./generators/shortcode.js",
4748
"generate:taxonomy": "node ./generators/taxonomy.js",

src/php/patterns/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)