Skip to content

Commit

Permalink
Refactor to ES6
Browse files Browse the repository at this point in the history
This is an almost complete rewrite of most of the rules. The goal
started as a simple update to take advantage of some newer ES6 features
but ended up being a complete refactor to make the code more concise
and, hopefully, readable.
  • Loading branch information
Casey Visco committed Sep 17, 2016
1 parent 7426782 commit f2bafa4
Show file tree
Hide file tree
Showing 48 changed files with 2,144 additions and 2,287 deletions.
13 changes: 13 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,16 @@ rules:
- error
- declaration
- allowArrowFunctions: true
quote-props:
- error
- consistent
no-undefined: off
no-multi-spaces:
- error
- exceptions: { VariableDeclarator: true }
curly:
- error
- multi-line
key-spacing:
- error
- mode: minimum
161 changes: 89 additions & 72 deletions lib/rules/amd-function-arity.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,110 @@
/**
* @fileoverview Ensure AMD-style callbacks contain correct number of arguments
* @author Kevin Partington
* @file Rule to ensure AMD-style callbacks contain correct number of
* arguments
* @author Kevin Partington
*/

"use strict";

const util = require("../util");
const rjs = require("../utils/rjs");
const ast = require("../utils/ast");

module.exports = {
meta: {
docs: {},
schema: [
{
"type": "object",
"properties": {
"allowExtraDependencies": {
anyOf: [
{
"type": "boolean",
"default": false
},
{
"type": "array",
"uniqueItems": true,
"items": {
"type": "string"
}
}
]
const hasCallback = ast.hasCallback;
const getDependencyNodes = rjs.getDependencyNodes;
const getModuleFunction = rjs.getModuleFunction;
const isAmdDefine = rjs.isAmdDefine;
const isRequireCall = rjs.isRequireCall;

// -----------------------------------------------------------------------------
// Configuration
// -----------------------------------------------------------------------------

const docs = {
description: "Ensure AMD-style callbacks contain correct number of arguments",
category: "Stylistic Choices",
recommended: false
};

const schema = [
{
type: "object",
properties: {
allowExtraDependencies: {
anyOf: [
{
type: "boolean",
default: false
},
{
type: "array",
uniqueItems: true,
items: {
type: "string"
}
}
},
"additionalProperties": false
]
}
]
},
},
additionalProperties: false
}
];

create: function (context) {
const allowExtraDependencies = (context.options && context.options[0] && context.options[0].allowExtraDependencies) || false;
const TOO_MANY_PARAMS_MESSAGE = "Too many parameters in {{functionName}} callback (expected {{expected}}, found {{actual}}).";
const TOO_FEW_PARAMS_MESSAGE = "Not enough parameters in {{functionName}} callback (expected {{expected}}, found {{actual}}).";
const defaults = {
allowExtraDependencies: false
};

function allUnassignedPathsAreAllowed(dependencyNodes, callbackParams) {
if (typeof allowExtraDependencies === "boolean") {
return allowExtraDependencies;
}
// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------

const unassignedPaths = dependencyNodes.slice(callbackParams.length);
const isBoolean = (value) => typeof value === "boolean";
const unassigned = (paths, params) => paths.slice(params.length);
const includes = (list) => (path) => list.indexOf(path.value) !== -1;

return unassignedPaths.every(function (path) {
return allowExtraDependencies.indexOf(path.value) !== -1;
});
}
const isAmdWithCallback = (node) =>
isAmdDefine(node) || isRequireCall(node) && hasCallback(node);

const reportTooFew = (expected, actual) =>
`Not enough parameters in callback (expected ${expected}, found ${actual}).`;

function checkArity(node, funcName) {
const dependencyNodes = util.getDependencyNodes(node);
const reportTooMany = (expected, actual) =>
`Too many parameters in callback (expected ${expected}, found ${actual}).`;

if (!dependencyNodes) {
return;
// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------

function create(context) {
const opts = Object.assign({}, defaults, context.options[0]);
const allowed = opts.allowExtraDependencies;

const isAllowed = (paths) =>
isBoolean(allowed) ? allowed : paths.every(includes(allowed));

return {
CallExpression(node) {
if (!isAmdWithCallback(node)) return;

const paths = getDependencyNodes(node);
const pathCount = paths.length;

if (!pathCount) return;

const params = getModuleFunction(node).params;
const paramCount = params.length;

if (pathCount < paramCount) {
context.report(node, reportTooMany(pathCount, paramCount));
}

const dependencyCount = dependencyNodes.length;
const callbackParams = util.getAmdCallback(node).params;
const actualParamCount = callbackParams.length;

if (dependencyCount < actualParamCount) {
context.report(node, TOO_MANY_PARAMS_MESSAGE, {
functionName: funcName,
expected: dependencyCount,
actual: actualParamCount
});
} else if (dependencyCount > actualParamCount && !allUnassignedPathsAreAllowed(dependencyNodes, callbackParams)) {
context.report(node, TOO_FEW_PARAMS_MESSAGE, {
functionName: funcName,
expected: dependencyCount,
actual: actualParamCount
});
if (pathCount > paramCount && !isAllowed(unassigned(paths, params))) {
context.report(node, reportTooFew(pathCount, paramCount));
}
}
};
}

return {
"CallExpression": function (node) {
/* istanbul ignore else: correctly does nothing */
if (util.isDefineCall(node) && util.isAmdDefine(node)) {
checkArity(node, "define");
} else if (util.isRequireCall(node) && ast.hasCallback(node)) {
checkArity(node, node.callee.name);
}
}
};
}
module.exports = {
meta: { docs, schema },
create
};
111 changes: 60 additions & 51 deletions lib/rules/enforce-define.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,77 @@
/**
* @fileoverview Disallow files that are not wrapped in a call to `define`
* @author Casey Visco
* @file Rule to disallow files that are not wrapped in a call to `define`
* @author Casey Visco <cvisco@gmail.com>
*/

"use strict";

const path = require("path");
const util = require("../util");
const rjs = require("../utils/rjs");
const ast = require("../utils/ast");

module.exports = {
meta: {
docs: {},
schema: [
const isDefineCall = rjs.isDefineCall;
const isExprStatement = ast.isExprStatement;
const basename = path.basename;

// -----------------------------------------------------------------------------
// Configuration
// -----------------------------------------------------------------------------

const docs = {
description: "Disallow files that are not wrapped in a call to `define`",
category: "Stylistic Choices",
recommended: false
};

const schema = [
{
anyOf: [
{
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"uniqueItems": true,
"items": {
"type": "string"
}
}
]
type: "string"
},
{
type: "array",
uniqueItems: true,
items: {
type: "string"
}
}
]
},

create: function (context) {
const MESSAGE = "File must be wrapped in a `define` call";
const ignoredFiles = context.options[0] || [];

/**
* Determine if supplied `filename` is allowed to not be wrapped in a
* `define` call.
* @private
* @param {String} filename - filename to test
* @returns {Boolean} true if filename is allowed
*/
function isIgnoredFile(filename) {
const basename = path.basename(filename);
return ignoredFiles.indexOf(basename) !== -1;
}
}
];

return {
"Program": function (node) {
const filename = context.getFilename();
const message = "File must be wrapped in a `define` call";

if (isIgnoredFile(filename)) {
return;
}
// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------

for (let i = 0, length = node.body.length; i < length; i += 1) {
const child = node.body[i];
const includes = (list, item) => list.indexOf(item) !== -1;

if (!(ast.isExprStatement(child) && util.isDefineCall(child.expression))) {
context.report(node, MESSAGE);
break;
}
}
const isDefineExpr = (child) =>
isExprStatement(child) && isDefineCall(child.expression);

// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------

function create(context) {
const ignoredFiles = context.options[0] || [];

return {
Program(node) {
const filename = basename(context.getFilename());

if (includes(ignoredFiles, filename)) return;

if (!node.body.every(isDefineExpr)) {
context.report(node, message);
}
};
}
}
};
}

module.exports = {
meta: { docs, schema },
create
};
54 changes: 35 additions & 19 deletions lib/rules/no-amd-define.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
/**
* @fileoverview Disallow use of AMD (dependency array) form of `define`
* @author Casey Visco
* @file Rule to disallow use of AMD (dependency array) form of `define`
* @author Casey Visco <cvisco@gmail.com>
*/

"use strict";

const util = require("../util");
const rjs = require("../utils/rjs");

module.exports = {
meta: {
docs: {},
schema: []
},

create: function (context) {
const MESSAGE = "AMD form of `define` is not allowed.";

return {
"CallExpression": function (node) {
if (util.isDefineCall(node) && util.isAmdDefine(node)) {
context.report(node, MESSAGE);
}
const isAmdDefine = rjs.isAmdDefine;

// -----------------------------------------------------------------------------
// Configuration
// -----------------------------------------------------------------------------

const docs = {
description: "Disallow use of AMD (dependency array) form of `define`",
category: "Stylistic Choices",
recommended: false
};

const schema = [];

const message = "AMD form of `define` is not allowed.";

// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------

function create(context) {
return {
CallExpression(node) {
if (isAmdDefine(node)) {
context.report(node, message);
}
};
}
}
};
}

module.exports = {
meta: { docs, schema },
create
};
Loading

0 comments on commit f2bafa4

Please sign in to comment.