Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configs/eslint-config-devtools/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const tsRules = {
'error',
{ prefer: 'type-imports' },
],
'@mongodb-js/devtools/no-plain-object-records': 'error',
};

const tsxRules = {
Expand Down
1 change: 1 addition & 0 deletions configs/eslint-plugin-devtools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
module.exports = {
rules: {
'no-expect-method-without-call': require('./rules/no-expect-method-without-call'),
'no-plain-object-records': require('./rules/no-plain-object-records'),
},
};
3 changes: 2 additions & 1 deletion configs/eslint-plugin-devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
"devDependencies": {
"@mongodb-js/mocha-config-devtools": "^1.0.5",
"@mongodb-js/prettier-config-devtools": "^1.0.1",
"@typescript-eslint/rule-tester": "^8.49.0",
"depcheck": "^1.4.7",
"eslint": "^7.25.0",
"eslint": "^7.25.0 || ^8.0.0",
"mocha": "^8.4.0",
"nyc": "^15.1.0"
}
Expand Down
46 changes: 46 additions & 0 deletions configs/eslint-plugin-devtools/rules/no-plain-object-records.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'Using {} to initialize records means that keys present on Object.prototype will not be handled properly',
},
fixable: 'code',
schema: [],
},
create(context) {
return {
VariableDeclarator(node) {
if (
node.id.type === 'Identifier' &&
node.id.typeAnnotation &&
node.id.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
node.id.typeAnnotation.typeAnnotation.typeName.name === 'Record' &&
node.init &&
node.init.type === 'ObjectExpression' &&
!node.init.properties.some(
(prop) =>
prop.key.type === 'Identifier' && prop.key.name === '__proto__',
)
) {
context.report({
node,
message:
'{} is not a good initializer for records. Use Object.create(null) instead.',
fix:
node.init.properties.length === 0
? (fixer) => fixer.replaceText(node.init, 'Object.create(null)')
: (fixer) =>
fixer.insertTextBefore(
node.init.properties[0],
'__proto__: null, ',
),
});
}
},
};
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';
const { RuleTester } = require('@typescript-eslint/rule-tester');
const rule = require('./no-plain-object-records');

RuleTester.afterAll = after;
const ruleTester = new RuleTester();

ruleTester.run('no-plain-object-records', rule, {
valid: [
{
code: 'const record: Record<string, number> = Object.create(null);',
},
{
code: 'const record: Record<string, number> = {__proto__: null, a: 1, b: 2};',
},
{
code: 'const record: Record<string, number> = {__proto__: 42, a: 1, b: 2};',
},
{
code: 'const record: Record<string, number> = {a, b, __proto__: 42};',
},
],

invalid: [
{
code: 'const record: Record<string, number> = {a, b};',
output: 'const record: Record<string, number> = {__proto__: null, a, b};',
errors: [
{
message:
'{} is not a good initializer for records. Use Object.create(null) instead.',
},
],
},
{
code: 'const record: Record<string, unknown> = {};',
output: 'const record: Record<string, unknown> = Object.create(null);',
errors: [
{
message:
'{} is not a good initializer for records. Use Object.create(null) instead.',
},
],
},
{
code: 'let foo, record: Record<string, unknown> = {};',
output: 'let foo, record: Record<string, unknown> = Object.create(null);',
errors: [
{
message:
'{} is not a good initializer for records. Use Object.create(null) instead.',
},
],
},
],
});
Loading
Loading