Skip to content

Commit

Permalink
feat: add transform for v9 migration
Browse files Browse the repository at this point in the history
  • Loading branch information
snitin315 committed May 22, 2024
1 parent 61b9ffe commit 24b6100
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 0 deletions.
159 changes: 159 additions & 0 deletions lib/v9-migration/v9-migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* @fileoverview Transform that migrates an ESLint API from v8 to v9
* Refer to https://github.com/eslint/eslint-transforms/issues/25 for more information
*
* @author Nitin Kumar
*/

"use strict";

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

/**
* Formats a message string with ANSI escape codes to display it in yellow with bold styling in the terminal.
* @param {string} message The message to be formatted.
* @returns {string} The formatted message string.
*/
function formatBoldYellow(message) {
return `\u001b[1m\u001b[33m${message}\u001b[39m\u001b[22m`;
}

const contextMethodsToPropertyMapping = {
getSourceCode: "sourceCode",
getFilename: "filename",
getPhysicalFilename: "physicalFilename",
getCwd: "cwd"
};

const contextToSourceCodeMapping = {
getSource: "getText",
getSourceLines: "getLines",
getAllComments: "getAllComments",
getNodeByRangeIndex: "getNodeByRangeIndex",
getComments: "getComments",
getCommentsBefore: "getCommentsBefore",
getCommentsAfter: "getCommentsAfter",
getCommentsInside: "getCommentsInside",
getJSDocComment: "getJSDocComment",
getFirstToken: "getFirstToken",
getFirstTokens: "getFirstTokens",
getLastToken: "getLastToken",
getLastTokens: "getLastTokens",
getTokenAfter: "getTokenAfter",
getTokenBefore: "getTokenBefore",
getTokenByRangeStart: "getTokenByRangeStart",
getTokens: "getTokens",
getTokensAfter: "getTokensAfter",
getTokensBefore: "getTokensBefore",
getTokensBetween: "getTokensBetween",
parserServices: "parserServices",
getDeclaredVariables: "getDeclaVariables"
};

//------------------------------------------------------------------------------
// Transform Definition
//------------------------------------------------------------------------------

/**
* Transforms an ESLint rule from the old format to the new format.
* @param {Object} fileInfo holds information about the currently processed file.
* * @param {Object} api holds the jscodeshift API
* @returns {string} the new source code, after being transformed.
*/

