├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .yarn └── releases │ └── yarn-3.2.3.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs └── rules │ ├── no-cjs-in-config.md │ ├── no-env-in-context.md │ ├── no-env-in-hooks.md │ ├── no-globals-in-created.md │ ├── no-this-in-fetch-data.md │ ├── no-timing-in-fetch-data.md │ └── require-func-head.md ├── lib ├── configs │ ├── base.js │ └── recommended.js ├── index.js ├── processors │ ├── .gitkeep │ └── index.js ├── rules │ ├── .gitkeep │ ├── __tests__ │ │ ├── no-cjs-in-config.test.js │ │ ├── no-env-in-context.test.js │ │ ├── no-env-in-hooks.test.js │ │ ├── no-globals-in-created.test.js │ │ ├── no-this-in-fetch-data.test.js │ │ ├── no-timing-in-fetch-data.test.js │ │ └── require-func-head.test.js │ ├── no-cjs-in-config.js │ ├── no-env-in-context.js │ ├── no-env-in-hooks.js │ ├── no-globals-in-created.js │ ├── no-this-in-fetch-data.js │ ├── no-timing-in-fetch-data.js │ └── require-func-head.js └── utils │ ├── index.js │ └── resolver.js ├── package.json ├── renovate.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'standard', 3 | rules: { 4 | 'no-template-curly-in-string': 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | ci: 13 | runs-on: ${{ matrix.os }} 14 | 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, macos-latest, windows-latest] 18 | node: [14, 16, 18] 19 | 20 | steps: 21 | - uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node }} 24 | 25 | - name: checkout 26 | uses: actions/checkout@master 27 | 28 | - name: cache node_modules 29 | uses: actions/cache@v2 30 | with: 31 | path: node_modules 32 | key: ${{ matrix.os }}-node-v${{ matrix.node }}-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }} 33 | 34 | - name: install 35 | run: yarn install 36 | 37 | - name: lint 38 | run: yarn lint 39 | 40 | - name: test 41 | run: yarn test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # yarn 64 | .pnp.* 65 | .yarn/* 66 | !.yarn/patches 67 | !.yarn/plugins 68 | !.yarn/releases 69 | !.yarn/sdks 70 | !.yarn/versions 71 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-3.2.3.cjs 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [4.0.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v3.2.0...v4.0.0) (2022-08-31) 6 | 7 | ## 3.2.0 (2022-03-13) 8 | 9 | 10 | ### Features 11 | 12 | * add base config ([fb94d7b](https://github.com/nuxt/eslint-plugin-nuxt/commit/fb94d7b20b6eded652b911cd82626c798192ea9e)) 13 | * add no-env-in-context ([42ada82](https://github.com/nuxt/eslint-plugin-nuxt/commit/42ada82b306c3c259388392da565bd5fef260c76)) 14 | * add no-env-in-hooks ([016f609](https://github.com/nuxt/eslint-plugin-nuxt/commit/016f6096cef49f5956d37c024faa64b7ac5ac96d)) 15 | * add no-env-in-mounted ([4b32052](https://github.com/nuxt/eslint-plugin-nuxt/commit/4b3205269be683d258a45f06f9cddb3a4126f4ad)) 16 | * add no-this-in-fetch ([94552c7](https://github.com/nuxt/eslint-plugin-nuxt/commit/94552c73299f2b60d58e895c1ab89917e2fca099)) 17 | * add no-timing-in-fetch-data ([1436642](https://github.com/nuxt/eslint-plugin-nuxt/commit/1436642cdd6bf41f95f53c17020be80b25fed296)) 18 | * add rule no-this-in-async-data ([96ce607](https://github.com/nuxt/eslint-plugin-nuxt/commit/96ce6077102c51bbabc51e606c0659c8b7ea1543)) 19 | * init no-this-in-async-data ([cb03b4d](https://github.com/nuxt/eslint-plugin-nuxt/commit/cb03b4d9e05d10d4646b584a26ee227a7db2c72e)) 20 | * init no-this-in-fetch ([581005a](https://github.com/nuxt/eslint-plugin-nuxt/commit/581005a1753e3394e021ee423d215e540f61c54c)) 21 | * move ssr to base ([1da2f0c](https://github.com/nuxt/eslint-plugin-nuxt/commit/1da2f0cb61f4a2edce473784934eb5e93d66f70d)) 22 | * **rule:** add no-globals-in-created ([09eaa50](https://github.com/nuxt/eslint-plugin-nuxt/commit/09eaa505a2a4691b94f7e1ae7e7ce2e19d5042c3)) 23 | * **rule:** add require-func-head in recommended ([#62](https://github.com/nuxt/eslint-plugin-nuxt/issues/62)) ([f7e7b87](https://github.com/nuxt/eslint-plugin-nuxt/commit/f7e7b87670c510358811b4bc2847c176c77f6f03)) 24 | * **rule:** no commonjs api in nuxt config ([eb02e0e](https://github.com/nuxt/eslint-plugin-nuxt/commit/eb02e0ebdb32608b67ad4de497313d5e10055b38)) 25 | * upgrade eslint to v7 ([3e46242](https://github.com/nuxt/eslint-plugin-nuxt/commit/3e4624268fd2678539c34e0119554a8fadc81484)) 26 | * upgrade eslint-plugin-vue to v6 ([b5b1bf4](https://github.com/nuxt/eslint-plugin-nuxt/commit/b5b1bf4c8abf5f2df66232130e83ef5412bce5a7)) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * correct category for no-timing-in-fetch-data ([ae6b499](https://github.com/nuxt/eslint-plugin-nuxt/commit/ae6b499653252e126b9d524dbe9a700d8e48cd5b)) 32 | * disallow `process.browser` in `no-env-in-hooks` ([#127](https://github.com/nuxt/eslint-plugin-nuxt/issues/127)) ([e421323](https://github.com/nuxt/eslint-plugin-nuxt/commit/e421323256ced99f8210105e3f2d215d7d4078a6)) 33 | * enable no-this-in-fetch only for nuxt < 2.12 ([a0e3f73](https://github.com/nuxt/eslint-plugin-nuxt/commit/a0e3f736c94d3504cad7b09a718d86df8b54f0e4)) 34 | * incompatible version of vue-eslint-parser ([a0e355f](https://github.com/nuxt/eslint-plugin-nuxt/commit/a0e355fb19613746810c4fa84b314a3724a78451)) 35 | * not-null check for hooks ([ee73e8b](https://github.com/nuxt/eslint-plugin-nuxt/commit/ee73e8b68d541580a7017dbe8ed5c8e51bb257c7)) 36 | * nuxt/require-func-head doesnt work with factories ([11492e2](https://github.com/nuxt/eslint-plugin-nuxt/commit/11492e20fb0a6ceaf9b7d379dada7e6b48da63ed)), closes [#94](https://github.com/nuxt/eslint-plugin-nuxt/issues/94) 37 | * remove require-func-head from recommend ([25d108c](https://github.com/nuxt/eslint-plugin-nuxt/commit/25d108cbb30005048d1972d285e70e8e67ceefe5)), closes [#93](https://github.com/nuxt/eslint-plugin-nuxt/issues/93) 38 | * rule not found ([976f28d](https://github.com/nuxt/eslint-plugin-nuxt/commit/976f28d5a3f6e7f75c082f4c941e2c36b86331b1)) 39 | * undefined value in nuxt/no-env-in-context ([b9fed00](https://github.com/nuxt/eslint-plugin-nuxt/commit/b9fed007783217495c2c324f0d74576d3fb665a6)) 40 | * use filename instead of full path ([d22e011](https://github.com/nuxt/eslint-plugin-nuxt/commit/d22e011c90700cc51279345740b2e6648c442c59)) 41 | * wrong index file ([f95cf1d](https://github.com/nuxt/eslint-plugin-nuxt/commit/f95cf1d923ebb4a2fb7c0e8675c7ec54fb828671)) 42 | 43 | ## [3.1.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v3.0.0...v3.1.0) (2021-11-28) 44 | 45 | ## [3.0.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v2.0.2...v3.0.0) (2021-10-24) 46 | 47 | ### [2.0.2](https://github.com/nuxt/eslint-plugin-nuxt/compare/v2.0.0...v2.0.2) (2021-10-24) 48 | 49 | 50 | ### Bug Fixes 51 | 52 | * disallow `process.browser` in `no-env-in-hooks` ([#127](https://github.com/nuxt/eslint-plugin-nuxt/issues/127)) ([07299c0](https://github.com/nuxt/eslint-plugin-nuxt/commit/07299c0ef16dc8efeef19bb2a29df9381aa99e77)) 53 | 54 | ### [2.0.1](https://github.com/nuxt/eslint-plugin-nuxt/compare/v2.0.0...v2.0.1) (2021-10-24) 55 | 56 | 57 | ### Bug Fixes 58 | 59 | * disallow `process.browser` in `no-env-in-hooks` ([#127](https://github.com/nuxt/eslint-plugin-nuxt/issues/127)) ([07299c0](https://github.com/nuxt/eslint-plugin-nuxt/commit/07299c0ef16dc8efeef19bb2a29df9381aa99e77)) 60 | 61 | ## [2.0.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v1.0.0...v2.0.0) (2020-11-14) 62 | 63 | ## [1.0.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.5.2...v1.0.0) (2020-05-12) 64 | 65 | 66 | ### Features 67 | 68 | * upgrade eslint to v7 ([c57a3de](https://github.com/nuxt/eslint-plugin-nuxt/commit/c57a3de0aaa71d33b09a1710d6b5c7ce478f71e3)) 69 | 70 | 71 | ### Bug Fixes 72 | 73 | * nuxt/require-func-head doesnt work with factories ([5480fd0](https://github.com/nuxt/eslint-plugin-nuxt/commit/5480fd0d8eda0d6a5794548d60d3d84db8cbf86e)), closes [#94](https://github.com/nuxt/eslint-plugin-nuxt/issues/94) 74 | * remove require-func-head from recommend ([580ffa2](https://github.com/nuxt/eslint-plugin-nuxt/commit/580ffa26fa533c6e2cef2330965609ca879b118d)), closes [#93](https://github.com/nuxt/eslint-plugin-nuxt/issues/93) 75 | 76 | ### [0.5.2](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.5.1...v0.5.2) (2020-03-20) 77 | 78 | 79 | ### Bug Fixes 80 | 81 | * enable no-this-in-fetch only for nuxt < 2.12 ([66fc2e0](https://github.com/nuxt/eslint-plugin-nuxt/commit/66fc2e0f517865b3bf160eb096a5c81f788b33af)) 82 | 83 | ### [0.5.1](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.5.0...v0.5.1) (2020-02-09) 84 | 85 | 86 | ### Features 87 | 88 | * **rule:** add require-func-head in recommended ([#62](https://github.com/nuxt/eslint-plugin-nuxt/issues/62)) ([7d0926f](https://github.com/nuxt/eslint-plugin-nuxt/commit/7d0926f4a73bbd3c91496f7ab8e0fc232f38d423)) 89 | 90 | 91 | ### Bug Fixes 92 | 93 | * correct category for no-timing-in-fetch-data ([bbf6ce9](https://github.com/nuxt/eslint-plugin-nuxt/commit/bbf6ce985b0e40c1c9450fd3b3a7188d3a7a214a)) 94 | 95 | ## [0.5.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.4.3...v0.5.0) (2019-11-09) 96 | 97 | 98 | ### Features 99 | 100 | * upgrade eslint-plugin-vue to v6 ([ecf42f2](https://github.com/nuxt/eslint-plugin-nuxt/commit/ecf42f2)) 101 | 102 | ## [0.4.3](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.4.0...v0.4.3) (2019-03-12) 103 | 104 | 105 | ### Bug Fixes 106 | 107 | * incompatible version of vue-eslint-parser ([9bb12ae](https://github.com/nuxt/eslint-plugin-nuxt/commit/9bb12ae)) 108 | 109 | 110 | 111 | ## [0.4.2](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.4.0...v0.4.2) (2019-02-21) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * use filename instead of full path ([4c62a37](https://github.com/nuxt/eslint-plugin-nuxt/commit/4c62a37)) 117 | 118 | 119 | 120 | 121 | # [0.3.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.2.0...v0.3.0) (2018-12-19) 122 | 123 | 124 | ### Bug Fixes 125 | 126 | * not-null check for hooks ([daa93f3](https://github.com/nuxt/eslint-plugin-nuxt/commit/daa93f3)) 127 | 128 | 129 | ### Features 130 | 131 | * add no-env-in-hooks ([bdcc9ec](https://github.com/nuxt/eslint-plugin-nuxt/commit/bdcc9ec)) 132 | 133 | 134 | 135 | 136 | # [0.2.0](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.1.3...v0.2.0) (2018-12-10) 137 | 138 | 139 | ### Features 140 | 141 | * add no-env-in-mounted ([0037b34](https://github.com/nuxt/eslint-plugin-nuxt/commit/0037b34)) 142 | * move ssr to base ([8f408dd](https://github.com/nuxt/eslint-plugin-nuxt/commit/8f408dd)) 143 | 144 | 145 | 146 | 147 | ## [0.1.3](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.1.2...v0.1.3) (2018-12-10) 148 | 149 | 150 | 151 | 152 | ## [0.1.2](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.1.1...v0.1.2) (2018-12-10) 153 | 154 | 155 | ### Bug Fixes 156 | 157 | * rule not found ([c63f657](https://github.com/nuxt/eslint-plugin-nuxt/commit/c63f657)) 158 | * undefined value in nuxt/no-env-in-context ([17cb2d9](https://github.com/nuxt/eslint-plugin-nuxt/commit/17cb2d9)) 159 | 160 | 161 | 162 | 163 | ## [0.1.1](https://github.com/nuxt/eslint-plugin-nuxt/compare/v0.1.0...v0.1.1) (2018-12-10) 164 | 165 | 166 | ### Bug Fixes 167 | 168 | * wrong index file ([c50e96f](https://github.com/nuxt/eslint-plugin-nuxt/commit/c50e96f)) 169 | 170 | 171 | 172 | 173 | # 0.1.0 (2018-12-10) 174 | 175 | 176 | ### Features 177 | 178 | * add base config ([bf0759a](https://github.com/nuxt/eslint-plugin-nuxt/commit/bf0759a)) 179 | * **rule:** add no-globals-in-created ([d8736e9](https://github.com/nuxt/eslint-plugin-nuxt/commit/d8736e9)) 180 | * add no-env-in-context ([71b09e9](https://github.com/nuxt/eslint-plugin-nuxt/commit/71b09e9)) 181 | * add no-this-in-fetch ([fe4d381](https://github.com/nuxt/eslint-plugin-nuxt/commit/fe4d381)) 182 | * add no-timing-in-fetch-data ([2945b70](https://github.com/nuxt/eslint-plugin-nuxt/commit/2945b70)) 183 | * add rule no-this-in-async-data ([9a56aae](https://github.com/nuxt/eslint-plugin-nuxt/commit/9a56aae)) 184 | * init no-this-in-async-data ([e23f4ed](https://github.com/nuxt/eslint-plugin-nuxt/commit/e23f4ed)) 185 | * init no-this-in-fetch ([4bf0385](https://github.com/nuxt/eslint-plugin-nuxt/commit/4bf0385)) 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xin (Clark) Du 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 | > [!NOTE] 2 | > `eslint-plugin-nuxt` was designed for Nuxt 2 and will not receive active development. Since many rules are no longer relevant in Nuxt 3, we created a new nuxt plugin [`@nuxt/eslint-plugin`](https://eslint.nuxt.com/packages/plugin) for Nuxt 3 under the [nuxt/eslint](https://github.com/nuxt/eslint) repository. 3 | 4 | ---- 5 | 6 | 7 | # eslint-plugin-nuxt 8 | 9 | [![Standard JS][standard-js-src]][standard-js-href] 10 | [![Circle CI][circle-ci-src]][circle-ci-href] 11 | [![npm version][npm-version-src]][npm-version-href] 12 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 13 | 14 | [standard-js-src]: https://flat.badgen.net/badge/code%20style/standard/green 15 | [standard-js-href]: https://standardjs.com 16 | [circle-ci-src]: https://flat.badgen.net/circleci/github/nuxt/eslint-plugin-nuxt 17 | [circle-ci-href]: https://circleci.com/gh/nuxt/eslint-plugin-nuxt 18 | [codecov-src]: https://flat.badgen.net/codecov/c/github/nuxt/eslint-plugin-nuxt 19 | [codecov-href]: https://codecov.io/gh/nuxt/eslint-plugin-nuxt 20 | [npm-version-src]: https://flat.badgen.net/npm/v/eslint-plugin-nuxt/latest 21 | [npm-version-href]: https://npmjs.com/package/eslint-plugin-nuxt 22 | [npm-downloads-src]: https://flat.badgen.net/npm/dt/eslint-plugin-nuxt 23 | [npm-downloads-href]: https://npmjs.com/package/eslint-plugin-nuxt 24 | 25 | :sparkles: ESLint plugin for Nuxt.js 26 | 27 | ## :cd: Installation 28 | 29 | You'll first need to install [ESLint](http://eslint.org): 30 | 31 | ```sh 32 | $ npm i eslint --save-dev 33 | success Saved 1 new dependencies 34 | ``` 35 | 36 | Next, install `eslint-plugin-nuxt`: 37 | 38 | ```sh 39 | $ npm install eslint-plugin-nuxt --save-dev 40 | success Saved 1 new dependencies 41 | ``` 42 | 43 | **Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-nuxt` globally. 44 | 45 | ## :rocket: Usage 46 | 47 | Add `nuxt` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: 48 | 49 | 1. Use our preset to extend recommended defaults: 50 | 51 | ```json 52 | { 53 | "extends": [ 54 | "plugin:nuxt/recommended" 55 | ] 56 | } 57 | ``` 58 | 59 | 2. Or specify individual rules manually: 60 | 61 | ```json 62 | { 63 | "plugins": [ 64 | "nuxt" 65 | ], 66 | "rules": { 67 | "nuxt/rule-name": 2 68 | } 69 | } 70 | ``` 71 | 72 | ## :gear: Configs 73 | 74 | This plugin provides four predefined configs: 75 | 76 | - `plugin:nuxt/base` - Settings and rules to enable correct ESLint parsing 77 | - `plugin:nuxt/recommended` - Above, plus rules to enforce subjective community defaults to ensure consistency 78 | 79 | ## :bulb: Rules 80 | 81 | 82 | 83 | ### Base Rules 84 | 85 | ```json 86 | { 87 | "extends": "plugin:nuxt/base" 88 | } 89 | ``` 90 | 91 | | | Rule ID | Description | 92 | |:---|:--------|:------------| 93 | | | [nuxt/no-env-in-context](./docs/rules/no-env-in-context.md) | Disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit` | 94 | | | [nuxt/no-env-in-hooks](./docs/rules/no-env-in-hooks.md) | Disallow `process.server/process.client` in client only Vue lifecycle hooks like: `mounted, beforeMount, updated...` | 95 | | | [nuxt/no-globals-in-created](./docs/rules/no-globals-in-created.md) | Disallow `window/document` in `created/beforeCreate` | 96 | | | [nuxt/no-this-in-fetch-data](./docs/rules/no-this-in-fetch-data.md) | Disallow `this` in `asyncData/fetch` | 97 | | | [nuxt/no-cjs-in-config](./docs/rules/no-cjs-in-config.md) | Disallow `require/modules.exports/exports` in `nuxt.config.js` | 98 | 99 | ### Recommended Rules 100 | 101 | Include all the below rules, as well as all priority rules in above categories, with: 102 | 103 | ```json 104 | { 105 | "extends": "plugin:nuxt/recommended" 106 | } 107 | ``` 108 | 109 | | | Rule ID | Description | 110 | |:---|:--------|:------------| 111 | | | [nuxt/no-timing-in-fetch-data](./docs/rules/no-timing-in-fetch-data.md) | Disallow `setTimeout/setInterval` in `asyncData/fetch` | 112 | 113 | ### Other Rules 114 | 115 | | | Rule ID | Description | 116 | |:---|:--------|:------------| 117 | | | [nuxt/require-func-head](./docs/rules/require-func-head.md) | Enforce `head` property in component to be a function. | 118 | -------------------------------------------------------------------------------- /docs/rules/no-cjs-in-config.md: -------------------------------------------------------------------------------- 1 | # nuxt/no-cjs-in-config 2 | 3 | > Disallow commonjs module api `require/modules.exports/exports` in `nuxt.config.js` 4 | 5 | - :gear: This rule is included in `"plugin:nuxt/base"`. 6 | 7 | ## Rule Details 8 | 9 | This rule is for preventing using `require/modules.exports/exports` in `nuxt.config.js` 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```js 14 | 15 | const { name } = require('./package.json') 16 | 17 | module.exports = { 18 | mode: 'universal', 19 | name 20 | } 21 | 22 | ``` 23 | 24 | Examples of **correct** code for this rule: 25 | 26 | ```js 27 | 28 | import { name } from './package.json' 29 | 30 | export default { 31 | mode: 'universal', 32 | name 33 | } 34 | 35 | ``` 36 | 37 | ## :mag: Implementation 38 | 39 | - [Rule source](../../lib/rules/no-cjs-in-config.js) 40 | - [Test source](../../lib/rules/__tests__/no-cjs-in-config.test.js) 41 | -------------------------------------------------------------------------------- /docs/rules/no-env-in-context.md: -------------------------------------------------------------------------------- 1 | # nuxt/no-env-in-context 2 | 3 | > disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit` 4 | 5 | - :gear: This rule is included in `"plugin:nuxt/base"`. 6 | 7 | ## Rule Details 8 | 9 | This rule is for preventing using `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit` 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```js 14 | 15 | export default { 16 | asyncData(context) { 17 | if (context.isServer) { 18 | const foo = 'bar' 19 | } 20 | }, 21 | fetch({ isClient }) { 22 | if (isClient) { 23 | const foo = 'bar' 24 | } 25 | } 26 | } 27 | 28 | ``` 29 | 30 | Examples of **correct** code for this rule: 31 | 32 | ```js 33 | 34 | export default { 35 | async asyncData() { 36 | if (process.server) { 37 | const foo = 'bar' 38 | } 39 | }, 40 | fetch() { 41 | if (process.client) { 42 | const foo = 'bar' 43 | } 44 | } 45 | } 46 | 47 | ``` 48 | 49 | ## :mag: Implementation 50 | 51 | - [Rule source](../../lib/rules/no-env-in-context.js) 52 | - [Test source](../../lib/rules/__tests__/no-env-in-context.test.js) 53 | -------------------------------------------------------------------------------- /docs/rules/no-env-in-hooks.md: -------------------------------------------------------------------------------- 1 | # nuxt/no-env-in-hooks 2 | 3 | > Disallow `process.server`, `process.client` and `process.browser` in the following lifecycle hooks: `beforeMount`, `mounted`, `beforeUpdate`, `updated`, `activated`, `deactivated`, `beforeDestroy` and `destroyed`. 4 | 5 | - :gear: This rule is included in `"plugin:nuxt/base"`. 6 | 7 | ## Rule Details 8 | 9 | This rule is for preventing using `process.server`/`process.client`/`process.browser` in client only Vue lifecycle hooks since they're only executed in client side. 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```js 14 | 15 | export default { 16 | mounted() { 17 | if (process.server) { 18 | const foo = 'bar' 19 | } 20 | }, 21 | beforeMount() { 22 | if (process.client) { 23 | const foo = 'bar' 24 | } 25 | }, 26 | beforeDestroy() { 27 | if (process.browser) { 28 | const foo = 'bar' 29 | } 30 | } 31 | } 32 | 33 | ``` 34 | 35 | Examples of **correct** code for this rule: 36 | 37 | 38 | ```js 39 | 40 | export default { 41 | mounted() { 42 | const foo = 'bar' 43 | }, 44 | beforeMount() { 45 | const foo = 'bar' 46 | }, 47 | beforeDestroy() { 48 | const foo = 'bar' 49 | } 50 | } 51 | 52 | ``` 53 | 54 | ## :mag: Implementation 55 | 56 | - [Rule source](../../lib/rules/no-env-in-hooks.js) 57 | - [Test source](../../lib/rules/__tests__/no-env-in-hooks.test.js) 58 | -------------------------------------------------------------------------------- /docs/rules/no-globals-in-created.md: -------------------------------------------------------------------------------- 1 | # nuxt/no-globals-in-created 2 | 3 | > disallow `window/document` in `created/beforeCreate` 4 | 5 | - :gear: This rule is included in `"plugin:nuxt/base"`. 6 | 7 | ## Rule Details 8 | 9 | This rule is for preventing using `window/document` in `created/beforeCreate`, since `created/beforeCreate` may be executed in server side in SSR. 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```js 14 | 15 | export default { 16 | created() { 17 | window.foo = 'bar' 18 | } 19 | } 20 | 21 | ``` 22 | 23 | Examples of **correct** code for this rule: 24 | 25 | ```js 26 | 27 | export default { 28 | created() { 29 | const foo = 'bar' 30 | } 31 | } 32 | 33 | ``` 34 | 35 | ## :mag: Implementation 36 | 37 | - [Rule source](../../lib/rules/no-globals-in-created.js) 38 | - [Test source](../../lib/rules/__tests__/no-globals-in-created.test.js) 39 | -------------------------------------------------------------------------------- /docs/rules/no-this-in-fetch-data.md: -------------------------------------------------------------------------------- 1 | # nuxt/no-this-in-fetch-data 2 | 3 | > disallow `this` in `asyncData/fetch` 4 | 5 | - :gear: This rule is included in `"plugin:nuxt/base"`. 6 | 7 | ## Rule Details 8 | 9 | This rule is for preventing using `this` in `asyncData/fetch` 10 | 11 | > **NOTE**: `No this in fetch` is only for Nuxt.js < 2.12.0, Nuxt.js v2.12 has introduced new `fetch` API which supports `this`. 12 | 13 | Examples of **incorrect** code for this rule: 14 | 15 | ```js 16 | 17 | export default { 18 | ...foo, 19 | async asyncData() { 20 | if (this.$route.path === 'foo') { 21 | 22 | } 23 | } 24 | } 25 | 26 | ``` 27 | 28 | Examples of **correct** code for this rule: 29 | 30 | ```js 31 | 32 | export default { 33 | ...foo, 34 | async asyncData() { 35 | // no this 36 | } 37 | } 38 | 39 | ``` 40 | 41 | ## :mag: Implementation 42 | 43 | - [Rule source](../../lib/rules/no-this-in-fetch-data.js) 44 | - [Test source](../../lib/rules/__tests__/no-this-in-fetch-data.test.js) 45 | -------------------------------------------------------------------------------- /docs/rules/no-timing-in-fetch-data.md: -------------------------------------------------------------------------------- 1 | # nuxt/no-timing-in-fetch-data 2 | 3 | > disallow `setTimeout/setInterval` in `asyncData/fetch` 4 | 5 | - :gear: This rule is included in `"plugin:nuxt/recommended"`. 6 | 7 | ## Rule Details 8 | 9 | This rule is for preventing using `setTimeout/setInterval` in `asyncData/fetch` since it may lead to memory leak 10 | 11 | Examples of **incorrect** code for this rule: 12 | 13 | ```js 14 | 15 | export default { 16 | asyncData() { 17 | let foo = 'bar' 18 | setTimeout(() => { 19 | foo = 'baz' 20 | }, 0) 21 | }, 22 | fetch() { 23 | let foo = 'bar' 24 | setInterval(() => { 25 | foo = 'baz' 26 | }, 0) 27 | } 28 | } 29 | 30 | ``` 31 | 32 | Examples of **correct** code for this rule: 33 | 34 | 35 | ```js 36 | 37 | export default { 38 | async asyncData() { 39 | let foo = 'baz' 40 | }, 41 | fetch() { 42 | let foo = 'baz' 43 | } 44 | } 45 | 46 | ``` 47 | 48 | ## :mag: Implementation 49 | 50 | - [Rule source](../../lib/rules/no-timing-in-fetch-data.js) 51 | - [Test source](../../lib/rules/__tests__/no-timing-in-fetch-data.test.js) 52 | -------------------------------------------------------------------------------- /docs/rules/require-func-head.md: -------------------------------------------------------------------------------- 1 | # nuxt/require-func-head 2 | 3 | > enforce `head` property in component to be a function. 4 | 5 | ## Rule Details 6 | 7 | This rule is enforcing `head` property in component to be a function. 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```js 12 | 13 | export default { 14 | head: { 15 | title: "My page" 16 | } 17 | } 18 | 19 | ``` 20 | 21 | Examples of **correct** code for this rule: 22 | 23 | ```js 24 | 25 | export default { 26 | head() { 27 | return { 28 | title: "My page" 29 | } 30 | } 31 | } 32 | 33 | ``` 34 | 35 | ## :mag: Implementation 36 | 37 | - [Rule source](../../lib/rules/require-func-head.js) 38 | - [Test source](../../lib/rules/__tests__/require-func-head.test.js) 39 | -------------------------------------------------------------------------------- /lib/configs/base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: require.resolve('vue-eslint-parser'), 3 | parserOptions: { 4 | ecmaVersion: 2018, 5 | sourceType: 'module', 6 | ecmaFeatures: { 7 | jsx: true 8 | } 9 | }, 10 | env: { 11 | browser: true, 12 | es6: true 13 | }, 14 | plugins: [ 15 | 'nuxt' 16 | ], 17 | rules: { 18 | 'nuxt/no-env-in-context': 'error', 19 | 'nuxt/no-env-in-hooks': 'error', 20 | 'nuxt/no-globals-in-created': 'error', 21 | 'nuxt/no-this-in-fetch-data': 'error', 22 | 'nuxt/no-cjs-in-config': 'error' 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/configs/recommended.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: require.resolve('./base.js'), 3 | rules: { 4 | 'nuxt/no-timing-in-fetch-data': 'error' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'no-env-in-context': require('./rules/no-env-in-context'), 4 | 'no-env-in-hooks': require('./rules/no-env-in-hooks'), 5 | 'no-globals-in-created': require('./rules/no-globals-in-created'), 6 | 'no-this-in-fetch-data': require('./rules/no-this-in-fetch-data'), 7 | 'no-timing-in-fetch-data': require('./rules/no-timing-in-fetch-data'), 8 | 'no-cjs-in-config': require('./rules/no-cjs-in-config'), 9 | 'require-func-head': require('./rules/require-func-head') 10 | }, 11 | configs: { 12 | base: require('./configs/base'), 13 | recommended: require('./configs/recommended') 14 | }, 15 | processors: { 16 | '.vue': require('./processors') 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/processors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/eslint-plugin-nuxt/e9e88eba7d35d3fdccc0c04b5630fb4cf3a05113/lib/processors/.gitkeep -------------------------------------------------------------------------------- /lib/processors/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('eslint-plugin-vue/lib/processor') 2 | -------------------------------------------------------------------------------- /lib/rules/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxt/eslint-plugin-nuxt/e9e88eba7d35d3fdccc0c04b5630fb4cf3a05113/lib/rules/.gitkeep -------------------------------------------------------------------------------- /lib/rules/__tests__/no-cjs-in-config.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Disallow `require/modules.exports/exports` in `nuxt.config.js` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../no-cjs-in-config') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('no-cjs-in-config', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'nuxt.config.js', 30 | code: ` 31 | import { name } from './package.json' 32 | 33 | export default { 34 | mode: 'universal', 35 | name 36 | } 37 | `, 38 | parserOptions 39 | } 40 | ], 41 | 42 | invalid: [ 43 | { 44 | filename: 'nuxt.config.js', 45 | code: ` 46 | const { name } = require('./package.json') 47 | `, 48 | errors: [{ 49 | message: 'Unexpected require, please use import instead.', 50 | type: 'Identifier' 51 | }], 52 | parserOptions 53 | }, 54 | { 55 | filename: 'nuxt.config.js', 56 | code: ` 57 | module.exports = { 58 | mode: 'universal', 59 | name 60 | } 61 | `, 62 | errors: [{ 63 | message: 'Unexpected module.exports, please use export default instead.', 64 | type: 'MemberExpression' 65 | }], 66 | parserOptions 67 | }, 68 | { 69 | filename: 'nuxt.config.js', 70 | code: ` 71 | exports.test = { 72 | mode: 'universal', 73 | name 74 | } 75 | `, 76 | errors: [{ 77 | message: 'Unexpected exports, please use export default instead.', 78 | type: 'MemberExpression' 79 | }], 80 | parserOptions 81 | } 82 | ] 83 | }) 84 | -------------------------------------------------------------------------------- /lib/rules/__tests__/no-env-in-context.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../no-env-in-context') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('no-env-in-context', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'test.vue', 30 | code: ` 31 | export default { 32 | async asyncData() { 33 | if(process.server) { 34 | const foo = 'bar' 35 | } 36 | }, 37 | fetch() { 38 | if(process.client) { 39 | const foo = 'bar' 40 | } 41 | } 42 | } 43 | `, 44 | parserOptions 45 | } 46 | ], 47 | 48 | invalid: [ 49 | { 50 | filename: 'test.vue', 51 | code: ` 52 | export default { 53 | asyncData(context) { 54 | if(context.isServer) { 55 | const foo = 'bar' 56 | } 57 | }, 58 | fetch(context) { 59 | if(context.isClient) { 60 | const foo = 'bar' 61 | } 62 | } 63 | } 64 | `, 65 | errors: [{ 66 | message: 'Unexpected isServer in asyncData.', 67 | type: 'MemberExpression' 68 | }, { 69 | message: 'Unexpected isClient in fetch.', 70 | type: 'MemberExpression' 71 | }], 72 | parserOptions 73 | }, 74 | { 75 | filename: 'test.vue', 76 | code: ` 77 | export default { 78 | asyncData(context) { 79 | if(context['isClient']) { 80 | const foo = 'bar' 81 | } 82 | }, 83 | fetch(context) { 84 | if(context['isServer']) { 85 | const foo = 'bar' 86 | } 87 | } 88 | } 89 | `, 90 | errors: [{ 91 | message: 'Unexpected isClient in asyncData.', 92 | type: 'MemberExpression' 93 | }, { 94 | message: 'Unexpected isServer in fetch.', 95 | type: 'MemberExpression' 96 | }], 97 | parserOptions 98 | }, 99 | { 100 | filename: 'test.vue', 101 | code: ` 102 | export default { 103 | asyncData({ isClient }) { 104 | if(isClient) { 105 | const foo = 'bar' 106 | } 107 | }, 108 | fetch({ isServer }) { 109 | if(isServer) { 110 | const foo = 'bar' 111 | } 112 | } 113 | } 114 | `, 115 | errors: [{ 116 | message: 'Unexpected isClient in asyncData.', 117 | type: 'Property' 118 | }, { 119 | message: 'Unexpected isServer in fetch.', 120 | type: 'Property' 121 | }], 122 | parserOptions 123 | } 124 | ] 125 | }) 126 | -------------------------------------------------------------------------------- /lib/rules/__tests__/no-env-in-hooks.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `process.server`/`process.client`/`process.browser` in `Vue Lifecycle Hooks` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../no-env-in-hooks') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('no-env-in-hooks', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'test.vue', 30 | code: ` 31 | export default { 32 | mounted() { 33 | const foo = 'bar' 34 | }, 35 | beforeMount() { 36 | const foo = 'bar' 37 | }, 38 | beforeDestroy() { 39 | const foo = 'bar' 40 | } 41 | } 42 | `, 43 | parserOptions 44 | } 45 | ], 46 | 47 | invalid: [ 48 | { 49 | filename: 'test.vue', 50 | code: ` 51 | export default { 52 | mounted() { 53 | if(process.server) { 54 | const foo = 'bar' 55 | } 56 | }, 57 | beforeMount() { 58 | if(process.client) { 59 | const foo = 'bar' 60 | } 61 | }, 62 | beforeDestroy() { 63 | if(process.browser) { 64 | const foo = 'bar' 65 | } 66 | } 67 | } 68 | `, 69 | errors: [{ 70 | message: 'Unexpected process.server in mounted.', 71 | type: 'MemberExpression' 72 | }, { 73 | message: 'Unexpected process.client in beforeMount.', 74 | type: 'MemberExpression' 75 | }, { 76 | message: 'Unexpected process.browser in beforeDestroy.', 77 | type: 'MemberExpression' 78 | }], 79 | parserOptions 80 | }, 81 | { 82 | filename: 'test.vue', 83 | code: ` 84 | export default { 85 | mounted() { 86 | if(process['client']) { 87 | const foo = 'bar' 88 | } 89 | }, 90 | beforeMount() { 91 | if(process['server']) { 92 | const foo = 'bar' 93 | } 94 | }, 95 | beforeDestroy() { 96 | if(process['browser']) { 97 | const foo = 'bar' 98 | } 99 | } 100 | } 101 | `, 102 | errors: [{ 103 | message: 'Unexpected process.client in mounted.', 104 | type: 'MemberExpression' 105 | }, { 106 | message: 'Unexpected process.server in beforeMount.', 107 | type: 'MemberExpression' 108 | }, { 109 | message: 'Unexpected process.browser in beforeDestroy.', 110 | type: 'MemberExpression' 111 | }], 112 | parserOptions 113 | } 114 | ] 115 | }) 116 | -------------------------------------------------------------------------------- /lib/rules/__tests__/no-globals-in-created.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `window/document` in `created/beforeCreate` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../no-globals-in-created') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('no-globals-in-created', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'test.vue', 30 | code: ` 31 | export default { 32 | created() { 33 | const path = this.$route.path 34 | }, 35 | beforeCreate() { 36 | const path = this.$route.params.foo 37 | } 38 | } 39 | `, 40 | parserOptions 41 | } 42 | ], 43 | 44 | invalid: [ 45 | { 46 | filename: 'test.vue', 47 | code: ` 48 | export default { 49 | created() { 50 | const path = window.location.pathname 51 | }, 52 | beforeCreate() { 53 | const foo = document.foo 54 | } 55 | } 56 | `, 57 | errors: [{ 58 | message: 'Unexpected window in created.', 59 | type: 'MemberExpression' 60 | }, { 61 | message: 'Unexpected document in beforeCreate.', 62 | type: 'MemberExpression' 63 | }], 64 | parserOptions 65 | }, 66 | { 67 | filename: 'test.vue', 68 | code: ` 69 | export default { 70 | created() { 71 | document.foo = 'bar' 72 | }, 73 | beforeCreate() { 74 | window.foo = 'bar' 75 | } 76 | } 77 | `, 78 | errors: [{ 79 | message: 'Unexpected document in created.', 80 | type: 'MemberExpression' 81 | }, { 82 | message: 'Unexpected window in beforeCreate.', 83 | type: 'MemberExpression' 84 | }], 85 | parserOptions 86 | }, 87 | { 88 | filename: 'test.vue', 89 | code: ` 90 | export default { 91 | created() { 92 | return window.foo 93 | }, 94 | beforeCreate() { 95 | return document.foo 96 | } 97 | } 98 | `, 99 | errors: [{ 100 | message: 'Unexpected window in created.', 101 | type: 'MemberExpression' 102 | }, { 103 | message: 'Unexpected document in beforeCreate.', 104 | type: 'MemberExpression' 105 | }], 106 | parserOptions 107 | } 108 | ] 109 | }) 110 | -------------------------------------------------------------------------------- /lib/rules/__tests__/no-this-in-fetch-data.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `this` in `asyncData/fetch` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../no-this-in-fetch-data') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('no-this-in-fetch-data', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'test.vue', 30 | code: ` 31 | export default { 32 | ...foo, 33 | asyncData() { 34 | }, 35 | fetch() { 36 | } 37 | } 38 | `, 39 | parserOptions 40 | } 41 | ], 42 | 43 | invalid: [ 44 | { 45 | filename: 'test.vue', 46 | code: ` 47 | export default { 48 | ...foo, 49 | asyncData() { 50 | if(this.$route.path === 'foo') {} 51 | } 52 | } 53 | `, 54 | errors: [{ 55 | message: 'Unexpected this in asyncData.', 56 | type: 'ThisExpression' 57 | }], 58 | parserOptions 59 | }, 60 | { 61 | filename: 'test.vue', 62 | code: ` 63 | export default { 64 | ...foo, 65 | async asyncData() { 66 | if(this.$route.path === 'foo') {} 67 | } 68 | } 69 | `, 70 | errors: [{ 71 | message: 'Unexpected this in asyncData.', 72 | type: 'ThisExpression' 73 | }], 74 | parserOptions 75 | }, 76 | { 77 | filename: 'test.vue', 78 | code: ` 79 | export default { 80 | ...foo, 81 | asyncData: () => { 82 | if(this.$route.path === 'foo') {} 83 | } 84 | } 85 | `, 86 | errors: [{ 87 | message: 'Unexpected this in asyncData.', 88 | type: 'ThisExpression' 89 | }], 90 | parserOptions 91 | }, 92 | { 93 | filename: 'test.vue', 94 | code: ` 95 | export default { 96 | ...foo, 97 | asyncData: function test() { 98 | if(this.$route.path === 'foo') {} 99 | } 100 | } 101 | `, 102 | errors: [{ 103 | message: 'Unexpected this in asyncData.', 104 | type: 'ThisExpression' 105 | }], 106 | parserOptions 107 | }, 108 | { 109 | filename: 'test.vue', 110 | code: ` 111 | export default { 112 | ...foo, 113 | fetch() { 114 | if(this.$route.path === 'foo') {} 115 | } 116 | } 117 | `, 118 | errors: [{ 119 | message: 'Unexpected this in fetch.', 120 | type: 'ThisExpression' 121 | }], 122 | options: [{ methods: ['fetch'] }], 123 | parserOptions 124 | }, 125 | { 126 | filename: 'test.vue', 127 | code: ` 128 | export default { 129 | ...foo, 130 | async fetch() { 131 | if(this.$route.path === 'foo') {} 132 | } 133 | } 134 | `, 135 | errors: [{ 136 | message: 'Unexpected this in fetch.', 137 | type: 'ThisExpression' 138 | }], 139 | options: [{ methods: ['fetch'] }], 140 | parserOptions 141 | }, 142 | { 143 | filename: 'test.vue', 144 | code: ` 145 | export default { 146 | ...foo, 147 | fetch: () => { 148 | if(this.$route.path === 'foo') {} 149 | } 150 | } 151 | `, 152 | errors: [{ 153 | message: 'Unexpected this in fetch.', 154 | type: 'ThisExpression' 155 | }], 156 | options: [{ methods: ['fetch'] }], 157 | parserOptions 158 | }, 159 | { 160 | filename: 'test.vue', 161 | code: ` 162 | export default { 163 | ...foo, 164 | fetch: function test() { 165 | if(this.$route.path === 'foo') {} 166 | } 167 | } 168 | `, 169 | errors: [{ 170 | message: 'Unexpected this in fetch.', 171 | type: 'ThisExpression' 172 | }], 173 | options: [{ methods: ['fetch'] }], 174 | parserOptions 175 | } 176 | ] 177 | }) 178 | -------------------------------------------------------------------------------- /lib/rules/__tests__/no-timing-in-fetch-data.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `setTimeout/setInterval` in `asyncData/fetch` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../no-timing-in-fetch-data') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('no-timing-in-fetch-data', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'test.vue', 30 | code: ` 31 | export default { 32 | async asyncData() { 33 | let foo = 'baz' 34 | }, 35 | fetch() { 36 | let foo = 'baz' 37 | } 38 | } 39 | `, 40 | parserOptions 41 | } 42 | ], 43 | 44 | invalid: [ 45 | { 46 | filename: 'test.vue', 47 | code: ` 48 | export default { 49 | asyncData() { 50 | let foo = 'bar' 51 | setTimeout(() => { 52 | foo = 'baz' 53 | }, 0) 54 | }, 55 | fetch() { 56 | let foo = 'bar' 57 | setInterval(() => { 58 | foo = 'baz' 59 | }, 0) 60 | } 61 | } 62 | `, 63 | errors: [{ 64 | message: 'Unexpected setTimeout in asyncData.', 65 | type: 'CallExpression' 66 | }, { 67 | message: 'Unexpected setInterval in fetch.', 68 | type: 'CallExpression' 69 | }], 70 | parserOptions 71 | }, 72 | { 73 | filename: 'test.vue', 74 | code: ` 75 | export default { 76 | asyncData() { 77 | let timer = setInterval 78 | }, 79 | fetch() { 80 | let timer = setTimeout 81 | } 82 | } 83 | `, 84 | errors: [{ 85 | message: 'Unexpected setInterval in asyncData.', 86 | type: 'VariableDeclarator' 87 | }, { 88 | message: 'Unexpected setTimeout in fetch.', 89 | type: 'VariableDeclarator' 90 | }], 91 | parserOptions 92 | } 93 | ] 94 | }) 95 | -------------------------------------------------------------------------------- /lib/rules/__tests__/require-func-head.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `setTimeout/setInterval` in `asyncData/fetch` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | // ------------------------------------------------------------------------------ 8 | // Requirements 9 | // ------------------------------------------------------------------------------ 10 | 11 | const rule = require('../require-func-head') 12 | 13 | const RuleTester = require('eslint').RuleTester 14 | 15 | const parserOptions = { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | } 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Tests 22 | // ------------------------------------------------------------------------------ 23 | 24 | const ruleTester = new RuleTester() 25 | ruleTester.run('require-func-head', rule, { 26 | 27 | valid: [ 28 | { 29 | filename: 'test.vue', 30 | code: ` 31 | export default { 32 | head() { 33 | return { 34 | title: "My page" 35 | } 36 | } 37 | } 38 | `, 39 | parserOptions 40 | } 41 | ], 42 | 43 | invalid: [ 44 | { 45 | filename: 'test.vue', 46 | code: ` 47 | export default { 48 | head: { 49 | title: "My page" 50 | } 51 | } 52 | `, 53 | errors: [{ 54 | message: '`head` property in component must be a function.', 55 | type: 'Property' 56 | }], 57 | output: ` 58 | export default { 59 | head: function() { 60 | return { 61 | title: "My page" 62 | }; 63 | } 64 | } 65 | `, 66 | parserOptions 67 | } 68 | ] 69 | }) 70 | -------------------------------------------------------------------------------- /lib/rules/no-cjs-in-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Disallow `require/modules.exports/exports` in `nuxt.config.js` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const path = require('path') 8 | 9 | // ------------------------------------------------------------------------------ 10 | // Rule Definition 11 | // ------------------------------------------------------------------------------ 12 | 13 | module.exports = { 14 | meta: { 15 | docs: { 16 | description: 17 | 'disallow commonjs module api `require/modules.exports/exports` in `nuxt.config.js`', 18 | category: 'base' 19 | }, 20 | messages: { 21 | noCjs: 'Unexpected {{cjs}}, please use {{esm}} instead.' 22 | } 23 | }, 24 | 25 | create (context) { 26 | // variables should be defined here 27 | const options = context.options[0] || {} 28 | const configFile = options.file || 'nuxt.config.js' 29 | let isNuxtConfig = false 30 | 31 | // ---------------------------------------------------------------------- 32 | // Public 33 | // ---------------------------------------------------------------------- 34 | 35 | return { 36 | Program (node) { 37 | const filename = path.basename(context.getFilename()) 38 | if (filename === configFile) { 39 | isNuxtConfig = true 40 | } 41 | }, 42 | MemberExpression: function (node) { 43 | if (!isNuxtConfig) { 44 | return 45 | } 46 | 47 | // module.exports 48 | if (node.object.name === 'module' && node.property.name === 'exports') { 49 | context.report({ 50 | node, 51 | messageId: 'noCjs', 52 | data: { 53 | cjs: 'module.exports', 54 | esm: 'export default' 55 | } 56 | }) 57 | } 58 | 59 | // exports. 60 | if (node.object.name === 'exports') { 61 | const isInScope = context.getScope() 62 | .variables 63 | .some(variable => variable.name === 'exports') 64 | if (!isInScope) { 65 | context.report({ 66 | node, 67 | messageId: 'noCjs', 68 | data: { 69 | cjs: 'exports', 70 | esm: 'export default' 71 | } 72 | }) 73 | } 74 | } 75 | }, 76 | CallExpression: function (call) { 77 | const module = call.arguments[0] 78 | 79 | if ( 80 | !isNuxtConfig || 81 | context.getScope().type !== 'module' || 82 | !['ExpressionStatement', 'VariableDeclarator'].includes(call.parent.type) || 83 | call.callee.type !== 'Identifier' || 84 | call.callee.name !== 'require' || 85 | call.arguments.length !== 1 || 86 | module.type !== 'Literal' || 87 | typeof module.value !== 'string' 88 | ) { 89 | return 90 | } 91 | 92 | context.report({ 93 | node: call.callee, 94 | messageId: 'noCjs', 95 | data: { 96 | cjs: 'require', 97 | esm: 'import' 98 | } 99 | }) 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/rules/no-env-in-context.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const utils = require('../utils') 8 | 9 | // ------------------------------------------------------------------------------ 10 | // Rule Definition 11 | // ------------------------------------------------------------------------------ 12 | 13 | module.exports = { 14 | meta: { 15 | docs: { 16 | description: 17 | 'disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit`', 18 | category: 'base' 19 | }, 20 | messages: { 21 | noEnv: 'Unexpected {{env}} in {{funcName}}.' 22 | } 23 | }, 24 | 25 | create (context) { 26 | // variables should be defined here 27 | const forbiddenNodes = [] 28 | const options = context.options[0] || {} 29 | 30 | const ENV = ['isServer', 'isClient'] 31 | const HOOKS = new Set(['asyncData', 'fetch'].concat(options.methods || [])) 32 | 33 | // ---------------------------------------------------------------------- 34 | // Public 35 | // ---------------------------------------------------------------------- 36 | 37 | return { 38 | MemberExpression (node) { 39 | const propertyName = node.computed ? node.property.value : node.property.name 40 | if (propertyName && ENV.includes(propertyName)) { 41 | forbiddenNodes.push({ name: propertyName, node }) 42 | } 43 | }, 44 | ...utils.executeOnVue(context, obj => { 45 | for (const funcName of HOOKS) { 46 | const func = utils.getFunctionWithName(obj, funcName) 47 | const param = func && func.value ? func.value.params && func.value.params[0] : false 48 | if (param) { 49 | if (param.type === 'ObjectPattern') { 50 | for (const prop of param.properties) { 51 | if (prop.key && prop.key.name && ENV.includes(prop.key.name)) { 52 | context.report({ 53 | node: prop, 54 | messageId: 'noEnv', 55 | data: { 56 | env: prop.key.name, 57 | funcName 58 | } 59 | }) 60 | } 61 | } 62 | } else { 63 | for (const { name, node: child } of forbiddenNodes) { 64 | if (utils.isInFunction(func, child)) { 65 | if (param.name === child.object.name) { 66 | context.report({ 67 | node: child, 68 | messageId: 'noEnv', 69 | data: { 70 | env: name, 71 | funcName 72 | } 73 | }) 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | }) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/rules/no-env-in-hooks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow process.server, process.client and process.browser in the following lifecycle hooks: beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy and destroyed 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const utils = require('../utils') 8 | 9 | // ------------------------------------------------------------------------------ 10 | // Rule Definition 11 | // ------------------------------------------------------------------------------ 12 | 13 | module.exports = { 14 | meta: { 15 | docs: { 16 | description: 17 | 'disallow process.server and process.client in the following lifecycle hooks: beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy and destroyed', 18 | category: 'base' 19 | }, 20 | messages: { 21 | noEnv: 'Unexpected {{name}} in {{funcName}}.' 22 | } 23 | }, 24 | 25 | create (context) { 26 | // variables should be defined here 27 | const forbiddenNodes = [] 28 | const options = context.options[0] || {} 29 | 30 | const ENV = ['server', 'client', 'browser'] 31 | const HOOKS = new Set(['beforeMount', 'mounted', 'beforeUpdate', 'updated', 'activated', 'deactivated', 'beforeDestroy', 'destroyed'].concat(options.methods || [])) 32 | 33 | // ---------------------------------------------------------------------- 34 | // Public 35 | // ---------------------------------------------------------------------- 36 | 37 | return { 38 | MemberExpression (node) { 39 | const objectName = node.object.name 40 | if (objectName === 'process') { 41 | const propertyName = node.computed ? node.property.value : node.property.name 42 | if (propertyName && ENV.includes(propertyName)) { 43 | forbiddenNodes.push({ name: 'process.' + propertyName, node }) 44 | } 45 | } 46 | }, 47 | ...utils.executeOnVue(context, obj => { 48 | for (const funcName of HOOKS) { 49 | const func = utils.getFunctionWithName(obj, funcName) 50 | if (func) { 51 | for (const { name, node: child } of forbiddenNodes) { 52 | if (utils.isInFunction(func, child)) { 53 | context.report({ 54 | node: child, 55 | messageId: 'noEnv', 56 | data: { 57 | name, 58 | funcName 59 | } 60 | }) 61 | } 62 | } 63 | } 64 | } 65 | }) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/rules/no-globals-in-created.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `window/document` in `created/beforeCreate` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const utils = require('../utils') 8 | 9 | // ------------------------------------------------------------------------------ 10 | // Rule Definition 11 | // ------------------------------------------------------------------------------ 12 | 13 | module.exports = { 14 | meta: { 15 | docs: { 16 | description: 'disallow `window/document` in `created/beforeCreate`', 17 | category: 'base' 18 | }, 19 | messages: { 20 | noGlobals: 'Unexpected {{name}} in {{funcName}}.' 21 | } 22 | }, 23 | 24 | create (context) { 25 | const forbiddenNodes = [] 26 | const options = context.options[0] || {} 27 | 28 | const HOOKS = new Set( 29 | ['created', 'beforeCreate'].concat(options.methods || []) 30 | ) 31 | const GLOBALS = ['window', 'document'] 32 | 33 | function isGlobals (name) { 34 | return GLOBALS.includes(name) 35 | } 36 | 37 | return { 38 | MemberExpression (node) { 39 | if (!node.object) return 40 | 41 | const name = node.object.name 42 | 43 | if (isGlobals(name)) { 44 | forbiddenNodes.push({ name, node }) 45 | } 46 | }, 47 | VariableDeclarator (node) { 48 | if (!node.init) return 49 | 50 | const name = node.init.name 51 | 52 | if (isGlobals(name)) { 53 | forbiddenNodes.push({ name, node }) 54 | } 55 | }, 56 | ...utils.executeOnVue(context, obj => { 57 | for (const { funcName, name, node } of utils.getFunctionWithChild(obj, HOOKS, forbiddenNodes)) { 58 | context.report({ 59 | node, 60 | messageId: 'noGlobals', 61 | data: { 62 | name, 63 | funcName 64 | } 65 | }) 66 | } 67 | }) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/rules/no-this-in-fetch-data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `this` in `asyncData/fetch` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const satisfies = require('semver/ranges/intersects') 8 | const utils = require('../utils') 9 | const resolver = require('../utils/resolver') 10 | 11 | // ------------------------------------------------------------------------------ 12 | // Rule Definition 13 | // ------------------------------------------------------------------------------ 14 | 15 | module.exports = { 16 | meta: { 17 | docs: { 18 | description: 'disallow `this` in `asyncData/fetch`', 19 | category: 'base' 20 | }, 21 | messages: { 22 | noThis: 'Unexpected this in {{funcName}}.' 23 | } 24 | }, 25 | 26 | create (context) { 27 | // variables should be defined here 28 | const forbiddenNodes = new Map() 29 | const options = context.options[0] || {} 30 | 31 | const HOOKS = new Set( 32 | ['asyncData'].concat(options.methods || []) 33 | ) 34 | 35 | const { version } = resolver 36 | // new fetch API can use `this` since 2.12.0 37 | if (version && satisfies(version, '<2.12.0')) { 38 | HOOKS.add('fetch') 39 | } 40 | 41 | let nodeUsingThis = [] 42 | 43 | // ---------------------------------------------------------------------- 44 | // Helpers 45 | // ---------------------------------------------------------------------- 46 | 47 | function enterFunction () { 48 | nodeUsingThis = [] 49 | } 50 | 51 | function exitFunction (node) { 52 | if (nodeUsingThis.length > 0) { 53 | forbiddenNodes.set(node, nodeUsingThis) 54 | } 55 | } 56 | 57 | function markThisUsed (node) { 58 | nodeUsingThis.push(node) 59 | } 60 | 61 | // ---------------------------------------------------------------------- 62 | // Public 63 | // ---------------------------------------------------------------------- 64 | 65 | return { 66 | FunctionExpression: enterFunction, 67 | 'FunctionExpression:exit': exitFunction, 68 | ArrowFunctionExpression: enterFunction, 69 | 'ArrowFunctionExpression:exit': exitFunction, 70 | ThisExpression: markThisUsed, 71 | Super: markThisUsed, 72 | ...utils.executeOnVue(context, obj => { 73 | for (const funcName of HOOKS) { 74 | const prop = utils.getFunctionWithName(obj, funcName) 75 | if (prop && forbiddenNodes.has(prop.value)) { 76 | for (const node of forbiddenNodes.get(prop.value)) { 77 | context.report({ 78 | node, 79 | messageId: 'noThis', 80 | data: { 81 | funcName 82 | } 83 | }) 84 | } 85 | } 86 | } 87 | }) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/rules/no-timing-in-fetch-data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview disallow `setTimeout/setInterval` in `asyncData/fetch` 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const utils = require('../utils') 8 | 9 | // ------------------------------------------------------------------------------ 10 | // Rule Definition 11 | // ------------------------------------------------------------------------------ 12 | 13 | module.exports = { 14 | meta: { 15 | docs: { 16 | description: 'disallow `setTimeout/setInterval` in `asyncData/fetch`', 17 | category: 'recommended' 18 | }, 19 | messages: { 20 | noTiming: 'Unexpected {{name}} in {{funcName}}.' 21 | } 22 | }, 23 | 24 | create (context) { 25 | const forbiddenNodes = [] 26 | const options = context.options[0] || {} 27 | 28 | const HOOKS = new Set( 29 | ['fetch', 'asyncData'].concat(options.methods || []) 30 | ) 31 | const TIMING = ['setTimeout', 'setInterval'] 32 | 33 | function isTiming (name) { 34 | return TIMING.includes(name) 35 | } 36 | 37 | return { 38 | CallExpression (node) { 39 | if (!node.callee) return 40 | 41 | const name = node.callee.name 42 | 43 | if (isTiming(name)) { 44 | forbiddenNodes.push({ name, node }) 45 | } 46 | }, 47 | VariableDeclarator (node) { 48 | if (!node.init) return 49 | 50 | const name = node.init.name 51 | 52 | if (isTiming(name)) { 53 | forbiddenNodes.push({ name, node }) 54 | } 55 | }, 56 | ...utils.executeOnVue(context, obj => { 57 | for (const { funcName, name, node } of utils.getFunctionWithChild(obj, HOOKS, forbiddenNodes)) { 58 | context.report({ 59 | node, 60 | messageId: 'noTiming', 61 | data: { 62 | name, 63 | funcName 64 | } 65 | }) 66 | } 67 | }) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/rules/require-func-head.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview enforce component's head property to be a function. 3 | * @author Xin Du 4 | */ 5 | 'use strict' 6 | 7 | const utils = require('../utils') 8 | 9 | // ------------------------------------------------------------------------------ 10 | // Rule Definition 11 | // ------------------------------------------------------------------------------ 12 | 13 | module.exports = { 14 | meta: { 15 | docs: { 16 | description: "enforce component's head property to be a function", 17 | category: 'recommended' 18 | }, 19 | fixable: 'code', 20 | messages: { 21 | head: '`head` property in component must be a function.' 22 | } 23 | }, 24 | 25 | create (context) { 26 | const sourceCode = context.getSourceCode() 27 | 28 | return utils.executeOnVueComponent(context, (obj) => { 29 | obj.properties 30 | .filter(p => 31 | p.type === 'Property' && 32 | p.key.type === 'Identifier' && 33 | p.key.name === 'head' && 34 | p.value.type !== 'FunctionExpression' && 35 | p.value.type !== 'ArrowFunctionExpression' && 36 | p.value.type !== 'Identifier' && 37 | p.value.type !== 'CallExpression' 38 | ) 39 | .forEach(p => { 40 | context.report({ 41 | node: p, 42 | messageId: 'head', 43 | fix (fixer) { 44 | const tokens = utils.getFirstAndLastTokens(p.value, sourceCode) 45 | 46 | return [ 47 | fixer.insertTextBefore(tokens.first, 'function() {\nreturn '), 48 | fixer.insertTextAfter(tokens.last, ';\n}') 49 | ] 50 | } 51 | }) 52 | }) 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/utils/index.js: -------------------------------------------------------------------------------- 1 | const utils = require('eslint-plugin-vue/lib/utils') 2 | 3 | module.exports = Object.assign( 4 | { 5 | getProperty (node, name, condition) { 6 | return node.properties.find( 7 | p => p.type === 'Property' && name === utils.getStaticPropertyName(p) && condition(p) 8 | ) 9 | }, 10 | getProperties (node, names) { 11 | return node.properties.filter( 12 | p => p.type === 'Property' && (!names.size || names.has(utils.getStaticPropertyName(p))) 13 | ) 14 | }, 15 | getFunctionWithName (rootNode, name) { 16 | return this.getProperty( 17 | rootNode, 18 | name, 19 | item => item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression' 20 | ) 21 | }, 22 | isInFunction (func, child) { 23 | if (func.value.type === 'FunctionExpression') { 24 | if ( 25 | child && 26 | child.loc.start.line >= func.value.loc.start.line && 27 | child.loc.end.line <= func.value.loc.end.line 28 | ) { 29 | return true 30 | } 31 | } 32 | }, 33 | * getFunctionWithChild (rootNode, funcNames, childNodes) { 34 | const funcNodes = this.getProperties(rootNode, funcNames) 35 | 36 | for (const func of funcNodes) { 37 | for (const { name, node: child } of childNodes) { 38 | const funcName = utils.getStaticPropertyName(func) 39 | if (!funcName) continue 40 | 41 | if (this.isInFunction(func, child)) { 42 | yield { name, node: child, func, funcName } 43 | } 44 | } 45 | } 46 | }, 47 | isOpenParen (token) { 48 | return token.type === 'Punctuator' && token.value === '(' 49 | }, 50 | isCloseParen (token) { 51 | return token.type === 'Punctuator' && token.value === ')' 52 | }, 53 | getFirstAndLastTokens (node, sourceCode) { 54 | let first = sourceCode.getFirstToken(node) 55 | let last = sourceCode.getLastToken(node) 56 | 57 | // If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses. 58 | while (true) { 59 | const prev = sourceCode.getTokenBefore(first) 60 | const next = sourceCode.getTokenAfter(last) 61 | if (this.isOpenParen(prev) && this.isCloseParen(next)) { 62 | first = prev 63 | last = next 64 | } else { 65 | return { first, last } 66 | } 67 | } 68 | } 69 | }, 70 | utils 71 | ) 72 | -------------------------------------------------------------------------------- /lib/utils/resolver.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | __version: undefined, 3 | get version () { 4 | if (this.__version === undefined) { 5 | return this.loadNuxtPkg() 6 | } 7 | return this.__version 8 | }, 9 | loadPkg (pkgName) { 10 | try { 11 | return require(`${pkgName}/package.json`) 12 | } catch (e) { 13 | return {} 14 | } 15 | }, 16 | loadNuxtPkg () { 17 | const { version } = this.loadPkg('nuxt') || this.loadPkg('nuxt-edge') 18 | this.__version = version || false 19 | return this.__version 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-nuxt", 3 | "version": "4.0.0", 4 | "description": "ESLint plugin for Nuxt.js", 5 | "author": "Clark Du ", 6 | "license": "MIT", 7 | "main": "lib/index.js", 8 | "homepage": "https://github.com/nuxt/eslint-plugin-nuxt#readme", 9 | "bugs": { 10 | "url": "https://github.com/nuxt/eslint-plugin-nuxt/issues" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/nuxt/eslint-plugin-nuxt.git" 15 | }, 16 | "keywords": [ 17 | "nuxt", 18 | "eslint", 19 | "eslintplugin" 20 | ], 21 | "files": [ 22 | "lib", 23 | "!__tests__" 24 | ], 25 | "scripts": { 26 | "test": "jest --silent", 27 | "lint": "eslint ./", 28 | "release": "standard-version", 29 | "postinstall": "husky install", 30 | "prepublishOnly": "pinst --disable", 31 | "postpublish": "pinst --enable", 32 | "prepare": "husky install" 33 | }, 34 | "lint-staged": { 35 | "*.js": [ 36 | "eslint" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "eslint": "^8.23.0", 41 | "eslint-config-standard": "^17.0.0", 42 | "eslint-plugin-import": "^2.26.0", 43 | "eslint-plugin-n": "^15.2.5", 44 | "eslint-plugin-promise": "^6.0.1", 45 | "eslint-plugin-standard": "^5.0.0", 46 | "husky": "^8.0.1", 47 | "jest": "^29.0.1", 48 | "lint-staged": "^13.0.3", 49 | "pinst": "^3.0.0", 50 | "standard-version": "^9.3.2" 51 | }, 52 | "dependencies": { 53 | "eslint-plugin-vue": "^9.4.0", 54 | "semver": "^7.3.7", 55 | "vue-eslint-parser": "^9.0.3" 56 | }, 57 | "packageManager": "yarn@3.2.3" 58 | } 59 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@nuxtjs" 4 | ], 5 | "lockFileMaintenance": { 6 | "enabled": true 7 | } 8 | } 9 | --------------------------------------------------------------------------------