first commit
This commit is contained in:
16
calcom/packages/eslint-plugin/package.json
Normal file
16
calcom/packages/eslint-plugin/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
4
calcom/packages/eslint-plugin/src/configs/index.ts
Normal file
4
calcom/packages/eslint-plugin/src/configs/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires, import/no-anonymous-default-export */
|
||||
export default {
|
||||
recommended: require("./recommended").default,
|
||||
};
|
||||
13
calcom/packages/eslint-plugin/src/configs/recommended.ts
Normal file
13
calcom/packages/eslint-plugin/src/configs/recommended.ts
Normal 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;
|
||||
11
calcom/packages/eslint-plugin/src/index.js
Normal file
11
calcom/packages/eslint-plugin/src/index.js
Normal 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,
|
||||
};
|
||||
@@ -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;
|
||||
45
calcom/packages/eslint-plugin/src/rules/avoid-web-storage.ts
Normal file
45
calcom/packages/eslint-plugin/src/rules/avoid-web-storage.ts
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
10
calcom/packages/eslint-plugin/src/rules/index.ts
Normal file
10
calcom/packages/eslint-plugin/src/rules/index.ts
Normal 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"];
|
||||
23
calcom/packages/eslint-plugin/src/rules/my-first-rule.ts
Normal file
23
calcom/packages/eslint-plugin/src/rules/my-first-rule.ts
Normal 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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user