diff --git a/pipen_report/frontend/Makefile b/pipen_report/frontend/Makefile new file mode 100644 index 0000000..af918dd --- /dev/null +++ b/pipen_report/frontend/Makefile @@ -0,0 +1,5 @@ + +patch: + cp patch-svelte-compiler.js node_modules/svelte/; + cd node_modules/svelte/ && node patch-svelte-compiler.js; + cd -; diff --git a/pipen_report/frontend/package-lock.json b/pipen_report/frontend/package-lock.json index d405e6f..151caf7 100644 --- a/pipen_report/frontend/package-lock.json +++ b/pipen_report/frontend/package-lock.json @@ -8,6 +8,9 @@ "name": "pipen-report", "version": "1.0.0", "license": "MIT", + "dependencies": { + "acorn": "^8.6.0" + }, "devDependencies": { "@rollup/plugin-commonjs": "^17.0.0", "@rollup/plugin-node-resolve": "^11.0.0", @@ -18,7 +21,7 @@ "rollup-plugin-livereload": "^2.0.0", "rollup-plugin-svelte": "^7.0.0", "rollup-plugin-terser": "^7.0.0", - "svelte": "^3.0.0" + "svelte": "^3.44.3" } }, "node_modules/@babel/code-frame": { @@ -141,6 +144,17 @@ "@types/node": "*" } }, + "node_modules/acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1041,6 +1055,11 @@ "@types/node": "*" } }, + "acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", diff --git a/pipen_report/frontend/package.json b/pipen_report/frontend/package.json index 75e1286..62a2d8a 100644 --- a/pipen_report/frontend/package.json +++ b/pipen_report/frontend/package.json @@ -16,6 +16,9 @@ "rollup-plugin-livereload": "^2.0.0", "rollup-plugin-svelte": "^7.0.0", "rollup-plugin-terser": "^7.0.0", - "svelte": "^3.0.0" + "svelte": "^3.44.3" + }, + "dependencies": { + "acorn": "^8.6.0" } } diff --git a/pipen_report/frontend/patch-svelte-compiler.js b/pipen_report/frontend/patch-svelte-compiler.js new file mode 100644 index 0000000..e1f3a42 --- /dev/null +++ b/pipen_report/frontend/patch-svelte-compiler.js @@ -0,0 +1,148 @@ +/* + Patch svelte compiler to fix + "Max Stack Size Exceeded for huge HTML" + https://github.com/sveltejs/svelte/issues/4694 + + All credits to @milahu + https://github.com/sveltejs/svelte/issues/4694#issuecomment-637434624 + + To run: + > make patch +*/ + +// 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 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);