Skip to content

Commit

Permalink
Merge pull request #1 from dbos-inc/chuck/eslint-initial
Browse files Browse the repository at this point in the history
Initial eslint custom rule / plugin
  • Loading branch information
chuck-dbos authored Jan 4, 2024
2 parents 69a6351 + 6063642 commit cffa14a
Show file tree
Hide file tree
Showing 5 changed files with 1,876 additions and 0 deletions.
38 changes: 38 additions & 0 deletions .gitignore
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
127 changes: 127 additions & 0 deletions dbos-rules.js
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,
}
};

49 changes: 49 additions & 0 deletions dbos-rules.test.js
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!");
Loading

0 comments on commit cffa14a

Please sign in to comment.