diff --git a/.eslintignore b/.eslintignore index f28e67b54..a82960313 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,11 +1,8 @@ -apps/animclk/V29.LBM.js -apps/banglerun/rollup.config.js -apps/schoolCalendar/fullcalendar/main.js -apps/authentiwatch/qr_packed.js -apps/qrcode/qr-scanner.umd.min.js -apps/gipy/pkg/gps.js -apps/health/chart.min.js -*.test.js -# typescript/generated files -apps/btadv/*.js +# Needs to be ignored because it uses ESM export/import +apps/gipy/pkg/gps.js +apps/gipy/pkg/gps.d.ts +apps/gipy/pkg/gps_bg.wasm.d.ts + +# Needs to be ignored because it includes broken JS +apps/health/chart.min.js diff --git a/modules/.eslintrc.json b/.eslintrc.js similarity index 58% rename from modules/.eslintrc.json rename to .eslintrc.js index d656c2555..b7590a77e 100644 --- a/modules/.eslintrc.json +++ b/.eslintrc.js @@ -1,7 +1,32 @@ -{ +const lintExemptions = require("./apps/lint_exemptions.js"); +const fs = require("fs"); +const path = require("path"); + +function findGeneratedJS(roots) { + function* listFiles(dir, allow) { + for (const f of fs.readdirSync(dir)) { + const filepath = path.join(dir, f); + const stat = fs.statSync(filepath); + + if (stat.isDirectory()) { + yield* listFiles(filepath, allow); + } else if(allow(filepath)) { + yield filepath; + } + } + } + + return roots.flatMap(root => + [...listFiles(root, f => f.endsWith(".ts"))] + .map(f => f.replace(/\.ts$/, ".js")) + ); +} + +module.exports = { "env": { // TODO: "espruino": false // TODO: "banglejs": false + // For a prototype of the above, see https://github.com/espruino/BangleApps/pull/3237 }, "extends": "eslint:recommended", "globals": { @@ -23,10 +48,8 @@ "Flash": "readonly", "Float32Array": "readonly", "Float64Array": "readonly", - "fs": "readonly", "Function": "readonly", "Graphics": "readonly", - "heatshrink": "readonly", "I2C": "readonly", "Int16Array": "readonly", "Int32Array": "readonly", @@ -46,11 +69,9 @@ "RegExp": "readonly", "Serial": "readonly", "SPI": "readonly", - "Storage": "readonly", "StorageFile": "readonly", "String": "readonly", "SyntaxError": "readonly", - "tensorflow": "readonly", "TFMicroInterpreter": "readonly", "TypeError": "readonly", "Uint16Array": "readonly", @@ -58,8 +79,10 @@ "Uint32Array": "readonly", "Uint8Array": "readonly", "Uint8ClampedArray": "readonly", + "Unistroke": "readonly", "Waveform": "readonly", // Methods and Fields at https://banglejs.com/reference + "__FILE__": "readonly", "analogRead": "readonly", "analogWrite": "readonly", "arguments": "readonly", @@ -89,6 +112,7 @@ "getSerial": "readonly", "getTime": "readonly", "global": "readonly", + "globalThis": "readonly", "HIGH": "readonly", "I2C1": "readonly", "Infinity": "readonly", @@ -129,7 +153,43 @@ "VIBRATE": "readonly", // Aliases and not defined at https://banglejs.com/reference "g": "readonly", - "WIDGETS": "readonly" + "WIDGETS": "readonly", + "module": "readonly", + "exports": "writable", + "D0": "readonly", + "D1": "readonly", + "D2": "readonly", + "D3": "readonly", + "D4": "readonly", + "D5": "readonly", + "D6": "readonly", + "D7": "readonly", + "D8": "readonly", + "D9": "readonly", + "D10": "readonly", + "D11": "readonly", + "D12": "readonly", + "D13": "readonly", + "D14": "readonly", + "D15": "readonly", + "D16": "readonly", + "D17": "readonly", + "D18": "readonly", + "D19": "readonly", + "D20": "readonly", + "D21": "readonly", + "D22": "readonly", + "D23": "readonly", + "D24": "readonly", + "D25": "readonly", + "D26": "readonly", + "D27": "readonly", + "D28": "readonly", + "D29": "readonly", + "D30": "readonly", + "D31": "readonly", + + "bleServiceOptions": "writable", // available in boot.js code that's called ad part of bootupdate }, "parserOptions": { "ecmaVersion": 11 @@ -142,22 +202,49 @@ "SwitchCase": 1 } ], - "no-case-declarations": "off", "no-constant-condition": "off", "no-delete-var": "off", - "no-empty": "off", + "no-empty": ["warn", { "allowEmptyCatch": true }], "no-global-assign": "off", "no-inner-declarations": "off", - "no-octal": "off", "no-prototype-builtins": "off", "no-redeclare": "off", "no-unreachable": "warn", "no-cond-assign": "warn", "no-useless-catch": "warn", - // TODO: "no-undef": "warn", - "no-undef": "off", - "no-unused-vars": "off", + "no-undef": "warn", + "no-unused-vars": ["warn", { "args": "none" } ], "no-useless-escape": "off", "no-control-regex" : "off" - } + }, + overrides: [ + { + files: ["*.ts"], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + rules: { + "no-delete-var": "off", + "no-empty": ["error", { "allowEmptyCatch": true }], + "no-prototype-builtins": "off", + "prefer-const": "off", + "prefer-rest-params": "off", + "no-control-regex" : "off", + "@typescript-eslint/no-delete-var": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-this-alias": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-var-requires": "off", + } + }, + ...Object.entries(lintExemptions).map(([filePath, {rules}]) => ({ + files: [filePath], + rules: Object.fromEntries(rules.map(rule => [rule, "off"])), + })), + ], + ignorePatterns: findGeneratedJS(["apps/", "modules/"]), + reportUnusedDisableDirectives: true, } diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 7c0cfca3a..bebe18748 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -11,10 +11,10 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive - - name: Use Node.js 16.x + - name: Use Node.js 18.x uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 18.x - name: Install testing dependencies run: npm ci - name: Test all apps and widgets diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..69aa0ab3d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +Contributing to BangleApps +========================== + +https://github.com/espruino/BangleApps?tab=readme-ov-file#getting-started +has some links to tutorials on developing for Bangle.js. + +Please check out the Wiki to get an idea what sort of things +we'd like to see for contributed apps: https://github.com/espruino/BangleApps/wiki/App-Contribution + diff --git a/README.md b/README.md index 066ad0ad0..d595c7df1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Bangle.js App Loader (and Apps) * Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps) * Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/) +The release version is manually refreshed with regular intervals while the development version is continuously updated as new code is committed to this repository. + **All software (including apps) in this repository is MIT Licensed - see [LICENSE](LICENSE)** By submitting code to this repository you confirm that you are happy with it being MIT licensed, and that it is not licensed in another way that would make this impossible. @@ -251,7 +253,7 @@ and which gives information about the app for the Launcher. "description": "...", // long description (can contain markdown) "icon": "icon.png", // icon in apps/ "screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app - "type":"...", // optional(if app) - + "type":"...", // optional(if app) - // 'app' - an application // 'clock' - a clock - required for clocks to automatically start // 'widget' - a widget @@ -278,6 +280,7 @@ and which gives information about the app for the Launcher. // 'bluetooth' - uses Bluetooth LE // 'system' - used by the system // 'clkinfo' - provides or uses clock_info module for data on your clock face or clocks that support it (see apps/clock_info/README.md) + // 'health' - e.g. heart rate monitors or step counting "supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS or BANGLEJS2 "dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above) "dependencies" : { "messages":"app" } // optional, depend on a specific app ID @@ -299,7 +302,7 @@ and which gives information about the app for the Launcher. "customConnect": true, // if supplied, ensure we are connected to a device // before the "custom.html" iframe is loaded. An // onInit function in "custom.html" is then called - // with info on the currently connected device. + // with info on the currently connected device. "interface": "interface.html", // if supplied, apps/interface.html is loaded in an // iframe, and it may interact with the connected Bangle @@ -327,9 +330,9 @@ and which gives information about the app for the Launcher. {"name":"appid.data.json", // filename used in storage "storageFile":true // if supplied, file is treated as storageFile "url":"", // if supplied URL of file to load (currently relative to apps/) - "content":"...", // if supplied, this content is loaded directly + "content":"...", // if supplied, this content is loaded directly "evaluate":true, // if supplied, data isn't quoted into a String before upload - // (eg it's evaluated as JS) + // (eg it's evaluated as JS) }, {"wildcard":"appid.data.*" // wildcard of filenames used in storage }, // this is mutually exclusive with using "name" @@ -402,7 +405,7 @@ in an iframe.
- +