diff --git a/.travis.yml b/.travis.yml index f7c5b6433..c007f11ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ branches: - /^master$/ - /^publish$/ - /^task$/ +cache: + npm: false env: global: # this value is auto-created by shTravisCryptoAesEncryptYml diff --git a/README.md b/README.md index 9a731495b..24e5af3c7 100644 --- a/README.md +++ b/README.md @@ -56,18 +56,26 @@ this zero-dependency package will provide browser-compatible versions of jslint [![apidoc](https://kaizhu256.github.io/node-jslint-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fapidoc.html.png)](https://kaizhu256.github.io/node-jslint-lite/build..beta..travis-ci.org/apidoc.html) #### todo +- add test-coverage for autofix +- jslint - remove bad_property_a and unexpected_a hacks +- jslint - fix lineOffset issue with ignored-lines +- jslint - sort nested switch-statements - jslint-autofix - move inner-loop to outer -- jslint - refactor files to 80 chr column-limit -- add jslint-rule unexpected_empty_lines -- merge function jslintAndPrintConditional into jslintAndPrint -- update jslint-function do_var to check if var-statemsnts are sorted -- update jslint-function jslintAndPrint to validate sorted vars -- update jslint-function jslintAndPrint to validate 3 or more continuous-newlines - none -#### changelog 2018.12.8 -- npm publish 2018.12.8 -- upgrade to jslint commit-de413767154641f490d6e96185e525255cca5f7e (v2018.11.14) +#### changelog 2019.8.10 +- npm publish 2019.8.10 +- csslint - unminify csslint v1.0.5 +- jslint - refactor files to 80 chr column-limit +- update to jslint commit ea8401c6a72e21d66f49766af692b09e81d7a79f +- csslint - reset sorted-line with \n\n instead of macro /* validateLineSortedReset */ +- jslint-autofix - split ([aa, bb]) into multiple-lines +- jslint-autofix - sort var,let,const statements +- jslint-autofix - enforce only 1,2,4 continuous newlines +- jslint - merge function local.jslintUtility20 into local.jslintAndPrint +- jslint - simplify lint and autofix for json +- jslint - add function jslintAndPrintDir +- lint json with JSON.parse instead of jslint - none #### this package requires @@ -80,7 +88,7 @@ this zero-dependency package will provide browser-compatible versions of jslint # quickstart standalone app -#### to run this example, follow the instruction in the script below +#### to run this example, follow instruction in script below - [assets.app.js](https://kaizhu256.github.io/node-jslint-lite/build..beta..travis-ci.org/app/assets.app.js) ```shell # example.sh @@ -91,7 +99,7 @@ this zero-dependency package will provide browser-compatible versions of jslint curl -O https://kaizhu256.github.io/node-jslint-lite/build..beta..travis-ci.org/app/assets.app.js # 2. run standalone app PORT=8081 node ./assets.app.js -# 3. open a browser to http://127.0.0.1:8081 and play with the web-demo +# 3. open a browser to http://127.0.0.1:8081 and play with web-demo # 4. edit file assets.app.js to suit your needs ``` @@ -106,7 +114,7 @@ PORT=8081 node ./assets.app.js # quickstart example.js [![screenshot](https://kaizhu256.github.io/node-jslint-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-jslint-lite/build/app/assets.example.html) -#### to run this example, follow the instruction in the script below +#### to run this example, follow instruction in script below - [example.js](https://kaizhu256.github.io/node-jslint-lite/build..beta..travis-ci.org/example.js) ```javascript /* @@ -116,9 +124,9 @@ this script will run a web-demo of jslint-lite instruction 1. save this script as example.js - 2. run the shell-command: + 2. run shell-command: $ npm install jslint-lite && PORT=8081 node example.js - 3. open a browser to http://127.0.0.1:8081 and play with the web-demo + 3. open a browser to http://127.0.0.1:8081 and play with web-demo 4. edit this script to suit your needs */ @@ -167,35 +175,35 @@ instruction && window === globalThis && typeof window.XMLHttpRequest === "function" && window.document - && typeof window.document.querySelectorAll === "function" + && typeof window.document.querySelector === "function" ); // init function local.assertThrow = function (passed, message) { /* - * this function will throw the error if is falsy + * this function will throw err. if is falsy */ - var error; + var err; if (passed) { return; } - error = ( - // ternary-condition + err = ( + // ternary-operator ( message && typeof message.message === "string" && typeof message.stack === "string" ) - // if message is an error-object, then leave it as is + // if message is errObj, then leave as is ? message : new Error( typeof message === "string" - // if message is a string, then leave it as is + // if message is a string, then leave as is ? message // else JSON.stringify message : JSON.stringify(message, null, 4) ) ); - throw error; + throw err; }; local.functionOrNop = function (fnc) { /* @@ -217,6 +225,24 @@ instruction */ return; }; + local.objectAssignDefault = function (target, source) { + /* + * this function will if items from are + * null, undefined, or empty-string, + * then overwrite them with items from + */ + target = target || {}; + Object.keys(source || {}).forEach(function (key) { + if ( + target[key] === null + || target[key] === undefined + || target[key] === "" + ) { + target[key] = target[key] || source[key]; + } + }); + return target; + }; // require builtin if (!local.isBrowser) { local.assert = require("assert"); @@ -276,151 +302,132 @@ globalThis.local = local; if (!local.isBrowser) { return; } -local.testRunBrowser = function (event) { - if (!event || ( - event - && event.currentTarget - && event.currentTarget.className - && event.currentTarget.className.includes - && event.currentTarget.className.includes("onreset") - )) { - // reset output - Array.from(document.querySelectorAll( - "body > .resettable" - )).forEach(function (element) { - switch (element.tagName) { - case "INPUT": - case "TEXTAREA": - element.value = ""; - break; - default: - element.textContent = ""; - } - }); +// log stderr and stdout to #outputStdout1 +["error", "log"].forEach(function (key) { + var argList; + var elem; + var fnc; + elem = document.querySelector( + "#outputStdout1" + ); + if (!elem) { + return; } - switch (event && event.currentTarget && event.currentTarget.id) { - case "testRunButton1": - // show tests - if (document.querySelector("#testReportDiv1").style.maxHeight === "0px") { - local.uiAnimateSlideDown(document.querySelector("#testReportDiv1")); - document.querySelector("#testRunButton1").textContent = "hide internal test"; - local.modeTest = 1; - local.testRunDefault(local); - // hide tests - } else { - local.uiAnimateSlideUp(document.querySelector("#testReportDiv1")); - document.querySelector("#testRunButton1").textContent = "run internal test"; - } - break; + fnc = console[key]; + console[key] = function () { + argList = Array.from(arguments); // jslint ignore:line + fnc.apply(console, argList); + // append text to #outputStdout1 + elem.textContent += argList.map(function (arg) { + return ( + typeof arg === "string" + ? arg + : JSON.stringify(arg, null, 4) + ); + }).join(" ").replace(( + /\u001b\[\d*m/g + ), "") + "\n"; + // scroll textarea to bottom + elem.scrollTop = elem.scrollHeight; + }; +}); +Object.assign(local, globalThis.domOnEventDelegateDict); +globalThis.domOnEventDelegateDict = local; +local.onEventDomDb = ( + local.db && local.db.onEventDomDb +); +local.testRunBrowser = function (evt) { +/* + * this function will run browser-tests + */ + switch ( + !evt.ctrlKey + && !evt.metaKey + && ( + evt.modeInit + || (evt.type + "." + (evt.target && evt.target.id)) + ) + ) { // custom-case - default: - // csslint #inputTextareaCsslint1 + case "click.inputCsslint1": + case "click.inputJslint1": + case "click.jslintAutofixButton1": + case "keydown.inputCsslint1": + case "keydown.inputJslint1": + case true: + // csslint #inputCsslint1 local.jslintAndPrint( - document.querySelector("#inputTextareaCsslint1").value, - "inputTextareaCsslint1.css" + document.querySelector( + "#inputCsslint1" + ).value, + "inputCsslint1.css" ); - document.querySelector("#outputCsslintPre1").textContent = local.jslintResult.errorText - .replace(( + document.querySelector( + "#outputCsslint1" + ).textContent = local.jslintResult.errText.replace(( /\u001b\[\d*m/g ), "").trim(); - // jslint #inputTextareaEval1 - local.jslintAndPrint( - document.querySelector("#inputTextareaEval1").value, - "inputTextareaEval1.js", - { - autofix: ( - event - && event.currentTarget - && event.currentTarget.id === "jslintAutofixButton1" - ) - } - ); + // jslint #inputJslint1 + local.jslintAndPrint(document.querySelector( + "#inputJslint1" + ).value, "inputJslint1.js", { + autofix: ( + event + && event.targetOnEvent + && event.targetOnEvent.id === "jslintAutofixButton1" + ) + }); if (local.jslint.jslintResult.autofix) { - document.querySelector("#inputTextareaEval1").value = ( + document.querySelector( + "#inputJslint1" + ).value = ( local.jslint.jslintResult.code ); } - document.querySelector("#outputJslintPre1").textContent = local.jslintResult.errorText - .replace(( + document.querySelector( + "#outputJslint1" + ).textContent = local.jslintResult.errText.replace(( /\u001b\[\d*m/g ), "").trim(); - } -}; - -local.uiEventDelegate = local.uiEventDelegate || function (event) { - // filter non-input keyup-event - event.targetOnEvent = event.target.closest("[data-event]"); - if (!event.targetOnEvent) { return; - } - // rate-limit keyup - if (event.type === "keyup") { - local.uiEventDelegateKeyupEvent = event; - if (local.uiEventDelegateKeyupTimerTimeout !== 2) { - local.uiEventDelegateKeyupTimerTimeout = ( - local.uiEventDelegateKeyupTimerTimeout - || setTimeout(function () { - local.uiEventDelegateKeyupTimerTimeout = 2; - local.uiEventDelegate(local.uiEventDelegateKeyupEvent); - }, 100) - ); + // run browser-tests + default: + if ( + (evt.target && evt.target.id) !== "testRunButton1" + && !(evt.modeInit && ( + /\bmodeTest=1\b/ + ).test(location.search)) + ) { return; } - local.uiEventDelegateKeyupTimerTimeout = null; - if (!event.target.closest("input, option, select, textarea")) { + // show browser-tests + if (document.querySelector( + "#testReportDiv1" + ).style.maxHeight === "0px") { + globalThis.domOnEventDelegateDict.domOnEventResetOutput(); + local.uiAnimateSlideDown(document.querySelector( + "#testReportDiv1" + )); + document.querySelector( + "#testRunButton1" + ).textContent = "hide internal test"; + local.modeTest = 1; + local.testRunDefault(local); return; } + // hide browser-tests + local.uiAnimateSlideUp(document.querySelector( + "#testReportDiv1" + )); + document.querySelector( + "#testRunButton1" + ).textContent = "run internal test"; } - switch (event.targetOnEvent.tagName) { - case "BUTTON": - case "FORM": - event.preventDefault(); - break; - } - event.stopPropagation(); - local.uiEventListenerDict[event.targetOnEvent.dataset.event](event); }; -local.uiEventListenerDict = local.uiEventListenerDict || {}; - -local.uiEventListenerDict.testRunBrowser = local.testRunBrowser; - -// log stderr and stdout to #outputStdoutTextarea1 -["error", "log"].forEach(function (key) { - console[key + "_original"] = console[key + "_original"] || console[key]; - console[key] = function () { - var argList; - var element; - argList = Array.from(arguments); // jslint ignore:line - console[key + "_original"].apply(console, argList); - element = document.querySelector("#outputStdoutTextarea1"); - if (!element) { - return; - } - // append text to #outputStdoutTextarea1 - element.value += argList.map(function (arg) { - return ( - typeof arg === "string" - ? arg - : JSON.stringify(arg, null, 4) - ); - }).join(" ").replace(( - /\u001b\[\d*m/g - ), "") + "\n"; - // scroll textarea to bottom - element.scrollTop = element.scrollHeight; - }; -}); -// init event-handling -["Change", "Click", "Keyup", "Submit"].forEach(function (eventType) { - Array.from(document.querySelectorAll( - ".eventDelegate" + eventType - )).forEach(function (element) { - element.addEventListener(eventType.toLowerCase(), local.uiEventDelegate); - }); +local.testRunBrowser({ + modeInit: true }); -// run tests -local.testRunBrowser(); }()); @@ -471,23 +478,25 @@ local.assetsDict["/assets.index.template.html"] = '\ }\n\ /* csslint ignore:end */\n\ @keyframes uiAnimateShake {\n\ - 0%, 50% {\n\ - transform: translateX(10px);\n\ - }\n\ - 25%, 75% {\n\ - transform: translateX(-10px);\n\ - }\n\ - 100% {\n\ - transform: translateX(0);\n\ - }\n\ +0%,\n\ +50% {\n\ + transform: translateX(10px);\n\ +}\n\ +100% {\n\ + transform: translateX(0);\n\ +}\n\ +25%,\n\ +75% {\n\ + transform: translateX(-10px);\n\ +}\n\ }\n\ @keyframes uiAnimateSpin {\n\ - 0% {\n\ - transform: rotate(0deg);\n\ - }\n\ - 100% {\n\ - transform: rotate(360deg);\n\ - }\n\ +0% {\n\ + transform: rotate(0deg);\n\ +}\n\ +100% {\n\ + transform: rotate(360deg);\n\ +}\n\ }\n\ a {\n\ overflow-wrap: break-word;\n\ @@ -495,19 +504,21 @@ a {\n\ body {\n\ background: #eef;\n\ font-family: Arial, Helvetica, sans-serif;\n\ + font-size: small;\n\ margin: 0 40px;\n\ }\n\ body > div,\n\ body > form > div,\n\ body > form > input,\n\ body > form > pre,\n\ -body > form > textarea,\n\ body > form > .button,\n\ +body > form > .textarea,\n\ body > input,\n\ body > pre,\n\ -body > textarea,\n\ -body > .button {\n\ +body > .button,\n\ +body > .textarea {\n\ margin-bottom: 20px;\n\ + margin-top: 0;\n\ }\n\ body > form > input,\n\ body > form > .button,\n\ @@ -515,28 +526,24 @@ body > input,\n\ body > .button {\n\ width: 20rem;\n\ }\n\ -body > form > textarea,\n\ -body > textarea {\n\ +body > form > .textarea,\n\ +body > .textarea {\n\ height: 10rem;\n\ width: 100%;\n\ }\n\ -body > textarea[readonly] {\n\ +body > .readonly {\n\ background: #ddd;\n\ }\n\ code,\n\ pre,\n\ -textarea {\n\ +.textarea {\n\ font-family: Consolas, Menlo, monospace;\n\ - font-size: small;\n\ + font-size: smaller;\n\ }\n\ pre {\n\ overflow-wrap: break-word;\n\ white-space: pre-wrap;\n\ }\n\ -textarea {\n\ - overflow: auto;\n\ - white-space: pre;\n\ -}\n\ .button {\n\ background-color: #fff;\n\ border: 1px solid;\n\ @@ -560,6 +567,14 @@ textarea {\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\ .uiAnimateShake {\n\ animation-duration: 500ms;\n\ animation-name: uiAnimateShake;\n\ @@ -584,13 +599,13 @@ textarea {\n\
\n\ \n\ \n\ -\n\ +\n\ , + // autofix-all - recurse , code = code.replace(( /(^\/\*\u0020jslint\u0020utility2:true\u0020\*\/\\n\\\n(?:^.*?\\n\\\n)*?)(^(?:\/\/\u0020)?<\/(?:script|style)>\\n\\\n)/gm ), function (ignore, match1, match2, ii) { @@ -7495,47 +16805,70 @@ local.jslintAndPrint = function (code, file, options) { ? ". + code = code.replace(( + /(^\/\*\u0020jslint\u0020utility2:true\u0020\*\/\n(?:^.*?\n)*?)(^<\/(?:script|style)>\n)/gm + ), function (ignore, match1, match2, ii) { + return local.jslintAndPrint( + match1, + file + ( + match2.indexOf("style") >= 0 + ? ".