Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scripts: Enhance the way entry points are detected in projects consisting of blocks and editor plugins #55936

Open
mrleemon opened this issue Nov 7, 2023 · 25 comments · May be fixed by #66466
Assignees
Labels
[Status] In Progress Tracking issues with work in progress [Tool] WP Scripts /packages/scripts [Type] Enhancement A suggestion for improvement.

Comments

@mrleemon
Copy link
Contributor

mrleemon commented Nov 7, 2023

Description

I regularly use @wordpress/scripts for building both blocks and non-block components. Recently, I started working in a new plugin that registers both some blocks and non-block components that add additional panels using the registerPlugin() function. This is the structure of my /src directory:

/src
    /blocks
        /my-first-block
            block.json
            ...
        /my-second-block
            block.json
            ...
        /my-third-block 
            block.json
            ...
    /components
        /my-first-panel
            index.js
        /my-second-panel
            index.js
    index.js
    index.css
    style.css

This is the content of the root index.js file:

import { __ } from '@wordpress/i18n';
import { registerPlugin } from '@wordpress/plugins';
import { calendar } from '@wordpress/icons';

import FirstPanelOptionsPanelComponent from './components/my-first-panel';
import SecondPanelOptionsPanelComponent from './components/my-second-panel';
import './index.scss';
import './style.scss';

registerPlugin('first-panel-options-panel', {
    icon: calendar,
    render: FirstPanelOptionsPanelComponent,
});

registerPlugin('second-panel-options-panel', {
    icon: calendar,
    render: SecondPanelOptionsPanelComponent,
});
  

Until now, I can't remember having any problems with the build script in @wordpress/scripts: It built both the blocks and the components found in the /src directory. But now, for some reason, it just builds the blocks, ignoring the components completely. The thing is that if I remove the /blocks directory then the components are built with no issues.

Has anything changed in @wordpress/scripts recently?

Step-by-step reproduction instructions

Create a /src directory with blocks and non-block components
Run npm build
The script ignores the non-block components

Screenshots, screen recording, code snippet

No response

Environment info

WP 6.3
No Gutenberg plugin
Windows 10
NPM 10.2.0

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Yes

@mrleemon mrleemon added the [Type] Bug An existing feature does not function as intended label Nov 7, 2023
@t-hamano t-hamano added Needs Testing Needs further testing to be confirmed. [Tool] WP Scripts /packages/scripts labels Nov 7, 2023
@mrleemon
Copy link
Contributor Author

mrleemon commented Nov 7, 2023

If I run npm build src/index.js the components are built, but not the blocks. Is there a way to build everything that is found inside the /src directory?

@gziolo
Copy link
Member

gziolo commented Nov 24, 2023

Until now, I can't remember having any problems with the build script in @wordpress/scripts: It built both the blocks and the components found in the /src directory. But now, for some reason, it just builds the blocks, ignoring the components completely. The thing is that if I remove the /blocks directory then the components are built with no issues.

Has anything changed in @wordpress/scripts recently?

Yes, this is the order of how entry points are created for JavaScript files using webpack:

