2
0

first commit

This commit is contained in:
2024-08-09 00:39:27 +02:00
commit 79688abe2e
5698 changed files with 497838 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
{
"name": "@calcom/eslint-plugin-eslint",
"sideEffects": false,
"private": true,
"version": "0.1.0",
"main": "./src/index.js",
"dependencies": {
"@typescript-eslint/parser": "^5.52.0",
"@typescript-eslint/utils": "^5.52.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"devDependencies": {
"@types/eslint": "^8.4.5"
}
}

View File

@@ -0,0 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires, import/no-anonymous-default-export */
export default {
recommended: require("./recommended").default,
};

View File

@@ -0,0 +1,13 @@
const recommended = {
parser: "@typescript-eslint/parser",
parserOptions: { sourceType: "module" },
rules: {
"@calcom/eslint/deprecated-imports": "error",
"@calcom/eslint/deprecated-imports-next-router": "error",
"@calcom/eslint/avoid-web-storage": "error",
"@calcom/eslint/avoid-prisma-client-import-for-enums": "error",
"@calcom/eslint/no-prisma-include-true": "warn",
},
};
export default recommended;

View File

@@ -0,0 +1,11 @@
// This registers Typescript compiler instance onto node.js.
// Now it is possible to just require typescript files without any compilation steps in the environment run by node
require("ts-node").register();
// re-export our rules so that eslint run by node can understand them
module.exports = {
// import our rules from the typescript file
rules: require("./rules/index.ts").default,
// import our config from the typescript file
configs: require("./configs/index.ts").default,
};

View File

@@ -0,0 +1,45 @@
import { ESLintUtils } from "@typescript-eslint/utils";
const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`);
const rule = createRule({
create(context) {
return {
ImportDeclaration(node) {
node.source.value === "@prisma/client" &&
node.importKind !== "type" &&
node.specifiers.forEach((item) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const enumType = item.imported?.name; // ts doesn't know about imported, bad type?
if (!enumType || enumType === "Prisma" || enumType === "PrismaClient") return null;
return context.report({
node: item,
loc: node.loc,
messageId: "avoid-prisma-client-import",
data: {
enumType,
},
});
});
},
};
},
name: "avoid-prisma-client-import-for-enums",
meta: {
fixable: "code",
docs: {
description: "Avoid prisma client import for enums",
recommended: "error",
},
messages: {
"avoid-prisma-client-import": `Import { {{enumType}} } from '@calcom/prisma/enums' to avoid including @prisma/client.`,
},
type: "suggestion",
schema: [],
},
defaultOptions: [],
});
export default rule;

View File

@@ -0,0 +1,45 @@
import { ESLintUtils } from "@typescript-eslint/utils";
const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`);
const rule = createRule({
create(context) {
return {
CallExpression(node) {
const webStorages = ["localStorage", "sessionStorage"];
const callee = node.callee;
if (
// Can't figure out how to fix this TS issue
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
callee.object?.object?.name === "window" &&
// Can't figure out how to fix this TS issue
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
webStorages.includes(node?.callee?.object?.property?.name)
) {
return context.report({
node: node,
loc: node.loc,
messageId: "possible-issue-with-embed",
});
}
},
};
},
name: "avoid-web-storage",
meta: {
fixable: "code",
docs: {
description: "Avoid deprecated imports",
recommended: "warn",
},
messages: {
"possible-issue-with-embed": `Be aware that accessing localStorage/sessionStorage throws error in Chrome Incognito mode when embed is in cross domain context. If you know what you are doing, \`import {localStorage, sessionStorage} from "@calcom/lib/webstorage"\` for safe usage. See https://github.com/calcom/cal.com/issues/2618`,
},
type: "suggestion",
schema: [],
},
defaultOptions: [],
});
export default rule;

View File

