summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorLeko <leko.noor@gmail.com>2020-09-30 02:29:40 +0800
committerLeko <leko.noor@gmail.com>2020-11-07 18:28:14 +0800
commitcef144421c5ff6e9677ecf0b7a607000b744aa13 (patch)
treed50d0463aa07df3764fc7452415194411c60e19f /tools
parent1d525f55bc9f090a167acc286529b9a424dd0d5a (diff)
downloadios-node-v8-cef144421c5ff6e9677ecf0b7a607000b744aa13.tar.gz
ios-node-v8-cef144421c5ff6e9677ecf0b7a607000b744aa13.tar.bz2
ios-node-v8-cef144421c5ff6e9677ecf0b7a607000b744aa13.zip
tools: add new ESLint rule: prefer-primordials
I added a new custom ESLint rule to fix these problems. We have a lot of replaceable codes with primordials. Accessing built-in objects is restricted by existing rule (no-restricted-globals), but accessing property in the built-in objects is not restricted right now. We manually review codes that can be replaced by primordials, but there's a lot of code that actually needs to be fixed. We have often made pull requests to replace the primordials with. Restrict accessing global built-in objects such as `Promise`. Restrict calling static methods such as `Array.from` or `Symbol.for`. Don't restrict prototype methods to prevent false-positive. PR-URL: https://github.com/nodejs/node/pull/35448 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com> Reviewed-By: Ben Coe <bencoe@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/eslint-rules/prefer-primordials.js156
1 files changed, 156 insertions, 0 deletions
diff --git a/tools/eslint-rules/prefer-primordials.js b/tools/eslint-rules/prefer-primordials.js
new file mode 100644
index 0000000000..ffbb1e6e30
--- /dev/null
+++ b/tools/eslint-rules/prefer-primordials.js
@@ -0,0 +1,156 @@
+/**
+ * @fileoverview We shouldn't use global built-in object for security and
+ * performance reason. This linter rule reports replacable codes
+ * that can be replaced with primordials.
+ * @author Leko <leko.noor@gmail.com>
+ */
+'use strict';
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+function toPrimordialsName(obj, prop) {
+ return obj + toUcFirst(prop);
+}
+
+function toUcFirst(str) {
+ return str[0].toUpperCase() + str.slice(1);
+}
+
+function isTarget(map, varName) {
+ return map.has(varName);
+}
+
+function isIgnored(map, varName, propName) {
+ if (!map.has(varName) || !map.get(varName).has(propName)) {
+ return false;
+ }
+ return map.get(varName).get(propName).ignored;
+}
+
+function getReportName({ name, parentName, into }) {
+ if (into) {
+ return toPrimordialsName(into, name);
+ }
+ if (parentName) {
+ return toPrimordialsName(parentName, name);
+ }
+ return name;
+}
+
+/**
+ * Get identifier of object spread assignment
+ *
+ * code: 'const { ownKeys } = Reflect;'
+ * argument: 'ownKeys'
+ * return: 'Reflect'
+ */
+function getDestructuringAssignmentParent(scope, node) {
+ const declaration = scope.set.get(node.name);
+ if (
+ !declaration ||
+ !declaration.defs ||
+ declaration.defs.length === 0 ||
+ declaration.defs[0].type !== 'Variable' ||
+ !declaration.defs[0].node.init
+ ) {
+ return null;
+ }
+ return declaration.defs[0].node.init.name;
+}
+
+const identifierSelector =
+ '[type!=VariableDeclarator][type!=MemberExpression]>Identifier';
+
+module.exports = {
+ meta: {
+ messages: {
+ error: 'Use `const { {{name}} } = primordials;` instead of the global.'
+ }
+ },
+ create(context) {
+ const globalScope = context.getSourceCode().scopeManager.globalScope;
+ const nameMap = context.options.reduce((acc, option) =>
+ acc.set(
+ option.name,
+ (option.ignore || [])
+ .concat(['prototype'])
+ .reduce((acc, name) => acc.set(name, {
+ ignored: true
+ }), new Map())
+ )
+ , new Map());
+ const renameMap = context.options
+ .filter((option) => option.into)
+ .reduce((acc, option) =>
+ acc.set(option.name, option.into)
+ , new Map());
+ let reported;
+
+ return {
+ Program() {
+ reported = new Map();
+ },
+ [identifierSelector](node) {
+ if (reported.has(node.range[0])) {
+ return;
+ }
+ const name = node.name;
+ const parentName = getDestructuringAssignmentParent(
+ context.getScope(),
+ node
+ );
+ if (!isTarget(nameMap, name) && !isTarget(nameMap, parentName)) {
+ return;
+ }
+
+ const defs = (globalScope.set.get(name) || {}).defs || null;
+ if (parentName && isTarget(nameMap, parentName)) {
+ if (!defs || defs[0].name.name !== 'primordials') {
+ reported.set(node.range[0], true);
+ const into = renameMap.get(name);
+ context.report({
+ node,
+ messageId: 'error',
+ data: {
+ name: getReportName({ into, parentName, name })
+ }
+ });
+ }
+ return;
+ }
+ if (defs.length === 0 || defs[0].node.init.name !== 'primordials') {
+ reported.set(node.range[0], true);
+ const into = renameMap.get(name);
+ context.report({
+ node,
+ messageId: 'error',
+ data: {
+ name: getReportName({ into, parentName, name })
+ }
+ });
+ }
+ },
+ MemberExpression(node) {
+ const obj = node.object.name;
+ const prop = node.property.name;
+ if (!prop || !isTarget(nameMap, obj) || isIgnored(nameMap, obj, prop)) {
+ return;
+ }
+
+ const variables =
+ context.getSourceCode().scopeManager.getDeclaredVariables(node);
+ if (variables.length === 0) {
+ context.report({
+ node,
+ messageId: 'error',
+ data: {
+ name: toPrimordialsName(obj, prop),
+ }
+ });
+ }
+ }
+ };
+ }
+};