Create lint exemptions

pull/3213/head
Anton 2024-03-04 21:34:17 +01:00
parent 825a8b8290
commit 29cfd17778
5 changed files with 705 additions and 4 deletions

View File

@ -1,9 +1,14 @@
{
const lintExemptions = require("./lint_exemptions.js");
module.exports = {
"env": {
// TODO: "espruino": false
// TODO: "banglejs": false
},
"extends": "eslint:recommended",
"plugins": [
],
"globals": {
// Methods and Fields at https://banglejs.com/reference
"Array": "readonly",
@ -157,5 +162,11 @@
"no-unused-vars": ["warn", { "args": "none" } ],
"no-useless-escape": "off",
"no-control-regex" : "off"
}
},
overrides: [
...Object.entries(lintExemptions).map(([filePath, {rules}]) => ({
files: [filePath],
rules: Object.fromEntries(rules.map(rule => [rule, "off"])),
})),
],
}

578
apps/lint_exemptions.js Normal file
View File

@ -0,0 +1,578 @@
module.exports = {
"xxlmessage/app.js": {
"hash": "14a9ea1fbcf0e1053df1a77d7ee7acd543f1ed63e26ecf220468c3813f93de4f",
"rules": [
"no-unused-vars"
]
},
"waternet/app.js": {
"hash": "a03fbfc731811f8cc9ea8c96781ff7fe46532c5f9e628b317892069e43b97012",
"rules": [
"no-unused-vars"
]
},
"tabata/tabata.js": {
"hash": "bba68fd35b57afe00adb054d598d8bde74cf2c9cb1d1d7ae27c17e13899383ee",
"rules": [
"no-unused-vars"
]
},
"speedalt2/GPS Adv Sports II.js": {
"hash": "1b0dff5c4ebe0c8042dec9456f809ebaada379357188b8a18dc17a144ee8c071",
"rules": [
"no-unused-vars"
]
},
"timerclk/alarm.alert.js": {
"hash": "2d422f738c044dcf0d6a43712c672dccc0ad4f2d0a877ee11f0a96ec3d939714",
"rules": [
"no-unused-vars"
]
},
"spacew/prep/split.js": {
"hash": "06f25475a3765417e42dc7638ee500b546d3ce75ac28b39b8b836c0aee2ea2cb",
"rules": [
"no-unused-vars"
]
},
"spacew/bench.js": {
"hash": "428a741d8dfba625cb1d2958dab3b6c7b0ca05f582b00e06ec9f7f495658beb6",
"rules": [
"no-unused-vars"
]
},
"spacew/app.js": {
"hash": "6467f44962655f10a649226a2ea180a3662308146d2f19d15903a16ae081d282",
"rules": [
"no-unused-vars"
]
},
"smclock/app.js": {
"hash": "8f338b03c24738793d62b7a2c4d0c2b41ab0538394adc6f3d977776b68ee3287",
"rules": [
"no-unused-vars"
]
},
"sleepphasealarm/app.js": {
"hash": "d234fea5d711ae5a149879e53b28bc9002860c6b6a75285e1fc04093fe400a88",
"rules": [
"no-unused-vars"
]
},
"sleeplog/settings.js": {
"hash": "bd5e3e1382321df6682ef1cb718b0e15ab355422bef77278eb086f213f643021",
"rules": [
"no-unused-vars"
]
},
"showimg/app.js": {
"hash": "98975f1cdec81d982d1ee8ca23e38f6851c12c2ccf57c4503c449d20f747e9cf",
"rules": [
"no-unused-vars"
]
},
"sensible/sensible.js": {
"hash": "4b8b4417c352d7a86f39653c812356c36e56770e44051d645c522f986e98abfe",
"rules": [
"no-unused-vars"
]
},
"rpnsci/app.js": {
"hash": "e5bf536babe9b30e7f40b03826d7052aec98f806a7ab3b80b10438e9bf2cc61b",
"rules": [
"no-unused-vars"
]
},
"recorder/app.js": {
"hash": "64c838b63a756a712555ae54d0bae89f0373ce317c289f7b19a583d3f91d473b",
"rules": [
"no-unused-vars"
]
},
"podadrem/app.js": {
"hash": "8d02fced6187142b7943c63ddefd874700d0a8731d49121de06d211959db58c4",
"rules": [
"no-unused-vars"
]
},
"openstmap/imagefilter.js": {
"hash": "8a71e9a0ecfa94150379c41a5a915cfdac39f8b2e485e5cc1ce4629e85293acd",
"rules": [
"no-unused-vars"
]
},
"mtnclock/app.js": {
"hash": "c48e3ed1a605e6a131e5947718e26cc9481c6eeab36c5670bb74f0c58cb96cd8",
"rules": [
"no-unused-vars"
]
},
"mmonday/manic-monday.js": {
"hash": "2eff38d4d1cde2b9f17800a554da58300a6250de3e014995a6958a11bcb5b76a",
"rules": [
"no-unused-vars"
]
},
"mixdiganclock/mixdiganclock.app.js": {
"hash": "97beda5daa3b6e60f27dcad1230dd49a9c3b3e81cf55d644496c797fbef1256f",
"rules": [
"no-unused-vars"
]
},
"locale/locales.js": {
"hash": "c89110fccb76824508aab0ed8374eb06843a9173262fd3a93cf3a67b098ee5a3",
"rules": [
"no-unused-vars"
]
},
"legoremote/app.js": {
"hash": "af12068cd1b84369aa8416a82e9335298f6a4b69b56aa7bc70b9eb0d91e6d78e",
"rules": [
"no-unused-vars"
]
},
"lightswitch/settings.js": {
"hash": "340a664f7f82fbffcb108bfceded78dbbe162717d4e4dda0f08452ca79fa3881",
"rules": [
"no-unused-vars"
]
},
"kbtouch/lib.js": {
"hash": "725b41b792a0f9879257e61cc5c2ba30df777c21137bd2d2e324ab83be7b8012",
"rules": [
"no-unused-vars"
]
},
"jbm8b_IT/app.js": {
"hash": "52df7b9bcac876176d66cbb65f7015cb0b5933ba585a6e3f6b93766bbe19fb11",
"rules": [
"no-unused-vars"
]
},
"jbm8b/app.js": {
"hash": "7ec45c7d21590f8fa9167f4953fa6da40190d0f287f436dac7a2c4b62d82d429",
"rules": [
"no-unused-vars"
]
},
"wristlight/app.js": {
"hash": "422b63acf557ac81beb0f0d08d85a8988a004752e74b50a08e23c97293351d0b",
"rules": [
"no-unused-vars"
]
},
"widhrm/widget.js": {
"hash": "9c5fd63130c579a015f5600862ec1a5032387a41e058950c739c9875bc81c6bf",
"rules": [
"no-unused-vars"
]
},
"waypoints/waypoints.app.js": {
"hash": "ff02639eb8f81784c942093d57d254295e2c78918adb4bcffdf0d0284ebfac55",
"rules": [
"no-unused-vars"
]
},
"timerclk/timer.js": {
"hash": "f44702e61ad833b53fb28d222ff0fbd77d0307c6eb69c5ecebb8626a1cb20ed4",
"rules": [
"no-unused-vars"
]
},
"timerclk/stopwatch.js": {
"hash": "92838c03923e162c0e9ab4f37ca0220a1fee61ed16cba1d38c79d7bdd0e18765",
"rules": [
"no-unused-vars"
]
},
"timerclk/alarm.js": {
"hash": "6f521742cc6a6d6f215e499084a596ffc3494d1e1f950b204368c051a18773de",
"rules": [
"no-unused-vars"
]
},
"speedalt2/app.js": {
"hash": "8cf396b8c2b3a449635274d537ac12dda88a829fd32336e6c2c0cd85e7f56b2d",
"rules": [
"no-unused-vars"
]
},
"skyspy/skyspy.app.js": {
"hash": "49a727a4c052e8c6322a502750ca036b0d58896f476b1cffebe9c53e426c8bcc",
"rules": [
"no-unused-vars"
]
},
"sixths/sixths.app.js": {
"hash": "b4572a529b31a5edcbd6ca5843b91c54abcbf75b2a28e90bd33751f7d6a3ebcd",
"rules": [
"no-unused-vars"
]
},
"scribble/app.js": {
"hash": "6d13abd27bab8009a6bdabe1df2df394bc14aac20c68f67e8f8b085fa6b427cd",
"rules": [
"no-unused-vars"
]
},
"promenu/boot.js": {
"hash": "8b0ed99c61c877348365d9ec50f904744f4694c5e2c0a93200e998955034ed0f",
"rules": [
"no-unused-vars"
]
},
"promenu/bootb2.js": {
"hash": "558ab22819c2bc83ebf97c960c4fa494fccccbc894f1582f28972dbf8f563170",
"rules": [
"no-unused-vars"
]
},
"poweroff/settings.js": {
"hash": "c197afe72c612a4b3825a3a12a628d0f4ed83823da3f28885bbf473037a02506",
"rules": [
"no-unused-vars"
]
},
"oxofocus/app.js": {
"hash": "cd29309373974ef038725e26d8a2ff2634a437c89cdffc3b12defd65a948db75",
"rules": [
"no-unused-vars"
]
},
"orloj/app.js": {
"hash": "9240830a097ba545fdbdb06305817234e30238bf2fbb8fbb47af83897cd588a9",
"rules": [
"no-unused-vars"
]
},
"nixie/m_vatch.js": {
"hash": "36a7fa956f99d5815cd6bac570d2b86833d1d37474d7eef0bb21892bdf6723a8",
"rules": [
"no-unused-vars"
]
},
"moonphase/app.js": {
"hash": "0e78c9ec9e7ab04f22f1838fcf033691024c8734de23f8d8dd51d6f11c30a2f2",
"rules": [
"no-unused-vars"
]
},
"lcars/lcars.app.js": {
"hash": "a305f73ee4e77b6534746ea79e699f700fd3db305f2b0289ef081d8869e1faf1",
"rules": [
"no-unused-vars"
]
},
"widagps/widget.js": {
"hash": "a58cdc481962575ef0aa0bfaedcc1f9de3ce966218c5b16168d8ed8b4b9672b8",
"rules": [
"no-unused-vars"
]
},
"sleeplog/lib.js": {
"hash": "5210446eb507f62897782735e7d56a2e84ea5f9dba557ef67a977f53065f3461",
"rules": [
"no-unused-vars"
]
},
"sleeplog/app.js": {
"hash": "336da552e4b04677447cf76a253b40bc259a597ea11d455121933f93afe99794",
"rules": [
"no-unused-vars"
]
},
"recorder/widget.js": {
"hash": "b48698bab90fc4f38f880b6663eda108b8d31ce929ba1d32d815aa4ec8c74bca",
"rules": [
"no-unused-vars"
]
},
"qmsched/app.js": {
"hash": "4b7dbabed6c252021531d6b0449c16a3adc2e405f2ddda33ca0a65f5fa42c663",
"rules": [
"no-unused-vars"
]
},
"runplus/app.js": {
"hash": "fabec449552d17ceb5d510aa058e420757f3caba11999efbe6ebf2ac1a81eb32",
"rules": [
"no-unused-vars"
]
},
"powermanager/boot.js": {
"hash": "662d9d29a80a4f2c2855097b4073a099604f4f6d444c13a33304346c788cc5cb",
"rules": [
"no-unused-vars"
]
},
"ohmcalc/app.js": {
"hash": "ac1ac172c9c4f9c4faf9bc2ce8ad7dd51ecd9a5c38f28a107d39f0c86a836899",
"rules": [
"no-unused-vars"
]
},
"nightwatch/nightwatch.app.js": {
"hash": "9366528bc5bb229cd08e68000a4deebaa27036a18ac5215dce7c38ffa9d69b31",
"rules": [
"no-unused-vars"
]
},
"messagelist/app.js": {
"hash": "a76b5cc3ddcdbd60a9979972f274a717a901ea65e108545b13d415740a71b4e0",
"rules": [
"no-unused-vars"
]
},
"imageclock/app.js": {
"hash": "198ad2d18b41ba30e3ee2545372c84ba443cec7d6f9ce4e9ca494defc9476a8e",
"rules": [
"no-unused-vars"
]
},
"hworldclock/app.js": {
"hash": "e4c34cc76e242541156006952a2cf05427feb0fbe70ae55a1afb9bd0e1d5fae1",
"rules": [
"no-unused-vars"
]
},
"homework/app.js": {
"hash": "f367470e50c9105ee1f48c3193c605293338c35bd1ae1488467404270a20747c",
"rules": [
"no-unused-vars"
]
},
"hidmsicswipe/hidmsicswipe.js": {
"hash": "8c8c2058315c6debaec71b65b1409ebf5725233c72dbcc2ac921bec0d238fd4f",
"rules": [
"no-unused-vars"
]
},
"hidcam/app.js": {
"hash": "29a9e8add8daa19ec5c12bf6b372fe4e59c2b07be30e305e9d75dac55995dffd",
"rules": [
"no-unused-vars"
]
},
"gpstrek/app.js": {
"hash": "9c941a29790dd642579478bf4b19a2afd5473a782d07fafd2c8113b2d7392bec",
"rules": [
"no-unused-vars"
]
},
"gpstouch/gpstouch.app.js": {
"hash": "3492b202c866d6d619298456a48dd8501c4b34f5cce84698e92c62acaf3b8ce4",
"rules": [
"no-unused-vars"
]
},
"gpspoilog/app.js": {
"hash": "38c72e56a4929102f5748ed5e5415a63a31bfd3bf6d853527a10d71a268bc294",
"rules": [
"no-unused-vars"
]
},
"gpsrec/app.js": {
"hash": "4d842608edea63975c7da9db6be2746af8849ea2dcc9135fc7d77e5806701f60",
"rules": [
"no-unused-vars"
]
},
"golfview/maptools.js": {
"hash": "2c3bdb849dce1710863693f2370f7fb32070a7c3bec915d8d2ef8106fda937d8",
"rules": [
"no-unused-vars"
]
},
"golfview/golfview.js": {
"hash": "76d8a7743122a63bd82c8e95c234229261829b465a4bc998cdc9e45860ec7fc4",
"rules": [
"no-unused-vars"
]
},
"golfscore/app.js": {
"hash": "2df86a3f159b72229011f6276ef023c764837b39552b852f1caa431450a06f3a",
"rules": [
"no-unused-vars"
]
},
"geissclk/clock.js": {
"hash": "2e547f904f17995668e351a9031bc4701ddaff71cba4f88ca28d51a4dd4f6154",
"rules": [
"no-unused-vars"
]
},
"gbmusic/app.js": {
"hash": "c36e9ccf6813264000732cf8aae5b16bb63afafb3bdc81f6f98d670c008b3416",
"rules": [
"no-unused-vars"
]
},
"gbdebug/app.js": {
"hash": "5372409dd09a1b165b009afc7dc9af24d4be223ffc6507a3f6e1cbb694a3eeec",
"rules": [
"no-unused-vars"
]
},
"gallery/app.js": {
"hash": "81b119cc61c9d1ae76071f7ef05580f4f93ac60bf97fbdfe418463817e045402",
"rules": [
"no-unused-vars"
]
},
"football/app.js": {
"hash": "a24c293073557c259cffbf4f1340b6c9111832b7230b3e36072f32335ce4047b",
"rules": [
"no-unused-vars"
]
},
"fd6fdetect/app.js": {
"hash": "5fb67c4e3699ed30815b6e316ce669f1b8479aa312bf4d8348e30cdf0a29409f",
"rules": [
"no-unused-vars"
]
},
"dotmatrixclock/app.js": {
"hash": "ad51c66072f5dd799826e1b3b69c6febb6ab93c93b2eb2245b4035c36b496063",
"rules": [
"no-unused-vars"
]
},
"dragboard/lib.js": {
"hash": "e3cbf03054134e9b8e570f02ea5cf0d3e784c8f47d68c85eaa2c5b005291b668",
"rules": [
"no-unused-vars"
]
},
"doztime/app-bangle2.js": {
"hash": "81592fe1bc6537f80d7dbe7b2e8145924d2400f98ea780c68dd5bd9694376c8a",
"rules": [
"no-unused-vars"
]
},
"doztime/app-bangle1.js": {
"hash": "ba263e0ff80cb0ab1df9fe19e62a4972bb8646ea709af8bc41206e9a6395d539",
"rules": [
"no-unused-vars"
]
},
"devstopwatch/app.js": {
"hash": "bb75693cc6ed2ffef0d227a40b53079e06d1ebcc7c2d9eed671b5401a8e06433",
"rules": [
"no-unused-vars"
]
},
"coretemp/settings.js": {
"hash": "b5aa84ecab587b444028b4491b2c2e8f3b2303aa33745265ab629c6ff5141cfb",
"rules": [
"no-unused-vars"
]
},
"coretemp/boot.js": {
"hash": "02ee3a04d7dc485367394a91e570affd712ab919bbb155bdafc00e534d3bcd4a",
"rules": [
"no-unused-vars"
]
},
"configurable_clock/app.js": {
"hash": "09d1d02b63abc4ecea5e00a7372de7150ccfe67c2502ff5d298ec7b8e29c353b",
"rules": [
"no-unused-vars"
]
},
"chronowid/widget.js": {
"hash": "1056a77f508708a6667e7ae3d201add1d747a1ea4eeab2b3b8afce24a97f1e65",
"rules": [
"no-unused-vars"
]
},
"cliclockJS2Enhanced/app.js": {
"hash": "056b4eeddbabbcf0f94db83d188e55dc0008bccb3913af76e31236e74a780aa1",
"rules": [
"no-unused-vars"
]
},
"chrono/chrono.js": {
"hash": "62a4fe13d32528af5107e8008197510115134d749851f8ecfe062c7190b4fc5e",
"rules": [
"no-unused-vars"
]
},
"chess/engine.js": {
"hash": "5ab335ed76f39b69e5b3436325229d8b3bd2c20a5da6d32bcb7c194f79c9f89c",
"rules": [
"no-unused-vars"
]
},
"carcrazy/app.js": {
"hash": "82b9b291ad6e2efcb689e5bb7f48a5ba1a3b59f740a5ab4634006f3d413cee7e",
"rules": [
"no-unused-vars"
]
},
"animclk/create_images.js": {
"hash": "05a222b265effe46b2c7a9cc0822c6c4cd86335d5d3a0cecce5fdd15d5e09b9f",
"rules": [
"no-unused-vars"
]
},
"calculator/app.js": {
"hash": "fcb7c7b6c4ec5ce0f425d2a690baab8da235a12e685fe2680cbd4cf2cfdef0b0",
"rules": [
"no-unused-vars"
]
},
"bthrm/bthrm.js": {
"hash": "0ad6b3150200efa959096d239a136865fc0ee1df0c50ebaae76693c07df7a02e",
"rules": [
"no-unused-vars"
]
},
"bowserWF/app.js": {
"hash": "ec1e9fabf53e16dc91b19d305616ae0dfd879c32c3b1116da97928ebb8d2eb56",
"rules": [
"no-unused-vars"
]
},
"blackjack/blackjack.app.js": {
"hash": "392382463c90b11b31818e974c3198ed4ec4ed681c9668b4a39865ab055d68ad",
"rules": [
"no-unused-vars"
]
},
"blackjack/appb2.js": {
"hash": "d2e866a297811dfec4cba834c996e1e085421820673a038a9a9e9d438e4fabf9",
"rules": [
"no-unused-vars"
]
},
"astral/app.js": {
"hash": "a210269f0d57eb496a3b75cfc4550676f9d7e85a697c77e18f41bf02549179e3",
"rules": [
"no-unused-vars"
]
},
"ballmaze/app.js": {
"hash": "cd493a329063e879f44ead211cbecb4ca25da2d5c40b65ba05d6e9b2b91ff8a5",
"rules": [
"no-unused-vars"
]
},
"astrocalc/astrocalc-app.js": {
"hash": "00e29864b2bfeec7f11bd8509181b26f7916c25dd4a777abfe1fbd555822983d",
"rules": [
"no-unused-vars"
]
},
"a_dndtoggle/a_dndtoggle.app.js": {
"hash": "8ba115261df8ddb14bf7d16a300a114a1e59ace4da478657968390b62ba53cbb",
"rules": [
"no-unused-vars"
]
},
"HRV/app.js": {
"hash": "265b4b0272c43126670b5026079711354cee0a211096bcd2c18139b7a3ee774a",
"rules": [
"no-unused-vars"
]
}
};

