@wordpress/create-block 3.7.0 → 4.0.1-next.957ca95e4c.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.0.0 (2022-08-24)
6
+
7
+ ### Breaking Change
8
+
9
+ - Increase the minimum Node.js version to 14 and minimum npm version to 6.14.4 ([#43141](https://github.com/WordPress/gutenberg/pull/43141)).
10
+
11
+ ### New Feature
12
+
13
+ - Add `--no-plugin` flag to allow scaffolding of a block in an existing plugin ([#41642](https://github.com/WordPress/gutenberg/pull/41642))
14
+ - Introduce the `--variant` flag to allow selection of a variant as defined in the template ([#41289](https://github.com/WordPress/gutenberg/pull/41289), [#43481](https://github.com/WordPress/gutenberg/pull/43481)).
15
+
5
16
  ## 3.6.0 (2022-07-13)
6
17
 
7
18
  ### Enhancement
package/README.md CHANGED
@@ -20,7 +20,7 @@ $ cd todo-list
20
20
  $ npm start
21
21
  ```
22
22
 
23
- _(requires `node` version `12.0.0` or above, and `npm` version `6.9.0` or above)_
23
+ _(requires `node` version `14.0.0` or above, and `npm` version `6.14.4` or above)_
24
24
 
25
25
  It creates a WordPress plugin that you need to [install manually](https://wordpress.org/support/article/managing-plugins/#manual-plugin-installation).
26
26
 
@@ -41,6 +41,7 @@ Options:
41
41
  ```bash
42
42
  -V, --version output the version number
43
43
  -t, --template <name> project template type name; allowed values: "static" (default), "es5", the name of an external npm package, or the path to a local directory
44
+ --no-plugin scaffold block files only
44
45
  --namespace <value> internal namespace for the block name
45
46
  --title <value> display title for the block and the WordPress plugin
46
47
  --short-description <value> short description for the block and the WordPress plugin
@@ -49,6 +50,7 @@ Options:
49
50
  --no-wp-scripts disable integration with `@wordpress/scripts` package
50
51
  --wp-env enable integration with `@wordpress/env` package
51
52
  -h, --help output usage information
53
+ --variant choose a block variant as defined by the template
52
54
  ```
53
55
 
54
56
  More examples:
@@ -71,12 +73,25 @@ $ npx @wordpress/create-block --template my-template-package
71
73
  $ npx @wordpress/create-block --template ./path/to/template-directory
72
74
  ```
73
75
 
74
- 4. Help you need to use `npx` to output usage information.
76
+ 4. Generating a dynamic block based on the built-in template.
77
+
78
+ ```bash
79
+ $ npx @wordpress/create-block --variant dynamic
80
+ ```
81
+
82
+ 5. Help – you need to use `npx` to output usage information.
75
83
 
76
84
  ```bash
77
85
  $ npx @wordpress/create-block --help
78
86
  ```
79
87
 
88
+ 5. No plugin mode – it is also possible to scaffold only block files into the current directory.
89
+
90
+
91
+ ```bash
92
+ $ npx @wordpress/create-block --no-plugin
93
+ ```
94
+
80
95
  When you scaffold a block, you must provide at least a `slug` name, the `namespace` which usually corresponds to either the `theme` or `plugin` name. In most cases, we recommended pairing blocks with WordPress plugins rather than themes, because only using plugin ensures that all blocks still work when your theme changes.
81
96
 
82
97
  ## Available Commands
@@ -4,7 +4,6 @@
4
4
  const inquirer = require( 'inquirer' );
5
5
  const checkSync = require( 'check-node-version' );
6
6
  const tools = require( 'check-node-version/tools' );
7
- const { forEach } = require( 'lodash' );
8
7
  const { promisify } = require( 'util' );
9
8
 
10
9
  /**
@@ -21,13 +20,15 @@ async function checkSystemRequirements( engines ) {
21
20
  log.error( 'Minimum system requirements not met!' );
22
21
  log.info( '' );
23
22
 
24
- forEach( result.versions, ( { isSatisfied, wanted }, name ) => {
25
- if ( ! isSatisfied ) {
26
- log.error(
27
- `Error: Wanted ${ name } version ${ wanted.raw } (${ wanted.range })`
28
- );
23
+ Object.entries( result.versions ).forEach(
24
+ ( [ name, { isSatisfied, wanted } ] ) => {
25
+ if ( ! isSatisfied ) {
26
+ log.error(
27
+ `Error: Wanted ${ name } version ${ wanted.raw } (${ wanted.range })`
28
+ );
29
+ }
29
30
  }
30
- } );
31
+ );
31
32
 
32
33
  log.info( '' );
33
34
  log.error( 'The program may not complete correctly if you continue.' );
@@ -46,13 +47,16 @@ async function checkSystemRequirements( engines ) {
46
47
  log.error( 'Cancelled.' );
47
48
  log.info( '' );
48
49
 
49
- forEach( result.versions, ( { isSatisfied, wanted }, name ) => {
50
- if ( ! isSatisfied ) {
51
- log.info(
52
- tools[ name ].getInstallInstructions( wanted.raw )
53
- );
50
+ Object.entries( result.versions ).forEach(
51
+ ( [ name, { isSatisfied, wanted } ] ) => {
52
+ if ( ! isSatisfied ) {
53
+ log.info(
54
+ tools[ name ].getInstallInstructions( wanted.raw )
55
+ );
56
+ }
54
57
  }
55
- } );
58
+ );
59
+
56
60
  process.exit( 1 );
57
61
  }
58
62
  }
package/lib/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
  * External dependencies
3
3
  */
4
4
  const inquirer = require( 'inquirer' );
5
+ const { capitalCase } = require( 'change-case' );
5
6
  const program = require( 'commander' );
6
- const { pickBy, startCase } = require( 'lodash' );
7
7
 
8
8
  /**
9
9
  * Internal dependencies
@@ -34,8 +34,8 @@ program
34
34
  .arguments( '[slug]' )
35
35
  .option(
36
36
  '-t, --template <name>',
37
- 'project template type name; allowed values: "static", "es5", the name of an external npm package, or the path to a local directory',
38
- 'static'
37
+ 'project template type name; allowed values: "standard", "es5", the name of an external npm package, or the path to a local directory',
38
+ 'standard'
39
39
  )
40
40
  .option( '--namespace <value>', 'internal namespace for the block name' )
41
41
  .option(
@@ -57,10 +57,13 @@ program
57
57
  'disable integration with `@wordpress/scripts` package'
58
58
  )
59
59
  .option( '--wp-env', 'enable integration with `@wordpress/env` package' )
60
+ .option( '--no-plugin', 'scaffold only block files' )
61
+ .option( '--variant <variant>', 'the variant of the template to use' )
60
62
  .action(
61
63
  async (
62
64
  slug,
63
65
  {
66
+ plugin,
64
67
  category,
65
68
  namespace,
66
69
  shortDescription: description,
@@ -68,81 +71,131 @@ program
68
71
  title,
69
72
  wpScripts,
70
73
  wpEnv,
74
+ variant,
71
75
  }
72
76
  ) => {
73
77
  await checkSystemRequirements( engines );
74
78
  try {
75
79
  const pluginTemplate = await getPluginTemplate( templateName );
76
- const defaultValues = getDefaultValues( pluginTemplate );
77
- const optionsValues = pickBy(
78
- {
80
+ const availableVariants = Object.keys(
81
+ pluginTemplate.variants
82
+ );
83
+ if ( variant && ! availableVariants.includes( variant ) ) {
84
+ if ( ! availableVariants.length ) {
85
+ throw new CLIError(
86
+ `"${ variant }" variant was selected. This template does not have any variants!`
87
+ );
88
+ }
89
+ throw new CLIError(
90
+ `"${ variant }" is not a valid variant for this template. Available variants are: ${ availableVariants.join(
91
+ ', '
92
+ ) }.`
93
+ );
94
+ }
95
+
96
+ const optionsValues = Object.fromEntries(
97
+ Object.entries( {
98
+ plugin,
79
99
  category,
80
100
  description,
81
101
  namespace,
82
102
  title,
83
103
  wpScripts,
84
104
  wpEnv,
85
- },
86
- ( value ) => value !== undefined
105
+ } ).filter( ( [ , value ] ) => value !== undefined )
87
106
  );
88
107
 
89
108
  if ( slug ) {
109
+ const defaultValues = getDefaultValues(
110
+ pluginTemplate,
111
+ variant
112
+ );
90
113
  const answers = {
91
114
  ...defaultValues,
92
115
  slug,
93
116
  // Transforms slug to title as a fallback.
94
- title: startCase( slug ),
117
+ title: capitalCase( slug ),
95
118
  ...optionsValues,
96
119
  };
97
120
  await scaffold( pluginTemplate, answers );
98
121
  } else {
99
122
  log.info( '' );
100
123
  log.info(
101
- "Let's customize your WordPress plugin with blocks:"
124
+ plugin
125
+ ? "Let's customize your WordPress plugin with blocks:"
126
+ : "Let's add a new block to your existing WordPress plugin:"
127
+ );
128
+
129
+ if ( ! variant && availableVariants.length > 1 ) {
130
+ const result = await inquirer.prompt( {
131
+ type: 'list',
132
+ name: 'variant',
133
+ message:
134
+ 'The template variant to use for this block:',
135
+ choices: availableVariants,
136
+ } );
137
+ variant = result.variant;
138
+ }
139
+
140
+ const defaultValues = getDefaultValues(
141
+ pluginTemplate,
142
+ variant
102
143
  );
103
144
 
104
145
  const filterOptionsProvided = ( { name } ) =>
105
146
  ! Object.keys( optionsValues ).includes( name );
106
- const blockPrompts = getPrompts( pluginTemplate, [
107
- 'slug',
108
- 'namespace',
109
- 'title',
110
- 'description',
111
- 'dashicon',
112
- 'category',
113
- ] ).filter( filterOptionsProvided );
147
+ const blockPrompts = getPrompts(
148
+ pluginTemplate,
149
+ [
150
+ 'slug',
151
+ 'namespace',
152
+ 'title',
153
+ 'description',
154
+ 'dashicon',
155
+ 'category',
156
+ ],
157
+ variant
158
+ ).filter( filterOptionsProvided );
114
159
  const blockAnswers = await inquirer.prompt( blockPrompts );
115
160
 
116
- const pluginAnswers = await inquirer
117
- .prompt( {
118
- type: 'confirm',
119
- name: 'configurePlugin',
120
- message:
121
- 'Do you want to customize the WordPress plugin?',
122
- default: false,
123
- } )
124
- .then( async ( { configurePlugin } ) => {
125
- if ( ! configurePlugin ) {
126
- return {};
127
- }
161
+ const pluginAnswers = plugin
162
+ ? await inquirer
163
+ .prompt( {
164
+ type: 'confirm',
165
+ name: 'configurePlugin',
166
+ message:
167
+ 'Do you want to customize the WordPress plugin?',
168
+ default: false,
169
+ } )
170
+ .then( async ( { configurePlugin } ) => {
171
+ if ( ! configurePlugin ) {
172
+ return {};
173
+ }
174
+
175
+ const pluginPrompts = getPrompts(
176
+ pluginTemplate,
177
+ [
178
+ 'pluginURI',
179
+ 'version',
180
+ 'author',
181
+ 'license',
182
+ 'licenseURI',
183
+ 'domainPath',
184
+ 'updateURI',
185
+ ],
186
+ variant
187
+ ).filter( filterOptionsProvided );
188
+ const result = await inquirer.prompt(
189
+ pluginPrompts
190
+ );
191
+ return result;
192
+ } )
193
+ : {};
128
194
 
129
- const pluginPrompts = getPrompts( pluginTemplate, [
130
- 'pluginURI',
131
- 'version',
132
- 'author',
133
- 'license',
134
- 'licenseURI',
135
- 'domainPath',
136
- 'updateURI',
137
- ] ).filter( filterOptionsProvided );
138
- const result = await inquirer.prompt(
139
- pluginPrompts
140
- );
141
- return result;
142
- } );
143
195
  await scaffold( pluginTemplate, {
144
196
  ...defaultValues,
145
197
  ...optionsValues,
198
+ variant,
146
199
  ...blockAnswers,
147
200
  ...pluginAnswers,
148
201
  } );
package/lib/init-block.js CHANGED
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- const { omitBy } = require( 'lodash' );
5
4
  const { dirname, join } = require( 'path' );
6
5
  const makeDir = require( 'make-dir' );
7
6
  const { writeFile } = require( 'fs' ).promises;
@@ -15,6 +14,7 @@ const { writeOutputTemplate } = require( './output' );
15
14
  async function initBlockJSON( {
16
15
  $schema,
17
16
  apiVersion,
17
+ plugin,
18
18
  slug,
19
19
  namespace,
20
20
  title,
@@ -33,13 +33,15 @@ async function initBlockJSON( {
33
33
  info( '' );
34
34
  info( 'Creating a "block.json" file.' );
35
35
 
36
- const outputFile = join( process.cwd(), slug, folderName, 'block.json' );
36
+ const outputFile = plugin
37
+ ? join( process.cwd(), slug, folderName, 'block.json' )
38
+ : join( process.cwd(), slug, 'block.json' );
37
39
  await makeDir( dirname( outputFile ) );
38
40
  await writeFile(
39
41
  outputFile,
40
42
  JSON.stringify(
41
- omitBy(
42
- {
43
+ Object.fromEntries(
44
+ Object.entries( {
43
45
  $schema,
44
46
  apiVersion,
45
47
  name: namespace + '/' + slug,
@@ -54,8 +56,7 @@ async function initBlockJSON( {
54
56
  editorScript,
55
57
  editorStyle,
56
58
  style,
57
- },
58
- ( value ) => ! value
59
+ } ).filter( ( [ , value ] ) => !! value )
59
60
  ),
60
61
  null,
61
62
  '\t'
@@ -65,15 +66,17 @@ async function initBlockJSON( {
65
66
 
66
67
  module.exports = async function ( outputTemplates, view ) {
67
68
  await Promise.all(
68
- Object.keys( outputTemplates ).map(
69
- async ( outputFile ) =>
70
- await writeOutputTemplate(
71
- outputTemplates[ outputFile ],
72
- join( view.folderName, outputFile ),
73
- view
74
- )
75
- )
76
- );
69
+ Object.keys( outputTemplates ).map( async ( outputFile ) => {
70
+ const pathName = view.plugin
71
+ ? join( view.folderName, outputFile )
72
+ : join( process.cwd(), view.slug, outputFile );
77
73
 
74
+ await writeOutputTemplate(
75
+ outputTemplates[ outputFile ],
76
+ pathName,
77
+ view
78
+ );
79
+ } )
80
+ );
78
81
  await initBlockJSON( view );
79
82
  };
@@ -2,7 +2,6 @@
2
2
  * External dependencies
3
3
  */
4
4
  const { command } = require( 'execa' );
5
- const { isEmpty, omitBy } = require( 'lodash' );
6
5
  const npmPackageArg = require( 'npm-package-arg' );
7
6
  const { join } = require( 'path' );
8
7
  const writePkg = require( 'write-pkg' );
@@ -24,6 +23,7 @@ module.exports = async ( {
24
23
  npmDependencies,
25
24
  npmDevDependencies,
26
25
  customScripts,
26
+ isDynamicVariant,
27
27
  } ) => {
28
28
  const cwd = join( process.cwd(), slug );
29
29
 
@@ -32,8 +32,8 @@ module.exports = async ( {
32
32
 
33
33
  await writePkg(
34
34
  cwd,
35
- omitBy(
36
- {
35
+ Object.fromEntries(
36
+ Object.entries( {
37
37
  name: slug,
38
38
  version,
39
39
  description,
@@ -43,19 +43,22 @@ module.exports = async ( {
43
43
  main: wpScripts && 'build/index.js',
44
44
  scripts: {
45
45
  ...( wpScripts && {
46
- build: 'wp-scripts build',
46
+ build: isDynamicVariant
47
+ ? 'wp-scripts build --webpack-copy-php'
48
+ : 'wp-scripts build',
47
49
  format: 'wp-scripts format',
48
50
  'lint:css': 'wp-scripts lint-style',
49
51
  'lint:js': 'wp-scripts lint-js',
50
52
  'packages-update': 'wp-scripts packages-update',
51
53
  'plugin-zip': 'wp-scripts plugin-zip',
52
- start: 'wp-scripts start',
54
+ start: isDynamicVariant
55
+ ? 'wp-scripts start --webpack-copy-php'
56
+ : 'wp-scripts start',
53
57
  } ),
54
58
  ...( wpEnv && { env: 'wp-env' } ),
55
59
  ...customScripts,
56
60
  },
57
- },
58
- isEmpty
61
+ } ).filter( ( [ , value ] ) => !! value )
59
62
  )
60
63
  );
61
64
 
package/lib/output.js CHANGED
@@ -13,13 +13,15 @@ const writeOutputAsset = async ( inputFile, outputFile, view ) => {
13
13
  };
14
14
 
15
15
  const writeOutputTemplate = async ( inputFile, outputFile, view ) => {
16
- // Output files can have names that depend on the slug provided.
17
- const outputFilePath = join(
18
- view.slug,
19
- outputFile.replace( /\$slug/g, view.slug )
20
- );
16
+ const outputFilePath = view.plugin
17
+ ? join( view.slug, outputFile.replace( /\$slug/g, view.slug ) )
18
+ : outputFile;
21
19
  await makeDir( dirname( outputFilePath ) );
22
- writeFile( outputFilePath, render( inputFile, view ) );
20
+ // If the rendered template is empty, don't write it. This is how we can conditionally add template files.
21
+ const renderedFile = render( inputFile, view );
22
+ if ( renderedFile.trim().length ) {
23
+ writeFile( outputFilePath, renderedFile );
24
+ }
23
25
  };
24
26
 
25
27
  module.exports = {
package/lib/prompts.js CHANGED
@@ -1,14 +1,19 @@
1
1
  /**
2
- * External dependencies
2
+ * Capitalizes the first letter in a string.
3
+ *
4
+ * @param {string} str The string whose first letter the function will capitalize.
5
+ *
6
+ * @return {string} Capitalized string.
3
7
  */
4
- const { isEmpty, upperFirst } = require( 'lodash' );
8
+ const upperFirst = ( [ firstLetter, ...rest ] ) =>
9
+ firstLetter.toUpperCase() + rest.join( '' );
5
10
 
6
11
  // Block metadata.
7
12
  const slug = {
8
13
  type: 'input',
9
14
  name: 'slug',
10
15
  message:
11
- 'The block slug used for identification (also the plugin and output folder name):',
16
+ 'The block slug used for identification (also the output folder name):',
12
17
  validate( input ) {
13
18
  if ( ! /^[a-z][a-z0-9\-]*$/.test( input ) ) {
14
19
  return 'Invalid block slug specified. Block slug can contain only lowercase alphanumeric characters or dashes, and start with a letter.';
@@ -56,7 +61,7 @@ const dashicon = {
56
61
  message:
57
62
  'The dashicon to make it easier to identify your block (optional):',
58
63
  validate( input ) {
59
- if ( ! isEmpty( input ) && ! /^[a-z][a-z0-9\-]*$/.test( input ) ) {
64
+ if ( input.length && ! /^[a-z][a-z0-9\-]*$/.test( input ) ) {
60
65
  return 'Invalid dashicon name specified. Visit https://developer.wordpress.org/resource/dashicons/ to discover available names.';
61
66
  }
62
67
 
package/lib/scaffold.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- const { camelCase, snakeCase } = require( 'change-case' );
4
+ const { pascalCase, snakeCase } = require( 'change-case' );
5
5
 
6
6
  /**
7
7
  * Internal dependencies
@@ -10,18 +10,15 @@ const initBlock = require( './init-block' );
10
10
  const initPackageJSON = require( './init-package-json' );
11
11
  const initWPScripts = require( './init-wp-scripts' );
12
12
  const initWPEnv = require( './init-wp-env' );
13
- const { code, info, success } = require( './log' );
13
+ const { code, info, success, error } = require( './log' );
14
14
  const { writeOutputAsset, writeOutputTemplate } = require( './output' );
15
15
 
16
- function upperFirst( str ) {
17
- return str.charAt( 0 ).toUpperCase() + str.substr( 1 );
18
- }
19
-
20
16
  module.exports = async (
21
17
  { blockOutputTemplates, pluginOutputTemplates, outputAssets },
22
18
  {
23
19
  $schema,
24
20
  apiVersion,
21
+ plugin,
25
22
  namespace,
26
23
  slug,
27
24
  title,
@@ -46,22 +43,39 @@ module.exports = async (
46
43
  editorScript,
47
44
  editorStyle,
48
45
  style,
46
+ variantVars,
49
47
  }
50
48
  ) => {
51
49
  slug = slug.toLowerCase();
52
50
  namespace = namespace.toLowerCase();
51
+ /**
52
+ * --no-plugin relies on the used template supporting the [blockTemplatesPath property](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/#blocktemplatespath).
53
+ * If the blockOutputTemplates object has no properties, we can assume that there was a custom --template passed that
54
+ * doesn't support it.
55
+ */
56
+ if ( ! plugin && Object.keys( blockOutputTemplates ) < 1 ) {
57
+ error(
58
+ 'No block files found in the template. Please ensure that the template supports the blockTemplatesPath property.'
59
+ );
60
+ return;
61
+ }
53
62
 
54
63
  info( '' );
55
- info( `Creating a new WordPress plugin in the "${ slug }" directory.` );
64
+ info(
65
+ plugin
66
+ ? `Creating a new WordPress plugin in the ${ slug } directory.`
67
+ : `Creating a new block in the ${ slug } directory.`
68
+ );
56
69
 
57
70
  const view = {
58
71
  $schema,
59
72
  apiVersion,
73
+ plugin,
60
74
  namespace,
61
75
  namespaceSnakeCase: snakeCase( namespace ),
62
76
  slug,
63
77
  slugSnakeCase: snakeCase( slug ),
64
- slugPascalCase: upperFirst( camelCase( slug ) ),
78
+ slugPascalCase: pascalCase( slug ),
65
79
  title,
66
80
  description,
67
81
  dashicon,
@@ -85,18 +99,21 @@ module.exports = async (
85
99
  editorScript,
86
100
  editorStyle,
87
101
  style,
102
+ ...variantVars,
88
103
  };
89
104
 
90
- await Promise.all(
91
- Object.keys( pluginOutputTemplates ).map(
92
- async ( outputFile ) =>
93
- await writeOutputTemplate(
94
- pluginOutputTemplates[ outputFile ],
95
- outputFile,
96
- view
97
- )
98
- )
99
- );
105
+ if ( plugin ) {
106
+ await Promise.all(
107
+ Object.keys( pluginOutputTemplates ).map(
108
+ async ( outputFile ) =>
109
+ await writeOutputTemplate(
110
+ pluginOutputTemplates[ outputFile ],
111
+ outputFile,
112
+ view
113
+ )
114
+ )
115
+ );
116
+ }
100
117
 
101
118
  await Promise.all(
102
119
  Object.keys( outputAssets ).map(
@@ -111,21 +128,26 @@ module.exports = async (
111
128
 
112
129
  await initBlock( blockOutputTemplates, view );
113
130
 
114
- await initPackageJSON( view );
115
-
116
- if ( wpScripts ) {
117
- await initWPScripts( view );
118
- }
131
+ if ( plugin ) {
132
+ await initPackageJSON( view );
133
+ if ( wpScripts ) {
134
+ await initWPScripts( view );
135
+ }
119
136
 
120
- if ( wpEnv ) {
121
- await initWPEnv( view );
137
+ if ( wpEnv ) {
138
+ await initWPEnv( view );
139
+ }
122
140
  }
123
141
 
124
142
  info( '' );
143
+
125
144
  success(
126
- `Done: WordPress plugin "${ title }" bootstrapped in the "${ slug }" directory.`
145
+ plugin
146
+ ? `Done: WordPress plugin ${ title } bootstrapped in the ${ slug } directory.`
147
+ : `Done: Block "${ title }" bootstrapped in the ${ slug }directory.`
127
148
  );
128
- if ( wpScripts ) {
149
+
150
+ if ( plugin && wpScripts ) {
129
151
  info( '' );
130
152
  info( 'You can run several commands inside:' );
131
153
  info( '' );
@@ -149,18 +171,18 @@ module.exports = async (
149
171
  info( '' );
150
172
  code( ' $ npm run packages-update' );
151
173
  info( ' Updates WordPress packages to the latest version.' );
174
+ info( '' );
175
+ info( 'To enter the directory type:' );
176
+ info( '' );
177
+ code( ` $ cd ${ slug }` );
152
178
  }
153
- info( '' );
154
- info( 'To enter the directory type:' );
155
- info( '' );
156
- code( ` $ cd ${ slug }` );
157
- if ( wpScripts ) {
179
+ if ( plugin && wpScripts ) {
158
180
  info( '' );
159
181
  info( 'You can start development with:' );
160
182
  info( '' );
161
183
  code( ' $ npm start' );
162
184
  }
163
- if ( wpEnv ) {
185
+ if ( plugin && wpEnv ) {
164
186
  info( '' );
165
187
  info( 'You can start WordPress with:' );
166
188
  info( '' );
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Retrieves the translation of text.
3
3
  *
4
- * @see https://developer.wordpress.org/block-editor/packages/packages-i18n/
4
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
5
5
  */
6
6
  import { __ } from '@wordpress/i18n';
7
7
 
@@ -9,7 +9,7 @@ import { __ } from '@wordpress/i18n';
9
9
  * React hook that is used to mark the block wrapper element.
10
10
  * It provides all the necessary props like the class name.
11
11
  *
12
- * @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps
12
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
13
13
  */
14
14
  import { useBlockProps } from '@wordpress/block-editor';
15
15
 
@@ -25,7 +25,7 @@ import './editor.scss';
25
25
  * The edit function describes the structure of your block in the context of the
26
26
  * editor. This represents what the editor will render when the block is used.
27
27
  *
28
- * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#edit
28
+ * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
29
29
  *
30
30
  * @return {WPElement} Element to render.
31
31
  */
@@ -18,7 +18,9 @@ import './style.scss';
18
18
  * Internal dependencies
19
19
  */
20
20
  import Edit from './edit';
21
+ {{#isStaticVariant}}
21
22
  import save from './save';
23
+ {{/isStaticVariant}}
22
24
  import metadata from './block.json';
23
25
 
24
26
  /**
@@ -31,8 +33,11 @@ registerBlockType( metadata.name, {
31
33
  * @see ./edit.js
32
34
  */
33
35
  edit: Edit,
36
+ {{#isStaticVariant}}
37
+
34
38
  /**
35
39
  * @see ./save.js
36
40
  */
37
41
  save,
42
+ {{/isStaticVariant}}
38
43
  } );
@@ -1,15 +1,9 @@
1
- /**
2
- * Retrieves the translation of text.
3
- *
4
- * @see https://developer.wordpress.org/block-editor/packages/packages-i18n/
5
- */
6
- import { __ } from '@wordpress/i18n';
7
-
1
+ {{#isStaticVariant}}
8
2
  /**
9
3
  * React hook that is used to mark the block wrapper element.
10
4
  * It provides all the necessary props like the class name.
11
5
  *
12
- * @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps
6
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
13
7
  */
14
8
  import { useBlockProps } from '@wordpress/block-editor';
15
9
 
@@ -18,17 +12,15 @@ import { useBlockProps } from '@wordpress/block-editor';
18
12
  * be combined into the final markup, which is then serialized by the block
19
13
  * editor into `post_content`.
20
14
  *
21
- * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#save
15
+ * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
22
16
  *
23
17
  * @return {WPElement} Element to render.
24
18
  */
25
19
  export default function save() {
26
20
  return (
27
21
  <p { ...useBlockProps.save() }>
28
- { __(
29
- '{{title}} – hello from the saved content!',
30
- '{{textdomain}}'
31
- ) }
22
+ { '{{title}} – hello from the saved content!' }
32
23
  </p>
33
24
  );
34
25
  }
26
+ {{/isStaticVariant}}
@@ -0,0 +1,5 @@
1
+ {{#isDynamicVariant}}
2
+ <p <?php echo get_block_wrapper_attributes(); ?>>
3
+ <?php esc_html_e( '{{title}} – hello from a dynamic block!', '{{textdomain}}' ); ?>
4
+ </p>
5
+ {{/isDynamicVariant}}
@@ -75,7 +75,27 @@ function {{namespaceSnakeCase}}_{{slugSnakeCase}}_block_init() {
75
75
  'editor_script' => '{{namespace}}-{{slug}}-block-editor',
76
76
  'editor_style' => '{{namespace}}-{{slug}}-block-editor',
77
77
  'style' => '{{namespace}}-{{slug}}-block',
78
+ {{#isDynamicVariant}}
79
+ 'render_callback' => '{{namespaceSnakeCase}}_{{slugSnakeCase}}_render_callback',
80
+ {{/isDynamicVariant}}
78
81
  )
79
82
  );
80
83
  }
81
84
  add_action( 'init', '{{namespaceSnakeCase}}_{{slugSnakeCase}}_block_init' );
85
+ {{#isDynamicVariant}}
86
+
87
+ /**
88
+ * Render callback function.
89
+ *
90
+ * @param array $attributes The block attributes.
91
+ * @param string $content The block content.
92
+ * @param WP_Block $block Block instance.
93
+ *
94
+ * @return string The rendered output.
95
+ */
96
+ function {{namespaceSnakeCase}}_{{slugSnakeCase}}_render_callback( $attributes, $content, $block ) {
97
+ ob_start();
98
+ require plugin_dir_path( __FILE__ ) . '/template.php';
99
+ return ob_get_clean();
100
+ }
101
+ {{/isDynamicVariant}}
@@ -16,14 +16,14 @@
16
16
  /**
17
17
  * Retrieves the translation of text.
18
18
  *
19
- * @see https://developer.wordpress.org/block-editor/packages/packages-i18n/
19
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
20
20
  */
21
21
  var __ = wp.i18n.__;
22
22
 
23
23
  /**
24
24
  * This hook is used to mark the block wrapper element.
25
25
  *
26
- * @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps
26
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
27
27
  */
28
28
  var useBlockProps = wp.blockEditor.useBlockProps;
29
29
 
@@ -84,7 +84,7 @@
84
84
  * The edit function describes the structure of your block in the context of the editor.
85
85
  * This represents what the editor will render when the block is used.
86
86
  *
87
- * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#edit
87
+ * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
88
88
  *
89
89
  * @return {WPElement} Element to render.
90
90
  */
@@ -95,12 +95,13 @@
95
95
  __( '{{title}} – hello from the editor!', '{{textdomain}}' )
96
96
  );
97
97
  },
98
+ {{#isStaticVariant}}
98
99
 
99
100
  /**
100
101
  * The save function defines the way in which the different attributes should be combined
101
102
  * into the final markup, which is then serialized by the block editor into `post_content`.
102
103
  *
103
- * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#save
104
+ * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
104
105
  *
105
106
  * @return {WPElement} Element to render.
106
107
  */
@@ -108,9 +109,10 @@
108
109
  return el(
109
110
  'p',
110
111
  useBlockProps.save(),
111
- __( '{{title}} – hello from the saved content!', '{{textdomain}}' )
112
+ '{{title}} – hello from the saved content!',
112
113
  );
113
114
  },
115
+ {{/isStaticVariant}}
114
116
  } );
115
117
  }(
116
118
  window.wp
@@ -0,0 +1,5 @@
1
+ {{#isDynamicVariant}}
2
+ <p <?php echo get_block_wrapper_attributes(); ?>>
3
+ <?php esc_html_e( '{{title}} – hello from a dynamic block!', '{{textdomain}}' ); ?>
4
+ </p>
5
+ {{/isDynamicVariant}}
@@ -38,6 +38,33 @@
38
38
  * @see https://developer.wordpress.org/reference/functions/register_block_type/
39
39
  */
40
40
  function {{namespaceSnakeCase}}_{{slugSnakeCase}}_block_init() {
41
+ {{#isStaticVariant}}
41
42
  register_block_type( __DIR__ . '/build' );
43
+ {{/isStaticVariant}}
44
+ {{#isDynamicVariant}}
45
+ register_block_type(
46
+ __DIR__ . '/build',
47
+ array(
48
+ 'render_callback' => '{{namespaceSnakeCase}}_{{slugSnakeCase}}_render_callback',
49
+ )
50
+ );
51
+ {{/isDynamicVariant}}
42
52
  }
43
53
  add_action( 'init', '{{namespaceSnakeCase}}_{{slugSnakeCase}}_block_init' );
54
+ {{#isDynamicVariant}}
55
+
56
+ /**
57
+ * Render callback function.
58
+ *
59
+ * @param array $attributes The block attributes.
60
+ * @param string $content The block content.
61
+ * @param WP_Block $block Block instance.
62
+ *
63
+ * @return string The rendered output.
64
+ */
65
+ function {{namespaceSnakeCase}}_{{slugSnakeCase}}_render_callback( $attributes, $content, $block ) {
66
+ ob_start();
67
+ require plugin_dir_path( __FILE__ ) . 'build/template.php';
68
+ return ob_get_clean();
69
+ }
70
+ {{/isDynamicVariant}}
package/lib/templates.js CHANGED
@@ -24,7 +24,7 @@ const predefinedPluginTemplates = {
24
24
  slug: 'example-static-es5',
25
25
  title: 'Example Static (ES5)',
26
26
  description:
27
- 'Example static block scaffolded with Create Block tool – no build step required.',
27
+ 'Example block scaffolded with Create Block tool – no build step required.',
28
28
  dashicon: 'smiley',
29
29
  wpScripts: false,
30
30
  editorScript: 'file:./index.js',
@@ -32,18 +32,31 @@ const predefinedPluginTemplates = {
32
32
  style: 'file:./style.css',
33
33
  },
34
34
  templatesPath: join( __dirname, 'templates', 'es5' ),
35
+ variants: {
36
+ static: {},
37
+ dynamic: {
38
+ slug: 'example-dynamic-es5',
39
+ title: 'Example Dynamic (ES5)',
40
+ },
41
+ },
35
42
  },
36
- static: {
43
+ standard: {
37
44
  defaultValues: {
38
45
  slug: 'example-static',
39
46
  title: 'Example Static',
40
- description:
41
- 'Example static block scaffolded with Create Block tool.',
47
+ description: 'Example block scaffolded with Create Block tool.',
42
48
  dashicon: 'smiley',
43
49
  supports: {
44
50
  html: false,
45
51
  },
46
52
  },
53
+ variants: {
54
+ static: {},
55
+ dynamic: {
56
+ slug: 'example-dynamic',
57
+ title: 'Example Dynamic',
58
+ },
59
+ },
47
60
  },
48
61
  };
49
62
 
@@ -98,8 +111,9 @@ const externalTemplateExists = async ( templateName ) => {
98
111
  const configToTemplate = async ( {
99
112
  pluginTemplatesPath,
100
113
  blockTemplatesPath,
101
- defaultValues = {},
102
114
  assetsPath,
115
+ defaultValues = {},
116
+ variants = {},
103
117
  ...deprecated
104
118
  } ) => {
105
119
  if ( defaultValues === null || typeof defaultValues !== 'object' ) {
@@ -126,9 +140,10 @@ const configToTemplate = async ( {
126
140
  blockOutputTemplates: blockTemplatesPath
127
141
  ? await getOutputTemplates( blockTemplatesPath )
128
142
  : {},
129
- defaultValues,
130
- outputAssets: assetsPath ? await getOutputAssets( assetsPath ) : {},
131
143
  pluginOutputTemplates: await getOutputTemplates( pluginTemplatesPath ),
144
+ outputAssets: assetsPath ? await getOutputAssets( assetsPath ) : {},
145
+ defaultValues,
146
+ variants,
132
147
  };
133
148
  };
134
149
 
@@ -197,7 +212,7 @@ const getPluginTemplate = async ( templateName ) => {
197
212
  }
198
213
  };
199
214
 
200
- const getDefaultValues = ( pluginTemplate ) => {
215
+ const getDefaultValues = ( pluginTemplate, variant ) => {
201
216
  return {
202
217
  $schema: 'https://schemas.wp.org/trunk/block.json',
203
218
  apiVersion: 2,
@@ -216,11 +231,13 @@ const getDefaultValues = ( pluginTemplate ) => {
216
231
  editorStyle: 'file:./index.css',
217
232
  style: 'file:./style-index.css',
218
233
  ...pluginTemplate.defaultValues,
234
+ ...pluginTemplate.variants?.[ variant ],
235
+ variantVars: getVariantVars( pluginTemplate.variants, variant ),
219
236
  };
220
237
  };
221
238
 
222
- const getPrompts = ( pluginTemplate, keys ) => {
223
- const defaultValues = getDefaultValues( pluginTemplate );
239
+ const getPrompts = ( pluginTemplate, keys, variant ) => {
240
+ const defaultValues = getDefaultValues( pluginTemplate, variant );
224
241
  return keys.map( ( promptName ) => {
225
242
  return {
226
243
  ...prompts[ promptName ],
@@ -229,6 +246,24 @@ const getPrompts = ( pluginTemplate, keys ) => {
229
246
  } );
230
247
  };
231
248
 
249
+ const getVariantVars = ( variants, variant ) => {
250
+ const variantVars = {};
251
+ const variantNames = Object.keys( variants );
252
+ if ( variantNames.length === 0 ) {
253
+ return variantVars;
254
+ }
255
+
256
+ const currentVariant = variant ?? variantNames[ 0 ];
257
+ for ( const variantName of variantNames ) {
258
+ const key =
259
+ variantName.charAt( 0 ).toUpperCase() + variantName.slice( 1 );
260
+ variantVars[ `is${ key }Variant` ] =
261
+ currentVariant === variantName ?? false;
262
+ }
263
+
264
+ return variantVars;
265
+ };
266
+
232
267
  module.exports = {
233
268
  getPluginTemplate,
234
269
  getDefaultValues,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/create-block",
3
- "version": "3.7.0",
3
+ "version": "4.0.1-next.957ca95e4c.0",
4
4
  "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -20,8 +20,8 @@
20
20
  "url": "https://github.com/WordPress/gutenberg/issues"
21
21
  },
22
22
  "engines": {
23
- "node": ">=12",
24
- "npm": ">=6.9"
23
+ "node": ">=14",
24
+ "npm": ">=6.14.4"
25
25
  },
26
26
  "files": [
27
27
  "lib"
@@ -31,7 +31,7 @@
31
31
  "wp-create-block": "./index.js"
32
32
  },
33
33
  "dependencies": {
34
- "@wordpress/lazy-import": "^1.4.2",
34
+ "@wordpress/lazy-import": "^1.4.3-next.957ca95e4c.0",
35
35
  "chalk": "^4.0.0",
36
36
  "change-case": "^4.1.2",
37
37
  "check-node-version": "^4.1.0",
@@ -39,7 +39,6 @@
39
39
  "execa": "^4.0.2",
40
40
  "fast-glob": "^3.2.7",
41
41
  "inquirer": "^7.1.0",
42
- "lodash": "^4.17.21",
43
42
  "make-dir": "^3.0.0",
44
43
  "mustache": "^4.0.0",
45
44
  "npm-package-arg": "^8.1.5",
@@ -49,5 +48,5 @@
49
48
  "publishConfig": {
50
49
  "access": "public"
51
50
  },
52
- "gitHead": "0315dbc240cb2aa146d7c1bafd251f004b88300e"
51
+ "gitHead": "272a74bbbaab10ee24424eafe9578e705fbfbbb4"
53
52
  }