initial commit of actions

This commit is contained in:
Dominik Polakovics Polakovics 2026-01-31 18:56:04 +01:00
commit 949ece5785
44660 changed files with 12034344 additions and 0 deletions

View file

@ -0,0 +1,288 @@
'use strict';
const path = require("path");
const browserslist = require("browserslist");
const { version, homepage } = require("../package.json");
const flatTypeScriptConfigs = {};
const createRule = (name, browserstring, description, { ts = null } = {}) => {
const rule = require(`./rules/${name}`);
module.exports.rules[name] = {
meta: Object.assign(
{
type: "problem",
docs: {
description,
recommended: true,
url: `${homepage}/blob/v${version}/docs/${name}.md`,
},
fixable: false,
schema: [],
deprecated: false,
replacedBy: null,
},
rule.meta || {}
),
create(context) {
let browsers = browserslist(browserstring);
const config = browserslist.findConfig(path.dirname(
context.filename ?? context.getFilename()
)) || {
defaults: "defaults",
};
const desiredBrowsers = browserslist(config.defaults);
const badBrowsers = desiredBrowsers
.filter((browser) => browsers.includes(browser))
.join(", ");
if (globalThis.ESLINT_TESTING || badBrowsers) {
const create = typeof rule === "function" ? rule : rule.create;
return create(context, globalThis.ESLINT_TESTING ? undefined : badBrowsers);
}
return {};
},
};
const configName = `typescript-${ts || "base"}`;
const flatConfigName = `flat/${configName}`;
if (!module.exports.configs[configName]) {
flatTypeScriptConfigs[configName] = [{
name: `escompat/${configName}`,
plugins: {
escompat: module.exports
},
rules: {}
}];
const config = { rules: {} };
if (ts === 2016) {
config.extends = [`plugin:escompat/typescript-base`];
flatTypeScriptConfigs[configName].unshift(
...flatTypeScriptConfigs['typescript-base']
);
} else if (ts) {
let previous = ts - 1;
while (!module.exports.configs[`typescript-${previous}`]) previous -= 1;
config.extends = [`plugin:escompat/typescript-${previous}`];
flatTypeScriptConfigs[configName].unshift(
...flatTypeScriptConfigs[`typescript-${previous}`]
);
}
module.exports.configs[configName] = config;
module.exports.configs[flatConfigName] = flatTypeScriptConfigs[configName];
}
module.exports.configs[`typescript-base`].rules[`escompat/${name}`] = "off";
module.exports.configs[`flat/typescript-base`].at(-1).rules[`escompat/${name}`] = "off";
module.exports.configs[configName].rules[`escompat/${name}`] = "error";
module.exports.configs[flatConfigName].at(-1).rules[`escompat/${name}`] = "error";
};
module.exports = { rules: {}, configs: {} };
// ES2015
createRule(
"no-edge-destructure-bug",
"edge < 18",
"disallow the use of specific destructuring patterns that cause bugs in old Edge"
);
// ES2016
createRule(
"no-exponentiation-operator",
"chrome < 52, edge < 14, firefox < 52, safari < 10.1",
"disallow use of exponentiation operator (**)",
{ ts: 2016 }
);
// ES2018
createRule(
"no-async-iteration",
"edge < 79, safari < 12, firefox < 57, chrome < 63",
"disallow the use of `for await of` style loops",
{ ts: 2018 }
);
createRule(
"no-async-generator",
"edge < 79, safari < 12, firefox < 57, chrome < 63",
"disallow the use of async generator functions",
{ ts: 2018 }
);
createRule(
"no-object-rest-spread",
"edge < 79, safari < 11.1, firefox < 55, chrome < 60",
"disallow object rest/spread patterns",
{ ts: 2018 }
);
createRule(
"no-regexp-s-flag",
"edge < 79, safari < 11.1, firefox < 78, chrome < 62",
"disallow the use of the RegExp `s` flag"
);
createRule(
"no-regexp-lookbehind",
"edge < 79, safari < 16.4, firefox < 78, chrome < 62",
"disallow the use of RegExp lookbehinds"
);
createRule(
"no-regexp-named-group",
"edge < 79, safari 11.1, firefox < 78, chrome < 64",
"disallow the use of RegExp named groups"
);
// ES2019
createRule(
"no-optional-catch",
"edge < 79, safari < 11.1, firefox < 58, chrome < 66",
"always require catch() to have an argument",
{ ts: 2019 }
);
// ES2020
createRule(
"no-dynamic-imports",
"edge < 79, safari < 11, firefox < 67, chrome < 63",
"disallow dynamic import statements"
);
createRule(
"no-optional-chaining",
"edge < 80, safari < 13.1, firefox < 72, chrome < 80",
"disallow the .? optional chaining operator",
{ ts: 2020 }
);
createRule(
"no-nullish-coalescing",
"edge < 80, safari < 13.1, firefox < 72, chrome < 80",
"disallow the ?? nullish coalescing operator",
{ ts: 2020 }
);
createRule(
"no-bigint",
"edge < 79, safari < 14, firefox < 68, chrome < 67",
"disallow bigints"
);
// ES2021
createRule(
"no-numeric-separators",
"edge < 79, safari < 13, firefox < 68, chrome < 75",
"disallow use of numeric separators like 1_000_000",
{ ts: 2021 }
);
createRule(
"no-logical-assignment-operator",
"edge < 85, safari < 14, firefox < 79, chrome < 85",
"disallow logical assignment operators like &&=",
{ ts: 2021 }
);
// ES2022
createRule(
"no-public-static-class-fields",
"edge < 79, safari < 14.5, firefox < 75, chrome < 72",
"disallow public static class fields like foo = 1",
{ ts: 2022 }
);
createRule(
"no-public-instance-class-fields",
"edge < 79, safari < 14.5, firefox < 69, chrome < 72",
"disallow public class fields like foo = 1",
{ ts: 2022 }
);
createRule(
"no-computed-public-class-fields",
"edge < 79, safari < 14.5, firefox < 69, chrome < 74",
"disallow computed public static or instance class fields like [foo] = 1",
{ ts: 2022 }
);
createRule(
"no-private-class-fields",
"edge < 79, safari < 14.5, firefox < 90, chrome < 74",
"disallow private class fields like #foo = 1",
{ ts: 2022 }
);
createRule(
"no-class-static-blocks",
"edge < 94, safari < 16.4, firefox < 93, chrome < 94",
"disallow static blocks like `static { x = 1 }`",
{ ts: 2022 }
);
createRule(
"no-top-level-await",
"edge < 89, safari < 15, firefox < 89, chrome < 89",
"disallow await keyword outside of async function context",
{ ts: 2022 }
);
// ES2023
createRule(
"no-hashbang-comment",
"edge < 79, safari < 13.1, firefox < 67, chrome < 74",
"disallow hashbang comments",
{ ts: 2023 }
);
// ES2024
createRule(
"no-regexp-v-flag",
"edge > 0, safari < 17, firefox < 116, chrome < 112",
"disallow the use of the RegExp `v` flag",
{ ts: 2024 }
);
// ES2025
createRule(
"no-regexp-duplicate-named-groups",
"edge < 125, safari < 17, firefox < 129, chrome < 125",
"disallow the use of RegExp duplicate named groups",
{ ts: 2025 }
);
// Proposals...
createRule(
"no-do-expression",
"edge > 0, safari > 0, firefox > 0, chrome > 0",
'disallow "do" expressions'
);
createRule(
"no-bind-operator",
"edge > 0, safari > 0, firefox > 0, chrome > 0",
"disallow the :: bind operator"
);
createRule(
"no-pipeline-operator",
"edge > 0, safari > 0, firefox > 0, chrome > 0",
"disallow the > pipeline operator"
);
module.exports.configs.recommended = {
plugins: ["escompat"],
parserOptions: { ecmaVersion: 2025 },
rules: Object.keys(module.exports.rules).reduce(
(o, r) => ((o["escompat/" + r] = ["error"]), o),
{}
),
};
module.exports.configs["flat/recommended"] = {
name: 'escompat/flat/recommended',
plugins: {
escompat: module.exports
},
languageOptions: {
ecmaVersion: 2025
},
rules: Object.keys(module.exports.rules).reduce(
(o, r) => ((o["escompat/" + r] = ["error"]), o),
{}
),
};
module.exports.configs.typescript = {
extends: ["plugin:escompat/typescript-2025"],
};
module.exports.configs['flat/typescript'] = flatTypeScriptConfigs[
'typescript-2025'
];
if (require.main === module) {
console.log(require("util").inspect(module.exports, { depth: Infinity }));
}

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
':function[async=true][generator=true]'(node) {
context.report(node, `Async Generators are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'ForOfStatement[await=true]'(node) {
context.report(node, `Async Iteration is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'Literal[bigint]'(node) {
context.report(node, `BigInts are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
BindExpression(node) {
context.report(node, `The Bind Operator is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,10 @@
'use strict';
module.exports = (context, badBrowser) => ({
StaticBlock(node) {
context.report(
node,
`Class Static Blocks are not supported in ${badBrowser}`
);
},
});

View file

@ -0,0 +1,11 @@
'use strict';
module.exports = (context, badBrowser) => ({
// Ignore type annotations that don't assign
'ClassProperty[computed=true]:not([typeAnnotation]:not([value]))'(node) {
context.report(node, `Computed Class Fields are not supported in ${badBrowser}`)
},
'PropertyDefinition[computed=true]:not([typeAnnotation]:not([value]))'(node) {
context.report(node, `Computed Class Fields are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
DoExpression(node) {
context.report(node, `Do Expressions are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'ImportExpression, CallExpression[callee.type="Import"]'(node) {
context.report(node, `Dynamic import is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,31 @@
'use strict';
const objectPatternHasDefaults = node =>
node.type === 'ObjectPattern' && node.properties.some(prop => prop.value.type === 'AssignmentPattern')
module.exports = function(context) {
return {
ArrowFunctionExpression(node) {
// Unary functions don't trip on this bug
if (node.params.length < 2) return
// This bug only occurs when some arguments use Object destructuring
if (!node.params.some(param => param.type === 'ObjectPattern')) return
const objectPatternArgs = node.params.filter(node => node.type === 'ObjectPattern')
// This bug is only occurs when an argument uses Object Destructuring with Default assignment
if (!objectPatternArgs.some(objectPatternHasDefaults)) return
// This bug gets fixed if the first argument uses Object destructuring with default assignments!
if (node.params[0].type === 'ObjectPattern' && objectPatternHasDefaults(node.params[0])) return
context.report(
node,
'There is an Edge 15-17 bug which causes second argument destructuring to fail. See https://git.io/fhd7N for more'
)
}
}
}
module.exports.schema = []

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'AssignmentExpression[operator="**="], BinaryExpression[operator="**"]'(node) {
context.report(node, `Exponentiation Operator is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,13 @@
'use strict';
module.exports = (context, badBrowser) => {
const { sourceCode = context.getSourceCode() } = context;
return {
'Program:exit' (node) {
const [comment] = sourceCode.getAllComments();
if (comment && comment.type === 'Shebang') {
context.report(node, `Hashbang comments are not supported in ${badBrowser}`)
}
}
}
}

View file

@ -0,0 +1,13 @@
'use strict';
module.exports = (context, badBrowser) => ({
'AssignmentExpression[operator="||="]'(node) {
context.report(node, `Logical assignment operators are not supported in ${badBrowser}`)
},
'AssignmentExpression[operator="&&="]'(node) {
context.report(node, `Logical assignment operators are not supported in ${badBrowser}`)
},
'AssignmentExpression[operator="??="]'(node) {
context.report(node, `Logical assignment operators are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'LogicalExpression[operator="??"]'(node) {
context.report(node, `the Nullish Coalescing Operator is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,16 @@
'use strict';
module.exports = {
meta: {
fixable: 'code'
},
create: (context, badBrowser) => ({
'Literal[raw=/_/][value>=0], Literal[raw=/_/][value<=0]'(node) {
context.report({
node,
message: `Numeric Separators are not supported in ${badBrowser}`,
fix: fixer => fixer.replaceText(node, String(node.value))
})
}
})
}

View file

@ -0,0 +1,18 @@
'use strict';
module.exports = (context, badBrowser) => ({
'ObjectExpression > SpreadElement'(node) {
context.report(node, `Object Rest/Spread is not supported in ${badBrowser}`)
},
'ObjectPattern > RestElement'(node) {
context.report(node, `Object Rest/Spread is not supported in ${badBrowser}`)
},
// Catch older versions of eslint and babel-eslint
ExperimentalRestProperty(node) {
context.report(node, `Object Rest/Spread is not supported in ${badBrowser}`)
},
ExperimentalSpreadProperty(node) {
context.report(node, `Object Rest/Spread is not supported in ${badBrowser}`)
},
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'CatchClause:not([param])'(node) {
context.report(node, `Optional Catch Parameters are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,10 @@
'use strict';
module.exports = (context, badBrowser) => ({
OptionalMemberExpression(node) {
context.report(node, `Optional Chaining is not supported in ${badBrowser}`)
},
ChainExpression(node) {
context.report(node, `Optional Chaining is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,7 @@
'use strict';
module.exports = (context, badBrowser) => ({
'BinaryExpression[operator="|>"]'(node) {
context.report(node, `The Pipeline Operator is not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,10 @@
'use strict';
module.exports = (context, badBrowser) => ({
ClassPrivateProperty(node) {
context.report(node, `Private Class Fields are not supported in ${badBrowser}`)
},
PrivateIdentifier(node) {
context.report(node, `Private Class Fields are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,13 @@
'use strict';
module.exports = (context, badBrowser) => ({
// Ignore type annotations that don't assign
'ClassProperty[static=false]:not([typeAnnotation]:not([value]))'(node) {
if (node.value === null) return
context.report(node, `Instance Class Fields are not supported in ${badBrowser}`)
},
'PropertyDefinition[static=false]:not([typeAnnotation]:not([value]))'(node) {
if (node.value === null) return
context.report(node, `Instance Class Fields are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,11 @@
'use strict';
module.exports = (context, badBrowser) => ({
// Ignore type annotations that don't assign
'ClassProperty[static=true]:not([typeAnnotation]:not([value]))'(node) {
context.report(node, `Static Class Fields are not supported in ${badBrowser}`)
},
'PropertyDefinition[static=true]:not([typeAnnotation]:not([value]))'(node) {
context.report(node, `Static Class Fields are not supported in ${badBrowser}`)
}
})

View file

@ -0,0 +1,30 @@
'use strict';
const hasDuplicateNamedGroups = s => /(\(\?<[_$\w]*?)>.*?\1>/.test(s)
module.exports = (context, badBrowser) => ({
'Literal[regex]'(node) {
if (hasDuplicateNamedGroups(node.regex.pattern)) {
context.report(node, `RegExp duplicate named groups are not supported in ${badBrowser}`)
}
},
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
const [source] = node.arguments;
if (
source &&
(
(
source.type === 'Literal' &&
typeof source.value === 'string' &&
hasDuplicateNamedGroups(source.value)
) ||
(
source.type === 'TemplateLiteral' &&
source.quasis.some(({value: {raw}}) => hasDuplicateNamedGroups(raw))
)
)
) {
context.report(node, `RegExp duplicate named groups are not supported in ${badBrowser}`)
}
}
})

View file

@ -0,0 +1,30 @@
'use strict';
const hasLookbehind = s => s.includes('(?<=') || s.includes('(?<!')
module.exports = (context, badBrowser) => ({
'Literal[regex]'(node) {
if (hasLookbehind(node.regex.pattern)) {
context.report(node, `RegExp lookbehinds are not supported in ${badBrowser}`)
}
},
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
const [source] = node.arguments;
if (
source &&
(
(
source.type === 'Literal' &&
typeof source.value === 'string' &&
hasLookbehind(source.value)
) ||
(
source.type === 'TemplateLiteral' &&
source.quasis.some(({value: {raw}}) => hasLookbehind(raw))
)
)
) {
context.report(node, `RegExp lookbehinds are not supported in ${badBrowser}`)
}
}
})

View file

@ -0,0 +1,30 @@
'use strict';
const hasNamedGroup = s => /\(\?<[_$\w]/.test(s)
module.exports = (context, badBrowser) => ({
'Literal[regex]'(node) {
if (hasNamedGroup(node.regex.pattern)) {
context.report(node, `RegExp named groups are not supported in ${badBrowser}`)
}
},
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
const [source] = node.arguments;
if (
source &&
(
(
source.type === 'Literal' &&
typeof source.value === 'string' &&
hasNamedGroup(source.value)
) ||
(
source.type === 'TemplateLiteral' &&
source.quasis.some(({value: {raw}}) => hasNamedGroup(raw))
)
)
) {
context.report(node, `RegExp named groups are not supported in ${badBrowser}`)
}
}
})

View file

@ -0,0 +1,28 @@
'use strict';
module.exports = (context, badBrowser) => ({
'Literal[regex]'(node) {
if (node.regex.flags.includes('s')) {
context.report(node, `RegExp "s" flag is not supported in ${badBrowser}`)
}
},
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
const [, flags] = node.arguments;
if (
flags &&
(
(
flags.type === 'Literal' &&
typeof flags.value === 'string' &&
flags.value.includes('s')
) ||
(
flags.type === 'TemplateLiteral' &&
flags.quasis.some(({value: {raw}}) => raw.includes('s'))
)
)
) {
context.report(node, `RegExp "s" flag is not supported in ${badBrowser}`)
}
}
})

View file

@ -0,0 +1,28 @@
'use strict';
module.exports = (context, badBrowser) => ({
'Literal[regex]'(node) {
if (node.regex.flags.includes('v')) {
context.report(node, `RegExp "v" flag is not supported in ${badBrowser}`)
}
},
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
const [, flags] = node.arguments;
if (
flags &&
(
(
flags.type === 'Literal' &&
typeof flags.value === 'string' &&
flags.value.includes('v')
) ||
(
flags.type === 'TemplateLiteral' &&
flags.quasis.some(({value: {raw}}) => raw.includes('v'))
)
)
) {
context.report(node, `RegExp "v" flag is not supported in ${badBrowser}`)
}
}
})

View file

@ -0,0 +1,20 @@
'use strict';
const functionTypes = new Set([
'FunctionDeclaration',
'FunctionExpression',
'ArrowFunctionExpression',
]);
module.exports = (context, badBrowser) => ({
AwaitExpression(node) {
let currentNode = node;
while (currentNode.parent) {
currentNode = currentNode.parent;
if (functionTypes.has(currentNode.type) && currentNode.async) {
return;
}
}
context.report(node, `Top-level await is not supported in ${badBrowser}`)
},
})