Enable unicorn/prefer-string-replace-all rule (#13788)

This commit is contained in:
fisker Cheung authored and GitHub committed 2022-11-06 19:55:24 +08:00
1 parent db43ab6409
commit 9eafbf6854
17 files changed
+107 -37

No files matched your search

+2 -2
View File
@@ -5,13 +5,13 @@ Note:
2. `postcss` contains `Container` class with `.at` method
*/
const at = (isOptionalObject, object, index, ...args) => {
const at = (isOptionalObject, object, index) => {
if (isOptionalObject && (object === undefined || object === null)) {
return;
}
return object.at
? object.at(index, ...args)
? object.at(index)
: object[index < 0 ? object.length + index : index];
};
+19
View File
@@ -0,0 +1,19 @@
const stringReplaceAll = (isOptionalObject, original, pattern, replacement) => {
if (isOptionalObject && (original === undefined || original === null)) {
return;
}
if (original.replaceAll) {
return original.replaceAll(pattern, replacement);
}
// Simple detection to check if pattern is a `RegExp`
if (pattern.global) {
return original.replace(pattern, replacement);
}
// Doesn't work for substitutes, eg `.replaceAll("*", "\\$&")`
return original.split(pattern).join(replacement);
};
export default stringReplaceAll;
+51 -10
View File
@@ -7,6 +7,9 @@ import { SOURCE_DIR } from "../../utils/index.mjs";
const generate = babelGenerator.default;
const atHelperPath = fileURLToPath(new URL("../shims/at.js", import.meta.url));
const stringReplaceAllHelperPath = fileURLToPath(
new URL("../shims/string-replace-all.js", import.meta.url)
);
/* Doesn't work for dependencies, optional call, computed property, and spread arguments */
@@ -59,33 +62,40 @@ function transformObjectHasOwnCall(node) {
* @param {import("@babel/types").Node} node
* @returns {boolean}
*/
function transformRelativeIndexingCall(node) {
function transformMethodCall({
node,
method,
replacement = `__${method}`,
argumentsLength,
}) {
if (
!(
(node.type === "CallExpression" ||
node.type === "OptionalCallExpression") &&
!node.optional &&
node.arguments.length === 1 &&
node.arguments.length === argumentsLength &&
node.arguments.every(({ type }) => type !== "SpreadElement") &&
(node.callee.type === "MemberExpression" ||
node.callee.type === "OptionalMemberExpression") &&
!node.callee.computed &&
node.callee.object.type !== "ThisExpression" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "at"
node.callee.property.name === method
)
) {
return false;
}
// `__at(isOptionalObject, object, ...arguments)`
node.arguments.unshift(
{
type: "BooleanLiteral",
value: node.callee.type === "OptionalMemberExpression",
leadingComments: [{ type: "CommentBlock", value: " isOptionalObject" }],
},
node.callee.object
);
node.callee = { type: "Identifier", name: "__at" };
node.callee = { type: "Identifier", name: replacement };
return true;
}
@@ -93,22 +103,40 @@ function transformRelativeIndexingCall(node) {
function transform(original, file) {
if (
file === atHelperPath ||
file === stringReplaceAllHelperPath ||
!file.startsWith(SOURCE_DIR) ||
(!original.includes(".at(") && !original.includes("Object.hasOwn("))
(!original.includes(".at(") &&
!original.includes(".replaceAll(") &&
!original.includes("Object.hasOwn("))
) {
return original;
}
let changed = false;
let shouldInjectRelativeIndexingHelper = false;
let shouldInjectStringReplaceAllHelperPath = false;
const ast = parse(original, { sourceType: "module" });
traverse(ast, (node) => {
const hasObjectHasOwnCall = transformObjectHasOwnCall(node);
changed ||= hasObjectHasOwnCall;
const hasRelativeIndexingCall = transformRelativeIndexingCall(node);
const hasRelativeIndexingCall = transformMethodCall({
node,
method: "at",
argumentsLength: 1,
});
shouldInjectRelativeIndexingHelper ||= hasRelativeIndexingCall;
changed ||= hasRelativeIndexingCall;
const hasStringReplaceAllCall = transformMethodCall({
node,
method: "replaceAll",
replacement: "__stringReplaceAll",
argumentsLength: 2,
});
shouldInjectStringReplaceAllHelperPath ||= hasStringReplaceAllCall;
changed ||=
hasObjectHasOwnCall || hasRelativeIndexingCall || hasStringReplaceAllCall;
});
if (!changed) {
@@ -117,9 +145,22 @@ function transform(original, file) {
let { code } = generate(ast);
if (shouldInjectRelativeIndexingHelper) {
const helpers = [
shouldInjectRelativeIndexingHelper && { name: "__at", path: atHelperPath },
shouldInjectStringReplaceAllHelperPath && {
name: "__stringReplaceAll",
path: stringReplaceAllHelperPath,
},
]
.filter(Boolean)
.map(
(helper) => `import ${helper.name} from ${JSON.stringify(helper.path)};`
)
.join("\n");
if (helpers) {
code = outdent`
import __at from ${JSON.stringify(atHelperPath)};
${helpers}
${code}
`;
@@ -25,7 +25,7 @@ async function update() {
.match(
/<svg.*?octicon-code-square.*?>.*?<\/svg>\s*([\d,]+)\s*Repositories\s*<\/a>/
)[1]
.replace(/,/g, "")
.replaceAll(",", "")
);
if (Number.isNaN(dependentsCountNpm)) {
throw new TypeError(
@@ -32,7 +32,7 @@ module.exports = {
return;
}
const fixed = name.replace(/JSX/g, "Jsx");
const fixed = name.replaceAll("JSX", "Jsx");
context.report({
node,
messageId: MESSAGE_ID,