├── .codeclimate.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── lib ├── common │ ├── aliases.js │ └── getRegex.js ├── index.js ├── match.js └── notMatch.js ├── package-lock.json ├── package.json └── test └── lib ├── index.test.js ├── match.test.js └── notMatch.test.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | enabled: true 4 | 5 | ratings: 6 | paths: 7 | - "**.js" 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | .idea/** 3 | build/** 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'airbnb-base', 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | .nyc_output 4 | node_modules/ 5 | build/ 6 | coverage/ 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | test 3 | yarn.lock 4 | node_modules 5 | npm-debug.log 6 | coverage 7 | .nyc_output 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | 6 | stages: 7 | - test 8 | - release 9 | 10 | jobs: 11 | include: 12 | - stage: test 13 | before_script: npm run lint 14 | script: npm run cover 15 | after_success: npm run coveralls 16 | - stage: release 17 | if: tag IS present 18 | deploy: 19 | provider: npm 20 | email: "$NPM_EMAIL" 21 | api_key: "$NPM_TOKEN" 22 | skip_cleanup: true 23 | on: 24 | tags: true 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Denis Semenenko 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 | # eslint-plugin-filename-rules 2 | 3 | Adds an ESLint rule to enforce filename conventions for linted files. Allows different options for different file extensions. Supports custom regular expressions. 4 | 5 | [![NPM Version][npm-image]][npm-url] 6 | [![NPM Downloads][downloads-image]][downloads-url] 7 | [![Build Status][travis-image]][travis-url] 8 | [![Coverage Status][coverage-image]][coverage-url] 9 | [![License: MIT][license-image]][license-url] 10 | 11 | 12 | ## Installation 13 | 14 | ```bash 15 | $ npm install -D eslint-plugin-filename-rules 16 | ``` 17 | 18 | Add it to your `.eslintrc.js`: 19 | 20 | ```js 21 | module.exports = { 22 | plugins: [ 23 | 'filename-rules', 24 | ], 25 | rules: { 26 | 'filename-rules/match': [2, 'camelcase'], 27 | }, 28 | }; 29 | ``` 30 | 31 | ## Plugin Options 32 | 33 | The following built-in values are supported: `pascalcase`/`PascalCase`, `camelcase`/`camelCase`, `snakecase`/`snake_case`, `kebabcase`/`kebab-case`. You can also provide your own regex: 34 | 35 | ```js 36 | ... 37 | 'filename-rules/match': [2, /^([a-z]+-)*[a-z]+(?:\..*)?$/], 38 | ... 39 | ``` 40 | 41 | You can also specify different options for different file extensions. In this case the plugin will only check files with extensions you explicitly provided: 42 | 43 | ```js 44 | ... 45 | 'filename-rules/match': [2, { '.js': 'camelCase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 46 | ... 47 | ``` 48 | 49 | You can use the `includePath: true` option to have the pattern matched against the full file path (instead of only the file basename): 50 | 51 | ```js 52 | ... 53 | 'filename-rules/match': [2, { includePath: true, pattern: /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 54 | ... 55 | ``` 56 | 57 | The inverse rule `not-match` checks that the files do NOT match the given pattern. Supports all the same options: 58 | 59 | ```js 60 | ... 61 | 'filename-rules/not-match': [2, 'camelCase'], 62 | ... 63 | ``` 64 | 65 | ## License 66 | 67 | MIT 68 | 69 | [npm-image]: https://img.shields.io/npm/v/eslint-plugin-filename-rules.svg?style=flat-square 70 | [npm-url]: https://npmjs.org/package/eslint-plugin-filename-rules 71 | [downloads-image]: https://img.shields.io/npm/dm/eslint-plugin-filename-rules.svg?style=flat-square 72 | [downloads-url]: https://npmjs.org/package/eslint-plugin-filename-rules 73 | [travis-image]: https://img.shields.io/travis/dolsem/eslint-plugin-filename-rules.svg?style=flat-square 74 | [travis-url]: https://travis-ci.org/dolsem/eslint-plugin-filename-rules 75 | [coverage-image]: https://img.shields.io/coveralls/dolsem/eslint-plugin-filename-rules.svg?style=flat-square 76 | [coverage-url]: https://coveralls.io/github/dolsem/eslint-plugin-filename-rules?branch=master 77 | [license-image]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square 78 | [license-url]: https://opensource.org/licenses/MIT 79 | -------------------------------------------------------------------------------- /lib/common/aliases.js: -------------------------------------------------------------------------------- 1 | const aliases = { 2 | pascalcase: /^[A-Z]([A-Z0-9]*[a-z]+)+[A-Z0-9]*(?:\..*)?$/, 3 | camelcase: /^[a-z]+((\d)|([A-Z0-9][a-z0-9]+))*([A-Z])?(?:\..*)?$/, 4 | snakecase: /^([a-z]+_)*[a-z]+(?:\..*)?$/, 5 | kebabcase: /^([a-z]+-)*[a-z]+(?:\..*)?$/, 6 | }; 7 | 8 | aliases.PascalCase = aliases.pascalcase; 9 | aliases.camelCase = aliases.camelcase; 10 | aliases.snake_case = aliases.snakecase; 11 | aliases['kebab-case'] = aliases.kebabcase; 12 | 13 | exports.aliases = aliases; 14 | -------------------------------------------------------------------------------- /lib/common/getRegex.js: -------------------------------------------------------------------------------- 1 | const { aliases } = require('./aliases'); 2 | 3 | const getRegex = (value, filename) => { 4 | if (value instanceof RegExp) return [value, value.toString()]; 5 | 6 | if (typeof value === 'string') { 7 | const predefinedRegex = aliases[value]; 8 | if (predefinedRegex) return [predefinedRegex, value]; 9 | 10 | if (value[0] === '/') { 11 | const ix = value.lastIndexOf('/'); 12 | if (ix > 0) { 13 | const regex = new RegExp(value.substring(1, ix), value.substring(ix + 1)); 14 | return [regex, value]; 15 | } 16 | } 17 | 18 | throw new Error(`Unrecognized option "${value}"`); 19 | } 20 | 21 | if (value.pattern) return getRegex(value.pattern, filename); 22 | 23 | const extension = filename.substr(filename.lastIndexOf('.')); 24 | const valueForExtension = value[extension]; 25 | return valueForExtension ? getRegex(valueForExtension) : []; 26 | }; 27 | 28 | exports.getRegex = getRegex; 29 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const match = require('./match'); 2 | const notMatch = require('./notMatch'); 3 | 4 | module.exports = { 5 | rules: { 6 | match, 7 | 'not-match': notMatch, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /lib/match.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { getRegex } = require('./common/getRegex'); 3 | 4 | const meta = { 5 | type: 'layout', 6 | docs: { 7 | description: 'checks that filenames match a chosen pattern', 8 | }, 9 | fixable: false, 10 | messages: { 11 | noMatch: "Filename '{{name}}' does not match {{value}}.", 12 | }, 13 | }; 14 | 15 | module.exports = { 16 | meta, 17 | create: (context) => ({ 18 | Program: (node) => { 19 | const filename = context.getFilename(); 20 | const includePath = !!(context.options[0] || {}).includePath; 21 | const name = includePath ? filename : path.basename(filename); 22 | const [regex, regexStr] = getRegex(context.options[0], name); 23 | if (!regex) return; 24 | if (!regex.test(name)) { 25 | context.report({ 26 | node, 27 | messageId: 'noMatch', 28 | data: { 29 | name, 30 | value: regexStr, 31 | }, 32 | }); 33 | } 34 | }, 35 | }), 36 | }; 37 | -------------------------------------------------------------------------------- /lib/notMatch.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { getRegex } = require('./common/getRegex'); 3 | 4 | const meta = { 5 | type: 'layout', 6 | docs: { 7 | description: 'checks that filenames do not match a chosen pattern', 8 | }, 9 | fixable: false, 10 | messages: { 11 | match: "Filename '{{name}}' must not match {{value}}.", 12 | }, 13 | }; 14 | 15 | module.exports = { 16 | meta, 17 | create: (context) => ({ 18 | Program: (node) => { 19 | const filename = context.getFilename(); 20 | const includePath = !!(context.options[0] || {}).includePath; 21 | const name = includePath ? filename : path.basename(filename); 22 | const [regex, regexStr] = getRegex(context.options[0], name); 23 | if (!regex) return; 24 | if (regex.test(name)) { 25 | context.report({ 26 | node, 27 | messageId: 'match', 28 | data: { 29 | name, 30 | value: regexStr, 31 | }, 32 | }); 33 | } 34 | }, 35 | }), 36 | }; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-filename-rules", 3 | "version": "1.3.1", 4 | "description": "Enforce filename conventions for linted files", 5 | "main": "lib/index.js", 6 | "engines": { 7 | "node": ">=6.0.0" 8 | }, 9 | "scripts": { 10 | "test": "ava 'test/**/*.test.js'", 11 | "test:watch": "npm run test -- --watch", 12 | "lint": "esw lib/ test/", 13 | "lint:watch": "npm run lint -- --watch", 14 | "cover": "nyc npm test", 15 | "coveralls": "nyc report --reporter=text-lcov | coveralls", 16 | "do-publish": "npm run lint && npm run cover && npm publish" 17 | }, 18 | "files": [ 19 | "lib", 20 | "LICENSE.md", 21 | "README.md" 22 | ], 23 | "directories": { 24 | "lib": "lib" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/dolsem/eslint-plugin-filename-rules.git" 29 | }, 30 | "keywords": [], 31 | "author": "Denis Olsem ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/dolsem/eslint-plugin-filename-rules/issues" 35 | }, 36 | "homepage": "https://github.com/dolsem/eslint-plugin-filename-rules#readme", 37 | "devDependencies": { 38 | "ava": "^0.18.2", 39 | "babel-register": "^6.24.0", 40 | "coveralls": "^2.12.0", 41 | "eslint": "^7.6.0", 42 | "eslint-config-airbnb-base": "^14.2.0", 43 | "eslint-plugin-import": "^2.22.0", 44 | "eslint-watch": "^7.0.0", 45 | "nyc": "^10.1.2" 46 | }, 47 | "dependencies": {}, 48 | "ava": { 49 | "require": [ 50 | "babel-register" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/lib/index.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import * as lib from '../../lib/index'; 4 | import match from '../../lib/match'; 5 | import notMatch from '../../lib/notMatch'; 6 | 7 | test('exports the match rule', (t) => { 8 | t.is(lib.rules.match, match); 9 | }); 10 | 11 | test('exports the notMatch rule', (t) => { 12 | t.is(lib.rules['not-match'], notMatch); 13 | }); 14 | -------------------------------------------------------------------------------- /test/lib/match.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { RuleTester } from 'eslint'; 3 | 4 | import * as match from '../../lib/match'; 5 | 6 | const tester = new RuleTester(); 7 | const testRule = (tests) => () => tester.run('match', match, tests); 8 | const code = '"hello world";'; 9 | 10 | test('single regex', testRule({ 11 | valid: [ 12 | { 13 | code, 14 | filename: '/foo/bar/test.txt', 15 | options: [/^test(?:\..*)?$/], 16 | }, 17 | { 18 | code, 19 | filename: '/foo/bar/test.txt', 20 | options: ['/^test(?:\\..*)/'], 21 | }, 22 | ], 23 | invalid: [ 24 | { 25 | code, 26 | filename: '/foo/bar/test_.txt', 27 | options: [/^test(?:\..*)?$/], 28 | errors: [ 29 | { message: "Filename 'test_.txt' does not match /^test(?:\\..*)?$/.", column: 1, line: 1 }, 30 | ], 31 | }, 32 | ], 33 | })); 34 | 35 | test('includePath', testRule({ 36 | valid: [ 37 | { 38 | code, 39 | filename: '/foo/bar/test.txt', 40 | options: [{ 41 | pattern: /^.*bar.*$/, 42 | includePath: true, 43 | }], 44 | }, 45 | ], 46 | invalid: [ 47 | { 48 | code, 49 | filename: '/foo/bar/test.txt', 50 | options: [{ 51 | pattern: /^.*baz.*$/, 52 | includePath: true, 53 | }], 54 | errors: [ 55 | { message: "Filename '/foo/bar/test.txt' does not match /^.*baz.*$/.", column: 1, line: 1 }, 56 | ], 57 | }, 58 | ], 59 | })); 60 | 61 | test('throws on unknown alias', (t) => t.throws(testRule({ 62 | valid: [ 63 | { 64 | code, 65 | filename: '/foo/bar/module.js', 66 | options: ['bla'], 67 | }, 68 | ], 69 | invalid: [], 70 | }), 'Unrecognized option "bla"\nOccurred while linting /foo/bar/module.js:1')); 71 | 72 | ['camelcase', 'camelCase'].forEach((alias) => { 73 | test(`single alias - ${alias}`, testRule({ 74 | valid: [ 75 | { 76 | code, 77 | filename: '/foo/bar/module.js', 78 | options: [alias], 79 | }, 80 | { 81 | code, 82 | filename: '/foo/bar/anotherModule.js', 83 | options: [alias], 84 | }, 85 | { 86 | code, 87 | filename: '/foo/bar/module.test.js', 88 | options: [alias], 89 | }, 90 | ], 91 | invalid: [ 92 | { 93 | code, 94 | filename: '/foo/bar/Module.js', 95 | options: [alias], 96 | errors: [ 97 | { message: `Filename 'Module.js' does not match ${alias}.`, column: 1, line: 1 }, 98 | ], 99 | }, 100 | { 101 | code, 102 | filename: '/foo/bar/AnotherModule.js', 103 | options: [alias], 104 | errors: [ 105 | { message: `Filename 'AnotherModule.js' does not match ${alias}.`, column: 1, line: 1 }, 106 | ], 107 | }, 108 | { 109 | code, 110 | filename: '/foo/bar/another-module.js', 111 | options: [alias], 112 | errors: [ 113 | { message: `Filename 'another-module.js' does not match ${alias}.`, column: 1, line: 1 }, 114 | ], 115 | }, 116 | { 117 | code, 118 | filename: '/foo/bar/another_module.js', 119 | options: [alias], 120 | errors: [ 121 | { message: `Filename 'another_module.js' does not match ${alias}.`, column: 1, line: 1 }, 122 | ], 123 | }, 124 | ], 125 | })); 126 | }); 127 | 128 | ['pascalcase', 'PascalCase'].forEach((alias) => { 129 | test(`single alias - ${alias}`, testRule({ 130 | valid: [ 131 | { 132 | code, 133 | filename: '/foo/bar/Url.js', 134 | options: [alias], 135 | }, 136 | { 137 | code, 138 | filename: '/foo/bar/AnotherModule.js', 139 | options: [alias], 140 | }, 141 | { 142 | code, 143 | filename: '/foo/bar/AnotherModule.test.js', 144 | options: [alias], 145 | }, 146 | { 147 | code, 148 | filename: '/foo/bar/ModuleNameWithACRONYM.js', 149 | options: [alias], 150 | }, 151 | ], 152 | invalid: [ 153 | { 154 | code, 155 | filename: '/foo/bar/URL.js', 156 | options: [alias], 157 | errors: [ 158 | { message: `Filename 'URL.js' does not match ${alias}.`, column: 1, line: 1 }, 159 | ], 160 | }, 161 | { 162 | code, 163 | filename: '/foo/bar/url.js', 164 | options: [alias], 165 | errors: [ 166 | { message: `Filename 'url.js' does not match ${alias}.`, column: 1, line: 1 }, 167 | ], 168 | }, 169 | { 170 | code, 171 | filename: '/foo/bar/anotherModule.js', 172 | options: [alias], 173 | errors: [ 174 | { message: `Filename 'anotherModule.js' does not match ${alias}.`, column: 1, line: 1 }, 175 | ], 176 | }, 177 | { 178 | code, 179 | filename: '/foo/bar/Another-Module.js', 180 | options: [alias], 181 | errors: [ 182 | { message: `Filename 'Another-Module.js' does not match ${alias}.`, column: 1, line: 1 }, 183 | ], 184 | }, 185 | { 186 | code, 187 | filename: '/foo/bar/Another_Module.js', 188 | options: [alias], 189 | errors: [ 190 | { message: `Filename 'Another_Module.js' does not match ${alias}.`, column: 1, line: 1 }, 191 | ], 192 | }, 193 | ], 194 | })); 195 | }); 196 | 197 | ['snakecase', 'snake_case'].forEach((alias) => { 198 | test(`single alias - ${alias}`, testRule({ 199 | valid: [ 200 | { 201 | code, 202 | filename: '/foo/bar/module.js', 203 | options: [alias], 204 | }, 205 | { 206 | code, 207 | filename: '/foo/bar/another_module.js', 208 | options: [alias], 209 | }, 210 | { 211 | code, 212 | filename: '/foo/bar/another_module.test.js', 213 | options: [alias], 214 | }, 215 | ], 216 | invalid: [ 217 | { 218 | code, 219 | filename: '/foo/bar/Module.js', 220 | options: [alias], 221 | errors: [ 222 | { message: `Filename 'Module.js' does not match ${alias}.`, column: 1, line: 1 }, 223 | ], 224 | }, 225 | { 226 | code, 227 | filename: '/foo/bar/AnotherModule.js', 228 | options: [alias], 229 | errors: [ 230 | { message: `Filename 'AnotherModule.js' does not match ${alias}.`, column: 1, line: 1 }, 231 | ], 232 | }, 233 | { 234 | code, 235 | filename: '/foo/bar/anotherModule.js', 236 | options: [alias], 237 | errors: [ 238 | { message: `Filename 'anotherModule.js' does not match ${alias}.`, column: 1, line: 1 }, 239 | ], 240 | }, 241 | { 242 | code, 243 | filename: '/foo/bar/another-module.js', 244 | options: [alias], 245 | errors: [ 246 | { message: `Filename 'another-module.js' does not match ${alias}.`, column: 1, line: 1 }, 247 | ], 248 | }, 249 | { 250 | code, 251 | filename: '/foo/bar/Another_Module.js', 252 | options: [alias], 253 | errors: [ 254 | { message: `Filename 'Another_Module.js' does not match ${alias}.`, column: 1, line: 1 }, 255 | ], 256 | }, 257 | ], 258 | })); 259 | }); 260 | 261 | ['kebabcase', 'kebab-case'].forEach((alias) => { 262 | test(`single alias - ${alias}`, testRule({ 263 | valid: [ 264 | { 265 | code, 266 | filename: '/foo/bar/module.js', 267 | options: [alias], 268 | }, 269 | { 270 | code, 271 | filename: '/foo/bar/another-module.js', 272 | options: [alias], 273 | }, 274 | { 275 | code, 276 | filename: '/foo/bar/another-module.test.js', 277 | options: [alias], 278 | }, 279 | ], 280 | invalid: [ 281 | { 282 | code, 283 | filename: '/foo/bar/Module.js', 284 | options: [alias], 285 | errors: [ 286 | { message: `Filename 'Module.js' does not match ${alias}.`, column: 1, line: 1 }, 287 | ], 288 | }, 289 | { 290 | code, 291 | filename: '/foo/bar/AnotherModule.js', 292 | options: [alias], 293 | errors: [ 294 | { message: `Filename 'AnotherModule.js' does not match ${alias}.`, column: 1, line: 1 }, 295 | ], 296 | }, 297 | { 298 | code, 299 | filename: '/foo/bar/anotherModule.js', 300 | options: [alias], 301 | errors: [ 302 | { message: `Filename 'anotherModule.js' does not match ${alias}.`, column: 1, line: 1 }, 303 | ], 304 | }, 305 | { 306 | code, 307 | filename: '/foo/bar/Another-Module.js', 308 | options: [alias], 309 | errors: [ 310 | { message: `Filename 'Another-Module.js' does not match ${alias}.`, column: 1, line: 1 }, 311 | ], 312 | }, 313 | { 314 | code, 315 | filename: '/foo/bar/another_module.js', 316 | options: [alias], 317 | errors: [ 318 | { message: `Filename 'another_module.js' does not match ${alias}.`, column: 1, line: 1 }, 319 | ], 320 | }, 321 | ], 322 | })); 323 | }); 324 | 325 | test('file extension mapping', testRule({ 326 | valid: [ 327 | { 328 | code, 329 | filename: '/foo/bar/someModule.js', 330 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 331 | }, 332 | { 333 | code, 334 | filename: '/foo/bar/some-module.ts', 335 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 336 | }, 337 | { 338 | code, 339 | filename: '/foo/bar/SkippedModule.jsx', 340 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 341 | }, 342 | ], 343 | invalid: [ 344 | { 345 | code, 346 | filename: '/foo/bar/some-module.js', 347 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 348 | errors: [ 349 | { message: "Filename 'some-module.js' does not match camelcase.", column: 1, line: 1 }, 350 | ], 351 | }, 352 | { 353 | code, 354 | filename: '/foo/bar/someModule.ts', 355 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 356 | errors: [ 357 | { message: "Filename 'someModule.ts' does not match /^([a-z]+-)*[a-z]+(?:\\..*)?$/.", column: 1, line: 1 }, 358 | ], 359 | }, 360 | ], 361 | })); 362 | -------------------------------------------------------------------------------- /test/lib/notMatch.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { RuleTester } from 'eslint'; 3 | 4 | import * as notMatch from '../../lib/notMatch'; 5 | 6 | const tester = new RuleTester(); 7 | const testRule = (tests) => () => tester.run('not-match', notMatch, tests); 8 | const code = '"hello world";'; 9 | 10 | test('single regex', testRule({ 11 | invalid: [ 12 | { 13 | code, 14 | filename: '/foo/bar/test.txt', 15 | options: [/^test(?:\..*)?$/], 16 | errors: [ 17 | { message: "Filename 'test.txt' must not match /^test(?:\\..*)?$/.", column: 1, line: 1 }, 18 | ], 19 | }, 20 | ], 21 | valid: [ 22 | { 23 | code, 24 | filename: '/foo/bar/test_.txt', 25 | options: [/^test(?:\..*)?$/], 26 | }, 27 | ], 28 | })); 29 | 30 | test('includePath', testRule({ 31 | invalid: [ 32 | { 33 | code, 34 | filename: '/foo/bar/test.txt', 35 | options: [{ 36 | pattern: /^.*bar.*$/, 37 | includePath: true, 38 | }], 39 | errors: [ 40 | { message: "Filename '/foo/bar/test.txt' must not match /^.*bar.*$/.", column: 1, line: 1 }, 41 | ], 42 | }, 43 | ], 44 | valid: [ 45 | { 46 | code, 47 | filename: '/foo/bar/test.txt', 48 | options: [{ 49 | pattern: /^.*baz.*$/, 50 | includePath: true, 51 | }], 52 | }, 53 | ], 54 | })); 55 | 56 | test('throws on unknown alias', (t) => t.throws(testRule({ 57 | valid: [ 58 | { 59 | code, 60 | filename: '/foo/bar/module.js', 61 | options: ['bla'], 62 | }, 63 | ], 64 | invalid: [], 65 | }), 'Unrecognized option "bla"\nOccurred while linting /foo/bar/module.js:1')); 66 | 67 | ['camelcase', 'camelCase'].forEach((alias) => { 68 | test(`single alias - ${alias}`, testRule({ 69 | invalid: [ 70 | { 71 | code, 72 | filename: '/foo/bar/module.js', 73 | options: [alias], 74 | errors: [ 75 | { message: `Filename 'module.js' must not match ${alias}.`, column: 1, line: 1 }, 76 | ], 77 | }, 78 | { 79 | code, 80 | filename: '/foo/bar/anotherModule.js', 81 | options: [alias], 82 | errors: [ 83 | { message: `Filename 'anotherModule.js' must not match ${alias}.`, column: 1, line: 1 }, 84 | ], 85 | }, 86 | { 87 | code, 88 | filename: '/foo/bar/module.test.js', 89 | options: [alias], 90 | errors: [ 91 | { message: `Filename 'module.test.js' must not match ${alias}.`, column: 1, line: 1 }, 92 | ], 93 | }, 94 | ], 95 | valid: [ 96 | { 97 | code, 98 | filename: '/foo/bar/Module.js', 99 | options: [alias], 100 | }, 101 | { 102 | code, 103 | filename: '/foo/bar/AnotherModule.js', 104 | options: [alias], 105 | }, 106 | { 107 | code, 108 | filename: '/foo/bar/another-module.js', 109 | options: [alias], 110 | }, 111 | { 112 | code, 113 | filename: '/foo/bar/another_module.js', 114 | options: [alias], 115 | }, 116 | ], 117 | })); 118 | }); 119 | 120 | ['pascalcase', 'PascalCase'].forEach((alias) => { 121 | test(`single alias - ${alias}`, testRule({ 122 | invalid: [ 123 | { 124 | code, 125 | filename: '/foo/bar/Url.js', 126 | options: [alias], 127 | errors: [ 128 | { message: `Filename 'Url.js' must not match ${alias}.`, column: 1, line: 1 }, 129 | ], 130 | }, 131 | { 132 | code, 133 | filename: '/foo/bar/AnotherModule.js', 134 | options: [alias], 135 | errors: [ 136 | { message: `Filename 'AnotherModule.js' must not match ${alias}.`, column: 1, line: 1 }, 137 | ], 138 | }, 139 | { 140 | code, 141 | filename: '/foo/bar/AnotherModule.test.js', 142 | options: [alias], 143 | errors: [ 144 | { message: `Filename 'AnotherModule.test.js' must not match ${alias}.`, column: 1, line: 1 }, 145 | ], 146 | }, 147 | { 148 | code, 149 | filename: '/foo/bar/ModuleNameWithACRONYM.js', 150 | options: [alias], 151 | errors: [ 152 | { message: `Filename 'ModuleNameWithACRONYM.js' must not match ${alias}.`, column: 1, line: 1 }, 153 | ], 154 | }, 155 | ], 156 | valid: [ 157 | { 158 | code, 159 | filename: '/foo/bar/URL.js', 160 | options: [alias], 161 | }, 162 | { 163 | code, 164 | filename: '/foo/bar/url.js', 165 | options: [alias], 166 | }, 167 | { 168 | code, 169 | filename: '/foo/bar/anotherModule.js', 170 | options: [alias], 171 | }, 172 | { 173 | code, 174 | filename: '/foo/bar/Another-Module.js', 175 | options: [alias], 176 | }, 177 | { 178 | code, 179 | filename: '/foo/bar/Another_Module.js', 180 | options: [alias], 181 | }, 182 | ], 183 | })); 184 | }); 185 | 186 | ['snakecase', 'snake_case'].forEach((alias) => { 187 | test(`single alias - ${alias}`, testRule({ 188 | invalid: [ 189 | { 190 | code, 191 | filename: '/foo/bar/module.js', 192 | options: [alias], 193 | errors: [ 194 | { message: `Filename 'module.js' must not match ${alias}.`, column: 1, line: 1 }, 195 | ], 196 | }, 197 | { 198 | code, 199 | filename: '/foo/bar/another_module.js', 200 | options: [alias], 201 | errors: [ 202 | { message: `Filename 'another_module.js' must not match ${alias}.`, column: 1, line: 1 }, 203 | ], 204 | }, 205 | { 206 | code, 207 | filename: '/foo/bar/another_module.test.js', 208 | options: [alias], 209 | errors: [ 210 | { message: `Filename 'another_module.test.js' must not match ${alias}.`, column: 1, line: 1 }, 211 | ], 212 | }, 213 | ], 214 | valid: [ 215 | { 216 | code, 217 | filename: '/foo/bar/Module.js', 218 | options: [alias], 219 | }, 220 | { 221 | code, 222 | filename: '/foo/bar/AnotherModule.js', 223 | options: [alias], 224 | }, 225 | { 226 | code, 227 | filename: '/foo/bar/anotherModule.js', 228 | options: [alias], 229 | }, 230 | { 231 | code, 232 | filename: '/foo/bar/another-module.js', 233 | options: [alias], 234 | }, 235 | { 236 | code, 237 | filename: '/foo/bar/Another_Module.js', 238 | options: [alias], 239 | }, 240 | ], 241 | })); 242 | }); 243 | 244 | ['kebabcase', 'kebab-case'].forEach((alias) => { 245 | test(`single alias - ${alias}`, testRule({ 246 | invalid: [ 247 | { 248 | code, 249 | filename: '/foo/bar/module.js', 250 | options: [alias], 251 | errors: [ 252 | { message: `Filename 'module.js' must not match ${alias}.`, column: 1, line: 1 }, 253 | ], 254 | }, 255 | { 256 | code, 257 | filename: '/foo/bar/another-module.js', 258 | options: [alias], 259 | errors: [ 260 | { message: `Filename 'another-module.js' must not match ${alias}.`, column: 1, line: 1 }, 261 | ], 262 | }, 263 | { 264 | code, 265 | filename: '/foo/bar/another-module.test.js', 266 | options: [alias], 267 | errors: [ 268 | { message: `Filename 'another-module.test.js' must not match ${alias}.`, column: 1, line: 1 }, 269 | ], 270 | }, 271 | ], 272 | valid: [ 273 | { 274 | code, 275 | filename: '/foo/bar/Module.js', 276 | options: [alias], 277 | }, 278 | { 279 | code, 280 | filename: '/foo/bar/AnotherModule.js', 281 | options: [alias], 282 | }, 283 | { 284 | code, 285 | filename: '/foo/bar/anotherModule.js', 286 | options: [alias], 287 | }, 288 | { 289 | code, 290 | filename: '/foo/bar/Another-Module.js', 291 | options: [alias], 292 | }, 293 | { 294 | code, 295 | filename: '/foo/bar/another_module.js', 296 | options: [alias], 297 | }, 298 | ], 299 | })); 300 | }); 301 | 302 | test('file extension mapping', testRule({ 303 | invalid: [ 304 | { 305 | code, 306 | filename: '/foo/bar/someModule.js', 307 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 308 | errors: [ 309 | { message: "Filename 'someModule.js' must not match camelcase.", column: 1, line: 1 }, 310 | ], 311 | }, 312 | { 313 | code, 314 | filename: '/foo/bar/some-module.ts', 315 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 316 | errors: [ 317 | { message: "Filename 'some-module.ts' must not match /^([a-z]+-)*[a-z]+(?:\\..*)?$/.", column: 1, line: 1 }, 318 | ], 319 | }, 320 | ], 321 | valid: [ 322 | { 323 | code, 324 | filename: '/foo/bar/some-module.js', 325 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 326 | }, 327 | { 328 | code, 329 | filename: '/foo/bar/someModule.ts', 330 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 331 | }, 332 | { 333 | code, 334 | filename: '/foo/bar/SkippedModule.jsx', 335 | options: [{ '.js': 'camelcase', '.ts': /^([a-z]+-)*[a-z]+(?:\..*)?$/ }], 336 | }, 337 | ], 338 | })); 339 | --------------------------------------------------------------------------------