chore: created shared utils package and moved objectKeys function to it (#35615)

This commit is contained in:
Aman Agarwal 2024-08-23 00:54:05 +05:30 committed by GitHub
parent 26e1c88a7b
commit 87d22cadb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 357 additions and 46 deletions

View File

@ -16,7 +16,8 @@
"sort-destructure-keys",
"cypress",
"testing-library",
"jest"
"jest",
"@appsmith"
],
"extends": [
"plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
@ -24,6 +25,7 @@
"plugin:cypress/recommended",
"plugin:testing-library/react",
"plugin:react-hooks/recommended",
"plugin:@appsmith/recommended",
// Note: Please keep this as the last config to make sure that this (and by extension our .prettierrc file) overrides all configuration above it
// https://www.npmjs.com/package/eslint-plugin-prettier#recommended-configuration
"plugin:prettier/recommended"

View File

@ -50,6 +50,7 @@
"dependencies": {
"@appsmith/ads": "workspace:^",
"@appsmith/ads-old": "workspace:^",
"@appsmith/utils": "workspace:^",
"@appsmith/wds": "workspace:^",
"@appsmith/wds-theming": "workspace:^",
"@aws-sdk/client-s3": "^3.622.0",
@ -251,6 +252,7 @@
"not op_mini all"
],
"devDependencies": {
"@appsmith/eslint-plugin": "workspace:^",
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-string-parser": "^7.19.4",
"@craco/craco": "^7.0.0",

View File

@ -11,6 +11,7 @@
"build:icons": "npx tsx ./src/scripts/build-icons.ts"
},
"dependencies": {
"@appsmith/utils": "workspace:^",
"@appsmith/wds-headless": "workspace:^",
"@appsmith/wds-theming": "workspace:^",
"@emotion/css": "^11.11.2",

View File

@ -1,7 +1,8 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { StoryGrid, DataAttrWrapper } from "@design-system/storybook";
import { Button, BUTTON_VARIANTS, COLORS, objectKeys } from "@appsmith/wds";
import { Button, BUTTON_VARIANTS, COLORS } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
const variants = objectKeys(BUTTON_VARIANTS);
const colors = Object.values(COLORS);

View File

@ -1,13 +1,7 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import {
Button,
Flex,
BUTTON_VARIANTS,
COLORS,
SIZES,
objectKeys,
} from "@appsmith/wds";
import { Button, Flex, BUTTON_VARIANTS, COLORS, SIZES } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
/**
* A button is a clickable element that is used to trigger an action.

View File

@ -6,8 +6,8 @@ import {
SIZES,
BUTTON_VARIANTS,
COLORS,
objectKeys,
} from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
/**
* Icon Button is a button component that only contains an icon.

View File

@ -2,12 +2,12 @@ import React from "react";
import {
InlineButtons,
Flex,
objectKeys,
BUTTON_VARIANTS,
COLORS,
SIZES,
} from "@appsmith/wds";
import type { Meta, StoryObj } from "@storybook/react";
import { objectKeys } from "@appsmith/utils";
import {
itemList,
longItemList,

View File

@ -4,10 +4,10 @@ import {
BUTTON_VARIANTS,
COLORS,
Flex,
objectKeys,
ToolbarButtons,
SIZES,
} from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
import {
itemList,
itemListWithIcons,

View File

@ -1,2 +1 @@
export { filterDataProps } from "./filterDataProps";
export { objectKeys } from "./objectKeys";

View File

@ -0,0 +1,4 @@
{
"ignorePatterns": ["dist/*", "Readme.md"],
"extends": ["../../.eslintrc.base.json"]
}

View File

@ -0,0 +1,4 @@
dist/
node_modules/
package-lock.json
yarn.lock

View File

@ -0,0 +1,106 @@
# Adding a Custom Rule to Your Appsmith ESLint Plugin
Welcome to the guide for adding a custom rule to your Appsmith ESLint plugin. Follow these steps to create and integrate a new rule into your Appsmith ESLint plugin.
You can create one by following the [official ESLint custom rule](https://eslint.org/docs/latest/extend/custom-rule-tutorial).
## Step 1: Create the Custom Rule File
1. Navigate to your Appsmith ESLint plugin directory i.e. `app/client/packages/eslint-plugin`.
2. Create a new directory for your custom rule in the root of `app/client/packages/eslint-plugin` directory. For example, `custom-rule/rule.js`.
```bash
mkdir custom-rule
touch custom-rule/rule.js
```
3. Open `custom-rule/rule.js` and define your rule. Here's a basic template to get you started:
```js
module.exports = {
meta: {
type: "problem", // or "suggestion" or "layout"
docs: {
description: "A description of what the rule does",
category: "Best Practices",
recommended: false,
},
fixable: null, // or "code" if the rule can fix issues automatically
schema: [], // JSON Schema for rule options
},
create(context) {
return {
// Define the rule's behavior here
// e.g., "Identifier": (node) => { /* logic */ }
};
},
};
```
## Step 2: Update the Plugin Index File
1. Open the `index.js` file inside `eslint-plugin` directory.
2. Import your custom rule and add it to the rules object in `index.js`. For example:
```js
const customRule = require("./custom-rule/rule.js");
module.exports = {
rules: {
"custom-rule": customRule,
},
};
```
## Step 3: Add Tests for Your Custom Rule
1. Create a test file for your rule in the `custom-rule` directory. For example, `custom-rule/rule.test.js`.
```bash
touch custom-rule/rule.test.js
```
2. Open `custom-rule/rule.test.js` and write tests using a testing framework like Mocha or Jest. Here's a basic example using ESLint's `RuleTester`:
```js
const rule = require("./rule");
const RuleTester = require("eslint").RuleTester;
const ruleTester = new RuleTester();
ruleTester.run("custom-rule", rule, {
valid: [
// Examples of valid code
],
invalid: [
{
code: "const foo = 1;",
errors: [{ message: "Your custom error message" }],
},
],
});
```
3. Run your tests to ensure your rule works as expected:
```bash
yarn run test:unit
```
## Step 4: Steps to add it to client
1. Go to `app/client/.eslintrc.base.json`
2. Add your `custom-rule` entry to the rules object. e.g.
```javascript
"custom-rule": "warn"
```
## Additional Resources
- [ESLint Plugin Developer Guide](https://eslint.org/docs/developer-guide/working-with-plugins)
- [ESLint Rules API](https://eslint.org/docs/developer-guide/working-with-rules)
- [ESLint Testing Guidelines](https://eslint.org/docs/developer-guide/unit-testing)
Happy linting!

View File

@ -0,0 +1,11 @@
module.exports = {
roots: ["<rootDir>/src"],
transform: {
"^.+\\.(ts)$": [
"ts-jest",
{
isolatedModules: true,
},
],
},
};

View File

@ -0,0 +1,14 @@
{
"name": "@appsmith/eslint-plugin",
"version": "1.0.0",
"private": true,
"main": "dist/index.js",
"scripts": {
"build": "npx tsc",
"build:watch": "npx tsc --watch",
"lint": "yarn g:lint",
"prettier": "yarn g:prettier",
"postinstall": "yarn build",
"test:unit": "yarn g:jest"
}
}

View File

@ -0,0 +1,16 @@
import { objectKeysRule } from "./object-keys/rule";
const plugin = {
rules: {
"object-keys": objectKeysRule,
},
configs: {
recommended: {
rules: {
"@appsmith/object-keys": "warn",
},
},
},
};
module.exports = plugin;

View File

@ -0,0 +1,18 @@
import { TSESLint } from "@typescript-eslint/utils";
import { objectKeysRule } from "./rule";
const ruleTester = new TSESLint.RuleTester();
ruleTester.run("object-keys", objectKeysRule, {
valid: [
{
code: "objectKeys({ 'a': 'b' })",
},
],
invalid: [
{
code: "Object.keys({ 'a': 'b' })",
errors: [{ messageId: "useObjectKeys" }],
},
],
});

View File

@ -0,0 +1,36 @@
import type { TSESLint } from "@typescript-eslint/utils";
export const objectKeysRule: TSESLint.RuleModule<"useObjectKeys"> = {
defaultOptions: [],
meta: {
type: "suggestion",
docs: {
description: "Warns when Object.keys is used instead of objectKeys",
recommended: "warn",
},
schema: [], // No options
messages: {
useObjectKeys:
"Use objectKeys from '@appsmith/utils' package instead of Object.keys",
},
},
create(context) {
return {
CallExpression(node) {
// Check if the callee is Object.keys
if (
node.callee.type === "MemberExpression" &&
node.callee.object.type === "Identifier" &&
node.callee.object.name === "Object" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "keys"
) {
context.report({
node,
messageId: "useObjectKeys",
});
}
},
};
},
};

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"outDir": "dist",
"target": "es2022",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

View File

@ -0,0 +1,3 @@
{
"extends": ["../../.eslintrc.base.json"]
}

View File

@ -0,0 +1,11 @@
module.exports = {
roots: ["<rootDir>/src"],
transform: {
"^.+\\.(ts)$": [
"ts-jest",
{
isolatedModules: true,
},
],
},
};

View File

@ -0,0 +1,14 @@
{
"name": "@appsmith/utils",
"version": "1.0.0",
"description": "This package has all the shared util functions which can be used in different packages e.g. client, rts etc",
"main": "src/index.ts",
"types": "src/index.d.ts",
"scripts": {
"lint": "yarn g:lint",
"prettier": "yarn g:prettier",
"test:unit": "yarn g:jest"
},
"author": "Aman Agarwal <aman@appsmith.com>, Pawan Kumar <pawan@appsmith.com>",
"license": "ISC"
}

View File

@ -0,0 +1 @@
export * from "./object";

View File

@ -0,0 +1 @@
export { objectKeys } from "./keys";

View File

@ -0,0 +1,7 @@
import { objectKeys } from "./keys";
test("objectKeys should return all the keys in the object map pass to it.", () => {
const objectMap = { a: 1, b: 2, c: 3 };
const keys = objectKeys(objectMap);
expect(keys).toStrictEqual(["a", "b", "c"]);
});

View File

@ -4,11 +4,12 @@
* when looping through the keys. This function returns an array of keys with the correct type information.
*
* with classic Object.keys: Object.keys({ a: 1, b: 2 }) -> string[]
* with objeetKeys: objectKeys({ a: 1, b: 2 }) -> ("a" | "b")[]
* with objectKeys: objectKeys({ a: 1, b: 2 }) -> ("a" | "b")[]
*
* @param object
* @returns array of keys with correct type information
*/
export function objectKeys<T extends object>(object: T) {
export function objectKeys<T extends object>(object: T): Array<keyof T> {
return Object.keys(object) as Array<keyof T>;
}

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["./src/**/*"]
}

View File

@ -1,6 +1,7 @@
import { RecaptchaTypes } from "components/constants";
import { COLORS, BUTTON_VARIANTS, objectKeys } from "@appsmith/wds";
import { COLORS, BUTTON_VARIANTS } from "@appsmith/wds";
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
import { objectKeys } from "@appsmith/utils";
import {
BUTTON_WIDGET_DEFAULT_LABEL,
createMessage,

View File

@ -1,6 +1,7 @@
import { capitalize } from "lodash";
import { ValidationTypes } from "constants/WidgetValidation";
import { COLORS, BUTTON_VARIANTS, objectKeys } from "@appsmith/wds";
import { COLORS, BUTTON_VARIANTS } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
export const propertyPaneStyleConfig = [
{

View File

@ -1,4 +1,5 @@
import { BUTTON_VARIANTS, COLORS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS, COLORS } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
export const defaultsConfig = {

View File

@ -1,5 +1,6 @@
import { capitalize } from "lodash";
import { BUTTON_VARIANTS, COLORS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS, COLORS } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
import { ValidationTypes } from "constants/WidgetValidation";
export const propertyPaneStyleConfig = [

View File

@ -1,10 +1,11 @@
import { BUTTON_VARIANTS, COLORS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS, COLORS } from "@appsmith/wds";
import {
BUTTON_WIDGET_DEFAULT_LABEL,
createMessage,
} from "ee/constants/messages";
import { ValidationTypes } from "constants/WidgetValidation";
import { capitalize } from "lodash";
import { objectKeys } from "@appsmith/utils";
export const propertyPaneContentConfig = [
{

View File

@ -1,5 +1,6 @@
import { BUTTON_VARIANTS, COLORS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS, COLORS } from "@appsmith/wds";
import type { WidgetDefaultProps } from "WidgetProvider/constants";
import { objectKeys } from "@appsmith/utils";
export const defaultsConfig = {
label: "Open The Menu…",

View File

@ -1,6 +1,7 @@
import { capitalize } from "lodash";
import { ValidationTypes } from "constants/WidgetValidation";
import { BUTTON_VARIANTS, COLORS, ICONS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS, COLORS, ICONS } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
import type { MenuButtonWidgetProps } from "../../widget/types";

View File

@ -2,7 +2,8 @@ import { ValidationTypes } from "constants/WidgetValidation";
import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants";
import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants";
import { hideByColumnType } from "../../../widget/propertyUtils";
import { BUTTON_VARIANTS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS } from "@appsmith/wds";
import { objectKeys } from "@appsmith/utils";
export default {
sectionName: "General",

View File

@ -1,6 +1,7 @@
import { BUTTON_VARIANTS, COLORS, objectKeys } from "@appsmith/wds";
import { BUTTON_VARIANTS, COLORS } from "@appsmith/wds";
import { ValidationTypes } from "constants/WidgetValidation";
import { capitalize } from "lodash";
import { objectKeys } from "@appsmith/utils";
export const propertyPaneStyleConfig = [
{

View File

@ -228,6 +228,18 @@ __metadata:
languageName: unknown
linkType: soft
"@appsmith/eslint-plugin@workspace:^, @appsmith/eslint-plugin@workspace:packages/eslint-plugin":
version: 0.0.0-use.local
resolution: "@appsmith/eslint-plugin@workspace:packages/eslint-plugin"
languageName: unknown
linkType: soft
"@appsmith/utils@workspace:^, @appsmith/utils@workspace:packages/utils":
version: 0.0.0-use.local
resolution: "@appsmith/utils@workspace:packages/utils"
languageName: unknown
linkType: soft
"@appsmith/wds-headless@workspace:^, @appsmith/wds-headless@workspace:packages/design-system/headless":
version: 0.0.0-use.local
resolution: "@appsmith/wds-headless@workspace:packages/design-system/headless"
@ -277,6 +289,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@appsmith/wds@workspace:packages/design-system/widgets"
dependencies:
"@appsmith/utils": "workspace:^"
"@appsmith/wds-headless": "workspace:^"
"@appsmith/wds-theming": "workspace:^"
"@emotion/css": ^11.11.2
@ -10904,12 +10917,12 @@ __metadata:
linkType: hard
"@types/jest@npm:*, @types/jest@npm:^29.2.3":
version: 29.4.0
resolution: "@types/jest@npm:29.4.0"
version: 29.5.12
resolution: "@types/jest@npm:29.5.12"
dependencies:
expect: ^29.0.0
pretty-format: ^29.0.0
checksum: 23760282362a252e6690314584d83a47512d4cd61663e957ed3398ecf98195fe931c45606ee2f9def12f8ed7d8aa102d492ec42d26facdaf8b78094a31e6568e
checksum: 19b1efdeed9d9a60a81edc8226cdeae5af7479e493eaed273e01243891c9651f7b8b4c08fc633a7d0d1d379b091c4179bbaa0807af62542325fd72f2dd17ce1c
languageName: node
linkType: hard
@ -13143,6 +13156,8 @@ __metadata:
dependencies:
"@appsmith/ads": "workspace:^"
"@appsmith/ads-old": "workspace:^"
"@appsmith/eslint-plugin": "workspace:^"
"@appsmith/utils": "workspace:^"
"@appsmith/wds": "workspace:^"
"@appsmith/wds-theming": "workspace:^"
"@aws-sdk/client-s3": ^3.622.0
@ -24771,15 +24786,6 @@ __metadata:
languageName: node
linkType: hard
"lru-cache@npm:^6.0.0":
version: 6.0.0
resolution: "lru-cache@npm:6.0.0"
dependencies:
yallist: ^4.0.0
checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297
languageName: node
linkType: hard
"lru-cache@npm:^7.7.1":
version: 7.17.0
resolution: "lru-cache@npm:7.17.0"
@ -31983,13 +31989,11 @@ __metadata:
linkType: hard
"semver@npm:7.x, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4":
version: 7.5.4
resolution: "semver@npm:7.5.4"
dependencies:
lru-cache: ^6.0.0
version: 7.6.3
resolution: "semver@npm:7.6.3"
bin:
semver: bin/semver.js
checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3
checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8
languageName: node
linkType: hard
@ -34009,11 +34013,11 @@ __metadata:
linkType: hard
"ts-api-utils@npm:^1.0.1":
version: 1.0.3
resolution: "ts-api-utils@npm:1.0.3"
version: 1.3.0
resolution: "ts-api-utils@npm:1.3.0"
peerDependencies:
typescript: ">=4.2.0"
checksum: 441cc4489d65fd515ae6b0f4eb8690057add6f3b6a63a36073753547fb6ce0c9ea0e0530220a0b282b0eec535f52c4dfc315d35f8a4c9a91c0def0707a714ca6
checksum: c746ddabfdffbf16cb0b0db32bb287236a19e583057f8649ee7c49995bb776e1d3ef384685181c11a1a480369e022ca97512cb08c517b2d2bd82c83754c97012
languageName: node
linkType: hard
@ -34047,7 +34051,7 @@ __metadata:
languageName: node
linkType: hard
"ts-jest@npm:29.1.0, ts-jest@npm:^29.1.0":
"ts-jest@npm:29.1.0":
version: 29.1.0
resolution: "ts-jest@npm:29.1.0"
dependencies:
@ -34080,6 +34084,43 @@ __metadata:
languageName: node
linkType: hard
"ts-jest@npm:^29.1.0":
version: 29.2.4
resolution: "ts-jest@npm:29.2.4"
dependencies:
bs-logger: 0.x
ejs: ^3.1.10
fast-json-stable-stringify: 2.x
jest-util: ^29.0.0
json5: ^2.2.3
lodash.memoize: 4.x
make-error: 1.x
semver: ^7.5.3
yargs-parser: ^21.0.1
peerDependencies:
"@babel/core": ">=7.0.0-beta.0 <8"
"@jest/transform": ^29.0.0
"@jest/types": ^29.0.0
babel-jest: ^29.0.0
jest: ^29.0.0
typescript: ">=4.3 <6"
peerDependenciesMeta:
"@babel/core":
optional: true
"@jest/transform":
optional: true
"@jest/types":
optional: true
babel-jest:
optional: true
esbuild:
optional: true
bin:
ts-jest: cli.js
checksum: 142246f12bb11d5edbfb5a65e298097667e2c4d390e316e356416ce00d3cd157220dbfb9de2a56b38f30776bc92ba59eff9fd78e9345ba4c6712783f27f5475a
languageName: node
linkType: hard
"ts-loader@npm:^9.4.1":
version: 9.4.1
resolution: "ts-loader@npm:9.4.1"