/**
* Detects the list of entry points to use with webpack. There are three ways to do this:
* 1. Use the legacy webpack 4 format passed as CLI arguments.
* 2. Scan `block.json` files for scripts.
* 3. Fallback to `src/index.*` file.
*
* @see https://webpack.js.org/concepts/entry-points/
*
* @return {Object<string,string>} The list of entry points.
*/
function getWebpackEntryPoints() {

When npm run build finds block.json files, it reads all script-related entries and uses them to create entry points. index.js is only used as an entry point when there are no block.json files found.

That's said. We are open for iteration based on the feedback received as we plan to expand the application of the build tools outside of blocks, but also to make them helpful when building JS plugins and block themes.

@gziolo gziolo added [Type] Enhancement A suggestion for improvement. and removed [Type] Bug An existing feature does not function as intended labels Nov 24, 2023
@gziolo gziolo changed the title @wordpress/scripts ignores non-block components when blocks are present in the /src directory Scripts: Enhance the way entry points are detected in projects consisting of blocks and editor plugins Nov 24, 2023
@mrleemon
Copy link
Contributor Author

@gziolo Thanks for the explanation. I hope you can add a way to build both blocks and plugins found in the same directory. It would be very useful for those of us coding for clients.

Copy link

Hi,
This issue has gone 30 days without any activity. This means it is time for a check-in to make sure it is still relevant. If you are still experiencing this issue with the latest versions, you can help the project by responding to confirm the problem and by providing any updated reproduction steps.
Thanks for helping out.

@github-actions github-actions bot added the [Status] Stale Gives the original author opportunity to update before closing. Can be reopened as needed. label Dec 25, 2023
@mrleemon
Copy link
Contributor Author

The issue is still not resolved.
Thanks.

@github-actions github-actions bot removed the [Status] Stale Gives the original author opportunity to update before closing. Can be reopened as needed. label Dec 26, 2023
@gziolo
Copy link
Member

gziolo commented Jan 3, 2024

Until now, I can't remember having any problems with the build script in @wordpress/scripts: It built both the blocks and the components found in the /src directory. But now, for some reason, it just builds the blocks, ignoring the components completely. The thing is that if I remove the /blocks directory then the components are built with no issues.

I was thinking about it again. We added, at some point, automatic detection of the block.json files that allows discovering entry points defined with script, editorScript and viewScript. It looks like there is a check that could be avoided:

if ( Object.keys( entryPoints ).length > 0 ) {
return entryPoints;
}

If we remove that condition, we could combine the entry points detected for blocks with index.js to satisfy more advanced configurations in the projects that also ship JavaScript code not directly related to blocks.

@gziolo gziolo added Needs Dev Ready for, and needs developer efforts Good First Issue An issue that's suitable for someone looking to contribute for the first time and removed Needs Testing Needs further testing to be confirmed. labels Jan 3, 2024
@gziolo
Copy link
Member

gziolo commented Jan 3, 2024

@bacoords and @aurooba, I remember you discussing in the viewSource podcast the need to improve wp-scripts build to work not only with blocks. What else could we improve the developer experience?

@bacoords
Copy link
Contributor

bacoords commented Jan 9, 2024

@gziolo Thanks for the tag! So from what I recall, our issues mostly have to do with multiple entry points.

Basically every entry point is treated as a block, meaning if you had a theme and wanted to also use wp-scripts for your theme stylesheet, or maybe a global frontend JavaScript file (in addition to using it for your custom blocks), it would always generate the asset.php file and rename everything to index or style-index or what have you. Basically, could you define an entry point that doesn't rename the file, doesn't generate an asset file, etc.

I believe that was the main issue, but will let @aurooba add or correct me where I'm wrong.

@bacoords
Copy link
Contributor

I wanted to add another issue (or need for documentation) that seems to be related to entry points.

When building a block and pulling in external CSS dependencies or scss partials into block stylesheets:

  • If you @import some local .scss partials into src/style.scss, they'll will be loaded (properly, I believe) into the build/style-index.css file (the frontend and editor CSS file).
  • If you @import a CSS file from a dependency into src/style.scss, the result will be imported into build/index.css (the editor-only CSS file).
  • If you @import a local .scss partial that then @imports a CSS file from a dependency into src/style.scss, your code goes to build/style-index.css and the module code goes to build/index.css.

Meaning the only way to import CSS from a dependency into a frontend stylesheet for a block is to import it in your src/view.js file, which will then result in a new build/view.css file you can add to your block.json file.

@jdamner
Copy link

jdamner commented Jan 30, 2024

You can have multiple entry points using webpack.config.js at the root of your project with something like:

const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );

module.exports = {
	...defaultConfig,
	entry: {
		...defaultConfig.entry(),
		index: './src/index.js',
	},
};

This would build all the usual build tools for blocks (ie anything in /src/blocks/) and would also compile the index.js file out to build/index.js. I do think it would be a sensible default for the standard webpack config used in WP-Scripts to look for index.js|ts as well also everything inside /src/blocks instead of having it as a fallback, but not sure on backwards compatibility for making that sort of change.

@mrleemon
Copy link
Contributor Author

I added your webpack.config.js file to my project and now the build script builds both blocks and non-blocks.
I hope it's added to the standard webpack config used in WP-Scripts in a near future.
Thanks, @jdamner!

@xsonic
Copy link

xsonic commented Feb 23, 2024

