Describe the bug
Native support for CSS/style loaders was just added to @wordpress/scripts in 10.0 via #21730. This is mad awesome and I've been keeping an eye on @gziolo 's + @fabiankaegy 's progress for a while, but I'm running into an issue where the resulting build/index.js' code doesn't run when the entry point (or anything it imports imports a ./style.css). For example, registerBlockType can be right after the import ./style.scss and the block will not show up in the block inserter. Importing other CSS files, no matter the name, doesn't appear to disrupt the code.
I think it may have to do with the way Webpack uses splitChunks/cacheGroups on style (webpack.config.js). Whenever a style.css is imported, it adds some "deferredModules` stuff to the built JS and it maybe it just isn't getting to it?
To reproduce
I tried this in a fresh plugin closely replicating what was done on #21730's _How has this been tested?_:
cd to wp-content/plugins and create a new block plugin with npm init @wordpress/block yo-whats-goodcd yo-whats-good and npm run start (no issues here--watch task seems to run fine)page post in block editoryo-whats-good/src/editor.scss with some styles for the editor.yo-whats-good/src/style.scss with some styles for both the front-end and the editor.yo-whats-good/src/index.js and add CSS imports the beginning:import './editor.scss';
import './style.scss';
start script watch process runimport './style.scss;, save, and refresh the page editor. The registered block should appear.Expected behavior
The creation of two new CSS files in the /build directory: index.css and style.css (this happens), as well as an index.js (also happens) with block registration, a console log, or anything else that runs when enqueued (block doesn't end up registering, console logs don't happen).
Editor version (please complete the following information):
Desktop (please complete the following information):
Additional context
Pull request that added CSS support to @wordpress/scripts: https://github.com/WordPress/gutenberg/pull/21730
Thanks for the detailed report. I haven鈥檛 had a chance to try how it works with npm yet. I鈥檒l try to reproduce it early next week and see how we could prevent it from happening.
Hmm, I just noticed the same. Will try to investigate as well. Removing the optimization part makes it working again (but breaks the style.css feature).
Thanks @gziolo, @ocean90! What are you referring to with npm? The block creation (@wordpress/block), or npm run start? Is there an alternative I should try? Please let me know if there's any other details I can provide.
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/408 sounds related although non of the suggested changes did work for me.
But I got it working by commenting out the removable of style.js in https://github.com/WordPress/gutenberg/blob/77fb18e001e1a4a767ae935673ae41a29b2d7519/packages/scripts/config/webpack.config.js#L133
and actually registering the file via wp_register_script() and adding it to the dependencies array for the main JS file.
@JordanPak Can you confirm that this is working for you too?
The content of the (non-minified) style.js is
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["style"],{
/***/ "./blocks/example/style.css":
/*!**********************************!*\
!*** ./blocks/example/style.css ***!
\**********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ })
}]);
Notice the webpackJsonp part which seems to actually initialize the JS parts.
This is the header of the entry file:
Source
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
/******/ var chunkIds = data[0];
/******/ var moreModules = data[1];
/******/ var executeModules = data[2];
/******/
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ // add entry modules from loaded chunk to deferred list
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/ // run deferred modules when all chunks ready
/******/ return checkDeferredModules();
/******/ };
/******/ function checkDeferredModules() {
/******/ var result;
/******/ for(var i = 0; i < deferredModules.length; i++) {
/******/ var deferredModule = deferredModules[i];
/******/ var fulfilled = true;
/******/ for(var j = 1; j < deferredModule.length; j++) {
/******/ var depId = deferredModule[j];
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
/******/ }
/******/ if(fulfilled) {
/******/ deferredModules.splice(i--, 1);
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/ }
/******/ }
/******/
/******/ return result;
/******/ }
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "blocks": 0
/******/ };
/******/
/******/ var deferredModules = [];
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
/******/ jsonpArray = jsonpArray.slice();
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ // add entry module to deferred list
/******/ deferredModules.push(["./blocks.js","style"]);
/******/ // run deferred modules when ready
/******/ return checkDeferredModules();
/******/ })
And this without the splitChunks setting:
Source
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./blocks.js");
/******/ })
After reading through https://github.com/webpack-contrib/mini-css-extract-plugin/issues/85, https://github.com/webpack/webpack/issues/7300, and https://github.com/webpack/webpack/pull/9040 I came to the conclusion that this a known and long-standing issue without a proper workaround. Though, a fix has been committed to Webpack in https://github.com/webpack/webpack/commit/c5f94f3b6a79a88da9ed93b5f980830f496f4fad which is slated for v5, currently available in beta.
I think we should consider removing the current broken behaviour for style.(sc|sa|c)ss and re-add it once the new Webpack version has been released.
I'm also able to replicate all of the above. I think the IgnoreEmitPlugin was meant to prevent this issue, but like mentioned above, the output file waits on all chunked JS files to load before it executes, so the lack of style.js prevents it from running.
A while ago I solved this for another project by creating a simple webpack plugin to append those extra "empty" files onto the main file. This is definitely just another workaround, but could help if we want to keep the style functionality now.
@ryelle Thanks for the link to the webpack plugin. I found some similar plugins but none did work for me.. Yours look promising as it also allows to limit the files to merge like IgnoreEmitPlugin does.
@gziolo Curious about your thoughts on the recent comments.
I plan to take a closer look at it during Contributor Day at WC EU today. Thanks for all the testing and very in-depth investigation 馃挴 I guess we could use some workaround until webpack 5 becomes stable hoping it鈥檚 going to resolve this issue 馃槂 We might also try using webpack externals (https://webpack.js.org/configuration/externals/#externals) to trick webpack temporary or use what @ryelle shared. Let鈥檚 make it work 馃槂
I have the same problem here: what is the recommended workaround for whom needs to include a .scss file for front end in gutenberg? go back to scripts version 9 with a split webpack.config?
ok I got it working renaming the css with another name (es: my-block-styles.scss) and imoprting to another .js (my-block-styles.scss.js), then registering as front end styles my-block-styles.css
I'm having the same problem. Any workaround?
@mrleemon If you don't have to, don't use a style.(s)css file. Otherwise you'd need a custom webpack config file which removes this plugin and you either enqueue the style.js manually or use another plugin as mentioned above.
@gziolo Did you already had the chance to try the externals idea?
Did you already had the chance to try the externals idea?
Yes, it didn't work for me so far as expected. I might have been doing it wrong, it either doesn't match files or prevents processing them when it properly converts all style files into undefined 馃し
It looks like the solution that could work is to prepend code that tells webpack that style chunk was loaded already.
Long story short, we need to ensure that this line resolves properly:
deferredModules.push(["./src/index.js","style"]);
I hope that https://github.com/WordPress/gutenberg/pull/23127 will fix this issue. I got it working locally when running both wp-scripts start and wp-scripts build 馃帀
A new version of @wordpress/scripts with the fix for this issue should be released to npm later today.
Most helpful comment
A new version of
@wordpress/scriptswith the fix for this issue should be released to npm later today.