solium
Advanced tools
Comparing version 0.0.2 to 0.1.0
@@ -18,3 +18,3 @@ /** | ||
var CWD = process.cwd (), | ||
CONFIG_FILENAME = '.soliumrc'; | ||
CONFIG_FILENAME = '.soliumrc.json'; | ||
@@ -28,3 +28,6 @@ var errorCodes = { | ||
function createDefaultConfigJSON () { | ||
var config = { rules: {} }; | ||
var config = { | ||
'custom-rules-filename': null, | ||
rules: {} | ||
}; | ||
@@ -102,4 +105,4 @@ Object.keys (soliumRules).forEach (function (rulename) { | ||
try { | ||
userConfig = fs.readFileSync ( | ||
path.join (CWD, CONFIG_FILENAME), 'utf8' | ||
userConfig = require ( | ||
path.join (CWD, CONFIG_FILENAME) | ||
); | ||
@@ -113,3 +116,12 @@ } catch (e) { | ||
userConfig = JSON.parse (userConfig); | ||
//if custom rules' file is set, make sure we have its absolute path | ||
if ( | ||
userConfig ['custom-rules-filename'] && | ||
!path.isAbsolute (userConfig ['custom-rules-filename']) | ||
) { | ||
userConfig ['custom-rules-filename'] = path.join ( | ||
CWD, userConfig ['custom-rules-filename'] | ||
); | ||
} | ||
cli.file ? lint (userConfig, cli.file) : lint (userConfig); | ||
@@ -116,0 +128,0 @@ } |
@@ -19,10 +19,14 @@ /** | ||
/** | ||
* load the user-specified rules from the rules/ directory | ||
* load the user-specified rules from the rules/ directory and custom rules from specified file | ||
* @param {Object} userRules object whose keys specify the rule name and boolean values specify whether to include it | ||
* @param {String} customRulesFilePath The file from where definitions of user-defined rules are loaded | ||
* @returns {Object} Definitions of all user-requested rules. Throws error if a rule in userRules is not amongst available rules | ||
*/ | ||
load: function (userRules) { | ||
load: function (userRules, customRulesFilePath) { | ||
var ruleFiles = fs.readdirSync (RULES_DIR), | ||
idCounter = 1; | ||
idCounter = 1, | ||
customRules = customRulesFilePath ? require (customRulesFilePath) : {}; | ||
rules = {}; //clear rules before populating them | ||
ruleFiles.forEach (function (filename) { | ||
@@ -45,3 +49,7 @@ var ruleName = filename.slice (0, -JS_EXT.length); | ||
userRules [key] = soliumRules [key]; | ||
userRules [key].id = idCounter++; | ||
Object.assign (userRules [key], { | ||
id: idCounter++, | ||
custom: false | ||
}); | ||
} | ||
@@ -52,2 +60,29 @@ } | ||
Object.keys (customRules).forEach (function (ruleName) { | ||
if (!userRules [ruleName]) { | ||
return; | ||
} | ||
if (rules [ruleName]) { | ||
/** | ||
* Gets executed when a pre-defined and a custom rule have a name clash. | ||
* The custom rule over-writes the pre-defined one. | ||
*/ | ||
} | ||
rules [ruleName] = { | ||
verify: customRules [ruleName] | ||
}; | ||
//in case of rule name clash, assign the custom rule the same id as the pre-defined rule with the same name | ||
userRules [ruleName] = { | ||
enabled: true, | ||
custom: true, | ||
type: 'CUSTOM-ERROR', | ||
id: userRules [ruleName] ? userRules [ruleName].id : idCounter++ | ||
} | ||
}); | ||
return userRules; | ||
@@ -54,0 +89,0 @@ }, |
/** | ||
* @fileoverview Ensure that all constants (and only constants) contain only upper case letters and underscore | ||
* @fileoverview Ensure that contract, library, modifier and struct names follow CamelCase notation | ||
* @author Raghav Dua <duaraghav8@gmail.com> | ||
@@ -17,3 +17,2 @@ */ | ||
'LibraryStatement', | ||
'ModifierName', | ||
'ModifierDeclaration', | ||
@@ -34,3 +33,3 @@ 'StructDeclaration' | ||
node: node, | ||
message: 'Identifier name doesn\'t follow the CamelCase notation' | ||
message: '\'' + node.name + '\' doesn\'t follow the CamelCase notation' | ||
}); | ||
@@ -41,4 +40,32 @@ } | ||
/** | ||
* A few Modifier Names (like 'returns' in 'function foo () returns (uint bar) {}') are exceptions | ||
* to CamelCase notation. | ||
* They shouldn't be flagged by the linter. | ||
*/ | ||
context.on ('ModifierName', function (emitted) { | ||
var node = emitted.node, | ||
exceptionsToCamelCase = [ | ||
'returns' | ||
]; | ||
function isExempted (name) { | ||
return exceptionsToCamelCase.indexOf (name) > -1; | ||
} | ||
if (emitted.exit || isExempted (node.name)) { | ||
return; | ||
} | ||
if (!camelCaseRegEx.test (node.name)) { | ||
context.report ({ | ||
node: node, | ||
message: '\'' + node.name + '\' doesn\'t follow the CamelCase notation' | ||
}); | ||
} | ||
}); | ||
} | ||
}; |
@@ -13,4 +13,2 @@ /** | ||
var SourceCode = context.getSourceCode (); | ||
context.on ('IfStatement', function (emitted) { | ||
@@ -89,2 +87,2 @@ var node = emitted.node; | ||
}; | ||
}; |
@@ -14,9 +14,2 @@ /** | ||
function reportNode (node) { | ||
context.report ({ | ||
node: node, | ||
message: 'Identifier name doesn\'t follow the mixedCase notation' | ||
}); | ||
} | ||
context.on ('VariableDeclarator', function (emitted) { | ||
@@ -30,3 +23,6 @@ var node = emitted.node; | ||
if (!mixedCaseRegEx.test (node.id.name)) { | ||
reportNode (node); | ||
context.report ({ | ||
node: node, | ||
message: 'Identifier name \'' + node.id.name + '\' doesn\'t follow the mixedCase notation' | ||
}); | ||
} | ||
@@ -43,3 +39,6 @@ }); | ||
if (!node.is_constant && !mixedCaseRegEx.test (node.name)) { | ||
reportNode (node); | ||
context.report ({ | ||
node: node, | ||
message: '\'' + node.name + '\' doesn\'t follow the mixedCase notation' | ||
}); | ||
} | ||
@@ -56,3 +55,6 @@ }); | ||
if (!mixedCaseRegEx.test (node.name)) { | ||
reportNode (node); | ||
context.report ({ | ||
node: node, | ||
message: '\'' + node.name + '\' doesn\'t follow the mixedCase notation' | ||
}); | ||
} | ||
@@ -69,3 +71,6 @@ }); | ||
if (!mixedCaseRegEx.test (node.id)) { | ||
reportNode (node); | ||
context.report ({ | ||
node: node, | ||
message: '\'' + node.id + '\' doesn\'t follow the mixedCase notation' | ||
}); | ||
} | ||
@@ -72,0 +77,0 @@ }); |
/** | ||
* @fileoverview Ensure that contract, library, modifier and struct names follow CamelCase notation | ||
* @fileoverview Ensure that all constants (and only constants) contain only upper case letters and underscore | ||
* @author Raghav Dua <duaraghav8@gmail.com> | ||
@@ -17,3 +17,3 @@ */ | ||
node: node, | ||
message: 'Constant name doesn\'t follow the UPPER_CASE notation' | ||
message: 'Constant name \'' + node.name + '\' doesn\'t follow the UPPER_CASE notation' | ||
}); | ||
@@ -20,0 +20,0 @@ } |
@@ -27,3 +27,3 @@ /** | ||
* @param {String} sourceCode The Source Code to lint | ||
* @param {Object} config An object whose keys specify the rules to use | ||
* @param {Object} config An object that specifies the rules to use and path of file containing custom rule definitions | ||
* @returns {Array} Array of objects, each containing lint error messages and supporting info, empty if no errors | ||
@@ -37,3 +37,3 @@ */ | ||
this.removeAllListeners (); | ||
config.rules = rules.load (config.rules); | ||
config.rules = rules.load (config.rules, config ['custom-rules-filename']); | ||
sourceCodeText = sourceCode; | ||
@@ -46,3 +46,5 @@ astUtils.init (sourceCodeText); | ||
try { | ||
rule.verify (new RuleContext (name, config.rules [name], Solium)); | ||
rule.verify ( | ||
new RuleContext (name, config.rules [name], Solium) | ||
); | ||
} catch (e) { | ||
@@ -49,0 +51,0 @@ throw new Error ( |
{ | ||
"name": "solium", | ||
"version": "0.0.2", | ||
"version": "0.1.0", | ||
"description": "A flexible, stand-alone linter for Ethereum Solidity", | ||
@@ -5,0 +5,0 @@ "main": "./lib/solium.js", |
# Solium | ||
Solium is a linter for Solidity which uses Abstract Syntax Trees and allows the user to enable/disable existing rules and add their own ones! | ||
Solium is a linter for Solidity which uses Abstract Syntax Trees and allows the user to enable/disable existing rules and add their own ones! | ||
It internally uses [solparse](https://github.com/duaraghav8/solparse) to parse your solidity code into a Spider Monkey compliant AST | ||
#Install | ||
@@ -10,3 +12,3 @@ ```bash | ||
#Usage | ||
In the root directory of your DApp, run the following once: | ||
In the root directory of your DApp, run the following: | ||
```bash | ||
@@ -16,3 +18,3 @@ solium --init | ||
This create ```.soliumrc``` inside your root directory, which has the configuration for the enabled rules. | ||
This creates ```.soliumrc.json``` inside your root directory, which has the configuration for the enabled and custom rules. | ||
@@ -31,2 +33,54 @@ You can disable a particular rule by setting its value to ```false``` | ||
#Plugging in your custom rules | ||
-> Open up the ```.soliumrc.json``` configuration file and set the value of ```custom-rules-filename``` to the path of the file that defines your rules. You can either provide an absolute path or a path relative to the directory in which .soliumrc.json resides. For example: ```"custom-rules-filename": "./my-rules.js"``` | ||
The format for writing your custom rule file (for example, ```my-rules.js```) is: | ||
```js | ||
module.exports = { | ||
'my-rule-name-1': function (context) { | ||
//Solium internally uses EventEmitter and emits an event every time it enters or leaves a node during the Depth First Traversal of the AST | ||
context.on ('IfStatement', function (emittedObject) { | ||
//exit property is set to true if we are leaving the node | ||
if (emittedObject.exit) { | ||
return; | ||
} | ||
//View the node representing an If Statement | ||
console.log (emittedObject.node); | ||
//report an error | ||
context.report ({ | ||
node: emittedObject.node, | ||
message: 'I JUST ENTERED AN IF STATEMENT!!', | ||
line: 1, //optional | ||
column: 2 //optional | ||
}); | ||
}); | ||
}, | ||
'my-rule-name-2': function (context) { | ||
context.on ('ContractStatement', function (emittedObject) { | ||
//similarly define this rule to do something with Contract Declarations | ||
}); | ||
} | ||
}; | ||
``` | ||
**NOTE**: The best way to know which event you're looking for is to simply install [solparse](https://github.com/duaraghav8/solparse) or [solidity-parser](https://github.com/ConsenSys/solidity-parser), then parse your code into the AST and see the value of the ```type``` field of the node that you wish to target. | ||
See the [existing rules](https://github.com/duaraghav8/Solium/tree/master/lib/rules) to get an idea of how the rules are making use of the context object being provided to them. | ||
-> Then, inside the ```rules``` object in the same file, set your rule names to ```true```. For instance: | ||
```json | ||
"rules": { | ||
"my-rule-name-1": true, | ||
"my-rule-name-2": true | ||
} | ||
``` | ||
**NOTE**: If you write a rule whose name clashes with the name of a pre-defined rule, your custom rule overrides the pre-defined one. | ||
#Integrate Solium in your app | ||
@@ -33,0 +87,0 @@ To access Solium's API, first install it: |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
29338
775
107
7