-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from dbos-inc/chuck/eslint-initial
Initial eslint custom rule / plugin
- Loading branch information
Showing
5 changed files
with
1,876 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
.dbos/ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
coverage/ | ||
|
||
# TypeScript build | ||
dist/ | ||
|
||
# Diagnostic reports (https://nodejs.org/api/report.html) | ||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | ||
|
||
# node-waf configuration | ||
.lock-wscript | ||
|
||
# Dependency directories | ||
node_modules/ | ||
|
||
# TypeScript cache | ||
*.tsbuildinfo | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Optional eslint cache | ||
.eslintcache | ||
|
||
# Optional REPL history | ||
.node_repl_history | ||
|
||
# Output of 'npm pack' | ||
*.tgz | ||
|
||
# Editor temp files | ||
*.swp | ||
*.swo | ||
examples/hello/.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
const tslintPlugin = require("@typescript-eslint/eslint-plugin"); | ||
const secPlugin = require("eslint-plugin-security"); | ||
const noSecrets = require("eslint-plugin-no-secrets"); | ||
|
||
const baseConfig = | ||
{ | ||
plugins: [ | ||
"@typescript-eslint", | ||
"security", | ||
"no-secrets", | ||
], | ||
env: { | ||
"node" : true | ||
}, | ||
rules: { | ||
"no-eval": "error", | ||
"no-console": "error", | ||
"security/detect-unsafe-regex": "error", | ||
"no-secrets/no-secrets": "error", | ||
"@dbos-inc/detect-nondeterministic-calls": "error", | ||
"@dbos-inc/detect-new-date": "error", | ||
}, | ||
"extends": [ | ||
], | ||
}; | ||
|
||
const recConfig = | ||
{ | ||
...baseConfig, | ||
rules: { | ||
...baseConfig.rules, | ||
}, | ||
"extends" : [ | ||
...baseConfig.extends, | ||
"plugin:@typescript-eslint/recommended-requiring-type-checking", | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
] | ||
} | ||
|
||
const extConfig = | ||
{ | ||
...recConfig, | ||
rules: { | ||
...recConfig.rules, | ||
}, | ||
"extends" : [ | ||
...recConfig.extends, | ||
] | ||
} | ||
|
||
|
||
module.exports = { | ||
meta: { | ||
"name": "@dbos-inc/eslint-plugin", | ||
"version": "0.0.2", | ||
}, | ||
rules: { | ||
'detect-nondeterministic-calls': { | ||
// Rule configuration for Math.random() detection | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Detect calls to nondeterministic functions like Math.random(), which should be called via DBOS rather than directly', | ||
}, | ||
schema: [], | ||
}, | ||
create: function (context) { | ||
return { | ||
CallExpression(node) { | ||
//console.log(node.callee.type+JSON.stringify(node)); | ||
if (node.callee.type === 'MemberExpression' && | ||
node.callee.object.name === 'Math' && | ||
node.callee.property.name === 'random') | ||
{ | ||
context.report({ | ||
node: node, | ||
message: 'Avoid calling Math.random() directly; it can lead to non-reproducible behavior.', | ||
}); | ||
} | ||
if (node.callee.type === 'Identifier' && | ||
node.callee.name === 'setTimeout') | ||
{ | ||
context.report({ | ||
node: node, | ||
message: 'Avoid calling setTimeout() directly; it can lead to undesired behavior when debugging.', | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}, | ||
'detect-new-date': { | ||
// Rule configuration for new Date() detection | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Detect calls to new Date(), which should be called via DBOS rather than directly', | ||
}, | ||
schema: [], | ||
}, | ||
create: function (context) { | ||
return { | ||
NewExpression(node) { | ||
if (node.callee.name === 'Date') { | ||
context.report({ | ||
node: node, | ||
message: 'Avoid using new Date(); consider using the DBOS SDK functions for consistency and testability.', | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}, | ||
}, | ||
plugins: { | ||
"@typescript-eslint" : tslintPlugin, | ||
"security" : secPlugin, | ||
"no-secrets" : noSecrets, | ||
}, | ||
configs: { | ||
dbosBaseConfig: baseConfig, | ||
dbosRecommendedConfig: recConfig, | ||
dbosExtendedConfig: extConfig, | ||
} | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const {RuleTester} = require("eslint"); | ||
const ruleUnderTest = require("./dbos-rules.js"); | ||
|
||
const ruleTester = new RuleTester({ | ||
parserOptions: { ecmaVersion: 2015 } | ||
}); | ||
|
||
// Throws error if the tests in ruleTester.run() do not pass | ||
ruleTester.run( | ||
"detect-nondeterministic-calls", // rule name | ||
ruleUnderTest.rules['detect-nondeterministic-calls'], // rule code | ||
{ // checks | ||
// 'valid' checks cases that should pass | ||
valid: [{ | ||
code: "const foo = 'bar';", | ||
}], | ||
// 'invalid' checks cases that should not pass | ||
invalid: [{ | ||
code: "const foo = Math.random();", | ||
//output: 'const foo = *NEED SUGGESTION*;', | ||
errors: 1, | ||
}, | ||
{ | ||
code: "setTimeout(1000).then();", | ||
//output: 'const foo = *NEED SUGGESTION*;', | ||
errors: 1, | ||
}], | ||
} | ||
); | ||
|
||
ruleTester.run( | ||
"detect-new-date", // rule name | ||
ruleUnderTest.rules['detect-new-date'], // rule code | ||
{ // checks | ||
// 'valid' checks cases that should pass | ||
valid: [{ | ||
code: "const foo = 'bar';", | ||
}], | ||
// 'invalid' checks cases that should not pass | ||
invalid: [{ | ||
code: "const foo = new Date();", | ||
//output: 'const foo = *NEED SUGGESTION*;', | ||
errors: 1, | ||
}], | ||
} | ||
); | ||
|
||
|
||
console.log("All tests passed!"); |
Oops, something went wrong.