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
JavaScript
#!/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\
[](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app)\n\
\n\
\n\
[](https://travis-ci.com/kaizhu256/node-my-app-lite) [](https://kaizhu256.github.io/node-my-app-lite/build/coverage/index.html)\n\
\n\
[](https://www.npmjs.com/package/my-app-lite)\n\
\n\
[](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 : | [](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/app) | [](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/app) | [](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/app)|\n\
| test-server-heroku : | [](https://h1-my-app-master.herokuapp.com) | [](https://h1-my-app-beta.herokuapp.com) | [](https://h1-my-app-alpha.herokuapp.com)|\n\
| test-report : | [](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/test-report.html) | [](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/test-report.html) | [](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/test-report.html)|\n\
| coverage : | [](https://kaizhu256.github.io/node-my-app-lite/build..master..travis-ci.com/coverage/index.html) | [](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/coverage/index.html) | [](https://kaizhu256.github.io/node-my-app-lite/build..alpha..travis-ci.com/coverage/index.html)|\n\
| build-artifacts : | [](https://github.com/kaizhu256/node-my-app-lite/tree/gh-pages/build..master..travis-ci.com) | [](https://github.com/kaizhu256/node-my-app-lite/tree/gh-pages/build..beta..travis-ci.com) | [](https://github.com/kaizhu256/node-my-app-lite/tree/gh-pages/build..alpha..travis-ci.com)|\n\
\n\
[](https://github.com/kaizhu256/node-my-app-lite)\n\
\n\
\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\
[](https://kaizhu256.github.io/node-my-app-lite/build..beta..travis-ci.com/apidoc.html)\n\
\n\
#### cli help\n\
\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\
[](https://kaizhu256.github.io/node-my-app-lite/build/app/assets.example.html)\n\
\n\
#### output from shell\n\
\n\
\n\
\n\
# quickstart example.js\n\
[](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\
[](https://kaizhu256.github.io/node-my-app-lite/build/app/assets.example.html)\n\
\n\
#### output from shell\n\
\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\
[](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\
[](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\
[](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\
[](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\
[](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\
[](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\
[](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\
[](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\
[](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\
[](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\
[](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]*?