{"id":32682,"date":"2020-11-03T00:42:22","date_gmt":"2020-11-03T00:42:22","guid":{"rendered":"https:\/\/www.designbombs.com\/?p=32682"},"modified":"2020-11-10T09:10:19","modified_gmt":"2020-11-10T09:10:19","slug":"reusing-functionality-for-wordpress-plugins-with-blocks","status":"publish","type":"post","link":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/","title":{"rendered":"Reusing Functionality for WordPress Plugins with Blocks"},"content":{"rendered":"\n<p>When creating a plugin shipping one or more blocks for the WordPress editor, what is the best way to organize the plugin as to make its different components reusable?<\/p>\n\n\n\n<p>The block directory (launched in WordPress 5.5) enables to install a block while writing a post in the WordPress editor. This feature might persuade plugin developers to ship their blocks through single-block plugins, which can be installed through the block directory, instead of through multi-block plugins, which cannot.<\/p>\n\n\n\n<p>However, there are many situations for which multi-block plugins shouldn&#8217;t be transformed to a series of single-block plugins, such as when:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The blocks are similar, and share plenty of logic<\/li><li>The plugin implements a custom post type (CPT), and we want to enhance it with its own set of blocks<\/li><li>Shipping many blocks together makes the product better than shipping them separately<\/li><li>We don&#8217;t need to publish our blocks to the block directory, as when an agency creates a blocks for its clients<\/li><\/ul>\n\n\n\n<p>I find the first item, when blocks share plenty of logic, of particular interest. Moreover, also single-block plugins could need to provide common logic, not to blocks, but to the different components within the block.<\/p>\n\n\n\n<p>In this article, we will tackle two considerations:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>What is the best way to create and manage a multi-block plugin?<\/li><li>What is the most effective way to reuse code within a (single or multi-block) plugin?<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-repurposing-wordpress-create-block-to-create-multi-block-plugins\">Repurposing <code>@wordpress\/create-block<\/code> to create multi-block plugins<\/h2>\n\n\n\n<p><a href=\"https:\/\/developer.wordpress.org\/block-editor\/packages\/packages-create-block\/\"><code>@wordpress\/create-block<\/code><\/a>, the official package to scaffold blocks created and maintained by the team developing Gutenberg, tackles most of the complexities associated with modern JavaScript projects, allowing us to focus on the block&#8217;s business logic.<\/p>\n\n\n\n<p>For instance, <code>@wordpress\/create-block<\/code> provides a <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/blob\/master\/packages\/scripts\/config\/webpack.config.js\">default configuration<\/a> fo <a href=\"https:\/\/webpack.js.org\/\">webpack<\/a> (the module bundler at the core of Gutenberg), designed to cover the majority of cases, and we can <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/master\/packages\/scripts#provide-your-own-webpack-config\">override the configuration<\/a> whenever we need more control.<\/p>\n\n\n\n<p>Currently, <code>@wordpress\/create-block<\/code> can only tackle creating single-block plugins, not multi-block plugins (hopefully, in the not so distant future, it will be possible to <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/issues\/23514#issuecomment-650910611\">generate any type of output through templates<\/a>). However, with a bit of extra effort, we can already <a href=\"https:\/\/blog.logrocket.com\/setting-up-first-gutenberg-project\/\">leverage <code>@wordpress\/create-block<\/code> to create multi-block plugins<\/a> too.<\/p>\n\n\n\n<p>The goal is to create a folder <code>blocks\/<\/code> within the plugin, and then create there all the required blocks, all of them independent from each other. The structure of the plugin will be this one:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nmy-plugin\/\n\u2502\n\u251c\u2500\u2500blocks\/\n\u2502 \u251c\u2500\u2500block1\/\n\u2502 \u251c\u2500\u2500block2\/\n\u2502 \u2514\u2500\u2500block3\/\n\u2502\n\u2514\u2500\u2500 my-plugin.php\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-using-packages-to-share-code-among-the-blocks\">Using packages to share code among the blocks<\/h2>\n\n\n\n<p>Different blocks in the plugin may require some common functionality.<\/p>\n\n\n\n<p>For instance, I have built a plugin which uses blocks to configure its several custom post types, one custom block per CPT. On their left side, the configuration blocks use a common component to select the elements to configure, which are shared across CPTs, and on the right side, each block displays the custom properties for that CPT:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"380\" src=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-1-1024x380.png\" alt=\"\" class=\"wp-image-32684\" srcset=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-1-1024x380.png 1024w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-1-300x111.png 300w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-1-768x285.png 768w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-1-1536x570.png 1536w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-1-2048x760.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>A configuration block, with shared functionality<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"413\" src=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-2-1024x413.png\" alt=\"\" class=\"wp-image-32685\" srcset=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-2-1024x413.png 1024w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-2-300x121.png 300w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-2-768x310.png 768w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-2-1536x619.png 1536w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/configuration-block-2-2048x826.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Another configuration block, with shared functionality<\/figcaption><\/figure>\n\n\n\n<p>That shared functionality on the left side, where should it live? On both blocks? On only one of them, and the other one access it from there? Or where else?<\/p>\n\n\n\n<p>The best solution is to extract the shared logic out from the blocks, put it into packages, and have the blocks import the functionalities from the packages. We can create as many packages as needed, each of them containing all the functionality for some specific topic or category (eg: data access and storage, user interface, internationalization, etc).<\/p>\n\n\n\n<p>We place all packages under a <code>packages\/<\/code> folder in the plugin. The plugin&#8217;s structure now becomes:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nmy-plugin\/\n\u2502\n\u251c\u2500\u2500blocks\/\n\u2502 \u251c\u2500\u2500block1\/\n\u2502 \u251c\u2500\u2500block2\/\n\u2502 \u2514\u2500\u2500block3\/\n\u2502\n\u251c\u2500\u2500packages\/\n\u2502 \u251c\u2500\u2500package1\/\n\u2502 \u2514\u2500\u2500package2\/\n\u2502\n\u2514\u2500\u2500 my-plugin.php\n<\/pre><\/div>\n\n\n<p>There is a potential issue we must deal with. When running <code>npm run build<\/code> to compile the block, webpack will be loading code that involves more than one webpack configuration: the one from the block, and the configuration from each of the referenced packages. And this <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/issues\/23607#issuecomment-656026433\">can create conflict<\/a>.<\/p>\n\n\n\n<p>To solve it, we need to create a custom <code>webpack.config.js<\/code> file for the block (if it doesn&#8217;t already exist):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nmy-plugin\/\n\u2514\u2500\u2500blocks\/\n  \u2514\u2500\u2500my-block\/\n     \u2514\u2500\u2500webpack.config.js\n<\/pre><\/div>\n\n\n<p>And, within this file, we must define a unique value for property <code>config.output.jsonpFunction<\/code> (otherwise, all blocks use the default name <code>\"webpackJsonp\"<\/code>):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; gutter: false; title: ; notranslate\" title=\"\">\nconst config = require( &#039;@wordpress\/scripts\/config\/webpack.config&#039; );\n\nconfig.output.jsonpFunction = &quot;ADD_SOME_UNIQUE_NAME_HERE&quot;;\n\nmodule.exports = config;\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-creating-and-referencing-a-package\">Creating and referencing a package<\/h2>\n\n\n\n<p>Similar to a block, a package is also a modern JavaScript project, so it is affected by the same complex underpinnings as blocks are.<\/p>\n\n\n\n<p>Concerning blocks, these complexities are taken care of by the <code>@wordpress\/create-block<\/code> block-scaffolding tool, through the structure of the block, and the dependency on <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/master\/packages\/scripts\"><code>@wordpress\/scripts<\/code><\/a> declared on the block&#8217;s <code>package.json<\/code> file.<\/p>\n\n\n\n<p>So let&#8217;s copy these over to the package. The structure of the package is this one:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nmy-package\/\n\u251c\u2500\u2500src\/\n\u2502 \u2514\u2500\u2500index.js\n\u2514\u2500\u2500 package.json\n<\/pre><\/div>\n\n\n<p>The contents of <code>package.json<\/code> are the same ones as when creating a new block, plus the addition of entry <code>\"module\": \"src\/index.js\"<\/code>, required to tell the blocks where to find the source code for the modules to import.<\/p>\n\n\n\n<p>It is a good idea to group the plugin&#8217;s packages under the plugin namespace <code>@my-plugin<\/code>. Then, the package name becomes <code>\"@my-plugin\/my-package\"<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\n{\n  &quot;name&quot;: &quot;@my-plugin\/my-package&quot;,\n  &quot;version&quot;: &quot;0.1.0&quot;,\n  &quot;description&quot;: &quot;Common components for the blocks in the plugin&quot;,\n  &quot;author&quot;: &quot;Your name&quot;,\n  &quot;license&quot;: &quot;Your license&quot;,\n  &quot;main&quot;: &quot;build\/index.js&quot;,\n  &quot;module&quot;: &quot;src\/index.js&quot;,\n  &quot;scripts&quot;: {\n    &quot;build&quot;: &quot;wp-scripts build&quot;,\n    &quot;format:js&quot;: &quot;wp-scripts format-js&quot;,\n    &quot;lint:css&quot;: &quot;wp-scripts lint-style&quot;,\n    &quot;lint:js&quot;: &quot;wp-scripts lint-js&quot;,\n    &quot;start&quot;: &quot;wp-scripts start&quot;,\n    &quot;packages-update&quot;: &quot;wp-scripts packages-update&quot;\n  },\n  &quot;devDependencies&quot;: {\n    &quot;@wordpress\/scripts&quot;: &quot;^12.1.0&quot;\n  }\n}\n<\/pre><\/div>\n\n\n<p>In order to import modules from a package, the block needs to add the package as a dependency. Since blocks and packages live in the same plugin, the dependency can point to a local folder.<\/p>\n\n\n\n<p>To do this, in a terminal window browse to the block folder:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\ncd path-to-my-plugin\/blocks\/my-block\n<\/pre><\/div>\n\n\n<p>And then execute:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nnpm install --save-dev ..\/..\/packages\/my-package\n<\/pre><\/div>\n\n\n<p>As a result, we can observe that the block&#8217;s <code>package.json<\/code> will contain a local dependency to the package, like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\n{\n  &quot;devDependencies&quot;: {\n    &quot;@my-plugin\/my-package&quot;: &quot;file:..\/..\/packages\/my-package&quot;\n  }\n}\n<\/pre><\/div>\n\n\n<p>And in the block&#8217;s <code>node_modules\/<\/code> folder, there will be a new symlink <code>@my-plugin\/my-package<\/code> pointing to the package&#8217;s source folder.<\/p>\n\n\n\n<p>If we decide to, a package can also be published to the <a href=\"https:\/\/www.npmjs.com\">npm directory<\/a>, as to make it accessible for other parties too, not just to be used within our plugin. Then, these other parties can install it as any other dependency:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nnpm install --save-dev @my-plugin\/my-package\n<\/pre><\/div>\n\n\n<p>Within the same plugin, though, it makes sense to install the package as a local dependency, because it allows the block to access the code from the package directly from its source, without needing to publish a new version to the registry first, making development much faster.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-importing-modules-from-the-package\">Importing modules from the package<\/h2>\n\n\n\n<p>Let&#8217;s say that a component <code>&lt;CustomSelect&gt;<\/code> stored under <code>block1<\/code> is then also required by <code>block2<\/code>. To make it accessible by all blocks, we extract the component out from the block, and move it to package <code>@my-plugin\/components<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\ncomponents\/\n\u2514\u2500\u2500src\/\n  \u251c\u2500\u2500custom-select.js\n  \u2514\u2500\u2500index.js\n<\/pre><\/div>\n\n\n<p>Then, we <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/export\"><code>export<\/code><\/a> the component from the package&#8217;s <code>index.js<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; gutter: false; title: ; notranslate\" title=\"\">\nexport { CustomSelect } from &#039;.\/custom-select&#039;;\n<\/pre><\/div>\n\n\n<p>To make sure that the package compiles well, and that the component is being exported, we step on the package folder in the terminal, and execute:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nnpm start\n<\/pre><\/div>\n\n\n<p>(Before then, we must have downloaded all the JavaScript dependencies in the <code>node_modules\/<\/code> folder, done by running <code>npm install<\/code>.)<\/p>\n\n\n\n<p>The output in the console will indicate if there is any error in the code, and what files were included:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"829\" src=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/npm-start-package-1024x829.png\" alt=\"\" class=\"wp-image-32686\" srcset=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/npm-start-package-1024x829.png 1024w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/npm-start-package-300x243.png 300w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/npm-start-package-768x622.png 768w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/npm-start-package.png 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Building a package with npm start<\/figcaption><\/figure>\n\n\n\n<p>The package is compiled under file <code>build\/index.js<\/code>, which is not referenced by the block, so we can delete it safely if we decide to. The package&#8217;s structure now looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\ncomponents\/\n\u251c\u2500\u2500src\/\n\u2502 \u251c\u2500\u2500custom-select.js\n\u2502 \u2514\u2500\u2500index.js\n\u2514\u2500\u2500build\/\n  \u2514\u2500\u2500index.js\n<\/pre><\/div>\n\n\n<p>Finally, we can <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/import\"><code>import<\/code><\/a> and use the component in our block:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; gutter: false; title: ; notranslate\" title=\"\">\nimport { CustomSelect } from &#039;@my-plugin\/components&#039;;\n\nconst BlockCustomSelect = () =&gt; (\n  &lt;CustomSelect\n    options={ &#x5B;&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;] }\n  \/&gt;\n)\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-managing-the-node_modules-oversize\">Managing the node_modules oversize<\/h2>\n\n\n\n<p>All blocks and packages in the plugin are independent from each other, which means that each of them has its own <code>package.json<\/code> file, and will require its own <code>node_modules\/<\/code> folder for storing its JavaScript dependencies.<\/p>\n\n\n\n<p>This is a problem, because the <code>node_modules\/<\/code> folder can demand an incredibly big amount of space, as this famous image makes clear:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"990\" height=\"712\" src=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/heaviest-objects-in-universe.jpg\" alt=\"\" class=\"wp-image-32687\" srcset=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/heaviest-objects-in-universe.jpg 990w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/heaviest-objects-in-universe-300x216.jpg 300w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/heaviest-objects-in-universe-768x552.jpg 768w, https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/heaviest-objects-in-universe-84x60.jpg 84w\" sizes=\"auto, (max-width: 990px) 100vw, 990px\" \/><figcaption>Heaviest objects in the universe<\/figcaption><\/figure>\n\n\n\n<p>Then, if our plugin has 10 blocks, it will require 10 <code>node_modules\/<\/code> folders for development, which can make our computers quickly run out of space.<\/p>\n\n\n\n<p>A solution to this problem is to keep a single <code>node_modules\/<\/code> folder containing all the dependencies used by all blocks, and then create a symlink (which is a pointer to some file or folder) under every block&#8217;s directory, pointing to this single <code>node_modules\/<\/code> folder (the symlink must have name <code>node_modules<\/code>).<\/p>\n\n\n\n<p>For this strategy to work, all blocks must have the same version of their dependencies. For instance, if a block depends on the latest version of <code>@wordpress\/scripts<\/code>, which is <code>12.1.0<\/code>, then all blocks depending on <code>@wordpress\/scripts<\/code> must also use this same version.<\/p>\n\n\n\n<p>Forcing blocks to use the same version for dependencies takes some independence away from the blocks. However, it also provides the plugin with integrity, since it removes the chance of some error arising from interacting with different versions of the same dependency.<\/p>\n\n\n\n<p>Let&#8217;s put this solution into practice. Since the <code>node_modules\/<\/code> folder is required only when developing the plugin, we can create a folder <code>development\/<\/code> in the root of the plugin (at the same level of <code>blocks\/<\/code> and <code>packages\/<\/code>), with a <code>package.json<\/code> file containing all the dependencies from all the blocks:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nmy-plugin\/\n\u2502\n\u251c\u2500\u2500blocks\/\n\u2502 \u251c\u2500\u2500block1\/\n\u2502 \u2502 \u2514\u2500\u2500package.json\n\u2502 \u251c\u2500\u2500block2\/\n\u2502 \u2502 \u2514\u2500\u2500package.json\n\u2502 \u2514\u2500\u2500block3\/\n\u2502   \u2514\u2500\u2500package.json\n\u2502\n\u251c\u2500\u2500development\/\n\u2502 \u2514\u2500\u2500package.json\n\u2502\n\u251c\u2500\u2500packages\/\n\u2502 \u251c\u2500\u2500package1\/\n\u2502 \u2502 \u2514\u2500\u2500package.json\n\u2502 \u2514\u2500\u2500package2\/\n\u2502   \u2514\u2500\u2500package.json\n\u2502\n\u2514\u2500\u2500 my-plugin.php\n<\/pre><\/div>\n\n\n<p>Then, we download install all JavaScript dependencies, by executing in the terminal:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n$ cd path-to-my-plugin\/development\n$ npm install\n<\/pre><\/div>\n\n\n<p>All the dependencies are now stored under the single folder <code>development\/node_modules\/<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nmy-plugin\/\n\u2514\u2500\u2500development\/\n  \u251c\u2500\u2500node_modules\/\n  \u2502  \u251c\u2500\u2500dependency1\/\n  \u2502  \u251c\u2500\u2500dependency2\/\n  \u2502  \u2514\u2500\u2500dependency3\/\n  \u2514\u2500\u2500package.json\n<\/pre><\/div>\n\n\n<p>Next, let&#8217;s have each block use this single <code>node_modules\/<\/code> folder by symlinking to it. For this, we step on each block directory, and execute:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nln -snf ..\/..\/development\/node_modules .\n<\/pre><\/div>\n\n\n<p>Creating the symlinks for all blocks can be automated. The following bash script, stored as <code>development\/create-symlinks.sh<\/code>, iterates through all the directories under <code>blocks\/<\/code>, and executes the <code>ln<\/code> command to create the symlink on each of them:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n#!\/bin\/bash\n# Create the symlinks for all blocks in the plugin to the single node_modules\/ folder\n# Make sure package.json contains ALL dependencies needed for all blocks\n\n# Directory of the bash script\nDIR=$(cd $(dirname ${BASH_SOURCE&#x5B;0]}) &amp;&amp; pwd)\n\n# Single node_modules\/ folder\nNODE_MODULES_DIR=&quot;$DIR\/node_modules\/&quot;\n\n# Function `createSymlinks` will create a &#039;node_modules\/&#039; symlink under every subfolder in the current directory\ncreateSymlinks(){\n  # Iterate all subdirectories (which are the blocks)\n  for file in .\/*\n  do\n    if &#x5B; -d &quot;$file&quot; ]; then\n      # Create symlink\n      ln -snf &quot;$NODE_MODULES_DIR&quot; &quot;$file&quot;\n    fi\n  done\n}\n\n# Step on the root folder where all blocks are located\ncd &quot;$DIR\/..\/blocks\/&quot;\n\n# Create the symlinks for all the blocks\ncreateSymlinks\n<\/pre><\/div>\n\n\n<p>The bash script is executed like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nbash -x path-to-my-plugin\/development\/create-symlinks.sh\n<\/pre><\/div>\n\n\n<p>This script creates symlinks for blocks, but not for packages. This is because, for some reason unknown to me (possibly a bug with Node.js, or maybe some incompatibility with Gutenberg&#8217;s dependencies), the block is not built correctly when it references packages which symlink to the <code>node_modules\/<\/code> folder.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-automating-building-blocks\">Automating building blocks<\/h2>\n\n\n\n<p>Once development is ready, we need to build the block for production. This is done by browsing to the block directory in the terminal, and then executing:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nnpm run build\n<\/pre><\/div>\n\n\n<p>This command will produce the optimized script file <code>build\/index.js<\/code>, and generate the sylesheets <code>build\/index.css<\/code> and <code>build\/style-index.css<\/code>.<\/p>\n\n\n\n<p>If we have many blocks in the plugin, this can become tedious. However, this task can also be automated with a bash script, stored as <code>development\/build-all-blocks.sh<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n#!\/bin\/bash\n# This bash script builds all the blocks\n\n# Directory of the bash script\nDIR=$(cd $( dirname ${BASH_SOURCE&#x5B;0]}) &amp;&amp; pwd)\n\n# Function `buildScripts` will run `npm run build` on all subfolders in the current directory\nbuildScripts(){\n  for file in .\/*\n  do\n    if &#x5B; -d &quot;$file&quot; ]; then\n      cd &quot;$file&quot;\n      npm run build\n      cd ..\n    fi\n  done\n}\n\n# Step on the root folder where all blocks are located\ncd &quot;$DIR\/..\/blocks\/&quot;\n\n# Build all blocks\nbuildScripts\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-automating-publishing-packages\">Automating publishing packages<\/h2>\n\n\n\n<p>If we have many packages in our plugin, and we decide to expose them to 3rd parties through the npm registry, we can use a specialized tool for this.<\/p>\n\n\n\n<p><a href=\"https:\/\/lerna.js.org\/\">Lerna<\/a> is a tool that optimizes the workflow around managing multi-package repositories. It enables to publish many packages together, running a single command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nlerna publish\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\n<p>The recently launched block directory works with single-block plugins only. However, it doesn&#8217;t always make sense to ship all our functionality through a series of single-block plugins, and using a multi-block plugin is still more suitable.<\/p>\n\n\n\n<p>In addition, on both multi-block and single-block plugins we may find shared code, reused across blocks and components. Distributing this code through packages, available not just to our plugin but also to 3rd parties, is a sensible approach.<\/p>\n\n\n\n<p>In this article we learnt how to create a multi-block plugin, how to extract shared code into packages, and how to manage the complexity of dealing with many blocks and packages.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When creating a plugin shipping one or more blocks for the WordPress editor, what is the best way to organize&#8230;<\/p>\n","protected":false},"author":50,"featured_media":32932,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[157],"tags":[],"class_list":{"0":"post-32682","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-wordpress-tutorials","8":"entry"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v18.1 (Yoast SEO v26.4) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Reusing Functionality for WordPress Plugins with Blocks<\/title>\n<meta name=\"description\" content=\"When working with the WordPress editor, whether creating a multi-block or a single-block plugin, we need to find the most effective way to reuse code.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Reusing Functionality for WordPress Plugins with Blocks\" \/>\n<meta property=\"og:description\" content=\"When creating a plugin shipping one or more blocks for the WordPress editor, what is the best way to organize the plugin as to make its different\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\" \/>\n<meta property=\"og:site_name\" content=\"Design Bombs\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/designbombs\/\" \/>\n<meta property=\"article:published_time\" content=\"2020-11-03T00:42:22+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-11-10T09:10:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png\" \/>\n\t<meta property=\"og:image:width\" content=\"800\" \/>\n\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Leonardo Losoviz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@designbombs\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/www.designbombs.com\/#\/schema\/person\/4259b761dbd3ee01f8c7e2cd9d74df39\"},\"headline\":\"Reusing Functionality for WordPress Plugins with Blocks\",\"datePublished\":\"2020-11-03T00:42:22+00:00\",\"dateModified\":\"2020-11-10T09:10:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\"},\"wordCount\":1750,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.designbombs.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png\",\"articleSection\":[\"WordPress Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\",\"url\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\",\"name\":\"Reusing Functionality for WordPress Plugins with Blocks\",\"isPartOf\":{\"@id\":\"https:\/\/www.designbombs.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png\",\"datePublished\":\"2020-11-03T00:42:22+00:00\",\"dateModified\":\"2020-11-10T09:10:19+00:00\",\"description\":\"When working with the WordPress editor, whether creating a multi-block or a single-block plugin, we need to find the most effective way to reuse code.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage\",\"url\":\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png\",\"contentUrl\":\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png\",\"width\":800,\"height\":350,\"caption\":\"Reusing functionality for WordPress plugins with blocks\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.designbombs.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorials\",\"item\":\"https:\/\/www.designbombs.com\/category\/tutorials\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"WordPress Tutorials\",\"item\":\"https:\/\/www.designbombs.com\/category\/tutorials\/wordpress-tutorials\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Reusing Functionality for WordPress Plugins with Blocks\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.designbombs.com\/#website\",\"url\":\"https:\/\/www.designbombs.com\/\",\"name\":\"Design Bombs\",\"description\":\"Droppin&#039; design bombs everyday!\",\"publisher\":{\"@id\":\"https:\/\/www.designbombs.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.designbombs.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.designbombs.com\/#organization\",\"name\":\"DesignBombs\",\"url\":\"https:\/\/www.designbombs.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.designbombs.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2019\/04\/db-logo.png\",\"contentUrl\":\"https:\/\/www.designbombs.com\/wp-content\/uploads\/2019\/04\/db-logo.png\",\"width\":219,\"height\":92,\"caption\":\"DesignBombs\"},\"image\":{\"@id\":\"https:\/\/www.designbombs.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/designbombs\/\",\"https:\/\/x.com\/designbombs\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.designbombs.com\/#\/schema\/person\/4259b761dbd3ee01f8c7e2cd9d74df39\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.designbombs.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bb059a4470fd73113804d5dd7430f6a66aaba66ba161798d0fac79b124c74611?s=96&d=https%3A%2F%2Fwww.designbombs.com%2Fwp-content%2Fthemes%2FDesignBombs%2Fimages%2Fgravatar.jpg&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bb059a4470fd73113804d5dd7430f6a66aaba66ba161798d0fac79b124c74611?s=96&d=https%3A%2F%2Fwww.designbombs.com%2Fwp-content%2Fthemes%2FDesignBombs%2Fimages%2Fgravatar.jpg&r=g\",\"caption\":\"Leonardo Losoviz\"},\"description\":\"Leonardo Losoviz is an open source developer and technical writer, author of GraphQL by PoP, a CMS-agnostic GraphQL server in PHP. Find him on his blog leoloso.com and on Twitter @losoviz.\",\"sameAs\":[\"https:\/\/leoloso.com\",\"https:\/\/x.com\/losoviz\"],\"url\":\"https:\/\/www.designbombs.com\/author\/leo\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Reusing Functionality for WordPress Plugins with Blocks","description":"When working with the WordPress editor, whether creating a multi-block or a single-block plugin, we need to find the most effective way to reuse code.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/","og_locale":"en_US","og_type":"article","og_title":"Reusing Functionality for WordPress Plugins with Blocks","og_description":"When creating a plugin shipping one or more blocks for the WordPress editor, what is the best way to organize the plugin as to make its different","og_url":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/","og_site_name":"Design Bombs","article_publisher":"https:\/\/www.facebook.com\/designbombs\/","article_published_time":"2020-11-03T00:42:22+00:00","article_modified_time":"2020-11-10T09:10:19+00:00","og_image":[{"width":800,"height":350,"url":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png","type":"image\/png"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_creator":"@losoviz","twitter_site":"@designbombs","twitter_misc":{"Written by":"Leonardo Losoviz","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#article","isPartOf":{"@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/www.designbombs.com\/#\/schema\/person\/4259b761dbd3ee01f8c7e2cd9d74df39"},"headline":"Reusing Functionality for WordPress Plugins with Blocks","datePublished":"2020-11-03T00:42:22+00:00","dateModified":"2020-11-10T09:10:19+00:00","mainEntityOfPage":{"@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/"},"wordCount":1750,"commentCount":1,"publisher":{"@id":"https:\/\/www.designbombs.com\/#organization"},"image":{"@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage"},"thumbnailUrl":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png","articleSection":["WordPress Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/","url":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/","name":"Reusing Functionality for WordPress Plugins with Blocks","isPartOf":{"@id":"https:\/\/www.designbombs.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage"},"image":{"@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage"},"thumbnailUrl":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png","datePublished":"2020-11-03T00:42:22+00:00","dateModified":"2020-11-10T09:10:19+00:00","description":"When working with the WordPress editor, whether creating a multi-block or a single-block plugin, we need to find the most effective way to reuse code.","breadcrumb":{"@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#primaryimage","url":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png","contentUrl":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png","width":800,"height":350,"caption":"Reusing functionality for WordPress plugins with blocks"},{"@type":"BreadcrumbList","@id":"https:\/\/www.designbombs.com\/reusing-functionality-for-wordpress-plugins-with-blocks\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.designbombs.com\/"},{"@type":"ListItem","position":2,"name":"Tutorials","item":"https:\/\/www.designbombs.com\/category\/tutorials\/"},{"@type":"ListItem","position":3,"name":"WordPress Tutorials","item":"https:\/\/www.designbombs.com\/category\/tutorials\/wordpress-tutorials\/"},{"@type":"ListItem","position":4,"name":"Reusing Functionality for WordPress Plugins with Blocks"}]},{"@type":"WebSite","@id":"https:\/\/www.designbombs.com\/#website","url":"https:\/\/www.designbombs.com\/","name":"Design Bombs","description":"Droppin&#039; design bombs everyday!","publisher":{"@id":"https:\/\/www.designbombs.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.designbombs.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.designbombs.com\/#organization","name":"DesignBombs","url":"https:\/\/www.designbombs.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.designbombs.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2019\/04\/db-logo.png","contentUrl":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2019\/04\/db-logo.png","width":219,"height":92,"caption":"DesignBombs"},"image":{"@id":"https:\/\/www.designbombs.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/designbombs\/","https:\/\/x.com\/designbombs"]},{"@type":"Person","@id":"https:\/\/www.designbombs.com\/#\/schema\/person\/4259b761dbd3ee01f8c7e2cd9d74df39","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.designbombs.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/bb059a4470fd73113804d5dd7430f6a66aaba66ba161798d0fac79b124c74611?s=96&d=https%3A%2F%2Fwww.designbombs.com%2Fwp-content%2Fthemes%2FDesignBombs%2Fimages%2Fgravatar.jpg&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bb059a4470fd73113804d5dd7430f6a66aaba66ba161798d0fac79b124c74611?s=96&d=https%3A%2F%2Fwww.designbombs.com%2Fwp-content%2Fthemes%2FDesignBombs%2Fimages%2Fgravatar.jpg&r=g","caption":"Leonardo Losoviz"},"description":"Leonardo Losoviz is an open source developer and technical writer, author of GraphQL by PoP, a CMS-agnostic GraphQL server in PHP. Find him on his blog leoloso.com and on Twitter @losoviz.","sameAs":["https:\/\/leoloso.com","https:\/\/x.com\/losoviz"],"url":"https:\/\/www.designbombs.com\/author\/leo\/"}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/www.designbombs.com\/wp-content\/uploads\/2020\/10\/wordpress-plugin-with-blocks.png","jetpack-related-posts":[],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/posts\/32682","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/users\/50"}],"replies":[{"embeddable":true,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/comments?post=32682"}],"version-history":[{"count":3,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/posts\/32682\/revisions"}],"predecessor-version":[{"id":33058,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/posts\/32682\/revisions\/33058"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/media\/32932"}],"wp:attachment":[{"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/media?parent=32682"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/categories?post=32682"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.designbombs.com\/wp-json\/wp\/v2\/tags?post=32682"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}