@jdamner, this works great, thank you. Do you know if this is compatible with HMR using --hot? I get an error from the additional JS file:

Uncaught TypeError: Cannot read properties of undefined (reading 'injectIntoGlobalHook')

@jdamner
Copy link

jdamner commented Feb 26, 2024

@xsonic I'm not sure, I don't use --hot. But it's only providing webpack with additional entry-points, so it's not likely to cause any issues. I'm assuming you have hot-reloading sets as per the docs here: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#start

--hot – enables “Fast Refresh”. The page will automatically reload if you make changes to the code. For now, it requires that WordPress has the SCRIPT_DEBUG flag enabled and the Gutenberg plugin installed.

@james0r
Copy link

james0r commented May 24, 2024

const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );

Trying this out but getting

webpack-cli] Failed to load '/Users/jamesauble/Projects/diesel/wp-content/themes/fictional-clean-blocks/webpack.config.js' config
[webpack-cli] TypeError: defaultConfig.entry is not a function
    at Object.<anonymous> (/Users/jamesauble/Projects/diesel/wp-content/themes/fictional-clean-blocks/webpack.config.js:14:22)
    at Module._compile (node:internal/modules/cjs/loader:1241:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1295:10)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12)

webpack.config.js

// WordPress webpack config.
const defaultConfig = require('@wordpress/scripts/config/webpack.config');

// Plugins.
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');

// Utilities.
const path = require('path');

// Add any new entry points by extending the webpack config.
module.exports = {
  ...defaultConfig,
  entry: {
    ...defaultConfig.entry(),
    'theme-js': path.resolve(process.cwd(), 'src/js', 'index.js'),
    'theme-css': path.resolve(process.cwd(), 'src/css', 'style.scss'),
  },
};

@sirreal
Copy link
Member

sirreal commented May 24, 2024

@james0r this line is calling defaultConfig.entry() like a function:

    ...defaultConfig.entry(),

Will you try replacing it with this:

    ...defaultConfig.entry,

@james0r
Copy link

james0r commented May 24, 2024

@james0r this line is calling defaultConfig.entry() like a function:

    ...defaultConfig.entry(),

Will you try replacing it with this:

    ...defaultConfig.entry,

When using defaultConfig.entry I get

