Valeriy Kuzmin, Lilt, 2021
Trick 1: use (@typescript-eslint/parser + transform:jscodeshift)
const Promise = require("Promise");
// eslint-disable-next-line local-rules/no-bluebird
const Promise = require("bluebird");
const Promise = require("bluebird");
// eslint-inline-ignores.spec.ts
import { defineTest } from "jscodeshift/dist/testUtils";
jest.autoMockOff();
defineTest(
__dirname,
"eslint-inline-ignores",
{ extensions: "ts" },
"eslint-inline-ignores-no-bluebird",
{
parser: "ts"
}
Trick 2: use jscodeshift defineTest helper
// __testfixtures__/eslint-inline-ignores-no-bluebird.input.ts
import Promise1 from "bluebird";
const Promise2 = require("bluebird");
// __testfixtures__/eslint-inline-ignores-no-bluebird.output.ts
// eslint-disable-next-line local-rules/no-bluebird
import Promise1 from "bluebird";
// eslint-disable-next-line local-rules/no-bluebird
const Promise2 = require("bluebird");
import { API, ASTPath, FileInfo } from "jscodeshift/src/core";
import { namedTypes } from "ast-types";
import ImportDeclaration = namedTypes.ImportDeclaration;
import VariableDeclaration = namedTypes.VariableDeclaration;
const transformation = (file: FileInfo, api: API): string => {
const j = api.jscodeshift;
return j(file.source)
.find(j.ImportDeclaration)
.replaceWith((path: ASTPath<ImportDeclaration>) => {
// TODO
})
.toSource();
};
replaceWith((path: ASTPath<ImportDeclaration>) => {
const { value: originalImportDeclaration } = path;
return j.importDeclaration.from({
comments: [
j.commentLine(
` eslint-disable-next-line local-rules/no-bluebird`,
true,
false
)
],
source: originalImportDeclaration.source,
specifiers: originalImportDeclaration.specifiers
});
});
Trick 3: comments cannot be inserted alone
Comments may appear as statements in otherwise empty statement lists, but may not coexist with non-Comment nodes.
replaceWith((path: ASTPath<VariableDeclaration>) => {
const { value: originalDeclaration } = path;
// ... a lot of checks ...
return j.variableDeclaration.from({
comments: [
j.commentLine(
` eslint-disable-next-line local-rules/no-bluebird`,
true,
false
)
],
kind: originalDeclaration.kind,
declarations: originalDeclaration.declarations
});
});
const { declarations } = originalDeclaration;
if (declarations.length !== 1) {
return originalDeclaration;
}
const firstDeclaration = declarations[0];
if (firstDeclaration.type !== "VariableDeclarator") {
return originalDeclaration;
}
const { init } = firstDeclaration;
if (!init || init.type !== "CallExpression") {
return originalDeclaration;
}
const { callee, arguments: initArgs } = init;
if (callee.type !== "Identifier" || callee.name !== "require") {
return originalDeclaration;
}
if (initArgs.length !== 1) {
return originalDeclaration;
}
const firstArg = initArgs[0];
if (
!firstArg ||
firstArg.type !== "StringLiteral" ||
firstArg.value !== "bluebird"
) {
return originalDeclaration;
}
✕ transforms correctly using "eslint-inline-ignores-no-bluebird" data (4476 ms)
● eslint-inline-ignores › transforms correctly using "eslint-inline-ignores-no-bluebird" data
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Trick 4: avoid errors at all costs
Trick 5: do not use _.get, use .type checks
const afterFirstTransform = j(file.source)
.find(j.ImportDeclaration)
.replaceWith((path: ASTPath<ImportDeclaration>) => {
...
})
.toSource();
const afterSecondTransform = j(file.source)
.find(j.ImportDeclaration)
.replaceWith((path: ASTPath<VariableDeclaration>) => {
...
})
.toSource();
Trick 6: parse and replace separately for different transformations
questions?
How do we continue?
Full code at https://github.com/lilt/front/pull/9520