66
bin/exempt-lint.mjs Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env node
/**
* @file
* You can use this script to exempt an app file from a specific eslint rule.
*
* This should only be used to exempt existing apps when a new lint rule is added.
* You are not allowed to exempt your new app from existing lint rules.
*
* Run it like this:
* node bin/exempt-lint.mjs LINTRULE FILEPATH
*
* Example command:
* node bin/exempt-lint.mjs no-unused-vars ./apps/_example_app/app.js
*/
import fs from "node:fs/promises";
const lintRule = process.argv[2];
if(!lintRule){
throw new Error("First argument needs to be a lint rule, something like 'no-unused-vars'");
}
const filePathInput = process.argv[3];
const filePathMatch = filePathInput?.match(/^(?:.*?\/apps\/|apps\/|\/)?(?<path>.*\.js)$/ui);
const filePath = filePathMatch?.groups?.path;
if(!filePath){
throw new Error("Second argument needs to be a file path that looks something like './apps/_example_app/app.js'");
}
const exemptionsFilePath = "../apps/lint_exemptions.js";
const exemptions = (await import(exemptionsFilePath)).default;
const fileContents = await fs.readFile("apps/" + filePath, "utf8");
const exemption = exemptions[filePath] || {};
exemption.hash = await hashContents(fileContents);
const rules = new Set(exemption.rules || []);
rules.add(lintRule);
exemption.rules = [...rules];
exemptions[filePath] = exemption;
const output = `module.exports = ${JSON.stringify(exemptions, undefined, 2)};\n`;
await fs.writeFile("bin/" + exemptionsFilePath, output);
console.log(`✔️ '${filePath}' is now exempt from the rule '${lintRule}'`);
/**
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
*/
async function hashContents(message) {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join(""); // convert bytes to hex string
return hashHex;
}