[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
 - configuration has an unknown property '1'. These properties are valid:
   object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, extends?, externals?, externalsPresets?, externalsType?, ignoreWarnings?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, snapshot?, stats?, target?, watch?, watchOptions? }
   -> Options object as provided by the user.
   For typos: please correct them.
   For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.
     Loaders should be updated to allow passing options via loader options in module.rules.
     Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
     plugins: [
       new webpack.LoaderOptionsPlugin({
         // test: /\.xxx$/, // may apply this only for some modules
         options: {
           1: …
         }
       })

@t-hamano
Copy link
Contributor

@james0r What version of @wordpress/scripts? It may be related to this discussion.

@wasek23
Copy link

wasek23 commented May 25, 2024

@james0r this line is calling defaultConfig.entry() like a function:

    ...defaultConfig.entry(),

Will you try replacing it with this:

    ...defaultConfig.entry,

When using defaultConfig.entry I get

[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
 - configuration has an unknown property '1'. These properties are valid:
   object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, extends?, externals?, externalsPresets?, externalsType?, ignoreWarnings?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, snapshot?, stats?, target?, watch?, watchOptions? }
   -> Options object as provided by the user.
   For typos: please correct them.
   For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.
     Loaders should be updated to allow passing options via loader options in module.rules.
     Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
     plugins: [
       new webpack.LoaderOptionsPlugin({
         // test: /\.xxx$/, // may apply this only for some modules
         options: {
           1: …
         }
       })

I am fetching the same error with this code

const defaultConfig = require('@wordpress/scripts/config/webpack.config');

module.exports = {
    ...defaultConfig
};

The version I am using is: 27.9.0

@james0r
Copy link

james0r commented May 25, 2024

@james0r What version of @wordpress/scripts? It may be related to this discussion.

I just figured out that I'm only having the defaultConfig.entry is not a function issue when running wp-scripts build with the --experimental-modules flag with version 27.9.0. If I remove the flag it works as expected.

I also found this starter theme which is a great resource https://github.com/bacoords/block-theme

@ghostintranslation
Copy link

I ran into the same problem.

With the added flag the default config becomes an array.

You can make the custom Webpack config working with the following:

const defaultConfig = require('@wordpress/scripts/config/webpack.config');

module.exports = [
	{
		...defaultConfig[0],
		entry: {
			...defaultConfig[0].entry(),
			// admin: './src/admin/index.js',
		},
	},
	{
		...defaultConfig[1],
	},
];

@daotw
Copy link

daotw commented Sep 26, 2024

@james0r troychaplin published a multi-block, with additional non-block scripts, in one plugin solution: https://github.com/wptrainingteam/building-multi-block-plugin/tree/DontCombineBlockScripts. Not tested with --experimental-modules yet but it's on to-do list.

@ghostintranslation
Copy link

@daotw I just tested and that repo you shared does not fix the issue of the wrong Webpack config arising when using the --experimental-modules flag.
I also tested applying the change I mention just above to the webpack.config.js file and it then works.

@aaronware
Copy link
Contributor

aaronware commented Oct 2, 2024

@daotw and @ghostintranslation you can also use the following. Below is an example of a block I am creating using the interactivity api along with an "admin" slot fill for a panel on multiple post types

// Check if the --experimental-modules flag is set.
const { getAsBooleanFromENV } = require( '@wordpress/scripts/utils' );

const hasExperimentalModulesFlag = getAsBooleanFromENV( 'WP_EXPERIMENTAL_MODULES' );

let scriptConfig, moduleConfig;

if ( hasExperimentalModulesFlag ) {
  [ scriptConfig, moduleConfig ] = require( '@wordpress/scripts/config/webpack.config' );
} else {
  scriptConfig = require( '@wordpress/scripts/config/webpack.config' );
}

Then from there can use default define or expand the config

module.exports = {
  ...scriptConfig,
  entry: {
    ...scriptConfig.entry(),
    admin: path.resolve(__dirname, 'src/admin/index.js')
  },
  plugins: [
    new MiniCssExtractPlugin(),
    new DependencyExtractionWebpackPlugin(),
    new RemoveEmptyScriptsPlugin()
  ],
};

@gziolo
Copy link
Member

gziolo commented Nov 10, 2024

PR with the required changes to improve handling for projects containing editor plugins or theme styles is ready for review:

@gziolo gziolo removed Good First Issue An issue that's suitable for someone looking to contribute for the first time Needs Dev Ready for, and needs developer efforts labels Nov 10, 2024
@theaminuli
Copy link

theaminuli commented Nov 16, 2024

Adding the experimental module has this issue with webpack.config:

{
    "scripts": {
        ...
        "build": "wp-scripts build --experimental-modules",
        "start": "wp-scripts start --experimental-modules"
    }
[webpack-cli] Failed to load 'D:\wampserver\www\wp-plugin\exoole\wp-content\plugins\exoole\webpack.config.js' config
[webpack-cli] TypeError: defaultConfig.plugins is not iterable
    at Object.<anonymous> (D:\wampserver\www\wp-plugin\exoole\wp-content\plugins\exoole\webpack.config.js:19:21)
    at Module._compile (node:internal/modules/cjs/loader:1368:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1426:10)
    at Module.load (node:internal/modules/cjs/loader:1205:32)
    at Module._load (node:internal/modules/cjs/loader:1021:12)
    at Module.require (node:internal/modules/cjs/loader:1230:19)
    at require (node:internal/modules/helpers:179:18)
    at WebpackCLI.tryRequireThenImport (D:\wampserver\www\wp-plugin\exoole\wp-content\plugins\exoole\node_modules\webpack-cli\lib\webpack-cli.js:223:30)
    at loadConfigByPath (D:\wampserver\www\wp-plugin\exoole\wp-content\plugins\exoole\node_modules\webpack-cli\lib\webpack-cli.js:1406:38)
    at WebpackCLI.loadConfig (D:\wampserver\www\wp-plugin\exoole\wp-content\plugins\exoole\node_modules\webpack-cli\lib\webpack-cli.js:1515:44)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Status] In Progress Tracking issues with work in progress [Tool] WP Scripts /packages/scripts [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging a pull request may close this issue.