Max Stack Size Exceeded for huge HTML #4694
Comments
the compiling process involves push spreading all nodes into another array at some point and the v8 engine does not support spreading that many items |
Interesting. I've run into this before in V8 and ended up using an array spread and a reassignment, rather than an argument spread in I'm not sure how many places we're doing |
Same issue when using Tailwind UI. In a regular svelte + tailwindcss project if I add in the Could we update the issue name as well to include tailwindUI word? @siebeneicher |
same here im building a large single page app / single file app, with many many html nodes error is
with
chunks.push(
c(') '),
...handle(node.body, state)
); .... as expected, using push instead of reassign compiles to const chunks = [c('for (')];
//
chunks.push(
c(') '),
...handle(node.body, state)
); todo: push ----> reassign like let chunks = [c('for (')];
//
chunks = [
...chunks,
c(') '),
...handle(node.body, state)
]; |
fixed : ) here is a quick-and-dirty patch script, to replace push with reassign patch-svelte-compiler.js/*
src/patch-svelte-compiler.js
replace push with reassign
cp src/patch-svelte-compiler.js node_modules/svelte/
cd node_modules/svelte
node patch-svelte-compiler.js
*/
// TODO chose your package type from package.json
// TODO patch both js and mjs files
var base_file = "compiler.js"; // "type": "commonjs"
//var base_file = "compiler.mjs"; // "type": "module" // TODO verify
const input_file = base_file + ".orig";
const output_file = base_file + ".new";
// replaceMethod
// origin: a.push(...a1, ...a2, e, ...a3); // error: Max Stack Size Exceeded
// spread: a = [...a1, ...a2, e, ...a3];
// concat: a = a1.concat(a2, [e], a3);
// performance is equal on nodejs
const replaceMethod = "spread";
//const replaceMethod = "concat";
const acorn_parse = require("acorn").parse;
const estree_walk = require("estree-walker").walk;
const node_tosource = require("tosource");
const magicString = require("magic-string");
const fs = require("fs");
if (fs.existsSync(input_file) || fs.existsSync(output_file)) {
console.log('error: input or output file exists. run this script only once');
process.exit(1);
}
console.log(`move file: ${base_file} --> ${input_file}`)
fs.renameSync(base_file, input_file);
// input
const content = fs.readFileSync(input_file, 'utf8');
// output
let code = new magicString(content);
const ast = acorn_parse(
content, {
// ecmaVersion: 10, // default in year 2019
sourceType: 'module',
});
const funcName = "push";
let arrayNameList = [];
estree_walk( ast, {
enter: function ( node, parent, prop, index ) {
// node must be array.push()
if (
node.type !== 'CallExpression' ||
node.callee === undefined ||
node.callee.property === undefined ||
node.callee.property.name !== funcName
) { return; }
// argument list must include spread operators
if (node.arguments.find(
a => (a.type == 'SpreadElement')) === undefined)
{ return; }
const nodeSrc = content.substring(node.start, node.end);
const pushObj = node.callee.object;
const arrayName = content.substring(pushObj.start, pushObj.end);
const pushProp = node.callee.property;
arrayNameList.push(arrayName);
// patch .push(
if (replaceMethod == "spread") {
// push --> assign array
// find "(" bracket after .push
const pushPropLen = content.substring(pushProp.start, node.end).indexOf("(");
code.overwrite(
(pushProp.start - 1),
(pushProp.start + pushPropLen + 1),
" /* PATCHED */ = [..."+arrayName+", "
);
// patch closing bracket
const closeIdx = node.start + nodeSrc.lastIndexOf(")");
code.overwrite(closeIdx, (closeIdx + 1), "]");
}
if (replaceMethod == "concat") {
// push --> assign concat
// ".push" --> " = array.concat"
code.overwrite(
(pushProp.start - 1),
pushProp.end,
" /* PATCHED */ = "+arrayName+".concat");
// patch arguments of .concat()
node.arguments.forEach(a => {
if (a.type == 'SpreadElement') {
// unspread: ...array --> array
const spreadArgSrc = content.substring(a.argument.start, a.argument.end);
//console.log('spread argument: '+spreadArgSrc);
code.overwrite(a.start, a.end, spreadArgSrc);
} else {
// enlist: element --> [element]
const argSrc = content.substring(a.start, a.end);
//console.log('non spread argument: '+argSrc);
code.overwrite(a.start, a.end, "["+argSrc+"]");
}
});
}
}});
code = code.toString();
function filterUnique(value, index, array) {
return array.indexOf(value) === index;
}
// replace const with let
arrayNameList.filter(filterUnique).forEach(arrayName => {
console.log(`arrayName = ${arrayName}`)
code = code.replace(
new RegExp("const "+arrayName+" = ", 'g'), // global = replace all
"/* PATCHED const "+arrayName+" */ let "+arrayName+" = "
);
})
fs.writeFileSync(output_file, code);
console.log(`move file: ${output_file} --> ${base_file}`)
fs.renameSync(output_file, base_file); edit: included patch by rhythm-section |
@milahu Thank you for the compiler patch. It works great. With version
Because of the tertiary operator the Not sure if it is a good solution or if it leads to other issues with other svelte versions, but the following worked for me. I changed the regex from:
to:
Hope this helps someone still having troubles. |
Is there any CI-able solution? |
you mean patching the typescript source files? here is a start: todo:
cosmetic:
|
the problem with concat is: concat is slower than push this should be fastest: Array.prototype._concat_inplace = function(other) { // aka _push_array
for (let i = 0; i < other.length; i++) {
this.push(other[i]);
}
return this; // chainable
};
array1._concat_inplace(array2)._concat_inplace(array3); its even faster than |
This issue is affecting working with .svg aswell. I have a bunch of huge SVGs that I want to animate with Svelte (in Sapper). Unfortunately I run into
My workaround is to downsize the svg, by reducing the complexity of paths used in it. |
Is there any fix available for this? I'm also facing the max stack size issue while importing the big SVG file as svelte component. |
there is a fix hidden 7 posts above but it may be better to outsource static parts, at least the svg |
@milahu My SVG is a svelte component (converted from .svg to .svelte) as I have to attach the multiple events like open a pop-up if anyone clicks on any specific area or show a image slider on screen (dynamically using the api data) inside that SVG. |
you can get it running with my patch, but development will be slower than with smaller components another solution: https://svelte.dev/repl/b0910aa018514053ae798250e17faa7a |
This should really be fixed. Much thanks to @milahu for the excellent workaround |
Discovered this whilst evaluating SvelteKit (my issue was closed above). Trying to convert a large static HTML theme. @NetOperatorWibby - I've attempted to use the patch here (https://gist.github.com/NetOperatorWibby/8515baa503ccc7b60189c6871c2c6096). I ran all 4 commands without error but I am still seeing the 'maximum call stack size' error. I'm not a front-end engineer at heart so I'm not sure how to proceed with this. The original patch was submitted over a year ago. Breaking it down and composing smaller components doesn't appear to have any effect. System: |
whats the exact error trace? maybe your node uses compiler.mjs which is not patched |
Yeah, looks like compiler.mjs. My original issue with the details is here: (sveltejs/kit#2061). |
ive updated my //var base_file = "compiler.mjs"; // "type": "module" // TODO verify uncomment that line, run the script and hope for the best ; ) |
@milahu Haven't been able to get back to this for a few days. Thanks a lot - that works great and the error is no longer present. |
I encountered this one today. Just wanted to ask if there is a specific reason, why there seems to be a working patch but no pr to bring that fix into svelte? Several hundret html-tags on a page don't seem that unreasonable to me today and if this is really a |
this would require patching multiple packages (svelte, code-red, ...) which is ... work |
The crash(!) occurs with no special prerequisites in quite simple, normal usecases and without documentation or warning (see my repro https://svelte.dev/repl/0a52a75c95d34423bf6d328ca4e1db88?version=3.42.4). The problem worsens as svelte is advertised to be especially able to handle very large node-counts for visualization etc better than react & co. Imho(!), apart from being a nasty bug, the problem with the patch-solution, which I appreciate(!), is, that it has no contract or guarantees inside the actual svelte-code. It could break anytime. Or you might have something better to do. Or whatever. In the end you can't simply upgrade packages without manual testing and patching - which you might eagerly need to for some reasons. I get that this is a volunteer-driven-project and that its much hard work to develop and maintain this project (which I, again, very much appreciate!) but I'm arguing from a standpoint where one might decide to run user-facing production-code on the basis of svelte. And from that standpoint this is quite a big (undocumented) problem. |
not really : P https://github.com/milahu/random/tree/master/svelte/patch-svelte-compiler-sources current output
feel free to continue |
@milahu I was referring to svelte(kit) as a whole ;) |
Describe the bug
The content of the the svelte file is a large HTML chunk, without any script logic or styling being involved. Max stack size exceeded error while compiling.
To Reproduce
https://svelte.dev/repl/a9dfcc17551c4aeb95e8fe748a97061d?version=3.20.1
Expected behavior
Compiling should not break
Information about your Svelte project:
Svelte 3.20.1, Rollup, Windows
Severity
Its probably an uncommon case for most people. I ran into it, when using a generated data privacy HTML template. I would not see this as a priority. Workaround for me: copy the HTML into a JS template literal variable and inject it via @html: https://svelte.dev/repl/1ab32c5a3c2c426e973512cfc8da023c?version=3.20.1
The text was updated successfully, but these errors were encountered: