Skip to content

Commit

Permalink
scripts: Add build-blocks-manifest command (WordPress#65866)
Browse files Browse the repository at this point in the history
* scripts: Add build-blocks-manifest command

* Remove package.json entry

* add test

* npm install in root to update lockfile

* Add changelog entry

* error on empty and missing dirs, update tests

* Update CHANGELOG.md

---------

Co-authored-by: Greg Ziółkowski <[email protected]>

Co-authored-by: mreishus <[email protected]>
Co-authored-by: gziolo <[email protected]>
Co-authored-by: felixarntz <[email protected]>
Co-authored-by: sirreal <[email protected]>
Co-authored-by: jsnajdr <[email protected]>
  • Loading branch information
6 people authored Oct 17, 2024
1 parent 8aa9985 commit 76cf075
Show file tree
Hide file tree
Showing 11 changed files with 433 additions and 0 deletions.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/scripts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

- Refactor to extract license related logic to a reusable module ([#66179](https://github.com/WordPress/gutenberg/pull/66179)).

### New Features

- Add new `build-blocks-manifest` command to generate a PHP file containing block metadata from all `block.json` files in a project ([#65866](https://github.com/WordPress/gutenberg/pull/65866)).

## 30.2.0 (2024-10-16)

## 30.1.0 (2024-10-03)
Expand Down
35 changes: 35 additions & 0 deletions packages/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,41 @@ and should be registered in WordPress using the Modules API.

This script uses [webpack](https://webpack.js.org/) behind the scenes. It’ll look for a webpack config in the top-level directory of your package and will use it if it finds one. If none is found, it’ll use the default config provided by `@wordpress/scripts` packages. Learn more in the [Advanced Usage](#advanced-usage) section.


### `build-blocks-manifest`

This script generates a PHP file containing block metadata from all
`block.json` files in the project. This is useful for enhancing performance
when registering multiple block types, as it allows you to use
`wp_register_block_metadata_collection()` in WordPress.

Usage: `wp-scripts build-blocks-manifest [options]`

Options:
- `--input`: Specify the input directory (default: 'build')
- `--output`: Specify the output file path (default: 'build/blocks-manifest.php')

Example:
```bash
wp-scripts build-blocks-manifest --input=src --output=dist/blocks-manifest.php
```

This command will scan the specified input directory for `block.json` files,
compile their metadata into a single PHP file, and output it to the specified
location. You can then use this file with
`wp_register_block_metadata_collection()` in your plugin:

```php
wp_register_block_metadata_collection(
plugin_dir_path( __FILE__ ) . 'dist',
plugin_dir_path( __FILE__ ) . 'dist/blocks-manifest.php'
);
```

Using this approach can improve performance when registering multiple block
types, especially for plugins with several custom blocks. Note that this
feature is only available in WordPress 6.7 and later versions.

### `check-engines`

Checks if the current `node`, `npm` (or `yarn`) versions match the given [semantic version](https://semver.org/) ranges. If the given version is not satisfied, information about installing the needed version is printed and the program exits with an error code.
Expand Down
1 change: 1 addition & 0 deletions packages/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"jest-dev-server": "^9.0.1",
"jest-environment-jsdom": "^29.6.2",
"jest-environment-node": "^29.6.2",
"json2php": "^0.0.9",
"markdownlint-cli": "^0.31.1",
"merge-deep": "^3.0.3",
"mini-css-extract-plugin": "^2.5.1",
Expand Down
72 changes: 72 additions & 0 deletions packages/scripts/scripts/build-blocks-manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* External dependencies
*/
const fs = require( 'fs' );
const path = require( 'path' );
const { sync: glob } = require( 'fast-glob' );
const json2php = require( 'json2php' );
const chalk = require( 'chalk' );

/**
* Internal dependencies
*/
const { getArgFromCLI } = require( '../utils' );

// Set default paths
const defaultInputDir = 'build';
const defaultOutputFile = path.join( 'build', 'blocks-manifest.php' );

// Parse command line arguments
const inputDir = getArgFromCLI( '--input' ) || defaultInputDir;
const outputFile = getArgFromCLI( '--output' ) || defaultOutputFile;

const resolvedInputDir = path.resolve( process.cwd(), inputDir );
if ( ! fs.existsSync( resolvedInputDir ) ) {
const ERROR = chalk.reset.inverse.bold.red( ' ERROR ' );
process.stdout.write(
`${ ERROR } Input directory "${ inputDir }" does not exist.\n`
);
process.exit( 1 );
}

// Find all block.json files
const blockJsonFiles = glob( './**/block.json', {
cwd: resolvedInputDir,
absolute: true,
} );

const blocks = {};

blockJsonFiles.forEach( ( file ) => {
const blockJson = JSON.parse( fs.readFileSync( file, 'utf8' ) );
const directoryName = path.basename( path.dirname( file ) );
blocks[ directoryName ] = blockJson;
} );

if ( Object.keys( blocks ).length === 0 ) {
const ERROR = chalk.reset.inverse.bold.red( ' ERROR ' );
process.stdout.write(
`${ ERROR } No block.json files were found in path: ${ inputDir }.\n`
);
process.exit( 1 );
}

// Generate PHP content
const printer = json2php.make( { linebreak: '\n', indent: '\t' } );
const phpContent = `<?php
// This file is generated. Do not modify it manually.
return ${ printer( blocks ) };
`;

// Ensure output directory exists
const outputDir = path.dirname( outputFile );
if ( ! fs.existsSync( outputDir ) ) {
fs.mkdirSync( outputDir, { recursive: true } );
}

// Write the file
fs.writeFileSync( outputFile, phpContent );

process.stdout.write(
`Block metadata PHP file generated at: ${ outputFile }\n`
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`build-blocks-manifest script should generate expected blocks manifest 1`] = `
"<?php
// This file is generated. Do not modify it manually.
return array(
'custom-header' => array(
'$schema' => 'https://schemas.wp.org/trunk/block.json',
'apiVersion' => 2,
'name' => 'my-plugin/custom-header',
'title' => 'Custom Header',
'category' => 'text',
'icon' => 'heading',
'description' => 'A custom header block with color options.',
'attributes' => array(
'content' => array(
'type' => 'string',
'source' => 'html',
'selector' => 'h2'
),
'textColor' => array(
'type' => 'string'
),
'backgroundColor' => array(
'type' => 'string'
)
),
'supports' => array(
'html' => false,
'color' => array(
'background' => true,
'text' => true
)
),
'textdomain' => 'my-plugin',
'editorScript' => 'file:./index.js',
'editorStyle' => 'file:./index.css',
'style' => 'file:./style-index.css'
),
'image-gallery' => array(
'$schema' => 'https://schemas.wp.org/trunk/block.json',
'apiVersion' => 2,
'name' => 'my-plugin/image-gallery',
'title' => 'Image Gallery',
'category' => 'media',
'icon' => 'format-gallery',
'description' => 'An image gallery block with customizable layout.',
'attributes' => array(
'images' => array(
'type' => 'array',
'default' => array(
),
'source' => 'query',
'selector' => 'img',
'query' => array(
'url' => array(
'type' => 'string',
'source' => 'attribute',
'attribute' => 'src'
),
'alt' => array(
'type' => 'string',
'source' => 'attribute',
'attribute' => 'alt',
'default' => ''
),
'id' => array(
'type' => 'number',
'source' => 'attribute',
'attribute' => 'data-id'
)
)
),
'columns' => array(
'type' => 'number',
'default' => 3
),
'imageCrop' => array(
'type' => 'boolean',
'default' => true
)
),
'supports' => array(
'align' => array(
'wide',
'full'
)
),
'textdomain' => 'my-plugin',
'editorScript' => 'file:./index.js',
'editorStyle' => 'file:./index.css',
'style' => 'file:./style-index.css'
),
'simple-button' => array(
'$schema' => 'https://schemas.wp.org/trunk/block.json',
'apiVersion' => 2,
'name' => 'my-plugin/simple-button',
'title' => 'Simple Button',
'category' => 'design',
'icon' => 'button',
'description' => 'A simple button block.',
'supports' => array(
'html' => false
),
'textdomain' => 'my-plugin',
'editorScript' => 'file:./index.js',
'editorStyle' => 'file:./index.css',
'style' => 'file:./style-index.css'
)
);
"
`;
101 changes: 101 additions & 0 deletions packages/scripts/scripts/test/build-blocks-manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* External dependencies
*/
const fs = require( 'fs' );
const path = require( 'path' );
const { execSync } = require( 'child_process' );
const rimraf = require( 'rimraf' );

const fixturesPath = path.join(
__dirname,
'fixtures',
'build-blocks-manifest'
);
const outputPath = path.join( __dirname, 'build', 'test-blocks-manifest' );

describe( 'build-blocks-manifest script', () => {
beforeAll( () => {
if ( ! fs.existsSync( outputPath ) ) {
fs.mkdirSync( outputPath, { recursive: true } );
}
rimraf.sync( outputPath );
} );

afterAll( () => {
rimraf.sync( outputPath );
} );

it( 'should generate expected blocks manifest', () => {
const inputDir = path.join( fixturesPath, 'input' );
const outputFile = path.join( outputPath, 'blocks-manifest.php' );

// Run the build-blocks-manifest script
const scriptPath = path.resolve(
__dirname,
'..',
'build-blocks-manifest.js'
);
execSync(
`node ${ scriptPath } --input=${ inputDir } --output=${ outputFile }`
);

const generatedContent = fs.readFileSync( outputFile, 'utf8' );
expect( generatedContent ).toMatchSnapshot();
} );

it( 'should error on empty input directory', () => {
const emptyInputDir = path.join( fixturesPath, 'empty-input' );
const outputFile = path.join( outputPath, 'empty-blocks-manifest.php' );

const scriptPath = path.resolve(
__dirname,
'..',
'build-blocks-manifest.js'
);
let error;
try {
execSync(
`node ${ scriptPath } --input=${ emptyInputDir } --output=${ outputFile }`,
{ encoding: 'utf8' }
);
} catch ( e ) {
error = e;
}

// Check that an error was thrown.
expect( error ).toBeDefined();
expect( error.stdout ).toContain(
`No block.json files were found in path`
);

// Ensure that the output file was not created
expect( fs.existsSync( outputFile ) ).toBe( false );
} );

it( 'should error on missing input directory', () => {
const nonExistentInputDir = path.join( fixturesPath, 'missing-input' );
const outputFile = path.join( outputPath, 'empty-blocks-manifest.php' );

const scriptPath = path.resolve(
__dirname,
'..',
'build-blocks-manifest.js'
);
let error;
try {
execSync(
`node ${ scriptPath } --input=${ nonExistentInputDir } --output=${ outputFile }`,
{ encoding: 'utf8' }
);
} catch ( e ) {
error = e;
}

// Check that an error was thrown.
expect( error ).toBeDefined();
expect( error.stdout ).toContain( `does not exist` );

// Ensure that the output file was not created
expect( fs.existsSync( outputFile ) ).toBe( false );
} );
} );
Empty file.
Loading

0 comments on commit 76cf075

Please sign in to comment.