Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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