css-loader: "RangeError: Maximum call stack size exceeded" when compiling a file with many requires

Created based on comment https://github.com/webpack-contrib/css-loader/issues/689#issuecomment-422039086

Do you want to request a feature or report a bug? Bug

What is the current behavior? Given CSS file with 10000 or more classes with URLs, in our case generated from sprites.

...
.classN { ... background-image: url(./path/to/sprite.png); ... }
...

The css-loader creates such a JS file, which on compile throws RangeError: Maximum call stack size exceeded.

...
exports.push([
  module.id, 
  ".class0 { background-image: url(" +
  escape(require("./path/to/sprite.png")) +
  "); }.class1 { background-image: url(" +
  escape(require("./path/to/sprite.png")) +
  ...
])

What is the expected behavior? The expected behaviour is to generate a JS file, which can be compiled and executed.

Please mention other relevant information such as your webpack version, Node.js version and Operating System. Reproducible on 1.0.0, Node 6.11.3 & Node 8.9.4.

Technical details The issue is that URLs are replaced by requires and cssAsString in loader.js is built by string concatenation "..." + "..." + ... + "..." which has upper limit in each version of v8, given by it’s default stack size.

Experimentally measured:

  • node 6.11.3 has ~6250 strings,
  • node 8.9.4 has ~3285 strings.

The fix is straightforward, group the string concatenations by fixed amount so it can be compiled using default stack size. ("..." + ... + "...") + ("..." + ... + "...")

Smallest possible test-case for css-loader

/*globals describe */

var helpers = require("./helpers");
var test = helpers.test;

describe("string concat", function() {
	this.timeout(20000);

	var actualCSS, expectedCSS, i;

	actualCSS = '';
	expectedCSS = '';
	for (i = 0; i < 10000; i++) {
		actualCSS += ".class" + i + " { background-image: url(./path/to/file.png); }";
		expectedCSS += ".class" + i + " { background-image: url({./path/to/file.png}); }";
	}

	test("should handle concat of 20001 strings", actualCSS, [[1, expectedCSS, ""]]);
});

Repo with reproducible test-case https://github.com/Gobie/css-loader/tree/issue-771

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 18 (12 by maintainers)

Most upvoted comments

@sgarfinkel Okay, let’ use this https://github.com/webpack-contrib/css-loader/pull/1525, shorty:

  • if your enviroment supports templateLiteral, i.e. output. environment,templateLiteral: true we will use it (if you have modern browsers it will be true for you), otherwise you can set it to true for dev enviroment, because in production mini-css-extract-plugin will extract it to file and you will not have the such problem
  • otherwise we will use binary, using ( "string" + "string" ) + ( "string" + "string" ) doesn’t work, because acorn fails in other places (tokenizer), but I still think what acorn should use another approach to solve it

Let me try to create a minimal reproduction for you. It looks like the one in the original ticket is gone.