3

I have a multiblock plugin build with @wordpress/scripts, that used to include blocks only and did not need a custom webpack configuration since I am working with block folders with a block.json each. That seemed to be enough for the auto block detection.

But now I want to add a settings page for the plugin, which makes things trickier. I am following this Tutorial, where they customize the webpack configuration accordingly. But unfortunately this resulted in my other blocks to be ignored in the build. My research lead me to this question, where several options to customize the configuration are mentioned.

My current configuration is the following:

/**
 * `@wordpress/scripts` multi-config multi-block Webpack configuration.
 * @see https://wordpress.stackexchange.com/questions/390282
 */

// Native Depedencies.
const path = require("path");

// Third-Party Dependencies.
const CopyPlugin = require("copy-webpack-plugin");

const default_config_path = require.resolve(
    "@wordpress/scripts/config/webpack.config.js"
);

/**
 * Retrieves a new instance of `@wordpress/scripts`' default webpack configuration object.
 * @returns WebpackOptions
 */
const getBaseConfig = () => {
    // If the default config's already been imported, clear the module from the cache so that Node
    // will interpret the module file again and provide a brand new object.
    if (require.cache[default_config_path])
        delete require.cache[default_config_path];

    // Import a new instance of the default configuration object.
    return require(default_config_path);
};

/**
 * @callback buildConfig~callback
 * @param {WebpackOptions} config An instance of `@wordpress/scripts`' default configuration object.
 * @returns WebpackOptions The modified or replaced configuration object.
 */

/**
 * Returns the result of executing a callback function provided with a new default configuration
 * instance.
 *
 * @param {buildConfig~callback} callback
 * @returns WebpackOptions The modified or replaced configuration object.
 */
const buildConfig = (callback) => callback(getBaseConfig());

/**
 * Extends `@wordpress/scripts`'s default webpack config to build block sources from a common
 * `./src` directory and output built assets to a common `./build` directory.
 *
 * @param {string} block_name
 * @returns WebpackOptions A configuration object for this block.
 * @see https://wordpress.stackexchange.com/questions/390282/possible-to-use-wordpress-create-block-with-multiple-blocks
 */
const buildBlockConfig = (block_name) =>
    buildConfig((config) => ({
        // Copy all properties from the base config into the new config, then override some.
        ...config,
        // Override the block's "index" entry point to be `./src/{block name}/index.js`.
        entry: {
            index: path.resolve(process.cwd(), "src", block_name, "index.js"),
        },
        // This block's built assets should be output to `./build/{block name}/`.
        output: {
            ...config.output,
            path: path.resolve(config.output.path, block_name),
        },
        // Add a CopyWebpackPlugin to copy over the `block.json` file and the styles.
        plugins: [
            ...config.plugins,
            new CopyPlugin({
                patterns: [
                    { from: `src/${block_name}/block.json` },
                    { from: `src/${block_name}/frontend.js` },
                ],
            }),
        ],
    }));

module.exports = [
    buildBlockConfig("accordion"),
    buildBlockConfig("accordion-item"),
    buildBlockConfig("admin")
];

This leaves me with the following output:

build
-- accordion
  -- accordion
    - block.json
  -- accordion-item
    - block.json
  - block.json
  - frontend.js
  - index.asset.php
  - index.css
  - index.js
-- accordion-item
  -- accordion
    - block.json
  -- accordion-item
    - block.json
  - block.json
  - index.asset.php
  - index.css
  - index.js
  - style-index.css
-- admin
  -- accordion
    - block.json
  -- accordion-item
    - block.json
  - index.asset.php
  - index.js
  - style-index.css

Where do all those block.jsons come from? It feels like for each folder the block auto detection decides to force all block.json inside. How can I prevent that?

And also it seems like something else is wrong, too, as even if the excess subfolders are manually deleted, the blocks are not usable in the editor. The admin page works though.

Thanks in advance for any tips!


EDIT

Since I made some good progress since I wrote this question, I felt like an update is due.

My webpack.config.js:

/**
 * `@wordpress/scripts` path-based name multi-block Webpack configuration.
 * @see https://wordpress.stackexchange.com/questions/390282
 */

// Native Depedencies.
const path = require("path");

// Third-Party Dependencies.
const CopyPlugin = require("copy-webpack-plugin");
const config = require("@wordpress/scripts/config/webpack.config.js");

/**
 * Resolve a series of path parts relative to `./src`.
 * @param string[] path_parts An array of path parts.
 * @returns string A normalized path, relative to `./src`.
 **/
const resolveSource = (...path_parts) =>
    path.resolve(process.cwd(), "src", ...path_parts);

/**
 * Resolve a block name to the path to it's main `index.js` entry-point.
 * @param string name The name of the block.
 * @returns string A normalized path to the block's entry-point file.
 **/
const resolveBlockEntry = (name) => resolveSource("blocks", name, "index.js");

config.entry = {
    "admin/index": resolveSource("admin", "index.js"),
    "blocks/accordion/index": resolveBlockEntry("accordion"),
    "blocks/accordion-item/index": resolveBlockEntry("accordion-item")
};

// Add a CopyPlugin to copy over block.json files.
config.plugins.push(
    new CopyPlugin({
        patterns: [
            {
                context: "src",
                from: `blocks/*/block.json`,
            },
            {
                context: "src",
                from: `blocks/*/frontend.js`,
                /** @see https://webpack.js.org/guides/asset-modules/ */
            },
        ],
    })
);

module.exports = config;

The output:

build
-- admin
  - index.asset.php
  - index.js
  - style-index.css
-- blocks
  -- accordion
    - block.json
    - frontend.js
    - index.asset.php
    - index.css
    - index.js
  -- accordion-item
    - block.json
    - index.asset.php
    - index.css
    - index.js
    - style-index.css

That is way better already. The last bit for me to fix seems to compile a "frontend.asset.php' for the viewScript, since currently I get the following Error:

Notice: Function register_block_script_handle was called incorrectly. The asset file (/var/www/html/wp-content/plugins/ew-blocks/build/blocks/accordion/frontend.asset.php) for the "viewScript" defined in "eyeworkers/accordion" block definition is missing. Please see Debugging in WordPress for more information. (This message was added in version 5.5.0.) in /var/www/html/wp-includes/functions.php on line 5865

2 Answers 2

1

Okay I found a quick and dirty solution for the viewScript.

My webpack.config.js:

/**
 * `@wordpress/scripts` path-based name multi-block Webpack configuration.
 * @see https://wordpress.stackexchange.com/questions/390282
 */

// Native Depedencies.
const path = require("path");

// Third-Party Dependencies.
const CopyPlugin = require("copy-webpack-plugin");
const config = require("@wordpress/scripts/config/webpack.config.js");

/**
 * Resolve a series of path parts relative to `./src`.
 * @param string[] path_parts An array of path parts.
 * @returns string A normalized path, relative to `./src`.
 **/
const resolveSource = (...path_parts) =>
    path.resolve(process.cwd(), "src", ...path_parts);

/**
 * Resolve a block name to the path to it's main `index.js` entry-point.
 * @param string name The name of the block.
 * @returns string A normalized path to the block's entry-point file.
 **/
const resolveBlockEntry = (name) => resolveSource("blocks", name, "index.js");

/**
 * Resolve a block name to the path to it's script `frontend.js`
 * @param string name The name of the block.
 * @returns string A normalized path to the block's viewScript file.
 **/
const resolveBlockViewScript = (name) =>
    resolveSource("blocks", name, "frontend.js");

config.entry = {
    "admin/index": resolveSource("admin", "index.js"),
    "blocks/accordion/index": resolveBlockEntry("accordion"),
    "blocks/accordion-item/index": resolveBlockEntry("accordion-item"),
    "blocks/accordion/frontend": resolveBlockViewScript("accordion"),
};

// Add a CopyPlugin to copy over block.json files.
config.plugins.push(
    new CopyPlugin({
        patterns: [
            {
                context: "src",
                from: `blocks/*/block.json`,
            },
        ],
    })
);

module.exports = config;

The Output:

build
-- admin
  - index.asset.php
  - index.js
  - style-index.css
-- blocks
  -- accordion
    - block.json
    - frontend.asset.php
    - frontend.js
    - index.asset.php
    - index.css
    - index.js
  -- accordion-item
    - block.json
    - index.asset.php
    - index.css
    - index.js
    - style-index.css

This works for me. I am open for improvements, though, since this is an annoyance to maintain when adding new blocks.

0

You can use getWebpackEntryPoints() (as used in @wordpress/scripts) to solve this problem for a cleaner webpack config. This will auto-generate the block entrypoints in /src as per the default WordPress config while allowing you to add custom entrypoints.

webpack.config.js

const path = require( 'path' )

// Import the @wordpress/scripts config.
const wordpressConfig = require( '@wordpress/scripts/config/webpack.config' )

// Import the utility to auto-generate the entry points in the src directory.
const { getWebpackEntryPoints } = require( '@wordpress/scripts/utils/config' )

module.exports = {
    // Spread the existing WordPress config.
    ...wordpressConfig,

    entry: {
        // Spread the auto-generated entrypoints.
        ...getWebpackEntryPoints(),

        // Extend with new entrypoints.
        'example': path.resolve( process.cwd(), 'src', 'example.js' ),
    },
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.