module.exports = function(fileInfo, api) {
const j = api.jscodeshift;
const root = j(fileInfo.source);

// Update context methods
// context.getSourceCode() -> context.sourceCode ?? context.getSourceCode()
root.find(j.CallExpression, {
callee: {
object: {
type: "Identifier",
name: "context"
},
property: {
type: "Identifier",
name: name =>
Object.keys(contextMethodsToPropertyMapping).includes(name)
}
}
}).replaceWith(({ node }) => {
const method = node.callee.property.name;
const args = node.arguments;

return j.logicalExpression(
"??",
j.memberExpression(
j.identifier("context"),
j.identifier(contextMethodsToPropertyMapping[method])
),
j.callExpression(
j.memberExpression(
j.identifier("context"),
j.identifier(method)
),
[...args]
)
);
});

// Move context methods to SourceCode
// context.getSource() -> context.sourceCode.getText()
root.find(j.MemberExpression, {
object: {
type: "Identifier",
name: "context"
},
property: {
type: "Identifier",
name: name =>
Object.keys(contextToSourceCodeMapping).includes(name)
}
}).replaceWith(({ node }) => {
const method = node.property.name;

if (method === "getComments") {
// eslint-disable-next-line no-console -- This is an intentional warning message
console.warn(
formatBoldYellow(
`${fileInfo.path}:${node.loc.start.line}:${node.loc.start.column} The "getComments()" method has been removed. Please use "getCommentsBefore()", "getCommentsAfter()", or "getCommentsInside()" instead. https://eslint.org/docs/latest/use/migrate-to-9.0.0#-removed-sourcecodegetcomments`
)
);
return node;
}

node.property.name = contextToSourceCodeMapping[method];
node.object.name = "context.sourceCode";

return node;
});

// Warn for codePath.currentSegments
root.find(j.Property, {
key: {
type: "Identifier",
name: name =>
name === "onCodePathStart" || name === "onCodePathEnd"
}
})
.find(j.MemberExpression, {
property: {
type: "Identifier",
name: "currentSegments"
}
})
.forEach(({ node }) => {
// eslint-disable-next-line no-console -- This is an intentional warning message
console.warn(
formatBoldYellow(
`${fileInfo.path}:${node.loc.start.line}:${node.loc.start.column} The "CodePath#currentSegments" property has been removed and it can't be migrated automatically.\nPlease read https://eslint.org/blog/2023/09/preparing-custom-rules-eslint-v9/#codepath%23currentsegments for more information.\n`
)
);
});

return root.toSource();
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"test": "mocha ./tests/lib/**/*.js"
},
"devDependencies": {
"@hypermod/utils": "^0.4.2",
"eslint": "^9.2.0",
"eslint-config-eslint": "^10.0.0",
"eslint-release": "^1.0.0",
Expand Down
100 changes: 100 additions & 0 deletions tests/lib/v9-migration/v9-migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @fileoverview Tests for v9-migration transform.
* @author Nitin Kumar
* MIT License
*/

"use strict";

const { applyTransform } = require("@hypermod/utils");
const assert = require("assert");

const v9MigrationTransform = require("../../../lib/v9-migration/v9-migration");

describe("v9 migration transform", () => {
it("should migrate deprecated context methods to new properties", async () => {
const result = await applyTransform(
v9MigrationTransform,
`
const sourceCode = context.getSourceCode();
const cwd = context.getCwd();
const filename = context.getFilename();
const physicalFilename = context.getPhysicalFilename();
`
);

assert.strictEqual(
result,
`
const sourceCode = context.sourceCode ?? context.getSourceCode();
const cwd = context.cwd ?? context.getCwd();
const filename = context.filename ?? context.getFilename();
const physicalFilename = context.physicalFilename ?? context.getPhysicalFilename();
`.trim()
);
});

it("should migrate deprecated context methods to SourceCode", async () => {
const result = await applyTransform(
v9MigrationTransform,
`
const sourceCode = context.getSource();
const sourceLines = context.getSourceLines();
const allComments = context.getAllComments();
const nodeByRangeIndex = context.getNodeByRangeIndex();
const commentsBefore = context.getCommentsBefore(nodeOrToken);
const commentsAfter = context.getCommentsAfter(nodeOrToken);
const commentsInside = context.getCommentsInside(nodeOrToken);
`
);

assert.strictEqual(
result,
`
const sourceCode = context.sourceCode.getText();
const sourceLines = context.sourceCode.getLines();
const allComments = context.sourceCode.getAllComments();
const nodeByRangeIndex = context.sourceCode.getNodeByRangeIndex();
const commentsBefore = context.sourceCode.getCommentsBefore(nodeOrToken);
const commentsAfter = context.sourceCode.getCommentsAfter(nodeOrToken);
`.trim()
);
});

it.only("should warn about codePath.currentSegments", async () => {
await applyTransform(
v9MigrationTransform,
`
module.exports = {
meta: {
docs: {},
schema: []
},
create(context) {
return {
onCodePathStart(codePath, node) {
const currentSegments = codePath.currentSegments();
},
onCodePathEnd(endCodePath, node) {
const currentSegments = endCodePath.currentSegments();
},
};
}
}
`
);

// assert.strictEqual(
// result,
// `
// const sourceCode = context.sourceCode.getText();
// const sourceLines = context.sourceCode.getLines();
// const allComments = context.sourceCode.getAllComments();
// const nodeByRangeIndex = context.sourceCode.getNodeByRangeIndex();
// const commentsBefore = context.sourceCode.getCommentsBefore(nodeOrToken);
// const commentsAfter = context.sourceCode.getCommentsAfter(nodeOrToken);
// `.trim()
// );
});
});

0 comments on commit 24b6100

Please sign in to comment.