46
bin/sync-lint-exemptions.mjs Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env node
/**
* @file
* Run this to ensure that the lint exemptions are all valid.
* If any of the exempt app files have been changed, this script will remove the exemption for that file.
*
* Run it like this:
* node bin/sync-lint-exemptions.mjs
*/
import fs from "node:fs/promises";
const exemptionsFilePath = "../apps/lint_exemptions.js";
const exemptions = (await import(exemptionsFilePath)).default;
for(const filePath of Object.keys(exemptions)){
const fileContents = await fs.readFile("apps/" + filePath, "utf8");
const currentHash = await hashContents(fileContents);
if(exemptions[filePath].hash !== currentHash){
delete exemptions[filePath];
console.log(`! Removed lint exemptions for '${filePath}' because it has been edited`);
}
}
const output = `module.exports = ${JSON.stringify(exemptions, undefined, 2)};\n`;
await fs.writeFile("bin/" + exemptionsFilePath, output);
console.log(`✔️ Synchronized all lint exemptions\n`);
/**
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
*/
async function hashContents(message) {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join(""); // convert bytes to hex string
return hashHex;
}

View File

@ -13,8 +13,8 @@
"npm-watch": "^0.11.0"
},
"scripts": {
"lint-apps": "eslint ./apps --ext .js",
"test": "node bin/sanitycheck.js && eslint ./apps --ext .js && eslint ./modules --ext .js",
"lint-apps": "node bin/sync-lint-exemptions.mjs && eslint ./apps --ext .js",
"test": "node bin/sanitycheck.js && npm run lint-apps && eslint ./modules --ext .js",
"update-local-apps": "./bin/create_apps_json.sh apps.local.json",
"local": "npm-watch & npx http-server -a localhost -c-1",
"start": "npx http-server -c-1"