UNPKG

utility2

Version:

this zero-dependency package will provide high-level functions to to build, test, and deploy webapps

1,415 lines (1,387 loc) 233 kB
#!/usr/bin/env node /* * lib.utility2.js (2020.11.12) * https://github.com/kaizhu256/node-utility2 * this zero-dependency package will provide high-level functions to to build, test, and deploy webapps * */ /* istanbul instrument in package utility2 */ // assets.utility2.header.js - start /* jslint utility2:true */ /* istanbul ignore next */ // run shared js-env code - init-local (function () { "use strict"; let isBrowser; let isWebWorker; let local; // polyfill globalThis if (!(typeof globalThis === "object" && globalThis)) { if (typeof window === "object" && window && window.window === window) { window.globalThis = window; } if (typeof global === "object" && global && global.global === global) { global.globalThis = global; } } // init debugInline if (!globalThis.debugInline) { let consoleError; consoleError = console.error; globalThis.debugInline = function (...argList) { /* * this function will both print <argList> to stderr * and return <argList>[0] */ consoleError("\n\ndebugInline"); consoleError(...argList); consoleError("\n"); return argList[0]; }; } // init isBrowser isBrowser = ( typeof globalThis.XMLHttpRequest === "function" && globalThis.navigator && typeof globalThis.navigator.userAgent === "string" ); // init isWebWorker isWebWorker = ( isBrowser && typeof globalThis.importScripts === "function" ); // init function function objectDeepCopyWithKeysSorted(obj) { /* * this function will recursively deep-copy <obj> with keys sorted */ let sorted; if (typeof obj !== "object" || !obj) { return obj; } // recursively deep-copy list with child-keys sorted if (Array.isArray(obj)) { return obj.map(objectDeepCopyWithKeysSorted); } // recursively deep-copy obj with keys sorted sorted = {}; Object.keys(obj).sort().forEach(function (key) { sorted[key] = objectDeepCopyWithKeysSorted(obj[key]); }); return sorted; } function assertJsonEqual(aa, bb) { /* * this function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>) */ aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa)); bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb)); if (aa !== bb) { throw new Error(JSON.stringify(aa) + " !== " + JSON.stringify(bb)); } } function assertOrThrow(passed, msg) { /* * this function will throw <msg> if <passed> is falsy */ if (passed) { return; } throw ( ( msg && typeof msg.message === "string" && typeof msg.stack === "string" ) // if msg is err, then leave as is ? msg : new Error( typeof msg === "string" // if msg is string, then leave as is ? msg // else JSON.stringify(msg) : JSON.stringify(msg, undefined, 4) ) ); } function coalesce(...argList) { /* * this function will coalesce null, undefined, or "" in <argList> */ let arg; let ii; ii = 0; while (ii < argList.length) { arg = argList[ii]; if (arg !== undefined && arg !== null && arg !== "") { return arg; } ii += 1; } return arg; } function identity(val) { /* * this function will return <val> */ return val; } function noop() { /* * this function will do nothing */ return; } function objectAssignDefault(tgt = {}, src = {}, depth = 0) { /* * this function will if items from <tgt> are null, undefined, or "", * then overwrite them with items from <src> */ let recurse; recurse = function (tgt, src, depth) { Object.entries(src).forEach(function ([ key, bb ]) { let aa; aa = tgt[key]; if (aa === undefined || aa === null || aa === "") { tgt[key] = bb; return; } if ( depth !== 0 && typeof aa === "object" && aa && !Array.isArray(aa) && typeof bb === "object" && bb && !Array.isArray(bb) ) { recurse(aa, bb, depth - 1); } }); }; recurse(tgt, src, depth | 0); return tgt; } function onErrorThrow(err) { /* * this function will throw <err> if exists */ if (err) { throw err; } } // bug-workaround - throw unhandledRejections in node-process if ( typeof process === "object" && process && typeof process.on === "function" && process.unhandledRejections !== "strict" ) { process.unhandledRejections = "strict"; process.on("unhandledRejection", function (err) { throw err; }); } // init local local = { assertJsonEqual, assertOrThrow, coalesce, identity, isBrowser, isWebWorker, local, noop, objectAssignDefault, objectDeepCopyWithKeysSorted, onErrorThrow }; globalThis.globalLocal = local; }()); // assets.utility2.header.js - end (function (local) { "use strict"; /* istanbul ignore next */ // run shared js-env code - init-before (function () { // init local local = ( globalThis.utility2_rollup // || globalThis.utility2_rollup_old // || require("./assets.utility2.rollup.js") || globalThis.globalLocal ); // init exports if (local.isBrowser) { globalThis.utility2_utility2 = local; } else { module.exports = local; module.exports.__dirname = __dirname; } // init lib main local.utility2 = local; /* validateLineSortedReset */ // init lib utility2 globalThis.utility2 = local; // init lib extra [ "apidoc", "istanbul", "jslint", "marked" ].forEach(function (key) { try { local[key] = ( local.isBrowser ? globalThis["utility2_" + key] : require("./lib." + key + ".js") ); } catch (errCaught) { local.assertOrThrow(errCaught.code === "MODULE_NOT_FOUND", errCaught); } local[key] = local[key] || {}; }); // run shared js-env code - assets local.assetsDict = local.assetsDict || {}; /* jslint ignore:start */ local.assetsDict["/assets.utility2.header.js"] = '\ // assets.utility2.header.js - start\n\ /* jslint utility2:true */\n\ /* istanbul ignore next */\n\ // run shared js\-env code - init-local\n\ (function () {\n\ "use strict";\n\ let isBrowser;\n\ let isWebWorker;\n\ let local;\n\ // polyfill globalThis\n\ if (!(typeof globalThis === "object" && globalThis)) {\n\ if (typeof window === "object" && window && window.window === window) {\n\ window.globalThis = window;\n\ }\n\ if (typeof global === "object" && global && global.global === global) {\n\ global.globalThis = global;\n\ }\n\ }\n\ // init debugInline\n\ if (!globalThis.debugInline) {\n\ let consoleError;\n\ consoleError = console.error;\n\ globalThis.debugInline = function (...argList) {\n\ /*\n\ * this function will both print <argList> to stderr\n\ * and return <argList>[0]\n\ */\n\ consoleError("\\n\\ndebugInline");\n\ consoleError(...argList);\n\ consoleError("\\n");\n\ return argList[0];\n\ };\n\ }\n\ // init isBrowser\n\ isBrowser = (\n\ typeof globalThis.XMLHttpRequest === "function"\n\ && globalThis.navigator\n\ && typeof globalThis.navigator.userAgent === "string"\n\ );\n\ // init isWebWorker\n\ isWebWorker = (\n\ isBrowser && typeof globalThis.importScripts === "function"\n\ );\n\ // init function\n\ function objectDeepCopyWithKeysSorted(obj) {\n\ /*\n\ * this function will recursively deep-copy <obj> with keys sorted\n\ */\n\ let sorted;\n\ if (typeof obj !== "object" || !obj) {\n\ return obj;\n\ }\n\ // recursively deep-copy list with child-keys sorted\n\ if (Array.isArray(obj)) {\n\ return obj.map(objectDeepCopyWithKeysSorted);\n\ }\n\ // recursively deep-copy obj with keys sorted\n\ sorted = {};\n\ Object.keys(obj).sort().forEach(function (key) {\n\ sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);\n\ });\n\ return sorted;\n\ }\n\ function assertJsonEqual(aa, bb) {\n\ /*\n\ * this function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>)\n\ */\n\ aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa));\n\ bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb));\n\ if (aa !== bb) {\n\ throw new Error(JSON.stringify(aa) + " !== " + JSON.stringify(bb));\n\ }\n\ }\n\ function assertOrThrow(passed, msg) {\n\ /*\n\ * this function will throw <msg> if <passed> is falsy\n\ */\n\ if (passed) {\n\ return;\n\ }\n\ throw (\n\ (\n\ msg\n\ && typeof msg.message === "string"\n\ && typeof msg.stack === "string"\n\ )\n\ // if msg is err, then leave as is\n\ ? msg\n\ : new Error(\n\ typeof msg === "string"\n\ // if msg is string, then leave as is\n\ ? msg\n\ // else JSON.stringify(msg)\n\ : JSON.stringify(msg, undefined, 4)\n\ )\n\ );\n\ }\n\ function coalesce(...argList) {\n\ /*\n\ * this function will coalesce null, undefined, or "" in <argList>\n\ */\n\ let arg;\n\ let ii;\n\ ii = 0;\n\ while (ii < argList.length) {\n\ arg = argList[ii];\n\ if (arg !== undefined && arg !== null && arg !== "") {\n\ return arg;\n\ }\n\ ii += 1;\n\ }\n\ return arg;\n\ }\n\ function identity(val) {\n\ /*\n\ * this function will return <val>\n\ */\n\ return val;\n\ }\n\ function noop() {\n\ /*\n\ * this function will do nothing\n\ */\n\ return;\n\ }\n\ function objectAssignDefault(tgt = {}, src = {}, depth = 0) {\n\ /*\n\ * this function will if items from <tgt> are null, undefined, or "",\n\ * then overwrite them with items from <src>\n\ */\n\ let recurse;\n\ recurse = function (tgt, src, depth) {\n\ Object.entries(src).forEach(function ([\n\ key, bb\n\ ]) {\n\ let aa;\n\ aa = tgt[key];\n\ if (aa === undefined || aa === null || aa === "") {\n\ tgt[key] = bb;\n\ return;\n\ }\n\ if (\n\ depth !== 0\n\ && typeof aa === "object" && aa && !Array.isArray(aa)\n\ && typeof bb === "object" && bb && !Array.isArray(bb)\n\ ) {\n\ recurse(aa, bb, depth - 1);\n\ }\n\ });\n\ };\n\ recurse(tgt, src, depth | 0);\n\ return tgt;\n\ }\n\ function onErrorThrow(err) {\n\ /*\n\ * this function will throw <err> if exists\n\ */\n\ if (err) {\n\ throw err;\n\ }\n\ }\n\ // bug-workaround - throw unhandledRejections in node-process\n\ if (\n\ typeof process === "object" && process\n\ && typeof process.on === "function"\n\ && process.unhandledRejections !== "strict"\n\ ) {\n\ process.unhandledRejections = "strict";\n\ process.on("unhandledRejection", function (err) {\n\ throw err;\n\ });\n\ }\n\ // init local\n\ local = {\n\ assertJsonEqual,\n\ assertOrThrow,\n\ coalesce,\n\ identity,\n\ isBrowser,\n\ isWebWorker,\n\ local,\n\ noop,\n\ objectAssignDefault,\n\ objectDeepCopyWithKeysSorted,\n\ onErrorThrow\n\ };\n\ globalThis.globalLocal = local;\n\ }());\n\ // assets.utility2.header.js - end\n\ ' local.assetsDict["/assets.index.template.html"] = local.assetsDict["/assets.utility2.template.html"] = '\ <!doctype html>\n\ <html lang="en">\n\ <head>\n\ <meta charset="utf-8">\n\ <meta name="viewport" content="width=device-width, initial-scale=1">\n\ <!-- "assets.utility2.template.html" -->\n\ <title>{{env.npm_package_name}} ({{env.npm_package_version}})</title>\n\ <style>\n\ /* jslint utility2:true */\n\ /*csslint\n\ */\n\ /* csslint ignore:start */\n\ *,\n\ *:after,\n\ *:before {\n\ box-sizing: border-box;\n\ }\n\ .uiAnimateSlide {\n\ overflow-y: hidden;\n\ transition: max-height ease-in 250ms, min-height ease-in 250ms, padding-bottom ease-in 250ms, padding-top ease-in 250ms;\n\ }\n\ /* csslint ignore:end */\n\ @keyframes uiAnimateSpin {\n\ 0% {\n\ transform: rotate(0deg);\n\ }\n\ 100% {\n\ transform: rotate(360deg);\n\ }\n\ }\n\ a {\n\ overflow-wrap: break-word;\n\ }\n\ body {\n\ background: #f7f7f7;\n\ font-family: Arial, Helvetica, sans-serif;\n\ font-size: small;\n\ margin: 0 40px;\n\ }\n\ body > div,\n\ body > input,\n\ body > pre,\n\ body > .button,\n\ body > .textarea {\n\ margin-bottom: 20px;\n\ margin-top: 0;\n\ }\n\ body > input,\n\ body > .button {\n\ width: 20rem;\n\ }\n\ body > .readonly {\n\ background: #ddd;\n\ }\n\ body > .textarea {\n\ height: 10rem;\n\ resize: vertical;\n\ width: 100%;\n\ }\n\ code,\n\ pre,\n\ .textarea {\n\ font-family: Consolas, Menlo, monospace;\n\ font-size: smaller;\n\ }\n\ pre {\n\ overflow-wrap: break-word;\n\ white-space: pre-wrap;\n\ }\n\ .button {\n\ background: #ddd;\n\ border: 1px solid #999;\n\ color: #000;\n\ cursor: pointer;\n\ display: inline-block;\n\ padding: 2px 5px;\n\ text-align: center;\n\ text-decoration: none;\n\ }\n\ .button:hover {\n\ background: #bbb;\n\ }\n\ .colorError {\n\ color: #d00;\n\ }\n\ .textarea {\n\ background: #fff;\n\ border: 1px solid #999;\n\ border-radius: 0;\n\ cursor: auto;\n\ overflow: auto;\n\ padding: 2px;\n\ }\n\ .zeroPixel {\n\ border: 0;\n\ height: 0;\n\ margin: 0;\n\ padding: 0;\n\ width: 0;\n\ }\n\ </style>\n\ </head>\n\ <body>\n\ <div class="uiAnimateSpin" style="animation: uiAnimateSpin 2s linear infinite; border: 5px solid #999; border-radius: 50%; border-top: 5px solid #7d7; display: none; height: 25px; vertical-align: middle; width: 25px;"></div>\n\ <script>\n\ /* jslint utility2:true */\n\ // polyfill globalThis\n\ (function () {\n\ /*\n\ * this function will polyfill globalThis\n\ */\n\ "use strict";\n\ window.globalThis = window.globalThis || globalThis;\n\ }());\n\ \n\ \n\ // init domOnEventWindowOnloadTimeElapsed\n\ (function () {\n\ /*\n\ * this function will measure and print time-elapsed for window.onload\n\ */\n\ "use strict";\n\ if (!(\n\ typeof window === "object" && window && window.document\n\ && typeof document.addEventListener === "function"\n\ ) || window.domOnEventWindowOnloadTimeElapsed) {\n\ return;\n\ }\n\ window.domOnEventWindowOnloadTimeElapsed = Date.now() + 100;\n\ window.addEventListener("load", function () {\n\ setTimeout(function () {\n\ window.domOnEventWindowOnloadTimeElapsed = (\n\ Date.now()\n\ - window.domOnEventWindowOnloadTimeElapsed\n\ );\n\ console.error(\n\ "domOnEventWindowOnloadTimeElapsed = "\n\ + window.domOnEventWindowOnloadTimeElapsed\n\ );\n\ }, 100);\n\ });\n\ }());\n\ \n\ \n\ // init domOnEventAjaxProgressUpdate\n\ (function () {\n\ /*\n\ * this function will display incrementing ajax-progress-bar\n\ */\n\ "use strict";\n\ let opt;\n\ let styleBar0;\n\ let styleBar;\n\ let styleModal0;\n\ let styleModal;\n\ let timeStart;\n\ let timerInterval;\n\ let timerTimeout;\n\ let tmp;\n\ let width;\n\ try {\n\ if (\n\ window.domOnEventAjaxProgressUpdate\n\ || !document.getElementById("domElementAjaxProgressBar1").style\n\ ) {\n\ return;\n\ }\n\ } catch (ignore) {\n\ return;\n\ }\n\ window.domOnEventAjaxProgressUpdate = function (gotoState, onError) {\n\ gotoState = (gotoState | 0) + 1;\n\ switch (gotoState) {\n\ // ajaxProgress - show\n\ case 1:\n\ // init timerInterval and timerTimeout\n\ if (!timerTimeout) {\n\ timeStart = Date.now();\n\ timerInterval = setInterval(opt, 2000, 1, onError);\n\ timerTimeout = setTimeout(opt, opt.timeout, 2, onError);\n\ }\n\ // show ajaxProgressBar\n\ if (width !== -1) {\n\ styleBar.background = styleBar0.background;\n\ }\n\ setTimeout(opt, 50, gotoState, onError);\n\ break;\n\ // ajaxProgress - increment\n\ case 2:\n\ // show ajaxProgressBar\n\ if (width === -1) {\n\ break;\n\ }\n\ styleBar.background = styleBar0.background;\n\ // reset ajaxProgress if it reaches end\n\ if ((styleBar.width.slice(0, -1) | 0) > 95) {\n\ width = 0;\n\ }\n\ // this algorithm will indefinitely increment ajaxProgress\n\ // with successively smaller increments without reaching 100%\n\ width += 1;\n\ styleBar.width = Math.max(\n\ 100 - 75 * Math.exp(-0.125 * width),\n\ styleBar.width.slice(0, -1) | 0\n\ ) + "%";\n\ // show ajaxProgressModal\n\ styleModal.height = "100%";\n\ styleModal.opacity = styleModal0.opacity;\n\ if (!opt.cnt) {\n\ setTimeout(opt, 0, gotoState, onError);\n\ }\n\ break;\n\ // ajaxProgress - 100%\n\ case 3:\n\ width = -1;\n\ styleBar.width = "100%";\n\ setTimeout(opt, 1000, gotoState, onError);\n\ break;\n\ // ajaxProgress - hide\n\ case 4:\n\ // debug timeElapsed\n\ tmp = Date.now();\n\ console.error(\n\ "domOnEventAjaxProgressUpdate - timeElapsed - "\n\ + (tmp - timeStart)\n\ + " ms"\n\ );\n\ // cleanup timerInterval and timerTimeout\n\ timeStart = tmp;\n\ clearInterval(timerInterval);\n\ timerInterval = undefined;\n\ clearTimeout(timerTimeout);\n\ timerTimeout = undefined;\n\ // hide ajaxProgressBar\n\ styleBar.background = "transparent";\n\ // hide ajaxProgressModal\n\ styleModal.opacity = "0";\n\ if (onError) {\n\ onError();\n\ }\n\ setTimeout(opt, 250, gotoState);\n\ break;\n\ // ajaxProgress - reset\n\ default:\n\ opt.cnt = 0;\n\ width = 0;\n\ styleBar.width = "0%";\n\ styleModal.height = "0";\n\ }\n\ };\n\ opt = window.domOnEventAjaxProgressUpdate;\n\ opt.end = function (onError) {\n\ opt.cnt = 0;\n\ window.domOnEventAjaxProgressUpdate(2, onError);\n\ };\n\ // init styleBar\n\ styleBar = document.getElementById("domElementAjaxProgressBar1").style;\n\ styleBar0 = Object.assign({}, styleBar);\n\ Object.entries({\n\ background: "#d00",\n\ height: "2px",\n\ left: "0",\n\ margin: "0",\n\ padding: "0",\n\ position: "fixed",\n\ top: "0",\n\ transition: "background 250ms, width 750ms",\n\ width: "0%",\n\ "z-index": "1"\n\ }).forEach(function (entry) {\n\ styleBar[entry[0]] = styleBar[entry[0]] || entry[1];\n\ });\n\ // init styleModal\n\ styleModal = document.getElementById("domElementAjaxProgressModal1") || {};\n\ styleModal = styleModal.style || {};\n\ styleModal0 = Object.assign({}, styleModal);\n\ Object.entries({\n\ height: "0",\n\ left: "0",\n\ margin: "0",\n\ padding: "0",\n\ position: "fixed",\n\ top: "0",\n\ transition: "opacity 125ms",\n\ width: "100%",\n\ "z-index": "1"\n\ }).forEach(function (entry) {\n\ styleModal[entry[0]] = styleModal[entry[0]] || entry[1];\n\ });\n\ // init state\n\ width = 0;\n\ opt.cnt = 0;\n\ opt.timeout = 30000;\n\ // init ajaxProgress\n\ window.domOnEventAjaxProgressUpdate();\n\ }());\n\ \n\ \n\ // init domOnEventDelegateDict\n\ (function () {\n\ /*\n\ * this function will handle delegated dom-evt\n\ */\n\ "use strict";\n\ let debounce;\n\ let timerTimeout;\n\ debounce = function () {\n\ return setTimeout(function () {\n\ timerTimeout = undefined;\n\ }, 30);\n\ };\n\ if (!(\n\ typeof window === "object" && window && window.document\n\ && typeof document.addEventListener === "function"\n\ ) || window.domOnEventDelegateDict) {\n\ return;\n\ }\n\ window.domOnEventDelegateDict = {};\n\ window.domOnEventDelegateDict.domOnEventDelegate = function (evt) {\n\ evt.targetOnEvent = evt.target.closest("[data-onevent]");\n\ if (\n\ !evt.targetOnEvent\n\ || evt.targetOnEvent.dataset.onevent === "domOnEventNoop"\n\ || evt.target.closest(".disabled,.readonly")\n\ ) {\n\ return;\n\ }\n\ // filter evt-change\n\ switch (evt.type !== "change" && evt.target.type) {\n\ case "checkbox":\n\ case "file":\n\ case "select-one":\n\ case "radio":\n\ return;\n\ }\n\ // filter evt-keyup\n\ switch (evt.type) {\n\ case "keyup":\n\ if (!timerTimeout && (\n\ evt.target.tagName === "INPUT"\n\ || evt.target.tagName === "TEXTAREA"\n\ )) {\n\ timerTimeout = debounce();\n\ if (evt.target.dataset.valueOld !== evt.target.value) {\n\ evt.target.dataset.valueOld = evt.target.value;\n\ break;\n\ }\n\ }\n\ return;\n\ }\n\ switch (evt.targetOnEvent.tagName) {\n\ case "BUTTON":\n\ case "FORM":\n\ evt.preventDefault();\n\ break;\n\ }\n\ evt.stopPropagation();\n\ // handle domOnEventClickTarget\n\ if (evt.targetOnEvent.dataset.onevent === "domOnEventClickTarget") {\n\ document.querySelector(\n\ evt.targetOnEvent.dataset.clickTarget\n\ ).click();\n\ return;\n\ }\n\ window.domOnEventDelegateDict[evt.targetOnEvent.dataset.onevent](evt);\n\ };\n\ // handle evt\n\ [\n\ "change",\n\ "click",\n\ "keyup",\n\ "submit"\n\ ].forEach(function (eventType) {\n\ document.addEventListener(\n\ eventType,\n\ window.domOnEventDelegateDict.domOnEventDelegate\n\ );\n\ });\n\ }());\n\ \n\ \n\ // init domOnEventSelectAllWithinPre\n\ (function () {\n\ /*\n\ * this function will limit select-all within <pre tabIndex="0"> elem\n\ * https://stackoverflow.com/questions/985272/selecting-text-in-an-element-akin-to-highlighting-with-your-mouse\n\ */\n\ "use strict";\n\ if (!(\n\ typeof window === "object" && window && window.document\n\ && typeof document.addEventListener === "function"\n\ ) || window.domOnEventSelectAllWithinPre) {\n\ return;\n\ }\n\ window.domOnEventSelectAllWithinPre = function (evt) {\n\ let range;\n\ let selection;\n\ if (\n\ evt && (evt.ctrlKey || evt.metaKey) && evt.key === "a"\n\ && evt.target.closest("pre")\n\ ) {\n\ range = document.createRange();\n\ range.selectNodeContents(evt.target.closest("pre"));\n\ selection = window.getSelection();\n\ selection.removeAllRanges();\n\ selection.addRange(range);\n\ evt.preventDefault();\n\ }\n\ };\n\ // handle evt\n\ document.addEventListener(\n\ "keydown",\n\ window.domOnEventSelectAllWithinPre\n\ );\n\ }());\n\ </script>\n\ <h1>\n\ <!-- utility2-comment\n\ <a\n\ {{#if env.npm_package_homepage}}\n\ href="{{env.npm_package_homepage}}"\n\ {{/if env.npm_package_homepage}}\n\ target="_blank"\n\ >\n\ utility2-comment -->\n\ {{env.npm_package_name}} ({{env.npm_package_version}})\n\ <!-- utility2-comment\n\ </a>\n\ utility2-comment -->\n\ </h1>\n\ <h3>{{env.npm_package_description}}</h3>\n\ <!-- utility2-comment\n\ <a class="button" download href="assets.app.js">download standalone app</a><br>\n\ <button class="button" data-onevent="testRunBrowser" id="buttonTestRun1">run browser-tests</button><br>\n\ <div class="uiAnimateSlide" id="htmlTestReport1" style="border-bottom: 0; border-top: 0; margin-bottom: 0; margin-top: 0; max-height: 0; padding-bottom: 0; padding-top: 0;"></div>\n\ utility2-comment -->\n\ \n\ \n\ <!-- custom-html-start -->\n\ <label>stderr and stdout</label>\n\ <textarea class="onevent-reset-output readonly textarea" id="outputStdout1" readonly></textarea>\n\ <!-- custom-html-end -->\n\ \n\ \n\ <!-- utility2-comment\n\ {{#if isRollup}}\n\ <script src="assets.app.js"></script>\n\ {{#unless isRollup}}\n\ <script src="assets.utility2.rollup.js"></script>\n\ <script>window.utility2_onReadyBefore.cnt += 1;</script>\n\ <script src="utility2.state.init.js"></script>\n\ utility2-comment -->\n\ <script src="assets.{{packageJson.nameLib}}.js"></script>\n\ <script src="assets.example.js"></script>\n\ <script src="assets.test.js"></script>\n\ <script>\n\ if (window.utility2_onReadyBefore) {\n\ window.utility2_onReadyBefore();\n\ }\n\ </script>\n\ <!-- utility2-comment\n\ {{/if isRollup}}\n\ utility2-comment -->\n\ <div style="text-align: center;">\n\ [\n\ this app was created with\n\ <a\n\ href="https://github.com/kaizhu256/node-utility2" target="_blank"\n\ >utility2</a>\n\ ]\n\ </div>\n\ </body>\n\ </html>\n\ '; // https://img.shields.io/badge/last_build-0000_00_00_00_00_00_UTC_--_master_--_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-0077ff.svg?style=flat local.assetsDict["/assets.buildBadge.template.svg"] = '<svg xmlns="http://www.w3.org/2000/svg" width="563" height="20"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><rect rx="0" width="563" height="20" fill="#555"/><rect rx="0" x="61" width="502" height="20" fill="#07f"/><path fill="#07f" d="M61 0h4v20h-4z"/><rect rx="0" width="563" height="20" fill="url(#a)"/><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="31.5" y="15" fill="#010101" fill-opacity=".3">last build</text><text x="31.5" y="14">last build</text><text x="311" y="15" fill="#010101" fill-opacity=".3">0000-00-00 00:00:00 UTC - master - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</text><text x="311" y="14">0000-00-00 00:00:00 UTC - master - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</text></g></svg>'; local.assetsDict["/assets.example.html"] = ""; local.assetsDict["/assets.example.template.js"] = '\ /*\n\ example.js\n\ \n\ this script will run web-demo of my-app-lite\n\ \n\ instruction\n\ 1. save this script as example.js\n\ 2. run shell-command:\n\ $ npm install my-app-lite && \\\n\ PORT=8081 node example.js\n\ 3. open browser to http://127.0.0.1:8081 and play with web-demo\n\ 4. edit this script to suit your needs\n\ */\n\ \n\ \n\ /* istanbul instrument in package my_app */\n\ ' + local.assetsDict["/assets.utility2.header.js"] + '\ \n\ \n\ /* jslint utility2:true */\n\ (function (local) {\n\ "use strict";\n\ \n\ \n\ // run shared js\-env code - init-before\n\ (function () {\n\ // init local\n\ local = (\n\ globalThis.utility2_rollup\n\ || globalThis.utility2_my_app\n\ || require("my-app-lite")\n\ );\n\ // init exports\n\ globalThis.local = local;\n\ }());\n\ \n\ \n\ /* istanbul ignore next */\n\ // run browser js\-env code - init-test\n\ (function () {\n\ if (!local.isBrowser) {\n\ return;\n\ }\n\ // log stderr and stdout to #outputStdout1\n\ ["error", "log"].forEach(function (key) {\n\ let elem;\n\ let fnc;\n\ elem = document.querySelector("#outputStdout1");\n\ if (!elem) {\n\ return;\n\ }\n\ fnc = console[key];\n\ console[key] = function (...argList) {\n\ fnc(...argList);\n\ // append text to #outputStdout1\n\ elem.textContent += argList.map(function (arg) {\n\ return (\n\ typeof arg === "string"\n\ ? arg\n\ : JSON.stringify(arg, undefined, 4)\n\ );\n\ }).join(" ").replace((\n\ /\\u001b\\[\\d*m/g\n\ ), "") + "\\n";\n\ // scroll textarea to bottom\n\ elem.scrollTop = elem.scrollHeight;\n\ };\n\ });\n\ local.objectAssignDefault(local, globalThis.domOnEventDelegateDict);\n\ globalThis.domOnEventDelegateDict = local;\n\ }());\n\ \n\ \n\ /* istanbul ignore next */\n\ // run node js\-env code - init-test\n\ (function () {\n\ if (local.isBrowser) {\n\ return;\n\ }\n\ // init exports\n\ module.exports = local;\n\ // init assetsDict\n\ local.assetsDict = local.assetsDict || {};\n\ /* jslint ignore:start */\n\ local.assetsDict["/assets.index.template.html"] = \'\\\n\ ' + local.assetsDict["/assets.index.template.html"].replace((/\n/g), "\\n\\\n") + '\';\n\ /* jslint ignore:end */\n\ local.assetsDict["/assets.my_app.js"] = (\n\ local.assetsDict["/assets.my_app.js"]\n\ || require("fs").readFileSync(\n\ require("path").resolve(local.__dirname + "/lib.my_app.js"),\n\ "utf8"\n\ ).replace((\n\ /^#!\\//\n\ ), "// ")\n\ );\n\ /* validateLineSortedReset */\n\ local.assetsDict["/"] = local.assetsDict[\n\ "/assets.index.template.html"\n\ ].replace((\n\ /\\{\\{env\\.(\\w+?)\\}\\}/g\n\ ), function (match0, match1) {\n\ switch (match1) {\n\ case "npm_package_description":\n\ return "the greatest app in the world!";\n\ case "npm_package_name":\n\ return "my-app-lite";\n\ case "npm_package_nameLib":\n\ return "my_app";\n\ case "npm_package_version":\n\ return "0.0.1";\n\ default:\n\ return match0;\n\ }\n\ });\n\ local.assetsDict["/assets.example.html"] = local.assetsDict["/"];\n\ // init cli\n\ if (module !== require.main || globalThis.utility2_rollup) {\n\ return;\n\ }\n\ local.assetsDict["/assets.example.js"] = (\n\ local.assetsDict["/assets.example.js"]\n\ || require("fs").readFileSync(__filename, "utf8")\n\ );\n\ local.assetsDict["/favicon.ico"] = local.assetsDict["/favicon.ico"] || "";\n\ local.assetsDict["/index.html"] = local.assetsDict["/"];\n\ // if $npm_config_timeout_exit exists,\n\ // then exit this process after $npm_config_timeout_exit ms\n\ if (Number(process.env.npm_config_timeout_exit)) {\n\ setTimeout(process.exit, Number(process.env.npm_config_timeout_exit));\n\ }\n\ // start server\n\ if (globalThis.utility2_serverHttp1) {\n\ return;\n\ }\n\ process.env.PORT = process.env.PORT || "8081";\n\ console.error("http-server listening on port " + process.env.PORT);\n\ require("http").createServer(function (req, res) {\n\ let data;\n\ data = local.assetsDict[require("url").parse(req.url).pathname];\n\ if (data !== undefined) {\n\ res.end(data);\n\ return;\n\ }\n\ res.statusCode = 404;\n\ res.end();\n\ }).listen(process.env.PORT);\n\ }());\n\ }());\n\ '; local.assetsDict["/assets.my_app.template.js"] = '\ #!/usr/bin/env node\n\ /*\n\ * lib.my_app.js ({{packageJson.version}})\n\ * https://github.com/kaizhu256/node-my-app-lite\n\ * {{packageJson.description}}\n\ *\n\ */\n\ \n\ \n\ /* istanbul instrument in package my_app */\n\ ' + local.assetsDict["/assets.utility2.header.js"] + '\ \n\ \n\ (function (local) {\n\ "use strict";\n\ \n\ \n\ /* istanbul ignore next */\n\ // run shared js\-env code - init-before\n\ (function () {\n\ // init local\n\ local = (\n\ globalThis.utility2_rollup\n\ // || globalThis.utility2_rollup_old\n\ // || require("./assets.utility2.rollup.js")\n\ || globalThis.globalLocal\n\ );\n\ // init exports\n\ if (local.isBrowser) {\n\ globalThis.utility2_my_app = local;\n\ } else {\n\ module.exports = local;\n\ module.exports.__dirname = __dirname;\n\ }\n\ // init lib main\n\ local.my_app = local;\n\ \n\ \n\ /* validateLineSortedReset */\n\ return;\n\ }());\n\ }());\n\ '; local.assetsDict["/assets.readme.template.md"] = '\ # my-app-lite\n\ the greatest app in the world!\n\ \n\ # live web demo\n\ - [https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app)\n\ \n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithub.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app)\n\ \n\ \n\ [![travis-ci.com build-status](https://api.travis-ci.com/kaizhu256/node-my-app-lite.svg)](https://travis-ci.com/kaizhu256/node-my-app-lite) [![coverage](https://kaizhu256.github.io/node-my-app-lite/build/coverage/coverage.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build/coverage/index.html)\n\ \n\ [![NPM](https://nodei.co/npm/my-app-lite.png?downloads=true)](https://www.npmjs.com/package/my-app-lite)\n\ \n\ [![build commit status](https://kaizhu256.github.io/node-my-app-lite/build/build.badge.svg)](https://travis-ci.com/kaizhu256/node-my-app-lite)\n\ \n\ | git-branch : | [master](https://github.com/kaizhu256/node-my-app-lite/tree/master) | [beta](https://github.com/kaizhu256/node-my-app-lite/tree/beta) | [alpha](https://github.com/kaizhu256/node-my-app-lite/tree/alpha)|\n\ |--:|:--|:--|:--|\n\ | test-server-github : | [![github.com test-server](https://kaizhu256.github.io/node-my-app-lite/GitHub-Mark-32px.png)](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/app) | [![github.com test-server](https://kaizhu256.github.io/node-my-app-lite/GitHub-Mark-32px.png)](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app) | [![github.com test-server](https://kaizhu256.github.io/node-my-app-lite/GitHub-Mark-32px.png)](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/app)|\n\ | test-server-heroku : | [![heroku.com test-server](https://kaizhu256.github.io/node-my-app-lite/heroku-logo.75x25.png)](https://h1-my-app-master.herokuapp.com) | [![heroku.com test-server](https://kaizhu256.github.io/node-my-app-lite/heroku-logo.75x25.png)](https://h1-my-app-beta.herokuapp.com) | [![heroku.com test-server](https://kaizhu256.github.io/node-my-app-lite/heroku-logo.75x25.png)](https://h1-my-app-alpha.herokuapp.com)|\n\ | test-report : | [![test-report](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/test-report.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/test-report.html) | [![test-report](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/test-report.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/test-report.html) | [![test-report](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/test-report.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/test-report.html)|\n\ | coverage : | [![coverage](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/coverage/coverage.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/coverage/index.html) | [![coverage](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/coverage/coverage.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/coverage/index.html) | [![coverage](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/coverage/coverage.badge.svg)](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/coverage/index.html)|\n\ | build-artifacts : | [![build-artifacts](https://kaizhu256.github.io/node-my-app-lite/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-my-app-lite/tree/gh-pages/build..master..travis-ci.com) | [![build-artifacts](https://kaizhu256.github.io/node-my-app-lite/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-my-app-lite/tree/gh-pages/build..beta..travis-ci.com) | [![build-artifacts](https://kaizhu256.github.io/node-my-app-lite/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-my-app-lite/tree/gh-pages/build..alpha..travis-ci.com)|\n\ \n\ [![npmPackageListing](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmPackageListing.svg)](https://github.com/kaizhu256/node-my-app-lite)\n\ \n\ ![npmPackageDependencyTree](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmPackageDependencyTree.svg)\n\ \n\ \n\ # table of contents\n\ \n\ \n\ # cdn download\n\ - [https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app/assets.my_app.js](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app/assets.my_app.js)\n\ \n\ \n\ # documentation\n\ #### api doc\n\ - [https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/apidoc.html](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/apidoc.html)\n\ \n\ [![apidoc](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fapidoc.html.png)](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/apidoc.html)\n\ \n\ #### cli help\n\ ![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmPackageCliHelp.svg)\n\ \n\ #### changelog 0.0.1\n\ - update build\n\ - none\n\ \n\ #### todo\n\ - none\n\ \n\ \n\ # quickstart standalone app\n\ #### to run this example, follow instruction in script below\n\ - [assets.app.js](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app/assets.app.js)\n\ ```shell\n\ # example.sh\n\ \n\ # this shell script will download and run web-demo of my-app-lite as standalone app\n\ \n\ # 1. download standalone app\n\ curl -O https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app/assets.app.js\n\ # 2. run standalone app\n\ PORT=8081 node ./assets.app.js\n\ # 3. open browser to http://127.0.0.1:8081 and play with web-demo\n\ # 4. edit file assets.app.js to suit your needs\n\ ```\n\ \n\ #### output from browser\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleSh.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/app/assets.example.html)\n\ \n\ #### output from shell\n\ ![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleSh.svg)\n\ \n\ \n\ # quickstart example.js\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/app/assets.example.html)\n\ \n\ #### to run this example, follow instruction in script below\n\ - [example.js](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/example.js)\n\ ```javascript\n' + local.assetsDict["/assets.example.template.js"] + '```\n\ \n\ #### output from browser\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/app/assets.example.html)\n\ \n\ #### output from shell\n\ ![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.svg)\n\ \n\ \n\ # extra screenshots\n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fapidoc.html.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fapidoc.html.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fapidoc.html.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fapidoc.html.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fcoverage.lib.html.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fcoverage.lib.html.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fcoverage.lib.html.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fcoverage.lib.html.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Ftest-report.html.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Ftest-report.html.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Ftest-report.html.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Ftest-report.html.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithub.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithub.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithub.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithub.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithubTest.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithubTest.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithubTest.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployGithubTest.browser.%252Fnode-my-app-lite%252Fbuild%252Fapp.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHeroku.browser.%252F.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHeroku.browser.%252F.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHeroku.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHeroku.browser.%252F.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHerokuTest.browser.%252F.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHerokuTest.browser.%252F.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHerokuTest.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.deployHerokuTest.browser.%252F.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmTest.browser.%252F.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmTest.browser.%252F.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmTest.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.npmTest.browser.%252F.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.browser.%252F.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.browser.%252F.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleJs.browser.%252F.png)\n\ \n\ 1. [https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleSh.browser.%252F.png](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleSh.browser.%252F.png)\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleSh.browser.%252F.png)](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.testExampleSh.browser.%252F.png)\n\ \n\ \n\ # package.json\n\ ```json\n\ {\n\ "!!jslint_utility2": true,\n\ "author": "kai zhu <kaizhu256@gmail.com>",\n\ "description": "the greatest app in the world!",\n\ "devDependencies": {\n\ "utility2": "kaizhu256/node-utility2#alpha"\n\ },\n\ "engines": {\n\ "node": ">=12.0"\n\ },\n\ "fileCount": 0,\n\ "homepage": "https://github.com/kaizhu256/node-my-app-lite",\n\ "keywords": [],\n\ "license": "MIT",\n\ "main": "lib.my_app.js",\n\ "name": "my-app-lite",\n\ "nameAliasPublish": "",\n\ "repository": {\n\ "type": "git",\n\ "url": "https://github.com/kaizhu256/node-my-app-lite.git"\n\ },\n\ "scripts": {\n\ "build-ci": "./npm_scripts.sh",\n\ "env": "env",\n\ "eval": "./npm_scripts.sh",\n\ "heroku-postbuild": "./npm_scripts.sh",\n\ "postinstall": "./npm_scripts.sh",\n\ "start": "./npm_scripts.sh",\n\ "test": "./npm_scripts.sh",\n\ "utility2": "./npm_scripts.sh"\n\ },\n\ "version": "0.0.1"\n\ }\n\ ```\n\ \n\ \n\ # changelog of last 50 commits\n\ [![screenshot](https://kaizhu256.github.io/node-my-app-lite/build/screenshot.gitLog.svg)](https://github.com/kaizhu256/node-my-app-lite/commits)\n\ \n\ \n\ # internal build script\n\ - build_ci.sh\n\ ```shell\n\ # build_ci.sh\n\ \n\ # this shell script will run build-ci for this package\n\ \n\ shBuildCiAfter () {(set -e\n\ # shDeployCustom\n\ shDeployGithub\n\ # shDeployHeroku\n\ shReadmeTest example.sh\n\ )}\n\ \n\ shBuildCiBefore () {(set -e\n\ # shNpmTestPublished\n\ shReadmeTest example.js\n\ )}\n\ \n\ # run shBuildCi\n\ eval "$(utility2 source)"\n\ shBuildCi\n\ ```\n\ \n\ \n\ # misc\n\ - this package was created with [utility2](https://github.com/kaizhu256/node-utility2)\n\ '; local.assetsDict["/assets.test.template.js"] = '\ /* istanbul instrument in package my_app */\n\ ' + local.assetsDict["/assets.utility2.header.js"] + '\ \n\ \n\ /* jslint utility2:true */\n\ (function (local) {\n\ "use strict";\n\ \n\ \n\ /* istanbul ignore next */\n\ // run shared js\-env code - init-before\n\ (function () {\n\ // init local\n\ local = globalThis.utility2 || require("utility2");\n\ local = local.requireReadme();\n\ globalThis.local = local;\n\ // init test\n\ local.testRunDefault(local);\n\ }());\n\ \n\ \n\ // run shared js\-env code - function\n\ (function () {\n\ return;\n\ }());\n\ }());\n\ '; local.assetsDict["/assets.testReport.template.html"] = local.assetsDict["/assets.utility2.template.html"] .replace("assets.utility2.template.html", "") .replace((/<title>.*?<\/title>/), "<title>test-report</title>") .replace("</style>\n", '\ <style>\n\ /* jslint utility2:true */\n\ /*csslint\n\ */\n\ .testReportDiv img {\n\ border: 1px solid #999;\n\ margin: 5px 0 5px 0;\n\ max-height: 256px;\n\ max-width: 512px;\n\ }\n\ .testReportDiv pre {\n\ background: #fdd;\n\ border-top: 1px solid #999;\n\ margin-bottom: 0;\n\ padding: 10px;\n\ }\n\ .testReportDiv span {\n\ display: inline-block;\n\ width: 120px;\n\ }\n\ .testReportDiv table {\n\ border-top: 1px solid #999;\n\ text-align: left;\n\ width: 100%;\n\ }\n\ .testReportDiv table > tbody > tr:nth-child(odd) {\n\ background: #bfb;\n\ }\n\ .testReportDiv .displayNone {\n\ display: none;\n\ }\n\ .testReportDiv .footer {\n\ text-align: center;\n\ }\n\ .testReportDiv .platform {\n\ background: #fff;\n\ border: 1px solid #999;\n\ margin-bottom: 20px;\n\ padding: 0 10px 10px 10px;\n\ text-align: left;\n\ }\n\ .testReportDiv .summary {\n\ background: #bfb;\n\ }\n\ .testReportDiv .testFailed {\n\ background: #f99;\n\ }\n\ .testReportDiv .testPending {\n\ background: #99f;\n\ }\n\ </style>\n\ '.replace("<style>\n", "")).replace((/<\/script>[\S\s]*?