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

importing style.(c/sc)ss with @wordpress/scripts 10.0 causes JavaScript to stop running? #22776

Closed
JordanPak opened this issue May 31, 2020 · 15 comments · Fixed by #23127
Closed
Assignees
Labels
[Status] In Progress Tracking issues with work in progress [Tool] WP Scripts /packages/scripts [Type] Bug An existing feature does not function as intended

Comments

@JordanPak
Copy link

JordanPak commented May 31, 2020

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?:

  1. cd to wp-content/plugins and create a new block plugin with npm init @wordpress/block yo-whats-good
  2. cd yo-whats-good and npm run start (no issues here--watch task seems to run fine)
  3. Activate Yo Whats Good and Gutenberg plugins and open up page post in block editor
  4. Add a new file yo-whats-good/src/editor.scss with some styles for the editor.
  5. Add a new file yo-whats-good/src/style.scss with some styles for both the front-end and the editor.
  6. Open yo-whats-good/src/index.js and add CSS imports the beginning:
import './editor.scss';
import './style.scss';
  1. Save and let the start script watch process run
  2. Refresh page editor and try to insert the custom block. It should not appear in inserter.
  3. Comment-out import './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):

  • WordPress version: 5.4.1
  • Gutenberg plugin version 8.2.1

Desktop (please complete the following information):

  • OS: Windows 10.0 Build 18363
  • Browser: Chrome 83.0.4103.61, Firefox Developer Edition

Additional context
Pull request that added CSS support to @wordpress/scripts: #21730

@gziolo gziolo added [Tool] WP Scripts /packages/scripts Needs Testing Needs further testing to be confirmed. labels May 31, 2020
@gziolo
Copy link
Member

gziolo commented May 31, 2020

Thanks for the detailed report. I haven’t had a chance to try how it works with npm yet. I’ll try to reproduce it early next week and see how we could prevent it from happening.

@ocean90
Copy link
Member

ocean90 commented Jun 2, 2020

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).

@JordanPak
Copy link
Author

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.

@ocean90
Copy link
Member

ocean90 commented Jun 2, 2020

webpack-contrib/mini-css-extract-plugin#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

new IgnoreEmitPlugin( [ 'style.js' ] ),

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");
/******/ })

@ocean90 ocean90 added the [Type] Bug An existing feature does not function as intended label Jun 2, 2020
@ocean90
Copy link
Member

ocean90 commented Jun 2, 2020

After reading through webpack-contrib/mini-css-extract-plugin#85, webpack/webpack#7300, and webpack/webpack#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 webpack/webpack@c5f94f3 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.

@ocean90 ocean90 removed the Needs Testing Needs further testing to be confirmed. label Jun 2, 2020
@ryelle
Copy link
Contributor

ryelle commented Jun 2, 2020

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.

@ocean90
Copy link
Member

ocean90 commented Jun 4, 2020

@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.

@gziolo
Copy link
Member

gziolo commented Jun 4, 2020

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’s 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’s make it work 😃

@giona69
Copy link

giona69 commented Jun 5, 2020

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?

@giona69
Copy link

giona69 commented Jun 5, 2020

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

@mrleemon
Copy link
Contributor

mrleemon commented Jun 7, 2020

I'm having the same problem. Any workaround?

@ocean90
Copy link
Member

ocean90 commented Jun 8, 2020

@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?

@gziolo
Copy link
Member

gziolo commented Jun 8, 2020

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"]);

@gziolo
Copy link
Member

gziolo commented Jun 12, 2020

I hope that #23127 will fix this issue. I got it working locally when running both wp-scripts start and wp-scripts build 🎉

@gziolo
Copy link
Member

gziolo commented Jun 15, 2020

A new version of @wordpress/scripts with the fix for this issue should be released to npm later today.

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] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants