@10up/block-renderer-serializer
v0.3.0
Published
Render WordPress block markup from JSON block trees using native serialization
Downloads
803
Readme
@10up/block-renderer-serializer
Render JSON block trees to WordPress block markup using native @wordpress/blocks serialization. This package converts the flat BlockTree JSON format into valid WordPress block markup.
Installation
npm install @10up/block-renderer-serializer
# or
pnpm add @10up/block-renderer-serializerOverview
This package:
- Sets up browser environment - Provides DOM APIs needed by
@wordpress/blocksin Node.js - Registers blocks - Core blocks and third-party blocks from plugins/themes
- Converts JSON to blocks - Transforms BlockTree format to WordPress block objects
- Serializes to markup - Produces valid
<!-- wp:block-name -->markup
Usage
Basic Rendering
import {
setupBrowserEnvironment,
registerCoreBlocks,
renderBlockTree
} from '@10up/block-renderer-serializer';
// Setup environment (required once at startup)
setupBrowserEnvironment();
await registerCoreBlocks();
// Render a block tree
const tree = {
root: 'para-1',
elements: {
'para-1': {
key: 'para-1',
type: 'core/paragraph',
props: { content: 'Hello world' }
}
}
};
const markup = renderBlockTree(tree);
// <!-- wp:paragraph -->
// <p>Hello world</p>
// <!-- /wp:paragraph -->Nested Blocks
const tree = {
root: 'group-1',
elements: {
'group-1': {
key: 'group-1',
type: 'core/group',
props: { layout: { type: 'constrained' } },
children: ['heading-1', 'para-1']
},
'heading-1': {
key: 'heading-1',
type: 'core/heading',
props: { content: 'Title', level: 2 },
parentKey: 'group-1'
},
'para-1': {
key: 'para-1',
type: 'core/paragraph',
props: { content: 'Content here' },
parentKey: 'group-1'
}
}
};
const markup = renderBlockTree(tree);Multiple Root Blocks
Render multiple separate block trees:
import { renderBlockTrees } from '@10up/block-renderer-serializer';
const trees = [tree1, tree2, tree3];
const markup = renderBlockTrees(trees);Custom Serialization Options
import { renderBlockTree } from '@10up/block-renderer-serializer';
const markup = renderBlockTree(tree, {
// Add newlines between blocks
pretty: true
});Rendering Block Templates
For migration scripts or when working with PHP-style block templates, use renderBlockTemplate:
import {
setupBrowserEnvironment,
registerCoreBlocks,
renderBlockTemplate
} from '@10up/block-renderer-serializer';
setupBrowserEnvironment();
await registerCoreBlocks();
// PHP-style nested array format: [blockType, attributes?, innerBlocks?]
const template = [
['core/paragraph', { content: 'Hello world' }],
['core/group', { layout: { type: 'constrained' } }, [
['core/heading', { content: 'Title', level: 2 }],
['core/paragraph', { content: 'Content here' }],
]],
];
const markup = renderBlockTemplate(template);Block Template Format
Each entry in the template is an array with 1-3 elements:
type BlockTemplateEntry =
| [string] // Just block type
| [string, Record<string, unknown>] // Block type + attributes
| [string, Record<string, unknown>, BlockTemplateEntry[]]; // Full entryExamples:
['core/separator']- Block with no attributes['core/paragraph', { content: 'Hello' }]- Block with attributes['core/group', {}, [['core/paragraph', {}]]]- Block with inner blocks
Convert Template to Blocks
For lower-level access, convert templates to WordPress block objects:
import { convertTemplateToBlocks } from '@10up/block-renderer-serializer';
const blocks = convertTemplateToBlocks(template);
// Returns WordPress BlockInstance[] for further manipulationThird-Party Block Support
The renderer supports third-party blocks from plugins and themes. These blocks must be registered before rendering.
Scan WordPress Installation
Scan a WordPress installation for all available blocks:
import {
scanBlocksFromWpContent,
registerScannedBlocks
} from '@10up/block-renderer-serializer';
// Scan wp-content directory for block.json files
const scannedBlocks = scanBlocksFromWpContent(
'/path/to/wp-content',
'theme-name' // Optional: scan theme blocks too
);
// Register all found blocks
const registeredCount = registerScannedBlocks(scannedBlocks);
console.log(`Registered ${registeredCount} third-party blocks`);This scans:
/wp-content/plugins/*/(excludinggutenbergto avoid duplicates)/wp-content/mu-plugins/*//wp-content/client-mu-plugins/*/(VIP)/wp-content/themes/{theme-name}/blocks/
Register Custom Block Manually
import { registerCustomBlock } from '@10up/block-renderer-serializer';
registerCustomBlock({
name: 'my-plugin/custom-block',
title: 'Custom Block',
attributes: {
title: { type: 'string' }
}
});Path Utilities
import {
getThemeNameFromPath,
getWpContentPathFromTheme
} from '@10up/block-renderer-serializer';
getThemeNameFromPath('/var/www/wp-content/themes/my-theme');
// 'my-theme'
getWpContentPathFromTheme('/var/www/wp-content/themes/my-theme');
// '/var/www/wp-content'Block Registration
Check Registration Status
import {
areCoreBlocksRegistered,
isBlockRegistered,
getRegisteredBlockNames,
getBlocksByCategory
} from '@10up/block-renderer-serializer';
// Check if core blocks are registered
if (!areCoreBlocksRegistered()) {
await registerCoreBlocks();
}
// Check specific block
isBlockRegistered('core/paragraph'); // true
isBlockRegistered('my-plugin/block'); // false (until registered)
// Get all registered block names
const allBlocks = getRegisteredBlockNames();
// ['core/paragraph', 'core/heading', 'core/group', ...]
// Get blocks by category
const categories = getBlocksByCategory();
// Map { 'text' => ['core/paragraph', 'core/heading', ...], ... }Unregister Blocks
import { unregisterBlock } from '@10up/block-renderer-serializer';
unregisterBlock('core/freeform'); // Remove Classic Editor blockBrowser Environment
WordPress block APIs require DOM APIs that don't exist in Node.js. This package sets up a browser-like environment using jsdom.
import {
setupBrowserEnvironment,
isBrowserEnvironmentReady
} from '@10up/block-renderer-serializer';
// Check if already initialized
if (!isBrowserEnvironmentReady()) {
setupBrowserEnvironment();
}The setup must be called before registering any blocks.
Low-Level APIs
Convert Tree to Blocks
Convert without serializing (useful for inspection):
import { convertTreeToBlocks } from '@10up/block-renderer-serializer';
const blocks = convertTreeToBlocks(tree);
// Returns WordPress block objectsConvert Multiple Roots
import { convertMultipleRootsToBlocks } from '@10up/block-renderer-serializer';
const blocks = convertMultipleRootsToBlocks([tree1, tree2]);Validate Tree References
Check that all parent/child references are valid:
import { validateTreeReferences } from '@10up/block-renderer-serializer';
const isValid = validateTreeReferences(tree);
// true if all children/parentKey references existSerialize Block Objects
import { serializeBlocks } from '@10up/block-renderer-serializer';
const markup = serializeBlocks(blocks);Pretty Printing
Format block markup for better readability:
import { formatBlockMarkup } from '@10up/block-renderer-serializer';
import type { PrettyPrintOptions } from '@10up/block-renderer-serializer';
const markup = '<!-- wp:paragraph --><p>Hello</p><!-- /wp:paragraph -->';
const formatted = formatBlockMarkup(markup, {
indentSize: 2, // Spaces per indent level (default: 2)
indentChar: ' ', // Character to use for indentation (default: space)
newlineBetweenBlocks: true // Add newlines between top-level blocks (default: true)
});
// Output:
// <!-- wp:paragraph -->
// <p>Hello</p>
// <!-- /wp:paragraph -->PHP Pattern Transformation
Transform rendered markup into production-ready PHP patterns with internationalization:
import {
transformToPhpPatternSync,
escapeText,
escapeImagePath,
} from '@10up/block-renderer-serializer';
// Transform complete markup
const phpMarkup = transformToPhpPatternSync(markup, {
textDomain: 'my-theme',
assetDirectory: 'assets' // optional, default: 'assets'
});
// Individual escape functions
escapeText('Hello World', 'my-theme');
// "<?php echo esc_html__( 'Hello World', 'my-theme' ); ?>"
escapeImagePath('assets/images/hero.jpg', 'my-theme');
// "<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/hero.jpg"What Gets Transformed
| Content Type | Transformation |
|-------------|----------------|
| Text inside tags (<h2>, <p>, <a>, etc.) | <?php echo esc_html__( 'text', 'domain' ); ?> |
| alt attribute | <?php echo esc_attr__( 'text', 'domain' ); ?> |
| title attribute | <?php echo esc_attr__( 'text', 'domain' ); ?> |
| placeholder attribute | <?php echo esc_attr__( 'text', 'domain' ); ?> |
| aria-label attribute | <?php echo esc_attr__( 'text', 'domain' ); ?> |
| Local image src (with assets/) | <?php echo esc_url( get_template_directory_uri() ); ?>/assets/... |
| Block JSON attributes | Escaped with appropriate function |
Usage with renderBlockTree
import { renderBlockTree } from '@10up/block-renderer-serializer';
// Regular rendering
const markup = renderBlockTree(tree);
// With PHP pattern transformation
const phpMarkup = renderBlockTree(tree, {
phpPattern: {
textDomain: 'my-theme',
assetDirectory: 'assets'
}
});Types
ScannedBlock
interface ScannedBlock {
blockJson: BlockJson;
source: 'plugin' | 'mu-plugin' | 'theme';
sourceName: string;
}SerializeOptions
interface SerializeOptions {
pretty?: boolean;
}Complete Exports
Setup Functions
| Function | Description |
|----------|-------------|
| setupBrowserEnvironment() | Initialize browser environment for Node.js |
| isBrowserEnvironmentReady() | Check if environment is initialized |
Rendering Functions
| Function | Description |
|----------|-------------|
| renderBlockTree(tree, options?) | Render single block tree to markup |
| renderBlockTrees(trees, options?) | Render multiple block trees |
| renderBlockTemplate(template, options?) | Render PHP-style nested array template to markup |
| serializeBlocks(blocks) | Serialize WordPress block objects |
Conversion Functions
| Function | Description |
|----------|-------------|
| convertTreeToBlocks(tree) | Convert BlockTree to WordPress blocks |
| convertTemplateToBlocks(template) | Convert BlockTemplate to WordPress blocks |
| convertMultipleRootsToBlocks(trees) | Convert multiple trees to blocks |
| validateTreeReferences(tree) | Validate tree has valid references |
Registration Functions
| Function | Description |
|----------|-------------|
| registerCoreBlocks() | Register all WordPress core blocks |
| registerCustomBlock(blockJson, settings?) | Register a custom block |
| registerScannedBlocks(blocks) | Register multiple scanned blocks |
| unregisterBlock(name) | Unregister a block |
| areCoreBlocksRegistered() | Check if core blocks are registered |
| isBlockRegistered(name) | Check if specific block is registered |
| getRegisteredBlockNames() | Get all registered block names |
| getBlocksByCategory() | Get blocks grouped by category |
Block Scanning Functions
| Function | Description |
|----------|-------------|
| scanBlocksFromWpContent(wpContentPath, themeName?) | Scan for block.json files |
| getThemeNameFromPath(themePath) | Extract theme name from path |
| getWpContentPathFromTheme(themePath) | Derive wp-content path from theme |
Pretty Printing Functions
| Function | Description |
|----------|-------------|
| formatBlockMarkup(markup, options?) | Format block markup for readability |
PHP Pattern Transformation Functions
| Function | Description |
|----------|-------------|
| transformToPhpPatternSync(markup, options) | Transform markup to PHP pattern |
| escapeText(text, textDomain) | Escape text with esc_html__ |
| escapeImagePath(path, textDomain) | Escape image path with get_template_directory_uri |
Types
| Type | Description |
|------|-------------|
| ScannedBlock | Block found during scanning |
| SerializeOptions | Options for BlockTree serialization |
| TemplateSerializeOptions | Options for BlockTemplate serialization |
| PrettyPrintOptions | Options for formatBlockMarkup |
| BlockTemplate | PHP-style nested array template (from core) |
| BlockTemplateEntry | Single entry in a BlockTemplate (from core) |
License
MIT
