utility2
this zero-dependency package will provide high-level functions to to build, test, and deploy webapps
live web demo
git-branch : | master | beta | alpha |
---|---|---|---|
test-server-github : | |||
test-server-heroku : | |||
test-report : | |||
coverage : | |||
build-artifacts : |
table of contents
- cdn download
- documentation
- quickstart standalone app
- quickstart example.js
- extra screenshots
- package.json
- changelog of last 50 commits
- internal build script
- misc
cdn download
documentation
api doc
cli help
changelog 2020.11.12
- jslint - update to v2020.11.6
- update function chromeDevtoolsClient with timeout
- remove file lib.puppeteer.js and replace with function chromeDevtoolsClientCreate
- remove dependency on env-var $CHROME_BIN
- remove functions base64FromBuffer, base64ToUtf8, cryptoAesXxxCbcRawDecrypt, cryptoAesXxxCbcRawEncrypt, gotoNext, onErrorWithStack
- jslint - fix off-by-one column in autofix-expected_a_before_b
- decouple build from npm-env-variables npm_config_xxx and npm_package_xxx
- update lib.utility2.sh to remove dependency on $UTILITY2_MACRO_JS
- none
todo
- fix test-report bug with duplicate github and heroku tests
- migrate from travis to github-actions
- update function fsWriteFileWithMkdirp to write to tmpfile first
- jslint - unmangle function jslintAutofixLocalFunction
- update function shRawLibFetch to minify assets
- remove dependency to req.urlParsed
- istanbul - inline class Instrumenter into function instrumentSync
- replace db-lite with sql_lite.js
- add default testCase _testCase_cliRun_help
- add server stress-test using puppeteer
- none
quickstart standalone app
to run this example, follow instruction in script below
# example.sh # this shell script will download and run web-demo of utility2 as standalone app # 1. download standalone app curl -O https://kaizhu256.github.io/node-utility2/build..beta..travis-ci.com/app/assets.app.js# 2. run standalone app PORT=8081 node ./assets.app.js# 3. open browser to http://127.0.0.1:8081 and play with web-demo # 4. edit file assets.app.js to suit your needs
output from browser
output from shell
quickstart example.js
to run this example, follow the instruction in the script below
/*example.js this script will demo automated browser-tests with coverage(via puppeteer and istanbul) instruction 1. save this script as example.js 2. run the shell-command: $ npm install utility2 && \ PATH="$(pwd)/node_modules/.bin:$PATH" \ PORT=8081 \ npm_config_mode_coverage=utility2 \ node_modules/.bin/utility2 test example.js 3. view test-report in .tmp/build/test-report.html 4. view coverage in .tmp/build/coverage/index.html*/ /* istanbul instrument in package utility2 */// assets.utility2.header.js - start/* jslint utility2:true *//* istanbul ignore next */// run shared js-env code - init-local { "use strict"; let isBrowser; let isWebWorker; let local; // polyfill globalThis if !typeof globalThis === "object" && globalThis if typeof window === "object" && window && windowwindow === window windowglobalThis = window; if typeof global === "object" && global && globalglobal === global globalglobalThis = global; // init debugInline if !globalThisdebugInline let consoleError; consoleError = consoleerror; globalThis { /* * this function will both print <argList> to stderr * and return <argList>[0] */ ; ; ; return argList0; }; // init isBrowser isBrowser = typeof globalThisXMLHttpRequest === "function" && globalThisnavigator && typeof globalThisnavigatoruserAgent === "string" ; // init isWebWorker isWebWorker = isBrowser && typeof globalThisimportScripts === "function" ; // init function { /* * 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 return obj; // recursively deep-copy obj with keys sorted sorted = {}; Object; return sorted; } { /* * this function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>) */ aa = JSON; bb = JSON; if aa !== bb throw JSON + " !== " + JSON; } { /* * this function will throw <msg> if <passed> is falsy */ if passed return; throw msg && typeof msgmessage === "string" && typeof msgstack === "string" // if msg is err, then leave as is ? msg : typeof msg === "string" // if msg is string, then leave as is ? msg // else JSON.stringify(msg) : JSON ; } { /* * this function will coalesce null, undefined, or "" in <argList> */ let arg; let ii; ii = 0; while ii < argListlength arg = argListii; if arg !== undefined && arg !== null && arg !== "" return arg; ii += 1; return arg; } { /* * this function will return <val> */ return val; } { /* * this function will do nothing */ return; } { /* * this function will if items from <tgt> are null, undefined, or "", * then overwrite them with items from <src> */ let recurse; { Object; }; ; return tgt; } { /* * this function will throw <err> if exists */ if err throw err; } // bug-workaround - throw unhandledRejections in node-process if typeof process === "object" && process && typeof processon === "function" && processunhandledRejections !== "strict" processunhandledRejections = "strict"; process; // init local local = assertJsonEqual assertOrThrow coalesce identity isBrowser isWebWorker local noop objectAssignDefault objectDeepCopyWithKeysSorted onErrorThrow ; globalThisglobalLocal = local;};// assets.utility2.header.js - end /* jslint utility2:true */ {"use strict"; // run shared js-env code - init-before {// init locallocal = globalThisutility2_rollup || globalThisutility2_utility2 || ;// init exportsglobalThislocal = local;// run test-serverlocal;// init assetslocalassetsDict"/assets.hello.txt" = "hello \ud83d\ude01\n";localassetsDict"/assets.index.template.html" = "";}; // run shared js-env code - function {local {/* * this function will test ajax's "200 ok" handling-behavior */ if !localisBrowser ; return; opt = {}; // test ajax-path "assets.hello.txt" local;}; local {/* * this function will test ajax's "404 not found" handling-behavior */ if !localisBrowser ; return; opt = {}; // test ajax-path "/undefined" local;}; local {/* * this function will test webpage's default handling-behavior */ if localisBrowser ; return; opt = url: localserverLocalHost + "?modeTest=1" ; local;};}; /* istanbul ignore next */// run browser js-env code - init-test {if !localisBrowser return;// log stderr and stdout to #outputStdout1"error" "log";local;globalThisdomOnEventDelegateDict = local;}; /* istanbul ignore next */// run node js-env code - init-test {if localisBrowser return;// init exportsmoduleexports = local;// init assetsDictlocalassetsDict = localassetsDict || {};/* jslint ignore:start */localassetsDict"/assets.index.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>edit or paste script below to cover and test</label>\n\<textarea class="textarea" data-onevent="domOnEventInputChange" id="inputTextarea1">\n\// remove comment below to disable jslint\n\/*jslint browser, devel*/\n\/*global window*/\n\(function () {\n\ "use strict";\n\ var testCaseDict;\n\ testCaseDict = {};\n\ testCaseDict.modeTest = 1;\n\\n\ // comment this testCase to disable failed error demo\n\ testCaseDict.testCase_failed_error_demo = function (opt, onError) {\n\ /*\n\ * this function will run a failed error demo\n\ */\n\ // hack-jslint\n\ window.utility2.noop(opt);\n\ onError(new Error("this is a failed error demo"));\n\ };\n\\n\ testCaseDict.testCase_passed_ajax_demo = function (opt, onError) {\n\ /*\n\ * this function will demo a passed ajax test\n\ */\n\ var data;\n\ opt = {url: "/"};\n\ // test ajax-req for main-page "/"\n\ window.utility2.ajax(opt, function (err, xhr) {\n\ try {\n\ // handle err\n\ console.assert(!err, err);\n\ // validate "200 ok" status\n\ console.assert(xhr.statusCode === 200, xhr.statusCode);\n\ // validate non-empty data\n\ data = xhr.responseText;\n\ console.assert(data && data.length > 0, data);\n\ onError();\n\ } catch (errCaught) {\n\ onError(errCaught);\n\ }\n\ });\n\ };\n\\n\ window.utility2.testRunDefault(testCaseDict);\n\}());\n\</textarea>\n\<button class="button" data-onevent="domOnEventInputChange" id="buttonJslintAutofix1">jslint autofix</button><br>\n\<pre class= "colorError" id="outputJslintPre1" tabindex="0"></pre>\n\<label>instrumented-code</label>\n\<textarea class="readonly textarea" id="outputTextarea1" readonly tabindex="0"></textarea>\n\<label>stderr and stdout</label>\n\<textarea class="onevent-reset-output readonly textarea" id="outputStdout1" readonly></textarea>\n\<div id="htmlTestReport2"></div>\n\<div id="htmlCoverageReport1"></div>\n\<script>\n\/* jslint utility2:true */\n\window.addEventListener("load", function (local) {\n\"use strict";\n\local = window.utility2;\n\document.querySelectorAll(\n\ "#buttonTestRun1, #htmlTestReport1"\n\).forEach(function (elem) {\n\ elem.style.display = "none";\n\});\n\local.domOnEventInputChange = function (evt) {\n\ switch (evt.type + "." + evt.target.id) {\n\ case "click.buttonJslintAutofix1":\n\ case "keyup.inputTextarea1":\n\ // jslint #inputTextarea1\n\ local.jslintAndPrint(document.querySelector(\n\ "#inputTextarea1"\n\ ).value, "inputTextarea1.js", {\n\ autofix: evt.target.id === "buttonJslintAutofix1",\n\ conditional: evt.target.id !== "buttonJslintAutofix1"\n\ });\n\ if (local.jslint.jslintResult.autofix) {\n\ document.querySelector(\n\ "#inputTextarea1"\n\ ).value = local.jslint.jslintResult.code;\n\ }\n\ document.querySelector(\n\ "#outputJslintPre1"\n\ ).textContent = local.jslint.jslintResult.errMsg.replace((\n\ /\\u001b\\[\\d*m/g\n\ ), "").trim();\n\ // try to cleanup __coverage__\n\ try {\n\ delete globalThis.__coverage__["/inputTextarea1.js"];\n\ } catch (ignore) {}\n\ // try to cover and eval #inputTextarea1\n\ try {\n\ document.querySelector(\n\ "#outputTextarea1"\n\ ).value = local.istanbul.instrumentSync(\n\ document.querySelector("#inputTextarea1").value,\n\ "/inputTextarea1.js"\n\ );\n\ eval( // jslint ignore:line\n\ document.querySelector("#outputTextarea1").value\n\ );\n\ } catch (errCaught) {\n\ console.error(errCaught);\n\ }\n\ break;\n\ }\n\};\n\// handle evt\n\local.eventListenerAdd("utility2.testRunEnd", function () {\n\ document.querySelector(\n\ "#htmlCoverageReport1"\n\ ).innerHTML = local.istanbul.coverageReportCreate({\n\ coverage: globalThis.__coverage__\n\ });\n\});\n\local.eventListenerAdd("utility2.testRunProgressUpdate", function (testReport) {\n\ document.querySelector(\n\ "#htmlTestReport2"\n\ ).innerHTML = local.testReportMerge(testReport, {});\n\});\n\local.domOnEventInputChange({\n\ target: {\n\ id: "inputTextarea1"\n\ },\n\ type: "keyup"\n\});\n\});\n\</script>\n\<!-- custom-html-end -->\n\\n\\n\<!-- utility2-comment\n\{{#if isRollup}}\n\<script src="assets.app.js"></script>\n\{{#unless isRollup}}\n\utility2-comment -->\n\<script src="assets.utility2.lib.istanbul.js"></script>\n\<script src="assets.utility2.lib.jslint.js"></script>\n\<script src="assets.utility2.lib.marked.js"></script>\n\<script src="assets.utility2.js"></script>\n\<script>window.utility2_onReadyBefore.cnt += 1;</script>\n\<script src="utility2.state.init.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\';/* jslint ignore:end */localassetsDict"/assets.utility2.js" = localassetsDict"/assets.utility2.js" || ;/* validateLineSortedReset */localassetsDict"/" = localassetsDict "/assets.index.template.html";localassetsDict"/assets.example.html" = localassetsDict"/";// init cliif module !== requiremain || globalThisutility2_rollup return;localassetsDict"/assets.example.js" = localassetsDict"/assets.example.js" || ;localassetsDict"/favicon.ico" = localassetsDict"/favicon.ico" || "";localassetsDict"/index.html" = localassetsDict"/";// if $npm_config_timeout_exit exists,// then exit this process after $npm_config_timeout_exit msif Numberprocessenvnpm_config_timeout_exit ;// start serverif globalThisutility2_serverHttp1 return;processenvPORT = processenvPORT || "8081";console;;};};
output from browser
output from shell
extra screenshots
-
https://kaizhu256.github.io/node-utility2/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fapidoc.html.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Fcoverage.lib.html.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.buildCi.browser.%252F.tmp%252Fbuild%252Ftest-report.html.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.deployGithub.browser.%252Fnode-utility2%252Fbuild%252Fapp.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.deployGithubTest.browser.%252Fnode-utility2%252Fbuild%252Fapp.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.deployHeroku.browser.%252F.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.deployHerokuTest.browser.%252F.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.npmTest.browser.%252F.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.testExampleJs.browser.%252F.png
-
https://kaizhu256.github.io/node-utility2/build/screenshot.testExampleSh.browser.%252F.png
package.json
changelog of last 50 commits
internal build script
- Dockerfile.base
# Dockerfile.base # docker build -f .tmp/README.Dockerfile.base -t kaizhu256/node-utility2:base . # docker build -f ".tmp/README.Dockerfile.$DOCKER_TAG" -t "$GITHUB_REPO:$DOCKER_TAG" . # https://hub.docker.com/_/node/ FROM debian:stable-slimMAINTAINER kai zhu <kaizhu256@gmail.com>VOLUME [ \ "/mnt", \ "/root", \ "/tmp", \ "/usr/share/doc", \ "/usr/share/man", \ "/var/cache", \ "/var/lib/apt", \ "/var/log", \ "/var/tmp" \]WORKDIR /tmp# install nodejs # https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions RUN # install google-chrome-stable RUN # install extra RUN
- Dockerfile.latest
# Dockerfile.latest FROM kaizhu256/node-utility2:baseMAINTAINER kai zhu <kaizhu256@gmail.com># install utility2 RUN
- Dockerfile.tmp
# Dockerfile.tmp FROM kaizhu256/node-utility2:baseMAINTAINER kai zhu <kaizhu256@gmail.com># install utility2 RUN
- build_ci.sh
# build_ci.sh # this shell script will run the build for this package # run shBuildCi . ./lib.utility2.shshBuildCi
misc
- this package was created with utility2