├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── discussion.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── stale.yml └── workflows │ └── ci.yml ├── .gitignore ├── .husky └── pre-commit ├── LICENSE ├── README.md ├── docs ├── .gitkeep ├── Browser Support.md ├── Getting Started.md └── Style Guide.md ├── fixtures └── app │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc │ └── package.json ├── lerna.json ├── package.json ├── packages ├── eslint-config-vtex-react │ ├── .eslintrc │ ├── CHANGELOG.md │ ├── README.md │ ├── demo │ │ ├── .eslintrc │ │ ├── Component.tsx │ │ └── package.json │ ├── gatsby.js │ ├── index.js │ ├── io.js │ ├── native.js │ ├── package.json │ └── rules │ │ ├── react-a11y.js │ │ ├── react-hooks.js │ │ └── react.js ├── eslint-config-vtex │ ├── .eslintrc │ ├── CHANGELOG.md │ ├── README.md │ ├── demo │ │ ├── index.js │ │ ├── index.ts │ │ └── package.json │ ├── index.js │ ├── lib │ │ └── utils.js │ ├── package.json │ └── rules │ │ ├── best-practices.js │ │ ├── errors.js │ │ ├── imports.js │ │ ├── node.js │ │ ├── prettier.js │ │ ├── style.js │ │ ├── tests.js │ │ ├── typescript.js │ │ └── variables.js ├── eslint-plugin-vtex │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ └── rules │ │ │ ├── consistent-props-type.md │ │ │ ├── prefer-early-return.md │ │ │ └── prefer-use-effect-named-callback.md │ ├── package.json │ ├── src │ │ ├── configs │ │ │ └── recommended.ts │ │ ├── createRule.ts │ │ ├── index.ts │ │ ├── rules │ │ │ ├── __tests__ │ │ │ │ ├── consistent-props-type.test.ts │ │ │ │ ├── prefer-early-return.test.ts │ │ │ │ └── prefer-use-effect-named-callback.test.ts │ │ │ ├── consistent-props-type.ts │ │ │ ├── prefer-early-return.ts │ │ │ └── prefer-use-effect-named-callback.ts │ │ └── utils │ │ │ ├── estree │ │ │ ├── getFunctionNodeName.ts │ │ │ ├── isFunctionNode.ts │ │ │ └── isInsideAnotherFunction.ts │ │ │ └── react │ │ │ └── isComponentName.ts │ ├── test │ │ └── babel.config.js │ └── tsconfig.json ├── prettier-config │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ └── package.json └── tsconfig │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ └── tsconfig.json ├── scripts └── bootstrap-typescript.sh ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # rules/ 2 | node_modules/ 3 | coverage/ 4 | dist/ 5 | fixtures/ 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "vtex", 3 | "root": true 4 | } 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @vtex/front-end-dx-guild 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **To Reproduce** 10 | Steps to reproduce the behavior: 11 | 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | 25 | - OS: [e.g. iOS] 26 | - Browser [e.g. chrome, safari] 27 | - Version [e.g. 22] 28 | 29 | **Smartphone (please complete the following information):** 30 | 31 | - Device: [e.g. iPhone6] 32 | - OS: [e.g. iOS8.1] 33 | - Browser [e.g. stock browser, safari] 34 | - Version [e.g. 22] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/discussion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Discussion 3 | about: Questions and discussions about best practices and patterns 4 | --- 5 | 6 | **What is to be discussed?** 7 | 8 | 9 | **Describe the solution you'd like** 10 | 11 | 12 | **How we do this today?** 13 | 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 8 | 9 | **Describe the solution you'd like** 10 | A clear and concise description of what you want to happen. 11 | 12 | **Describe alternatives you've considered** 13 | A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | **Additional context** 16 | Add any other context or screenshots about the feature request here. 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | 5 | ## How to test 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 7 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: false 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - todo 16 | - documentation 17 | - fresh 18 | 19 | # Set to true to ignore issues in a project (defaults to false) 20 | exemptProjects: false 21 | 22 | # Set to true to ignore issues in a milestone (defaults to false) 23 | exemptMilestones: false 24 | 25 | # Set to true to ignore issues with an assignee (defaults to false) 26 | exemptAssignees: false 27 | 28 | # Label to use when marking as stale 29 | staleLabel: stale 30 | 31 | # Comment to post when marking as stale. Set to `false` to disable 32 | markComment: > 33 | This issue has been automatically marked as stale because it has not had 34 | recent activity. It will be closed if no further activity occurs. 35 | 36 | You can add the `fresh` label to prevent me from taking any action. 37 | 38 | If this is a discussion thread, the most voted option will be final. Thank you for your contributions. 39 | 40 | # Comment to post when removing the stale label. 41 | # unmarkComment: > 42 | # Your comment here. 43 | 44 | # Comment to post when closing a stale Issue or Pull Request. 45 | # closeComment: > 46 | # Your comment here. 47 | 48 | # Limit the number of actions per hour, from 1-30. Default is 30 49 | limitPerRun: 30 50 | # Limit to only `issues` or `pulls` 51 | only: issues 52 | 53 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 54 | # pulls: 55 | # daysUntilStale: 30 56 | # markComment: > 57 | # This pull request has been automatically marked as stale because it has not had 58 | # recent activity. It will be closed if no further activity occurs. Thank you 59 | # for your contributions. 60 | 61 | # issues: 62 | # exemptLabels: 63 | # - confirmed 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | Lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '18' 19 | - name: Install dependencies 20 | run: yarn install --frozen-lockfile 21 | 22 | - name: Run build 23 | run: yarn build 24 | 25 | - name: Run lint 26 | run: yarn lint 27 | 28 | 'E2E_Tests': 29 | runs-on: ubuntu-latest 30 | services: 31 | verdaccio: 32 | image: verdaccio/verdaccio:4.0.0 33 | ports: 34 | - 4873:4873 35 | steps: 36 | - uses: actions/checkout@v1 37 | - uses: actions/setup-node@v1 38 | with: 39 | node-version: '18' 40 | 41 | - name: Install dependencies 42 | run: yarn install --frozen-lockfile 43 | 44 | - name: Verdaccio login 45 | run: | 46 | npx npm-cli-adduser \ 47 | -u username \ 48 | -p pass \ 49 | -e username@email.com \ 50 | -r http://localhost:4873 || true 51 | 52 | - name: Configure git 53 | run: | 54 | git config --global user.name 'Verdaccio User' 55 | git config --global user.email 'username@email.com' 56 | 57 | - name: Get changed packages 58 | id: changed-packages 59 | run: | 60 | changed_packages=$(yarn -s lerna list --since | sed 's/$/\@0.0.0-verdaccio/' | xargs echo) 61 | 62 | echo "::set-output name=PACKAGES::$changed_packages" 63 | 64 | - name: Publish packages 65 | run: | 66 | yarn lerna publish \ 67 | '0.0.0-verdaccio' \ 68 | --registry http://localhost:4873 \ 69 | --no-git-tag-version \ 70 | --no-push \ 71 | --yes 72 | 73 | - uses: actions/setup-node@v1 74 | with: 75 | node-version: '14' 76 | 77 | - name: Install dependencies 78 | run: yarn --cwd fixtures/app install --no-lockfile 79 | 80 | - name: Install packages and run test 81 | if: steps.changed-packages.outputs.PACKAGES 82 | run: | 83 | yarn --cwd fixtures/app add \ 84 | --registry http://localhost:4873 \ 85 | -D ${{ steps.changed-packages.outputs.PACKAGES }} 86 | yarn --cwd fixtures/app test 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### SublimeText ### 2 | *.sublime-workspace 3 | 4 | ### VS Code ### 5 | *.vscode 6 | 7 | ### OSX ### 8 | .DS_Store 9 | .AppleDouble 10 | .LSOverride 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear on external disk 17 | .Spotlight-V100 18 | .Trashes 19 | 20 | ### Windows ### 21 | # Windows image file caches 22 | Thumbs.db 23 | ehthumbs.db 24 | 25 | # Folder config file 26 | Desktop.ini 27 | 28 | # Recycle Bin used on file shares 29 | $RECYCLE.BIN/ 30 | 31 | # App specific 32 | node_modules/ 33 | docs/_book/ 34 | .tmp 35 | .idea 36 | npm-debug.log 37 | .build/ 38 | dist 39 | build 40 | lerna-debug.log 41 | yarn-error.log 42 | *.orig 43 | package-lock.json 44 | config-dump.json 45 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 VTEX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Typescript Standards 2 | 3 | Main repository for VTEX's Typescript standards. Issues should be used to start discussions about standards and patterns. PRs are welcome after being rightfully discussed. 4 | 5 | ## Content 6 | 7 | ##### `docs/` 8 | 9 | - [Style Guide](/docs/Style%20Guide.md) - VTEX Javascript/Typescript style guide 10 | - [Getting Started](/docs/Getting%20Started.md) - Getting starting guide to configure and automate our tooling process 11 | - [Browser Support](/docs/Browser%20Support.md) - VTEX browser support documentation 12 | 13 | ##### `packages/` 14 | 15 | - [eslint-config-vtex](/packages/eslint-config-vtex) - base eslint rule preset 16 | - [eslint-config-vtex-react](/packages/eslint-config-vtex-react) - eslint rule preset for react projects 17 | - [eslint-plugin-vtex](/packages/eslint-plugin-vtex) - eslint plugin with VTEX custom rules 18 | - [prettier-config](/packages/prettier-config) - prettier preset of all front-end projects 19 | - [tsconfig](/packages/tsconfig) - base tsconfig for all typescript projects 20 | 21 | ## Add these packages to a new project 22 | 23 | ### Bootstrap on a new node and typescript project 24 | 25 | > If you're not using Bash, change `/bin/bash` to your shell's executable path. 26 | 27 | ```bash 28 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/vtex/typescript/master/scripts/bootstrap-typescript.sh)" 29 | ``` 30 | 31 | > **Warning**: This will **override** the following configuration files: `.prettierrc`, `.eslintrc` and `tsconfig.json`. 32 | 33 | ## Contributing 34 | 35 | Nothing is written in stone, so if there's some kind of rule or pattern that you wish to discuss or implement, we encourage an open discussion via this [repository issues](/issues). The project use labels to keep everything organized and easy-to-find: 36 | 37 | - `discussion` - discussion thread; 38 | - `style` - related to code style, something that there's no wrong or right; 39 | - `practices/patterns` - related to good coding practices that should be standardized and documented; 40 | - `documentation` - a generic documentation issue; 41 | - `todo` - common TODO item, with no explicit SLA. 42 | 43 | Feel free to use more than one label in an issue, but try keeping them semantic to help developers. 44 | 45 | ### Working with the packages 46 | 47 | This project uses `lerna` with `yarn` workspaces, so it's highly recommended to read at least the [Lerna documentation](https://github.com/lerna/lerna). 48 | 49 | #### Bootstrap the repository 50 | 51 | Running `yarn` will automatically install all the dependencies for all packages and link them together when needed. 52 | 53 | ```bash 54 | yarn 55 | ``` 56 | 57 | #### Executing commands 58 | 59 | To run a `package.json` script on all packages, you can use: 60 | 61 | ```bash 62 | lerna run {scriptName} 63 | # will run the scriptName script on all package directories 64 | ``` 65 | 66 | If you want to only run on some packages, pass a `--scope=package-name` to the command above. For more instructions, see the [`lerna run` documentation](https://github.com/lerna/lerna/tree/master/commands/run#readme). 67 | 68 | ```bash 69 | lerna run --scope="eslint-*" test 70 | # run the test script on all packages that match the pattern above 71 | ``` 72 | 73 | The same can be done for regular shell programs with the `lerna exec` command: 74 | 75 | ```bash 76 | lerna exec "pwd" 77 | # will print the pwd of all package directories 78 | ``` 79 | 80 | _Note: The quotes are not needed if your command doesn't have a string with spaces._ 81 | 82 | #### Releasing new versions 83 | 84 | For every release, there should be at least one new changelog entry for every modified package. This repository follows the [keep a changelog](https://keepachangelog.com/en/1.0.0/) format. The [`chan`](https://github.com/geut/chan) CLI can be used to help adding changelog entries: 85 | 86 | ```bash 87 | chan fixed "Fix that nasty potato bug" 88 | ``` 89 | 90 | It's also possible to run the command on multiple packages with the `lerna exec` command: 91 | 92 | ```bash 93 | lerna exec "chan fixed 'Fix that nasty potato bug'" 94 | # note the quotes 95 | ``` 96 | 97 | Every package has a `version` script that will automatically update their changelog with the new version and entries whenever `lerna publish` or `lerna version` is used. 98 | 99 | Relevant commands: 100 | 101 | - [`lerna version`](https://github.com/lerna/lerna/blob/master/commands/version/README.md) - Only update versions without publishing the packages. 102 | - [`lerna publish`](https://github.com/lerna/lerna/blob/master/commands/publish/README.md) - Update the version and publish the packages. 103 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtex/typescript/e97bc405c546a3b8f0faf6c49d5cf4b7bc950c2a/docs/.gitkeep -------------------------------------------------------------------------------- /docs/Browser Support.md: -------------------------------------------------------------------------------- 1 | # Browser Support 2 | 3 | 4 | 5 | 6 | 7 | - [Guidelines](#guidelines) 8 | - [Polyfills](#polyfills) 9 | 10 | 11 | 12 | VTEX browser support is split in two contexts: `storefront` and `admin areas`. 13 | 14 | For `storefronts`, we strive to support all major browsers all the way to IE 11: 15 | 16 | ```text 17 | # Browserslist example (date 2020-01-29) 18 | IE 11 19 | Edge 17+ 20 | Safari 8+ 21 | Chrome 77+ 22 | Firefox 70+ 23 | ``` 24 | 25 | | [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | 26 | | --------- | --------- | --------- | --------- | 27 | | IE11, Edge| last 10 versions| last 10 versions| last 4 versions 28 | 29 | _Note: even though we support older browsers such as IE 11 and Safari 8, the layout may slightly vary due to CSS browser support._ 30 | 31 | For `admin areas`, the main targets are evergreen browsers, such as Firefox, Chrome, the new Edge, etc: 32 | 33 | ```text 34 | # Browserslist example (date 2020-01-29) 35 | Edge 17+ 36 | Safari 12.1 37 | Chrome 77+ 38 | Firefox 70+ 39 | ``` 40 | 41 | | [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | 42 | | --------- | --------- | --------- | --------- | 43 | | Edge| last 10 versions| last 10 versions| last 2 versions 44 | 45 | ## Guidelines 46 | 47 | It's usually in a dev's heart the desire to use the newest Javascript feature or a new fancy CSS property, such as `display: grid`, `position: sticky`, `Map()`, `Set()`, etc. However, _browser support_ and _cross-browser compatibility_ are the first concerns that have to come to the developer's mind. Make sure to always check `MDN` and `Can I Use` for the current support of the desired functionality. 48 | 49 | - [MDN - Mozilla Developer Network](https://developer.mozilla.org/) 50 | - [Can I Use](https://caniuse.com/) 51 | 52 | If the functionality is not well supported and yet it's needed for some _important_ reason, a [polyfill or ponyfill](https://ponyfoo.com/articles/polyfills-or-ponyfills) can help solving the problem. Keep in mind that polyfills add extra weight to the store assets and not always perform as good as their native implementations. 53 | 54 | ## Polyfills 55 | 56 | > A polyfill is a piece of code used to provide modern functionality on older browsers that do not natively support it. 57 | 58 | To be possible to write and use modern Javascript and CSS features while supporting all the way to IE 11, some polyfills are automatically added **only** to VTEX IO stores: 59 | 60 | - [`Array.from()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 61 | - [`Array.prototype.fill()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill) 62 | - [`Array.prototype.find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) 63 | - [`Array.prototype.findIndex()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) 64 | - [`Array.prototype.includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) 65 | - [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) 66 | - [`Function.name`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name) 67 | - [`IntersectionObserver()`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) 68 | - [`Intl.~locale.${locale}`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Locale) 69 | - [`Map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 70 | - [`Number.isInteger()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger) 71 | - [`Number.isNaN()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) 72 | - [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 73 | - [`Object.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) 74 | - [`Object.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) 75 | - [`Object.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values) 76 | - [`Promise()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 77 | - [`Set()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) 78 | - [`String.prototype.endsWith()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith) 79 | - [`String.prototype.includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) 80 | - [`String.prototype.startsWith()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith) 81 | - [`Symbol()`](https://developer.mozilla.org/en-US/docs/Glossary/Symbol) 82 | - [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) 83 | - [`WeakMap()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) 84 | -------------------------------------------------------------------------------- /docs/Getting Started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | 4 | 5 | 6 | 7 | - [IO Apps](#io-apps) 8 | - [Manual Setup](#manual-setup) 9 | - [1 - Installing and configuring the essential packages](#1-installing-and-configuring-the-essential-packages) 10 | - [1.1 - For `react` projects](#11-for-react-projects) 11 | - [1.2 - For `Typescript` projects](#12-for-typescript-projects) 12 | - [2 - Creating tasks](#2-creating-tasks) 13 | - [3 - Automating](#3-automating) 14 | - [CI Automation](#ci-automation) 15 | 16 | 17 | 18 | ## IO Apps 19 | 20 | For IO applications, the toolbelt command `vtex setup` should get you going. It installs the appropriate linting and formatting packages for your project. 21 | 22 | If you're configuring an already existing project, running `vtex setup` may not be enough and you'll need to update your project's files. Check the manual setup tutorial below to see these steps. 23 | 24 | ## Manual Setup 25 | 26 | VTEX `Javascript`/`Typescript` style guide consists of some separate packages: 27 | 28 | - [`@vtex/prettier-config`](/packages/prettier-config) - VTEX's unified `prettier` configuration. Responsible for automatically formatting all javascript code. 29 | - [`eslint-config-vtex`](/packages/eslint-config-vtex) - VTEX's base `eslint` configuration. Responsible for static analyzing every `Javascript` code and guaranteeing a baseline of code quality and good practices. 30 | - [`eslint-config-vtex-react`](/packages/eslint-config-vtex-react) - An extension of the base configuration with specific rules for react applications. 31 | - [`@vtex/tsconfig`](/packages/tsconfig) - VTEX's unified `tsconfig.json` preset for every `Typescript` project. 32 | 33 | Every project which uses `Javascript` or `Typescript` must have at least `eslint-config-vtex` and `@vtex/prettier-config` installed. Please check their _README_ for any specific package questions. 34 | 35 | ### 1 - Installing and configuring the essential packages 36 | 37 | First of all we need to install the _basis of everything_: 38 | 39 | ```bash 40 | $ yarn add -D eslint prettier eslint-config-vtex @vtex/prettier-config 41 | ``` 42 | 43 | Then create their configuration files at the root of your project: 44 | 45 | `.eslintrc`: 46 | 47 | ```jsonc 48 | { "extends": "vtex" } 49 | ``` 50 | 51 | `.prettierrc`: 52 | 53 | ```jsonc 54 | "@vtex/prettier-config" 55 | ``` 56 | 57 | > _Note: yes, it's a string literal in a JSON file._ 58 | 59 | #### 1.1 - For `react` projects 60 | 61 | You can install `eslint-config-vtex-react` instead of the base configuration. 62 | 63 | Then use the preset in your project's `.eslintrc`: 64 | 65 | `.eslintrc`: 66 | 67 | ```jsonc 68 | { "extends": "vtex-react" } 69 | ``` 70 | 71 | We also provide two other presets: 72 | 73 | - `vtex-react/io` - Specific for VTEX IO React apps. 74 | - `vtex-react/native` - Specific for `react-native` apps. 75 | 76 | Make sure to use the appropriate preset for your context. 77 | 78 | #### 1.2 - For `Typescript` projects 79 | 80 | ```bash 81 | $ yarn add -D typescript @vtex/tsconfig 82 | ``` 83 | 84 | Then create a `tsconfig.json` at the root of your project which extends the config: 85 | 86 | ```jsonc 87 | { 88 | "extends": "@vtex/tsconfig" 89 | } 90 | ``` 91 | 92 | ### 2 - Creating tasks 93 | 94 | After installing the essential tools, it's time for creating shortcut tasks for us to be able to lint and format our projects. Add the following scripts to your project's **root** `package.json`: 95 | 96 | ```jsonc 97 | { 98 | //..., 99 | "scripts": { 100 | //..., 101 | "lint": "eslint --ext js,jsx,ts,tsx .", 102 | "format": "prettier --write \"**/*.{ts,js,json}\"" 103 | }, 104 | ... 105 | } 106 | ``` 107 | 108 | Now you have two (and a half) commands: 109 | 110 | - `yarn lint` - Will lint all your files according to VTEX standards. 111 | - `yarn lint --fix` - Will lint all your project files and fix auto-fixable errors along the way :tada:. 112 | - `yarn format` - Will format all your files according to VTEX standards. 113 | 114 | ### 3 - Automating 115 | 116 | Ok, our tools are installed, configured, we have a quick and easy-to-use commands to adequate our project to VTEX's standard, what more can be done? **Automation!** 117 | 118 | A developer shouldn't have to think about linting and formatting, usually their IDE is configured to automatically do both things while someone is coding. However, not every IDE is equal nor rightly configured, so we need a way to automate these process to guarantee VTEX's standards. 119 | 120 | Install `husky` and `lint-staged` in the root of your project: 121 | 122 | ```bash 123 | $ yarn add -D husky lint-staged 124 | ``` 125 | 126 | - `husky` is used to easily configure [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) 127 | - `lint-staged` is used together with `husky` to lint and format _ONLY_ files that are being commited. No need to run on the whole project. 128 | 129 | Then, add these configuration objects to your project's **root** `package.json`: 130 | 131 | ```jsonc 132 | { 133 | //..., 134 | "husky": { 135 | "hooks": { 136 | "pre-commit": "lint-staged" 137 | } 138 | }, 139 | "lint-staged": { 140 | "*.{ts,js,tsx,jsx}": [ 141 | "eslint --fix", 142 | "prettier --write" 143 | ], 144 | "*.json": [ 145 | "prettier --write" 146 | ] 147 | }, 148 | ... 149 | } 150 | ``` 151 | 152 | Now, everytime someone is `git commit`ing, `lint-staged` will guarantee that every staged file is following VTEX's standards. 153 | 154 | > _Note: if for some reason you need to commit files that doesn't follow the standards, you can pass the [`--no-verify`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---no-verify) flag to the `git commit` command. We trust you know what you're doing._ 155 | 156 | ## CI Automation 157 | 158 | > Work in progress 159 | -------------------------------------------------------------------------------- /fixtures/app/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | !.eslintrc.js 3 | -------------------------------------------------------------------------------- /fixtures/app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: 'vtex-react', 4 | reportUnusedDisableDirectives: true, 5 | } 6 | -------------------------------------------------------------------------------- /fixtures/app/.gitignore: -------------------------------------------------------------------------------- 1 | yarn.lock 2 | -------------------------------------------------------------------------------- /fixtures/app/.prettierrc: -------------------------------------------------------------------------------- 1 | "@vtex/prettier-config" 2 | -------------------------------------------------------------------------------- /fixtures/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "test": "eslint --ext js,ts,jsx,tsx,mjs ." 8 | }, 9 | "devDependencies": { 10 | "@vtex/prettier-config": "*", 11 | "eslint": "^7", 12 | "eslint-config-vtex": "*", 13 | "eslint-config-vtex-react": "*", 14 | "prettier": "^2.5.1", 15 | "typescript": "^4.6.2" 16 | }, 17 | "dependencies": { 18 | "react": "^17.0.2", 19 | "react-dom": "^17.0.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "stream": true, 6 | "packages": ["packages/*"], 7 | "command": { 8 | "publish": { 9 | "ignoreChanges": [ 10 | "{.*,*}{.config,rc,ignore}{,.js}", 11 | "LICENSE", 12 | "lerna.json" 13 | ], 14 | "message": "chore(release): :robot: publish", 15 | "registry": "https://registry.npmjs.org" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vtex/typescript", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/*" 6 | ], 7 | "contributors": [ 8 | "Breno Calazans ", 9 | "Christian Kaisermann " 10 | ], 11 | "license": "MIT", 12 | "repository": "vtex/typescript", 13 | "bugs": { 14 | "url": "https://github.com/vtex/typescript/issues" 15 | }, 16 | "scripts": { 17 | "build": "lerna run build", 18 | "clean": "lerna clean && lerna run clean", 19 | "format:check": "prettier --list-different \"./**/*.{ts,js,json}\"", 20 | "format": "prettier --write \"**/*.{ts,js,json}\"", 21 | "lint": "eslint --ext .js,.ts,.jsx,.tsx .", 22 | "test": "lerna run test --concurrency 1", 23 | "prepare": "husky install" 24 | }, 25 | "prettier": "@vtex/prettier-config", 26 | "lint-staged": { 27 | "*.{ts,js}": [ 28 | "eslint --fix", 29 | "prettier --write" 30 | ], 31 | "*.json": [ 32 | "prettier --write" 33 | ] 34 | }, 35 | "devDependencies": { 36 | "@geut/chan": "^2.1.1", 37 | "eslint": "^8.11.0", 38 | "husky": "^7.0.0", 39 | "lerna": "^3.18.4", 40 | "lint-staged": "^12.3.5", 41 | "prettier": "^2.2.0", 42 | "typescript": "^4.6.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["./index.js"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [9.0.3] - 2022-10-28 10 | ### Removed 11 | - Deprecated eslint plugin and preset for VTEX Admin UI 12 | 13 | ## [9.0.0] - 2022-03-15 14 | ### Changed 15 | - Include `eslint` major 8 in peer dependencies range. 16 | 17 | ## [8.0.0] - 2021-03-25 18 | ### Changed 19 | - `eslint-config-prettier` updated 6.15.0 to 8.1.0 20 | - Requires at least ESLint 7.0.0 now, because `eslint-config-prettier` drops support for ESLint 6, [see here](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md#version-700-2020-12-05) 21 | 22 | ## [6.9.4] - 2021-01-29 23 | ### Changed 24 | - Update typescript-eslint dependencies 25 | 26 | ## 6.9.1 - 2020-12-09 27 | ### Fixed 28 | - Wrong preset extension. 29 | 30 | ## 6.9.0 - 2020-12-09 31 | ### Changed 32 | - Move hooks config to `react-hooks` file. 33 | 34 | ## 6.8.6 - 2020-12-03 35 | ### Fixed 36 | - Add `env.browser: true` to react preset. 37 | 38 | ## 6.8.4 - 2020-11-25 39 | ### Added 40 | - `useCustomClasses` hook to `react-hooks/exhaustive-deps` rule to io. 41 | 42 | ## 6.8.2 - 2020-11-19 43 | ### Added 44 | - Ignore jsx-filename rule for `gatsby-browser.js`. 45 | 46 | ## 6.8.1 - 2020-11-19 47 | ### Fixed 48 | - Missing entrypoing for gatsby preset. 49 | 50 | ## 6.8.0 - 2020-11-19 51 | ### Added 52 | - `vtex-react/gatsby` preset. 53 | 54 | ## 6.7.12 - 2020-11-18 55 | ### Changed 56 | - Allow components to have all capitalized names. 57 | 58 | ## 6.7.9 - 2020-10-21 59 | ### Changed 60 | - Repo url in package.json 61 | 62 | ## 6.7.7 - 2020-08-21 63 | ### Changed 64 | - Update hooks preset to v4 65 | 66 | ## 6.7.1 - 2020-07-20 67 | ### Changed 68 | - Message from imports of `ramda` and `lodash`. 69 | 70 | ## 6.5.0 - 2020-06-01 71 | ### Added 72 | - `prettier` `v2.0` as acceptable dependency. 73 | 74 | ## 6.3.1 - 2020-03-30 75 | ### Fixed 76 | - Release cycle. 77 | 78 | ## 6.2.2 - 2020-03-30 79 | ### Fixed 80 | - Allow to use ramda/lodash and node libraries in a testing context. 81 | 82 | ## [6.2.1] - 2020-02-28 83 | ### Fixed 84 | - Add sub presets to package bundle 85 | 86 | ## [6.2.0] - 2020-02-28 87 | ### Added 88 | - Add IO and Native presets 89 | 90 | ## [6.1.1] - 2020-02-14 91 | ### Fixed 92 | - Use explicitly latest react version 93 | 94 | ## [6.0.3] - 2020-01-24 95 | ### Fixed 96 | - Changelog on deploy workflow again. 97 | 98 | ## [6.0.2] - 2020-01-24 99 | ### Fixed 100 | - Changelog on deploy workflow. 101 | 102 | ## [6.0.1] - 2020-01-24 103 | ### Fixed 104 | - Missing files in packages. 105 | 106 | ## 6.0.0 - 2020-01-24 107 | ### Changed 108 | - Whole project structure. 109 | 110 | ### Added 111 | - New rules to ensure best practices. 112 | - New rules to enforce a default coding style. 113 | 114 | ## 5.1.0 - 2019-10-31 115 | ### Added 116 | - Rule to warn when a native node package is imported. 117 | 118 | ## 5.0.1 - 2019-07-25 119 | ### Changed 120 | - Updated `eslint-plugin-react` version.a 121 | 122 | ## 5.0.0 - 2019-07-15 123 | ### Changed 124 | - Updated `eslint-config-vtex` dependency version. 125 | 126 | ## 4.1.0 - 2019-04-29 127 | ### Fixed 128 | - Disabled all rules conflicting with `eslint-config-prettier/react`. 129 | 130 | ## 4.0.0 - 2019-03-15 131 | ### Changed 132 | - Add support for typescript by extending `eslint-config-vtex` v10. 133 | - Add more rules for a11y in components. 134 | 135 | [Unreleased]: https://github.com/vtex/typescript/compare/v9.0.3...HEAD 136 | [9.0.3]: https://github.com/vtex/typescript/compare/v9.0.0...v9.0.3 137 | [9.0.0]: https://github.com/vtex/typescript/compare/v8.0.0...v9.0.0 138 | [8.0.0]: https://github.com/vtex/typescript/compare/v6.9.4...v8.0.0 139 | [6.9.4]: https://github.com/vtex/typescript/compare/v6.9.1...v6.9.4 140 | [6.2.1]: https://github.com/vtex/javascript/compare/v6.2.0...v6.2.1 141 | [6.2.0]: https://github.com/vtex/javascript/compare/v6.1.1...v6.2.0 142 | [6.1.1]: https://github.com/vtex/javascript/compare/v6.0.3...v6.1.1 143 | [6.0.3]: https://github.com/vtex/js-standards/compare/v6.0.2...v6.0.3 144 | [6.0.2]: https://github.com/vtex/js-standards/compare/v6.0.1...v6.0.2 145 | [6.0.1]: https://github.com/vtex/js-standards/compare/v6.0.0...v6.0.1 146 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/README.md: -------------------------------------------------------------------------------- 1 | # `eslint-config-vtex-react` 2 | 3 | This package provides VTEX's `.eslintrc` for React projects as an extensible shared config. 4 | 5 | Not all React applications live in the same context, different rules are needed for different scenarios. To solve this, three presets are provided: 6 | 7 | - `vtex-react` - Base react configuration. Can be used in any React project. 8 | - `vtex-react/io` - To be used in VTEX IO React applications. 9 | - `vtex-react/native` - To be used in `react-native` applications. 10 | 11 | ## Installation 12 | 13 | Assuming you already have ESLint installed, run: 14 | 15 | ```bash 16 | yarn add -D eslint-config-vtex-react typescript prettier 17 | ``` 18 | 19 | ## Usage 20 | 21 | After installing the module, just add it to your `extends` array inside your `.eslintrc`. 22 | 23 | ```jsonc 24 | // .eslintrc 25 | { 26 | "extends": ["vtex-react"] 27 | } 28 | ``` 29 | 30 | As any other eslint preset, it's possible to override some rules and configurations. We encourage trying to keep the closest possible to the preset rules, but every project is different and sometimes overriding is needed, use it carefully. 31 | 32 | ## References 33 | 34 | - [`eslint-config-vtex` documentation](https://github.com/vtex/javascript/blob/master/packages/eslint-config-vtex/README.md) 35 | - [`eslint-plugin-jsx-a11y` documentation](https://github.com/evcohen/eslint-plugin-jsx-a11y#readme) 36 | - [`eslint-plugin-react` documentation](https://github.com/yannickcr/eslint-plugin-react) 37 | - [`eslint-plugin-react-hooks` documentation](https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks) 38 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/demo/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../index.js" 3 | } 4 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/demo/Component.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react' 2 | import React from 'react' 3 | 4 | const AnotherComponent: FC = () => { 5 | return null 6 | } 7 | 8 | const Component: FC = () => { 9 | return ( 10 |
11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | export default Component 18 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": "index.js", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "@types/react": "^17.0.0" 7 | }, 8 | "dependencies": { 9 | "react": "^17.0.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/gatsby.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./index.js'], 3 | overrides: [ 4 | // gatsby config files 5 | { 6 | files: ['gatsby-{ssr,browser,node,config}.{js,jsx,ts,tsx}'], 7 | rules: { 8 | 'global-require': 'off', 9 | '@typescript-eslint/no-require-imports': 'off', 10 | '@typescript-eslint/no-var-requires': 'off', 11 | }, 12 | }, 13 | // allowed to use node libs 14 | { 15 | files: ['gatsby-{node,config,ssr}.{js,jsx,ts,tsx}'], 16 | rules: { 17 | 'import/no-nodejs-modules': 'off', 18 | }, 19 | }, 20 | // needs to be .js/.js 21 | { 22 | files: ['gatsby-browser.{js,ts}'], 23 | rules: { 24 | 'react/jsx-filename-extension': 'off', 25 | }, 26 | }, 27 | ], 28 | } 29 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint-config-vtex', 4 | './rules/react.js', 5 | './rules/react-hooks.js', 6 | './rules/react-a11y.js', 7 | ], 8 | overrides: [ 9 | { 10 | files: '**/*.{ts,tsx,js,jsx}', 11 | excludedFiles: ['*{_,.}{test,spec}.{ts,tsx,js,jsx}', '__tests__/**/*'], 12 | // activate these rules in react files not related to testing 13 | rules: { 14 | // prevent people from importing native Node libs (url/path/crypto, etc) on browser env 15 | 'import/no-nodejs-modules': 'error', 16 | }, 17 | }, 18 | ], 19 | } 20 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/io.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./index.js'], 3 | overrides: [ 4 | { 5 | files: '**/*.{ts,tsx,js,jsx}', 6 | excludedFiles: ['*{_,.}{test,spec}.{ts,tsx,js,jsx}', '__tests__/**/*'], 7 | // activate these rules in react files not related to testing 8 | rules: { 9 | // Disallow specified import patterns 10 | // https://eslint.org/docs/rules/no-restricted-imports 11 | 'no-restricted-imports': [ 12 | 'error', 13 | { 14 | paths: [ 15 | { 16 | name: 'lodash', 17 | message: 18 | 'You might not need lodash, try using the default functions from the browser', 19 | }, 20 | { 21 | name: 'ramda', 22 | message: 23 | 'You might not need ramda, try using the default functions from the browser', 24 | }, 25 | ], 26 | // Patterns don't support messages yet :( 27 | // https://github.com/eslint/eslint/issues/11843 28 | patterns: [], 29 | }, 30 | ], 31 | 32 | // Add useCustomClasses hook from vtex-apps/css-handles 33 | // https://eslint.org/docs/rules/no-restricted-imports 34 | 'react-hooks/exhaustive-deps': [ 35 | 'warn', 36 | { 37 | // regexp 38 | additionalHooks: '(useCustomClasses)', 39 | }, 40 | ], 41 | }, 42 | }, 43 | ], 44 | } 45 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/native.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./index.js'], 3 | } 4 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-vtex-react", 3 | "version": "9.1.0", 4 | "description": "VTEX's eslint config for React", 5 | "main": "index.js", 6 | "scripts": { 7 | "eslint-check": "eslint-config-prettier index.js", 8 | "dump-config": "eslint-config-prettier index.js > config-dump.json", 9 | "version": "chan release $npm_package_version && git add CHANGELOG.md" 10 | }, 11 | "files": [ 12 | "index.js", 13 | "io.js", 14 | "native.js", 15 | "gatsby.js", 16 | "rules/" 17 | ], 18 | "keywords": [ 19 | "eslint", 20 | "config", 21 | "vtex", 22 | "react" 23 | ], 24 | "contributors": [ 25 | "Breno Calazans ", 26 | "Christian Kaisermann " 27 | ], 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/vtex/javascript/issues" 31 | }, 32 | "homepage": "https://github.com/vtex/typescript", 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/vtex/typescript.git", 36 | "directory": "packages/eslint-config-vtex-react" 37 | }, 38 | "dependencies": { 39 | "eslint-config-vtex": "^15.1.0", 40 | "eslint-plugin-jsx-a11y": "^6.3.1", 41 | "eslint-plugin-react": "^7.20.6", 42 | "eslint-plugin-react-hooks": "^4.1.0" 43 | }, 44 | "peerDependencies": { 45 | "eslint": "^7 || ^8", 46 | "prettier": "^1.18.2 || ^2.0.4", 47 | "typescript": "^3.8 || ^4.0.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/rules/react-a11y.js: -------------------------------------------------------------------------------- 1 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/ 2 | module.exports = { 3 | extends: ['plugin:jsx-a11y/recommended'], 4 | plugins: ['jsx-a11y'], 5 | parserOptions: { 6 | ecmaFeatures: { 7 | jsx: true, 8 | }, 9 | }, 10 | rules: { 11 | // Enforce that anchors have content 12 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-has-content.md 13 | 'jsx-a11y/anchor-has-content': 'error', 14 | 15 | // Require ARIA roles to be valid and non-abstract 16 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-role.md 17 | 'jsx-a11y/aria-role': 'error', 18 | 19 | // Enforce all aria-* props are valid. 20 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-props.md 21 | 'jsx-a11y/aria-props': 'error', 22 | 23 | // Enforce ARIA state and property values are valid. 24 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-proptypes.md 25 | 'jsx-a11y/aria-proptypes': 'error', 26 | 27 | // Enforce that elements that do not support ARIA roles, states, and 28 | // properties do not have those attributes. 29 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-unsupported-elements.md 30 | 'jsx-a11y/aria-unsupported-elements': 'error', 31 | 32 | // Enforce that all elements that require alternative text have meaningful information 33 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/alt-text.md 34 | 'jsx-a11y/alt-text': 'error', 35 | 36 | // Prevent img alt text from containing redundant words like "image", "picture", or "photo" 37 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/img-redundant-alt.md 38 | 'jsx-a11y/img-redundant-alt': 'error', 39 | 40 | // Require that JSX labels use "htmlFor" 41 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md 42 | // deprecated: replaced by `label-has-associated-control` rule 43 | 'jsx-a11y/label-has-for': 'off', 44 | 45 | // Enforce that a label tag has a text label and an associated control. 46 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/b800f40a2a69ad48015ae9226fbe879f946757ed/docs/rules/label-has-associated-control.md 47 | 'jsx-a11y/label-has-associated-control': [ 48 | 'error', 49 | { 50 | labelComponents: [], 51 | labelAttributes: [], 52 | controlComponents: ['Input'], 53 | }, 54 | ], 55 | 56 | // Enforce that a control (an interactive element) has a text label. 57 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/control-has-associated-label.md 58 | 'jsx-a11y/control-has-associated-label': [ 59 | 'off', 60 | { 61 | ignoreElements: [ 62 | 'audio', 63 | 'canvas', 64 | 'embed', 65 | 'input', 66 | 'textarea', 67 | 'tr', 68 | 'video', 69 | ], 70 | ignoreRoles: [ 71 | 'grid', 72 | 'listbox', 73 | 'menu', 74 | 'menubar', 75 | 'radiogroup', 76 | 'row', 77 | 'tablist', 78 | 'toolbar', 79 | 'tree', 80 | 'treegrid', 81 | ], 82 | includeRoles: ['alert', 'dialog'], 83 | }, 84 | ], 85 | 86 | // Require that mouseover/out come with focus/blur, for keyboard-only users 87 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/mouse-events-have-key-events.md 88 | 'jsx-a11y/mouse-events-have-key-events': 'error', 89 | 90 | // Prevent use of `accessKey` 91 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-access-key.md 92 | 'jsx-a11y/no-access-key': 'error', 93 | 94 | // Require onBlur instead of onChange 95 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-onchange.md 96 | 'jsx-a11y/no-onchange': 'off', 97 | 98 | // Elements with an interactive role and interaction handlers must be focusable 99 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/interactive-supports-focus.md 100 | 'jsx-a11y/interactive-supports-focus': [ 101 | 'error', 102 | { 103 | tabbable: [ 104 | 'button', 105 | 'checkbox', 106 | 'link', 107 | 'searchbox', 108 | 'spinbutton', 109 | 'switch', 110 | 'textbox', 111 | ], 112 | }, 113 | ], 114 | 115 | // Enforce that elements with ARIA roles must have all required attributes 116 | // for that role. 117 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/role-has-required-aria-props.md 118 | 'jsx-a11y/role-has-required-aria-props': 'error', 119 | 120 | // Enforce that elements with explicit or implicit roles defined contain 121 | // Only aria-* properties supported by that role. 122 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/role-supports-aria-props.md 123 | 'jsx-a11y/role-supports-aria-props': 'error', 124 | 125 | // Enforce tabIndex value is not greater than zero. 126 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/tabindex-no-positive.md 127 | 'jsx-a11y/tabindex-no-positive': 'error', 128 | 129 | // ensure tags have content and are not aria-hidden 130 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/heading-has-content.md 131 | 'jsx-a11y/heading-has-content': 'error', 132 | 133 | // Require HTML elements to have a "lang" prop 134 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/html-has-lang.md 135 | 'jsx-a11y/html-has-lang': 'error', 136 | 137 | // Require HTML element's lang prop to be valid 138 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/lang.md 139 | 'jsx-a11y/lang': 'error', 140 | 141 | // prevent distracting elements, like and 142 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-distracting-elements.md 143 | 'jsx-a11y/no-distracting-elements': [ 144 | 'error', 145 | { elements: ['marquee', 'blink'] }, 146 | ], 147 | 148 | // Only allow to have the "scope" attr 149 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/scope.md 150 | 'jsx-a11y/scope': 'error', 151 | 152 | // Require onClick be accompanied by onKeyUp/onKeyDown/onKeyPress 153 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/click-events-have-key-events.md 154 | 'jsx-a11y/click-events-have-key-events': 'error', 155 | 156 | // Enforce that DOM elements without semantic behavior not have interaction handlers 157 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md 158 | 'jsx-a11y/no-static-element-interactions': [ 159 | 'error', 160 | { 161 | allowExpressionValues: true, 162 | handlers: [ 163 | 'onClick', 164 | 'onMouseDown', 165 | 'onMouseUp', 166 | 'onKeyPress', 167 | 'onKeyDown', 168 | 'onKeyUp', 169 | ], 170 | }, 171 | ], 172 | 173 | // A non-interactive element does not support event handlers (mouse and key handlers) 174 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-noninteractive-element-interactions.md 175 | 'jsx-a11y/no-noninteractive-element-interactions': [ 176 | 'error', 177 | { 178 | handlers: [ 179 | 'onClick', 180 | 'onError', 181 | 'onLoad', 182 | 'onMouseDown', 183 | 'onMouseUp', 184 | 'onKeyPress', 185 | 'onKeyDown', 186 | 'onKeyUp', 187 | ], 188 | alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'], 189 | body: ['onError', 'onLoad'], 190 | dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'], 191 | iframe: ['onError', 'onLoad'], 192 | img: ['onError', 'onLoad'], 193 | }, 194 | ], 195 | 196 | // ensure emoji are accessible 197 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/accessible-emoji.md 198 | 'jsx-a11y/accessible-emoji': 'error', 199 | 200 | // elements with aria-activedescendant must be tabbable 201 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-activedescendant-has-tabindex.md 202 | 'jsx-a11y/aria-activedescendant-has-tabindex': 'error', 203 | 204 | // ensure iframe elements have a unique title 205 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/iframe-has-title.md 206 | 'jsx-a11y/iframe-has-title': 'error', 207 | 208 | // prohibit autoFocus prop 209 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-autofocus.md 210 | 'jsx-a11y/no-autofocus': 'error', 211 | 212 | // ensure HTML elements do not specify redundant ARIA roles 213 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-redundant-roles.md 214 | 'jsx-a11y/no-redundant-roles': 'error', 215 | 216 | // media elements must have captions 217 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/media-has-caption.md 218 | 'jsx-a11y/media-has-caption': 'error', 219 | 220 | // WAI-ARIA roles should not be used to convert an interactive element to non-interactive 221 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-interactive-element-to-noninteractive-role.md 222 | 'jsx-a11y/no-interactive-element-to-noninteractive-role': [ 223 | 'error', 224 | { tr: ['none', 'presentation'] }, 225 | ], 226 | 227 | // WAI-ARIA roles should not be used to convert a non-interactive element to interactive 228 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-noninteractive-element-to-interactive-role.md 229 | 'jsx-a11y/no-noninteractive-element-to-interactive-role': [ 230 | 'error', 231 | { 232 | ul: [ 233 | 'listbox', 234 | 'menu', 235 | 'menubar', 236 | 'radiogroup', 237 | 'tablist', 238 | 'tree', 239 | 'treegrid', 240 | ], 241 | ol: [ 242 | 'listbox', 243 | 'menu', 244 | 'menubar', 245 | 'radiogroup', 246 | 'tablist', 247 | 'tree', 248 | 'treegrid', 249 | ], 250 | li: ['menuitem', 'option', 'row', 'tab', 'treeitem'], 251 | table: ['grid'], 252 | td: ['gridcell'], 253 | }, 254 | ], 255 | 256 | // Tab key navigation should be limited to elements on the page that can be interacted with. 257 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-noninteractive-tabindex.md 258 | 'jsx-a11y/no-noninteractive-tabindex': [ 259 | 'error', 260 | { 261 | tags: [], 262 | roles: ['tabpanel'], 263 | allowExpressionValues: true, 264 | }, 265 | ], 266 | 267 | // ensure tags are valid 268 | // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/0745af376cdc8686d85a361ce36952b1fb1ccf6e/docs/rules/anchor-is-valid.md 269 | 'jsx-a11y/anchor-is-valid': [ 270 | 'error', 271 | { 272 | components: ['Link'], 273 | specialLink: ['to', `page`], 274 | }, 275 | ], 276 | }, 277 | } 278 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/rules/react-hooks.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['react-hooks'], 3 | rules: { 4 | // Enforce Rules of Hooks 5 | // https://github.com/facebook/react/blob/c11015ff4f610ac2924d1fc6d569a17657a404fd/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js 6 | 'react-hooks/rules-of-hooks': 'error', 7 | 8 | // Verify the list of the dependencies for Hooks like useEffect and similar 9 | // https://github.com/facebook/react/blob/1204c789776cb01fbaf3e9f032e7e2ba85a44137/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js 10 | 'react-hooks/exhaustive-deps': 'error', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex-react/rules/react.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['plugin:react/recommended'], 3 | plugins: ['react'], 4 | parserOptions: { 5 | ecmaFeatures: { 6 | jsx: true, 7 | }, 8 | }, 9 | settings: { 10 | react: { 11 | version: 'latest', 12 | }, 13 | linkComponents: [{ name: 'Link', linkAttribute: 'to' }], 14 | }, 15 | env: { 16 | browser: true, 17 | }, 18 | rules: { 19 | // Disallow target="_blank" on links 20 | // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-no-target-blank.md 21 | 'react/jsx-no-target-blank': ['error', { enforceDynamicLinks: 'always' }], 22 | 23 | // Prevent missing props validation in a React component definition 24 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md 25 | 'react/prop-types': ['error', { skipUndeclared: true }], 26 | 27 | // Require ES6 class declarations over React.createClass 28 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md 29 | 'react/prefer-es6-class': ['error', 'always'], 30 | 31 | // Require stateless functions when not using lifecycle methods, setState or ref 32 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md 33 | 'react/prefer-stateless-function': 'error', 34 | 35 | // Prevent extra closing tags for components without children 36 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md 37 | 'react/self-closing-comp': 'warn', 38 | 39 | // Enforce boolean attributes notation in JSX 40 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md 41 | 'react/jsx-boolean-value': 'error', 42 | 43 | // Enforce event handler naming conventions in JSX 44 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md 45 | 'react/jsx-handler-names': [ 46 | 'error', 47 | { 48 | eventHandlerPrefix: 'handle', 49 | eventHandlerPropPrefix: 'on', 50 | }, 51 | ], 52 | 53 | // Prevent usage of .bind() in JSX props 54 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md 55 | 'react/jsx-no-bind': [ 56 | 'error', 57 | { 58 | ignoreRefs: true, 59 | allowArrowFunctions: true, 60 | }, 61 | ], 62 | 63 | // Enforce PascalCase for user-defined JSX components 64 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md 65 | 'react/jsx-pascal-case': [ 66 | 'error', 67 | { 68 | allowAllCaps: true, 69 | }, 70 | ], 71 | 72 | // Remove unneeded fragments 73 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-useless-fragment.md 74 | // TODO waiting for https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20356 75 | 'react/jsx-no-useless-fragment': 'off', 76 | 77 | // Enforce a standard way of defining function components 78 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/function-component-definition.md 79 | // TODO enable? 80 | 'react/function-component-definition': 'off', 81 | 82 | // Enforce jsx/tsx file extension 83 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md 84 | 'react/jsx-filename-extension': [ 85 | 'warn', 86 | { 87 | extensions: ['.tsx', '.jsx'], 88 | }, 89 | ], 90 | 91 | // Remove unnecessary braces in attributes 92 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md 93 | 'react/jsx-curly-brace-presence': [ 94 | 'warn', 95 | { props: 'never', children: 'ignore' }, 96 | ], 97 | 98 | // Enforce a consistent way of using JSX Fragments 99 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react/jsx-fragments.md 100 | // TODO https://github.com/vtex/javascript/issues/36 101 | // See if there's a way to import the Fragment instead of using React.Fragment 102 | 'react/jsx-fragments': ['off', 'element'], 103 | 104 | // Prevent usage of this.state inside setState calls 105 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-access-state-in-setstate.md 106 | 'react/no-access-state-in-setstate': 'error', 107 | 108 | // Enforce each file to have no more than one component 109 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md 110 | // TODO discuss 111 | 'react/no-multi-comp': 'off', 112 | 113 | // Warns if using shouldComponentUpdate in a PureComponent extended component 114 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-redundant-should-component-update.md 115 | 'react/no-redundant-should-component-update': 'error', 116 | 117 | // Prevent from trying to read props via this.state on stateless components 118 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-this-in-sfc.md 119 | 'react/no-this-in-sfc': 'error', 120 | 121 | // Enforce the style attribute to be an object 122 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/style-prop-object.md 123 | // TODO maybe enable? it's annoying for the style prop of react-intl components 124 | 'react/style-prop-object': 'off', 125 | 126 | // Prevent void DOM elements from receiving children 127 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md 128 | 'react/void-dom-elements-no-children': 'error', 129 | }, 130 | } 131 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["./index.js"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [15.0.2] - 2022-10-28 10 | ### Removed 11 | - Option `forceSuggestionFixer` that no longer exists. 12 | 13 | ## [15.0.0] - 2022-03-15 14 | ### Changed 15 | - **BREAKING CHANGE** Upgrades `@typescript-eslint` dependencies to major 5. 16 | - **BREAKING CHANGE** Upgrades `eslint-plugin-prettier` to major 4, and bumps 17 | `prettier` peer dependency to only target major 2. 18 | - **BREAKING CHANGE** Updates peer dependency on `eslint` to only target major 8. 19 | 20 | ## [14.1.1] - 2021-08-17 21 | ### Changed 22 | - Upgrades @typescript-eslint/\* deps to 4.29.2 23 | 24 | ## [14.1.0] - 2021-06-24 25 | ### Fixed 26 | - Fix OOM on monorepos by optmizing parsing of typescript files 27 | 28 | ## [14.0.0] - 2021-03-25 29 | ### Changed 30 | - `eslint-config-prettier` updated 6.15.0 to 8.1.0 31 | - Requires at least ESLint 7.0.0 now, because `eslint-config-prettier` drops support for ESLint 6, [see here](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md#version-700-2020-12-05) 32 | 33 | ## [13.0.0] - 2021-03-16 34 | ### Changed 35 | - Use `eslint-plugin-node` instead of deprecated native node rules. 36 | 37 | ### Added 38 | - Encourage usage of Promise API for `fs` and `dns` node modules. 39 | 40 | ## [12.9.5] - 2021-03-15 41 | ### Fixed 42 | - `padding-line-between-statements` doesn't forces lines between cjs-import, import/order already handle them 43 | 44 | ## [12.9.4] - 2021-01-29 45 | ### Changed 46 | - Update typescript-eslint dependencies 47 | 48 | ## 12.9.3 - 2020-12-16 49 | ### Fixed 50 | - Change `projectFolderIgnoreList` parameter to regexps. 51 | 52 | ## 12.9.2 - 2020-12-11 53 | ### Added 54 | - `consistency` parameter to `curly` rule. 55 | 56 | ## 12.9.0 - 2020-12-09 57 | ### Added 58 | - Add convention for multi-line statements after ifs, whiles, fors, etc. 59 | 60 | ## 12.8.14 - 2020-12-04 61 | ### Changed 62 | - Allow to use `import()` for type annotations. 63 | 64 | ## 12.8.12 - 2020-12-03 65 | ### Added 66 | - `@typescript-eslint/no-unnecessary-type-constraint` rule. 67 | - `@typescript-eslint/consistent-type-imports` rule. 68 | 69 | ### Fixed 70 | - `no-shadow` rule for TypeScript. 71 | 72 | ## 12.8.11 - 2020-11-23 73 | ### Fixed 74 | - Explicitly set a `jest` version for rule `jest/no-deprecated-functions` to work. 75 | - Fix `tsconfig` resolution. 76 | 77 | ## 12.8.9 - 2020-11-18 78 | ### Changed 79 | - Update typescript related tools. 80 | 81 | ## 12.8.8 - 2020-10-22 82 | ### Changed 83 | - Allow `// @ts-expect-error` comments if it has a description. 84 | 85 | ## 12.8.7 - 2020-10-21 86 | ### Changed 87 | - Repo url in package.json 88 | 89 | ## 12.8.6 - 2020-08-25 90 | ### Fixed 91 | - [`naming-convention`] Strange issue regarding multiple selectors. 92 | 93 | ## 12.8.5 - 2020-08-21 94 | ### Changed 95 | - Update eslint dependencies 96 | 97 | ## 12.8.4 - 2020-08-20 98 | ### Fixed 99 | - Missing lib directory. 100 | 101 | ## 12.8.3 - 2020-08-20 102 | ### Added 103 | - Check if typescript is available before including its plugin. 104 | 105 | ## 12.8.2 - 2020-08-05 106 | ### Fixed 107 | - [`naming-convention`] Disable casing convention for any `memberLike` name. 108 | 109 | ## 12.8.1 - 2020-08-03 110 | ### Fixed 111 | - [`naming-convention`] Add missing casing cases. 112 | 113 | ## 12.8.0 - 2020-08-03 114 | ### Added 115 | - [`no-fallthrough`] Add comments in the format of `break .* omitted` or `fallsthrough` to allow falling through a switch case. 116 | - [`naming-convention`] Use the new `naming-convention` rule to enforce a consistent symbol naming experience. 117 | 118 | ### Changed 119 | - Update `@typescript-eslint` packages to latest major. 120 | 121 | ### Removed 122 | - Deprecated `camelcase` rule. 123 | 124 | ## 12.7.0 - 2020-07-01 125 | ### Changed 126 | - Block Typescript `enum`s. 127 | - Allow `console.info` 128 | 129 | ## 12.6.0 - 2020-06-24 130 | ### Added 131 | - Padding lines after multiline block-like statements such as `try-catch`, `if`, `while`, `for` etc. 132 | 133 | ### Changed 134 | - `padding-line-between-statements` is now a warning instead of an error. 135 | 136 | ## 12.5.1 - 2020-06-19 137 | ### Added 138 | - `env.es6: true` to base eslint config. 139 | 140 | ### Fixed 141 | - Make `Cypress` global available in every file inside the `cypress` directory. 142 | 143 | ## 12.5.0 - 2020-06-01 144 | ### Added 145 | - `prettier` `v2.0` as acceptable dependency. 146 | 147 | ## [12.3.2] - 2020-04-01 148 | ### Changed 149 | - Disable some import rules inside TypeScript declaration files. 150 | 151 | ## 12.3.1 - 2020-03-30 152 | ### Fixed 153 | - Release cycle. 154 | 155 | ## 12.2.2 - 2020-03-30 156 | ### Fixed 157 | - Deactivate `consistent-type` typescript rule. 158 | 159 | ## [12.2.1] - 2020-02-28 160 | ### Fixed 161 | - Add sub presets to package bundle 162 | 163 | ## [12.2.0] - 2020-02-28 164 | ### Removed 165 | - Import block of `lodash` and `testing-library` 166 | 167 | ## [12.1.0] - 2020-02-12 168 | ### Added 169 | - `eslint-plugin-jest` and `eslint-plugin-cypress` 170 | 171 | ## [12.0.5] - 2020-02-06 172 | ### Fixed 173 | - Prevent eslint trying to load tsconfig from node_modules. 174 | 175 | ## [12.0.4] - 2020-02-03 176 | ### Added 177 | - Add warning message for lodash and @testing-library imports. 178 | 179 | ## [12.0.3] - 2020-01-24 180 | ### Fixed 181 | - Changelog on deploy workflow again. 182 | 183 | ## [12.0.2] - 2020-01-24 184 | ### Fixed 185 | - Changelog on deploy workflow. 186 | 187 | ## [12.0.1] - 2020-01-24 188 | ### Fixed 189 | - Missing files in packages. 190 | 191 | ## 12.0.0 - 2020-01-24 192 | ### Changed 193 | - Whole project structure. 194 | 195 | ### Added 196 | - New rules to ensure best practices. 197 | - New rules to enforce a default coding style. 198 | 199 | ## 11.2.1 - 2019-12-11 200 | ### Changed 201 | - Keep local imports together with no newline between them. 202 | - Add `_+` as an ignore pattern for `no-unused-vars`. 203 | 204 | ## 11.2.0 - 2019-10-31 205 | ### Added 206 | - Add `eslint-plugin-import` to standardize import/export styles. 207 | 208 | ## 11.1.0 - 2019-09-16 209 | ### Changed 210 | - Bump `typescript-eslint` to `v2.3.0`. 211 | - Bump `eslint-plugin-lodash` to `v6.0.0`. 212 | - Bump `eslint-config-prettier` to `v6.2.0`. 213 | 214 | ## 11.0.0 - 2019-07-15 215 | ### Changed 216 | - Upgrade `typescript-eslint` dependencies to support latest TypeScript version. 217 | 218 | ## 10.1.0 - 2019-03-20 219 | ### Removed 220 | - Remove `eslint-plugin-import`. 221 | 222 | ## 10.0.1 - 2019-03-15 223 | ### Changed 224 | - Disabled rule 225 | - The current version of the parser doesn't have support for type analysis, so this rule will 226 | complain for all functions, and not only the ones that doesn't have a type signature or aren't 227 | inferable. 228 | - Added typescript extensions for import plugin. 229 | 230 | ## 10.0.0 - 2019-03-15 231 | ### Changed 232 | - Add support for typescript using the `@typescript-eslint` project packages. 233 | 234 | ## 9.2.0 - 2019-02-18 235 | ### Changed 236 | - Disable `no-console` rule for warnings and errors. 237 | 238 | ## 9.1.0 - 2019-02-06 239 | ### Added 240 | - Eslint recommended rules. 241 | 242 | ## 9.0.0 - 2019-01-28 243 | ### Changed 244 | - Lodash rules and prettier configs. 245 | 246 | [Unreleased]: https://github.com/vtex/typescript/compare/v15.0.2...HEAD 247 | [15.0.2]: https://github.com/vtex/typescript/compare/v15.0.0...v15.0.2 248 | [15.0.0]: https://github.com/vtex/typescript/compare/v14.1.1...v15.0.0 249 | [14.1.1]: https://github.com/vtex/typescript/compare/v14.1.0...v14.1.1 250 | [14.1.0]: https://github.com/vtex/typescript/compare/v14.0.0...v14.1.0 251 | [14.0.0]: https://github.com/vtex/typescript/compare/v13.0.0...v14.0.0 252 | [13.0.0]: https://github.com/vtex/typescript/compare/v12.9.5...v13.0.0 253 | [12.9.5]: https://github.com/vtex/typescript/compare/v12.9.4...v12.9.5 254 | [12.9.4]: https://github.com/vtex/typescript/compare/v12.9.3...v12.9.4 255 | [12.3.2]: https://github.com/vtex/typescript/compare/v12.3.1...v12.3.2 256 | [12.2.1]: https://github.com/vtex/javascript/compare/v12.2.0...v12.2.1 257 | [12.2.0]: https://github.com/vtex/javascript/compare/v12.1.0...v12.2.0 258 | [12.1.0]: https://github.com/vtex/javascript/compare/v12.0.5...v12.1.0 259 | [12.0.5]: https://github.com/vtex/javascript/compare/v12.0.4...v12.0.5 260 | [12.0.4]: https://github.com/vtex/js-standards/compare/v12.0.3...v12.0.4 261 | [12.0.3]: https://github.com/vtex/js-standards/compare/v12.0.2...v12.0.3 262 | [12.0.2]: https://github.com/vtex/js-standards/compare/v12.0.1...v12.0.2 263 | [12.0.1]: https://github.com/vtex/js-standards/compare/v12.0.0...v12.0.1 264 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/README.md: -------------------------------------------------------------------------------- 1 | # `eslint-config-vtex` 2 | 3 | This package provides VTEX's `.eslintrc` as an extensible shared config. 4 | 5 | ## Installation 6 | 7 | Give that you already have ESLint installed, run: 8 | 9 | ```bash 10 | yarn add -D eslint-config-vtex typescript prettier 11 | ``` 12 | 13 | ## Usage 14 | 15 | After installing the module, just add it to your `extends` array inside your `.eslintrc`. 16 | 17 | ```jsonc 18 | // .eslintrc 19 | { 20 | "extends": ["vtex"] 21 | } 22 | ``` 23 | 24 | As any other eslint preset, it's possible to override some rules and configurations. We encourage trying to keep the closest possible to the preset rules, but every project is different and sometimes overriding is needed, use it carefully. 25 | 26 | ### For typescript 27 | 28 | The preset will automatically load Typescript rules when dealing with `.ts` or `.tsx` files. However, there are some rules that require type-checking. This means that a `tsconfig.json`, which includes all files supposed to be linted, must be present. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json`, at the root of your project, as follows: 29 | 30 | ```jsonc 31 | // tsconfig.eslint.json 32 | { 33 | "extends": "./tsconfig.json", 34 | "include": ["**/*.ts", "**/*.tsx", "**/*.js"], 35 | "exclude": [] 36 | } 37 | ``` 38 | 39 | And you should be good to go. 40 | 41 | ### For Javascript 42 | 43 | Sometimes you want to use modern, not yet officially supported, syntax in your Javascript files, such as dynamic `import()`. This can be achieved by using the [`babel-eslint` parser](https://github.com/babel/babel-eslint). For size reasons, we don't include it in this preset but it's extremely simple to configure it: 44 | 45 | ```bash 46 | yarn add -D babel-eslint 47 | ``` 48 | 49 | ```jsonc 50 | // .eslintrc 51 | { 52 | "extends": "vtex", 53 | "parser": "babel-eslint", 54 | "parserOptions": { 55 | "sourceType": "module" 56 | } 57 | } 58 | ``` 59 | 60 | If a project uses both Typescript and Javascript, you can configure the parser inside an `override` block: 61 | 62 | ```jsonc 63 | // .eslintrc 64 | { 65 | "extends": "vtex", 66 | "overrides": [ 67 | { 68 | "files": ["*.js", "*.jsx"], 69 | "parser": "babel-eslint", 70 | "parserOptions": { 71 | "sourceType": "module" 72 | } 73 | } 74 | ] 75 | } 76 | ``` 77 | 78 | Please check the [`babel-eslint` documentation](https://github.com/babel/babel-eslint#additional-parser-configuration) for further options. 79 | 80 | ## References 81 | 82 | - [`@typescript-eslint` documentation](https://typescript-eslint.io/docs/) 83 | - [`eslint-plugin-import` documentation](https://github.com/benmosher/eslint-plugin-import) 84 | - [`eslint-plugin-prettier` documentation](https://github.com/prettier/eslint-plugin-prettier) 85 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/demo/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtex/typescript/e97bc405c546a3b8f0faf6c49d5cf4b7bc950c2a/packages/eslint-config-vtex/demo/index.js -------------------------------------------------------------------------------- /packages/eslint-config-vtex/demo/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtex/typescript/e97bc405c546a3b8f0faf6c49d5cf4b7bc950c2a/packages/eslint-config-vtex/demo/index.ts -------------------------------------------------------------------------------- /packages/eslint-config-vtex/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": "index.js", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "eslint": "^6.8.0", 7 | "prettier": "^1.19.1", 8 | "typescript": "^3.8" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'plugin:vtex/recommended', 5 | './rules/prettier.js', 6 | './rules/errors.js', 7 | './rules/node.js', 8 | './rules/style.js', 9 | './rules/variables.js', 10 | './rules/best-practices.js', 11 | './rules/imports.js', 12 | './rules/typescript.js', 13 | './rules/tests.js', 14 | ], 15 | plugins: ['vtex'], 16 | parserOptions: { 17 | ecmaVersion: 2019, 18 | sourceType: 'module', 19 | }, 20 | env: { 21 | es6: true, 22 | }, 23 | globals: { 24 | __DEV__: true, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/lib/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable node/global-require */ 2 | 3 | exports.hasPackage = (pkg) => { 4 | try { 5 | require(pkg) 6 | 7 | return true 8 | } catch (e) { 9 | return false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-vtex", 3 | "version": "15.1.0", 4 | "description": "VTEX's eslint config", 5 | "main": "index.js", 6 | "scripts": { 7 | "eslint-check": "eslint-config-prettier index.js", 8 | "dump-config": "eslint-config-prettier index.js > config-dump.json", 9 | "version": "chan release $npm_package_version && git add CHANGELOG.md" 10 | }, 11 | "keywords": [ 12 | "eslint", 13 | "config", 14 | "vtex" 15 | ], 16 | "contributors": [ 17 | "Breno Calazans ", 18 | "Christian Kaisermann " 19 | ], 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/vtex/javascript/issues" 23 | }, 24 | "homepage": "https://github.com/vtex/typescript", 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/vtex/typescript.git", 28 | "directory": "packages/eslint-config-vtex" 29 | }, 30 | "files": [ 31 | "index.js", 32 | "rules/", 33 | "lib/" 34 | ], 35 | "dependencies": { 36 | "@typescript-eslint/eslint-plugin": "^5.15.0", 37 | "@typescript-eslint/parser": "^5.15.0", 38 | "confusing-browser-globals": "^1.0.10", 39 | "eslint-config-prettier": "^8.1.0", 40 | "eslint-plugin-cypress": "^2.11.2", 41 | "eslint-plugin-import": "^2.22.1", 42 | "eslint-plugin-jest": "^26.1.1", 43 | "eslint-plugin-node": "^11.1.0", 44 | "eslint-plugin-prettier": "^4.0.0", 45 | "eslint-plugin-vtex": "^2.3.0" 46 | }, 47 | "peerDependencies": { 48 | "eslint": "^8", 49 | "prettier": "^2", 50 | "typescript": "^3 || ^4" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/best-practices.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | // Enforces return statements in callbacks of array's methods 4 | // https://eslint.org/docs/rules/array-callback-return 5 | 'array-callback-return': ['error', { allowImplicit: true }], 6 | 7 | // Treat var statements as if they were block scoped 8 | // https://eslint.org/docs/rules/block-scoped-var 9 | 'block-scoped-var': 'error', 10 | 11 | // Require default case in switch statements 12 | // https://eslint.org/docs/rules/default-case 13 | 'default-case': ['error', { commentPattern: '^no default$' }], 14 | 15 | // Disallow case statement fallthrough 16 | // https://eslint.org/docs/rules/no-fallthrough 17 | 'no-fallthrough': [ 18 | 'error', 19 | { 20 | commentPattern: '(break[\\s\\w]*omitted|falls\\s?through)', 21 | }, 22 | ], 23 | 24 | // Enforce parameters with default values to be last 25 | // https://eslint.org/docs/rules/default-param-last 26 | 'default-param-last': 'error', 27 | 28 | // Encourages use of dot notation whenever possible 29 | // https://eslint.org/docs/rules/dot-notation 30 | 'dot-notation': ['error', { allowKeywords: true }], 31 | 32 | // Require the use of === and !== 33 | // https://eslint.org/docs/rules/eqeqeq 34 | eqeqeq: ['error', 'always', { null: 'ignore' }], 35 | 36 | // Require grouped accessor pairs in object literals and classes 37 | // https://eslint.org/docs/rules/grouped-accessor-pairs 38 | 'grouped-accessor-pairs': 'error', 39 | 40 | // Enforce a maximum number of classes per file 41 | // https://eslint.org/docs/rules/max-classes-per-file 42 | // TODO, maybe turn on 43 | 'max-classes-per-file': ['off', 1], 44 | 45 | // Disallow the use of alert, confirm, and prompt 46 | // https://eslint.org/docs/rules/no-alert 47 | 'no-alert': 'warn', 48 | 49 | // Disallow use of arguments.caller or arguments.callee 50 | // https://eslint.org/docs/rules/no-caller 51 | 'no-caller': 'error', 52 | 53 | // Disallow lexical declarations in case/default clauses 54 | // https://eslint.org/docs/rules/no-case-declarations 55 | 'no-case-declarations': 'error', 56 | 57 | // Disallow returning value in constructor 58 | // https://eslint.org/docs/rules/no-constructor-return 59 | 'no-constructor-return': 'error', 60 | 61 | // Disallow else after a return in an if 62 | // https://eslint.org/docs/rules/no-else-return 63 | 'no-else-return': ['error', { allowElseIf: false }], 64 | 65 | // Disallow empty functions, except for standalone funcs/arrows 66 | // https://eslint.org/docs/rules/no-empty-function 67 | 'no-empty-function': [ 68 | 'error', 69 | { 70 | allow: ['arrowFunctions', 'functions', 'methods'], 71 | }, 72 | ], 73 | 74 | // Disallow use of eval() 75 | // https://eslint.org/docs/rules/no-eval 76 | 'no-eval': 'error', 77 | 78 | // Disallow adding to native types 79 | // https://eslint.org/docs/rules/no-extend-native 80 | 'no-extend-native': 'error', 81 | 82 | // Disallow unnecessary function binding 83 | // https://eslint.org/docs/rules/no-extra-bind 84 | 'no-extra-bind': 'error', 85 | 86 | // Disallow Unnecessary Labels 87 | // https://eslint.org/docs/rules/no-extra-label 88 | 'no-extra-label': 'error', 89 | 90 | // Disallow the use of leading or trailing decimal points in numeric literals 91 | // https://eslint.org/docs/rules/no-floating-decimal 92 | // Disabled because prettier already handle this rule 93 | 'no-floating-decimal': 'off', 94 | 95 | // Disallow reassignments of native objects or read-only globals 96 | // https://eslint.org/docs/rules/no-global-assign 97 | 'no-global-assign': 'error', 98 | 99 | // Disallow use of eval()-like methods 100 | // https://eslint.org/docs/rules/no-implied-eval 101 | 'no-implied-eval': 'error', 102 | 103 | // Disallow usage of __iterator__ property 104 | // https://eslint.org/docs/rules/no-iterator 105 | 'no-iterator': 'error', 106 | 107 | // Disallow use of labels for anything other then loops and switches 108 | // https://eslint.org/docs/rules/no-labels 109 | 'no-labels': 'error', 110 | 111 | // Disallow unnecessary nested blocks 112 | // https://eslint.org/docs/rules/no-lone-blocks 113 | 'no-lone-blocks': 'error', 114 | 115 | // Disallow creation of functions within loops 116 | // https://eslint.org/docs/rules/no-loop-func 117 | 'no-loop-func': 'error', 118 | 119 | // Disallow magic numbers 120 | // https://eslint.org/docs/rules/no-magic-numbers 121 | // TODO enable? maybe too much 122 | 'no-magic-numbers': [ 123 | 'off', 124 | { 125 | ignore: [0, 1, 2, 3], 126 | ignoreArrayIndexes: true, 127 | enforceConst: true, 128 | detectObjects: false, 129 | }, 130 | ], 131 | 132 | // Disallow use of multiline strings 133 | // https://eslint.org/docs/rules/no-multi-str 134 | 'no-multi-str': 'error', 135 | 136 | // Disallow use of new operator for Function object 137 | // https://eslint.org/docs/rules/no-new-func 138 | 'no-new-func': 'error', 139 | 140 | // Disallows creating new instances of String, Number, and Boolean 141 | // https://eslint.org/docs/rules/no-new-wrappers 142 | 'no-new-wrappers': 'error', 143 | 144 | // Disallow use of octal escape sequences in string literals, such as 145 | // var foo = 'Copyright \251'; 146 | // https://eslint.org/docs/rules/no-octal-escape 147 | 'no-octal-escape': 'error', 148 | 149 | // Disallow usage of __proto__ property 150 | // https://eslint.org/docs/rules/no-proto 151 | 'no-proto': 'error', 152 | 153 | // Disallow declaring the same variable more then once 154 | // https://eslint.org/docs/rules/no-redeclare 155 | 'no-redeclare': 'error', 156 | 157 | // Disallow use of assignment in return statement 158 | // https://eslint.org/docs/rules/no-return-assign 159 | 'no-return-assign': ['error', 'except-parens'], 160 | 161 | // Disallow redundant `return await` 162 | // https://eslint.org/docs/rules/no-return-await 163 | 'no-return-await': 'error', 164 | 165 | // Disallow use of `javascript:` urls. 166 | // https://eslint.org/docs/rules/no-script-url 167 | 'no-script-url': 'error', 168 | 169 | // Disallow comparisons where both sides are exactly the same 170 | // https://eslint.org/docs/rules/no-self-compare 171 | 'no-self-compare': 'error', 172 | 173 | // Disallow use of comma operator 174 | // https://eslint.org/docs/rules/no-sequences 175 | 'no-sequences': 'error', 176 | 177 | // Disallow unused labels 178 | // https://eslint.org/docs/rules/no-unused-labels 179 | 'no-unused-labels': 'error', 180 | 181 | // Disallow unnecessary catch clauses 182 | // https://eslint.org/docs/rules/no-useless-catch 183 | // TODO enable? 184 | 'no-useless-catch': 'off', 185 | 186 | // Disallow unnecessary string escaping 187 | // https://eslint.org/docs/rules/no-useless-escape 188 | 'no-useless-escape': 'error', 189 | 190 | // Disallow redundant return; keywords 191 | // https://eslint.org/docs/rules/no-useless-return 192 | 'no-useless-return': 'error', 193 | 194 | // Disallow use of void operator 195 | // https://eslint.org/docs/rules/no-void 196 | 'no-void': 'error', 197 | 198 | // Prefer using regex literals instead of constructor 199 | // https://eslint.org/docs/rules/prefer-regex-literals 200 | 'prefer-regex-literals': 'warn', 201 | 202 | // Require use of the second argument for parseInt() 203 | // https://eslint.org/docs/rules/radix 204 | radix: 'error', 205 | 206 | // Requires to declare all vars on top of their containing scope 207 | // https://eslint.org/docs/rules/vars-on-top 208 | 'vars-on-top': 'error', 209 | // https://eslint.org/docs/rules/constructor-super 210 | 'constructor-super': 'error', 211 | 212 | // Disallow useless computed property keys 213 | // https://eslint.org/docs/rules/no-useless-computed-key 214 | 'no-useless-computed-key': 'error', 215 | 216 | // Disallow unnecessary constructor 217 | // https://eslint.org/docs/rules/no-useless-constructor 218 | 'no-useless-constructor': 'error', 219 | 220 | // Disallow renaming import, export, and destructured assignments to the same name 221 | // https://eslint.org/docs/rules/no-useless-rename 222 | 'no-useless-rename': ['error'], 223 | 224 | // Require let or const instead of var 225 | // https://eslint.org/docs/rules/no-var 226 | 'no-var': 'error', 227 | 228 | // Require method and property shorthand syntax for object literals 229 | // https://eslint.org/docs/rules/object-shorthand 230 | 'object-shorthand': [ 231 | 'error', 232 | 'always', 233 | { 234 | ignoreConstructors: false, 235 | avoidQuotes: true, 236 | }, 237 | ], 238 | 239 | // Suggest using arrow functions as callbacks 240 | // https://eslint.org/docs/rules/prefer-arrow-callback 241 | // TODO discussion 242 | 'prefer-arrow-callback': [ 243 | 'off', 244 | { 245 | allowNamedFunctions: false, 246 | allowUnboundThis: true, 247 | }, 248 | ], 249 | 250 | // Suggest using of const declaration for variables that are never modified after declared 251 | // https://eslint.org/docs/rules/prefer-const 252 | 'prefer-const': [ 253 | 'error', 254 | { 255 | destructuring: 'all', 256 | ignoreReadBeforeAssign: true, 257 | }, 258 | ], 259 | 260 | // Prefer destructuring from arrays and objects 261 | // https://eslint.org/docs/rules/prefer-destructuring 262 | 'prefer-destructuring': [ 263 | 'warn', 264 | { 265 | VariableDeclarator: { 266 | array: true, 267 | object: true, 268 | }, 269 | AssignmentExpression: { 270 | array: false, 271 | object: false, 272 | }, 273 | }, 274 | { 275 | enforceForRenamedProperties: false, 276 | }, 277 | ], 278 | 279 | // Disallow parseInt() in favor of binary, octal, and hexadecimal literals 280 | // https://eslint.org/docs/rules/prefer-numeric-literals 281 | 'prefer-numeric-literals': 'error', 282 | 283 | // Use rest parameters instead of arguments 284 | // https://eslint.org/docs/rules/prefer-rest-params 285 | 'prefer-rest-params': 'error', 286 | 287 | // Suggest using the spread operator instead of .apply() 288 | // https://eslint.org/docs/rules/prefer-spread 289 | 'prefer-spread': 'error', 290 | 291 | // Suggest using template literals instead of string concatenation 292 | // https://eslint.org/docs/rules/prefer-template 293 | 'prefer-template': 'warn', 294 | 295 | // Disallow generator functions that do not have yield 296 | // https://eslint.org/docs/rules/require-yield 297 | 'require-yield': 'error', 298 | 299 | // Require a Symbol description 300 | // https://eslint.org/docs/rules/symbol-description 301 | 'symbol-description': 'error', 302 | }, 303 | } 304 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/errors.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // only rules not included in 'eslint:recommended' or with different values 3 | rules: { 4 | // Enforces that a return statement is present in property getters 5 | // https://eslint.org/docs/rules/getter-return 6 | 'getter-return': ['error', { allowImplicit: true }], 7 | 8 | // Disallow await inside of loops 9 | // https://eslint.org/docs/rules/no-await-in-loop 10 | 'no-await-in-loop': 'error', 11 | 12 | // Disallow assignment in conditional expressions 13 | // https://eslint.org/docs/rules/no-cond-assign 14 | 'no-cond-assign': ['error', 'except-parens'], 15 | 16 | // Disallow use of console 17 | // https://eslint.org/docs/rules/no-console 18 | 'no-console': ['error', { allow: ['warn', 'error', 'info'] }], 19 | 20 | // Disallow template literal placeholder syntax in regular strings 21 | // https://eslint.org/docs/rules/no-template-curly-in-string 22 | 'no-template-curly-in-string': 'error', 23 | 24 | // Disallow assignments that can lead to race conditions due to usage of await or yield 25 | // https://eslint.org/docs/rules/require-atomic-updates 26 | // TODO: https://github.com/vtex/javascript/issues/27 27 | 'require-atomic-updates': 'off', 28 | 29 | //! Require eslint >= 6.7.0 30 | 31 | // Disallow duplicate conditions in if-else-if chains 32 | // https://eslint.org/docs/rules/no-dupe-else-if 33 | 'no-dupe-else-if': 'error', 34 | 35 | // https://eslint.org/docs/rules/no-import-assign 36 | 'no-import-assign': 'error', 37 | 38 | // Disallow returning values from setters 39 | // https://eslint.org/docs/rules/no-setter-return 40 | 'no-setter-return': 'error', 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/imports.js: -------------------------------------------------------------------------------- 1 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules 2 | module.exports = { 3 | extends: ['plugin:import/typescript'], 4 | plugins: ['import'], 5 | rules: { 6 | // Disallow non-import statements appearing before import statements 7 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md 8 | 'import/first': 'error', 9 | 10 | // Disallow duplicate imports 11 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md 12 | 'import/no-duplicates': 'error', 13 | 14 | // Ensure native, external and internal imports are separated, above relative imports and that unassigned imports are ignored 15 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md 16 | // todo: add import-type to this, waiting for https://github.com/benmosher/eslint-plugin-import/issues/645 17 | 'import/order': [ 18 | 'error', 19 | { 20 | 'newlines-between': 'always', 21 | groups: [ 22 | 'builtin', 23 | 'external', 24 | 'internal', 25 | ['parent', 'sibling', 'index'], 26 | ], 27 | }, 28 | ], 29 | 30 | // Require a newline after the last import/require in a group 31 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/newline-after-import.md 32 | 'import/newline-after-import': 'error', 33 | 34 | // Forbid import of modules using absolute paths 35 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-absolute-path.md 36 | 'import/no-absolute-path': 'error', 37 | 38 | // Forbid mutable exports 39 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md 40 | 'import/no-mutable-exports': 'error', 41 | 42 | // Disallow invalid exports, e.g. multiple defaults 43 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/export.md 44 | 'import/export': 'error', 45 | 46 | // Ensures that there are no useless path segments 47 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-useless-path-segments.md 48 | 'import/no-useless-path-segments': ['error', { commonjs: true }], 49 | 50 | // Forbid a module from importing itself 51 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-self-import.md 52 | 'import/no-self-import': 'error', 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/node.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['node'], 3 | env: { 4 | node: true, 5 | }, 6 | rules: { 7 | // Disallow use of process.env 8 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-process-env.md 9 | 'node/no-process-env': 'off', 10 | 11 | // Enforce a callback to return 12 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/callback-return.md 13 | //! too annoying 14 | 'node/callback-return': 'off', 15 | 16 | // Require all requires be top-level 17 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/global-require.md 18 | 'node/global-require': 'error', 19 | 20 | // Disallow use of new operator with the require function 21 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-new-require.md 22 | 'node/no-new-require': 'error', 23 | 24 | // Disallow string concatenation with __dirname and __filename 25 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-path-concat.md 26 | 'node/no-path-concat': 'error', 27 | 28 | // Make process.exit() expressions the same code path as throw 29 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/process-exit-as-throw.md 30 | 'node/process-exit-as-throw': 'error', 31 | 32 | // Disallow deprecated APIs 33 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md 34 | 'node/no-deprecated-api': 'error', 35 | 36 | // Encourages use of promise APIs instead o callback APIs 37 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/prefer-promises/fs.md 38 | // https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/prefer-promises/dns.md 39 | 'node/prefer-promises/fs': 'warn', 40 | 'node/prefer-promises/dns': 'warn', 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/prettier.js: -------------------------------------------------------------------------------- 1 | // https://github.com/prettier/eslint-plugin-prettier 2 | module.exports = { 3 | extends: ['plugin:prettier/recommended'], 4 | plugins: ['prettier'], 5 | rules: { 6 | 'prettier/prettier': 'error', 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/style.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | // Allow brace-less single-line if, else if, else, for, while, or do, while still enforcing the use of curly braces for other instances. 4 | // https://eslint.org/docs/rules/curly 5 | // Disabled because conflicts with prettier 6 | // https://github.com/prettier/eslint-config-prettier#curly 7 | curly: 'off', 8 | 9 | // Require camel case names 10 | // https://eslint.org/docs/rules/camelcase 11 | camelcase: [ 12 | 'error', 13 | { 14 | properties: 'never', 15 | ignoreDestructuring: false, 16 | }, 17 | ], 18 | 19 | // Require function expressions to have a name 20 | // https://eslint.org/docs/rules/func-names 21 | 'func-names': 'warn', 22 | 23 | // Enforce position of line comments 24 | // https://eslint.org/docs/rules/line-comment-position 25 | // TODO https://github.com/vtex/front-end-coding-standard/issues/30 26 | 'line-comment-position': [ 27 | 'off', 28 | { 29 | position: 'above', 30 | applyDefaultPatterns: true, 31 | }, 32 | ], 33 | 34 | // Disallow comments inline after code 35 | // https://eslint.org/docs/rules/no-inline-comments 36 | // TODO https://github.com/vtex/front-end-coding-standard/issues/30 37 | 'no-inline-comments': 'off', 38 | 39 | // Require or disallow newlines around directives 40 | // https://eslint.org/docs/rules/lines-around-directive 41 | 'lines-around-directive': [ 42 | 'error', 43 | { 44 | before: 'always', 45 | after: 'always', 46 | }, 47 | ], 48 | 49 | // Limit the number of parameters that can be used in the function declaration. 50 | // https://eslint.org/docs/rules/max-params 51 | 'max-params': [ 52 | 'warn', 53 | { 54 | max: 3, 55 | }, 56 | ], 57 | 58 | // Require a capital letter for constructors 59 | // https://eslint.org/docs/rules/new-cap 60 | 'new-cap': [ 61 | 'error', 62 | { 63 | newIsCap: true, 64 | capIsNew: false, 65 | }, 66 | ], 67 | 68 | // Disallow use of the Array constructor 69 | // https://eslint.org/docs/rules/no-array-constructor 70 | 'no-array-constructor': 'error', 71 | 72 | // Disallow if as the only statement in an else block 73 | // https://eslint.org/docs/rules/no-lonely-if 74 | 'no-lonely-if': 'error', 75 | 76 | // Disallow use of chained assignment expressions 77 | // https://eslint.org/docs/rules/no-multi-assign 78 | 'no-multi-assign': 'error', 79 | 80 | // Disallow negated conditions 81 | // https://eslint.org/docs/rules/no-negated-condition 82 | // TODO discuss, maybe too much 83 | 'no-negated-condition': 'off', 84 | 85 | // Disallow use of the Object constructor 86 | // https://eslint.org/docs/rules/no-new-object 87 | 'no-new-object': 'error', 88 | 89 | // Disallow certain syntax forms 90 | // https://eslint.org/docs/rules/no-restricted-syntax 91 | 'no-restricted-syntax': [ 92 | 'error', 93 | { 94 | selector: 'LabeledStatement', 95 | message: 96 | 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', 97 | }, 98 | { 99 | selector: 'WithStatement', 100 | message: 101 | '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', 102 | }, 103 | // ban all enums 104 | { 105 | selector: 'TSEnumDeclaration:not([const=true])', 106 | message: 107 | "Literal types and enums, in many cases, solve the same problem while enum has some trade-offs that usually literal types don't. Consider using a literal type instead. See https://github.com/vtex/typescript/issues/60 for more information.", 108 | }, 109 | ], 110 | 111 | // Disallow the use of Boolean literals in conditional expressions 112 | // Also, prefer `a || b` over `a ? a : b` 113 | // https://eslint.org/docs/rules/no-unneeded-ternary 114 | 'no-unneeded-ternary': ['error', { defaultAssignment: false }], 115 | 116 | // Allow just one var statement per function 117 | // https://eslint.org/docs/rules/one-var 118 | 'one-var': ['error', 'never'], 119 | 120 | // Require assignment operator shorthand where possible or prohibit it entirely 121 | // https://eslint.org/docs/rules/operator-assignment 122 | 'operator-assignment': ['error', 'always'], 123 | 124 | // Prefer use of an object spread over Object.assign 125 | // https://eslint.org/docs/rules/prefer-object-spread 126 | 'prefer-object-spread': 'error', 127 | 128 | // Require or disallow a space immediately following the // or /* in a comment 129 | // https://eslint.org/docs/rules/spaced-comment 130 | 'spaced-comment': [ 131 | 'error', 132 | 'always', 133 | { 134 | line: { 135 | exceptions: ['-', '+'], 136 | markers: ['-', '+', '?', '!'], 137 | }, 138 | block: { 139 | exceptions: ['-', '+'], 140 | markers: ['-', '+', '?', '!'], 141 | balanced: true, 142 | }, 143 | }, 144 | ], 145 | 146 | // Require or disallow padding lines between statements 147 | // https://eslint.org/docs/rules/padding-line-between-statements 148 | 'padding-line-between-statements': [ 149 | 'warn', 150 | // empty lines after declarations 151 | { 152 | blankLine: 'always', 153 | prev: ['const', 'let', 'var'], 154 | next: '*', 155 | }, 156 | // allow to have none or one blank line between declarations 157 | { 158 | blankLine: 'any', 159 | prev: ['const', 'let', 'var'], 160 | next: ['const', 'let', 'var'], 161 | }, 162 | // enforce blank lines after multiline declarations 163 | { 164 | blankLine: 'always', 165 | prev: ['multiline-const', 'multiline-let', 'multiline-var'], 166 | next: '*', 167 | }, 168 | // empty lines before returns 169 | { 170 | blankLine: 'always', 171 | prev: '*', 172 | next: 'return', 173 | }, 174 | // empty lines between switch cases and breaks 175 | { 176 | blankLine: 'always', 177 | prev: ['case', 'break'], 178 | next: ['case', 'break', 'default'], 179 | }, 180 | // always require blankline after function, class declarations and multiline blocks (if, try-catch, etc) 181 | { 182 | blankLine: 'always', 183 | prev: ['function', 'class', 'multiline-block-like'], 184 | next: '*', 185 | }, 186 | // import/order already handle padding lines between cjs-imports 187 | // see https://github.com/vtex/typescript/issues/82 188 | { 189 | blankLine: 'any', 190 | prev: ['cjs-import'], 191 | next: ['cjs-import'], 192 | }, 193 | ], 194 | 195 | // Require or disallow padding lines between class members 196 | // https://eslint.org/docs/rules/lines-between-class-members 197 | 'lines-between-class-members': [ 198 | 'error', 199 | 'always', 200 | { 201 | exceptAfterSingleLine: true, 202 | }, 203 | ], 204 | 205 | //! Require eslint >= 6.7.0 206 | 207 | // Disallow the use of Math.pow in favor of the ** operator 208 | // https://eslint.org/docs/rules/prefer-exponentiation-operator 209 | 'prefer-exponentiation-operator': 'error', 210 | }, 211 | } 212 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/tests.js: -------------------------------------------------------------------------------- 1 | // Jest: 2 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules 3 | // Cypress: https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules 4 | const { hasPackage } = require('../lib/utils') 5 | 6 | const hasJest = hasPackage('jest') 7 | 8 | module.exports = { 9 | overrides: [ 10 | // ! CYPRESS 11 | { 12 | files: ['**/cypress/**/*.{ts,tsx,js,jsx}'], 13 | extends: ['plugin:cypress/recommended'], 14 | rules: { 15 | // Enforce assertions before taking a screenshot 16 | // https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/assertion-before-screenshot.md 17 | 'cypress/assertion-before-screenshot': 'warn', 18 | }, 19 | }, 20 | // ! JEST 21 | { 22 | // Run through every test file found 23 | files: ['*.{test,spec}.{ts,tsx,js,jsx}'], 24 | // Unless it's inside a cypress directory 25 | excludedFiles: ['**/cypress/**'], 26 | extends: ['plugin:jest/recommended', 'plugin:jest/style'], 27 | settings: { 28 | // need to explicitly set this for the IO apps. 29 | // we need to explicitly define jest version because IO apps 30 | // have jest installed in a subdirectory. 31 | jest: { 32 | ...(hasJest === false && { 33 | version: 26, 34 | }), 35 | }, 36 | }, 37 | rules: { 38 | // Enforce consistent a test method name 39 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/consistent-test-it.md 40 | 'jest/consistent-test-it': [ 41 | 'warn', 42 | { 43 | fn: 'test', 44 | withinDescribe: 'it', 45 | }, 46 | ], 47 | 48 | // Disallow alias methods 49 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-alias-methods.md 50 | // TODO: enable? 51 | 'jest/no-alias-methods': 'off', 52 | 53 | // Disallow duplicate setup/teardown hooks 54 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-duplicate-hooks.md 55 | 'jest/no-duplicate-hooks': 'error', 56 | 57 | // Suggest to have all hooks at top-level before tests 58 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/prefer-hooks-on-top.md 59 | 'jest/prefer-hooks-on-top': 'error', 60 | 61 | // Suggest jest.spyOn() instead of jest.fn() 62 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/prefer-spy-on.md 63 | 'jest/prefer-spy-on': 'warn', 64 | 65 | // Suggest using test.todo() 66 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/prefer-todo.md 67 | 'jest/prefer-todo': 'warn', 68 | 69 | // Disallow conditional logic inside tests 70 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-if.md 71 | // TODO: enable? 72 | 'jest/no-if': 'off', 73 | 74 | // Disallow return statements from tests 75 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-test-return-statement.md 76 | // TODO: enable? 77 | 'jest/no-test-return-statement': 'off', 78 | 79 | // Disallow deprecated jest functions 80 | // https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-deprecated-functions.md 81 | 'jest/no-deprecated-functions': 'error', 82 | }, 83 | }, 84 | ], 85 | } 86 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/typescript.js: -------------------------------------------------------------------------------- 1 | const { hasPackage } = require('../lib/utils') 2 | 3 | const hasTypescript = hasPackage('typescript') 4 | 5 | module.exports = !hasTypescript 6 | ? {} 7 | : { 8 | overrides: [ 9 | { 10 | files: ['*.ts', '*.tsx'], 11 | extends: [ 12 | 'plugin:@typescript-eslint/eslint-recommended', 13 | 'plugin:@typescript-eslint/recommended', 14 | ], 15 | plugins: ['@typescript-eslint'], 16 | parser: '@typescript-eslint/parser', 17 | parserOptions: { 18 | ecmaVersion: 2019, 19 | sourceType: 'module', 20 | project: [ 21 | // look in the root 22 | 'tsconfig{.eslint.json,.json}', 23 | // look in dirs like node/react 24 | '*/tsconfig{.eslint.json,.json}', 25 | // look in dirs like packages/package/* 26 | '*/*/tsconfig{.eslint.json,.json}', 27 | ], 28 | projectFolderIgnoreList: [/node_modules/i], 29 | // We need this configuration to avoid performance issues in monorepos 30 | // https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-862414778 31 | allowAutomaticSingleRunInference: true, 32 | }, 33 | rules: { 34 | //! extensions of native eslint rules 35 | //! when modifying a rule here, make sure to modify the native one and vice-versa 36 | 37 | // Disallow declaration of variables already declared in the outer scope 38 | // https://eslint.org/docs/rules/no-shadow 39 | 'no-shadow': 'off', 40 | '@typescript-eslint/no-shadow': [ 41 | 'error', 42 | { 43 | allow: ['done', 'next', 'resolve', 'reject', 'cb'], 44 | }, 45 | ], 46 | 47 | // Prevent unused declared variables 48 | // https://typescript-eslint.io/rules/no-unused-vars/ 49 | 'no-unused-vars': 'off', 50 | '@typescript-eslint/no-unused-vars': [ 51 | 'warn', 52 | { 53 | ignoreRestSiblings: true, 54 | argsIgnorePattern: '_+', 55 | }, 56 | ], 57 | 58 | // Disallows the use of eval()-like methods 59 | // https://typescript-eslint.io/rules/no-magic-numbers/ 60 | // TODO enable? maybe too much 61 | 'no-magic-numbers': 'off', 62 | '@typescript-eslint/no-magic-numbers': [ 63 | 'off', 64 | { 65 | ignore: [0, 1, 2, 3], 66 | ignoreArrayIndexes: true, 67 | enforceConst: true, 68 | detectObjects: false, 69 | ignoreNumericLiteralTypes: true, 70 | ignoreEnums: true, 71 | }, 72 | ], 73 | 74 | // Enforce parameters with default values to be last 75 | // https://typescript-eslint.io/rules/default-param-last/ 76 | 'default-param-last': 'off', 77 | '@typescript-eslint/default-param-last': 'error', 78 | 79 | // Disallow useless constructors 80 | // https://typescript-eslint.io/rules/no-useless-constructor/ 81 | 'no-useless-constructor': 'off', 82 | '@typescript-eslint/no-useless-constructor': 'error', 83 | 84 | // Disallow empty functions, except for standalone funcs/arrows 85 | // https://eslint.org/docs/rules/no-empty-function 86 | 'no-empty-function': 'off', 87 | '@typescript-eslint/no-empty-function': [ 88 | 'error', 89 | { 90 | allow: ['arrowFunctions', 'functions', 'methods'], 91 | }, 92 | ], 93 | 94 | // Require a consistent naming convention 95 | // https://typescript-eslint.io/rules/naming-convention/ 96 | camelcase: 'off', 97 | '@typescript-eslint/naming-convention': [ 98 | 'error', 99 | { 100 | selector: 'default', 101 | format: ['camelCase'], 102 | leadingUnderscore: 'allow', 103 | trailingUnderscore: 'allow', 104 | }, 105 | { 106 | selector: 'variable', 107 | format: ['camelCase', 'UPPER_CASE', 'PascalCase'], 108 | leadingUnderscore: 'allow', 109 | trailingUnderscore: 'allow', 110 | }, 111 | { 112 | selector: 'function', 113 | format: ['camelCase', 'PascalCase'], 114 | }, 115 | { 116 | selector: 'typeLike', 117 | format: ['PascalCase'], 118 | }, 119 | { 120 | selector: 'memberLike', 121 | format: null, 122 | }, 123 | { 124 | // have to leave this for now as this rule 125 | // doesn't separate regular parameters from 126 | // destructured parameters 127 | selector: 'parameter', 128 | format: null, 129 | }, 130 | ], 131 | 132 | // Disallow use of variables before they are defined 133 | // https://typescript-eslint.io/rules/no-use-before-define/ 134 | 'no-use-before-define': 'off', 135 | '@typescript-eslint/no-use-before-define': [ 136 | 'error', 137 | { 138 | functions: false, 139 | classes: false, 140 | variables: true, 141 | enums: false, 142 | typedefs: false, 143 | }, 144 | ], 145 | // ! ts only rules 146 | // Enforce explicit accessibility modifiers on class properties and methods 147 | // https://typescript-eslint.io/rules/explicit-member-accessibility/ 148 | '@typescript-eslint/explicit-member-accessibility': [ 149 | 'error', 150 | { 151 | accessibility: 'explicit', 152 | overrides: { 153 | accessors: 'explicit', 154 | constructors: 'no-public', 155 | methods: 'explicit', 156 | parameterProperties: 'explicit', 157 | }, 158 | }, 159 | ], 160 | 161 | // Enforce explicit function return type 162 | // https://typescript-eslint.io/rules/explicit-function-return-type/ 163 | '@typescript-eslint/explicit-function-return-type': 'off', 164 | 165 | // Enforce a consistent way of typing arrays 166 | // https://typescript-eslint.io/rules/array-type/v 167 | '@typescript-eslint/array-type': [ 168 | 'warn', 169 | { 170 | default: 'array-simple', 171 | readonly: 'array-simple', 172 | }, 173 | ], 174 | 175 | // Enforce a consitent way to type objects 176 | // https://typescript-eslint.io/rules/consistent-type-definitions/ 177 | '@typescript-eslint/consistent-type-definitions': 'off', 178 | 179 | // Disallow non null assertions (!), comes from the recommended config 180 | // https://typescript-eslint.io/rules/no-non-null-assertion/ 181 | '@typescript-eslint/no-non-null-assertion': 'warn', 182 | 183 | // Enforce that when adding two variables, operands must both be of type number or of type string 184 | // https://typescript-eslint.io/rules/restrict-plus-operands/ 185 | '@typescript-eslint/restrict-plus-operands': [ 186 | 'error', 187 | { 188 | checkCompoundAssignments: true, 189 | }, 190 | ], 191 | 192 | // Enforce optional chaining over chaining AND (&&) operators 193 | // https://typescript-eslint.io/rules/prefer-optional-chain/ 194 | '@typescript-eslint/prefer-optional-chain': 'warn', 195 | 196 | // Enforce optional chaining over chaining AND (&&) operators 197 | // https://typescript-eslint.io/rules/no-non-null-asserted-optional-chain/ 198 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', 199 | 200 | // Enforce nullish coalescing over short-circuiting 201 | // https://typescript-eslint.io/rules/prefer-nullish-coalescing/ 202 | '@typescript-eslint/prefer-nullish-coalescing': [ 203 | 'warn', 204 | { 205 | ignoreConditionalTests: true, 206 | ignoreMixedLogicalExpressions: true, 207 | }, 208 | ], 209 | 210 | // Prefer usage of as const over literal type 211 | // https://typescript-eslint.io/rules/prefer-as-const/ 212 | // TODO: turn it on when 2.18.x is out 213 | // '@typescript-eslint/prefer-as-const': 'error', 214 | 215 | // Prevent unnecessary type arguments 216 | // https://typescript-eslint.io/rules/no-unnecessary-type-arguments/ 217 | '@typescript-eslint/no-unnecessary-type-arguments': 'warn', 218 | 219 | // Warns when a namespace qualifier is unnecessary 220 | // https://typescript-eslint.io/rules/no-unnecessary-qualifier/ 221 | '@typescript-eslint/no-unnecessary-qualifier': 'warn', 222 | 223 | // Disallow throwing literals as exceptions 224 | // https://typescript-eslint.io/rules/no-throw-literal/ 225 | '@typescript-eslint/no-throw-literal': 'warn', 226 | 227 | // Disallows invocation of require() in favor of import statements 228 | // https://typescript-eslint.io/rules/no-require-imports/ 229 | '@typescript-eslint/no-require-imports': 'warn', 230 | 231 | // Disallows the use of eval()-like methods 232 | // https://typescript-eslint.io/rules/no-implied-eval/ 233 | '@typescript-eslint/no-implied-eval': 'error', 234 | 235 | // Requires Array#sort calls to always provide a compareFunction 236 | // https://typescript-eslint.io/rules/require-array-sort-compare/ 237 | '@typescript-eslint/require-array-sort-compare': 'error', 238 | 239 | // Enforce explicit enum item values 240 | // https://typescript-eslint.io/rules/prefer-enum-initializers/ 241 | '@typescript-eslint/prefer-enum-initializers': 'warn', 242 | 243 | // Explicitly defines what a module scoped method returns 244 | // https://typescript-eslint.io/rules/explicit-module-boundary-types/ 245 | '@typescript-eslint/explicit-module-boundary-types': 'off', 246 | 247 | // Disallow harmful bultin types 248 | // https://typescript-eslint.io/rules/ban-types/ 249 | //! Commented because we use the recommended version of this rule 250 | // '@typescript-eslint/ban-types': 'off', 251 | 252 | // Disallow // @ts comments 253 | // https://typescript-eslint.io/rules/ban-ts-comment/ 254 | '@typescript-eslint/ban-ts-comment': [ 255 | 'error', 256 | { 257 | 'ts-expect-error': 'allow-with-description', 258 | 'ts-ignore': true, 259 | 'ts-nocheck': true, 260 | 'ts-check': false, 261 | minimumDescriptionLength: 3, 262 | }, 263 | ], 264 | 265 | // Disallows unnecessary constraints on generic types 266 | // https://typescript-eslint.io/rules/no-unnecessary-type-constraint/ 267 | '@typescript-eslint/no-unnecessary-type-constraint': 'warn', 268 | 269 | // Enforces consistent usage of type imports 270 | // https://typescript-eslint.io/rules/consistent-type-imports/ 271 | '@typescript-eslint/consistent-type-imports': [ 272 | 'warn', 273 | { 274 | prefer: 'type-imports', 275 | disallowTypeAnnotations: false, 276 | }, 277 | ], 278 | }, 279 | }, 280 | { 281 | files: ['*.d.ts'], 282 | rules: { 283 | 'import/order': 'off', 284 | 'import/no-duplicates': 'off', 285 | 'import/export': 'off', 286 | }, 287 | }, 288 | ], 289 | } 290 | -------------------------------------------------------------------------------- /packages/eslint-config-vtex/rules/variables.js: -------------------------------------------------------------------------------- 1 | const confusingBrowserGlobals = require('confusing-browser-globals') 2 | 3 | module.exports = { 4 | rules: { 5 | // Disallow labels that share a name with a variable 6 | // https://eslint.org/docs/rules/no-label-var 7 | 'no-label-var': 'error', 8 | 9 | // Disallow specific globals 10 | // https://eslint.org/docs/rules/no-restricted-globals 11 | 'no-restricted-globals': ['error', 'isFinite', 'isNaN'].concat( 12 | confusingBrowserGlobals 13 | ), 14 | 15 | // Disallow declaration of variables already declared in the outer scope 16 | // https://eslint.org/docs/rules/no-shadow 17 | 'no-shadow': [ 18 | 'error', 19 | { 20 | allow: ['done', 'next', 'resolve', 'reject', 'cb'], 21 | }, 22 | ], 23 | 24 | // Disallow shadowing of names such as arguments 25 | // https://eslint.org/docs/rules/no-shadow-restricted-names 26 | 'no-shadow-restricted-names': 'error', 27 | 28 | // Disallow use of undefined when initializing variables 29 | // https://eslint.org/docs/rules/no-undef-init 30 | 'no-undef-init': 'error', 31 | 32 | // Disallow declaration of variables that are not used in the code 33 | // https://eslint.org/docs/rules/no-unused-vars 34 | 'no-unused-vars': [ 35 | 'error', 36 | { 37 | ignoreRestSiblings: true, 38 | argsIgnorePattern: '_+', 39 | }, 40 | ], 41 | 42 | // Disallow use of variables before they are defined 43 | // https://eslint.org/docs/rules/no-use-before-define 44 | 'no-use-before-define': [ 45 | 'off', 46 | { 47 | functions: false, 48 | classes: false, 49 | variables: true, 50 | }, 51 | ], 52 | }, 53 | } 54 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/.gitignore: -------------------------------------------------------------------------------- 1 | # package build 2 | dist 3 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [2.3.0] - 2024-04-29 10 | ### Added 11 | - `vtex/consistent-props-type` rule 12 | 13 | ## [2.2.1] - 2022-05-30 14 | ### Added 15 | - `vtex/prefer-use-effect-named-callback` rule. 16 | 17 | ## [2.2.0] - 2022-03-15 18 | ### Changed 19 | - Include `eslint` major 8 in peer dependencies range. 20 | 21 | ## [2.1.0] - 2021-06-24 22 | ### Fixed 23 | - Fix OOM on monorepos by optmizing parsing of typescript files 24 | 25 | ## 2.0.6 - 2020-11-18 26 | ### Changed 27 | - Update typescript related tools. 28 | 29 | ## 2.0.4 - 2020-10-21 30 | ### Changed 31 | - Repo url in package.json 32 | 33 | ## 2.0.2 - 2020-08-21 34 | ### Changed 35 | - Add eslint v7 as peer dep 36 | 37 | ## 2.0.0 - 2020-08-03 38 | ### Removed 39 | - `vtex/enforce-explicit-enum-values` in favor of `@typescript-eslint/prefer-enum-initializers`. 40 | 41 | ## 1.2.0 - 2020-07-01 42 | ### Added 43 | - Add `vtex/enforce-explicit-enum-values` rule. 44 | 45 | ## 1.1.1 - 2020-06-19 46 | ### Changed 47 | - [`vtex/prefer-early-return`] Default `maxStatements` from `1` to `2`. 48 | 49 | ## [1.0.3] - 2020-01-24 50 | ### Fixed 51 | - Changelog on deploy workflow again. 52 | 53 | ## [1.0.2] - 2020-01-24 54 | ### Fixed 55 | - Changelog on deploy workflow. 56 | 57 | ## 1.0.1 - 2020-01-24 58 | ### Fixed 59 | - Missing files in packages. 60 | 61 | [Unreleased]: https://github.com/vtex/typescript/compare/v2.3.0...HEAD 62 | [2.3.0]: https://github.com/vtex/typescript/compare/v2.2.1...v2.3.0 63 | [2.2.1]: https://github.com/vtex/typescript/compare/v2.2.0...v2.2.1 64 | [2.2.0]: https://github.com/vtex/typescript/compare/v2.1.0...v2.2.0 65 | [2.1.0]: https://github.com/vtex/typescript/compare/v2.0.6...v2.1.0 66 | [1.0.3]: https://github.com/vtex/js-standards/compare/v1.0.2...v1.0.3 67 | [1.0.2]: https://github.com/vtex/js-standards/compare/v1.0.1...v1.0.2 68 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/README.md: -------------------------------------------------------------------------------- 1 | # `eslint-plugin-vtex` 2 | 3 | This package provides [VTEX](https://vtex.com/)'s custom ESLint rules. 4 | 5 | ## Installation 6 | 7 | Give that you already have ESLint installed, run: 8 | 9 | ```bash 10 | yarn add -D eslint-plugin-vtex 11 | ``` 12 | 13 | ## Usage 14 | 15 | After installing the module, just add it to your `plugins` array inside your `.eslintrc`. 16 | 17 | ```jsonc 18 | // .eslintrc 19 | { 20 | ..., 21 | "plugins": ["vtex"] 22 | } 23 | ``` 24 | 25 | ## Rules 26 | 27 | This plugin provides the following custom ESLint rules: 28 | 29 | - [`prefer-early-return`](/packages/eslint-plugin-vtex/docs/rules/prefer-early-return.md): Suggest early returning to prevent nesting and improve code readability. 30 | - [`enforce-explicit-enum-values`](/packages/eslint-plugin-vtex/docs/rules/enforce-explicit-enum-values.md): Enforce explicit enum values. 31 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/docs/rules/consistent-props-type.md: -------------------------------------------------------------------------------- 1 | # Enforce the consistent way to define the props type (`vtex/consistent-props-type`) 2 | 3 | Examples of **incorrect** code for this rule: 4 | 5 | - Invalid props type name. Must be: DividerProps 6 | 7 | ```tsx 8 | type Props = {} 9 | 10 | const Divider = (props: Props) => { 11 | return
12 | } 13 | ``` 14 | 15 | - Invalid inline props type. DividerProps must define all the props. 16 | 17 | ```tsx 18 | type DividerProps = {} 19 | 20 | const Divider = (props: DividerProps & { inline: true }) => { 21 | return
22 | } 23 | ``` 24 | 25 | - Invalid generics props type name. Must be: Props 26 | 27 | ```tsx 28 | type DividerProps = {} 29 | 30 | const Divider = (props: T) => { 31 | return
32 | } 33 | ``` 34 | 35 | Examples of **correct** code for this rule: 36 | 37 | ```tsx 38 | type DividerProps = {} 39 | 40 | const Divider = (props: DividerProps) => { 41 | return
42 | } 43 | ``` 44 | 45 | ```tsx 46 | type DividerProps = {} 47 | 48 | const Divider = (props: Props) => { 49 | return
50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/docs/rules/prefer-early-return.md: -------------------------------------------------------------------------------- 1 | # Prefer early returns over nesting in function declarations. (prefer-early-return) 2 | 3 | A function whose last statement is a conditional statement adds unnecessary nesting and makes the code harder to read. An early return often makes the block more readable. 4 | 5 | ## Rule Details 6 | 7 | The following patterns are considered warnings: 8 | 9 | ```js 10 | function foo() { 11 | if (a) { 12 | b() 13 | c() 14 | } 15 | } 16 | 17 | function qux() { 18 | a() 19 | if (b) { 20 | c() 21 | d() 22 | } 23 | } 24 | ``` 25 | 26 | The following patterns are not warnings: 27 | 28 | ```js 29 | function foo() { 30 | if (!a) { 31 | return 32 | } 33 | 34 | b() 35 | c() 36 | } 37 | 38 | function bar() { 39 | if (a) { 40 | b() 41 | c() 42 | } 43 | 44 | d() 45 | } 46 | 47 | function baz() { 48 | if (a) { 49 | b() 50 | c() 51 | } else { 52 | d() 53 | } 54 | } 55 | ``` 56 | 57 | ### Options 58 | 59 | This plugin takes an option object: 60 | 61 | ```ts 62 | interface Options { 63 | maxStatements: number // default: 1 64 | } 65 | ``` 66 | 67 | - `maxStatements`: specifies the maximum number of statements allowed in the conditional block. The following will **not** be considered a warning: 68 | 69 | ```js 70 | function foo() { 71 | if (a) { 72 | b() 73 | } 74 | } 75 | ``` 76 | 77 | ## When Not To Use It 78 | 79 | If you don't care about nesting and readability, or dislike early returns, you can safely disable this rule. 80 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/docs/rules/prefer-use-effect-named-callback.md: -------------------------------------------------------------------------------- 1 | # Prefer early returns over nesting in function declarations. (prefer-use-effect-named-callback) 2 | 3 | A useEffect callback with name makes the effect block more readable and documented. 4 | 5 | ## Rule Details 6 | 7 | The following patterns are considered warnings: 8 | 9 | ```js 10 | function Component() { 11 | useEffect(() => {}, []) 12 | 13 | // ... 14 | } 15 | 16 | function Component() { 17 | useEffect(function () {}, []) 18 | 19 | // ... 20 | } 21 | ``` 22 | 23 | The following patterns are not warnings: 24 | 25 | ```js 26 | const effectCallback = () => {} 27 | function Component() { 28 | useEffect(effectCallback, []) 29 | 30 | // ... 31 | } 32 | 33 | function namedEffectCallback() {} 34 | function Component() { 35 | useEffect(namedEffectCallback, []) 36 | 37 | // ... 38 | } 39 | 40 | function Component() { 41 | useEffect(function namedEffectCallback() {}, []) 42 | 43 | // ... 44 | } 45 | ``` 46 | 47 | ## When Not To Use It 48 | 49 | If you don't care about readability, you can safely disable this rule. 50 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-vtex", 3 | "version": "2.3.0", 4 | "description": "VTEX's ESLint plugin", 5 | "main": "dist/index.js", 6 | "module": "dist/index.mjs", 7 | "types": "dist/index.d.ts", 8 | "files": [ 9 | "docs", 10 | "dist" 11 | ], 12 | "license": "MIT", 13 | "bugs": { 14 | "url": "https://github.com/vtex/javascript/issues" 15 | }, 16 | "homepage": "https://github.com/vtex/typescript", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/vtex/typescript.git", 20 | "directory": "packages/eslint-plugin-vtex" 21 | }, 22 | "contributors": [ 23 | "Christian Kaisermann ", 24 | "Gabriel Takashi Katakura " 25 | ], 26 | "keywords": [ 27 | "eslint", 28 | "eslint-plugin", 29 | "vtex" 30 | ], 31 | "scripts": { 32 | "version": "chan release $npm_package_version && git add CHANGELOG.md", 33 | "watch": "tsup src/index.ts --watch", 34 | "build": "tsup src/index.ts --format esm,cjs,iife --dts", 35 | "test": "jest", 36 | "prepare": "yarn build" 37 | }, 38 | "jest": { 39 | "transform": { 40 | "\\.(js|ts)$": [ 41 | "babel-jest", 42 | { 43 | "configFile": "./test/babel.config.js" 44 | } 45 | ] 46 | }, 47 | "testPathIgnorePatterns": [ 48 | "/node_modules/" 49 | ], 50 | "collectCoverage": false 51 | }, 52 | "dependencies": { 53 | "@typescript-eslint/utils": "^5.15.0" 54 | }, 55 | "devDependencies": { 56 | "@babel/core": "^7.24.4", 57 | "@babel/preset-env": "^7.24.4", 58 | "@babel/preset-typescript": "^7.24.1", 59 | "@typescript-eslint/experimental-utils": "^5.15.0", 60 | "@typescript-eslint/parser": "^5.15.0", 61 | "babel-jest": "^29.7.0", 62 | "jest": "^27.5.1", 63 | "tsup": "^8.0.2" 64 | }, 65 | "peerDependencies": { 66 | "eslint": "^6 || ^7 || ^8" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/configs/recommended.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from 'eslint' 2 | 3 | export const recommended: Linter.BaseConfig = { 4 | rules: { 5 | // Prefer an early return to prevent nesting and improve code readability. 6 | 'vtex/prefer-early-return': [ 7 | 'warn', 8 | { 9 | maxStatements: 2, 10 | }, 11 | ], 12 | 13 | 'vtex/consistent-props-type': 'off', 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/createRule.ts: -------------------------------------------------------------------------------- 1 | import { ESLintUtils } from '@typescript-eslint/utils' 2 | 3 | export const createRule = ESLintUtils.RuleCreator( 4 | (name) => 5 | `https://github.com/vtex/typescript/tree/main/packages/eslint-plugin-vtex/docs/rules/${name}.md` 6 | ) 7 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/index.ts: -------------------------------------------------------------------------------- 1 | import { recommended } from './configs/recommended' 2 | import { consistentPropsType } from './rules/consistent-props-type' 3 | import { preferEarlyReturn } from './rules/prefer-early-return' 4 | import { preferUseEffectNamedCallback } from './rules/prefer-use-effect-named-callback' 5 | 6 | // tsdx doesn't support we configure our tsconfig.json 7 | // to target module commonjs, so we need to manually 8 | // use module.exports here 9 | module.exports = { 10 | configs: { 11 | recommended, 12 | }, 13 | 14 | // TODO: these rules could be auto-generated using fs+path, 15 | // but tsdx doesn't works well with dynamic imports, 16 | // so we need to change our build system first 17 | rules: { 18 | 'consistent-props-type': consistentPropsType, 19 | 'prefer-early-return': preferEarlyReturn, 20 | 'prefer-use-effect-named-callback': preferUseEffectNamedCallback, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/rules/__tests__/consistent-props-type.test.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from '@typescript-eslint/utils/dist/ts-eslint' 2 | 3 | import { consistentPropsType } from '../consistent-props-type' 4 | 5 | const ruleTester = new RuleTester({ 6 | parser: require.resolve('@typescript-eslint/parser'), 7 | parserOptions: { 8 | ecmaFeatures: { 9 | jsx: true, 10 | }, 11 | }, 12 | }) 13 | 14 | ruleTester.run('consistent-props-type', consistentPropsType, { 15 | valid: [ 16 | { 17 | code: ` 18 | const func = (args: unknown) => {} 19 | `, 20 | }, 21 | 22 | { 23 | code: ` 24 | type DividerProps = {} 25 | 26 | const Divider = (props: DividerProps) => { 27 | return
28 | } 29 | `, 30 | }, 31 | 32 | { 33 | code: ` 34 | type DividerProps = {} 35 | 36 | const Divider: FC = (props) => { 37 | return
38 | } 39 | `, 40 | }, 41 | 42 | { 43 | code: ` 44 | type DividerProps = {} 45 | 46 | const Divider = (props: Props) => { 47 | return
48 | } 49 | `, 50 | }, 51 | 52 | { 53 | code: ` 54 | const createComponent = < 55 | TComponent extends FC, 56 | Props extends ComponentProps 57 | >(Component: TComponent) => { 58 | const Wrapper = (props: T) => { 59 | return 60 | } 61 | 62 | return Wrapper 63 | } 64 | `, 65 | }, 66 | ], 67 | 68 | invalid: [ 69 | { 70 | code: ` 71 | const Divider = (props: { inline: boolean }) => { 72 | return
73 | } 74 | `, 75 | errors: [ 76 | { 77 | messageId: 'invalidInlinePropsType', 78 | data: { expectedPropsTypeName: 'DividerProps' }, 79 | }, 80 | ], 81 | }, 82 | 83 | { 84 | code: ` 85 | function Divider(props: { inline: boolean }) { 86 | return
87 | } 88 | `, 89 | errors: [ 90 | { 91 | messageId: 'invalidInlinePropsType', 92 | data: { expectedPropsTypeName: 'DividerProps' }, 93 | }, 94 | ], 95 | }, 96 | 97 | { 98 | code: ` 99 | function Divider({ inline }: { inline: boolean }) { 100 | return
101 | } 102 | `, 103 | errors: [ 104 | { 105 | messageId: 'invalidInlinePropsType', 106 | data: { expectedPropsTypeName: 'DividerProps' }, 107 | }, 108 | ], 109 | }, 110 | 111 | { 112 | code: ` 113 | function Divider({ inline, ...props }: { inline: boolean }) { 114 | return
115 | } 116 | `, 117 | errors: [ 118 | { 119 | messageId: 'invalidInlinePropsType', 120 | data: { expectedPropsTypeName: 'DividerProps' }, 121 | }, 122 | ], 123 | }, 124 | 125 | { 126 | code: ` 127 | function Divider({ ...props }: { inline: boolean }) { 128 | return
129 | } 130 | `, 131 | errors: [ 132 | { 133 | messageId: 'invalidInlinePropsType', 134 | data: { expectedPropsTypeName: 'DividerProps' }, 135 | }, 136 | ], 137 | }, 138 | 139 | { 140 | code: ` 141 | const Divider = (props: { inline: boolean } & { intersection: true }) => { 142 | return
143 | } 144 | `, 145 | errors: [ 146 | { 147 | messageId: 'invalidInlinePropsType', 148 | data: { expectedPropsTypeName: 'DividerProps' }, 149 | }, 150 | ], 151 | }, 152 | 153 | { 154 | code: ` 155 | const Divider = (props: { inline: boolean } | { union: true }) => { 156 | return
157 | } 158 | `, 159 | errors: [ 160 | { 161 | messageId: 'invalidInlinePropsType', 162 | data: { expectedPropsTypeName: 'DividerProps' }, 163 | }, 164 | ], 165 | }, 166 | 167 | { 168 | code: ` 169 | type Props = {} 170 | 171 | const Divider = (props: Props) => { 172 | return
173 | } 174 | `, 175 | errors: [ 176 | { 177 | messageId: 'invalidPropsTypeName', 178 | data: { expectedPropsTypeName: 'DividerProps' }, 179 | }, 180 | ], 181 | }, 182 | 183 | { 184 | code: ` 185 | type Props = {} 186 | 187 | const Divider: FC = (props) => { 188 | return
189 | } 190 | `, 191 | errors: [ 192 | { 193 | messageId: 'invalidPropsTypeName', 194 | data: { expectedPropsTypeName: 'DividerProps' }, 195 | }, 196 | ], 197 | }, 198 | 199 | { 200 | code: ` 201 | type DividerProps = {} 202 | 203 | const Divider: FC = (props) => { 204 | return
205 | } 206 | `, 207 | errors: [ 208 | { 209 | messageId: 'propsTypeIncomplete', 210 | data: { expectedPropsTypeName: 'DividerProps' }, 211 | }, 212 | ], 213 | }, 214 | 215 | { 216 | code: ` 217 | type DividerProps = {} 218 | 219 | const Divider: FC = (props) => { 220 | return
221 | } 222 | `, 223 | errors: [ 224 | { 225 | messageId: 'propsTypeIncomplete', 226 | data: { expectedPropsTypeName: 'DividerProps' }, 227 | }, 228 | ], 229 | }, 230 | 231 | { 232 | code: ` 233 | type Props = {} 234 | 235 | const Divider = (props: T) => { 236 | return
237 | } 238 | `, 239 | errors: [ 240 | { messageId: 'genericsPropsTypeName' }, 241 | { 242 | messageId: 'invalidPropsTypeName', 243 | data: { expectedPropsTypeName: 'DividerProps' }, 244 | }, 245 | ], 246 | }, 247 | 248 | { 249 | code: ` 250 | type Props = {} 251 | 252 | function Divider(props: T) { 253 | return
254 | } 255 | `, 256 | errors: [ 257 | { messageId: 'genericsPropsTypeName' }, 258 | { 259 | messageId: 'invalidPropsTypeName', 260 | data: { expectedPropsTypeName: 'DividerProps' }, 261 | }, 262 | ], 263 | }, 264 | 265 | { 266 | code: ` 267 | type DividerProps = {} 268 | 269 | const Divider = (props: Props) => { 270 | return
271 | } 272 | `, 273 | errors: [ 274 | { 275 | messageId: 'propsTypeIncomplete', 276 | data: { expectedPropsTypeName: 'DividerProps' }, 277 | }, 278 | ], 279 | }, 280 | 281 | { 282 | code: ` 283 | type DividerProps = {} 284 | 285 | const Divider = (props: Props) => { 286 | return
287 | } 288 | `, 289 | errors: [ 290 | { 291 | messageId: 'propsTypeIncomplete', 292 | data: { expectedPropsTypeName: 'DividerProps' }, 293 | }, 294 | ], 295 | }, 296 | ], 297 | }) 298 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/rules/__tests__/prefer-early-return.test.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from '@typescript-eslint/utils/dist/ts-eslint' 2 | import { AST_NODE_TYPES } from '@typescript-eslint/types' 3 | 4 | import { preferEarlyReturn } from '../prefer-early-return' 5 | 6 | const ruleTester = new RuleTester() 7 | 8 | const error = { 9 | messageId: 'default', 10 | type: AST_NODE_TYPES.IfStatement, 11 | } as const 12 | 13 | ruleTester.run('prefer-early-return', preferEarlyReturn, { 14 | valid: [ 15 | { 16 | code: `function foo() { 17 | if (!something) { return; } 18 | 19 | doSomething(); 20 | doSomethingElse(); 21 | }`, 22 | }, 23 | { 24 | code: `function foo() { 25 | if (something) { 26 | doSomething(); 27 | } 28 | }`, 29 | }, 30 | { 31 | code: `function foo() { 32 | if (something) 33 | doSomething(); 34 | }`, 35 | }, 36 | { 37 | code: `function foo() { 38 | if (something) { 39 | doSomething(); 40 | doSomethingElse(); 41 | } 42 | }`, 43 | options: [{ maxStatements: 2 }], 44 | }, 45 | { 46 | code: `function foo() { 47 | if (something) { 48 | doSomething(); 49 | doSomethingElse(); 50 | } 51 | 52 | someOtherThing(); 53 | }`, 54 | }, 55 | { 56 | code: `function foo() { 57 | if (something) { 58 | doSomething(); 59 | doSomethingElse(); 60 | } else { 61 | doAnotherThing(); 62 | } 63 | }`, 64 | }, 65 | { 66 | code: `var foo = function() { 67 | if (something) { 68 | doSomething(); 69 | } 70 | }`, 71 | }, 72 | { 73 | code: `var foo = () => { 74 | if (something) { 75 | doSomething(); 76 | } 77 | }`, 78 | parserOptions: { ecmaVersion: 6 }, 79 | }, 80 | { 81 | code: `var foo = function() { 82 | if (something) { 83 | doSomething(); 84 | } 85 | }`, 86 | }, 87 | { 88 | code: "var foo = () => 'bar'", 89 | parserOptions: { ecmaVersion: 6 }, 90 | }, 91 | ], 92 | 93 | invalid: [ 94 | { 95 | code: `function foo() { 96 | if (something) { 97 | doSomething(); 98 | doSomethingElse(); 99 | } 100 | }`, 101 | options: [{ maxStatements: 1 }], 102 | errors: [error], 103 | }, 104 | { 105 | code: `function foo() { 106 | if (something) 107 | doSomething(); 108 | }`, 109 | options: [{ maxStatements: 0 }], 110 | errors: [error], 111 | }, 112 | { 113 | code: `function foo() { 114 | if (something) { 115 | doSomething(); 116 | } 117 | }`, 118 | options: [{ maxStatements: 0 }], 119 | errors: [error], 120 | }, 121 | { 122 | code: `var foo = function() { 123 | if (something) { 124 | doSomething(); 125 | doSomethingElse(); 126 | } 127 | }`, 128 | options: [{ maxStatements: 1 }], 129 | errors: [error], 130 | }, 131 | { 132 | code: `var foo = () => { 133 | if (something) { 134 | doSomething(); 135 | doSomethingElse(); 136 | } 137 | }`, 138 | parserOptions: { ecmaVersion: 6 }, 139 | options: [{ maxStatements: 1 }], 140 | errors: [error], 141 | }, 142 | { 143 | code: `callback(function() { 144 | if (something) { 145 | doSomething(); 146 | doSomethingElse(); 147 | } 148 | })`, 149 | options: [{ maxStatements: 1 }], 150 | errors: [error], 151 | }, 152 | { 153 | code: `function foo() { 154 | var bar = 'bar' 155 | if (something) { 156 | doSomething() 157 | doSomethingElse() 158 | } 159 | }`, 160 | options: [{ maxStatements: 1 }], 161 | errors: [error], 162 | }, 163 | ], 164 | }) 165 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/rules/__tests__/prefer-use-effect-named-callback.test.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from '@typescript-eslint/utils/dist/ts-eslint' 2 | import { AST_NODE_TYPES } from '@typescript-eslint/types' 3 | 4 | import { preferUseEffectNamedCallback } from '../prefer-use-effect-named-callback' 5 | 6 | const ruleTester = new RuleTester() 7 | 8 | const arrowFunctionError = { 9 | messageId: 'default', 10 | type: AST_NODE_TYPES.ArrowFunctionExpression, 11 | } as const 12 | 13 | const functionExpressionError = { 14 | messageId: 'default', 15 | type: AST_NODE_TYPES.FunctionExpression, 16 | } as const 17 | 18 | ruleTester.run( 19 | 'prefer-use-effect-named-callback', 20 | preferUseEffectNamedCallback, 21 | { 22 | valid: [ 23 | { 24 | code: `function Component() { 25 | useEffect(function namedEffect() {}, []) 26 | return null 27 | }`, 28 | }, 29 | { 30 | code: ` 31 | function Component() { 32 | React.useEffect(function namedEffect() {}, []) 33 | return null 34 | } 35 | `, 36 | }, 37 | { 38 | code: ` 39 | /* eslint-env es6 */ 40 | const effect = () => {} 41 | function Component() { 42 | useEffect(effect, []) 43 | return null; 44 | }`, 45 | }, 46 | { 47 | code: ` 48 | /* eslint-env es6 */ 49 | const effect = () => {} 50 | function Component() { 51 | React.useEffect(effect, []) 52 | return null; 53 | }`, 54 | }, 55 | { 56 | code: ` 57 | /* eslint-env es6 */ 58 | const effect = function () {} 59 | function Component() { 60 | React.useEffect(effect, []) 61 | return null; 62 | }`, 63 | }, 64 | { 65 | code: ` 66 | /* eslint-env es6 */ 67 | const effect = function () {} 68 | function Component() { 69 | React.useEffect(debouce(() => {}, 10), []) 70 | return null; 71 | }`, 72 | }, 73 | ], 74 | 75 | invalid: [ 76 | { 77 | code: ` 78 | function Component() { 79 | useEffect(function () {}, []) 80 | return null 81 | } 82 | `, 83 | errors: [functionExpressionError], 84 | }, 85 | { 86 | code: ` 87 | function Component() { 88 | React.useEffect(function () {}, []) 89 | return null 90 | } 91 | `, 92 | errors: [functionExpressionError], 93 | }, 94 | { 95 | code: ` 96 | /* eslint-env es6 */ 97 | function Component() { 98 | useEffect(() => {}, []) 99 | return null 100 | } 101 | `, 102 | errors: [arrowFunctionError], 103 | }, 104 | { 105 | code: ` 106 | /* eslint-env es6 */ 107 | function Component() { 108 | React.useEffect(() => {}, []) 109 | return null 110 | } 111 | `, 112 | errors: [arrowFunctionError], 113 | }, 114 | ], 115 | } 116 | ) 117 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/rules/consistent-props-type.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable vtex/prefer-early-return */ 2 | import type { TSESTree } from '@typescript-eslint/utils' 3 | 4 | import { isComponentName } from '../utils/react/isComponentName' 5 | import { createRule } from '../createRule' 6 | import { isInsideAnotherFunction } from '../utils/estree/isInsideAnotherFunction' 7 | import { getFunctionNodeName } from '../utils/estree/getFunctionNodeName' 8 | import { isFunctionNode } from '../utils/estree/isFunctionNode' 9 | 10 | export const consistentPropsType = createRule({ 11 | name: 'consistent-props-type', 12 | 13 | meta: { 14 | type: 'suggestion', 15 | docs: { 16 | recommended: false, 17 | description: 'Consistent way to define props type', 18 | }, 19 | messages: { 20 | invalidPropsTypeName: 21 | 'Invalid props type name. Must be: {{expectedPropsTypeName}}', 22 | 23 | propsTypeIncomplete: 24 | 'Invalid inline props type. {{expectedPropsTypeName}} must define all the props.', 25 | 26 | invalidInlinePropsType: 27 | 'Invalid inline props type. You must to create an type {{expectedPropsTypeName}}', 28 | 29 | genericsPropsTypeName: 'Invalid generics props type name. Must be: Props', 30 | }, 31 | schema: [], 32 | }, 33 | 34 | defaultOptions: [], 35 | 36 | create(context) { 37 | return { 38 | ':function > :matches(Identifier, ObjectPattern) > TSTypeAnnotation': 39 | function (node: TSESTree.TSTypeAnnotation) { 40 | if (isInsideAnotherFunction(context)) return 41 | 42 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 43 | const functionNode = context 44 | .getAncestors() 45 | .reverse() 46 | .find(isFunctionNode)! 47 | 48 | const functionName = getFunctionNodeName(functionNode) 49 | const [typeParameter] = functionNode.typeParameters?.params ?? [] 50 | 51 | if (!isComponentName(functionName)) return 52 | 53 | const expectedPropsTypeName = `${functionName}Props` 54 | 55 | if (typeParameter) { 56 | if (typeParameter.name.name !== 'Props') { 57 | context.report({ 58 | node: typeParameter.name, 59 | messageId: 'genericsPropsTypeName', 60 | }) 61 | } 62 | 63 | if ( 64 | typeParameter.constraint && 65 | typeParameter.constraint?.type !== 'TSTypeReference' 66 | ) { 67 | context.report({ 68 | node: typeParameter.constraint, 69 | messageId: 'propsTypeIncomplete', 70 | data: { expectedPropsTypeName }, 71 | }) 72 | } 73 | 74 | if ( 75 | typeParameter.constraint?.type === 'TSTypeReference' && 76 | typeParameter.constraint.typeName.type === 'Identifier' && 77 | typeParameter.constraint.typeName.name !== expectedPropsTypeName 78 | ) { 79 | context.report({ 80 | node: typeParameter.constraint, 81 | messageId: 'invalidPropsTypeName', 82 | data: { expectedPropsTypeName }, 83 | }) 84 | } 85 | 86 | return 87 | } 88 | 89 | if (node.typeAnnotation.type !== 'TSTypeReference') { 90 | context.report({ 91 | node: node.typeAnnotation, 92 | messageId: 'invalidInlinePropsType', 93 | data: { expectedPropsTypeName }, 94 | }) 95 | 96 | return 97 | } 98 | 99 | if ( 100 | node.typeAnnotation.typeName.type === 'Identifier' && 101 | node.typeAnnotation.typeName.name !== expectedPropsTypeName 102 | ) { 103 | context.report({ 104 | node: node.typeAnnotation, 105 | messageId: 'invalidPropsTypeName', 106 | data: { expectedPropsTypeName }, 107 | }) 108 | } 109 | }, 110 | 'VariableDeclarator > Identifier > TSTypeAnnotation': function ( 111 | node: TSESTree.TSTypeAnnotation & { parent: TSESTree.Identifier } 112 | ) { 113 | if (isInsideAnotherFunction(context)) return 114 | 115 | const expectedPropsTypeName = `${node.parent.name}Props` 116 | 117 | if ( 118 | node.typeAnnotation.type === 'TSTypeReference' && 119 | node.typeAnnotation.typeName.type === 'Identifier' && 120 | node.typeAnnotation.typeName.name === 'FC' && 121 | node.typeAnnotation.typeParameters 122 | ) { 123 | const [typeParameter] = node.typeAnnotation.typeParameters.params 124 | 125 | if ( 126 | typeParameter.type === 'TSTypeReference' && 127 | typeParameter.typeName.type === 'Identifier' && 128 | typeParameter.typeName.name !== expectedPropsTypeName 129 | ) { 130 | context.report({ 131 | node: typeParameter, 132 | messageId: 'invalidPropsTypeName', 133 | data: { expectedPropsTypeName }, 134 | }) 135 | } 136 | 137 | if (typeParameter.type !== 'TSTypeReference') { 138 | context.report({ 139 | node: typeParameter, 140 | messageId: 'propsTypeIncomplete', 141 | data: { expectedPropsTypeName }, 142 | }) 143 | } 144 | } 145 | }, 146 | } 147 | }, 148 | }) 149 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/rules/prefer-early-return.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from '@typescript-eslint/utils' 2 | 3 | import { createRule } from '../createRule' 4 | 5 | function isLonelyIfStatement( 6 | node: TSESTree.Node 7 | ): node is TSESTree.IfStatement { 8 | return node.type === 'IfStatement' && node.alternate == null 9 | } 10 | 11 | type Options = [{ maxStatements: number }] 12 | 13 | export const preferEarlyReturn = createRule({ 14 | name: 'prefer-early-return', 15 | 16 | meta: { 17 | type: 'layout', 18 | docs: { 19 | recommended: 'error', 20 | description: 21 | 'Prefer early returns over full-body conditional wrapping in function declarations.', 22 | }, 23 | schema: [ 24 | { 25 | type: 'object', 26 | properties: { 27 | maxStatements: { 28 | type: 'integer', 29 | }, 30 | }, 31 | additionalProperties: false, 32 | }, 33 | ], 34 | messages: { 35 | default: 36 | 'Prefer an early return to prevent nesting and improve code readability', 37 | }, 38 | }, 39 | 40 | defaultOptions: [{ maxStatements: 1 }], 41 | 42 | create(context) { 43 | const options = { 44 | ...context.options[0], 45 | } 46 | 47 | const { maxStatements } = options 48 | 49 | function isOffendingConsequent(consequentNode: TSESTree.Statement) { 50 | return ( 51 | (consequentNode.type === 'ExpressionStatement' && 52 | maxStatements === 0) || 53 | (consequentNode.type === 'BlockStatement' && 54 | consequentNode.body.length > maxStatements) 55 | ) 56 | } 57 | 58 | function isOffendingIfStatement(ifNode: TSESTree.Statement) { 59 | return ( 60 | isLonelyIfStatement(ifNode) && isOffendingConsequent(ifNode.consequent) 61 | ) 62 | } 63 | 64 | function checkFunctionBody( 65 | fnNode: 66 | | TSESTree.FunctionDeclaration 67 | | TSESTree.FunctionExpression 68 | | TSESTree.ArrowFunctionExpression 69 | ) { 70 | const bodyNode = fnNode.body 71 | 72 | if (bodyNode.type !== 'BlockStatement' || bodyNode.body.length === 0) { 73 | return 74 | } 75 | 76 | const lastNode = bodyNode.body[bodyNode.body.length - 1] 77 | 78 | if (!isOffendingIfStatement(lastNode)) { 79 | return 80 | } 81 | 82 | context.report({ 83 | node: lastNode, 84 | messageId: 'default', 85 | }) 86 | } 87 | 88 | return { 89 | FunctionDeclaration: checkFunctionBody, 90 | FunctionExpression: checkFunctionBody, 91 | ArrowFunctionExpression: checkFunctionBody, 92 | } 93 | }, 94 | }) 95 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/rules/prefer-use-effect-named-callback.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from '@typescript-eslint/utils' 2 | 3 | import { createRule } from '../createRule' 4 | 5 | const USE_EFFECT_NAME = 'useEffect' 6 | 7 | function checkCallExpressionIsUseEffect(node: TSESTree.Node) { 8 | if (node.type !== 'CallExpression') { 9 | return false 10 | } 11 | 12 | if (!node.callee) { 13 | return false 14 | } 15 | 16 | // verify this scenario `useEffect(...)` 17 | if ( 18 | node.callee.type === 'Identifier' && 19 | node.callee.name === USE_EFFECT_NAME 20 | ) { 21 | return true 22 | } 23 | 24 | // verify this scenario `React.useEffect(...)` 25 | if ( 26 | node.callee.type === 'MemberExpression' && 27 | node.callee.property && 28 | node.callee.property.type === 'Identifier' && 29 | node.callee.property.name === USE_EFFECT_NAME 30 | ) { 31 | return true 32 | } 33 | 34 | return false 35 | } 36 | 37 | export const preferUseEffectNamedCallback = createRule({ 38 | name: 'prefer-use-effect-named-callback', 39 | 40 | meta: { 41 | type: 'layout', 42 | docs: { 43 | recommended: 'error', 44 | description: 45 | 'Prefer useEffect with named function or constant callbacks.', 46 | }, 47 | messages: { 48 | default: 'Prefer useEffect with named function or constant callbacks.', 49 | }, 50 | schema: [], 51 | }, 52 | 53 | defaultOptions: [], 54 | 55 | create(context) { 56 | function checkUseEffectCallExpression( 57 | callExpressionNode: TSESTree.CallExpression 58 | ) { 59 | if (!checkCallExpressionIsUseEffect(callExpressionNode)) { 60 | return 61 | } 62 | 63 | const [useEffectFirstArgument] = callExpressionNode.arguments 64 | let hasError = false 65 | 66 | /* check this case `useEffect(() => {}, [])` */ 67 | if ( 68 | useEffectFirstArgument.type === 'ArrowFunctionExpression' && 69 | useEffectFirstArgument.id === null 70 | ) { 71 | hasError = true 72 | } 73 | 74 | /* check this case `useEffect(function () {}, [])` */ 75 | if ( 76 | useEffectFirstArgument.type === 'FunctionExpression' && 77 | useEffectFirstArgument.id === null 78 | ) { 79 | hasError = true 80 | } 81 | 82 | if (hasError) { 83 | context.report({ 84 | node: useEffectFirstArgument, 85 | messageId: 'default', 86 | }) 87 | } 88 | } 89 | 90 | return { 91 | CallExpression: checkUseEffectCallExpression, 92 | } 93 | }, 94 | }) 95 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/utils/estree/getFunctionNodeName.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from '@typescript-eslint/utils' 2 | 3 | export const getFunctionNodeName = (node: TSESTree.Node) => { 4 | if (node.type === 'FunctionDeclaration') { 5 | return node.id?.name ?? '' 6 | } 7 | 8 | if ( 9 | node.parent?.type === 'VariableDeclarator' && 10 | node.parent.id.type === 'Identifier' 11 | ) { 12 | return node.parent.id.name 13 | } 14 | 15 | return '' 16 | } 17 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/utils/estree/isFunctionNode.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from '@typescript-eslint/utils' 2 | 3 | type FunctionNode = 4 | | TSESTree.ArrowFunctionExpression 5 | | TSESTree.FunctionDeclaration 6 | | TSESTree.FunctionExpression 7 | 8 | export const isFunctionNode = (node: TSESTree.Node): node is FunctionNode => { 9 | return ( 10 | node.type === 'ArrowFunctionExpression' || 11 | node.type === 'FunctionExpression' || 12 | node.type === 'FunctionDeclaration' 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/utils/estree/isInsideAnotherFunction.ts: -------------------------------------------------------------------------------- 1 | import type { RuleContext } from '@typescript-eslint/utils/dist/ts-eslint' 2 | 3 | import { isFunctionNode } from './isFunctionNode' 4 | 5 | export const isInsideAnotherFunction = ( 6 | context: RuleContext 7 | ) => { 8 | const functions = context.getAncestors().filter(isFunctionNode) 9 | 10 | return functions.length > 1 11 | } 12 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/src/utils/react/isComponentName.ts: -------------------------------------------------------------------------------- 1 | export const isComponentName = (name: string) => 2 | name.length > 0 && name[0] === name[0].toUpperCase() 3 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/test/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', { targets: { node: 'current' } }], 4 | '@babel/preset-typescript', 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /packages/eslint-plugin-vtex/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "importHelpers": true, 7 | "declaration": true, 8 | "sourceMap": true, 9 | "rootDir": "./src", 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "moduleResolution": "node", 16 | "jsx": "react", 17 | "esModuleInterop": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/prettier-config/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.0.0] - 2021-12-15 10 | ### Deprecated 11 | - Raises minimal prettier to ^2.4.0 due to bracketSameLine rule breaking change 12 | 13 | ## 0.3.2 - 2020-10-21 14 | ### Changed 15 | - Repo url in package.json 16 | 17 | ## 0.3.0 - 2020-06-01 18 | ### Added 19 | - `prettier` `v2.0` as acceptable dependency. 20 | 21 | ## 0.2.0 - 2020-04-28 22 | ### Changed 23 | - Make prettier always add paranthesis to arrow fn args 24 | 25 | ## [0.1.3] - 2020-01-24 26 | ### Fixed 27 | - Changelog on deploy workflow again. 28 | 29 | ## 0.1.2 - 2020-01-24 30 | ### Changed 31 | - Move project to mono repo. 32 | 33 | ### Fixed 34 | - Changelog on deploy workflow. 35 | 36 | [Unreleased]: https://github.com/vtex/typescript/compare/v1.0.0...HEAD 37 | [1.0.0]: https://github.com/vtex/typescript/compare/v0.3.2...v1.0.0 38 | [0.1.3]: https://github.com/vtex/js-standards/compare/v0.1.2...v0.1.3 39 | -------------------------------------------------------------------------------- /packages/prettier-config/README.md: -------------------------------------------------------------------------------- 1 | # `@vtex/prettier-config` 2 | 3 | This package provides VTEX's `.prettierrc` shared config. 4 | 5 | ## Installation 6 | 7 | Give that you already have `prettier` installed, run: 8 | 9 | ```bash 10 | yarn add -D @vtex/prettier-config 11 | ``` 12 | 13 | ## Usage 14 | 15 | After installing the module, add it to your `.prettierrc` configuration file: 16 | 17 | ```jsonc 18 | "@vtex/prettier-config" 19 | ``` 20 | 21 | Or add it to your `package.json`: 22 | 23 | ```json 24 | { 25 | ... 26 | "prettier": "@vtex/prettier-config" 27 | ... 28 | } 29 | ``` 30 | 31 | Differently from `eslint`, `prettier` shared presets are not extensible, so if you want to override some property, which is not encouraged, you'll need to use the `.prettierrc.js` file. 32 | 33 | For more information about configuring `prettier`, please check the [Prettier configuration documentation](https://prettier.io/docs/en/configuration.html). 34 | 35 | ## References 36 | 37 | - [`prettier` options documentation](https://prettier.io/docs/en/options.html) 38 | -------------------------------------------------------------------------------- /packages/prettier-config/index.js: -------------------------------------------------------------------------------- 1 | // Reference: https://prettier.io/docs/en/options.html 2 | module.exports = { 3 | $schema: 'http://json.schemastore.org/prettierrc', 4 | 5 | // Specify the line length that the printer will wrap on. 6 | // TODO https://github.com/vtex/javascript/issues/1 7 | printWidth: 80, 8 | 9 | // Specify the number of spaces per indentation-level. 10 | tabWidth: 2, 11 | 12 | // Indent lines with tabs instead of spaces. 13 | useTabs: false, 14 | 15 | // Print semicolons at the ends of statements. 16 | semi: false, 17 | 18 | // Use single quotes instead of double quotes. 19 | singleQuote: true, 20 | 21 | // Use single quotes instead of double quotes in JSX. 22 | jsxSingleQuote: false, 23 | 24 | // Change when properties in objects are quoted. 25 | quoteProps: 'as-needed', 26 | 27 | // Print trailing commas wherever possible when multi-line. (A single-line array, for example, never gets trailing commas.) 28 | trailingComma: 'es5', 29 | 30 | // Print spaces between brackets in object literals. 31 | bracketSpacing: true, 32 | 33 | // Put the > of a multi-line JSX element at the end of the last line instead of being alone on the next line 34 | bracketSameLine: false, 35 | 36 | // Include parentheses around a sole arrow function parameter. 37 | arrowParens: 'always', 38 | } 39 | -------------------------------------------------------------------------------- /packages/prettier-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vtex/prettier-config", 3 | "version": "1.0.0", 4 | "description": "VTEX shared prettier config", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "homepage": "https://github.com/vtex/typescript", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/vtex/typescript.git", 12 | "directory": "packages/prettier-config" 13 | }, 14 | "contributors": [ 15 | "Christian Kaisermann " 16 | ], 17 | "license": "MIT", 18 | "main": "index.js", 19 | "files": [ 20 | "index.js" 21 | ], 22 | "scripts": { 23 | "version": "chan release $npm_package_version && git add CHANGELOG.md" 24 | }, 25 | "peerDependencies": { 26 | "prettier": "^2.4.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/tsconfig/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.6.0] - 2021-09-13 10 | ### Added 11 | - `forceConsistentCasingInFileNames` property. 12 | 13 | ## 0.5.5 - 2020-11-19 14 | ### Fixed 15 | - Added `typescript@v4` as possible peer dep. 16 | 17 | ## 0.5.1 - 2020-10-21 18 | ### Changed 19 | - Repo url in package.json 20 | 21 | ## [0.4.3] - 2020-01-24 22 | ### Fixed 23 | - Changelog on deploy workflow again. 24 | 25 | ## [0.4.2] - 2020-01-24 26 | ### Fixed 27 | - Changelog on deploy workflow. 28 | 29 | ## 0.4.0 - 2020-01-24 30 | ### Changed 31 | - Move project to mono repo. 32 | 33 | ## 0.3.0 - 2019-09-24 34 | ### Added 35 | - `allowSyntheticDefaultImports` flag. 36 | 37 | ## 0.2.0 - 2019-08-29 38 | ### Added 39 | - Add `@types/node`, `@types/graphql` and `@types/jest` as default declarations. 40 | 41 | ## 0.1.1 - 2019-08-28 42 | ### Fixed 43 | - `tsconfig.json` declared as a JavaScript file. 44 | 45 | ## 0.1.0 - 2019-08-28 46 | ### Added 47 | - Base config in `index.js`. 48 | 49 | [Unreleased]: https://github.com/vtex/typescript/compare/v0.6.0...HEAD 50 | [0.6.0]: https://github.com/vtex/typescript/compare/v0.5.5...v0.6.0 51 | [0.4.3]: https://github.com/vtex/js-standards/compare/v0.4.2...v0.4.3 52 | [0.4.2]: https://github.com/vtex/js-standards/compare/v0.4.0...v0.4.2 53 | -------------------------------------------------------------------------------- /packages/tsconfig/README.md: -------------------------------------------------------------------------------- 1 | # VTEX `tsconfig.json` 2 | 3 | This is default `tsconfig.json` that should be used by all VTEX IO apps. 4 | 5 | ## Install 6 | 7 | Add a `tsconfig.json` file inside your builder folder (react or node) with the following 8 | 9 | ```json 10 | { 11 | "extends": "@vtex/tsconfig" 12 | } 13 | ``` 14 | 15 | And run 16 | 17 | ```bash 18 | yarn add -D @vtex/tsconfig 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vtex/tsconfig", 3 | "version": "0.6.0", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "homepage": "https://github.com/vtex/typescript", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/vtex/typescript.git", 11 | "directory": "packages/tsconfig" 12 | }, 13 | "main": "tsconfig.json", 14 | "license": "MIT", 15 | "scripts": { 16 | "version": "chan release $npm_package_version && git add CHANGELOG.md" 17 | }, 18 | "peerDependencies": { 19 | "typescript": "^3 || ^4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/tsconfig/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "jsx": "react", 7 | "lib": ["es2017", "dom", "es2018.promise", "esnext.asynciterable"], 8 | "module": "es6", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "skipLibCheck": true, 16 | "sourceMap": true, 17 | "strictFunctionTypes": true, 18 | "strictNullChecks": true, 19 | "strictPropertyInitialization": true, 20 | "target": "es2017", 21 | "forceConsistentCasingInFileNames": true 22 | }, 23 | "typeAcquisition": { 24 | "enable": false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/bootstrap-typescript.sh: -------------------------------------------------------------------------------- 1 | yarn add -D eslint prettier eslint-config-vtex @vtex/prettier-config typescript @vtex/tsconfig 2 | echo '{ "extends": "vtex", "plugins": ["vtex"] }' > .eslintrc 3 | echo '"@vtex/prettier-config"' > .prettierrc 4 | echo '{ "extends": "@vtex/tsconfig" }' > tsconfig.json 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vtex/tsconfig" 3 | } 4 | --------------------------------------------------------------------------------