Skip to content

Commit

Permalink
feat!: Drop Node < 12, switch to eslint flat config (#16)
Browse files Browse the repository at this point in the history
* refactor: switch to eslint flat config

* chore: apply eslint autofix

* chore: up node version requirement

* chore: fix eslint errors manually

* revert: fixtures change

* chore: ignore package-lock.json
  • Loading branch information
kecrily committed Jul 18, 2023
1 parent e93aa9d commit b25c563
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 110 deletions.
3 changes: 0 additions & 3 deletions .eslintrc.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,3 +2,4 @@
npm-debug.log
.DS_Store
.eslint-release-info.json
package-lock.json
19 changes: 10 additions & 9 deletions bin/eslint-transforms.js
Expand Up @@ -2,35 +2,36 @@

"use strict";

var execSync = require("child_process").execSync;
var path = require("path");
const execSync = require("child_process").execSync;
const path = require("path");

var argv = process.argv.slice(2);
var args = argv.slice(1);
var transform = argv[0];
const argv = process.argv.slice(2);
const args = argv.slice(1);
const transform = argv[0];

/**
* Add possible node_modules/.bin paths to env and run the command passed in.
*
* @param {string} cmd The command to run
* @returns {void}
*/
function execWithNodeModules(cmd) {
var SEPARATOR = process.platform === "win32" ? ";" : ":",
const SEPARATOR = process.platform === "win32" ? ";" : ":",
env = Object.assign({}, process.env);

env.PATH = [

// Covers case when npm flattens dependencies and the jscodeshift bin will be directly under the root
// node_modules folder
path.resolve("./node_modules/.bin"),

// Covers case when dependencies are not flattened and the jscodeshift bin can be found under the
// node_modules folder of our package
path.resolve(__dirname, "../node_modules/.bin"),
env.PATH
].join(SEPARATOR);

execSync(cmd, {
env: env,
env,
cwd: process.cwd(),
stdio: "inherit"
});
Expand All @@ -39,6 +40,6 @@ function execWithNodeModules(cmd) {
execWithNodeModules([
"jscodeshift",
"-t",
path.resolve(__dirname, "../lib/" + transform + "/" + transform + ".js"),
path.resolve(__dirname, `../lib/${transform}/${transform}.js`),
args.join(" ")
].join(" "));
19 changes: 19 additions & 0 deletions eslint.config.js
@@ -0,0 +1,19 @@
"use strict";

const eslintConfig = require("eslint-config-eslint");
const globals = require("globals");

module.exports = [
{
ignores: ["tests/fixtures/"]
},
...eslintConfig,
{
files: ["tests/**/*"],
languageOptions: {
globals: {
...globals.mocha
}
}
}
];
127 changes: 62 additions & 65 deletions lib/new-rule-format/new-rule-format.js
Expand Up @@ -21,51 +21,47 @@

"use strict";

var j = require("jscodeshift");
const j = require("jscodeshift");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Returns `true` if the rule is already in the new format
*
* @param {Object} rootNode - where to look for the rule definition
* @returns {Boolean} `true` if rule is already in the new format
* @param {Object} rootNode where to look for the rule definition
* @returns {boolean} `true` if rule is already in the new format
*/
function isAlreadyInNewFormat(rootNode) {

// If there's already a module.exports.meta property, we assume the rule
// is already in the new format.
return rootNode
.find(j.Property)
.filter(function(node) {
return (
node.value.key.name === "meta" &&
.filter(node => (
node.value.key.name === "meta" &&
node.parent.value.type === "ObjectExpression" &&
node.parent.parent.value.type === "AssignmentExpression" &&
node.parent.parent.value.left.type === "MemberExpression" &&
node.parent.parent.value.left.object.name === "module" &&
node.parent.parent.value.left.property.name === "exports"
);
})
))
.size() > 0;
}

/**
* Checks if the node passed is a function expression or an arrow function expression
*
* @param {Object} node - node to check
* @returns {Boolean} - `true` if node is a function or arrow function expression
* @param {Object} node node to check
* @returns {boolean} - `true` if node is a function or arrow function expression
*/
function isFunctionOrArrowFunctionExpression(node) {
return node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
}

/**
* Checks if the node passed can be an "old format" rule definition
*
* @param {Object} node - node to check
* @returns {Boolean} - `true` if node looks like an "old format" rule definition
* @param {Object} node node to check
* @returns {boolean} - `true` if node looks like an "old format" rule definition
*/
function isOldFormatRuleDefinition(node) {
return isFunctionOrArrowFunctionExpression(node) || node.type === "CallExpression";
Expand All @@ -75,55 +71,49 @@ function isOldFormatRuleDefinition(node) {
* Returns the node in `rootNode` that is the rule definition in the old format,
* which will be in the format:
* module.exports = function(context) { ... };
*
* @param {Object} rootNode - where to look for the rule definition node
* @param {Object} rootNode where to look for the rule definition node
* @returns {Object} node - rule definition expression node
*/
function getOldFormatRuleDefinition(rootNode) {
return rootNode
.find(j.AssignmentExpression)
.filter(function(node) {
return (
node.value.left.type === "MemberExpression" &&
.filter(node => (
node.value.left.type === "MemberExpression" &&
node.value.left.property.name === "exports" &&
node.value.left.object.name === "module" &&
isOldFormatRuleDefinition(node.value.right)
);
});
));
}

/**
* Returns the node in `rootNode` that is the schema definition in the old format,
* which will be in the format:
* module.exports.schema = [ ... ];
*
* @param {Object} rootNode - where to look for the rule definition node
* @param {Object} rootNode where to look for the rule definition node
* @returns {Object} node - rule definition expression node
*/
function getOldFormatSchemaDefinition(rootNode) {
return rootNode
.find(j.AssignmentExpression)
.filter(function(node) {
return (
node.value.left.type === "MemberExpression" &&
.filter(node => (
node.value.left.type === "MemberExpression" &&
node.value.left.property.name === "schema" &&
node.value.left.object.type === "MemberExpression" &&
node.value.left.object.object.name === "module" &&
node.value.left.object.property.name === "exports"
);
});
));
}

/**
* Creates the object expression node that will be the `meta` property of this rule
*
* @param {Object} schemaNode - node that was the schema definition in the old rule format
* @param {Object} schemaNodeComments - comments that were above the old schema node
* @param {Boolean} isRuleFixable - `true` if the rule is fixable
* @returns {Object} ObjectExpression node
*/
* Creates the object expression node that will be the `meta` property of this rule
* @param {Object} schemaNode node that was the schema definition in the old rule format
* @param {Object} schemaNodeComments comments that were above the old schema node
* @param {boolean} isRuleFixable `true` if the rule is fixable
* @returns {Object} ObjectExpression node
*/
function createMetaObjectExpression(schemaNode, schemaNodeComments, isRuleFixable) {
var properties = [
const properties = [

// For docs, create just an empty object
j.property("init", j.identifier("docs"), j.objectExpression([]))
];
Expand All @@ -136,7 +126,9 @@ function createMetaObjectExpression(schemaNode, schemaNodeComments, isRuleFixabl

// The schema definition may not exist in some plugins
if (schemaNode) {
var schemaNodeProperty = j.property("init", j.identifier("schema"), schemaNode);
const schemaNodeProperty = j.property("init", j.identifier("schema"), schemaNode);


// Restore comments that were removed when the old format node was removed
schemaNodeProperty.comments = schemaNodeComments;
properties.push(schemaNodeProperty);
Expand All @@ -147,21 +139,20 @@ function createMetaObjectExpression(schemaNode, schemaNodeComments, isRuleFixabl

/**
* Creates the `exports` expression that wil contain the rule definition in the new format
*
* @param {Object} ruleDefinitionNode - node that was the rule definition in the old rule format
* @param {Object} ruleDefinitionNodeComments - comments that were above the old schema rule definition
* @param {Object} ruleMetaDefinitionNode - node that will be the meta definition expression
* @param {Object} ruleDefinitionNode node that was the rule definition in the old rule format
* @param {Object} ruleDefinitionNodeComments comments that were above the old schema rule definition
* @param {Object} ruleMetaDefinitionNode node that will be the meta definition expression
* @returns {Object} ExpressionStatement
*/
function createExportsExpression(ruleDefinitionNode, ruleDefinitionNodeComments, ruleMetaDefinitionNode) {
var exportsExpression = j.expressionStatement(
const exportsExpression = j.expressionStatement(
j.assignmentExpression(
"=",
j.memberExpression(j.identifier("module"), j.identifier("exports"), false),
j.objectExpression([
j.property("init", j.identifier("meta"), ruleMetaDefinitionNode),
j.property("init", j.identifier("create"), ruleDefinitionNode)
])
"=",
j.memberExpression(j.identifier("module"), j.identifier("exports"), false),
j.objectExpression([
j.property("init", j.identifier("meta"), ruleMetaDefinitionNode),
j.property("init", j.identifier("create"), ruleDefinitionNode)
])
)
);

Expand All @@ -176,22 +167,25 @@ function createExportsExpression(ruleDefinitionNode, ruleDefinitionNodeComments,
//------------------------------------------------------------------------------

/**
* @param {Object} fileInfo - holds information about the currently processed file.
* @returns {String} the new source code, after being transformed.
* Transforms an ESLint rule from the old format to the new format.
* @param {Object} fileInfo holds information about the currently processed file.
* @returns {string} the new source code, after being transformed.
*/
module.exports = function(fileInfo) {
var root = j(fileInfo.source);
const root = j(fileInfo.source);

if (isAlreadyInNewFormat(root)) {

// don't do anything and return
return root.toSource();
}

// for most plugins, the old format should be:
// module.exports = function(context) { ... }
// but maybe some plugins are using a diferent variable name instead of `context`
var ruleDefinitionExpression = getOldFormatRuleDefinition(root).get().value.right;
var identifierNameForContextObject = "context";
const ruleDefinitionExpression = getOldFormatRuleDefinition(root).get().value.right;
let identifierNameForContextObject = "context";

if (ruleDefinitionExpression.params && ruleDefinitionExpression.params.length > 0) {
identifierNameForContextObject = ruleDefinitionExpression.params[0].name;
}
Expand All @@ -202,37 +196,40 @@ module.exports = function(fileInfo) {
// ...
// fix: function() { ... }
// });
var isRuleFixable = root
const isRuleFixable = root
.find(j.Identifier)
.filter(function(node) {
return (
node.value.name === "fix" &&
.filter(node => (
node.value.name === "fix" &&
node.parent.value.type === "Property" &&
node.parent.parent.parent.value.type === "CallExpression" &&
node.parent.parent.parent.value.callee.type === "MemberExpression" &&
node.parent.parent.parent.value.callee.object.name === identifierNameForContextObject &&
node.parent.parent.parent.value.callee.property.name === "report"
);
})
))
.size() > 0;

var oldFormatSchemaDefinition = getOldFormatSchemaDefinition(root);
var schemaNode, schemaNodeComments;
const oldFormatSchemaDefinition = getOldFormatSchemaDefinition(root);
let schemaNode, schemaNodeComments;


// The schema definition may not exist in some plugins
if (oldFormatSchemaDefinition.size() > 0) {
schemaNode = getOldFormatSchemaDefinition(root).get().value.right;

// Store the comments too so we can attach it again later
schemaNodeComments = getOldFormatSchemaDefinition(root).get().parent.value.leadingComments;
getOldFormatSchemaDefinition(root).remove();
}

var ruleDefinitionNode = getOldFormatRuleDefinition(root).get().value.right;
const ruleDefinitionNode = getOldFormatRuleDefinition(root).get().value.right;

// Store the comments too so we can attach it again later
var ruleDefinitionNodeComments = getOldFormatRuleDefinition(root).get().parent.value.leadingComments;
const ruleDefinitionNodeComments = getOldFormatRuleDefinition(root).get().parent.value.leadingComments;

getOldFormatRuleDefinition(root).remove();

// Insert the rule definition in the new format at the end of the file
var newFormat = createExportsExpression(
const newFormat = createExportsExpression(
ruleDefinitionNode,
ruleDefinitionNodeComments,
createMetaObjectExpression(schemaNode, schemaNodeComments, isRuleFixable)
Expand Down
7 changes: 4 additions & 3 deletions package.json
Expand Up @@ -6,7 +6,7 @@
"author": "Vitor Balocco",
"repository": "eslint/eslint-transforms",
"engines": {
"node": ">=4"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"bin": {
"eslint-transforms": "./bin/eslint-transforms.js"
Expand All @@ -27,9 +27,10 @@
},
"devDependencies": {
"chai": "^3.5.0",
"eslint": "^2.9.0",
"eslint-config-eslint": "^3.0.0",
"eslint": "^8.45.0",
"eslint-config-eslint": "^8.0.0",
"eslint-release": "^1.0.0",
"globals": "^13.20.0",
"mocha": "^2.5.3"
},
"dependencies": {
Expand Down
2 changes: 0 additions & 2 deletions tests/.eslintrc.yml

This file was deleted.

0 comments on commit b25c563

Please sign in to comment.