@@ -0,0 +1,38 @@
import { ESLintUtils } from "@typescript-eslint/utils";
const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`);
const rule = createRule({
name: "deprecated-imports-next-router",
meta: {
fixable: "code",
docs: {
description: "Importing router from 'next/router' is deprecated, use 'next/navigation' instead",
recommended: "error",
},
messages: {
"deprecated-next-router":
"Importing router from 'next/router' is deprecated, use 'next/navigation' instead",
},
type: "problem",
schema: [],
},
defaultOptions: [],
create(context) {
return {
ImportDeclaration(node) {
if (node.source.value === "next/router") {
context.report({
node,
messageId: "deprecated-next-router",
fix: function (fixer) {
return fixer.replaceText(node.source, "'next/navigation'");
},
});
}
},
};
},
});
export default rule;

View File

@@ -0,0 +1,41 @@
import { ESLintUtils } from "@typescript-eslint/utils";
const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`);
const rule = createRule({
create(context) {
return {
ImportDeclaration(node) {
node.specifiers.length &&
node.source.value === "dayjs" &&
node.specifiers.forEach((item) => {
if (item.local.name === "dayjs") {
return context.report({
node: item,
loc: node.loc,
messageId: "dayjs-moved",
fix: (fixer) => fixer.replaceText(node, "import dayjs from '@calcom/dayjs'"),
});
}
return null;
});
},
};
},
name: "deprecated-imports",
meta: {
fixable: "code",
docs: {
description: "Avoid deprecated imports",
recommended: "warn",
},
messages: {
"dayjs-moved": `Import dayjs from '@calcom/daysjs' to avoid plugin conflicts.`,
},
type: "suggestion",
schema: [],
},
defaultOptions: [],
});
export default rule;

View File

@@ -0,0 +1,10 @@
/* eslint-disable @typescript-eslint/no-var-requires*/
import type { ESLint } from "eslint";
export default {
"deprecated-imports": require("./deprecated-imports").default,
"avoid-web-storage": require("./avoid-web-storage").default,
"avoid-prisma-client-import-for-enums": require("./avoid-prisma-client-import-for-enums").default,
"no-prisma-include-true": require("./no-prisma-include-true").default,
"deprecated-imports-next-router": require("./deprecated-imports-next-router").default,
} as ESLint.Plugin["rules"];

View File

@@ -0,0 +1,23 @@
import type { Rule } from "eslint";
const rule: Rule.RuleModule = {
meta: {
docs: {
description: "This rule is run on typescript!",
},
},
create: (context) => {
return {
VariableDeclarator: (node) => {
if (node.id.type === "Identifier" && node.id.name !== "bla") {
context.report({
node,
message: 'All variabled should be named "bla"!',
});
}
},
};
},
};
export default rule;

View File

@@ -0,0 +1,100 @@
import type { TSESTree } from "@typescript-eslint/utils";
import { ESLintUtils } from "@typescript-eslint/utils";
import type { ReportDescriptor } from "@typescript-eslint/utils/dist/ts-eslint";
const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`);
const assesIncludePropertyIncludesTrue = (
includeProperty: TSESTree.Property,
reporter: { (reportObj: ReportDescriptor<"no-prisma-include-true">): void }
) => {
if (includeProperty.value.type === "ObjectExpression") {
includeProperty.value.properties.forEach((childProperty) => {
if (
childProperty.type === "Property" &&
childProperty.value.type === "Literal" &&
childProperty.value.value === true
) {
reporter({
node: childProperty,
messageId: "no-prisma-include-true",
});
}
});
}
};
const searchIncludeProperty = (
property: TSESTree.Property,
reporter: { (reportObj: ReportDescriptor<"no-prisma-include-true">): void }
) => {
if (property.type === "Property") {
// If property is include, check if it has a child property with value true
if (property.key.type === "Identifier" && property.key.name === "include") {
assesIncludePropertyIncludesTrue(property, reporter);
}
// If property value is also an object, recursively search for include property
if (property.value.type === "ObjectExpression") {
property.value.properties.forEach((childProperty) => {
if (childProperty.type === "Property") {
searchIncludeProperty(childProperty, reporter);
}
});
}
}
};
const rule = createRule({
create: function (context) {
return {
CallExpression(node) {
if (!(node.callee as TSESTree.MemberExpression).property) {
return null;
}
const nodeName = ((node.callee as TSESTree.MemberExpression).property as TSESTree.Identifier).name;
if (
!["findUnique", "findUniqueOrThrow", "findFirst", "findFirstOrThrow", "findMany"].includes(nodeName)
) {
return null;
}
const nodeArgs = node.arguments[0] as TSESTree.ObjectExpression;
if (!nodeArgs) {
return null;
}
const backReporter = (reportObj: ReportDescriptor<"no-prisma-include-true">) => {
context.report(reportObj);
};
nodeArgs.properties?.forEach((property) => {
if (property.type === "Property") {
searchIncludeProperty(property, backReporter);
}
});
return null;
},
};
},
name: "no-prisma-include-true",
meta: {
type: "problem",
docs: {
description:
"Disallow passing argument object with include: { AnyPropertyName: true } to prisma methods",
recommended: "error",
},
messages: {
"no-prisma-include-true": `Do not pass argument object with include: { AnyPropertyName: true } to prisma methods`,
},
fixable: "code",
schema: [],
},
defaultOptions: [],
});
export default rule;