Merge pull request #5432 from tridactyl/per_platform_lint_rules
Some checks failed
lint / lint (unit) (push) Has been cancelled
e2e / test (firefox, ubuntu) (push) Has been cancelled
e2e / test (firefox, windows) (push) Has been cancelled
e2e / test (firefoxesr, ubuntu) (push) Has been cancelled
lint / lint (lint) (push) Has been cancelled
lint / lint (mozilla) (push) Has been cancelled
Website / build (push) Has been cancelled
Website / deploy (push) Has been cancelled

Per platform lint rules
This commit is contained in:
Oliver Blanthorn 2026-07-11 17:07:16 +00:00 committed by GitHub
commit ac017eb7f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 172 additions and 68 deletions

View file

@ -1,3 +1,5 @@
const browserTargets = require("./browser-targets.json")
/*
👋 Hi! This file was autogenerated by tslint-to-eslint-config.
https://github.com/typescript-eslint/tslint-to-eslint-config
@ -34,7 +36,14 @@ module.exports = {
"sonarjs"
],
"rules": {
"unsupported-apis": "warn",
"unsupported-apis-chrome": [
"warn",
{ "minimumVersion": browserTargets.chrome.minimumVersion }
],
"unsupported-apis-firefox": [
"warn",
{ "minimumVersion": browserTargets.firefox.minimumVersion }
],
"sonarjs/cognitive-complexity": "off", //"error",
"sonarjs/no-duplicate-string": "off",
"sonarjs/no-unused-collection": "off", //"error", // There seems to be a bug with this rule - exported collections are assumed unused

13
browser-targets.json Normal file
View file

@ -0,0 +1,13 @@
{
"firefox": {
"minimumVersion": "91.1.0",
"manifestVersionPath": [
"applications",
"gecko",
"strict_min_version"
]
},
"chrome": {
"minimumVersion": "114"
}
}

View file

@ -0,0 +1,90 @@
const bcd = require("@mdn/browser-compat-data")
const api = bcd.webextensions.api
function propertyNameOrValue(n) {
return n.property.type == "Literal" ? n.property.value : n.property.name
}
function isVersionNewer(version, minimumVersion) {
const versionParts = String(version).split(".").map(Number)
const minimumParts = String(minimumVersion).split(".").map(Number)
if (versionParts.some(Number.isNaN) || minimumParts.some(Number.isNaN))
return false
const length = Math.max(versionParts.length, minimumParts.length)
for (let i = 0; i < length; i++) {
const difference = (versionParts[i] || 0) - (minimumParts[i] || 0)
if (difference !== 0) return difference > 0
}
return false
}
function detectBrowserUsage(context, node, browser, minimumVersion) {
let localApi = api
const fullName = []
while (
node.type == "MemberExpression" &&
propertyNameOrValue(node) in localApi
) {
const n = node
node = node.parent
const name = propertyNameOrValue(n)
fullName.push(name)
localApi = localApi[name]
if (!localApi.__compat) {
continue
}
const support = localApi.__compat.support
if (support[browser].version_added === false) {
context.report({
node: n,
messageId: "unsupportedApis",
data: {
name: browser,
api: fullName.join("."),
},
})
} else {
const version = support[browser].version_added
if (
minimumVersion !== undefined &&
isVersionNewer(version, minimumVersion)
) {
context.report({
node: n,
messageId: "apiTooRecent",
data: {
api: fullName.join("."),
name: browser,
version: minimumVersion,
},
})
}
}
}
}
module.exports = browser => ({
meta: {
schema: [
{
type: "object",
properties: { minimumVersion: { type: "string" } },
additionalProperties: false,
},
],
messages: {
unsupportedApis: "{{ api }} unsupported on '{{ name }}'",
apiTooRecent:
"{{ api }} is not supported on {{ name }} {{ version }}",
},
},
create(context) {
const { minimumVersion } = context.options[0] || {}
const detect = node =>
detectBrowserUsage(context, node, browser, minimumVersion)
return {
'MemberExpression[object.name="browser"]': detect,
'MemberExpression[object.name="browserBg"]': detect,
}
},
})

View file

@ -0,0 +1,24 @@
# adding a new target
make a file here like
```js
// unsupported-apis-mosaic.js
module.exports = require("./lib/unsupported-apis")("mosaic")
```
add a line to `../browser-targets.json`
```json
"mosaic": { "minimumVersion": "1.0.0" }
```
add a few lines to `../.eslintrc.js`
```js
"unsupported-apis-mosaic": [
"warn",
{ "minimumVersion": browserTargets.mosaic.minimumVersion }
],
```

View file

@ -0,0 +1 @@
module.exports = require("./lib/unsupported-apis")("chrome")

View file

@ -0,0 +1 @@
module.exports = require("./lib/unsupported-apis")("firefox")

View file

@ -1,63 +0,0 @@
const bcd = require('@mdn/browser-compat-data');
const api = bcd.webextensions.api;
const supported_browsers = ["firefox", "chrome"];
const minimalSupportedFirefoxVersion = 114;
function propertyNameOrValue(n) {
return (n.property.type == "Literal" ? n.property.value : n.property.name)
}
function detectBrowserUsage(context, node) {
let localApi = api;
let fullName = [];
while (node.type == "MemberExpression" && propertyNameOrValue(node) in localApi) {
const n = node;
node = node.parent;
let name = propertyNameOrValue(n);
fullName.push(name);
localApi = localApi[name];
if (!localApi.__compat) {
continue;
}
let support = localApi.__compat.support;
for (let browser of supported_browsers) {
if (support[browser].version_added === false) {
context.report({
node: n,
messageId: "unsupportedApis",
data: {
name: browser,
api: fullName.join("."),
}
});
} else {
const version = Number(support[browser].version_added);
if (!isNaN(version) && version > minimalSupportedFirefoxVersion) {
context.report({
node: n,
messageId: "apiTooRecent",
data: {
api: fullName.join("."),
version: minimalSupportedFirefoxVersion
}
});
}
}
}
}
}
module.exports = {
meta: {
messages: {
unsupportedApis: "{{ api }} unsupported on '{{ name }}'",
apiTooRecent: "{{ api }} is not supported on firefox {{ version }}",
}
},
create(context) {
return {
'MemberExpression[object.name="browser"]': (n) => detectBrowserUsage(context, n),
'MemberExpression[object.name="browserBg"]': (n) => detectBrowserUsage(context, n),
};
}
};

View file

@ -85,7 +85,7 @@ rmdir buildtemp
# Copy extra static files across
cp src/manifest.json build/
node scripts/generate_manifest.js firefox src/manifest.json build/manifest.json
cp -r src/static build
cp -r generated/static build
cp issue_template.md build/

View file

@ -0,0 +1,30 @@
#!/usr/bin/env node
const fs = require("fs")
const targets = require("../browser-targets.json")
function generateManifest(template, targetName) {
const target = targets[targetName]
if (!target || !target.manifestVersionPath) {
throw new Error(`No manifest settings for ${targetName}`)
}
const manifest = JSON.parse(JSON.stringify(template))
const path = target.manifestVersionPath.slice()
const property = path.pop()
const parent = path.reduce((value, name) => {
if (!value[name]) value[name] = {}
return value[name]
}, manifest)
parent[property] = target.minimumVersion
return manifest
}
if (require.main === module) {
const [targetName, source, destination] = process.argv.slice(2)
const template = JSON.parse(fs.readFileSync(source, "utf8"))
const manifest = generateManifest(template, targetName)
fs.writeFileSync(destination, JSON.stringify(manifest, null, 4) + "\n")
}
module.exports = generateManifest

View file

@ -139,8 +139,7 @@
],
"applications": {
"gecko": {
"id": "tridactyl.vim.betas@cmcaine.co.uk",
"strict_min_version": "91.1.0"
"id": "tridactyl.vim.betas@cmcaine.co.uk"
}
},
"options_ui": {
@ -161,4 +160,4 @@
"omnibox": {
"keyword": "tri"
}
}
}